[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: carson-katri # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug Report\ndescription: File a bug report\ntitle: \"<title>\"\nlabels: [\"bug\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Before filing a bug report, [search for an existing issue](https://github.com/carson-katri/dream-textures/issues?q=is%3Aissue).\n        \n        Also, ensure you are running the [latest version](https://github.com/carson-katri/dream-textures/releases/latest).\n  - type: textarea\n    id: description\n    attributes:\n      label: Description\n      description: Provide a clear and concise description of what the bug is.\n      placeholder: Description\n    validations:\n      required: true\n  - type: textarea\n    id: steps\n    attributes:\n      label: Steps to Reproduce\n      description: List the steps needed to reproduce the issue.\n      placeholder: |\n        1. Go to '...'\n        2. Click on '...'\n    validations:\n      required: true\n  - type: textarea\n    id: expected-behavior\n    attributes:\n      label: Expected Behavior\n      description: Describe what you expected to happen.\n      placeholder: |\n        The 'action' would do 'some amazing thing'.\n    validations:\n      required: true\n  - type: markdown\n    attributes:\n      value: |\n        Unless you are running on an unsupported platform, ensure you downloaded a [packaged release](https://github.com/carson-katri/dream-textures/releases/latest) and not the source code.\n  - type: dropdown\n    id: addon-version\n    attributes:\n      label: Addon Version\n      multiple: false\n      options:\n        - Windows (CUDA)\n        - Windows (DirectML)\n        - macOS (Apple Silicon)\n        - Other (Built from source)\n    validations:\n      required: true\n  - type: dropdown\n    id: blender-version\n    attributes:\n      label: Blender Version\n      multiple: false\n      options:\n        - Blender 4.1+\n        - Blender 3.6 - 4.0\n    validations:\n      required: true\n  - type: dropdown\n    id: hardware\n    attributes:\n      label: GPU\n      description: NVIDIA 16 series cards are known to have a difficult time running Stable Diffusion. Please see the other issues regarding these cards.\n      multiple: false\n      options:\n        - NVIDIA\n        - NVIDIA 16 Series\n        - AMD\n        - Apple Silicon\n        - Other\n    validations:\n      required: true"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/workflows/package-release.yml",
    "content": "name: Package Release\non:\n  push:\n    branches:\n      - \"releases/**\"\n  workflow_dispatch:\n\njobs:\n  package-release:\n    strategy:\n      matrix:\n        platform:\n          - requirements: win-linux-cuda.txt\n            os: windows-latest\n            filename: windows-cuda\n          - requirements: win-dml.txt\n            os: windows-latest\n            filename:  windows-directml\n          - requirements: mac-mps-cpu.txt\n            os: macos-14\n            filename: macos-arm\n        version:\n          - python: '3.10'\n            filename_suffix: ''\n          - python: '3.11'\n            filename_suffix: '-4-1'\n    runs-on: ${{ matrix.platform.os }}\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v3\n        with:\n          path: dream_textures\n      - name: Setup Python\n        uses: actions/setup-python@v4\n        with:\n          python-version: ${{ matrix.version.python }}\n          cache: 'pip'\n          cache-dependency-path: '**/${{ matrix.platform.requirements }}'\n      - name: Install dependencies into target\n        shell: bash\n        run: 'python -m pip install -r requirements/${{ matrix.platform.requirements }} --no-cache-dir --target .python_dependencies'\n        working-directory: dream_textures\n      - name: Zip dependencies with long paths\n        shell: bash\n        run: 'python ./dream_textures/scripts/zip_dependencies.py'\n      - name: Archive Release\n        uses: thedoctor0/zip-release@main\n        with:\n          type: zip\n          filename: dream_textures-${{ matrix.platform.filename }}${{ matrix.version.filename_suffix }}.zip\n          exclusions: '*.git*'\n      - name: Archive and upload artifact\n        uses: actions/upload-artifact@v3\n        with:\n          name: dream_textures-${{ matrix.platform.filename }}${{ matrix.version.filename_suffix }}\n          path: dream_textures-${{ matrix.platform.filename }}${{ matrix.version.filename_suffix }}.zip"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: Close inactive issues\non:\n  schedule:\n    - cron: \"30 1 * * *\"\n  workflow_dispatch:\n\njobs:\n  close-issues:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n      pull-requests: write\n    steps:\n      - uses: actions/stale@v5\n        with:\n          days-before-issue-stale: 60\n          days-before-issue-close: 7\n          stale-issue-label: \"stale\"\n          stale-issue-message: \"This issue is stale because it has been open for 60 days with no activity.\"\n          close-issue-message: \"This issue was closed because it has been inactive for 7 days since being marked as stale.\"\n          days-before-pr-stale: -1\n          days-before-pr-close: -1\n          repo-token: ${{ secrets.GITHUB_TOKEN }}\n          only-labels: bug"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.python_dependencies.zip\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# poetry\n#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.\n#   This is especially recommended for binary packages to ensure reproducibility, and is more\n#   commonly ignored for libraries.\n#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control\n#poetry.lock\n\n# pdm\n#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.\n#pdm.lock\n#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it\n#   in version control.\n#   https://pdm.fming.dev/#use-with-ide\n.pdm.toml\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n# PyCharm\n#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can\n#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore\n#  and can be added to the global gitignore or merged into this file.  For a more nuclear\n#  option (not recommended) you can uncomment the following to ignore the entire idea folder.\n#.idea/\n"
  },
  {
    "path": ".python_dependencies/.gitignore",
    "content": "*\n!.gitignore"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "README.md",
    "content": "![Dream Textures, subtitle: Stable Diffusion built-in to Blender](docs/assets/banner.png)\n\n[![Latest Release](https://flat.badgen.net/github/release/carson-katri/dream-textures)](https://github.com/carson-katri/dream-textures/releases/latest)\n[![Join the Discord](https://flat.badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/EmDJ8CaWZ7)\n[![Total Downloads](https://img.shields.io/github/downloads/carson-katri/dream-textures/total?style=flat-square)](https://github.com/carson-katri/dream-textures/releases/latest)\n[![Buy on Blender Market](https://flat.badgen.net/badge/buy/blender%20market/orange)](https://www.blendermarket.com/products/dream-textures)\n\n* Create textures, concept art, background assets, and more with a simple text prompt\n* Use the 'Seamless' option to create textures that tile perfectly with no visible seam\n* Texture entire scenes with 'Project Dream Texture' and depth to image\n* Re-style animations with the Cycles render pass\n* Run the models on your machine to iterate without slowdowns from a service\n\n# Installation\nDownload the [latest release](https://github.com/carson-katri/dream-textures/releases/latest) and follow the instructions there to get up and running.\n\n> On macOS, it is possible you will run into a quarantine issue with the dependencies. To work around this, run the following command in the app `Terminal`: `xattr -r -d com.apple.quarantine ~/Library/Application\\ Support/Blender/3.3/scripts/addons/dream_textures/.python_dependencies`. This will allow the PyTorch `.dylib`s and `.so`s to load without having to manually allow each one in System Preferences.\n\nIf you want a visual guide to installation, see this video tutorial from Ashlee Martino-Tarr: https://youtu.be/kEcr8cNmqZk\n> Ensure you always install the [latest version](https://github.com/carson-katri/dream-textures/releases/latest) of the add-on if any guides become out of date.\n\n# Usage\n\nHere's a few quick guides:\n\n## [Setting Up](https://github.com/carson-katri/dream-textures/wiki/Setup)\nSetup instructions for various platforms and configurations.\n\n## [Image Generation](https://github.com/carson-katri/dream-textures/wiki/Image-Generation)\nCreate textures, concept art, and more with text prompts. Learn how to use the various configuration options to get exactly what you're looking for.\n\n![A graphic showing each step of the image generation process](docs/assets/image_generation.png)\n\n## [Texture Projection](https://github.com/carson-katri/dream-textures/wiki/Texture-Projection)\nTexture entire models and scenes with depth to image.\n\n![A graphic showing each step of the texture projection process](docs/assets/texture_projection.png)\n\n## [Inpaint/Outpaint](https://github.com/carson-katri/dream-textures/wiki/Inpaint-and-Outpaint)\nInpaint to fix up images and convert existing textures into seamless ones automatically.\n\nOutpaint to increase the size of an image by extending it in any direction.\n\n![A graphic showing each step of the outpainting process](docs/assets/inpaint_outpaint.png)\n\n## [Render Engine](https://github.com/carson-katri/dream-textures/wiki/Render-Engine)\nUse the Dream Textures node system to create complex effects.\n\n![A graphic showing each frame of a render, split with the scene and generated result](docs/assets/render_pass.png)\n\n## [AI Upscaling](https://github.com/carson-katri/dream-textures/wiki/AI-Upscaling)\nUpscale your low-res generations 4x.\n\n![A graphic showing each step of the upscaling process](docs/assets/upscale.png)\n\n## [History](https://github.com/carson-katri/dream-textures/wiki/History)\nRecall, export, and import history entries for later use.\n\n# Compatibility\nDream Textures has been tested with CUDA and Apple Silicon GPUs. Over 4GB of VRAM is recommended.\n\nIf you have an issue with a supported GPU, please create an issue.\n\n### Cloud Processing\nIf your hardware is unsupported, you can use DreamStudio to process in the cloud. Follow the instructions in the release notes to setup with DreamStudio.\n\n# Contributing\nFor detailed instructions on installing from source, see the guide on [setting up a development environment](https://github.com/carson-katri/dream-textures/wiki/Setting-Up-a-Development-Environment).\n\n# Troubleshooting\n\nIf you are experiencing trouble getting Dream Textures running, check Blender's system console (in the top left under the \"Window\" dropdown next to \"File\" and \"Edit\") for any error messages. Then [search in the issues list](https://github.com/carson-katri/dream-textures/issues?q=is%3Aissue) with your error message and symptoms.\n\n> **Note** On macOS there is no option to open the system console. Instead, you can get logs by opening the app *Terminal*, entering the command `/Applications/Blender.app/Contents/MacOS/Blender` and pressing the Enter key. This will launch Blender and any error messages will show up in the Terminal app.\n\n![A screenshot of the \"Window\" > \"Toggle System Console\" menu action in Blender](docs/assets/readme-toggle-console.png)\n\nFeatures and feedback are also accepted on the issues page. If you have any issues that aren't listed, feel free to add them there!\n\nThe [Dream Textures Discord server](https://discord.gg/EmDJ8CaWZ7) also has a common issues list and strong community of helpful people, so feel free to come by for some help there as well.\n\n"
  },
  {
    "path": "__init__.py",
    "content": "# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful, but\n# WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n# General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program. If not, see <http://www.gnu.org/licenses/>.\n\nbl_info = {\n    \"name\": \"Dream Textures\",\n    \"author\": \"Dream Textures contributors\",\n    \"description\": \"Use Stable Diffusion to generate unique textures straight from the shader editor.\",\n    \"blender\": (3, 1, 0),\n    \"version\": (0, 4, 1),\n    \"location\": \"Image Editor -> Sidebar -> Dream\",\n    \"category\": \"Paint\"\n}\n\nfrom multiprocessing import current_process\n\nif current_process().name != \"__actor__\":\n    import bpy\n    from bpy.props import IntProperty, PointerProperty, EnumProperty, BoolProperty, CollectionProperty\n    import sys\n    import os\n\n    module_name = os.path.basename(os.path.dirname(__file__))\n    def clear_modules():\n        for name in list(sys.modules.keys()):\n            if name.startswith(module_name) and name != module_name:\n                del sys.modules[name]\n    clear_modules() # keep before all addon imports\n\n    from .render_pass import register_render_pass, unregister_render_pass, pass_inputs\n    from .prompt_engineering import *\n    from .operators.open_latest_version import check_for_updates\n    from .operators.project import framebuffer_arguments\n    from .classes import CLASSES, PREFERENCE_CLASSES\n    from .tools import TOOLS\n    from .operators.dream_texture import DreamTexture, kill_generator\n    from .property_groups.dream_prompt import DreamPrompt\n    from .property_groups.seamless_result import SeamlessResult\n    from .ui.presets import register_default_presets\n    \n    from . import engine\n\n    from .diffusers_backend import DiffusersBackend\n\n    requirements_path_items = (\n        ('requirements/win-linux-cuda.txt', 'Linux/Windows (CUDA)', 'Linux or Windows with NVIDIA GPU'),\n        ('requirements/mac-mps-cpu.txt', 'Apple Silicon', 'Apple M1/M2'),\n        ('requirements/linux-rocm.txt', 'Linux (AMD)', 'Linux with AMD GPU'),\n        ('requirements/win-dml.txt', 'Windows (DirectML)', 'Windows with DirectX 12 GPU'),\n        ('requirements/dreamstudio.txt', 'DreamStudio', 'Cloud Compute Service')\n    )\n\n    def register():\n        dt_op = bpy.ops\n        for name in DreamTexture.bl_idname.split(\".\"):\n            dt_op = getattr(dt_op, name)\n        if hasattr(bpy.types, dt_op.idname()): # objects under bpy.ops are created on the fly, have to check that it actually exists a little differently\n            raise RuntimeError(\"Another instance of Dream Textures is already running.\")\n\n        bpy.types.Scene.dream_textures_requirements_path = EnumProperty(name=\"Platform\", items=requirements_path_items, description=\"Specifies which set of dependencies to install\", default='requirements/mac-mps-cpu.txt' if sys.platform == 'darwin' else 'requirements/win-linux-cuda.txt')\n\n        for cls in PREFERENCE_CLASSES:\n            bpy.utils.register_class(cls)\n        \n        bpy.types.Scene.dream_textures_history = CollectionProperty(type=DreamPrompt)\n\n        check_for_updates()\n\n        bpy.types.Scene.dream_textures_prompt = PointerProperty(type=DreamPrompt)\n        bpy.types.Scene.dream_textures_prompt_file = PointerProperty(type=bpy.types.Text)\n        bpy.types.Scene.init_img = PointerProperty(name=\"Init Image\", type=bpy.types.Image)\n        bpy.types.Scene.init_mask = PointerProperty(name=\"Init Mask\", type=bpy.types.Image)\n        bpy.types.Scene.init_depth = PointerProperty(name=\"Init Depth\", type=bpy.types.Image, description=\"Use an existing depth map. Leave blank to generate one from the init image\")\n        bpy.types.Scene.seamless_result = PointerProperty(type=SeamlessResult)\n        def get_selection_preview(self):\n            history = bpy.context.scene.dream_textures_history\n            if self.dream_textures_history_selection > 0 and self.dream_textures_history_selection < len(history):\n                return history[self.dream_textures_history_selection].generate_prompt()\n            return \"\"\n        bpy.types.Scene.dream_textures_history_selection = IntProperty(default=1)\n        bpy.types.Scene.dream_textures_history_selection_preview = bpy.props.StringProperty(name=\"\", default=\"\", get=get_selection_preview, set=lambda _, __: None)\n        bpy.types.Scene.dream_textures_progress = bpy.props.IntProperty(name=\"\", default=0, min=0, max=0)\n        bpy.types.Scene.dream_textures_info = bpy.props.StringProperty(name=\"Info\")\n        bpy.types.Scene.dream_textures_last_execution_time = bpy.props.StringProperty(name=\"Last Execution Time\", default=\"\")\n\n        bpy.types.Scene.dream_textures_viewport_enabled = BoolProperty(name=\"Viewport Enabled\", default=False)\n        bpy.types.Scene.dream_textures_render_properties_enabled = BoolProperty(default=False)\n        bpy.types.Scene.dream_textures_render_properties_prompt = PointerProperty(type=DreamPrompt)\n        bpy.types.Scene.dream_textures_render_properties_pass_inputs = EnumProperty(name=\"Pass Inputs\", items=pass_inputs)\n        \n        bpy.types.Scene.dream_textures_upscale_prompt = PointerProperty(type=DreamPrompt)\n        bpy.types.Scene.dream_textures_upscale_tile_size = IntProperty(name=\"Tile Size\", default=128, step=64, min=64, max=512)\n        bpy.types.Scene.dream_textures_upscale_blend = IntProperty(name=\"Blend\", default=32, step=8, min=0, max=512)\n        bpy.types.Scene.dream_textures_upscale_seamless_result = PointerProperty(type=SeamlessResult)\n        \n        bpy.types.Scene.dream_textures_project_prompt = PointerProperty(type=DreamPrompt)\n        bpy.types.Scene.dream_textures_project_framebuffer_arguments = EnumProperty(name=\"Inputs\", items=framebuffer_arguments)\n        bpy.types.Scene.dream_textures_project_bake = BoolProperty(name=\"Bake\", default=False, description=\"Re-maps the generated texture onto the specified UV map\")\n        def project_use_controlnet(self, context):\n            if self.dream_textures_project_use_control_net:\n                if len(self.dream_textures_project_prompt.control_nets) < 1:\n                    self.dream_textures_project_prompt.control_nets.add()\n            else:\n                self.dream_textures_project_prompt.control_nets.clear()\n        bpy.types.Scene.dream_textures_project_use_control_net = BoolProperty(name=\"Use ControlNet\", default=False, description=\"Use a depth ControlNet instead of a depth model\", update=project_use_controlnet)\n\n        engine.register()\n\n        for cls in CLASSES:\n            bpy.utils.register_class(cls)\n\n        for tool in TOOLS:\n            bpy.utils.register_tool(tool)\n\n        bpy.types.Scene.dream_textures_render_engine = PointerProperty(type=engine.DreamTexturesRenderEngineProperties)\n\n        bpy.types.RENDER_PT_context.append(engine.draw_device)\n\n        # Monkey patch cycles render passes\n        register_render_pass()\n\n        register_default_presets()\n        \n        # Register the default backend.\n        bpy.utils.register_class(DiffusersBackend)\n\n    def unregister():\n        for cls in PREFERENCE_CLASSES:\n            bpy.utils.unregister_class(cls)\n\n        for cls in CLASSES:\n            bpy.utils.unregister_class(cls)\n        for tool in TOOLS:\n            bpy.utils.unregister_tool(tool)\n\n        bpy.types.RENDER_PT_context.remove(engine.draw_device)\n\n        engine.unregister()\n        \n        unregister_render_pass()\n\n        # Unregister the default backend\n        bpy.utils.unregister_class(DiffusersBackend)\n\n        kill_generator()"
  },
  {
    "path": "absolute_path.py",
    "content": "import os\n\ndef absolute_path(component: str):\n    \"\"\"\n    Returns the absolute path to a file in the addon directory.\n\n    Alternative to `os.abspath` that works the same on macOS and Windows.\n    \"\"\"\n    return os.path.join(os.path.dirname(os.path.realpath(__file__)), component)\n\nREAL_ESRGAN_WEIGHTS_PATH = absolute_path(\"weights/realesrgan/realesr-general-x4v3.pth\")\nCLIPSEG_WEIGHTS_PATH = absolute_path(\"weights/clipseg/rd64-uni.pth\")"
  },
  {
    "path": "api/__init__.py",
    "content": "from .models import *\nfrom .backend import *"
  },
  {
    "path": "api/backend/__init__.py",
    "content": "from .backend import *"
  },
  {
    "path": "api/backend/backend.py",
    "content": "try:\n    import bpy\n    from typing import Callable, List, Tuple\n    from ..models.generation_arguments import GenerationArguments\n    from ..models.generation_result import GenerationResult\n    from ..models.model import Model\n\n    StepCallback = Callable[[List[GenerationResult]], bool]\n    Callback = Callable[[List[GenerationResult] | Exception], None]\n\n    class Backend(bpy.types.PropertyGroup):\n        \"\"\"A backend for Dream Textures.\n\n        Provide the following methods to create a valid backend.\n\n        ```python\n        def list_models(self) -> List[Model]\n        def generate(\n            self,\n            arguments: GenerationArguments,\n\n            step_callback: StepCallback,\n            callback: Callback\n        )\n        ```\n        \"\"\"\n\n        @classmethod\n        def register(cls):\n            from ...property_groups.dream_prompt import DreamPrompt\n            setattr(DreamPrompt, cls._attribute(), bpy.props.PointerProperty(type=cls))\n        \n        @classmethod\n        def unregister(cls):\n            from ...property_groups.dream_prompt import DreamPrompt\n            delattr(DreamPrompt, cls._attribute())\n\n        @classmethod\n        def _id(cls) -> str:\n            return f\"{cls.__module__}.{cls.__name__}\"\n        \n        @classmethod\n        def _attribute(cls) -> str:\n            return cls._id().replace('.', '_')\n        \n        @classmethod\n        def _lookup(cls, id):\n            return next(\n                (backend for backend in cls._list_backends() if backend._id() == id),\n                next(iter(cls._list_backends()), None)\n            )\n        \n        @classmethod\n        def _list_backends(cls):\n            return cls.__subclasses__()\n\n        def list_models(self, context) -> List[Model]:\n            \"\"\"Provide a list of available models.\n            \n            The `id` of the model will be provided.\n            \"\"\"\n            ...\n        \n        def list_controlnet_models(self, context) -> List[Model]:\n            \"\"\"Provide a list of available ControlNet models.\n\n            The `id` of the model will be provided.\n            \"\"\"\n            return []\n        \n        def list_schedulers(self, context) -> List[str]:\n            \"\"\"Provide a list of available schedulers.\"\"\"\n            ...\n        \n        def draw_prompt(self, layout, context):\n            \"\"\"Draw additional UI in the 'Prompt' panel\"\"\"\n            ...\n        \n        def draw_advanced(self, layout, context):\n            \"\"\"Draw additional UI in the 'Advanced' panel\"\"\"\n            ...\n\n        def draw_speed_optimizations(self, layout, context):\n            \"\"\"Draw additional UI in the 'Speed Optimizations' panel\"\"\"\n            ...\n        \n        def draw_memory_optimizations(self, layout, context):\n            \"\"\"Draw additional UI in the 'Memory Optimizations' panel\"\"\"\n            ...\n        \n        def draw_extra(self, layout, context):\n            \"\"\"Draw additional UI in the panel\"\"\"\n            ...\n        \n        def get_batch_size(self, context) -> int:\n            \"\"\"Return the selected batch size for the backend (if applicable).\n            \n            A default implementation is provided that returns `1`.\n            \"\"\"\n            return 1\n\n        def generate(\n            self,\n            arguments: GenerationArguments,\n            step_callback: StepCallback,\n            callback: Callback\n        ):\n            \"\"\"\n            A request to generate an image.\n\n            If the `step_callback` returns `False`, the generation should be cancelled.\n            After cancelling, `callback` should be called with an `InterruptedError`.\n            \"\"\"\n            ...\n        \n        def validate(\n            self,\n            arguments: GenerationArguments\n        ):\n            \"\"\"Validates the given arguments in the UI without generating.\n            \n            This validation should occur as quickly as possible.\n            \n            To report problems with the inputs, raise a `ValueError`.\n            Use the `FixItError` to provide a solution to the problem as well.\n            \n            ```python\n            if arguments.steps % 2 == 0:\n                throw FixItError(\n                    \"The number of steps is even\",\n                    solution=FixItError.UpdateGenerationArgumentsSolution(\n                        title=\"Add 1 more step\",\n                        arguments=dataclasses.replace(\n                            arguments,\n                            steps=arguments.steps + 1\n                        )\n                    )\n                )\n            ```\n            \"\"\"\n            ...\nexcept:\n    pass"
  },
  {
    "path": "api/models/__init__.py",
    "content": "from .generation_result import *\nfrom .model import *\nfrom .prompt import *\nfrom .seamless_axes import *\nfrom .step_preview_mode import *\nfrom .task import *\nfrom .fix_it_error import *"
  },
  {
    "path": "api/models/control_net.py",
    "content": "from dataclasses import dataclass\nfrom typing import Tuple, List\nfrom numpy.typing import NDArray\n\n@dataclass\nclass ControlNet:\n    model: str\n    \"\"\"The selected ControlNet model used for generation\"\"\"\n\n    image: NDArray\n    \"\"\"The control image\"\"\"\n\n    strength: float\n    \"\"\"The strength of the ControlNet's influence\"\"\""
  },
  {
    "path": "api/models/fix_it_error.py",
    "content": "from typing import Callable, Any\nfrom .generation_arguments import GenerationArguments\nfrom dataclasses import dataclass\n\nclass FixItError(Exception):\n    \"\"\"An exception with a solution.\n\n    Call the `draw` method to render the UI elements responsible for resolving this error.\n    \"\"\"\n    def __init__(self, message, solution: 'Solution'):\n        super().__init__(message)\n\n        self._solution = solution\n    \n    def _draw(self, dream_prompt, context, layout):\n        self._solution._draw(dream_prompt, context, layout)\n    \n    @dataclass\n    class Solution:\n        def _draw(self, dream_prompt, context, layout):\n            ...\n\n    @dataclass\n    class ChangeProperty(Solution):\n        \"\"\"Prompts the user to change the given `property` of the `GenerationArguments`.\"\"\"\n        property: str\n\n        def _draw(self, dream_prompt, context, layout):\n            layout.prop(dream_prompt, self.property)\n    \n    @dataclass\n    class RunOperator(Solution):\n        \"\"\"Runs the given operator\"\"\"\n        title: str\n        operator: str\n        modify_operator: Callable[[Any], None]\n\n        def _draw(self, dream_prompt, context, layout):\n            self.modify_operator(\n                layout.operator(self.operator, text=self.title)\n            )"
  },
  {
    "path": "api/models/generation_arguments.py",
    "content": "from dataclasses import dataclass\nfrom typing import Tuple, List\nfrom ..models.task import Task\nfrom ..models.model import Model\nfrom ..models.prompt import Prompt\nfrom ..models.seamless_axes import SeamlessAxes\nfrom ..models.step_preview_mode import StepPreviewMode\nfrom ..models.control_net import ControlNet\n\n@dataclass\nclass GenerationArguments:\n    task: Task\n    \"\"\"The type of generation to perform.\n    \n    Use a match statement to perform different actions based on the selected task.\n    \n    ```python\n    match task:\n        case PromptToImage():\n            ...\n        case ImageToImage(image=image, strength=strength, fit=fit):\n            ...\n        case Inpaint(image=image, fit=fit, strength=strength, mask_source=mask_source, mask_prompt=mask_prompt, confidence=confidence):\n            ...\n        case DepthToImage(depth=depth, image=image, strength=strength):\n            ...\n        case Outpaint(image=image, origin=origin):\n            ...\n        case _:\n            raise NotImplementedError()\n    ```\n    \"\"\"\n\n    model: Model\n    \"\"\"The selected model.\n\n    This is one of the options provided by `Backend.list_models`.\n    \"\"\"\n\n    prompt: Prompt\n    \"\"\"The positive and (optionally) negative prompt.\n\n    If `prompt.negative` is `None`, then the 'Negative Prompt' panel was disabled by the user.\n    \"\"\"\n    \n    size: Tuple[int, int] | None\n    \"\"\"The target size of the image, or `None` to use the native size of the model.\"\"\"\n\n    seed: int\n    \"\"\"The random or user-provided seed to use.\"\"\"\n\n    steps: int\n    \"\"\"The number of inference steps to perform.\"\"\"\n\n    guidance_scale: float\n    \"\"\"The selected classifier-free guidance scale.\"\"\"\n\n    scheduler: str\n    \"\"\"The selected scheduler.\n    \n    This is one of the options provided by `Backend.list_schedulers`.\n    \"\"\"\n\n    seamless_axes: SeamlessAxes\n    \"\"\"Which axes to tile seamlessly.\"\"\"\n\n    step_preview_mode: StepPreviewMode\n    \"\"\"The style of preview to display at each step.\"\"\"\n\n    iterations: int\n    \"\"\"The number of images to generate.\n    \n    The value sent to `callback` should contain the same number of `GenerationResult` instances in a list.\n    \"\"\"\n\n    control_nets: List[ControlNet]\n\n    @staticmethod\n    def _map_property_name(name: str) -> str | List[str] | None:\n        \"\"\"Converts a property name from `GenerationArguments` to the corresponding property of a `DreamPrompt`.\"\"\"\n        match name:\n            case \"model\":\n                return \"model\"\n            case \"prompt\":\n                return [\"prompt\", \"use_negative_prompt\", \"negative_prompt\"]\n            case \"prompt.positive\":\n                return \"prompt\"\n            case \"prompt.negative\":\n                return [\"use_negative_prompt\", \"negative_prompt\"]\n            case \"size\":\n                return [\"use_size\", \"width\", \"height\"]\n            case \"seed\":\n                return \"seed\"\n            case \"steps\":\n                return \"steps\"\n            case \"guidance_scale\":\n                return \"cfg_scale\"\n            case \"scheduler\":\n                return \"scheduler\"\n            case \"seamless_axes\":\n                return \"seamless_axes\"\n            case \"step_preview_mode\":\n                return \"step_preview_mode\"\n            case \"iterations\":\n                return \"iterations\"\n            case _:\n                return None"
  },
  {
    "path": "api/models/generation_result.py",
    "content": "from dataclasses import dataclass\nfrom numpy.typing import NDArray\nimport numpy as np\nimport math\n\n@dataclass\nclass GenerationResult:\n    \"\"\"The output of a `Backend`.\n\n    Create a result with an `image` and a `seed`.\n\n    ```python\n    result = GenerationResult(\n        progress=3,\n        total=5,\n        image=np.zeros((512, 512, 3)),\n        seed=42\n    )\n    ```\n\n    Alternatively, create a result with just a `title` and progress values.\n\n    ```python\n    result = GenerationResult(\n        progress=3,\n        total=5,\n        title=\"Loading model\"\n    )\n    ```\n    \"\"\"\n\n    progress: int\n    \"\"\"The amount out of `total` that has been completed\"\"\"\n\n    total: int\n    \"\"\"The number of steps to complete\"\"\"\n\n    seed: int\n    \"\"\"The seed used to generate the image.\"\"\"\n\n    title: str | None = None\n    \"\"\"The name of the currently executing task\"\"\"\n    \n    image: NDArray | None = None\n    \"\"\"The generated image as a Numpy array.\n\n    The shape should be `(height, width, channels)`, where `channels` is 3 or 4.\n    \"\"\"\n\n    @staticmethod\n    def tile_images(results: list['GenerationResult']) -> NDArray:\n        images = [result.image for result in results]\n        if len(images) == 0:\n            return None\n        elif len(images) == 1:\n            return images[0]\n        width = images[0].shape[1]\n        height = images[0].shape[0]\n        tiles_x = math.ceil(math.sqrt(len(images)))\n        tiles_y = math.ceil(len(images) / tiles_x)\n        tiles = np.zeros((height * tiles_y, width * tiles_x, images[0].shape[2]), dtype=images[0].dtype)\n        bottom_offset = (tiles_x*tiles_y-len(images)) * width // 2\n        bottom = (tiles_y - 1) * height\n        for i, image in enumerate(images):\n            x = i % tiles_x\n            y = int((i - x) / tiles_x)\n            x *= width\n            y *= height\n            if y == bottom:\n                x += bottom_offset\n            tiles[y: y + height, x: x + width] = image\n        return tiles\n"
  },
  {
    "path": "api/models/model.py",
    "content": "from dataclasses import dataclass\n\n@dataclass\nclass Model:\n    name: str\n    description: str\n    id: str"
  },
  {
    "path": "api/models/prompt.py",
    "content": "from dataclasses import dataclass\nfrom typing import List\n\n@dataclass\nclass Prompt:\n    positive: str | List[str]\n    negative: str | List[str] | None"
  },
  {
    "path": "api/models/seamless_axes.py",
    "content": "from enum import Enum\n\nclass SeamlessAxes(Enum):\n    \"\"\"Unified handling of seamless axes.\n    Can be converted from str (id or text) or bool tuple/list (x, y).\n    Each enum is equal to their respective convertible values.\n    Special cases:\n        AUTO: None\n        OFF: False, empty str\n        BOTH: True\n    \"\"\"\n\n    AUTO =       'auto', 'Auto-detect', None,  None\n    OFF =        'off',  'Off',         False, False\n    HORIZONTAL = 'x',    'X',           True,  False\n    VERTICAL =   'y',    'Y',           False, True\n    BOTH =       'xy',   'Both',        True,  True\n\n    def __init__(self, id, text, x, y):\n        self.id = id\n        self.text = text\n        self.x = x\n        self.y = y\n\n    def __eq__(self, other):\n        if isinstance(other, type(self)):\n            return self is other\n        if isinstance(other, str):\n            return self.id == other or self.text == other or (other == '' and self is self.OFF)\n        if isinstance(other, (tuple, list)) and len(other) == 2:\n            return self.x == other[0] and self.y == other[1]\n        if other is True and self is self.BOTH:\n            return True\n        if other is False and self is self.OFF:\n            return True\n        if other is None and self is self.AUTO:\n            return True\n        return False\n\n    def __and__(self, other):\n        return SeamlessAxes((self.x and other.x, self.y and other.y))\n\n    def __or__(self, other):\n        return SeamlessAxes((self.x or other.x, self.y or other.y))\n\n    def __xor__(self, other):\n        return SeamlessAxes((self.x != other.x, self.y != other.y))\n\n    def __invert__(self):\n        return SeamlessAxes((not self.x, not self.y))\n\n    @classmethod\n    def _missing_(cls, value):\n        if isinstance(value, str):\n            if value == '':\n                return cls.OFF\n            for e in cls:\n                if e.id == value or e.text == value:\n                    return e\n            raise ValueError(f'no {cls.__name__} with id {repr(id)}')\n        elif isinstance(value, (tuple, list)) and len(value) == 2:\n            for e in cls:\n                if e.x == value[0] and e.y == value[1]:\n                    return e\n            raise ValueError(f'no {cls.__name__} with x {value[0]} and y {value[1]}')\n        elif value is True:\n            return cls.BOTH\n        elif value is False:\n            return cls.OFF\n        elif value is None:\n            return cls.AUTO\n        raise TypeError(f'expected str, bool, tuple[bool, bool], or None, got {repr(value)}')\n\n    def bpy_enum(self, *args):\n        return self.id, self.text, *args"
  },
  {
    "path": "api/models/step_preview_mode.py",
    "content": "import enum\n\nclass StepPreviewMode(enum.Enum):\n    NONE = \"None\"\n    FAST = \"Fast\"\n    FAST_BATCH = \"Fast (Batch Tiled)\"\n    ACCURATE = \"Accurate\"\n    ACCURATE_BATCH = \"Accurate (Batch Tiled)\""
  },
  {
    "path": "api/models/task.py",
    "content": "from dataclasses import dataclass\nfrom typing import Tuple\nfrom numpy.typing import NDArray\nfrom enum import IntEnum\n\nclass Task:\n    \"\"\"A specific task type.\n    \n    Access the properties of the task using dot notation.\n    \n    ```python\n    # Task.ImageToImage\n    task.image\n    task.strength\n    task.fit\n    ```\n\n    Switch over the task to perform the correct actions.\n\n    ```python\n    match type(task):\n        case PromptToImage:\n            ...\n        case ImageToImage:\n            ...\n        case Inpaint:\n            ...\n        case DepthToImage:\n            ...\n        case Outpaint:\n            ...\n    ```\n    \"\"\"\n\n    @classmethod\n    def name(cls) -> str:\n        \"unknown\"\n    \"\"\"A human readable name for this task.\"\"\"\n\n@dataclass\nclass PromptToImage(Task):\n    @classmethod\n    def name(cls):\n        return \"prompt to image\"\n\n@dataclass\nclass ImageToImage(Task):\n    image: NDArray\n    strength: float\n    fit: bool\n\n    @classmethod\n    def name(cls):\n        return \"image to image\"\n\n@dataclass\nclass Inpaint(ImageToImage):\n    class MaskSource(IntEnum):\n        ALPHA = 0\n        PROMPT = 1\n\n    mask_source: MaskSource\n    mask_prompt: str\n    confidence: float\n\n    @classmethod\n    def name(cls):\n        return \"inpainting\"\n\n@dataclass\nclass DepthToImage(Task):\n    depth: NDArray | None\n    image: NDArray | None\n    strength: float\n\n    @classmethod\n    def name(cls):\n        return \"depth to image\"\n\n@dataclass\nclass Outpaint(Task):\n    image: NDArray\n    origin: Tuple[int, int]\n\n    @classmethod\n    def name(cls):\n        return \"outpainting\"\n\n@dataclass\nclass Upscale(Task):\n    image: NDArray\n    tile_size: int\n    blend: int\n\n    @classmethod\n    def name(cls):\n        return \"upscaling\""
  },
  {
    "path": "builtin_presets/Debug.py",
    "content": "import bpy\nprompt = bpy.context.scene.dream_textures_prompt\n\nprompt.steps = 20\nprompt.cfg_scale = 7.5\nprompt.scheduler = 'DPM Solver Multistep'\nprompt.step_preview_mode = 'Accurate'\nprompt.optimizations_attention_slicing = True\nprompt.optimizations_attention_slice_size_src = 'auto'\nprompt.optimizations_attention_slice_size = 1\nprompt.optimizations_cudnn_benchmark = False\nprompt.optimizations_tf32 = False\nprompt.optimizations_amp = False\nprompt.optimizations_half_precision = True\nprompt.optimizations_sequential_cpu_offload = False\nprompt.optimizations_channels_last_memory_format = False\nprompt.optimizations_batch_size = 1\nprompt.optimizations_vae_slicing = True\nprompt.optimizations_cpu_only = False\n"
  },
  {
    "path": "builtin_presets/Final.py",
    "content": "import bpy\nprompt = bpy.context.scene.dream_textures_prompt\n\nprompt.steps = 50\nprompt.cfg_scale = 7.5\nprompt.scheduler = 'DPM Solver Multistep'\nprompt.step_preview_mode = 'Fast'\nprompt.optimizations_attention_slicing = True\nprompt.optimizations_attention_slice_size_src = 'auto'\nprompt.optimizations_attention_slice_size = 1\nprompt.optimizations_cudnn_benchmark = False\nprompt.optimizations_tf32 = False\nprompt.optimizations_amp = False\nprompt.optimizations_half_precision = True\nprompt.optimizations_sequential_cpu_offload = False\nprompt.optimizations_channels_last_memory_format = False\nprompt.optimizations_batch_size = 1\nprompt.optimizations_vae_slicing = True\nprompt.optimizations_cpu_only = False\n"
  },
  {
    "path": "builtin_presets/Preview.py",
    "content": "import bpy\nprompt = bpy.context.scene.dream_textures_prompt\n\nprompt.steps = 20\nprompt.cfg_scale = 7.5\nprompt.scheduler = 'DPM Solver Multistep'\nprompt.step_preview_mode = 'Fast'\nprompt.optimizations_attention_slicing = True\nprompt.optimizations_attention_slice_size_src = 'auto'\nprompt.optimizations_attention_slice_size = 1\nprompt.optimizations_cudnn_benchmark = False\nprompt.optimizations_tf32 = False\nprompt.optimizations_amp = False\nprompt.optimizations_half_precision = True\nprompt.optimizations_sequential_cpu_offload = False\nprompt.optimizations_channels_last_memory_format = False\nprompt.optimizations_batch_size = 1\nprompt.optimizations_vae_slicing = True\nprompt.optimizations_cpu_only = False\n"
  },
  {
    "path": "classes.py",
    "content": "from .operators.install_dependencies import InstallDependencies, UninstallDependencies\nfrom .operators.open_latest_version import OpenLatestVersion\nfrom .operators.dream_texture import DreamTexture, ReleaseGenerator, CancelGenerator\nfrom .operators.view_history import SCENE_UL_HistoryList, RecallHistoryEntry, ClearHistory, RemoveHistorySelection, ExportHistorySelection, ImportPromptFile\nfrom .operators.inpaint_area_brush import InpaintAreaBrushActivated\nfrom .operators.upscale import Upscale\nfrom .operators.project import ProjectDreamTexture, dream_texture_projection_panels\nfrom .operators.notify_result import NotifyResult\nfrom .property_groups.control_net import ControlNet, ControlNetsAdd, ControlNetsRemove, ControlNetsAddMenu, BakeControlNetImage\nfrom .property_groups.dream_prompt import DreamPrompt\nfrom .property_groups.seamless_result import SeamlessResult\nfrom .ui.panels import dream_texture, history, upscaling, render_properties\nfrom .preferences import OpenURL, StableDiffusionPreferences,\\\n    ImportWeights, Model, ModelSearch, InstallModel, PREFERENCES_UL_ModelList,\\\n    CheckpointGroup, LinkCheckpoint, UnlinkCheckpoint, PREFERENCES_UL_CheckpointList\n\nfrom .ui.presets import DREAM_PT_AdvancedPresets, DREAM_MT_AdvancedPresets, AddAdvancedPreset, RestoreDefaultPresets\n\nfrom . import engine\n\nCLASSES = (\n    *render_properties.render_properties_panels(),\n    \n    DreamTexture,\n    ReleaseGenerator,\n    CancelGenerator,\n    OpenLatestVersion,\n    SCENE_UL_HistoryList,\n    RecallHistoryEntry,\n    ClearHistory,\n    RemoveHistorySelection,\n    ExportHistorySelection,\n    ImportPromptFile,\n    InpaintAreaBrushActivated,\n    Upscale,\n    ProjectDreamTexture,\n    \n    ControlNetsAddMenu,\n    ControlNetsAdd,\n    ControlNetsRemove,\n    BakeControlNetImage,\n\n    DREAM_PT_AdvancedPresets,\n    DREAM_MT_AdvancedPresets,\n    AddAdvancedPreset,\n\n    NotifyResult,\n\n    engine.DreamTexturesRenderEngineProperties,\n    engine.DreamTexturesRenderEngine,\n    engine.NewEngineNodeTree,\n    *engine.engine_panels(),\n    \n    # The order these are registered in matters\n    *dream_texture.dream_texture_panels(),\n    *upscaling.upscaling_panels(),\n    *history.history_panels(),\n    *dream_texture_projection_panels(),\n)\n\nPREFERENCE_CLASSES = (\n    PREFERENCES_UL_ModelList,\n    ModelSearch,\n    InstallModel,\n    Model,\n    ControlNet,\n    DreamPrompt,\n    SeamlessResult,\n    UninstallDependencies,\n    InstallDependencies,\n    OpenURL,\n    ImportWeights,\n    RestoreDefaultPresets,\n    CheckpointGroup,\n    LinkCheckpoint,\n    UnlinkCheckpoint,\n    PREFERENCES_UL_CheckpointList,\n    StableDiffusionPreferences,\n)"
  },
  {
    "path": "community_backends/test.py",
    "content": "bl_info = {\n    \"name\": \"Test Backend\",\n    \"blender\": (3, 1, 0),\n    \"category\": \"Paint\",\n}\n\nimport bpy\nfrom typing import List, Tuple\nfrom dream_textures.api import *\n\nclass TestBackend(Backend):\n    name = \"Test\"\n    description = \"A short description of this backend\"\n\n    custom_optimization: bpy.props.BoolProperty(name=\"My Custom Optimization\")\n\n    def list_models(self, context) -> List[Model]:\n        return []\n\n    def list_schedulers(self, context) -> List[str]:\n        return []\n\n    def generate(self, task: Task, model: Model, prompt: Prompt, size: Tuple[int, int] | None, seed: int, steps: int, guidance_scale: float, scheduler: str, seamless_axes: SeamlessAxes, step_preview_mode: StepPreviewMode, iterations: int, step_callback: StepCallback, callback: Callback):\n        raise NotImplementedError()\n\n    def draw_speed_optimizations(self, layout, context):\n        layout.prop(self, \"custom_optimization\")\n\ndef register():\n    bpy.utils.register_class(TestBackend)\n\ndef unregister():\n    bpy.utils.unregister_class(TestBackend)\n"
  },
  {
    "path": "diffusers_backend.py",
    "content": "import bpy\nfrom bpy.props import FloatProperty, IntProperty, EnumProperty, BoolProperty\nfrom typing import List\n\nfrom .api import Backend, StepCallback, Callback\nfrom .api.models import Model, GenerationArguments, GenerationResult\nfrom .api.models.task import PromptToImage, ImageToImage, Inpaint, DepthToImage, Outpaint, Upscale\nfrom .api.models.fix_it_error import FixItError\n\nfrom .generator_process import Generator\nfrom .generator_process.future import Future\nfrom .generator_process.models import CPUOffload, ModelType, Optimizations, Scheduler\n\nfrom .preferences import checkpoint_lookup, StableDiffusionPreferences, _template_model_download_progress, InstallModel, model_lookup\n\nfrom functools import reduce\n\ndef _convert_models(models):\n    return [\n        None if model is None else (model.id, model.name, model.description)\n        for model in models\n    ]\n\nclass DiffusersBackend(Backend):\n    name = \"HuggingFace Diffusers\"\n    description = \"Local image generation inside of Blender\"\n\n    attention_slicing: BoolProperty(name=\"Attention Slicing\", default=True, description=\"Computes attention in several steps. Saves some memory in exchange for a small speed decrease\")\n    attention_slice_size_src: EnumProperty(\n        name=\"Attention Slice Size\",\n        items=(\n            (\"auto\", \"Automatic\", \"Computes attention in two steps\", 1),\n            (\"manual\", \"Manual\", \"Computes attention in `attention_head_dim // size` steps. A smaller `size` saves more memory.\\n\"\n                                \"`attention_head_dim` must be a multiple of `size`, otherwise the image won't generate properly.\\n\"\n                                \"`attention_head_dim` can be found within the model snapshot's unet/config.json file\", 2)\n        ),\n        default=1\n    )\n    attention_slice_size: IntProperty(name=\"Attention Slice Size\", default=1, min=1)\n    cudnn_benchmark: BoolProperty(name=\"cuDNN Benchmark\", description=\"Allows cuDNN to benchmark multiple convolution algorithms and select the fastest\", default=False)\n    tf32: BoolProperty(name=\"TF32\", description=\"Utilizes tensor cores on Ampere (RTX 30xx) or newer GPUs for matrix multiplications.\\nHas no effect if half precision is enabled\", default=False)\n    half_precision: BoolProperty(name=\"Half Precision\", description=\"Reduces memory usage and increases speed in exchange for a slight loss in image quality.\\nHas no effect if CPU only is enabled or using a GTX 16xx GPU\", default=True)\n    cpu_offload: EnumProperty(\n        name=\"CPU Offload\",\n        items=(\n            (\"off\", \"Off\", \"\", 0),\n            (\"model\", \"Model\", \"Some memory savings with minimal speed penalty\", 1),\n            (\"submodule\", \"Submodule\", \"Better memory savings with large speed penalty\", 2)\n        ),\n        default=0,\n        description=\"Dynamically moves models in and out of device memory for reduced memory usage with reduced speed\"\n    )\n    channels_last_memory_format: BoolProperty(name=\"Channels Last Memory Format\", description=\"An alternative way of ordering NCHW tensors that may be faster or slower depending on the device\", default=False)\n    sdp_attention: BoolProperty(\n        name=\"SDP Attention\",\n        description=\"Scaled dot product attention requires less memory and often comes with a good speed increase.\\n\"\n                    \"Prompt recall may not produce the exact same image, but usually only minor noise differences.\\n\"\n                    \"Overrides attention slicing\",\n        default=True\n    )\n    batch_size: IntProperty(name=\"Batch Size\", default=1, min=1, description=\"Improves speed when using iterations or upscaling in exchange for higher memory usage.\\nHighly recommended to use with VAE slicing enabled\")\n    vae_slicing: BoolProperty(name=\"VAE Slicing\", description=\"Reduces memory usage of batched VAE decoding. Has no effect if batch size is 1.\\nMay have a small performance improvement with large batches\", default=True)\n    vae_tiling: EnumProperty(\n        name=\"VAE Tiling\",\n        items=(\n            (\"off\", \"Off\", \"\", 0),\n            (\"half\", \"Half\", \"Uses tiles of half the selected model's default size. Likely to cause noticeably inaccurate colors\", 1),\n            (\"full\", \"Full\", \"Uses tiles of the selected model's default size, intended for use where image size is manually set higher. May cause slightly inaccurate colors\", 2),\n            (\"manual\", \"Manual\", \"\", 3)\n        ),\n        default=0,\n        description=\"Decodes generated images in tiled regions to reduce memory usage in exchange for longer decode time and less accurate colors.\\nCan allow for generating larger images that would otherwise run out of memory on the final step\"\n    )\n    vae_tile_size: IntProperty(name=\"VAE Tile Size\", min=1, default=512, description=\"Width and height measurement of tiles. Smaller sizes are more likely to cause inaccurate colors and other undesired artifacts\")\n    vae_tile_blend: IntProperty(name=\"VAE Tile Blend\", min=0, default=64, description=\"Minimum amount of how much each edge of a tile will intersect its adjacent tile\")\n    cfg_end: FloatProperty(name=\"CFG End\", min=0, max=1, default=1, description=\"The percentage of steps to complete before disabling classifier-free guidance\")\n    cpu_only: BoolProperty(name=\"CPU Only\", default=False, description=\"Disables GPU acceleration and is extremely slow\")\n\n    use_sdxl_refiner: BoolProperty(name=\"Use SDXL Refiner\", default=False, description=\"Provide a refiner model to run automatically after the initial generation\")\n    sdxl_refiner_model: EnumProperty(name=\"SDXL Refiner Model\", items=lambda self, context: _convert_models(self.list_models(context)), description=\"Specify which model to use as a refiner\")\n\n    def list_models(self, context):\n        def model_case(model, i):\n            return Model(\n                name=model.model_base.replace('models--', '').replace('--', '/'),\n                description=ModelType[model.model_type].name,\n                id=model.model_base.replace('models--', '').replace('--', '/')\n            )\n        models = {}\n        for i, model in enumerate(context.preferences.addons[StableDiffusionPreferences.bl_idname].preferences.installed_models):\n            if model.model_type in {ModelType.CONTROL_NET.name, ModelType.UNKNOWN.name}:\n                continue\n            if model.model_type not in models:\n                models[model.model_type] = [model_case(model, i)]\n            else:\n                models[model.model_type].append(model_case(model, i))\n        return reduce(\n            lambda a, b: a + [None] + sorted(b, key=lambda m: m.id),\n            [\n                models[group]\n                for group in sorted(models.keys())\n            ],\n            []\n        )\n    \n    def list_controlnet_models(self, context):\n        return [\n            Model(\n                name=model.model_base.replace('models--', '').replace('--', '/'),\n                description=\"ControlNet\",\n                id=model.model_base.replace('models--', '').replace('--', '/')\n            )\n            for model in context.preferences.addons[StableDiffusionPreferences.bl_idname].preferences.installed_models\n            if model.model_type == ModelType.CONTROL_NET.name\n        ]\n\n    def list_schedulers(self, context) -> List[str]:\n        return [scheduler.value for scheduler in Scheduler]\n\n    def get_batch_size(self, context) -> int:\n        return self.batch_size\n\n    def optimizations(self) -> Optimizations:\n        optimizations = Optimizations()\n        for prop in dir(self):\n            if hasattr(optimizations, prop) and not prop.startswith('__'):\n                setattr(optimizations, prop, getattr(self, prop))\n        if self.attention_slice_size_src == 'auto':\n            optimizations.attention_slice_size = 'auto'\n        optimizations.cpu_offload = CPUOffload(optimizations.cpu_offload)\n        return optimizations\n\n    def generate(self, arguments: GenerationArguments, step_callback: StepCallback, callback: Callback):\n        gen = Generator.shared()\n        common_kwargs = {\n            'model': checkpoint_lookup.get(arguments.model.id),\n            'scheduler': Scheduler(arguments.scheduler),\n            'optimizations': self.optimizations(),\n            'prompt': arguments.prompt.positive,\n            'steps': arguments.steps,\n            'width': arguments.size[0] if arguments.size is not None else None,\n            'height': arguments.size[1] if arguments.size is not None else None,\n            'seed': arguments.seed,\n            'cfg_scale': arguments.guidance_scale,\n            'use_negative_prompt': arguments.prompt.negative is not None,\n            'negative_prompt': arguments.prompt.negative or \"\",\n            'seamless_axes': arguments.seamless_axes,\n            'iterations': arguments.iterations,\n            'step_preview_mode': arguments.step_preview_mode,\n            \n            'sdxl_refiner_model': (checkpoint_lookup.get(self.sdxl_refiner_model) if self.use_sdxl_refiner else None),\n        }\n        future: Future\n        match arguments.task:\n            case PromptToImage():\n                if len(arguments.control_nets) > 0:\n                    future = gen.control_net(\n                        **common_kwargs,\n                        control_net=[checkpoint_lookup.get(c.model) for c in arguments.control_nets],\n                        control=[c.image for c in arguments.control_nets],\n                        controlnet_conditioning_scale=[c.strength for c in arguments.control_nets],\n                        image=None,\n                        inpaint=False,\n                        inpaint_mask_src='alpha',\n                        text_mask='',\n                        text_mask_confidence=1,\n                        strength=1\n                    )\n                else:\n                    future = gen.prompt_to_image(**common_kwargs)\n            case Inpaint(image=image, fit=fit, strength=strength, mask_source=mask_source, mask_prompt=mask_prompt, confidence=confidence):\n                if len(arguments.control_nets) > 0:\n                    future = gen.control_net(\n                        **common_kwargs,\n                        control_net=[c.model for c in arguments.control_nets],\n                        control=[c.image for c in arguments.control_nets],\n                        controlnet_conditioning_scale=[c.strength for c in arguments.control_nets],\n                        image=image,\n                        inpaint=True,\n                        inpaint_mask_src='alpha' if mask_source == Inpaint.MaskSource.ALPHA else 'prompt',\n                        text_mask=mask_prompt,\n                        text_mask_confidence=confidence,\n                        strength=strength\n                    )\n                else:\n                    future = gen.inpaint(\n                        image=image,\n                        fit=fit,\n                        strength=strength,\n                        inpaint_mask_src='alpha' if mask_source == Inpaint.MaskSource.ALPHA else 'prompt',\n                        text_mask=mask_prompt,\n                        text_mask_confidence=confidence,\n                        **common_kwargs\n                    )\n            case ImageToImage(image=image, strength=strength, fit=fit):\n                if len(arguments.control_nets) > 0:\n                    future = gen.control_net(\n                        **common_kwargs,\n                        control_net=[c.model for c in arguments.control_nets],\n                        control=[c.image for c in arguments.control_nets],\n                        controlnet_conditioning_scale=[c.strength for c in arguments.control_nets],\n                        image=image,\n                        inpaint=False,\n                        inpaint_mask_src='alpha',\n                        text_mask='',\n                        text_mask_confidence=1,\n                        strength=strength\n                    )\n                else:\n                    future = gen.image_to_image(image=image, fit=fit, strength=strength, **common_kwargs)\n            case DepthToImage(depth=depth, image=image, strength=strength):\n                future = gen.depth_to_image(\n                    depth=depth,\n                    image=image,\n                    strength=strength,\n                    **common_kwargs\n                )\n            case Outpaint(image=image, origin=origin):\n                future = gen.outpaint(\n                    image=image,\n                    outpaint_origin=origin,\n                    fit=False,\n                    strength=1,\n                    inpaint_mask_src='alpha',\n                    text_mask='',\n                    text_mask_confidence=1,\n                    **common_kwargs\n                )\n            case Upscale(image=image, tile_size=tile_size, blend=blend):\n                future = gen.upscale(\n                    image=image,\n                    tile_size=tile_size,\n                    blend=blend,\n                    **common_kwargs\n                )\n            case _:\n                raise NotImplementedError()\n        def on_step(future: Future, step_image: [GenerationResult]):\n            should_continue = step_callback(step_image)\n            if not should_continue:\n                future.cancel()\n                callback(InterruptedError())\n        def on_done(future: Future):\n            callback(future.result(last_only=True))\n        def on_exception(_, exception):\n            callback(exception)\n        future.add_response_callback(on_step)\n        future.add_exception_callback(on_exception)\n        future.add_done_callback(on_done)\n\n    def validate(self, arguments: GenerationArguments):\n        model = None if arguments.model is None else model_lookup.get(arguments.model.id)\n        if model is None:\n            raise FixItError(\"No model selected.\", FixItError.ChangeProperty(\"model\"))\n        else:\n            if not model.model_type.matches_task(arguments.task):\n                class DownloadModel(FixItError.Solution):\n                    def _draw(self, dream_prompt, context, layout):\n                        if not _template_model_download_progress(context, layout):\n                            target_model_type = ModelType.from_task(arguments.task)\n                            if target_model_type is not None:\n                                install_model = layout.operator(InstallModel.bl_idname, text=f\"Download {target_model_type.recommended_model()} (Recommended)\", icon=\"IMPORT\")\n                                install_model.model = target_model_type.recommended_model()\n                                install_model.prefer_fp16_revision = context.preferences.addons[StableDiffusionPreferences.bl_idname].preferences.prefer_fp16_revision\n                model_task_description = f\"\"\"Incorrect model type selected for {type(arguments.task).name().replace('_', ' ').lower()} tasks.\nThe selected model is for {model.model_type.name.replace('_', ' ').lower()}.\"\"\"\n                if not any(m.model_type.matches_task(arguments.task) for m in model_lookup._models.values()):\n                    raise FixItError(\n                        message=model_task_description + \"\\nYou do not have any compatible models downloaded:\",\n                        solution=DownloadModel()\n                    )\n                else:\n                    raise FixItError(\n                        message=model_task_description + \"\\nSelect a different model below.\",\n                        solution=FixItError.ChangeProperty(\"model\")\n                    )\n\n    def draw_advanced(self, layout, context):\n        layout.prop(self, \"use_sdxl_refiner\")\n        col = layout.column()\n        col.enabled = self.use_sdxl_refiner\n        col.prop(self, \"sdxl_refiner_model\")\n\n    def draw_speed_optimizations(self, layout, context):\n        inferred_device = Optimizations.infer_device()\n        if self.cpu_only:\n            inferred_device = \"cpu\"\n        def optimization(prop):\n            if Optimizations.device_supports(prop, inferred_device):\n                layout.prop(self, prop)\n\n        optimization(\"cudnn_benchmark\")\n        optimization(\"tf32\")\n        optimization(\"half_precision\")\n        optimization(\"channels_last_memory_format\")\n        optimization(\"batch_size\")\n    \n    def draw_memory_optimizations(self, layout, context):\n        inferred_device = Optimizations.infer_device()\n        if self.cpu_only:\n            inferred_device = \"cpu\"\n        def optimization(prop):\n            if Optimizations.device_supports(prop, inferred_device):\n                layout.prop(self, prop)\n\n        optimization(\"sdp_attention\")\n        optimization(\"attention_slicing\")\n        slice_size_row = layout.row()\n        slice_size_row.prop(self, \"attention_slice_size_src\")\n        if self.attention_slice_size_src == 'manual':\n            slice_size_row.prop(self, \"attention_slice_size\", text=\"Size\")\n        optimization(\"cpu_offload\")\n        optimization(\"cpu_only\")\n        optimization(\"vae_slicing\")\n        optimization(\"vae_tiling\")\n        if self.vae_tiling == \"manual\":\n            optimization(\"vae_tile_size\")\n            optimization(\"vae_tile_blend\")"
  },
  {
    "path": "docs/AI_UPSCALING.md",
    "content": "# AI Upscaling\nUse the Stable Diffusion upscaler to increase images 4x in size while retaining detail. You can guide the upscaler with a text prompt.\n\n> Upscaling uses the model `stabilityai/stable-diffusion-4x-upscaler`. This model will automatically be downloaded when the operator is first run.\n\nUse the AI Upscaling panel to access this tool.\n\n1. Open the image to upscale in an *Image Editor* space\n2. Expand the *AI Upscaling* panel, located in the *Dream* sidebar tab\n3. Type a prompt to subtly influence the generation.\n4. Optionally configure the tile size, blend, and other advanced options.\n\n![](assets/ai_upscaling/panel.png)\n\nThe upscaled image will be opened in the *Image Editor*. The image will be named `Source Image Name (Upscaled)`.\n\n## Tile Size\nDue to the large VRAM consumption of the `stabilityai/stable-diffusion-4x-upscaler` model, the input image is split into tiles with each tile being upscaled independently, then stitched back together.\n\nThe default tile size is 128x128, which will result in an image of size 512x512. These 512x512 images are stitched back together to form the final image.\n\nYou can increase or decrease the tile size depending on your GPU's capabilities.\n\nThe *Blend* parameter controls how much overlap is included in the tiles to help reduce visible seams."
  },
  {
    "path": "docs/DEVELOPMENT_ENVIRONMENT.md",
    "content": "# Setting Up a Development Environment\n\nWith the following steps, you can start contributing to Dream Textures.\n\nThese steps can also be used to setup the add-on on Linux.\n\n## Cloning\n\nA basic knowledge of Git will be necessary to contribute. To start, clone the repository:\n\n```sh\ngit clone https://github.com/carson-katri/dream-textures.git dream_textures\n```\n\n> If you use SSH, clone with `git clone git@github.com:carson-katri/dream-textures.git dream_textures`\n\nThis will clone the repository into the `dream_textures` folder.\n\n## Installing to Blender\n\nYou can install the add-on to Blender in multiple ways. The easiest way is to copy the folder into the add-ons directory.\n\nThis directory is in different places on different systems.\n\n* Windows\n    * `%USERPROFILE%\\AppData\\Roaming\\Blender Foundation\\Blender\\3.4\\scripts\\addons`\n* macOS\n    * `/Users/$USER/Library/Application Support/Blender/3.4/scripts/addons`\n* Linux\n    * `$HOME/.config/blender/3.4/scripts/addons`\n\n> This path may be different depending on how you installed Blender. See [Blender's documentation](https://docs.blender.org/manual/en/latest/advanced/blender_directory_layout.html) for more information on the directory layout.\n\nIf you can't find the add-on folder, you can look at another third-party add-on you already have in Blender preferences and see where it is located.\n\n![A screenshot highlighting the add-on directory in Blender preferences](assets/development_environment/locating_addons.png)\n\n### Using Visual Studio Code\n\n> This is not necessary if you won't be making any changes to Dream Textures or prefer a different IDE.\n\nYou can also install and debug the add-on with the [Blender Development]() extension for Visual Studio Code.\n\nOpen the `dream_textures` folder in VS Code, open the command palette (Windows: <kbd>Shift</kbd> + <kbd>Ctrl</kbd> + <kbd>P</kbd>, macOS: <kbd>Shift</kbd> + <kbd>Command</kbd> + <kbd>P</kbd>), and search for the command `Blender: Start`.\n\n![](assets/development_environment/command_palette.png)\n\nThen choose which Blender installation to use.\n\n![](assets/development_environment/choose_installation.png)\n\nBlender will now start up with the add-on installed. You can verify this by going to Blender's preferences and searching for *Dream Textures*.\n\n## Installing Dependencies\n\nWhen installing from source, the dependencies are not included. You can install them from Blender's preferences.\n\nFirst, enable *Developer Extras* so Dream Textures' developer tools will be displayed.\n\n![](assets/development_environment/developer_extras.png)\n\nThen, use the *Developer Tools* section to install the dependencies.\n\n![](assets/development_environment/install_dependencies.png)\n\n### Installing Dependencies Manually\n\nIn some cases, the *Install Dependencies* tool may not work. In this case, you can install the dependencies from the command line.\n\nThe best way to install dependencies is using the Python that ships with Blender. The command will differ depending on your operating system and Blender installation.\n\nOn some platforms, Blender does not come with `pip` pre-installed. You can use `ensurepip` to install it if necessary.\n\n```sh\n# Windows\n\"C:\\Program Files\\Blender Foundation\\Blender 3.4\\3.4\\python\\bin\\python.exe\" -m ensurepip\n\n# macOS\n/Applications/Blender.app/Contents/Resources/3.4/python/bin/python3.10 -m ensurepip\n\n# Linux (via snap)\n/snap/blender/3132/3.4/python/bin/python3.10 -m ensurepip\n```\n\nOnce you have `pip`, the dependencies can be installed.\n\nAll of the packages *must* be installed to `dream_textures/.python_dependencies`. The following commands assume they are being run from inside the `dream_textures` folder.\n\n```sh\n# Windows\n\"C:\\Program Files\\Blender Foundation\\Blender 3.4\\3.4\\python\\bin\\python.exe\" -m pip install -r requirements/win-linux-cuda.txt --target .python_dependencies\n\n# macOS\n/Applications/Blender.app/Contents/Resources/3.4/python/bin/python3.10 -m pip install -r requirements/mac-mps-cpu.txt --target .python_dependencies\n\n# Linux (via snap)\n/snap/blender/3132/3.4/python/bin/python3.10 -m pip install -r requirements/win-linux-cuda.txt --target .python_dependencies\n```\n\n## Using the Add-on\n\nOnce you have the dependencies installed, the add-on will become fully usable. Continue setting up as described in the [setup guide](./SETUP.md).\n\n## Common Issues\n\n### macOS\n\n1. On Apple Silicon, with the `requirements-dream-studio.txt` you may run into an error with gRPC using an incompatible binary. If so, please use the following command to install the correct gRPC version:\n```sh\npip install --no-binary :all: grpcio --ignore-installed --target .python_dependencies --upgrade\n```"
  },
  {
    "path": "docs/HISTORY.md",
    "content": "# History\nEach time you generate, the full configuration is saved to the *History* panel. You can recall any previous generation to re-run it by clicking *Recall Prompt*.\n\n## Prompt Import/Export\nYou can also export the selected prompt to JSON for later import. This is a more permanent way to backup prompts, and can be useful for sharing an exact image.\n\n### Export\n1. Select a history entry row\n2. Click the export icon button\n3. Save the JSON file to your computer\n\n![A screenshot of the History panel with the Export icon button highlighted](assets/history/history-export.png)\n\n### Import\n1. Select the import icon button in the header of the *Dream Texture* panel\n2. Open a valid prompt JSON file\n3. Every configuration option will be loaded in\n\n![A screenshot of the Dream Texture panel with the Import icon button highlighted](assets/history/history-import.png)"
  },
  {
    "path": "docs/IMAGE_GENERATION.md",
    "content": "# Image Generation\n1. To open Dream Textures, go to an Image Editor or Shader Editor\n1. Ensure the sidebar is visible by pressing *N* or checking *View* > *Sidebar*\n2. Select the *Dream* panel to open the interface\n\n![A screenshot showing the 'Dream' panel in an Image Editor space](assets/image_generation/opening-ui.png)\n\nEnter a prompt then click *Generate*. It can take anywhere from a few seconds to a few minutes to generate, depending on your GPU.\n\n## Options\n\n### Pipeline\nTwo options are currently available:\n* Stable Diffusion - for local generation\n* DreamStudio - for cloud processing\n\nOnly the options available for the version you installed and the keys provided in the add-on preferences will be available.\n\n### Model\nChoose from any installed model. Some options require specific kinds of model.\n\nFor example, []\n\n### Prompt\n\nA few presets are available to help you create great prompts. They work by asking you to fill in a few simple fields, then generate a full prompt string that is passed to Stable Diffusion.\n\nThe default preset is *Texture*. It asks for a subject, and adds the word `texture` to the end. So if you enter `brick wall`, it will use the prompt `brick wall texture`.\n\n### Seamless\nChecking seamless will use a circular convolution to create a perfectly seamless image, which works great for textures.\n\nYou can also specify which axes should be seamless.\n\n### Negative\nEnabling negative prompts gives you finer control over your image. For example, if you asked for a `cloud city`, but you wanted to remove the buildings it added, you could enter the negative prompt `building`. This would tell Stable Diffusion to avoid drawing buildings. You can add as much content you want to the negative prompt, and it will avoid everything entered.\n\n### Size\nThe target image dimensions. The width/height should be a multiple of 64, or it will round to the closest one for you.\n\nMost graphics cards with 4+GB of VRAM should be able to generate 512x512 images. However, if you are getting CUDA memory errors, try decreasing the size.\n\n> Stable Diffusion was trained on 512x512 images, so you will get the best results at this size (or at least when leaving one dimensions at 512).\n\n### Source Image\nChoose an image from a specific *File* or use the *Open Image*.\n\nThree actions are available that work on a source image.\n\n#### Modify\nMixes the image with the noise with the ratio specified by the *Noise Strength*. This will make Stable Diffusion match the style, composition, etc. from it.\n\nStength specifies how much latent noise to mix with the image. A higher strength means more latent noise, and more deviation from the init image. If you want it to stick to the image more, decrease the strength.\n\n> Depending on the strength value, some steps will be skipped. For example, if you specified `10` steps and set strength to `0.5`, only `5` steps would be used.\n\nFit to width/height will ensure the image is contained within the configured size.\n\nThe *Image Type* option has a few options:\n1. Color - Mixes the image with noise\n\n> The following options require a depth model to be selected, such as `stabilityai/stable-diffusion-2-depth`. Follow the instructions to [download a model](setup.md#download-a-model).\n\n2. Color and Generated Depth - Uses MiDaS to infer the depth of the initial image and includes it in the conditioning. Can give results that more closely match the composition of the source image.\n3. Color and Depth Map - Specify a secondary image to use as the depth map, instead of generating one with MiDaS.\n4. Depth - Treats the intial image as a depth map, and ignores any color. The generated image will match the composition but not colors of the original.\n\n### Advanced\nYou can have more control over the generation by trying different values for these parameters:\n\n* Random Seed - When enabled, a seed will be selected for you\n    * Seed - The value used to seed RNG, if text is input instead of a number its hash will be used\n* Steps - Number of sampler steps, higher steps will give the sampler more time to converge and clear up artifacts\n* CFG Scale - How strongly the prompt influences the output\n* Scheduler - Some schedulers take fewer steps to produce a good result than others. Try each one and see what you prefer.\n* Step Preview - Whether to show each step in the image editor. Defaults to 'Fast', which samples the latents without using the VAE. 'Accurrate' will run the latents through the VAE at each step and slow down generation significantly.\n* Speed Optimizations - Various optimizations to increase generation speed, some at the cost of VRAM. Recommended default is *Half Precision*.\n* Memory Optimizations - Various optimizations to reduce VRAM consumption, some at the cost of speed. Recommended default is *Attention Slicing* with *Automatic* slice size.\n\n### Iterations\nHow many images to generate. This is only particularly useful when *Random Seed* is enabled."
  },
  {
    "path": "docs/INPAINT_OUTPAINT.md",
    "content": "# Inpaint/Outpaint\n\nThis guide shows how to use both [inpainting](#inpainting) and [outpainting](#outpainting).\n\n> For both inpainting and outpainting you *must* use a model fine-tuned for inpainting, such as `stabilityai/stable-diffusion-2-inpainting`. Follow the instructions to [download a model](setup.md#download-a-model).\n\n# Inpainting\nInpainting refers to filling in or replacing parts of an image. It can also be used to [make existing textures seamless](#making-textures-seamless).\n\nThe quickest way to inpaint is with the *Mark Inpaint Area* brush.\n\n1. Use the *Mark Inpaint Area* brush to remove the edges of the image\n2. Enter a prompt for what should fill the erased area\n3. Enable *Source Image*, select the *Open Image* source and the *Inpaint* action\n4. Choose the *Alpha Channel* mask source\n5. Click *Generate*\n\n![](assets/inpaint_outpaint/inpaint.png)\n\n## Making Textures Seamless\nInpainting can also be used to make an existing texture seamless.\n\n1. Use the *Mark Inpaint Area* brush to remove the edges of the image\n2. Enter a prompt that describes the texture, and check *Seamless*\n3. Enable *Source Image*, select the *Open Image* source and the *Inpaint* action\n4. Click *Generate*\n\n![](assets/inpaint_outpaint/seamless_inpaint.png)\n\n# Outpainting\nOutpainting refers to extending an image beyond its original size. Use an inpainting model such as `stabilityai/stable-diffusion-2-inpainting` for outpainting as well.\n\n1. Select an image to outpaint and open it in an Image Editor\n2. Choose a size, this is how large the outpaint will be\n3. Enable *Source Image*, select the *Open Image* source and the *Outpaint* action\n4. Set the origin of the outpaint. See [Choosing an Origin](#choosing-an-origin) for more info.\n\n### Choosing an Origin\nThe top left corner of the image is (0, 0), with the bottom right corner being the (width, height).\n\nYou should always include overlap or the outpaint will be completely unrelated to the original. The add-on will warn you if you do not include any.\n\nTake the image below for example. We want to outpaint the bottom right side. Let's figure out the correct origin.\n\nHere's what we know:\n1. We know our image is 512x960. You can find this in the sidebar on the *Image* tab.\n2. We set the size of the outpaint to 512x512 in the *Dream* tab\n\nWith this information we can calculate:\n1. The X origin will be the width of the image minus some overlap. The width is 512px, and we want 64px of overlap. So the X origin will be set to `512 - 64` or `448`.\n2. The Y origin will be the height of the image minus the height of the outpaint size. The height of the image is 960px, and the height of the outpaint is 512px. So the Y origin will be set to `960 - 512` or `448`.\n\n> Tip: You can enter math expressions into any Blender input field.\n\n![](assets/inpaint_outpaint/outpaint_origin.png)\n\nAfter selecting this origin, we can outpaint the bottom right side.\n\n![](assets/inpaint_outpaint/outpaint.gif)\n\nHere are other values we could have used for other parts of the image:\n\n* Bottom Left: `(-512 + 64, 512 - 64)` or `(-448, 448)`\n* Top Right: `(512 - 64, 0)` or `(448, 0)`\n* Top Left: `(-512 + 64, 0)` or `(-448, 0)`\n* Top: `(0, 0)`\n* Bottom: `(0, 512 - 64)` or `(0, 448)`"
  },
  {
    "path": "docs/RENDER_PASS.md",
    "content": "# Render Pass\nA custom 'Dream Textures' render pass is available for Cycles. This allows you to run Dream Textures on the render result each time the scene is rendered. It works with animations as well, and can be used to perform style transfer on each frame of an animation.\n\n> The render pass and Cycles will both use significant amounts of VRAM depending on the scene. You can use the CPU to render on Cycles to save resources for Dream Textures.\n\n1. In the *Render Properties* panel, switch to the *Cycles* render engine\n\n> In the *Output Properties* panel, ensure the image size is reasonable for your GPU and Stable Diffusion. 512x512 is a good place to start.\n\n![A screenshot of the Render Properties panel with the Cycles render engine selected, and the Dream Textures render pass checked](assets/render_pass/cycles.png)\n\n2. Enable the *Dream Textures* render pass, and enter a text prompt. You can also specify which pass inputs to use. Using depth information can help the rendered result match the scene geometry.\n\n> When using depth, you *must* select a depth model, such as `stabilityai/stable-diffusion-2-depth`. Follow the instructions to [download a model](setup.md#download-a-model).\n\n![](assets/render_pass/pass_inputs.png)\n\n3. To use the Dream Textures generated image as the final result, open the *Compositor* space\n4. Enable *Use Nodes*\n5. Connect the *Dream Textures* socket from the *Render Layers* node to the *Image* socket of the *Composite* node\n\n![A screenshot of the Compositor space with Use Nodes checked and the Dream Textures socket from the Render Layers node connected to the Image socket of the Composite node](assets/render_pass/render-pass-compositor.png)\n\nNow whenever you render your scene, it will automatically run through Stable Diffusion and output the result.\nHere's an example using only the *Depth* pass as input.\n\n![](assets/render_pass/spinning_cube_animation.gif)\n\n## Controlling the Output\n\n### Depth\nUsing a depth model and choosing *Pass Inputs* such as *Depth* or *Color and Depth* can go a long way in making the result closely match the geometry of your scene.\n\n### Noise Strength\nThe noise strength parameter is very important when using this render pass. It is the same as *Noise Strength* described in [Image Generation](IMAGE_GENERATION.md#modify). If you want your scene composition, colors, etc. to be preserved, use a lower strength value. If you want Stable Diffusion to take more control, use a higher strength value.\n\nThis does not apply when using the *Depth* pass input and no color.\n\n### Seed\nEnabling *Random Seed* can give you some cool effects, and allow for more experimentation. However, if you are trying to do simple style transfer on an animation, using a consistent seed can help the animation be more coherent.\n\n## Animation\nYou can animate most of the properties when using the render pass. Simply create keyframes as you typically would in Blender, and the properties will automatically be updated for each frame."
  },
  {
    "path": "docs/SETUP.md",
    "content": "# Setting Up\nGetting up and running is easy. Make sure you have several GBs of storage free, as the model weights and add-on consume a lot of storage space.\n\nIn general, all of the instructions you need to setup will be given within Blender. However, if you want to see screenshots and more explanation this can be helpful.\n\nIf you have any problems, you can get help in the [Dream Textures Discord server](https://discord.gg/EmDJ8CaWZ7).\n\n## Installation\n\nSee the [release notes](https://github.com/carson-katri/dream-textures/releases/latest) for the most recent version of Dream Textures. There you will find a section titled \"Choose Your Installation\". Use the dropdowns to find the right version for your system.\n\n> The add-on archive for Windows is compressed with 7-Zip to fit within GitHub's file size limits. Follow the instructions on the release notes page to extract the contained `.zip` file.\n\nAfter you have the add-on installed in Blender, check the box in Blender preferences to enable it. Then follow the steps below to complete setup.\n\nIf you downloaded a DreamStudio-only version, you can skip to the [DreamStudio section](#dreamstudio).\n\n> **DO NOT** try to install dependencies. Tools for doing so are intended for development. *Always* [download a prebuilt version](https://github.com/carson-katri/dream-textures/releases/latest).\n\n## Download a Model\nThere are [hundreds of models](https://huggingface.co/sd-dreambooth-library) to choose from. A good model to start with is `stabilityai/stable-diffusion-2-1-base`. This is the latest 512x512 Stable Diffusion model.\n\nIn the add-on preferences, search for this model. Then click the download button on the right.\n\n> Depending on your Internet speeds, it may take a few minutes to download.\n\n![](assets/setup/stable_diffusion_2_1_base.png)\n\n### Other Useful Models\nThere are a few other models you may want to download as well:\n\n* `stabilityai/stable-diffusion-2-inpainting` - Fine-tuned for inpainting, and required to use the [inpaint and outpaint](INPAINT_OUTPAINT.md) features\n* `stabilityai/stable-diffusion-2-depth` - Uses depth information to guide generation, required for [texture projection](TEXTURE_PROJECTION.md), the [render pass](RENDER_PASS.md) when using depth pass input, and image to image when using depth\n* `stabilityai/stable-diffusion-x4-upscaler` - Upscales the input image 4x, used only for [upscaling](AI_UPSCALING.md)\n* `stabilityai/stable-diffusion-2-1` - Fine-tuned for 768x768 images\n\n### Private Models\nSome models are gated or private to your account/organization. To download these models, generate a Hugging Face Hub token and paste it in the \"Token\" field. You can generate a token in your [account settings on Hugging Face](https://huggingface.co/settings/tokens).\n\n![](assets/setup/hfh_token.png)\n\n### Importing Checkpoints\nDream Textures can also import `.ckpt` files, which you may be familiar with if you've used [AUTOMATIC1111/stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui).\n\nClick *Import Checkpoint File* in add-on preferences, then select your checkpoint file. You should also specify what kind of model it is in the sidebar of the file picker.\n\n![](assets/setup/checkpoint_import.png)\n\nIf you don't see the sidebar, press *N* on your keyboard or click the gear icon in the top right.\n\nHere's what the various *Model Config* options are for:\n\n* `v1` - Weights from the original [CompVis/stable-diffusion](https://github.com/CompVis/stable-diffusion) code, such as `stable-diffusion-v1-x`\n* `v2 (512, epsilon)` - Weights from the updated [Stability-AI/stablediffusion](https://github.com/Stability-AI/stablediffusion) code, specifically models with `prediction_type=\"epsilon\"` such as the 512x512 models\n* `v2 (768, v_prediction)` - Weights from the updated [Stability-AI/stablediffusion](https://github.com/Stability-AI/stablediffusion) code, specifically models with `prediction_type=\"v_prediction\"` such as the 768x768 models\n\nAfter choosing the file, the model will be converted to the Diffusers format automatically and be available in your model list.\n\n## DreamStudio\nTo connect your DreamStudio account, enter your API key in the add-on preferences.\n\n![](assets/setup/dream_studio_key.png)\n\nYou can find your key in the [account settings page of DreamStudio](https://beta.dreamstudio.ai/membership?tab=apiKeys).\n\n![](assets/setup/dreamstudio.png)"
  },
  {
    "path": "docs/TEXTURE_PROJECTION.md",
    "content": "# Texture Projection\n\nUsing depth to image, Dream Textures is able to texture entire scenes automatically with a simple prompt.\n\nIt's sort of like [Ian Hubert's method](https://www.youtube.com/watch?v=v_ikG-u_6r0) in reverse. Instead of starting with an image and building geometry around that, we start with the geometry and generate an image that projects perfectly onto it.\n\n> Make sure you download a depth model such as `stabilityai/stable-diffusion-2-depth`. Follow the instructions to [download a model](SETUP.md#download-a-model).\n\nFollow the steps below to project a texture onto your mesh.\n\n## Selecting a Target\nIn the 3D Viewport, select the objects you want to project onto. Then in edit mode, select all of the faces to target. Only the selected faces will be given the new texture.\n\nEvery object in the viewport will be factored into the depth map. To only use the selected objects in the depth map, enter local view by pressing */* or selecting *View* > *Local View* > *Toggle Local View* in the viewport menu bar.\n\n> Tip: Large unbroken faces do not always project well. Try subdividing your mesh if the projection is warping.\n\n![](assets/texture_projection/edit_mode.png)\n\n## Prompting\n\nIn the sidebar, select the \"Dream\" panel. Choose a depth model from the dropdown, and enter a prompt. All of the options available for image generation are available here as well.\n\nAt the bottom you can choose what data to use for the projection. The default is \"Depth\". You can switch to \"Depth and Color\" to factor in the current color from the viewport.\n\n> The color data is retrieved from the current viewport. Switch to *Viewport Shading* or *Rendered* view to use the colors specified for EEVEE or Cycles.\n\nYou can also adjust the size of the image from the default 512x512. Note that the depth data is in the same aspect ratio as the 3D Viewport window. If you have the Blender window landscape, you may want to adjust the size to be 768x512 or something similar. You could also shrink your window to make it square.\n\n## Project Dream Texture\nAfter configuring everything, click the *Project Dream Texture* button. This will begin the depth to image process.\nYou are free to move the viewport around as it generates. Switch to *Viewport Shading* mode to see each step as it generates live.\n\nA new material will be added named with the seed of the generated image. The UVs for each selected face are also updated to be projected from the angle the material was generated at.\n\n> Tip: In the sidebar under *View* you can adjust the *Focal Length* of the viewport.\n\n![](assets/texture_projection/projection.gif)\n"
  },
  {
    "path": "docs/assets/banner_image_prompt.json",
    "content": "{\n    \"prompt_structure\": \"concept_art\",\n    \"use_negative_prompt\": true,\n    \"negative_prompt\": \"person dragon bird ocean buildings woman\",\n    \"width\": 1024,\n    \"height\": 256,\n    \"seamless\": false,\n    \"show_advanced\": false,\n    \"random_seed\": false,\n    \"seed\": \"4141004939\",\n    \"precision\": \"auto\",\n    \"iterations\": 1,\n    \"steps\": 25,\n    \"cfg_scale\": 7.5,\n    \"sampler_name\": \"k_lms\",\n    \"show_steps\": false,\n    \"use_init_img\": false,\n    \"use_inpainting\": false,\n    \"strength\": 0.75,\n    \"fit\": true,\n    \"prompt_structure_token_subject\": \"clouds ethereal mid-air by greg rutkowski and artgerm\",\n    \"prompt_structure_token_subject_enum\": \"custom\",\n    \"prompt_structure_token_framing\": \"\",\n    \"prompt_structure_token_framing_enum\": \"ecu\",\n    \"prompt_structure_token_position\": \"\",\n    \"prompt_structure_token_position_enum\": \"overhead\",\n    \"prompt_structure_token_film_type\": \"\",\n    \"prompt_structure_token_film_type_enum\": \"bw\",\n    \"prompt_structure_token_camera_settings\": \"\",\n    \"prompt_structure_token_camera_settings_enum\": \"high_speed\",\n    \"prompt_structure_token_shooting_context\": \"\",\n    \"prompt_structure_token_shooting_context_enum\": \"film_still\",\n    \"prompt_structure_token_lighting\": \"\",\n    \"prompt_structure_token_lighting_enum\": \"golden_hour\",\n    \"prompt_structure_token_subject_type\": \"\",\n    \"prompt_structure_token_subject_type_enum\": \"environment\",\n    \"prompt_structure_token_genre\": \"\",\n    \"prompt_structure_token_genre_enum\": \"fantasy\",\n    \"prompt\": \"clouds ethereal mid-air by greg rutkowski and artgerm, Environment concept art, Fantasy digital painting, trending on ArtStation [person dragon bird ocean buildings woman]\"\n}"
  },
  {
    "path": "engine/__init__.py",
    "content": "from .engine import *\nfrom .node_tree import *\nfrom .node_executor import *\nfrom .node import *\nfrom .nodes.input_nodes import *\nfrom .nodes.pipeline_nodes import *\nfrom .nodes.utility_nodes import *\nfrom .nodes.annotation_nodes import *\nfrom .annotations import openpose\nfrom .annotations import ade20k\n\nimport bpy\nimport nodeitems_utils\n\nclass DreamTexturesNodeCategory(nodeitems_utils.NodeCategory):\n    @classmethod\n    def poll(cls, context):\n        return context.space_data.tree_type == DreamTexturesNodeTree.__name__\n\npipeline_items = [\n    nodeitems_utils.NodeItem(NodeStableDiffusion.bl_idname),\n    nodeitems_utils.NodeItem(NodeControlNet.bl_idname),\n]\n\ninput_items = [\n    nodeitems_utils.NodeItem(NodeInteger.bl_idname),\n    nodeitems_utils.NodeItem(NodeString.bl_idname),\n    nodeitems_utils.NodeItem(NodeImage.bl_idname),\n    nodeitems_utils.NodeItem(NodeCollection.bl_idname),\n    nodeitems_utils.NodeItem(NodeRenderProperties.bl_idname),\n]\nif bpy.app.version >= (3, 5, 0):\n    input_items.append(nodeitems_utils.NodeItem(NodeImageFile.bl_idname))\n\nutility_items = [\n    nodeitems_utils.NodeItem(NodeMath.bl_idname),\n    nodeitems_utils.NodeItem(NodeRandomValue.bl_idname),\n    nodeitems_utils.NodeItem(NodeRandomSeed.bl_idname),\n    nodeitems_utils.NodeItem(NodeSeed.bl_idname),\n    nodeitems_utils.NodeItem(NodeClamp.bl_idname),\n    nodeitems_utils.NodeItem(NodeFramePath.bl_idname),\n    nodeitems_utils.NodeItem(NodeCropImage.bl_idname),\n    nodeitems_utils.NodeItem(NodeJoinImages.bl_idname),\n    nodeitems_utils.NodeItem(NodeColorCorrect.bl_idname),\n    nodeitems_utils.NodeItem(NodeSeparateColor.bl_idname),\n    nodeitems_utils.NodeItem(NodeCombineColor.bl_idname),\n    nodeitems_utils.NodeItem(NodeSwitch.bl_idname),\n    nodeitems_utils.NodeItem(NodeCompare.bl_idname),\n    nodeitems_utils.NodeItem(NodeReplaceString.bl_idname),\n]\nif bpy.app.version >= (3, 5, 0):\n    utility_items.append(nodeitems_utils.NodeItem(NodeResizeImage.bl_idname))\n\nannotations_items = [\n    nodeitems_utils.NodeItem(NodeAnnotationDepth.bl_idname),\n    nodeitems_utils.NodeItem(NodeAnnotationOpenPose.bl_idname),\n    nodeitems_utils.NodeItem(NodeAnnotationADE20K.bl_idname),\n    nodeitems_utils.NodeItem(NodeAnnotationViewport.bl_idname),\n]\n\ngroup_items = [\n    nodeitems_utils.NodeItem(bpy.types.NodeGroupOutput.__name__),\n]\n\ncategories = [\n    DreamTexturesNodeCategory(\"DREAM_TEXTURES_PIPELINE\", \"Pipeline\", items=pipeline_items),\n    DreamTexturesNodeCategory(\"DREAM_TEXTURES_INPUT\", \"Input\", items=input_items),\n    DreamTexturesNodeCategory(\"DREAM_TEXTURES_UTILITY\", \"Utilities\", items=utility_items),\n    DreamTexturesNodeCategory(\"DREAM_TEXTURES_ANNOTATIONS\", \"Annotations\", items=annotations_items),\n    DreamTexturesNodeCategory(\"DREAM_TEXTURES_GROUP\", \"Group\", items=group_items),\n]\n\ndef register():\n    # Prompt\n    bpy.types.Scene.dream_textures_engine_prompt = bpy.props.PointerProperty(type=DreamPrompt)\n    \n    # OpenPose\n    bpy.utils.register_class(openpose.ArmatureOpenPoseData)\n    bpy.types.Armature.dream_textures_openpose = bpy.props.PointerProperty(\n        type=openpose.ArmatureOpenPoseData\n    )\n    bpy.utils.register_class(openpose.BoneOpenPoseData)\n    bpy.types.Bone.dream_textures_openpose = bpy.props.PointerProperty(\n        type=openpose.BoneOpenPoseData\n    )\n\n    # ADE20K\n    bpy.utils.register_class(ade20k.ObjectADE20KData)\n    bpy.types.Object.dream_textures_ade20k = bpy.props.PointerProperty(\n        type=ade20k.ObjectADE20KData\n    )\n\n    bpy.utils.register_class(DreamTexturesNodeTree)\n    \n    # Nodes\n    bpy.utils.register_class(NodeSocketControlNet)\n    bpy.utils.register_class(NodeStableDiffusion)\n    bpy.utils.register_class(NodeControlNet)\n\n    bpy.utils.register_class(NodeInteger)\n    bpy.utils.register_class(NodeString)\n    bpy.utils.register_class(NodeCollection)\n    bpy.utils.register_class(NodeImage)\n    bpy.utils.register_class(NodeImageFile)\n    bpy.utils.register_class(NodeRenderProperties)\n    \n    bpy.utils.register_class(NodeAnnotationDepth)\n    bpy.utils.register_class(NodeAnnotationNormal)\n    bpy.utils.register_class(NodeAnnotationOpenPose)\n    bpy.utils.register_class(NodeAnnotationADE20K)\n    bpy.utils.register_class(NodeAnnotationViewport)\n\n    bpy.utils.register_class(NodeMath)\n    bpy.utils.register_class(NodeRandomValue)\n    bpy.utils.register_class(NodeRandomSeed)\n    bpy.utils.register_class(NodeSeed)\n    bpy.utils.register_class(NodeClamp)\n    bpy.utils.register_class(NodeFramePath)\n    bpy.utils.register_class(NodeCropImage)\n    bpy.utils.register_class(NodeResizeImage)\n    bpy.utils.register_class(NodeJoinImages)\n    bpy.utils.register_class(NodeColorCorrect)\n    bpy.utils.register_class(NodeSeparateColor)\n    bpy.utils.register_class(NodeCombineColor)\n    bpy.utils.register_class(NodeSwitch)\n    bpy.utils.register_class(NodeCompare)\n    bpy.utils.register_class(NodeReplaceString)\n\n    nodeitems_utils.register_node_categories(\"DREAM_TEXTURES_CATEGORIES\", categories)\n\ndef unregister():\n    # OpenPose\n    del bpy.types.Armature.dream_textures_openpose\n    bpy.utils.unregister_class(openpose.ArmatureOpenPoseData)\n    del bpy.types.Bone.dream_textures_openpose\n    bpy.utils.unregister_class(openpose.BoneOpenPoseData)\n\n    # ADE20K\n    del bpy.types.Object.dream_textures_ade20k\n    bpy.utils.unregister_class(ade20k.ObjectADE20KData)\n\n    bpy.utils.unregister_class(DreamTexturesNodeTree)\n    \n    # Nodes\n    bpy.utils.unregister_class(NodeSocketControlNet)\n    bpy.utils.unregister_class(NodeStableDiffusion)\n    bpy.utils.unregister_class(NodeControlNet)\n\n    bpy.utils.unregister_class(NodeInteger)\n    bpy.utils.unregister_class(NodeString)\n    bpy.utils.unregister_class(NodeCollection)\n    bpy.utils.unregister_class(NodeImage)\n    bpy.utils.unregister_class(NodeImageFile)\n    bpy.utils.unregister_class(NodeRenderProperties)\n\n    bpy.utils.unregister_class(NodeAnnotationDepth)\n    bpy.utils.unregister_class(NodeAnnotationNormal)\n    bpy.utils.unregister_class(NodeAnnotationOpenPose)\n    bpy.utils.unregister_class(NodeAnnotationADE20K)\n    bpy.utils.unregister_class(NodeAnnotationViewport)\n    \n    bpy.utils.unregister_class(NodeMath)\n    bpy.utils.unregister_class(NodeRandomValue)\n    bpy.utils.unregister_class(NodeRandomSeed)\n    bpy.utils.unregister_class(NodeSeed)\n    bpy.utils.unregister_class(NodeClamp)\n    bpy.utils.unregister_class(NodeFramePath)\n    bpy.utils.unregister_class(NodeCropImage)\n    bpy.utils.unregister_class(NodeResizeImage)\n    bpy.utils.unregister_class(NodeJoinImages)\n    bpy.utils.unregister_class(NodeColorCorrect)\n    bpy.utils.unregister_class(NodeSeparateColor)\n    bpy.utils.unregister_class(NodeCombineColor)\n    bpy.utils.unregister_class(NodeSwitch)\n    bpy.utils.unregister_class(NodeCompare)\n    bpy.utils.unregister_class(NodeReplaceString)\n\n    nodeitems_utils.unregister_node_categories(\"DREAM_TEXTURES_CATEGORIES\")"
  },
  {
    "path": "engine/annotations/ade20k.py",
    "content": "import bpy\nimport gpu\nfrom gpu_extras.batch import batch_for_shader\nimport numpy as np\nimport threading\nfrom .compat import UNIFORM_COLOR\n\nannotation_enum_cases = [('91', 'airplane', 'airplane;aeroplane;plane'), ('127', 'animal', 'animal;animate;being;beast;brute;creature;fauna'), ('93', 'apparel', 'apparel;wearing;apparel;dress;clothes'), ('79', 'arcade machine', 'arcade;machine'), ('31', 'armchair', 'armchair'), ('139', 'ashcan', 'ashcan;trash;can;garbage;can;wastebin;ash;bin;ash-bin;ashbin;dustbin;trash;barrel;trash;bin'), ('87', 'awning', 'awning;sunshade;sunblind'), ('116', 'bag', 'bag'), ('120', 'ball', 'ball'), ('96', 'bannister', 'bannister;banister;balustrade;balusters;handrail'), ('78', 'bar', 'bar'), ('112', 'barrel', 'barrel;cask'), ('41', 'base', 'base;pedestal;stand'), ('113', 'basket', 'basket;handbasket'), ('38', 'bathtub', 'bathtub;bathing;tub;bath;tub'), ('8', 'bed', 'bed'), ('70', 'bench', 'bench'), ('128', 'bicycle', 'bicycle;bike;wheel;cycle'), ('132', 'blanket', 'blanket;cover'), ('64', 'blind', 'blind;screen'), ('77', 'boat', 'boat'), ('63', 'book', 'bookcase'), ('63', 'bookcase', 'bookcase'), ('89', 'booth', 'booth;cubicle;stall;kiosk'), ('99', 'bottle', 'bottle'), ('42', 'box', 'box'), ('62', 'bridge', 'bridge;span'), ('100', 'buffet', 'buffet;counter;sideboard'), ('2', 'building', 'building;edifice'), ('145', 'bulletin board', 'bulletin;board;notice;board'), ('81', 'bus', 'bus;autobus;coach;charabanc;double-decker;jitney;motorbus;motorcoach;omnibus;passenger;vehicle'), ('11', 'cabinet', 'cabinet'), ('107', 'canopy', 'canopy'), ('21', 'car', 'car;auto;automobile;machine;motorcar'), ('56', 'case', 'case;display;case;showcase;vitrine'), ('6', 'ceiling', 'ceiling'), ('20', 'chair', 'chair'), ('86', 'chandelier', 'chandelier;pendant;pendent'), ('45', 'chest of drawers', 'chest;of;drawers;chest;bureau;dresser'), ('149', 'clock', 'clock'), ('65', 'coffee table', 'coffee;table;cocktail;table'), ('43', 'column', 'column;pillar'), ('75', 'computer', 'computer;computing;machine;computing;device;data;processor;electronic;computer;information;processing;system'), ('106', 'conveyer belt', 'conveyer;belt;conveyor;belt;conveyer;conveyor;transporter'), ('46', 'counter', 'counter'), ('71', 'countertop', 'countertop'), ('118', 'cradle', 'cradle'), ('142', 'crt screen', 'crt;screen'), ('19', 'curtain', 'curtain;drape;drapery;mantle;pall'), ('40', 'cushion', 'cushion'), ('34', 'desk', 'desk'), ('92', 'dirt track', 'dirt;track'), ('130', 'dishwasher', 'dishwasher;dish;washer;dishwashing;machine'), ('15', 'door', 'door;double;door'), ('14', 'earth', 'earth;ground'), ('97', 'escalator', 'escalator;moving;staircase;moving;stairway'), ('140', 'fan', 'fan'), ('33', 'fence', 'fence;fencing'), ('30', 'field', 'field'), ('50', 'fireplace', 'fireplace;hearth;open;fireplace'), ('150', 'flag', 'flag'), ('4', 'floor', 'floor;flooring'), ('67', 'flower', 'flower'), ('121', 'food', 'food;solid;food'), ('105', 'fountain', 'fountain'), ('148', 'glass', 'glass;drinking;glass'), ('52', 'grandstand', 'grandstand;covered;stand'), ('10', 'grass', 'grass'), ('69', 'hill', 'hill'), ('134', 'hood', 'hood;exhaust;hood'), ('26', 'house', 'house'), ('80', 'hovel', 'hovel;hut;hutch;shack;shanty'), ('74', 'land', 'kitchen;island'), ('74', 'kitchen island', 'kitchen;island'), ('129', 'lake', 'lake'), ('37', 'lamp', 'lamp'), ('83', 'light', 'light;light;source'), ('125', 'microwave', 'microwave;microwave;oven'), ('117', 'minibike', 'minibike;motorbike'), ('28', 'mirror', 'mirror'), ('144', 'monitor', 'monitor;monitoring;device'), ('17', 'mountain', 'mountain;mount'), ('98', 'ottoman', 'ottoman;pouf;pouffe;puff;hassock'), ('119', 'oven', 'oven'), ('23', 'painting', 'painting;picture'), ('73', 'palm', 'palm;palm;tree'), ('53', 'path', 'path'), ('13', 'person', 'person;individual;someone;somebody;mortal;soul'), ('141', 'pier', 'pier;wharf;wharfage;dock'), ('58', 'pillow', 'pillow'), ('18', 'plant', 'plant;flora;plant;life'), ('143', 'plate', 'plate'), ('109', 'plaything', 'plaything;toy'), ('94', 'pole', 'pole'), ('57', 'pool table', 'pool;table;billiard;table;snooker;table'), ('101', 'poster', 'poster;posting;placard;notice;bill;card'), ('147', 'radiator', 'radiator'), ('39', 'railing', 'railing;rail'), ('51', 'refrigerator', 'refrigerator;icebox'), ('61', 'river', 'river'), ('7', 'road', 'road;route'), ('35', 'rock', 'rock;stone'), ('29', 'rug', 'rug;carpet;carpeting'), ('55', 'runway', 'runway'), ('47', 'sand', 'sand'), ('135', 'sconce', 'sconce'), ('59', 'screen door', 'screen;door;screen'), ('59', 'screen', 'screen;door;screen'), ('133', 'sculpture', 'sculpture'), ('27', 'sea', 'sea'), ('32', 'seat', 'seat'), ('25', 'shelf', 'shelf'), ('104', 'ship', 'ship'), ('146', 'shower', 'shower'), ('12', 'sidewalk', 'sidewalk;pavement'), ('44', 'signboard', 'signboard;sign'), ('48', 'sink', 'sink'), ('3', 'sky', 'sky'), ('49', 'skyscraper', 'skyscraper'), ('24', 'sofa', 'sofa;couch;lounge'), ('102', 'stage', 'stage'), ('54', 'step', 'stairs;steps'), ('54', 'stairs', 'stairs;steps'), ('60', 'stairway', 'stairway;staircase'), ('72', 'stove', 'stove;kitchen;stove;range;kitchen;range;cooking;stove'), ('88', 'streetlight', 'streetlight;street;lamp'), ('110', 'swimming pool', 'swimming;pool;swimming;bath;natatorium'), ('76', 'swivel chair', 'swivel;chair'), ('16', 'table', 'table'), ('123', 'tank', 'tank;storage;tank'), ('90', 'television receiver', 'television;television;receiver;television;set;tv;tv;set;idiot;box;boob;tube;telly;goggle;box'), ('115', 'tent', 'tent;collapsible;shelter'), ('66', 'stool', 'toilet;can;commode;crapper;pot;potty;stool;throne'), ('66', 'pot', 'toilet;can;commode;crapper;pot;potty;stool;throne'), ('66', 'toilet', 'toilet;can;commode;crapper;pot;potty;stool;throne'), ('82', 'towel', 'towel'), ('85', 'tower', 'tower'), ('124', 'trade name', 'trade;name;brand;name;brand;marque'), ('137', 'traffic light', 'traffic;light;traffic;signal;stoplight'), ('138', 'tray', 'tray'), ('5', 'tree', 'tree'), ('84', 'truck', 'truck;motortruck'), ('103', 'van', 'van'), ('136', 'vase', 'vase'), ('1', 'wall', 'wall'), ('36', 'wardrobe', 'wardrobe;closet;press'), ('108', 'washer', 'washer;automatic;washer;washing;machine'), ('22', 'water', 'water'), ('114', 'waterfall', 'waterfall;falls'), ('9', 'windowpane', 'windowpane;window')]\nannotation_colors = {'80': (1.0, 0.0, 0.996078431372549, 1.0), '140': (0.00392156862745098, 0.9607843137254902, 1.0, 1.0), '115': (0.43529411764705883, 0.8784313725490196, 0.996078431372549, 1.0), '33': (1.0, 0.7215686274509804, 0.023529411764705882, 1.0), '77': (0.6784313725490196, 1.0, 0.0, 1.0), '58': (0.0, 0.9215686274509803, 1.0, 1.0), '98': (0.996078431372549, 0.6, 0.0, 1.0), '74': (0.0, 1.0, 0.1607843137254902, 1.0), '150': (0.3568627450980392, 0.0, 0.996078431372549, 1.0), '117': (0.6431372549019608, 0.0, 1.0, 1.0), '18': (0.8, 1.0, 0.01568627450980392, 1.0), '145': (0.7215686274509804, 1.0, 0.0, 1.0), '63': (0.0, 1.0, 0.9607843137254902, 1.0), '9': (0.9019607843137255, 0.9019607843137255, 0.9019607843137255, 1.0), '34': (0.0392156862745098, 1.0, 0.2784313725490196, 1.0), '120': (1.0, 0.00392156862745098, 0.6352941176470588, 1.0), '61': (0.0392156862745098, 0.7843137254901961, 0.7843137254901961, 1.0), '32': (0.027450980392156862, 0.996078431372549, 0.8745098039215686, 1.0), '124': (0.5215686274509804, 0.996078431372549, 0.0, 1.0), '6': (0.4666666666666667, 0.47058823529411764, 0.3137254901960784, 1.0), '104': (1.0, 0.9215686274509803, 0.0, 1.0), '38': (0.4, 0.03137254901960784, 1.0, 1.0), '106': (0.5215686274509804, 0.0, 1.0, 1.0), '56': (0.00392156862745098, 0.0, 0.996078431372549, 1.0), '12': (0.9215686274509803, 1.0, 0.027450980392156862, 1.0), '19': (1.0, 0.2, 0.03137254901960784, 1.0), '70': (0.7607843137254902, 1.0, 0.00392156862745098, 1.0), '88': (0.00392156862745098, 0.2784313725490196, 1.0, 1.0), '62': (1.0, 0.3254901960784314, 0.00392156862745098, 1.0), '7': (0.5490196078431373, 0.5490196078431373, 0.5490196078431373, 1.0), '29': (1.0, 0.03529411764705882, 0.3607843137254902, 1.0), '82': (1.0, 0.0, 0.4, 1.0), '142': (0.47843137254901963, 0.00392156862745098, 1.0, 1.0), '11': (0.8784313725490196, 0.0196078431372549, 1.0, 1.0), '147': (1.0, 0.8392156862745098, 0.00784313725490196, 1.0), '76': (0.0392156862745098, 0.0, 1.0, 1.0), '146': (0.0, 0.5215686274509804, 0.996078431372549, 1.0), '54': (1.0, 0.8784313725490196, 0.00392156862745098, 1.0), '94': (0.2, 0.0, 1.0, 1.0), '59': (0.0, 0.8, 1.0, 1.0), '118': (0.6039215686274509, 0.0, 1.0, 1.0), '39': (1.0, 0.24313725490196078, 0.027450980392156862, 1.0), '101': (0.5607843137254902, 0.996078431372549, 0.00392156862745098, 1.0), '46': (0.9254901960784314, 0.043137254901960784, 1.0, 1.0), '110': (0.0, 0.7215686274509804, 1.0, 1.0), '45': (0.023529411764705882, 0.2, 1.0, 1.0), '93': (0.0, 0.44313725490196076, 0.996078431372549, 1.0), '13': (0.5882352941176471, 0.0196078431372549, 0.24313725490196078, 1.0), '50': (0.9803921568627451, 0.03529411764705882, 0.058823529411764705, 1.0), '64': (0.00392156862745098, 0.23921568627450981, 1.0, 1.0), '30': (0.43529411764705883, 0.03529411764705882, 1.0, 1.0), '99': (0.0, 1.0, 0.043137254901960784, 1.0), '35': (1.0, 0.1568627450980392, 0.03529411764705882, 1.0), '125': (0.996078431372549, 0.0, 0.9176470588235294, 1.0), '105': (0.03137254901960784, 0.7215686274509804, 0.6705882352941176, 1.0), '135': (0.0, 0.1607843137254902, 1.0, 1.0), '53': (1.0, 0.11764705882352941, 0.0, 1.0), '66': (0.0, 1.0, 0.5215686274509804, 1.0), '8': (0.8, 0.0196078431372549, 0.996078431372549, 1.0), '26': (1.0, 0.03137254901960784, 0.8666666666666667, 1.0), '14': (0.47058823529411764, 0.47058823529411764, 0.27450980392156865, 1.0), '73': (0.0, 0.3215686274509804, 0.996078431372549, 1.0), '23': (1.0, 0.023529411764705882, 0.2, 1.0), '24': (0.043137254901960784, 0.4, 1.0, 1.0), '97': (0.0, 1.0, 0.6392156862745098, 1.0), '132': (0.0784313725490196, 0.0, 1.0, 1.0), '100': (1.0, 0.4392156862745098, 0.0, 1.0), '83': (1.0, 0.6784313725490196, 0.00392156862745098, 1.0), '85': (0.996078431372549, 0.7215686274509804, 0.7215686274509804, 1.0), '79': (1.0, 0.3607843137254902, 0.0, 1.0), '144': (0.0, 0.3607843137254902, 1.0, 1.0), '113': (0.3568627450980392, 1.0, 0.0, 1.0), '133': (1.0, 1.0, 0.0, 1.0), '52': (0.12156862745098039, 1.0, 0.0, 1.0), '28': (0.8627450980392157, 0.8627450980392157, 0.8627450980392157, 1.0), '10': (0.0196078431372549, 0.9803921568627451, 0.027450980392156862, 1.0), '2': (0.7058823529411765, 0.47058823529411764, 0.47058823529411764, 1.0), '103': (0.6392156862745098, 0.996078431372549, 0.0, 1.0), '91': (0.00392156862745098, 0.996078431372549, 0.3254901960784314, 1.0), '121': (1.0, 0.8, 0.0, 1.0), '86': (0.0, 0.12156862745098039, 0.996078431372549, 1.0), '27': (0.03529411764705882, 0.027450980392156862, 0.9058823529411765, 1.0), '114': (0.0, 0.8823529411764706, 1.0, 1.0), '20': (0.796078431372549, 0.27450980392156865, 0.011764705882352941, 1.0), '134': (0.0, 0.6, 1.0, 1.0), '17': (0.5568627450980392, 1.0, 0.5450980392156862, 1.0), '3': (0.023529411764705882, 0.9019607843137255, 0.9019607843137255, 1.0), '65': (0.00392156862745098, 0.996078431372549, 0.4392156862745098, 1.0), '139': (0.6823529411764706, 0.0, 1.0, 1.0), '130': (0.8392156862745098, 1.0, 0.0, 1.0), '136': (0.00392156862745098, 1.0, 0.803921568627451, 1.0), '51': (0.0784313725490196, 1.0, 0.0, 1.0), '15': (0.03137254901960784, 1.0, 0.20392156862745098, 1.0), '55': (0.6, 1.0, 0.0, 1.0), '5': (0.01568627450980392, 0.7843137254901961, 0.023529411764705882, 1.0), '22': (0.23921568627450981, 0.9019607843137255, 0.984313725490196, 1.0), '72': (0.2, 1.0, 0.0, 1.0), '41': (1.0, 0.4823529411764706, 0.0392156862745098, 1.0), '137': (0.1607843137254902, 0.0, 1.0, 1.0), '92': (0.0, 0.0392156862745098, 1.0, 1.0), '21': (0.0, 0.4, 0.7843137254901961, 1.0), '109': (1.0, 0.0, 0.11764705882352941, 1.0), '1': (0.47058823529411764, 0.47058823529411764, 0.47058823529411764, 1.0), '67': (1.0, 0.00392156862745098, 0.011764705882352941, 1.0), '60': (0.12156862745098039, 0.0, 0.996078431372549, 1.0), '44': (1.0, 0.0196078431372549, 0.6039215686274509, 1.0), '69': (1.0, 0.4, 0.0, 1.0), '107': (0.0, 0.996078431372549, 0.36470588235294116, 1.0), '4': (0.3137254901960784, 0.19607843137254902, 0.20392156862745098, 1.0), '90': (0.0, 1.0, 0.7647058823529411, 1.0), '149': (0.396078431372549, 1.0, 0.0, 1.0), '129': (0.0392156862745098, 0.7490196078431373, 0.8313725490196079, 1.0), '75': (0.0, 1.0, 0.6784313725490196, 1.0), '141': (0.2823529411764706, 0.0, 1.0, 1.0), '128': (1.0, 0.9607843137254902, 0.0, 1.0), '57': (1.0, 0.2784313725490196, 0.0, 1.0), '49': (0.5490196078431373, 0.5490196078431373, 0.5490196078431373, 1.0), '108': (0.7215686274509804, 0.0, 0.996078431372549, 1.0), '87': (0.0, 1.0, 0.23921568627450981, 1.0), '43': (1.0, 0.03137254901960784, 0.16470588235294117, 1.0), '71': (0.0, 0.5607843137254902, 1.0, 1.0), '31': (0.03529411764705882, 0.996078431372549, 0.8352941176470589, 1.0), '81': (1.0, 0.0, 0.9568627450980393, 1.0), '36': (0.027450980392156862, 1.0, 1.0, 1.0), '143': (0.0, 1.0, 0.7254901960784313, 1.0), '148': (0.09411764705882353, 0.7607843137254902, 0.7607843137254902, 1.0), '102': (0.3215686274509804, 0.0, 1.0, 1.0), '84': (1.0, 0.0, 0.08235294117647059, 1.0), '47': (0.6274509803921569, 0.5882352941176471, 0.07450980392156863, 1.0), '127': (1.0, 0.0, 0.47843137254901963, 1.0), '16': (0.996078431372549, 0.023529411764705882, 0.3215686274509804, 1.0), '78': (0.0, 0.996078431372549, 0.6078431372549019, 1.0), '25': (1.0, 0.023529411764705882, 0.27450980392156865, 1.0), '138': (0.16862745098039217, 0.996078431372549, 0.011764705882352941, 1.0), '42': (0.00392156862745098, 1.0, 0.07450980392156863, 1.0), '119': (0.2784313725490196, 1.0, 0.00392156862745098, 1.0), '112': (0.996078431372549, 0.0, 0.4392156862745098, 1.0), '89': (0.996078431372549, 0.0, 0.796078431372549, 1.0), '96': (0.0, 0.47843137254901963, 1.0, 1.0), '37': (0.8823529411764706, 1.0, 0.03529411764705882, 1.0), '48': (0.0, 0.6392156862745098, 0.996078431372549, 1.0), '116': (0.27058823529411763, 0.7176470588235294, 0.6196078431372549, 1.0), '40': (1.0, 0.7607843137254902, 0.027450980392156862, 1.0), '123': (0.0, 1.0, 0.9176470588235294, 1.0)}\n\ndef annotation_update(self, context):\n    self.color = annotation_colors[self.annotation][:3]\n\nclass ObjectADE20KData(bpy.types.PropertyGroup):\n    bl_label = \"ADE20K Segmentation\"\n    bl_idname = \"dream_textures.ObjectADE20KData\"\n\n    enabled: bpy.props.BoolProperty(name=\"Enabled\", default=False)\n    annotation: bpy.props.EnumProperty(\n        name=\"Class\",\n        items=annotation_enum_cases,\n        update=annotation_update\n    )\n    # for visualization only\n    color: bpy.props.FloatVectorProperty(name=\"\", subtype='COLOR', default=annotation_colors[annotation_enum_cases[0][0]][:3])\n\ndef render_ade20k_map(context, collection=None, invert=True):\n    e = threading.Event()\n    result = None\n    def _execute():\n        nonlocal result\n        width, height = context.scene.render.resolution_x, context.scene.render.resolution_y\n        matrix = context.scene.camera.matrix_world.inverted()\n        projection_matrix = context.scene.camera.calc_matrix_camera(\n            context,\n            x=width,\n            y=height\n        )\n        offscreen = gpu.types.GPUOffScreen(width, height)\n\n        with offscreen.bind():\n            fb = gpu.state.active_framebuffer_get()\n            fb.clear(color=(0.0, 0.0, 0.0, 0.0), depth=1)\n            \n            gpu.state.depth_test_set('LESS_EQUAL')\n            gpu.state.depth_mask_set(True)\n            with gpu.matrix.push_pop():\n                gpu.matrix.load_matrix(matrix)\n                gpu.matrix.load_projection_matrix(projection_matrix)\n\n                def render_mesh(mesh, transform, color):\n                    mesh.transform(transform)\n                    mesh.calc_loop_triangles()\n                    vertices = np.empty((len(mesh.vertices), 3), 'f')\n                    indices = np.empty((len(mesh.loop_triangles), 3), 'i')\n\n                    mesh.vertices.foreach_get(\"co\", np.reshape(vertices, len(mesh.vertices) * 3))\n                    mesh.loop_triangles.foreach_get(\"vertices\", np.reshape(indices, len(mesh.loop_triangles) * 3))\n                    \n                    draw_annotation(vertices, indices, color)\n                if collection is None:\n                    for object in context.object_instances:\n                        if not hasattr(object.object, 'dream_textures_ade20k') or not object.object.dream_textures_ade20k.enabled:\n                            continue\n                        try:\n                            mesh = object.object.to_mesh()\n                            if mesh is not None:\n                                render_mesh(mesh, object.matrix_world, annotation_colors[object.object.dream_textures_ade20k.annotation])\n                                object.object.to_mesh_clear()\n                        except:\n                            continue\n                else:\n                    for object in collection.objects:\n                        if not hasattr(object, 'dream_textures_ade20k') or not object.dream_textures_ade20k.enabled:\n                            continue\n                        try:\n                            mesh = object.to_mesh(depsgraph=context)\n                            if mesh is not None:\n                                render_mesh(mesh, object.matrix_world, annotation_colors[object.dream_textures_ade20k.annotation])\n                                object.to_mesh_clear()\n                        except:\n                            continue\n            result = np.array(fb.read_color(0, 0, width, height, 4, 0, 'FLOAT').to_list())\n            result[:, :, 3] = 1\n        gpu.state.depth_test_set('NONE')\n        offscreen.free()\n        e.set()\n    if threading.current_thread() == threading.main_thread():\n        _execute()\n        return result\n    else:\n        bpy.app.timers.register(_execute, first_interval=0)\n        e.wait()\n        return result\n\ndef draw_annotation(vertices, indices, color):\n    shader = gpu.shader.from_builtin(UNIFORM_COLOR)\n    batch = batch_for_shader(\n        shader, 'TRIS',\n        {\"pos\": vertices},\n        indices=indices,\n    )\n    shader.bind()\n    shader.uniform_float(\"color\", color)\n    batch.draw(shader)"
  },
  {
    "path": "engine/annotations/compat.py",
    "content": "import bpy\n\nUNIFORM_COLOR = 'UNIFORM_COLOR' if bpy.app.version >= (3, 4, 0) else '3D_UNIFORM_COLOR'\n\"\"\"\n2D_ and 3D_ prefixed built-in shaders were deprecated in Blender 3.4 and removed in Blender 4.0\n\"\"\"\n"
  },
  {
    "path": "engine/annotations/depth.py",
    "content": "import bpy\nimport gpu\nfrom gpu_extras.batch import batch_for_shader\nimport numpy as np\nimport threading\nfrom .compat import UNIFORM_COLOR\n\ndef render_depth_map(context, collection=None, invert=True, width=None, height=None, matrix=None, projection_matrix=None, main_thread=False):\n    e = threading.Event()\n    result = None\n\n    width, height = width or context.scene.render.resolution_x, height or context.scene.render.resolution_y\n    matrix = matrix or context.scene.camera.matrix_world.inverted()\n    projection_matrix = projection_matrix or context.scene.camera.calc_matrix_camera(\n        context,\n        x=width,\n        y=height\n    )\n\n    def _execute():\n        nonlocal result\n        offscreen = gpu.types.GPUOffScreen(width, height)\n\n        with offscreen.bind():\n            fb = gpu.state.active_framebuffer_get()\n            fb.clear(color=(0.0, 0.0, 0.0, 0.0), depth=1)\n            gpu.state.depth_test_set('LESS_EQUAL')\n            gpu.state.depth_mask_set(True)\n            with gpu.matrix.push_pop():\n                gpu.matrix.load_matrix(matrix)\n                gpu.matrix.load_projection_matrix(projection_matrix)\n                \n                shader = gpu.shader.from_builtin(UNIFORM_COLOR)\n\n                def render_mesh(mesh, transform):\n                    mesh.transform(transform)\n                    mesh.calc_loop_triangles()\n                    vertices = np.empty((len(mesh.vertices), 3), 'f')\n                    indices = np.empty((len(mesh.loop_triangles), 3), 'i')\n\n                    mesh.vertices.foreach_get(\"co\", np.reshape(vertices, len(mesh.vertices) * 3))\n                    mesh.loop_triangles.foreach_get(\"vertices\", np.reshape(indices, len(mesh.loop_triangles) * 3))\n                    \n                    batch = batch_for_shader(\n                        shader, 'TRIS',\n                        {\"pos\": vertices},\n                        indices=indices,\n                    )\n                    batch.draw(shader)\n                if collection is None:\n                    for object in context.object_instances:\n                        try:\n                            mesh = object.object.to_mesh()\n                            if mesh is not None:\n                                render_mesh(mesh, object.matrix_world)\n                                object.object.to_mesh_clear()\n                        except:\n                            continue\n                else:\n                    for object in collection.objects:\n                        try:\n                            mesh = object.to_mesh(depsgraph=context)\n                            if mesh is not None:\n                                render_mesh(mesh, object.matrix_world)\n                                object.to_mesh_clear()\n                        except:\n                            continue\n            depth = np.array(fb.read_depth(0, 0, width, height).to_list())\n            if invert:\n                depth = 1 - depth\n                mask = np.array(fb.read_color(0, 0, width, height, 4, 0, 'UBYTE').to_list())[:, :, 3]\n                depth *= mask\n            depth = np.interp(depth, [np.ma.masked_equal(depth, 0, copy=False).min(), depth.max()], [0, 1]).clip(0, 1)\n        gpu.state.depth_test_set('NONE')\n        offscreen.free()\n        result = depth\n        e.set()\n    if main_thread or threading.current_thread() == threading.main_thread():\n        _execute()\n        return result\n    else:\n        bpy.app.timers.register(_execute, first_interval=0)\n        e.wait()\n        return result"
  },
  {
    "path": "engine/annotations/normal.py",
    "content": "import bpy\nimport gpu\nfrom gpu_extras.batch import batch_for_shader\nimport bmesh\nimport numpy as np\nimport threading\n\ndef render_normal_map(context, collection=None, width=None, height=None, matrix=None, projection_matrix=None, main_thread=False):\n    e = threading.Event()\n    result = None\n\n    width, height = width or context.scene.render.resolution_x, height or context.scene.render.resolution_y\n    matrix = matrix or context.scene.camera.matrix_world.inverted()\n    projection_matrix = projection_matrix or context.scene.camera.calc_matrix_camera(\n        context,\n        x=width,\n        y=height\n    )\n\n    def normals_shader(smooth):\n        vert_out = gpu.types.GPUStageInterfaceInfo(\"my_interface\")\n        if smooth:\n            vert_out.smooth('VEC3', \"color\")\n        else:\n            vert_out.flat('VEC3', \"color\")\n\n        shader_info = gpu.types.GPUShaderCreateInfo()\n        shader_info.push_constant('MAT4', \"ModelViewProjectionMatrix\")\n        shader_info.push_constant('MAT4', \"ModelViewMatrix\")\n        shader_info.vertex_in(0, 'VEC3', \"position\")\n        shader_info.vertex_in(1, 'VEC3', \"normal\")\n        shader_info.vertex_out(vert_out)\n        shader_info.fragment_out(0, 'VEC4', \"fragColor\")\n\n        shader_info.vertex_source(\"\"\"\nvoid main()\n{\n    gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0f);\n    color = normalize(mat3(ModelViewMatrix) * normal);\n}\n\"\"\")\n\n        shader_info.fragment_source(\"\"\"\nvoid main()\n{\n    vec4 c = vec4((color + 1) / 2, 1.0f);\n    fragColor = vec4(1 - c.r, c.g, c.b, c.a);\n}\n\"\"\")\n\n        return gpu.shader.create_from_info(shader_info)\n\n    def _execute():\n        nonlocal result\n        offscreen = gpu.types.GPUOffScreen(width, height)\n\n        with offscreen.bind():\n            fb = gpu.state.active_framebuffer_get()\n            fb.clear(color=(0.5, 0.5, 1.0, 1.0), depth=1)\n            gpu.state.depth_test_set('LESS_EQUAL')\n            gpu.state.depth_mask_set(True)\n            with gpu.matrix.push_pop():\n                gpu.matrix.load_matrix(matrix)\n                gpu.matrix.load_projection_matrix(projection_matrix)\n\n                def render_mesh(mesh, transform):\n                    smooth_shader = normals_shader(smooth=True)\n                    smooth_shader.uniform_float(\"ModelViewMatrix\", gpu.matrix.get_model_view_matrix())\n                    flat_shader = normals_shader(smooth=False)\n                    flat_shader.uniform_float(\"ModelViewMatrix\", gpu.matrix.get_model_view_matrix())\n                    mesh.transform(transform)\n                    \n                    bm = bmesh.new()\n                    bm.from_mesh(mesh)\n                    \n                    smooth_mesh = bm.copy()\n                    flat_mesh = bm.copy()\n                    bmesh.ops.delete(smooth_mesh, geom=[f for f in smooth_mesh.faces if not f.smooth], context='FACES')\n                    bmesh.ops.delete(flat_mesh, geom=[f for f in flat_mesh.faces if f.smooth], context='FACES')\n\n                    def draw(mesh, smooth):\n                        vertices = [v.co for v in mesh.verts]\n                        normals = [v.normal for v in mesh.verts]\n                        indices = [[loop.vert.index for loop in looptris] for looptris in mesh.calc_loop_triangles()]\n                        \n                        shader = smooth_shader if smooth else flat_shader\n                        shader.bind()\n                        batch = batch_for_shader(\n                            shader, 'TRIS',\n                            {\"position\": vertices, \"normal\": normals},\n                            indices=indices,\n                        )\n                        batch.draw(shader)\n                    \n                    if len(smooth_mesh.verts) > 0:\n                        draw(smooth_mesh, smooth=True)\n                    if len(flat_mesh.verts) > 0:\n                        draw(flat_mesh, smooth=False)\n                if collection is None:\n                    for object in context.object_instances:\n                        try:\n                            mesh = object.object.to_mesh()\n                            if mesh is not None:\n                                render_mesh(mesh, object.matrix_world)\n                                object.object.to_mesh_clear()\n                        except:\n                            continue\n                else:\n                    for object in collection.objects:\n                        try:\n                            mesh = object.to_mesh(depsgraph=context)\n                            if mesh is not None:\n                                render_mesh(mesh, object.matrix_world)\n                                object.to_mesh_clear()\n                        except:\n                            continue\n            normal_map = np.array(fb.read_color(0, 0, width, height, 4, 0, 'FLOAT').to_list())\n        gpu.state.depth_test_set('NONE')\n        offscreen.free()\n        result = normal_map\n        e.set()\n    if main_thread or threading.current_thread() == threading.main_thread():\n        _execute()\n        return result\n    else:\n        bpy.app.timers.register(_execute, first_interval=0)\n        e.wait()\n        return result"
  },
  {
    "path": "engine/annotations/openpose.py",
    "content": "import bpy\nimport bpy_extras\nimport gpu\nfrom gpu_extras.batch import batch_for_shader\nimport mathutils\nimport numpy as np\nimport enum\nimport math\nimport threading\nfrom .compat import UNIFORM_COLOR\n\nclass Side(enum.IntEnum):\n    HEAD = 0\n    TAIL = 1\n\nclass Bone(enum.IntEnum):\n    NOSE = 0\n    CHEST = 1\n\n    SHOULDER_L = 2\n    SHOULDER_R = 3\n    ELBOW_L = 4\n    ELBOW_R = 5\n    HAND_L = 6\n    HAND_R = 7\n\n    HIP_L = 8\n    HIP_R = 9\n    KNEE_L = 10\n    KNEE_R = 11\n    FOOT_L = 12\n    FOOT_R = 13\n\n    EYE_L = 14\n    EYE_R = 15\n\n    EAR_L = 16\n    EAR_R = 17\n\n    def identify(self, armature, pose):\n        if not getattr(armature.dream_textures_openpose, self.name):\n            return None, None\n        for bone in pose.bones:\n            if bone.bone.dream_textures_openpose.enabled:\n                if bone.bone.dream_textures_openpose.bone == str(self.value):\n                    return bone, Side(int(bone.bone.dream_textures_openpose.side))\n        options = self.name_detection_options()\n        for option in options:\n            if (result := pose.bones.get(option[0], None)) is not None:\n                return result, option[1]\n        return None, None\n\n    def name_detection_options(self):\n        match self:\n            case Bone.NOSE:\n                return [('nose_master', Side.TAIL), ('nose.001', Side.TAIL), ('Head', Side.TAIL)]\n            case Bone.CHEST:\n                return [('spine_fk.003', Side.TAIL), ('spine.003', Side.TAIL), ('Spine4', Side.TAIL)]\n            case Bone.SHOULDER_L:\n                return [('shoulder_ik.L', Side.TAIL), ('shoulder.L', Side.TAIL), ('LeftShoulder', Side.TAIL)]\n            case Bone.SHOULDER_R:\n                return [('shoulder_ik.R', Side.TAIL), ('shoulder.R', Side.TAIL), ('RightShoulder', Side.TAIL)]\n            case Bone.ELBOW_L:\n                return [('upper_arm_ik.L', Side.TAIL), ('upper_arm.L', Side.TAIL), ('LeftArm', Side.TAIL)]\n            case Bone.ELBOW_R:\n                return [('upper_arm_ik.R', Side.TAIL), ('upper_arm.R', Side.TAIL), ('RightArm', Side.TAIL)]\n            case Bone.HAND_L:\n                return [('hand_ik.L', Side.TAIL), ('forearm.L', Side.TAIL), ('LeftForeArm', Side.TAIL)]\n            case Bone.HAND_R:\n                return [('hand_ik.R', Side.TAIL), ('forearm.R', Side.TAIL), ('RightForeArm', Side.TAIL)]\n            case Bone.HIP_L:\n                return [('thigh_ik.L', Side.HEAD), ('thigh.L', Side.HEAD), ('LeftThigh', Side.HEAD)]\n            case Bone.HIP_R:\n                return [('thigh_ik.R', Side.HEAD), ('thigh.R', Side.HEAD), ('RightThigh', Side.HEAD)]\n            case Bone.KNEE_L:\n                return [('thigh_ik.L', Side.TAIL), ('thigh.L', Side.TAIL), ('LeftShin', Side.HEAD)]\n            case Bone.KNEE_R:\n                return [('thigh_ik.R', Side.TAIL), ('thigh.R', Side.TAIL), ('RightShin', Side.HEAD)]\n            case Bone.FOOT_L:\n                return [('foot_ik.L', Side.TAIL), ('shin.L', Side.TAIL), ('LeftFoot', Side.HEAD)]\n            case Bone.FOOT_R:\n                return [('foot_ik.R', Side.TAIL), ('shin.R', Side.TAIL), ('RightFoot', Side.HEAD)]\n            case Bone.EYE_L:\n                return [('master_eye.L', Side.TAIL), ('eye.L', Side.TAIL)]\n            case Bone.EYE_R:\n                return [('master_eye.R', Side.TAIL), ('eye.R', Side.TAIL)]\n            case Bone.EAR_L:\n                return [('ear.L', Side.TAIL), ('ear.L.001', Side.TAIL)]\n            case Bone.EAR_R:\n                return [('ear.R', Side.TAIL), ('ear.R.001', Side.TAIL)]\n            \n    def color(self):\n        match self:\n            case Bone.NOSE: return (255, 0, 0)\n            case Bone.CHEST: return (255, 85, 0)\n            case Bone.SHOULDER_L: return (85, 255, 0)\n            case Bone.SHOULDER_R: return (255, 170, 0)\n            case Bone.ELBOW_L: return (0, 255, 0)\n            case Bone.ELBOW_R: return (255, 255, 0)\n            case Bone.HAND_L: return (0, 255, 85)\n            case Bone.HAND_R: return (170, 255, 0)\n            case Bone.HIP_L: return (0, 85, 255)\n            case Bone.HIP_R: return (0, 255, 170)\n            case Bone.KNEE_L: return (0, 0, 255)\n            case Bone.KNEE_R: return (0, 255, 255)\n            case Bone.FOOT_L: return (85, 0, 255)\n            case Bone.FOOT_R: return (0, 170, 255)\n            case Bone.EYE_L: return (255, 0, 255)\n            case Bone.EYE_R: return (170, 0, 255)\n            case Bone.EAR_L: return (255, 0, 85)\n            case Bone.EAR_R: return (255, 0, 170)\n\nopenpose_bones = ((str(b.value), b.name.title(), '') for b in Bone)\nopenpose_sides = ((str(s.value), s.name.title(), '') for s in Side)\nclass BoneOpenPoseData(bpy.types.PropertyGroup):\n    bl_label = \"OpenPose\"\n    bl_idname = \"dream_textures.BoneOpenPoseData\"\n\n    enabled: bpy.props.BoolProperty(name=\"Enabled\", default=False)\n    bone: bpy.props.EnumProperty(\n        name=\"OpenPose Bone\",\n        items=openpose_bones\n    )\n    side: bpy.props.EnumProperty(\n        name=\"Bone Side\",\n        items=openpose_sides\n    )\n\nArmatureOpenPoseData = type('ArmatureOpenPoseData', (bpy.types.PropertyGroup,), {\n    \"bl_label\": \"OpenPose\",\n    \"bl_idname\": \"dream_textures.ArmatureOpenPoseData\",\n    \"__annotations__\": { b.name: bpy.props.BoolProperty(name=b.name.title(), default=True) for b in Bone },\n})\n\ndef render_openpose_map(context, collection=None):\n    e = threading.Event()\n    result = None\n    def _execute():\n        nonlocal result\n        width, height = context.scene.render.resolution_x, context.scene.render.resolution_y\n        offscreen = gpu.types.GPUOffScreen(width, height)\n\n        with offscreen.bind():\n            fb = gpu.state.active_framebuffer_get()\n            fb.clear(color=(0.0, 0.0, 0.0, 0.0), depth=1)\n            gpu.state.depth_test_set('LESS_EQUAL')\n            gpu.state.depth_mask_set(True)\n\n            lines = {\n                (Bone.NOSE, Bone.CHEST): (0, 0, 255),\n                (Bone.CHEST, Bone.SHOULDER_L): (255, 85, 0),\n                (Bone.CHEST, Bone.SHOULDER_R): (255, 0, 0),\n                (Bone.SHOULDER_L, Bone.ELBOW_L): (170, 255, 0),\n                (Bone.SHOULDER_R, Bone.ELBOW_R): (255, 170, 0),\n                (Bone.ELBOW_L, Bone.HAND_L): (85, 255, 0),\n                (Bone.ELBOW_R, Bone.HAND_R): (255, 255, 0),\n                (Bone.CHEST, Bone.HIP_L): (0, 255, 255),\n                (Bone.CHEST, Bone.HIP_R): (0, 255, 0),\n                (Bone.HIP_L, Bone.KNEE_L): (0, 170, 255),\n                (Bone.HIP_R, Bone.KNEE_R): (0, 255, 85),\n                (Bone.KNEE_L, Bone.FOOT_L): (0, 85, 255),\n                (Bone.KNEE_R, Bone.FOOT_R): (0, 255, 170),\n                (Bone.NOSE, Bone.EYE_L): (255, 0, 255),\n                (Bone.NOSE, Bone.EYE_R): (85, 0, 255),\n                (Bone.EYE_L, Bone.EAR_L): (255, 0, 170),\n                (Bone.EYE_R, Bone.EAR_R): (170, 0, 255),\n            }\n                                \n            with gpu.matrix.push_pop():\n                ratio = width / height\n                projection_matrix = mathutils.Matrix((\n                    (1 / ratio, 0, 0, 0),\n                    (0, 1, 0, 0),\n                    (0, 0, -1, 0),\n                    (0, 0, 0, 1)\n                ))\n                gpu.matrix.load_matrix(mathutils.Matrix.Identity(4))\n                gpu.matrix.load_projection_matrix(projection_matrix)\n                gpu.state.blend_set('ALPHA')\n\n                def transform(x, y):\n                    return (\n                        (x - 0.5) * 2 * ratio,\n                        (y - 0.5) * 2\n                    )\n\n                shader = gpu.shader.from_builtin(UNIFORM_COLOR)\n                batch = batch_for_shader(shader, 'TRI_STRIP', {\"pos\": [(-ratio, -1, 0), (-ratio, 1, 0), (ratio, -1, 0), (ratio, 1, 0)]})\n                shader.bind()\n                shader.uniform_float(\"color\", (0, 0, 0, 1))\n                batch.draw(shader)\n\n                for object in (context.scene.objects if collection is None else collection.objects):\n                    object = object.evaluated_get(context)\n                    if object.hide_render:\n                        continue\n                    if object.pose is None:\n                        continue\n                    for connection, color in lines.items():\n                        a, a_side = connection[0].identify(object.data, object.pose)\n                        b, b_side = connection[1].identify(object.data, object.pose)\n                        if a is None or b is None:\n                            continue\n                        a = bpy_extras.object_utils.world_to_camera_view(context.scene, context.scene.camera, object.matrix_world @ (a.tail if a_side == Side.TAIL else a.head))\n                        b = bpy_extras.object_utils.world_to_camera_view(context.scene, context.scene.camera, object.matrix_world @ (b.tail if b_side == Side.TAIL else b.head))\n                        draw_ellipse_2d(transform(a[0], a[1]), transform(b[0], b[1]), .015, 32, (color[0] / 255.0, color[1] / 255.0, color[2] / 255.0, 0.5))\n                    for b in Bone:\n                        bone, side = b.identify(object.data, object.pose)\n                        color = b.color()\n                        if bone is None: continue\n                        tail = bpy_extras.object_utils.world_to_camera_view(context.scene, context.scene.camera, object.matrix_world @ (bone.tail if side == Side.TAIL else bone.head))\n                        draw_circle_2d(transform(tail[0], tail[1]), .015, 16, (color[0] / 255.0, color[1] / 255.0, color[2] / 255.0, 0.5))\n\n            depth = np.array(fb.read_color(0, 0, width, height, 4, 0, 'FLOAT').to_list())\n        gpu.state.depth_test_set('NONE')\n        offscreen.free()\n        result = depth\n        e.set()\n    if threading.current_thread() == threading.main_thread():\n        _execute()\n        return result\n    else:\n        bpy.app.timers.register(_execute, first_interval=0)\n        e.wait()\n        return result\n\ndef draw_circle_2d(center, radius, segments, color):\n    m = (1.0 / (segments - 1)) * (math.pi * 2)\n\n    coords = [\n        (\n            center[0] + math.cos(m * p) * radius,\n            center[1] + math.sin(m * p) * radius,\n            0\n        )\n        for p in range(segments)\n    ]\n\n    shader = gpu.shader.from_builtin(UNIFORM_COLOR)\n    batch = batch_for_shader(shader, 'TRI_FAN', {\"pos\": coords})\n    shader.uniform_float(\"color\", color)\n    batch.draw(shader)\n\ndef draw_ellipse_2d(start, end, thickness, segments, color):\n    length = math.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)\n    theta = math.atan2(end[1] - start[1], end[0] - start[0])\n    center = (\n        (start[0] + end[0]) / 2,\n        (start[1] + end[1]) / 2\n    )\n    major, minor = length / 2, thickness\n    m = (1.0 / (segments - 1)) * (math.pi * 2)\n\n    coords = [\n        (\n            center[0] + major * math.cos(m * p) * math.cos(theta) - minor * math.sin(m * p) * math.sin(theta),\n            center[1] + major * math.cos(m * p) * math.sin(theta) + minor * math.sin(m * p) * math.cos(theta),\n            0\n        )\n        for p in range(segments)\n    ]\n\n    shader = gpu.shader.from_builtin(UNIFORM_COLOR)\n    batch = batch_for_shader(shader, 'TRI_FAN', {\"pos\": coords})\n    shader.uniform_float(\"color\", color)\n    batch.draw(shader)"
  },
  {
    "path": "engine/annotations/viewport.py",
    "content": "import bpy\nimport gpu\nimport numpy as np\nimport threading\n\ndef render_viewport_color(context, width=None, height=None, matrix=None, projection_matrix=None, main_thread=False):\n    e = threading.Event()\n    result = None\n\n    width, height = width or context.scene.render.resolution_x, height or context.scene.render.resolution_y\n    matrix = matrix or context.scene.camera.matrix_world.inverted()\n    projection_matrix = projection_matrix or context.scene.camera.calc_matrix_camera(\n        context,\n        x=width,\n        y=height\n    )\n\n    def _execute():\n        nonlocal result\n        offscreen = gpu.types.GPUOffScreen(width, height)\n\n        with offscreen.bind():\n            fb = gpu.state.active_framebuffer_get()\n            fb.clear(color=(0.0, 0.0, 0.0, 0.0), depth=1)\n            gpu.state.depth_test_set('LESS_EQUAL')\n            gpu.state.depth_mask_set(True)\n            with gpu.matrix.push_pop():\n                gpu.matrix.load_matrix(matrix)\n                gpu.matrix.load_projection_matrix(projection_matrix)\n                area = next(a for a in bpy.context.screen.areas if a.type == 'VIEW_3D')\n                offscreen.draw_view3d(\n                    context.scene,\n                    context.view_layer,\n                    next(s for s in area.spaces),\n                    next(r for r in area.regions if r.type == 'WINDOW'),\n                    matrix,\n                    projection_matrix,\n                    do_color_management=False\n                )\n            color = np.array(fb.read_color(0, 0, width, height, 4, 0, 'FLOAT').to_list())\n        gpu.state.depth_test_set('NONE')\n        offscreen.free()\n        result = color\n        e.set()\n    if main_thread or threading.current_thread() == threading.main_thread():\n        _execute()\n        return result\n    else:\n        bpy.app.timers.register(_execute, first_interval=0)\n        e.wait()\n        return result"
  },
  {
    "path": "engine/engine.py",
    "content": "import bpy\nimport gpu\nfrom bl_ui.properties_render import RenderButtonsPanel\nfrom bl_ui.properties_output import RenderOutputButtonsPanel\nfrom bl_ui.properties_view_layer import ViewLayerButtonsPanel\nimport numpy as np\nfrom ..ui.panels.dream_texture import optimization_panels\nfrom .node_tree import DreamTexturesNodeTree\nfrom ..engine import node_executor\nfrom .annotations import depth\nfrom ..property_groups.dream_prompt import backend_options\nfrom .nodes.pipeline_nodes import NodeStableDiffusion\nfrom .nodes.input_nodes import NodeRenderProperties\nfrom ..generator_process import actor\nfrom .. import image_utils\n\nclass DreamTexturesRenderEngine(bpy.types.RenderEngine):\n    \"\"\"A custom Dream Textures render engine, that uses Stable Diffusion and scene data to render images, instead of as a pass on top of Cycles.\"\"\"\n\n    bl_idname = \"DREAM_TEXTURES\"\n    bl_label = \"Dream Textures\"\n    bl_use_preview = False\n    bl_use_postprocess = True\n    bl_use_gpu_context = actor.main_thread_rendering\n\n    def __init__(self):\n        pass\n\n    def __del__(self):\n        pass\n\n    def render(self, depsgraph):\n        scene = depsgraph.scene\n        result = self.begin_result(0, 0, scene.render.resolution_x, scene.render.resolution_y)\n        layer = result.layers[0].passes[\"Combined\"]\n        self.update_result(result)\n\n        try:\n            progress = 0\n            def node_begin(node):\n                self.update_stats(\"Node\", node.label or node.name)\n            def node_update(response):\n                if isinstance(response, np.ndarray):\n                    try:\n                        image_utils.np_to_render_pass(response, layer, top_to_bottom=False)\n                        self.update_result(result)\n                    except:\n                        pass\n            def node_end(_):\n                nonlocal progress\n                progress += 1\n                self.update_progress(progress / len(scene.dream_textures_render_engine.node_tree.nodes))\n            group_outputs = node_executor.execute(scene.dream_textures_render_engine.node_tree, depsgraph, node_begin=node_begin, node_update=node_update, node_end=node_end, test_break=self.test_break)\n            node_result = group_outputs[0][1]\n            for k, v in group_outputs:\n                if type(v) == int or type(v) == str or type(v) == float:\n                    self.get_result().stamp_data_add_field(k, str(v))\n        except Exception as error:\n            self.report({'ERROR'}, str(error))\n            raise error\n\n        image_utils.np_to_render_pass(node_result, layer, top_to_bottom=False)\n\n        if \"Depth\" in result.layers[0].passes:\n            z = depth.render_depth_map(depsgraph, invert=True)\n            image_utils.np_to_render_pass(z, result.layers[0].passes[\"Depth\"], top_to_bottom=False)\n        \n        self.end_result(result)\n    \n    def update_render_passes(self, scene=None, renderlayer=None):\n        self.register_pass(scene, renderlayer, \"Combined\", 4, \"RGBA\", 'COLOR')\n        self.register_pass(scene, renderlayer, \"Depth\", 1, \"Z\", 'VALUE')\n\nclass NewEngineNodeTree(bpy.types.Operator):\n    bl_idname = \"dream_textures.new_engine_node_tree\"\n    bl_label = \"New Node Tree\"\n\n    def execute(self, context):\n        # TODO: Get the name of the default node tree from a config file\n        # TODO: type is deprecated https://docs.blender.org/api/current/bpy.types.NodeTree.html \n        #       When testing the type of the resulting node tree using  bpy.data.node_groups['Dream Textures Node Tree'].type it is '' because of that\n        bpy.ops.node.new_node_tree(type=DreamTexturesNodeTree.bl_idname, name=\"Dream Textures Node Editor\")\n        # Get the newly generated node tree\n        node_tree = bpy.data.node_groups[-1]\n        node_sd = node_tree.nodes.new(type=NodeStableDiffusion.bl_idname)\n        node_sd.location = (200, 200)\n        node_out = node_tree.nodes.new(type=\"NodeGroupOutput\")\n        # Blender 4.0 uses a new API for in- and outputs\n        if bpy.app.version[0] > 3:\n            node_tree.interface.new_socket('Image', description=\"Output of the final image.\", in_out='OUTPUT', socket_type='NodeSocketColor')\n        else:\n            node_tree.outputs.new('NodeSocketColor','Image')\n        node_out.location = (400, 200)\n        node_tree.links.new(node_sd.outputs['Image'], node_out.inputs['Image'])\n        node_props = node_tree.nodes.new(type=NodeRenderProperties.bl_idname)\n        node_props.location = (0,200)\n        node_tree.links.new(node_props.outputs['Resolution X'], node_sd.inputs['Width'])\n        node_tree.links.new(node_props.outputs['Resolution Y'], node_sd.inputs['Height'])\n        # in case the node editor is open, synchronize the open node trees:\n        for area in context.screen.areas:\n            if area.type == 'NODE_EDITOR':\n                if area.spaces.active.tree_type == DreamTexturesNodeTree.bl_idname:\n                    area.spaces.active.node_tree = node_tree\n        return {'FINISHED'}\n\ndef draw_device(self, context):\n    scene = context.scene\n    layout = self.layout\n    layout.use_property_split = True\n    layout.use_property_decorate = False\n\n    if context.engine == DreamTexturesRenderEngine.bl_idname:\n        layout.template_ID(scene.dream_textures_render_engine, \"node_tree\", text=\"Node Tree\", new=NewEngineNodeTree.bl_idname)\n        layout.prop(scene.dream_textures_render_engine, \"backend\")\n\ndef _poll_node_tree(self, value):\n    return value.bl_idname == \"DreamTexturesNodeTree\"\n\ndef _update_engine_backend(self, context):\n    if self.node_tree is not None:\n        for node in self.node_tree.nodes:\n            if node.bl_idname == NodeStableDiffusion.bl_idname:\n                node.prompt.backend = self.backend\n    context.scene.dream_textures_engine_prompt.backend = self.backend\n\nclass DreamTexturesRenderEngineProperties(bpy.types.PropertyGroup):\n    node_tree: bpy.props.PointerProperty(type=DreamTexturesNodeTree, name=\"Node Tree\", poll=_poll_node_tree)\n    backend: bpy.props.EnumProperty(name=\"Backend\", items=backend_options, default=0, description=\"The backend to use for all pipeline nodes\", update=_update_engine_backend)\n\ndef engine_panels():\n    bpy.types.RENDER_PT_output.COMPAT_ENGINES.add(DreamTexturesRenderEngine.bl_idname)\n    bpy.types.RENDER_PT_color_management.COMPAT_ENGINES.add(DreamTexturesRenderEngine.bl_idname)\n    bpy.types.RENDER_PT_stamp.COMPAT_ENGINES.add(DreamTexturesRenderEngine.bl_idname)\n    bpy.types.RENDER_PT_format.COMPAT_ENGINES.add(DreamTexturesRenderEngine.bl_idname)\n    bpy.types.DATA_PT_lens.COMPAT_ENGINES.add(DreamTexturesRenderEngine.bl_idname)\n    def get_prompt(context):\n        return context.scene.dream_textures_engine_prompt\n    class RenderPanel(bpy.types.Panel, RenderButtonsPanel):\n        COMPAT_ENGINES = {DreamTexturesRenderEngine.bl_idname}\n\n        def draw(self, context):\n            self.layout.use_property_decorate = True\n    class OutputPanel(bpy.types.Panel, RenderOutputButtonsPanel):\n        COMPAT_ENGINES = {DreamTexturesRenderEngine.bl_idname}\n\n        def draw(self, context):\n            self.layout.use_property_decorate = True\n    \n    class ViewLayerPanel(bpy.types.Panel, ViewLayerButtonsPanel):\n        COMPAT_ENGINES = {DreamTexturesRenderEngine.bl_idname}\n\n        def draw(self, context):\n            pass\n\n    # Render Properties\n    yield from optimization_panels(RenderPanel, 'engine', get_prompt, \"\")\n\n    class NodeTreeInputsPanel(RenderPanel):\n        \"\"\"Create a subpanel for format options\"\"\"\n        bl_idname = f\"DREAM_PT_dream_panel_node_tree_inputs_engine\"\n        bl_label = \"Inputs\"\n\n        def draw(self, context):\n            super().draw(context)\n            layout = self.layout\n            layout.use_property_split = True\n\n            node_tree = context.scene.dream_textures_render_engine.node_tree\n            if node_tree is not None and hasattr(node_tree, \"inputs\"):\n                for input in node_tree.inputs:\n                    layout.prop(input, \"default_value\", text=input.name)\n    yield NodeTreeInputsPanel\n\n    # View Layer\n    class ViewLayerPassesPanel(ViewLayerPanel):\n        bl_idname = \"DREAM_PT_dream_panel_view_layer_passes\"\n        bl_label = \"Passes\"\n\n        def draw(self, context):\n            layout = self.layout\n            layout.use_property_split = True\n            layout.use_property_decorate = False\n\n            view_layer = context.view_layer\n\n            col = layout.column()\n            col.prop(view_layer, \"use_pass_combined\")\n            col.prop(view_layer, \"use_pass_z\")\n            col.prop(view_layer, \"use_pass_normal\")\n    yield ViewLayerPassesPanel\n\n    # Bone properties\n    class OpenPoseArmaturePanel(bpy.types.Panel):\n        bl_idname = \"DREAM_PT_dream_textures_armature_openpose\"\n        bl_label = \"OpenPose\"\n        bl_space_type = 'PROPERTIES'\n        bl_region_type = 'WINDOW'\n        bl_context = \"data\"\n\n        @classmethod\n        def poll(cls, context):\n            return context.armature\n        \n        def draw_header(self, context):\n            bone = context.bone or context.edit_bone\n            if bone:\n                self.layout.prop(bone.dream_textures_openpose, \"enabled\", text=\"\")\n\n        def draw(self, context):\n            layout = self.layout\n\n            armature = context.armature\n\n            p = armature.dream_textures_openpose\n\n            row = layout.row()\n            row.prop(p, \"EAR_L\", toggle=True)\n            row.prop(p, \"EYE_L\", toggle=True)\n            row.prop(p, \"EYE_R\", toggle=True)\n            row.prop(p, \"EAR_R\", toggle=True)\n            layout.prop(p, \"NOSE\", toggle=True)\n            row = layout.row()\n            row.prop(p, \"SHOULDER_L\", toggle=True)\n            row.prop(p, \"CHEST\", toggle=True)\n            row.prop(p, \"SHOULDER_R\", toggle=True)\n            row = layout.row()\n            row.prop(p, \"ELBOW_L\", toggle=True)\n            row.separator()\n            row.prop(p, \"HIP_L\", toggle=True)\n            row.prop(p, \"HIP_R\", toggle=True)\n            row.separator()\n            row.prop(p, \"ELBOW_R\", toggle=True)\n            row = layout.row()\n            row.prop(p, \"HAND_L\", toggle=True)\n            row.separator()\n            row.prop(p, \"KNEE_L\", toggle=True)\n            row.prop(p, \"KNEE_R\", toggle=True)\n            row.separator()\n            row.prop(p, \"HAND_R\", toggle=True)\n            row = layout.row()\n            row.prop(p, \"FOOT_L\", toggle=True)\n            row.prop(p, \"FOOT_R\", toggle=True)\n\n    yield OpenPoseArmaturePanel\n    class OpenPoseBonePanel(bpy.types.Panel):\n        bl_idname = \"DREAM_PT_dream_textures_bone_openpose\"\n        bl_label = \"OpenPose\"\n        bl_space_type = 'PROPERTIES'\n        bl_region_type = 'WINDOW'\n        bl_context = \"bone\"\n\n        @classmethod\n        def poll(cls, context):\n            return context.bone and context.scene.render.engine == 'DREAM_TEXTURES'\n        \n        def draw_header(self, context):\n            bone = context.bone\n            if bone:\n                self.layout.prop(bone.dream_textures_openpose, \"enabled\", text=\"\")\n\n        def draw(self, context):\n            layout = self.layout\n            layout.use_property_split = True\n\n            bone = context.bone\n\n            layout.enabled = bone.dream_textures_openpose.enabled\n            layout.prop(bone.dream_textures_openpose, \"bone\")\n            layout.prop(bone.dream_textures_openpose, \"side\")\n\n    yield OpenPoseBonePanel\n\n    class ADE20KObjectPanel(bpy.types.Panel):\n        bl_idname = \"DREAM_PT_dream_textures_object_ade20k\"\n        bl_label = \"ADE20K Segmentation\"\n        bl_space_type = 'PROPERTIES'\n        bl_region_type = 'WINDOW'\n        bl_context = \"object\"\n\n        @classmethod\n        def poll(cls, context):\n            return context.object and context.scene.render.engine == 'DREAM_TEXTURES'\n        \n        def draw_header(self, context):\n            object = context.object\n            if object:\n                self.layout.prop(object.dream_textures_ade20k, \"enabled\", text=\"\")\n\n        def draw(self, context):\n            layout = self.layout\n            layout.use_property_split = True\n\n            object = context.object\n\n            layout.enabled = object.dream_textures_ade20k.enabled\n            r = layout.split(factor=0.9)\n            r.prop(object.dream_textures_ade20k, \"annotation\")\n            c = r.column()\n            c.enabled = False\n            c.prop(object.dream_textures_ade20k, \"color\")\n\n    yield ADE20KObjectPanel"
  },
  {
    "path": "engine/node.py",
    "content": "import bpy\nfrom .node_tree import DreamTexturesNodeTree\n\nclass DreamTexturesNode(bpy.types.Node):\n    @classmethod\n    def poll(cls, tree):\n        return tree.bl_idname == DreamTexturesNodeTree.bl_idname"
  },
  {
    "path": "engine/node_executor.py",
    "content": "import bpy\n# from dream_textures.engine import node_executor\n# node_executor.execute(bpy.data.node_groups[\"NodeTree\"], bpy.context.evaluated_depsgraph_get())\n\nclass NodeExecutionContext:\n    def __init__(self, depsgraph, start, update, end, test_break, cache={}):\n        self.depsgraph = depsgraph\n        self.start = start\n        self.update = update\n        self.end = end\n        self.test_break = test_break\n        self.cache = {}\n        self.preferences = bpy.context.preferences\n    \n    def _evaluate_input(self, input):\n        if input.is_linked:\n            if len(input.links) > 1:\n                return [\n                    self.execute(link.from_socket.node)[link.from_socket.name]\n                    for link in input.links\n                ]\n            else:\n                return self.execute(input.links[0].from_socket.node)[input.links[0].from_socket.name]\n        else:\n            return getattr(input, 'default_value', None)\n\n    def execute(self, node):\n        if self.test_break():\n            return None\n        result = None\n        match node.bl_idname:\n            case 'dream_textures.node_switch':\n                kwargs = {\n                    'switch': self._evaluate_input(node.inputs[0]),\n                    'false': lambda: self._evaluate_input(node.inputs[1]),\n                    'true': lambda: self._evaluate_input(node.inputs[2])\n                }\n                self.start(node)\n                result = node.execute(self, **kwargs)\n            case _:\n                match node.type:\n                    case 'GROUP_INPUT':\n                        self.start(node)\n                        result = {\n                            input.name: input.default_value\n                            for input in self.depsgraph.scene.dream_textures_render_engine.node_tree.inputs\n                        }\n                    case 'GROUP_OUTPUT':\n                        self.start(node)\n                        result = [\n                            (input.name, self.execute(input.links[0].from_socket.node)[input.links[0].from_socket.name])\n                            for input in node.inputs if len(input.links) > 0\n                        ]\n                    case 'FRAME':\n                        self.start(node)\n                        result = None\n                    case _:\n                        if node in self.cache:\n                            self.start(node)\n                            result = self.cache[node]\n                        else:\n                            kwargs = {\n                                input.name.lower().replace(' ', '_'): self._evaluate_input(input)\n                                for input in node.inputs\n                            }\n                            self.start(node)\n                            result = node.execute(self, **kwargs)\n        self.end(node)\n        return result\n\ndef execute(node_tree, depsgraph, node_begin=lambda node: None, node_update=lambda result: None, node_end=lambda node: None, test_break=lambda: False):\n    output = next(n for n in node_tree.nodes if n.type == 'GROUP_OUTPUT')\n    context = NodeExecutionContext(depsgraph, node_begin, node_update, node_end, test_break)\n    return context.execute(output)"
  },
  {
    "path": "engine/node_tree.py",
    "content": "import bpy\n\nclass DreamTexturesNodeTree(bpy.types.NodeTree):\n    bl_idname = \"DreamTexturesNodeTree\"\n    bl_label = \"Dream Textures Node Editor\"\n    bl_description = \"Nodes for the Dream Textures Render Engine\"\n    bl_icon = 'NODETREE'\n\n    @classmethod\n    def poll(cls, context):\n        return context.scene.render.engine == 'DREAM_TEXTURES'"
  },
  {
    "path": "engine/nodes/annotation_nodes.py",
    "content": "import bpy\nfrom ..node import DreamTexturesNode\nfrom ..annotations import depth\nfrom ..annotations import normal\nfrom ..annotations import openpose\nfrom ..annotations import ade20k\nfrom ..annotations import viewport\nimport numpy as np\n\nannotation_src = (\n    ('collection', 'Collection', 'Render the annotation for a specific collection'),\n    ('scene', 'Scene', 'Render the annotation for the entire scene'),\n)\n\ndef _update_annotation_inputs(self, context):\n    inputs = {socket.name: socket for socket in self.inputs}\n    inputs['Collection'].enabled = self.src == 'collection'\n\nclass NodeAnnotationDepth(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_annotation_depth\"\n    bl_label = \"Depth Map\"\n\n    src: bpy.props.EnumProperty(name=\"\", items=annotation_src, update=_update_annotation_inputs)\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketCollection\", \"Collection\")\n        self.inputs.new(\"NodeSocketBool\", \"Invert\")\n\n        self.outputs.new(\"NodeSocketColor\", \"Depth Map\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"src\")\n\n    def execute(self, context, collection, invert):\n        depth_map = depth.render_depth_map(context.depsgraph, collection=collection if self.src == 'collection' else None, invert=invert)\n        context.update(depth_map)\n        return {\n            'Depth Map': depth_map,\n        }\n\nclass NodeAnnotationNormal(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_annotation_normal\"\n    bl_label = \"Normal Map\"\n\n    src: bpy.props.EnumProperty(name=\"\", items=annotation_src, update=_update_annotation_inputs)\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketCollection\", \"Collection\")\n\n        self.outputs.new(\"NodeSocketColor\", \"Normal Map\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"src\")\n\n    def execute(self, context, collection):\n        normal_map = normal.render_normal_map(context.depsgraph, collection=collection if self.src == 'collection' else None)\n        context.update(normal_map)\n        return {\n            'Normal Map': normal_map,\n        }\n\nclass NodeAnnotationOpenPose(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_annotation_openpose\"\n    bl_label = \"OpenPose Map\"\n\n    src: bpy.props.EnumProperty(name=\"\", items=annotation_src, update=_update_annotation_inputs)\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketCollection\", \"Collection\")\n\n        self.outputs.new(\"NodeSocketColor\", \"OpenPose Map\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"src\")\n\n    def execute(self, context, collection):\n        openpose_map = openpose.render_openpose_map(context.depsgraph, collection=collection if self.src == 'collection' else None)\n        context.update(openpose_map)\n        return {\n            'OpenPose Map': openpose_map\n        }\n\nclass NodeAnnotationADE20K(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_annotation_ade20k\"\n    bl_label = \"ADE20K Segmentation Map\"\n\n    src: bpy.props.EnumProperty(name=\"\", items=annotation_src, update=_update_annotation_inputs)\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketCollection\", \"Collection\")\n\n        self.outputs.new(\"NodeSocketColor\", \"Segmentation Map\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"src\")\n\n    def execute(self, context, collection):\n        ade20k_map = ade20k.render_ade20k_map(context.depsgraph, collection=collection if self.src == 'collection' else None)\n        context.update(ade20k_map)\n        return {\n            'Segmentation Map': ade20k_map\n        }\n\nclass NodeAnnotationViewport(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_annotation_viewport\"\n    bl_label = \"Viewport Color\"\n\n    def init(self, context):\n        self.outputs.new(\"NodeSocketColor\", \"Viewport Color\")\n\n    def draw_buttons(self, context, layout):\n        pass\n\n    def execute(self, context):\n        color = viewport.render_viewport_color(context.depsgraph)\n        context.update(color)\n        return {\n            'Viewport Color': color\n        }"
  },
  {
    "path": "engine/nodes/input_nodes.py",
    "content": "import bpy\nimport gpu\nfrom gpu_extras.batch import batch_for_shader\nimport numpy as np\nfrom ..node import DreamTexturesNode\nfrom ..annotations import openpose\nfrom ..annotations import depth\nfrom ... import image_utils\n\nclass NodeString(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_string\"\n    bl_label = \"String\"\n\n    value: bpy.props.StringProperty(name=\"\")\n\n    def init(self, context):\n        self.outputs.new(\"NodeSocketString\", \"String\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"value\")\n\n    def execute(self, context):\n        return {\n            'String': self.value\n        }\n\nclass NodeInteger(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_integer\"\n    bl_label = \"Integer\"\n\n    value: bpy.props.IntProperty(name=\"\")\n\n    def init(self, context):\n        self.outputs.new(\"NodeSocketInt\", \"Integer\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"value\")\n\n    def execute(self, context):\n        return {\n            'Integer': self.value\n        }\n\nclass NodeCollection(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_collection\"\n    bl_label = \"Collection\"\n\n    value: bpy.props.PointerProperty(type=bpy.types.Collection, name=\"\")\n\n    def init(self, context):\n        self.outputs.new(\"NodeSocketCollection\", \"Collection\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"value\")\n\n    def execute(self, context):\n        return {\n            'Collection': self.value\n        }\n\nclass NodeImage(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_image\"\n    bl_label = \"Image\"\n\n    value: bpy.props.PointerProperty(type=bpy.types.Image)\n\n    def init(self, context):\n        self.outputs.new(\"NodeSocketColor\", \"Image\")\n\n    def draw_buttons(self, context, layout):\n        layout.template_ID(self, \"value\", open=\"image.open\")\n        if self.value is not None:\n            layout.prop(self.value.colorspace_settings, \"name\", text=\"Color Space\")\n\n    def execute(self, context):\n        result = image_utils.bpy_to_np(self.value, color_space=\"Linear\", top_to_bottom=False)\n        context.update(result)\n        return {\n            'Image': result\n        }\n\nclass NodeImageFile(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_image_file\"\n    bl_label = \"Image File\"\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketString\", \"Path\")\n\n        self.outputs.new(\"NodeSocketColor\", \"Image\")\n\n    def draw_buttons(self, context, layout):\n        pass\n\n    def execute(self, context, path):\n        pixels = image_utils.image_to_np(path, default_color_space=\"sRGB\", to_color_space=\"Linear\", top_to_bottom=False)\n        context.update(pixels)\n        return {\n            'Image': pixels\n        }\n\nclass NodeRenderProperties(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_render_properties\"\n    bl_label = \"Render Properties\"\n\n    def init(self, context):\n        self.outputs.new(\"NodeSocketInt\", \"Resolution X\")\n        self.outputs.new(\"NodeSocketInt\", \"Resolution Y\")\n        self.outputs.new(\"NodeSocketString\", \"Output Filepath\")\n        self.outputs.new(\"NodeSocketInt\", \"Frame\")\n\n    def draw_buttons(self, context, layout):\n        pass\n\n    def execute(self, context):\n        return {\n            'Resolution X': context.depsgraph.scene.render.resolution_x,\n            'Resolution Y': context.depsgraph.scene.render.resolution_y,\n            'Output Filepath': context.depsgraph.scene.render.filepath,\n            'Frame': context.depsgraph.scene.frame_current\n        }"
  },
  {
    "path": "engine/nodes/pipeline_nodes.py",
    "content": "import bpy\nimport numpy as np\nfrom dataclasses import dataclass\nfrom typing import Any, List\nimport enum\nfrom ..node import DreamTexturesNode\nfrom ...generator_process import Generator\nfrom ...property_groups.control_net import control_net_options\nfrom ...property_groups.dream_prompt import DreamPrompt\nfrom ..annotations import openpose\nfrom ..annotations import depth\nfrom ..annotations import normal\nfrom ..annotations import ade20k\nfrom ... import api\nfrom ...property_groups.seamless_result import SeamlessAxes\nimport threading\nfrom ... import image_utils\n\nclass NodeSocketControlNet(bpy.types.NodeSocket):\n    bl_idname = \"NodeSocketControlNet\"\n    bl_label = \"ControlNet Socket\"\n\n    def __init__(self):\n        self.link_limit = 0\n\n    def draw(self, context, layout, node, text):\n        layout.label(text=text)\n\n    def draw_color(self, context, node):\n        return (0.63, 0.63, 0.63, 1)\n\nclass ControlType(enum.IntEnum):\n    DEPTH = 1\n    OPENPOSE = 2\n    NORMAL = 3\n    ADE20K_SEGMENTATION = 4\n\n@dataclass\nclass ControlNet:\n    model: str\n    image: Any\n    collection: Any\n    control_type: ControlType\n    conditioning_scale: float\n\n    def control(self, context):\n        if self.image is not None:\n            return np.flipud(self.image)\n        else:\n            match self.control_type:\n                case ControlType.DEPTH:\n                    return np.flipud(depth.render_depth_map(context, collection=self.collection))\n                case ControlType.OPENPOSE:\n                    return np.flipud(openpose.render_openpose_map(context, collection=self.collection))\n                case ControlType.NORMAL:\n                    return np.flipud(normal.render_normal_map(context, collection=self.collection))\n                case ControlType.ADE20K_SEGMENTATION:\n                    return np.flipud(ade20k.render_ade20k_map(context, collection=self.collection))\n\ndef _update_stable_diffusion_sockets(self, context):\n    inputs = {socket.name: socket for socket in self.inputs}\n    inputs['Source Image'].enabled = self.task in {'image_to_image', 'depth_to_image', 'inpaint'}\n    inputs['Noise Strength'].enabled = self.task in {'image_to_image', 'depth_to_image'}\n    if self.task == 'depth_to_image':\n        inputs['Noise Strength'].default_value = 1.0\n    inputs['Depth Map'].enabled = self.task == 'depth_to_image'\n    inputs['ControlNets'].enabled = self.task != 'depth_to_image'\nclass NodeStableDiffusion(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_stable_diffusion\"\n    bl_label = \"Stable Diffusion\"\n\n    prompt: bpy.props.PointerProperty(type=DreamPrompt)\n    task: bpy.props.EnumProperty(name=\"\", items=(\n        ('prompt_to_image', 'Prompt to Image', '', 1),\n        ('image_to_image', 'Image to Image', '', 2),\n        ('depth_to_image', 'Depth to Image', '', 3),\n        ('inpaint', 'Inpaint', '', 4),\n    ), update=_update_stable_diffusion_sockets)\n\n    def update(self):\n        self.prompt.backend = bpy.context.scene.dream_textures_render_engine.backend\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketColor\", \"Depth Map\")\n        self.inputs.new(\"NodeSocketColor\", \"Source Image\")\n        self.inputs.new(\"NodeSocketFloat\", \"Noise Strength\").default_value = 0.75\n\n        self.inputs.new(\"NodeSocketString\", \"Prompt\")\n        self.inputs.new(\"NodeSocketString\", \"Negative Prompt\")\n\n        self.inputs.new(\"NodeSocketInt\", \"Width\").default_value = 512\n        self.inputs.new(\"NodeSocketInt\", \"Height\").default_value = 512\n        \n        self.inputs.new(\"NodeSocketInt\", \"Steps\").default_value = 25\n        self.inputs.new(\"NodeSocketInt\", \"Seed\")\n        self.inputs.new(\"NodeSocketFloat\", \"CFG Scale\").default_value = 7.50\n        \n        self.inputs.new(\"NodeSocketControlNet\", \"ControlNets\")\n\n        self.outputs.new(\"NodeSocketColor\", \"Image\")\n\n        _update_stable_diffusion_sockets(self, context)\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"task\")\n        prompt = self.prompt\n        layout.prop(prompt, \"model\", text=\"\")\n        layout.prop(prompt, \"scheduler\", text=\"\")\n        layout.prop(prompt, \"seamless_axes\", text=\"\")\n    \n    def execute(self, context, prompt, negative_prompt, width, height, steps, seed, cfg_scale, controlnets, depth_map, source_image, noise_strength):\n        backend: api.Backend = self.prompt.get_backend()\n\n        if np.array(source_image).shape == (4,):\n            # the source image is a default color, ignore it.\n            source_image = None\n        else:\n            source_image = image_utils.color_transform(np.flipud(source_image), \"Linear\", \"sRGB\")\n\n        def get_task():\n            match self.task:\n                case 'prompt_to_image':\n                    return api.PromptToImage()\n                case 'image_to_image':\n                    return api.ImageToImage(source_image, noise_strength, fit=False)\n                case 'depth_to_image':\n                    return api.DepthToImage(image_utils.grayscale(depth_map), source_image, noise_strength)\n                case 'inpaint':\n                    return api.Inpaint(source_image, noise_strength, fit=False, mask_source=api.Inpaint.MaskSource.ALPHA, mask_prompt=\"\", confidence=0)\n        \n        def map_controlnet(c):\n            return api.models.control_net.ControlNet(c.model, c.control(context.depsgraph), c.conditioning_scale)\n\n        args = api.GenerationArguments(\n            get_task(),\n            model=next(model for model in self.prompt.get_backend().list_models(context) if model is not None and model.id == self.prompt.model),\n            prompt=api.Prompt(\n                prompt,\n                negative_prompt\n            ),\n            size=(width, height),\n            seed=seed,\n            steps=steps,\n            guidance_scale=cfg_scale,\n            scheduler=self.prompt.scheduler,\n            seamless_axes=SeamlessAxes(self.prompt.seamless_axes),\n            step_preview_mode=api.models.StepPreviewMode.FAST,\n            iterations=1,\n            control_nets=[map_controlnet(c) for c in controlnets] if isinstance(controlnets, list) else ([map_controlnet(controlnets)] if controlnets is not None else [])\n        )\n        \n        event = threading.Event()\n        result = None\n        exception = None\n        def step_callback(progress: List[api.GenerationResult]) -> bool:\n            context.update(image_utils.image_to_np(progress[-1].image, default_color_space=\"sRGB\", to_color_space=\"Linear\", top_to_bottom=False))\n            return True\n            # if context.test_break():\n            #     nonlocal result\n            #     result = [response]\n            #     event.set()\n\n        def callback(results: List[api.GenerationResult] | Exception):\n            if isinstance(results, Exception):\n                nonlocal exception\n                exception = results\n                event.set()\n            else:\n                nonlocal result\n                result = image_utils.image_to_np(results[-1].image, default_color_space=\"sRGB\", to_color_space=\"Linear\", top_to_bottom=False)\n                event.set()\n        \n        backend = self.prompt.get_backend()\n        backend.generate(args, step_callback=step_callback, callback=callback)\n\n        event.wait()\n        if exception is not None:\n            raise exception\n        return {\n            'Image': result\n        }\n\ndef _update_control_net_sockets(self, context):\n    inputs = {socket.name: socket for socket in self.inputs}\n    inputs['Collection'].enabled = self.input_type == 'collection'\n    inputs['Image'].enabled = self.input_type == 'image'\nclass NodeControlNet(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_control_net\"\n    bl_label = \"ControlNet\"\n\n    control_net: bpy.props.EnumProperty(name=\"\", items=control_net_options)\n    input_type: bpy.props.EnumProperty(name=\"\", items=(\n        ('collection', 'Collection', '', 1),\n        ('image', 'Image', '', 2),\n    ), update=_update_control_net_sockets)\n    control_type: bpy.props.EnumProperty(name=\"\", items=(\n        ('DEPTH', 'Depth', '', 1),\n        ('OPENPOSE', 'OpenPose', '', 2),\n        ('NORMAL', 'Normal Map', '', 3),\n        ('ADE20K_SEGMENTATION', 'ADE20K Segmentation', '', 4),\n    ))\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketCollection\", \"Collection\")\n        self.inputs.new(\"NodeSocketColor\", \"Image\")\n        self.inputs.new(\"NodeSocketFloat\", \"Conditioning Scale\").default_value = 1\n\n        self.outputs.new(NodeSocketControlNet.bl_idname, \"Control\")\n\n        _update_control_net_sockets(self, context)\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"control_net\")\n        layout.prop(self, \"input_type\")\n        if self.input_type != 'image':\n            layout.prop(self, \"control_type\")\n    \n    def execute(self, context, collection, image, conditioning_scale):\n        return {\n            'Control': ControlNet(\n                self.control_net,\n                image if self.input_type == 'image' else None,\n                collection if self.input_type == 'collection' else None,\n                ControlType[self.control_type],\n                conditioning_scale\n            )\n        }"
  },
  {
    "path": "engine/nodes/utility_nodes.py",
    "content": "import bpy\nimport numpy as np\nimport random\nfrom ..node import DreamTexturesNode\nfrom ...property_groups.dream_prompt import seed_clamp\nfrom ... import image_utils\n\nclass NodeMath(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_math\"\n    bl_label = \"Math\"\n\n    operation: bpy.props.EnumProperty(\n        name=\"Operation\",\n        items=(\n            (\"add\", \"Add\", \"\"),\n            (\"subtract\", \"Subtract\", \"\"),\n            (\"multiply\", \"Multiply\", \"\"),\n            (\"divide\", \"Divide\", \"\"),\n        )\n    )\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketFloat\", \"A\")\n        self.inputs.new(\"NodeSocketFloat\", \"B\")\n\n        self.outputs.new(\"NodeSocketFloat\", \"Value\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"operation\", text=\"\")\n\n    def perform(self, a, b):\n        match self.operation:\n            case 'add':\n                return a + b\n            case 'subtract':\n                return a - b\n            case 'multiply':\n                return a * b\n            case 'divide':\n                return a / b\n\n    def execute(self, context, a, b):\n        return {\n            'Value': self.perform(a, b)\n        }\n\nclass NodeRandomValue(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_random_value\"\n    bl_label = \"Random Value\"\n\n    data_type: bpy.props.EnumProperty(name=\"\", items=(\n        ('integer', 'Integer', '', 1),\n    ))\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketInt\", \"Min\")\n        self.inputs.new(\"NodeSocketInt\", \"Max\").default_value = np.iinfo(np.int32).max\n        \n        self.outputs.new(\"NodeSocketInt\", \"Value\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"data_type\")\n\n    def execute(self, context, min, max):\n        return {\n            'Value': random.randrange(min, max)\n        }\n\nclass NodeRandomSeed(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_random_seed\"\n    bl_label = \"Random Seed\"\n\n    def init(self, context):\n        self.outputs.new(\"NodeSocketInt\", \"Value\")\n\n    def draw_buttons(self, context, layout):\n        pass\n\n    def execute(self, context):\n        return {\n            'Value': random.randrange(0, np.iinfo(np.uint32).max)\n        }\n\nclass NodeSeed(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_seed\"\n    bl_label = \"Seed\"\n\n    seed: bpy.props.StringProperty(name=\"\", default=\"\", update=seed_clamp)\n\n    def init(self, context):\n        self.outputs.new(\"NodeSocketInt\", \"Value\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"seed\")\n\n    def execute(self, context):\n        return {\n            'Value': int(self.seed)\n        }\n\nclass NodeClamp(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_clamp\"\n    bl_label = \"Clamp\"\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketFloat\", \"Value\")\n        self.inputs.new(\"NodeSocketFloat\", \"Min\")\n        self.inputs.new(\"NodeSocketFloat\", \"Max\")\n\n        self.outputs.new(\"NodeSocketFloat\", \"Result\")\n\n    def draw_buttons(self, context, layout):\n        pass\n\n    def execute(self, context, value, min, max):\n        return {\n            'Result': np.clip(value, min, max)\n        }\n\nclass NodeFramePath(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_frame_path\"\n    bl_label = \"Frame Path\"\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketInt\", \"Frame\")\n\n        self.outputs.new(\"NodeSocketString\", \"Frame Path\")\n\n    def draw_buttons(self, context, layout):\n        pass\n\n    def execute(self, context, frame):\n        return {\n            'Frame Path': context.depsgraph.scene.render.frame_path(frame=int(frame)),\n        }\n\nclass NodeCropImage(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_crop_image\"\n    bl_label = \"Crop Image\"\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketColor\", \"Image\")\n        self.inputs.new(\"NodeSocketInt\", \"X\")\n        self.inputs.new(\"NodeSocketInt\", \"Y\")\n        self.inputs.new(\"NodeSocketInt\", \"Width\")\n        self.inputs.new(\"NodeSocketInt\", \"Height\")\n\n        self.outputs.new(\"NodeSocketColor\", \"Cropped Image\")\n\n    def draw_buttons(self, context, layout):\n        pass\n\n    def execute(self, context, image, x, y, width, height):\n        x, y = int(x), int(y)\n        width, height = int(width), int(height)\n        result = image[y:y+height, x:x+width, ...]\n        context.update(result)\n        return {\n            'Cropped Image': result,\n        }\n\nclass NodeResizeImage(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_resize_image\"\n    bl_label = \"Resize Image\"\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketColor\", \"Image\")\n        self.inputs.new(\"NodeSocketInt\", \"Width\")\n        self.inputs.new(\"NodeSocketInt\", \"Height\")\n\n        self.outputs.new(\"NodeSocketColor\", \"Resized Image\")\n\n    def draw_buttons(self, context, layout):\n        pass\n\n    def execute(self, context, image, width, height):\n        result = image_utils.resize(image, (width, height))\n        context.update(result)\n        return {\n            'Resized Image': result,\n        }\n\nclass NodeJoinImages(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_join_images\"\n    bl_label = \"Join Images\"\n\n    direction: bpy.props.EnumProperty(name=\"\", items=(\n        ('horizontal', 'Horizontal', ''),\n        ('vertical', 'Vertical', ''),\n    ))\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketColor\", \"A\")\n        self.inputs.new(\"NodeSocketColor\", \"B\")\n\n        self.outputs.new(\"NodeSocketColor\", \"Joined Images\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"direction\")\n\n    def execute(self, context, a, b):\n        match self.direction:\n            case 'horizontal':\n                result = np.hstack([a, b])\n            case 'vertical':\n                result = np.vstack([a, b])\n        context.update(result)\n        return {\n            'Joined Images': result,\n        }\n\nclass NodeSeparateColor(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_separate_color\"\n    bl_label = \"Separate Color\"\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketColor\", \"Color\")\n\n        self.outputs.new(\"NodeSocketFloat\", \"Red\")\n        self.outputs.new(\"NodeSocketFloat\", \"Green\")\n        self.outputs.new(\"NodeSocketFloat\", \"Blue\")\n        self.outputs.new(\"NodeSocketFloat\", \"Alpha\")\n\n    def draw_buttons(self, context, layout):\n        pass\n\n    def execute(self, context, color):\n        return {\n            'Red': color[..., 0],\n            'Green': color[..., 1] if color.shape[-1] > 1 else 0,\n            'Blue': color[..., 2] if color.shape[-1] > 2 else 0,\n            'Alpha': color[..., 3] if color.shape[-1] > 3 else 0,\n        }\n\nclass NodeCombineColor(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_combine_color\"\n    bl_label = \"Combine Color\"\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketFloat\", \"Red\")\n        self.inputs.new(\"NodeSocketFloat\", \"Green\")\n        self.inputs.new(\"NodeSocketFloat\", \"Blue\")\n        self.inputs.new(\"NodeSocketFloat\", \"Alpha\")\n\n        self.outputs.new(\"NodeSocketColor\", \"Color\")\n\n    def draw_buttons(self, context, layout):\n        pass\n\n    def execute(self, context, red, green, blue, alpha):\n        return {\n            'Color': np.stack([red, green, blue, alpha], axis=-1)\n        }\n\nclass NodeColorCorrect(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_color_correct\"\n    bl_label = \"Color Correct\"\n\n    mode: bpy.props.EnumProperty(name=\"Mode\", items=(\n        ('histogram', 'Match Histograms', ''),\n    ))\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketColor\", \"Image\")\n        self.inputs.new(\"NodeSocketColor\", \"Target\")\n\n        self.outputs.new(\"NodeSocketColor\", \"Image\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"mode\", text=\"\")\n\n    def execute(self, context, image, target):\n        match self.mode:\n            case 'histogram':\n                flat_image = image.ravel()\n                flat_target = target.ravel()\n\n                _, image_indices, image_counts = np.unique(flat_image, return_inverse=True, return_counts=True)\n                target_values, target_counts = np.unique(flat_target, return_counts=True)\n                \n                image_quantiles = np.cumsum(image_counts).astype(np.float64)\n                image_quantiles /= image_quantiles[-1]\n                \n                target_quantiles = np.cumsum(target_counts).astype(np.float64)\n                target_quantiles /= target_quantiles[-1]\n\n                result = np.interp(image_quantiles, target_quantiles, target_values)[image_indices].reshape(image.shape)\n        return {\n            'Image': result\n        }\n\nclass NodeSwitch(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_switch\"\n    bl_label = \"Switch\"\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketBool\", \"Switch\")\n        self.inputs.new(\"NodeSocketColor\", \"False\")\n        self.inputs.new(\"NodeSocketColor\", \"True\")\n\n        self.outputs.new(\"NodeSocketColor\", \"Output\")\n\n    def draw_buttons(self, context, layout):\n        pass\n\n    def execute(self, context, switch, false, true):\n        return {\n            'Output': true() if switch else false()\n        }\n\nclass NodeCompare(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_compare\"\n    bl_label = \"Compare\"\n\n    operation: bpy.props.EnumProperty(name=\"\", items=(\n        ('<', 'Less Than', ''),\n        ('<=', 'Less Than or Equal', ''),\n        ('>', 'Greater Than', ''),\n        ('>=', 'Greater Than or Equal', ''),\n        ('==', 'Equal', ''),\n        ('!=', 'Not Equal', ''),\n    ))\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketFloat\", \"A\")\n        self.inputs.new(\"NodeSocketFloat\", \"B\")\n\n        self.outputs.new(\"NodeSocketBool\", \"Result\")\n\n    def draw_buttons(self, context, layout):\n        layout.prop(self, \"operation\")\n\n    def execute(self, context, a, b):\n        match self.operation:\n            case '<':\n                result = a < b\n            case '<=':\n                result = a <= b\n            case '>':\n                result = a > b\n            case '>=':\n                result = a >= b\n            case '==':\n                result = a == b\n            case '!=':\n                result = a != b\n        return {\n            'Result': result\n        }\n\nclass NodeReplaceString(DreamTexturesNode):\n    bl_idname = \"dream_textures.node_replace_string\"\n    bl_label = \"Replace String\"\n\n    def init(self, context):\n        self.inputs.new(\"NodeSocketString\", \"String\")\n        self.inputs.new(\"NodeSocketString\", \"Find\")\n        self.inputs.new(\"NodeSocketString\", \"Replace\")\n\n        self.outputs.new(\"NodeSocketString\", \"String\")\n\n    def draw_buttons(self, context, layout):\n        pass\n\n    def execute(self, context, string, find, replace):\n        return {\n            'String': string.replace(find, replace)\n        }"
  },
  {
    "path": "generator_process/__init__.py",
    "content": "from typing import Callable\n\nfrom .actor import Actor, is_actor_process\n\nclass RunInSubprocess(Exception):\n    \"\"\"\n    Decorators to support running functions that are not defined under the Generator class in its subprocess.\n    This is to reduce what would otherwise be duplicate function definitions that logically don't belong to\n    the Generator, but require something in its subprocess (such as access to installed dependencies).\n    \"\"\"\n\n    def __new__(cls, func=None):\n        if func is None:\n            # support `raise RunInSubprocess`\n            return super().__new__(cls)\n        return cls.always(func)\n\n    @staticmethod\n    def always(func):\n        if is_actor_process:\n            return func\n        def wrapper(*args, **kwargs):\n            return Generator.shared().call(wrapper, *args, **kwargs).result()\n        RunInSubprocess._copy_attributes(func, wrapper)\n        return wrapper\n\n    @staticmethod\n    def when(condition: bool | Callable[..., bool]):\n        if not isinstance(condition, Callable):\n            if condition:\n                return RunInSubprocess.always\n            return lambda x: x\n        def decorator(func):\n            if is_actor_process:\n                return func\n            def wrapper(*args, **kwargs):\n                if condition(*args, **kwargs):\n                    return Generator.shared().call(wrapper, *args, **kwargs).result()\n                return func(*args, **kwargs)\n            RunInSubprocess._copy_attributes(func, wrapper)\n            return wrapper\n        return decorator\n\n    @staticmethod\n    def when_raised(func):\n        if is_actor_process:\n            return func\n        def wrapper(*args, **kwargs):\n            try:\n                return func(*args, **kwargs)\n            except RunInSubprocess:\n                return Generator.shared().call(wrapper, *args, **kwargs).result()\n        RunInSubprocess._copy_attributes(func, wrapper)\n        return wrapper\n\n    @staticmethod\n    def _copy_attributes(src, dst):\n        for n in [\"__annotations__\", \"__doc__\", \"__name__\", \"__module__\", \"__qualname__\"]:\n            if hasattr(src, n):\n                setattr(dst, n, getattr(src, n))\n\nclass Generator(Actor):\n    \"\"\"\n    The actor used for all background processes.\n    \"\"\"\n\n    from .actions.choose_device import choose_device\n    from .actions.load_model import load_model\n    from .actions.prompt_to_image import prompt_to_image\n    from .actions.image_to_image import image_to_image\n    from .actions.inpaint import inpaint\n    from .actions.outpaint import outpaint\n    from .actions.upscale import upscale\n    from .actions.depth_to_image import depth_to_image\n    from .actions.control_net import control_net\n    from .actions.huggingface_hub import hf_snapshot_download, hf_list_models, hf_list_installed_models\n    from .actions.convert_original_stable_diffusion_to_diffusers import convert_original_stable_diffusion_to_diffusers\n    from .actions.detect_seamless import detect_seamless\n    from .actions.controlnet_aux import controlnet_aux\n\n    @staticmethod\n    def call(func, *args, **kwargs):\n        return func(*args, **kwargs)\n"
  },
  {
    "path": "generator_process/actions/choose_device.py",
    "content": "import importlib.util\nimport sys\n\ndef choose_device(self, optimizations) -> str:\n    \"\"\"\n    Automatically select which PyTorch device to use.\n    \"\"\"\n    if optimizations.cpu_only:\n        return \"cpu\"\n    \n    import torch\n    \n    if torch.cuda.is_available():\n        return \"cuda\"\n    elif torch.backends.mps.is_available():\n        return \"mps\"\n    elif importlib.util.find_spec(\"torch_directml\"):\n        import torch_directml\n        if torch_directml.is_available():\n            torch.utils.rename_privateuse1_backend(\"dml\")\n            return \"dml\"\n    return \"cpu\""
  },
  {
    "path": "generator_process/actions/control_net.py",
    "content": "from typing import Union, Generator, Callable, List, Optional, Dict, Any\nfrom contextlib import nullcontext\n\nimport numpy as np\nimport logging\nimport os\nimport random\nfrom .prompt_to_image import Checkpoint, Scheduler, Optimizations, StepPreviewMode, step_latents, step_images, _configure_model_padding\nfrom ...api.models.seamless_axes import SeamlessAxes\nfrom ..future import Future\nfrom ...image_utils import image_to_np, rgb, resize, ImageOrPath\n\n\ndef control_net(\n    self,\n\n    model: str | Checkpoint,\n\n    scheduler: str | Scheduler,\n\n    optimizations: Optimizations,\n\n    control_net: list[str | Checkpoint],\n    control: list[ImageOrPath] | None,\n    controlnet_conditioning_scale: list[float],\n    \n    image: ImageOrPath | None, # image to image\n    # inpaint\n    inpaint: bool,\n    inpaint_mask_src: str,\n    text_mask: str,\n    text_mask_confidence: float,\n\n    strength: float,\n    prompt: str | list[str],\n    steps: int,\n    seed: int,\n\n    width: int | None,\n    height: int | None,\n\n    cfg_scale: float,\n    use_negative_prompt: bool,\n    negative_prompt: str,\n\n    seamless_axes: SeamlessAxes | str | bool | tuple[bool, bool] | None,\n\n    step_preview_mode: StepPreviewMode,\n\n    **kwargs\n) -> Generator[Future, None, None]:\n    future = Future()\n    yield future\n\n    import diffusers\n    import torch\n    \n    device = self.choose_device(optimizations)\n\n    # StableDiffusionPipeline w/ caching\n    if image is not None:\n        if inpaint:\n            pipe = self.load_model(diffusers.AutoPipelineForInpainting, model, optimizations, scheduler, controlnet=control_net)\n        else:\n            pipe = self.load_model(diffusers.AutoPipelineForImage2Image, model, optimizations, scheduler, controlnet=control_net)\n    else:\n        pipe = self.load_model(diffusers.AutoPipelineForText2Image, model, optimizations, scheduler, controlnet=control_net)\n\n    # Optimizations\n    pipe = optimizations.apply(pipe, device)\n\n    # RNG\n    batch_size = len(prompt) if isinstance(prompt, list) else 1\n    generator = []\n    for _ in range(batch_size):\n        gen = torch.Generator(device=\"cpu\" if device in (\"mps\", \"dml\") else device) # MPS and DML do not support the `Generator` API\n        generator.append(gen.manual_seed(random.randrange(0, np.iinfo(np.uint32).max) if seed is None else seed))\n    if batch_size == 1:\n        # Some schedulers don't handle a list of generators: https://github.com/huggingface/diffusers/issues/1909\n        generator = generator[0]\n\n    # Init Image\n    # FIXME: The `unet.config.sample_size` of the depth model is `32`, not `64`. For now, this will be hardcoded to `512`.\n    height = height or 512\n    width = width or 512\n    rounded_size = (\n        int(8 * (width // 8)),\n        int(8 * (height // 8)),\n    )\n    # StableDiffusionControlNetPipeline.check_image() currently fails without adding batch dimension\n    control_image = None if control is None else [image_to_np(c, mode=\"RGB\", size=rounded_size)[np.newaxis] for c in control]\n    image = image_to_np(image, size=rounded_size)\n    if inpaint:\n        match inpaint_mask_src:\n            case 'alpha':\n                mask_image = 1-image[..., -1]\n                image = rgb(image)\n            case 'prompt':\n                image = rgb(image)\n                from transformers import AutoProcessor, CLIPSegForImageSegmentation\n\n                processor = AutoProcessor.from_pretrained(\"CIDAS/clipseg-rd64-refined\", do_rescale=False)\n                clipseg = CLIPSegForImageSegmentation.from_pretrained(\"CIDAS/clipseg-rd64-refined\")\n                inputs = processor(text=[text_mask], images=[image], return_tensors=\"pt\", padding=True)\n                outputs = clipseg(**inputs)\n                mask_image = (torch.sigmoid(outputs.logits) >= text_mask_confidence).detach().numpy().astype(np.float32)\n                mask_image = resize(mask_image, (width, height))\n    else:\n        mask_image = None\n\n    # Seamless\n    if seamless_axes == SeamlessAxes.AUTO:\n        init_sa = None if image is None else self.detect_seamless(image)\n        control_sa = None if control_image is None else self.detect_seamless(control_image[0][0])\n        if init_sa is not None and control_sa is not None:\n            seamless_axes = init_sa & control_sa\n        elif init_sa is not None:\n            seamless_axes = init_sa\n        elif control_sa is not None:\n            seamless_axes = control_sa\n    _configure_model_padding(pipe.unet, seamless_axes)\n    _configure_model_padding(pipe.vae, seamless_axes)\n\n    # Inference\n    with (torch.inference_mode() if device not in ('mps', \"dml\") else nullcontext()), \\\n        (torch.autocast(device) if optimizations.can_use(\"amp\", device) else nullcontext()):\n        def callback(pipe, step, timestep, callback_kwargs):\n            if future.check_cancelled():\n                raise InterruptedError()\n            future.add_response(step_latents(pipe, step_preview_mode, callback_kwargs[\"latents\"], generator, step, steps))\n            return callback_kwargs\n        try:\n            if image is not None:\n                if mask_image is not None:\n                    result = pipe(\n                        prompt=prompt,\n                        negative_prompt=negative_prompt if use_negative_prompt else None,\n                        control_image=control_image,\n                        controlnet_conditioning_scale=controlnet_conditioning_scale,\n                        image=image,\n                        mask_image=mask_image,\n                        strength=strength,\n                        width=rounded_size[0],\n                        height=rounded_size[1],\n                        num_inference_steps=steps,\n                        guidance_scale=cfg_scale,\n                        generator=generator,\n                        callback_on_step_end=callback,\n                        callback_steps=1,\n                        output_type=\"np\"\n                    )\n                else:\n                    result = pipe(\n                        prompt=prompt,\n                        negative_prompt=negative_prompt if use_negative_prompt else None,\n                        control_image=control_image,\n                        controlnet_conditioning_scale=controlnet_conditioning_scale,\n                        image=image,\n                        strength=strength,\n                        width=rounded_size[0],\n                        height=rounded_size[1],\n                        num_inference_steps=steps,\n                        guidance_scale=cfg_scale,\n                        generator=generator,\n                        callback_on_step_end=callback,\n                        callback_steps=1,\n                        output_type=\"np\"\n                    )\n            else:\n                result = pipe(\n                    prompt=prompt,\n                    negative_prompt=negative_prompt if use_negative_prompt else None,\n                    image=control_image,\n                    controlnet_conditioning_scale=controlnet_conditioning_scale,\n                    width=rounded_size[0],\n                    height=rounded_size[1],\n                    num_inference_steps=steps,\n                    guidance_scale=cfg_scale,\n                    generator=generator,\n                    callback_on_step_end=callback,\n                    callback_steps=1,\n                    output_type=\"np\"\n                )\n\n            future.add_response(step_images(result.images, generator, steps, steps))\n        except InterruptedError:\n            pass\n    \n    future.set_done()"
  },
  {
    "path": "generator_process/actions/controlnet_aux.py",
    "content": "import numpy as np\nfrom numpy.typing import NDArray\nfrom ..models.optimizations import Optimizations\nfrom ...image_utils import np_to_pil\n\ndef controlnet_aux(\n    self,\n\n    processor_id: str,\n    image: NDArray,\n\n    optimizations: Optimizations,\n\n    **kwargs\n) -> NDArray:\n    if processor_id == \"none\":\n        return image\n    \n    from controlnet_aux.processor import Processor\n    processor = Processor(processor_id)\n    device = self.choose_device(optimizations)\n    try:\n        processor.processor.to(device)\n    except:\n        # not all processors can run on the GPU\n        pass\n    \n    processed_image = processor(np_to_pil(image))\n    return np.array(processed_image) / 255.0"
  },
  {
    "path": "generator_process/actions/convert_original_stable_diffusion_to_diffusers.py",
    "content": "import os\n\nfrom .huggingface_hub import DownloadStatus\nfrom ..future import Future\nfrom ..models import ModelConfig\n\n\ndef convert_original_stable_diffusion_to_diffusers(\n    self,\n    checkpoint_path: str,\n    model_config: ModelConfig,\n    half_precision: bool,\n) -> str:\n    import torch\n    from huggingface_hub.constants import HF_HUB_CACHE\n    from diffusers.pipelines.stable_diffusion.convert_from_ckpt import download_from_original_stable_diffusion_ckpt, download_controlnet_from_original_ckpt\n\n    future = Future()\n    yield future\n    DownloadStatus.hook_download_tqdm(future)\n\n    future.add_response(DownloadStatus(f\"Reading {checkpoint_path}\", 0, 1))\n    index = 0\n    def hook_save_pretrained(model, dirs_count, total):\n        old_save_pretrained = model.save_pretrained\n        def save_pretrained(self, save_directory, *args, **kwargs):\n            nonlocal index\n            dirs = []\n            directory = save_directory\n            for _ in range(dirs_count):\n                dirs.append(os.path.basename(directory))\n                directory = os.path.dirname(directory)\n            dirs.reverse()\n            future.add_response(DownloadStatus(f\"Saving {os.path.join(*dirs)}\", index, total))\n            index += 1\n            return old_save_pretrained(save_directory, *args, **kwargs)\n        model.save_pretrained = save_pretrained.__get__(model)\n\n    if model_config in [ModelConfig.CONTROL_NET_1_5, ModelConfig.CONTROL_NET_2_1]:\n        pipe = download_controlnet_from_original_ckpt(\n            checkpoint_path,\n            original_config_file=model_config.original_config,\n            from_safetensors=checkpoint_path.endswith(\".safetensors\"),\n        )\n        if half_precision:\n            pipe.to(dtype=torch.float16)\n        index = 1\n        hook_save_pretrained(pipe, 1, 2)\n    else:\n        pipe = download_from_original_stable_diffusion_ckpt(\n            checkpoint_path,\n            original_config_file=model_config.original_config,\n            from_safetensors=checkpoint_path.endswith(\".safetensors\"),\n            pipeline_class=model_config.pipeline\n        )\n        if half_precision:\n            pipe.to(torch_dtype=torch.float16)\n        models = []\n        for name in pipe._get_signature_keys(pipe)[0]:\n            model = getattr(pipe, name, None)\n            if model is not None and hasattr(model, \"save_pretrained\"):\n                models.append(model)\n        for i, model in enumerate(models):\n            hook_save_pretrained(model, 2, len(models))\n    dump_path = os.path.join(HF_HUB_CACHE, os.path.splitext(os.path.basename(checkpoint_path))[0])\n    pipe.save_pretrained(dump_path, variant=\"fp16\" if half_precision else None)\n    future.set_done()\n"
  },
  {
    "path": "generator_process/actions/depth_to_image.py",
    "content": "from typing import Union, Generator, Callable, List, Optional\nimport os\nfrom contextlib import nullcontext\n\nimport numpy as np\nimport random\nfrom .prompt_to_image import Checkpoint, Scheduler, Optimizations, StepPreviewMode, step_latents, step_images, _configure_model_padding\nfrom ...api.models.seamless_axes import SeamlessAxes\nfrom ..future import Future\nfrom ...image_utils import image_to_np, ImageOrPath\n\ndef depth_to_image(\n    self,\n    \n    model: str | Checkpoint,\n\n    scheduler: str | Scheduler,\n\n    optimizations: Optimizations,\n\n    depth: ImageOrPath | None,\n    image: ImageOrPath | None,\n    strength: float,\n    prompt: str | list[str],\n    steps: int,\n    seed: int,\n\n    width: int | None,\n    height: int | None,\n\n    cfg_scale: float,\n    use_negative_prompt: bool,\n    negative_prompt: str,\n\n    seamless_axes: SeamlessAxes | str | bool | tuple[bool, bool] | None,\n\n    step_preview_mode: StepPreviewMode,\n\n    **kwargs\n) -> Generator[Future, None, None]:\n    future = Future()\n    yield future\n\n    import diffusers\n    import torch\n    import PIL.Image\n    \n    class DreamTexturesDepth2ImgPipeline(diffusers.StableDiffusionInpaintPipeline):\n        def prepare_depth(self, depth, image, dtype, device):\n            device = torch.device('cpu' if device.type == 'mps' else device.type)\n            if depth is None:\n                from transformers import DPTFeatureExtractor, DPTForDepthEstimation\n                import contextlib\n                feature_extractor = DPTFeatureExtractor.from_pretrained(\"Intel/dpt-large\")\n                depth_estimator = DPTForDepthEstimation.from_pretrained(\"Intel/dpt-large\")\n                depth_estimator = depth_estimator.to(device)\n                \n                pixel_values = feature_extractor(images=image, return_tensors=\"pt\", do_rescale=False).pixel_values\n                pixel_values = pixel_values.to(device=device)\n                # The DPT-Hybrid model uses batch-norm layers which are not compatible with fp16.\n                # So we use `torch.autocast` here for half precision inference.\n                context_manger = torch.autocast(\"cuda\", dtype=dtype) if device.type == \"cuda\" else contextlib.nullcontext()\n                with context_manger:\n                    depth_map = depth_estimator(pixel_values).predicted_depth\n                depth_map = torch.nn.functional.interpolate(\n                    depth_map.unsqueeze(1),\n                    size=(height // self.vae_scale_factor, width // self.vae_scale_factor),\n                    mode=\"bicubic\",\n                    align_corners=False,\n                )\n\n                depth_min = torch.amin(depth_map, dim=[1, 2, 3], keepdim=True)\n                depth_max = torch.amax(depth_map, dim=[1, 2, 3], keepdim=True)\n                depth_map = 2.0 * (depth_map - depth_min) / (depth_max - depth_min) - 1.0\n                depth_map = depth_map.to(device)\n                return depth_map\n            else:\n                if isinstance(depth, PIL.Image.Image):\n                    depth = np.array(depth.convert(\"L\"))\n                    depth = depth.astype(np.float32) / 255.0\n                depth = depth[None, None]\n                depth = torch.from_numpy(depth)\n                return depth\n                \n        def prepare_depth_latents(\n            self, depth, batch_size, height, width, dtype, device, generator, do_classifier_free_guidance\n        ):\n            # resize the mask to latents shape as we concatenate the mask to the latents\n            # we do that before converting to dtype to avoid breaking in case we're using cpu_offload\n            # and half precision\n            depth = torch.nn.functional.interpolate(\n                depth, size=(height // self.vae_scale_factor, width // self.vae_scale_factor)\n            )\n            depth = depth.to(device=device, dtype=dtype)\n\n            # duplicate mask and masked_image_latents for each generation per prompt, using mps friendly method\n            depth = depth.repeat(batch_size, 1, 1, 1)\n            depth = torch.cat([depth] * 2) if do_classifier_free_guidance else depth\n            return depth\n\n        def prepare_img2img_latents(self, batch_size, num_channels_latents, height, width, dtype, device, generator, latents=None, image=None, timestep=None):\n            shape = (batch_size, num_channels_latents, height // self.vae_scale_factor, width // self.vae_scale_factor)\n            if isinstance(generator, list) and len(generator) != batch_size:\n                raise ValueError(\n                    f\"You have passed a list of generators of length {len(generator)}, but requested an effective batch\"\n                    f\" size of {batch_size}. Make sure the batch size matches the length of the generators.\"\n                )\n\n            if latents is None:\n                rand_device = \"cpu\" if device.type == \"mps\" else device\n\n                if isinstance(generator, list):\n                    shape = (1,) + shape[1:]\n                    latents = [\n                        torch.randn(shape, generator=generator[i], device=rand_device, dtype=dtype)\n                        for i in range(batch_size)\n                    ]\n                    latents = torch.cat(latents, dim=0).to(device)\n                else:\n                    latents = torch.randn(shape, generator=generator, device=rand_device, dtype=dtype).to(device)\n            else:\n                if latents.shape != shape:\n                    raise ValueError(f\"Unexpected latents shape, got {latents.shape}, expected {shape}\")\n                latents = latents.to(device)\n\n            # scale the initial noise by the standard deviation required by the scheduler\n            latents = latents * self.scheduler.init_noise_sigma\n\n            if image is not None:\n                image = image.to(device=device, dtype=dtype)\n                if isinstance(generator, list):\n                    image_latents = [\n                        self.vae.encode(image[0:1]).latent_dist.sample(generator[i]) for i in range(batch_size)\n                    ]\n                    image_latents = torch.cat(image_latents, dim=0)\n                else:\n                    image_latents = self.vae.encode(image).latent_dist.sample(generator)\n                image_latents = torch.nn.functional.interpolate(\n                    image_latents, size=(height // self.vae_scale_factor, width // self.vae_scale_factor)\n                )\n                image_latents = 0.18215 * image_latents\n                rand_device = \"cpu\" if device.type == \"mps\" else device\n                shape = image_latents.shape\n                if isinstance(generator, list):\n                    shape = (1,) + shape[1:]\n                    noise = [\n                        torch.randn(shape, generator=generator[i], device=rand_device, dtype=dtype) for i in\n                        range(batch_size)\n                    ]\n                    noise = torch.cat(noise, dim=0).to(device)\n                else:\n                    noise = torch.randn(shape, generator=generator, device=rand_device, dtype=dtype).to(device)\n                latents = self.scheduler.add_noise(image_latents, noise, timestep)\n\n            return latents\n\n\n        def get_timesteps(self, num_inference_steps, strength, device):\n            # get the original timestep using init_timestep\n            offset = self.scheduler.config.get(\"steps_offset\", 0)\n            init_timestep = int(num_inference_steps * strength) + offset\n            init_timestep = min(init_timestep, num_inference_steps)\n\n            t_start = max(num_inference_steps - init_timestep + offset, 0)\n            timesteps = self.scheduler.timesteps[t_start:]\n\n            return timesteps, num_inference_steps - t_start\n\n        @torch.no_grad()\n        def __call__(\n            self,\n            prompt: Union[str, List[str]],\n            depth_image: Union[torch.FloatTensor, PIL.Image.Image],\n            image: Optional[Union[torch.FloatTensor, PIL.Image.Image]] = None,\n            strength: float = 0.8,\n            height: Optional[int] = None,\n            width: Optional[int] = None,\n            num_inference_steps: int = 50,\n            guidance_scale: float = 7.5,\n            negative_prompt: Optional[Union[str, List[str]]] = None,\n            num_images_per_prompt: Optional[int] = 1,\n            eta: float = 0.0,\n            generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,\n            latents: Optional[torch.FloatTensor] = None,\n            output_type: Optional[str] = \"pil\",\n            return_dict: bool = True,\n            callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None,\n            callback_steps: Optional[int] = 1,\n            **kwargs,\n        ):\n            \n            # 0. Default height and width to unet\n            height = height or self.unet.config.sample_size * self.vae_scale_factor\n            width = width or self.unet.config.sample_size * self.vae_scale_factor\n\n            # 1. Check inputs\n            self.check_inputs(prompt=prompt, image=image, mask_image=depth_image, height=height, width=width, strength=strength, callback_steps=callback_steps, output_type=output_type)\n\n            # 2. Define call parameters\n            batch_size = 1 if isinstance(prompt, str) else len(prompt)\n            device = self._execution_device\n            # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)\n            # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`\n            # corresponds to doing no classifier free guidance.\n            do_classifier_free_guidance = guidance_scale > 1.0\n\n            # 3. Encode input prompt\n            text_embeddings = self._encode_prompt(\n                prompt, device, num_images_per_prompt, do_classifier_free_guidance, negative_prompt\n            )\n\n            # 4. Prepare the depth image\n            depth = self.prepare_depth(depth_image, image, text_embeddings.dtype, device)\n            if image is not None:\n                image = self.image_processor.preprocess(image)\n\n            # 5. set timesteps\n            self.scheduler.set_timesteps(num_inference_steps, device=device)\n            timesteps = self.scheduler.timesteps\n            if image is not None:\n                timesteps, num_inference_steps = self.get_timesteps(num_inference_steps, strength, device)\n\n            # 6. Prepare latent variables\n            num_channels_latents = self.vae.config.latent_channels\n            if image is not None:\n                latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)\n                latents = self.prepare_img2img_latents(\n                    batch_size * num_images_per_prompt,\n                    num_channels_latents,\n                    height,\n                    width,\n                    text_embeddings.dtype,\n                    device,\n                    generator,\n                    latents,\n                    image,\n                    latent_timestep\n                )\n            else:\n                latents = self.prepare_latents(\n                    batch_size * num_images_per_prompt,\n                    num_channels_latents,\n                    height,\n                    width,\n                    text_embeddings.dtype,\n                    device,\n                    generator,\n                    latents,\n                )[0]\n\n            # 7. Prepare mask latent variables\n            depth = self.prepare_depth_latents(\n                depth,\n                batch_size * num_images_per_prompt,\n                height,\n                width,\n                text_embeddings.dtype,\n                device,\n                generator,\n                do_classifier_free_guidance,\n            )\n\n            # 8. Check that sizes of mask, masked image and latents match\n            num_channels_depth = depth.shape[1]\n            if num_channels_latents + num_channels_depth != self.unet.config.in_channels:\n                raise ValueError(\n                    f\"Select a depth model, such as 'stabilityai/stable-diffusion-2-depth'\"\n                )\n\n            # 9. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline\n            extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)\n\n            # 10. Denoising loop\n            num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order\n            with self.progress_bar(total=num_inference_steps) as progress_bar:\n                for i, t in enumerate(timesteps):\n                    # expand the latents if we are doing classifier free guidance\n                    latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents\n\n                    # concat latents, mask, masked_image_latents in the channel dimension\n                    latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)\n                    latent_model_input = torch.cat([latent_model_input, depth], dim=1)\n\n                    # predict the noise residual\n                    noise_pred = self.unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample\n\n                    # perform guidance\n                    if do_classifier_free_guidance:\n                        noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)\n                        noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)\n\n                    # compute the previous noisy sample x_t -> x_t-1\n                    latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs).prev_sample\n\n                    # call the callback, if provided\n                    if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):\n                        progress_bar.update()\n                        if callback is not None and i % callback_steps == 0:\n                            callback(i, t, latents)\n\n            if not output_type == \"latent\":\n                condition_kwargs = {}\n                image = self.vae.decode(latents / self.vae.config.scaling_factor, return_dict=False, **condition_kwargs)[0]\n                image, has_nsfw_concept = self.run_safety_checker(image, device, text_embeddings.dtype)\n            else:\n                image = latents\n                has_nsfw_concept = None\n\n            if has_nsfw_concept is None:\n                do_denormalize = [True] * image.shape[0]\n            else:\n                do_denormalize = [not has_nsfw for has_nsfw in has_nsfw_concept]\n\n            image = self.image_processor.postprocess(image, output_type=output_type, do_denormalize=do_denormalize)\n\n            # Offload last model to CPU\n            if hasattr(self, \"final_offload_hook\") and self.final_offload_hook is not None:\n                self.final_offload_hook.offload()\n\n            if not return_dict:\n                return (image, has_nsfw_concept)\n\n            return diffusers.pipelines.stable_diffusion.StableDiffusionPipelineOutput(images=image, nsfw_content_detected=has_nsfw_concept)\n    \n    device = self.choose_device(optimizations)\n\n    # StableDiffusionPipeline w/ caching\n    pipe = self.load_model(DreamTexturesDepth2ImgPipeline, model, optimizations, scheduler)\n\n    # Optimizations\n    pipe = optimizations.apply(pipe, device)\n\n    # RNG\n    batch_size = len(prompt) if isinstance(prompt, list) else 1\n    generator = []\n    for _ in range(batch_size):\n        gen = torch.Generator(device=\"cpu\" if device in (\"mps\", \"dml\") else device) # MPS and DML do not support the `Generator` API\n        generator.append(gen.manual_seed(random.randrange(0, np.iinfo(np.uint32).max) if seed is None else seed))\n    if batch_size == 1:\n        # Some schedulers don't handle a list of generators: https://github.com/huggingface/diffusers/issues/1909\n        generator = generator[0]\n\n    # Init Image\n    # FIXME: The `unet.config.sample_size` of the depth model is `32`, not `64`. For now, this will be hardcoded to `512`.\n    height = height or 512\n    width = width or 512\n    rounded_size = (\n        int(8 * (width // 8)),\n        int(8 * (height // 8)),\n    )\n    depth = image_to_np(depth, mode=\"L\", size=rounded_size, to_color_space=None)\n    image = image_to_np(image, mode=\"RGB\", size=rounded_size)\n\n    # Seamless\n    if seamless_axes == SeamlessAxes.AUTO:\n        init_sa = None if image is None else self.detect_seamless(image)\n        depth_sa = None if depth is None else self.detect_seamless(depth)\n        if init_sa is not None and depth_sa is not None:\n            seamless_axes = init_sa & depth_sa\n        elif init_sa is not None:\n            seamless_axes = init_sa\n        elif depth_sa is not None:\n            seamless_axes = depth_sa\n    _configure_model_padding(pipe.unet, seamless_axes)\n    _configure_model_padding(pipe.vae, seamless_axes)\n\n    # Inference\n    with torch.inference_mode() if device not in ('mps', \"dml\") else nullcontext():\n        def callback(step, _, latents):\n            if future.check_cancelled():\n                raise InterruptedError()\n            future.add_response(step_latents(pipe, step_preview_mode, latents, generator, step, steps))\n        try:\n            result = pipe(\n                prompt=prompt,\n                negative_prompt=negative_prompt if use_negative_prompt else None,\n                depth_image=depth,\n                image=image,\n                strength=strength,\n                width=rounded_size[0],\n                height=rounded_size[1],\n                num_inference_steps=steps,\n                guidance_scale=cfg_scale,\n                generator=generator,\n                callback=callback,\n                callback_steps=1,\n                output_type=\"np\"\n            )\n            \n            future.add_response(step_images(result.images, generator, steps, steps))\n        except InterruptedError:\n            pass\n    \n    future.set_done()"
  },
  {
    "path": "generator_process/actions/detect_seamless/__init__.py",
    "content": "from enum import Enum\n\nimport numpy as np\nfrom numpy.typing import NDArray\n\nfrom ....api.models.seamless_axes import SeamlessAxes\nfrom .... import image_utils\n\ndef detect_seamless(self, image: image_utils.ImageOrPath) -> SeamlessAxes:\n    import os\n    import torch\n    from torch import nn\n\n    if image.shape[0] < 8 or image.shape[1] < 8:\n        return SeamlessAxes.OFF\n\n    model = getattr(self, 'detect_seamless_model', None)\n    if model is None:\n        state_npz = np.load(os.path.join(os.path.dirname(__file__), 'model.npz'))\n        state = {k: torch.tensor(v) for k, v in state_npz.items()}\n\n        class SeamlessModel(nn.Module):\n            def __init__(self):\n                super(SeamlessModel, self).__init__()\n                self.conv = nn.Sequential(\n                    nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),\n                    nn.Dropout(.2),\n                    nn.PReLU(64),\n                    nn.Conv2d(64, 16, kernel_size=3, stride=1, padding=1),\n                    nn.Dropout(.2),\n                    nn.PReLU(16),\n                    nn.Conv2d(16, 64, kernel_size=8, stride=4, padding=0),\n                    nn.Dropout(.2),\n                    nn.PReLU(64),\n                    nn.Conv2d(64, 64, kernel_size=(1, 3), stride=1, padding=0),\n                    nn.Dropout(.2)\n                )\n                self.gru = nn.GRU(64, 32, batch_first=True)\n                self.fc = nn.Linear(32, 1)\n\n            def forward(self, x: torch.Tensor):\n                if len(x.size()) == 3:\n                    x = x.unsqueeze(0)\n                x = self.conv(x)\n                h = torch.zeros(self.gru.num_layers, x.size()[0], self.gru.hidden_size,\n                                dtype=x.dtype, device=x.device)\n                x, h = self.gru(x.squeeze(3).transpose(2, 1), h)\n                return torch.tanh(self.fc(x[:, -1]))\n\n        model = SeamlessModel()\n        model.load_state_dict(state)\n        model.eval()\n        setattr(self, 'detect_seamless_model', model)\n\n    if torch.cuda.is_available():\n        device = 'cuda'\n    elif torch.backends.mps.is_available():\n        device = 'cpu'\n    else:\n        device = 'cpu'\n\n    image = image_utils.image_to_np(image, mode=\"RGB\")\n\n    # slice 8 pixels off each edge and combine opposing sides where the seam/seamless portion is in the middle\n    # may trim up to 3 pixels off the length of each edge to make them a multiple of 4\n    # expects pixel values to be between 0-1 before this step\n    edge_x = np.zeros((image.shape[0], 16, 3), dtype=np.float32)\n    edge_x[:, :8] = image[:, -8:]\n    edge_x[:, 8:] = image[:, :8]\n    edge_x *= 2\n    edge_x -= 1\n    edge_x = edge_x[:image.shape[0] // 4 * 4].transpose(2, 0, 1)\n\n    edge_y = np.zeros((16, image.shape[1], 3), dtype=np.float32)\n    edge_y[:8] = image[-8:]\n    edge_y[8:] = image[:8]\n    edge_y *= 2\n    edge_y -= 1\n    edge_y = edge_y[:, :image.shape[1] // 4 * 4].transpose(2, 1, 0)\n\n    @torch.no_grad()\n    def infer(*inputs):\n        try:\n            model.to(device)\n            results = []\n            for tensor in inputs:\n                results.append(model(tensor))\n            return results\n        finally:\n            # swap model in and out of device rather than reloading from file\n            model.to('cpu')\n\n    if edge_x.shape == edge_y.shape:\n        # both edges batched together\n        edges = torch.tensor(np.array([edge_x, edge_y]), dtype=torch.float32, device=device)\n        res = infer(edges)\n        return SeamlessAxes((res[0][0].item() > 0, res[0][1].item() > 0))\n    else:\n        edge_x = torch.tensor(edge_x, dtype=torch.float32, device=device)\n        edge_y = torch.tensor(edge_y, dtype=torch.float32, device=device)\n        res = infer(edge_x, edge_y)\n        return SeamlessAxes((res[0].item() > 0, res[1].item() > 0))\n"
  },
  {
    "path": "generator_process/actions/huggingface_hub.py",
    "content": "from dataclasses import dataclass\nimport os\nfrom pathlib import Path\nfrom typing import Dict, List, Optional, Union, Generator, BinaryIO\nimport copy\nimport io\nimport os\nimport tempfile\nimport warnings\nfrom contextlib import contextmanager\nfrom functools import partial\nfrom hashlib import sha256\nfrom pathlib import Path\nimport requests\nimport json\nimport enum\nfrom ..future import Future\nfrom ..models import ModelType\n\n\n@dataclass\nclass Model:\n    id: str\n    author: str\n    tags: list[str]\n    likes: int\n    downloads: int\n    model_type: ModelType\n\ndef hf_list_models(\n    self,\n    query: str,\n    token: str,\n) -> list[Model]:\n    from huggingface_hub import HfApi\n    \n    if hasattr(self, \"huggingface_hub_api\"):\n        api: HfApi = self.huggingface_hub_api\n    else:\n        api = HfApi()\n        setattr(self, \"huggingface_hub_api\", api)\n\n    models = api.list_models(\n        tags=\"diffusers\",\n        search=query,\n        token=token,\n    )\n    return [\n        Model(m.id, m.author or \"\", m.tags, m.likes if hasattr(m, \"likes\") else 0, getattr(m, \"downloads\", -1), ModelType.UNKNOWN)\n        for m in models\n        if m.id is not None and m.tags is not None and 'diffusers' in (m.tags or {})\n    ]\n\ndef hf_list_installed_models(self) -> list[Model]:\n    from huggingface_hub.constants import HF_HUB_CACHE\n    from diffusers.utils.hub_utils import old_diffusers_cache\n\n    def list_dir(cache_dir):\n        if not os.path.exists(cache_dir):\n            return []\n\n        def detect_model_type(snapshot_folder):\n            unet_config = os.path.join(snapshot_folder, 'unet', 'config.json')\n            config = os.path.join(snapshot_folder, 'config.json')\n            if os.path.exists(unet_config):\n                with open(unet_config, 'r') as f:\n                    return ModelType(json.load(f)['in_channels'])\n            elif os.path.exists(config):\n                with open(config, 'r') as f:\n                    config_dict = json.load(f)\n                    if '_class_name' in config_dict and config_dict['_class_name'] == 'ControlNetModel':\n                        return ModelType.CONTROL_NET\n                    else:\n                        return ModelType.UNKNOWN\n            else:\n                return ModelType.UNKNOWN\n\n        def _map_model(file):\n            storage_folder = os.path.join(cache_dir, file)\n            model_type = ModelType.UNKNOWN\n\n            if os.path.exists(os.path.join(storage_folder, 'model_index.json')) or os.path.exists(os.path.join(storage_folder, 'config.json')):\n                snapshot_folder = storage_folder\n                model_type = detect_model_type(snapshot_folder)\n            else:\n                refs_folder = os.path.join(storage_folder, \"refs\")\n                if not os.path.exists(refs_folder):\n                    return None\n                for revision in os.listdir(refs_folder):\n                    ref_path = os.path.join(storage_folder, \"refs\", revision)\n                    with open(ref_path) as f:\n                        commit_hash = f.read()\n                    snapshot_folder = os.path.join(storage_folder, \"snapshots\", commit_hash)\n                    if (detected_type := detect_model_type(snapshot_folder)) != ModelType.UNKNOWN:\n                        model_type = detected_type\n                        break\n\n            return Model(\n                storage_folder,\n                \"\",\n                [],\n                -1,\n                -1,\n                model_type\n            )\n        return [\n            model for model in (\n                _map_model(file) for file in os.listdir(cache_dir) if os.path.isdir(os.path.join(cache_dir, file))\n            )\n            if model is not None\n        ]\n    new_cache_list = list_dir(HF_HUB_CACHE)\n    model_ids = [os.path.basename(m.id) for m in new_cache_list]\n    for model in list_dir(old_diffusers_cache):\n        if os.path.basename(model.id) not in model_ids:\n            new_cache_list.append(model)\n    return new_cache_list\n\n@dataclass\nclass DownloadStatus:\n    file: str\n    index: int\n    total: int\n\n    @classmethod\n    def hook_download_tqdm(cls, future):\n        from huggingface_hub import utils, file_download\n        progresses = set()\n\n        class future_tqdm(utils.tqdm):\n            def __init__(self, *args, **kwargs):\n                super().__init__(*args, **kwargs)\n                self.progress()\n\n            def update(self, n=1):\n                ret = super().update(n=n)\n                self.progress()\n                return ret\n\n            def progress(self):\n                nonlocal progresses\n                progresses.add(self)\n                ratio = self.n / self.total\n                count = 0\n                for tqdm in progresses:\n                    r = tqdm.n / tqdm.total\n                    if r == 1:\n                        continue\n                    count += 1\n                    if tqdm != self and ratio < r:\n                        # only show download status of most complete file\n                        return\n                future.add_response(cls(f\"{count} file{'' if count == 1 else 's'}: {self.desc}\", self.n, self.total))\n        file_download.tqdm = future_tqdm\n\ndef hf_snapshot_download(\n    self,\n    model: str,\n    token: str,\n    variant: str | None = None,\n    resume_download=True\n):\n    from huggingface_hub import snapshot_download, repo_info\n    from diffusers import StableDiffusionPipeline\n    from diffusers.pipelines.pipeline_utils import variant_compatible_siblings\n\n    future = Future()\n    yield future\n    DownloadStatus.hook_download_tqdm(future)\n\n    info = repo_info(model, token=token)\n    files = [file.rfilename for file in info.siblings]\n\n    if \"model_index.json\" in files:\n        # check if the variant files are available before trying to download them\n        _, variant_files = variant_compatible_siblings(files, variant=variant)\n        StableDiffusionPipeline.download(\n            model,\n            token=token,\n            variant=variant if len(variant_files) > 0 else None,\n            resume_download=resume_download,\n        )\n    elif \"config.json\" in files:\n        # individual model, such as controlnet or vae\n\n        fp16_weights = [\"diffusion_pytorch_model.fp16.safetensors\", \"diffusion_pytorch_model.fp16.bin\"]\n        fp32_weights = [\"diffusion_pytorch_model.safetensors\", \"diffusion_pytorch_model.bin\"]\n        if variant == \"fp16\":\n            weights_names = fp16_weights + fp32_weights\n        else:\n            weights_names = fp32_weights + fp16_weights\n\n        weights = next((name for name in weights_names if name in files), None)\n        if weights is None:\n            raise FileNotFoundError(f\"Can't find appropriate weights in {model}\")\n\n        snapshot_download(\n            model,\n            token=token,\n            resume_download=resume_download,\n            allow_patterns=[\"config.json\", weights]\n        )\n    else:\n        raise ValueError(f\"{model} doesn't appear to be a pipeline or model\")\n\n    future.set_done()\n"
  },
  {
    "path": "generator_process/actions/image_to_image.py",
    "content": "from typing import Union, Generator, Callable, List, Optional\nimport os\nfrom contextlib import nullcontext\n\nfrom numpy.typing import NDArray\nimport numpy as np\nimport random\nfrom .prompt_to_image import Checkpoint, Scheduler, Optimizations, StepPreviewMode, step_latents, step_images, _configure_model_padding\nfrom ...api.models.seamless_axes import SeamlessAxes\nfrom ..future import Future\nfrom ...image_utils import image_to_np, size, resize, ImageOrPath\n\n\ndef image_to_image(\n    self,\n    \n    model: str | Checkpoint,\n\n    scheduler: str | Scheduler,\n\n    optimizations: Optimizations,\n\n    image: ImageOrPath,\n    fit: bool,\n    strength: float,\n    prompt: str | list[str],\n    steps: int,\n    width: int | None,\n    height: int | None,\n    seed: int,\n\n    cfg_scale: float,\n    use_negative_prompt: bool,\n    negative_prompt: str,\n    \n    seamless_axes: SeamlessAxes | str | bool | tuple[bool, bool] | None,\n\n    step_preview_mode: StepPreviewMode,\n\n    # Stability SDK\n    key: str | None = None,\n\n    **kwargs\n) -> Generator[Future, None, None]:\n    future = Future()\n    yield future\n\n    import diffusers\n    import torch\n    \n    device = self.choose_device(optimizations)\n\n    # Stable Diffusion pipeline w/ caching\n    pipe = self.load_model(diffusers.AutoPipelineForImage2Image, model, optimizations, scheduler)\n\n    # Optimizations\n    pipe = optimizations.apply(pipe, device)\n\n    # RNG\n    batch_size = len(prompt) if isinstance(prompt, list) else 1\n    generator = []\n    for _ in range(batch_size):\n        gen = torch.Generator(device=\"cpu\" if device in (\"mps\", \"dml\") else device) # MPS and DML do not support the `Generator` API\n        generator.append(gen.manual_seed(random.randrange(0, np.iinfo(np.uint32).max) if seed is None else seed))\n    if batch_size == 1:\n        # Some schedulers don't handle a list of generators: https://github.com/huggingface/diffusers/issues/1909\n        generator = generator[0]\n\n    # Init Image\n    image = image_to_np(image, mode=\"RGB\")\n    if fit:\n        height = height or pipe.unet.config.sample_size * pipe.vae_scale_factor\n        width = width or pipe.unet.config.sample_size * pipe.vae_scale_factor\n        image = resize(image, (width, height))\n    else:\n        width, height = size(image)\n    \n    # Seamless\n    if seamless_axes == SeamlessAxes.AUTO:\n        seamless_axes = self.detect_seamless(image)\n    _configure_model_padding(pipe.unet, seamless_axes)\n    _configure_model_padding(pipe.vae, seamless_axes)\n\n    # Inference\n    with torch.inference_mode() if device not in ('mps', \"dml\") else nullcontext():\n        def callback(pipe, step, timestep, callback_kwargs):\n            if future.check_cancelled():\n                raise InterruptedError()\n            future.add_response(step_latents(pipe, step_preview_mode, callback_kwargs[\"latents\"], generator, step, steps))\n            return callback_kwargs\n        try:\n            result = pipe(\n                prompt=prompt,\n                negative_prompt=negative_prompt if use_negative_prompt else None,\n                image=[image] * batch_size,\n                strength=strength,\n                num_inference_steps=steps,\n                guidance_scale=cfg_scale,\n                generator=generator,\n                callback_on_step_end=callback,\n                callback_steps=1,\n                output_type=\"np\"\n            )\n            future.add_response(step_images(result.images, generator, steps, steps))\n        except InterruptedError:\n            pass\n    \n    future.set_done()"
  },
  {
    "path": "generator_process/actions/inpaint.py",
    "content": "from typing import Union, Generator, Callable, List, Optional\nimport os\nfrom contextlib import nullcontext\nimport numpy as np\nimport random\nfrom .prompt_to_image import Checkpoint, Scheduler, Optimizations, StepPreviewMode, step_latents, step_images, _configure_model_padding\nfrom ...api.models.seamless_axes import SeamlessAxes\nfrom ..future import Future\nfrom ...image_utils import image_to_np, size, resize, rgb, ImageOrPath\n\ndef inpaint(\n    self,\n    \n    model: str | Checkpoint,\n\n    scheduler: str | Scheduler,\n\n    optimizations: Optimizations,\n\n    image: ImageOrPath,\n    fit: bool,\n    strength: float,\n    prompt: str | list[str],\n    steps: int,\n    width: int | None,\n    height: int | None,\n    seed: int,\n\n    cfg_scale: float,\n    use_negative_prompt: bool,\n    negative_prompt: str,\n    \n    seamless_axes: SeamlessAxes | str | bool | tuple[bool, bool] | None,\n\n    iterations: int,\n\n    step_preview_mode: StepPreviewMode,\n\n    inpaint_mask_src: str,\n    text_mask: str,\n    text_mask_confidence: float,\n\n    # Stability SDK\n    key: str | None = None,\n\n    **kwargs\n) -> Generator[Future, None, None]:\n    future = Future()\n    yield future\n\n    import diffusers\n    import torch\n    \n    device = self.choose_device(optimizations)\n\n    # StableDiffusionPipeline w/ caching\n    pipe = self.load_model(diffusers.AutoPipelineForInpainting, model, optimizations, scheduler)\n    height = height or pipe.unet.config.sample_size * pipe.vae_scale_factor\n    width = width or pipe.unet.config.sample_size * pipe.vae_scale_factor\n\n    # Optimizations\n    pipe = optimizations.apply(pipe, device)\n\n    # RNG\n    batch_size = len(prompt) if isinstance(prompt, list) else 1\n    generator = []\n    for _ in range(batch_size):\n        gen = torch.Generator(device=\"cpu\" if device in (\"mps\", \"dml\") else device) # MPS and DML do not support the `Generator` API\n        generator.append(gen.manual_seed(random.randrange(0, np.iinfo(np.uint32).max) if seed is None else seed))\n    if batch_size == 1:\n        # Some schedulers don't handle a list of generators: https://github.com/huggingface/diffusers/issues/1909\n        generator = generator[0]\n\n    # Init Image\n    image = image_to_np(image)\n    if fit:\n        height = height or pipe.unet.config.sample_size * pipe.vae_scale_factor\n        width = width or pipe.unet.config.sample_size * pipe.vae_scale_factor\n        image = resize(image, (width, height))\n    else:\n        width, height = size(image)\n    \n    # Seamless\n    if seamless_axes == SeamlessAxes.AUTO:\n        seamless_axes = self.detect_seamless(image)\n    _configure_model_padding(pipe.unet, seamless_axes)\n    _configure_model_padding(pipe.vae, seamless_axes)\n\n    # Inference\n    with torch.inference_mode() if device not in ('mps', \"dml\") else nullcontext():\n        match inpaint_mask_src:\n            case 'alpha':\n                mask_image = 1-image[..., -1]\n                image = rgb(image)\n            case 'prompt':\n                image = rgb(image)\n                from transformers import AutoProcessor, CLIPSegForImageSegmentation\n\n                processor = AutoProcessor.from_pretrained(\"CIDAS/clipseg-rd64-refined\", do_rescale=False)\n                clipseg = CLIPSegForImageSegmentation.from_pretrained(\"CIDAS/clipseg-rd64-refined\")\n                inputs = processor(text=[text_mask], images=[image], return_tensors=\"pt\", padding=True)\n                outputs = clipseg(**inputs)\n                mask_image = (torch.sigmoid(outputs.logits) >= text_mask_confidence).detach().numpy().astype(np.float32)\n                mask_image = resize(mask_image, (width, height))\n        \n        def callback(pipe, step, timestep, callback_kwargs):\n            if future.check_cancelled():\n                raise InterruptedError()\n            future.add_response(step_latents(pipe, step_preview_mode, callback_kwargs[\"latents\"], generator, step, steps))\n            return callback_kwargs\n        try:\n            result = pipe(\n                prompt=prompt,\n                negative_prompt=negative_prompt if use_negative_prompt else None,\n                image=[image] * batch_size,\n                mask_image=[mask_image] * batch_size,\n                strength=strength,\n                height=height,\n                width=width,\n                num_inference_steps=steps,\n                guidance_scale=cfg_scale,\n                generator=generator,\n                callback_on_step_end=callback,\n                callback_steps=1,\n                output_type=\"np\"\n            )\n            \n            future.add_response(step_images(result.images, generator, steps, steps))\n        except InterruptedError:\n            pass\n    \n    future.set_done()"
  },
  {
    "path": "generator_process/actions/load_model.py",
    "content": "import gc\nimport logging\nimport os\nfrom ..models import Checkpoint, ModelConfig, Scheduler\n\nlogger = logging.getLogger(__name__)\n\n\ndef revision_paths(model, config=\"model_index.json\"):\n    from huggingface_hub.constants import HF_HUB_CACHE\n\n    is_repo = \"/\" in model\n    if os.path.exists(os.path.join(model, config)):\n        is_repo = False\n    elif not is_repo and os.path.exists(os.path.join(HF_HUB_CACHE, model, config)):\n        model = os.path.join(HF_HUB_CACHE, model)\n    elif not is_repo:\n        raise ValueError(f\"{model} is not a valid repo, imported checkpoint, or path\")\n\n    if not is_repo:\n        return {\"main\": model}\n\n    model_path = os.path.join(HF_HUB_CACHE, \"--\".join([\"models\", *model.split(\"/\")]))\n    refs_path = os.path.join(model_path, \"refs\")\n    revisions = {}\n    if not os.path.isdir(refs_path):\n        return revisions\n    for ref in os.listdir(refs_path):\n        with open(os.path.join(refs_path, ref)) as f:\n            commit_hash = f.read()\n        snapshot_path = os.path.join(model_path, \"snapshots\", commit_hash)\n        if os.path.isdir(snapshot_path):\n            revisions[ref] = snapshot_path\n    return revisions\n\n\ndef cache_check(*, exists_callback=None):\n    def decorator(func):\n        def wrapper(cache, model, *args, **kwargs):\n            if model in cache:\n                r = cache[model]\n                if exists_callback is not None:\n                    r = cache[model] = exists_callback(cache, model, r, *args, **kwargs)\n            else:\n                r = cache[model] = func(cache, model, *args, **kwargs)\n            return r\n        return wrapper\n    return decorator\n\n\n@cache_check()\ndef _load_controlnet_model(cache, model, half_precision):\n    from diffusers import ControlNetModel\n    import torch\n\n    if isinstance(model, str) and os.path.isfile(model):\n        model = Checkpoint(model, None)\n\n    if isinstance(model, Checkpoint):\n        control_net_model = ControlNetModel.from_single_file(\n            model.path,\n            config_file=model.config.original_config if isinstance(model.config, ModelConfig) else model.config,\n        )\n        if half_precision:\n            control_net_model.to(torch.float16)\n        return control_net_model\n\n    revisions = revision_paths(model, \"config.json\")\n    if \"main\" not in revisions:\n        # controlnet models shouldn't have a fp16 revision to worry about\n        raise FileNotFoundError(f\"{model} does not contain a main revision\")\n\n    fp16_weights = [\"diffusion_pytorch_model.fp16.safetensors\", \"diffusion_pytorch_model.fp16.bin\"]\n    fp32_weights = [\"diffusion_pytorch_model.safetensors\", \"diffusion_pytorch_model.bin\"]\n    if half_precision:\n        weights_names = fp16_weights + fp32_weights\n    else:\n        weights_names = fp32_weights + fp16_weights\n\n    weights = next((name for name in weights_names if os.path.isfile(os.path.join(revisions[\"main\"], name))), None)\n    if weights is None:\n        raise FileNotFoundError(f\"Can't find appropriate weights in {model}\")\n    half_weights = weights in fp16_weights\n    if not half_precision and half_weights:\n        logger.warning(f\"Can't load fp32 weights for model {model}, attempting to load fp16 instead\")\n\n    return ControlNetModel.from_pretrained(\n        revisions[\"main\"],\n        torch_dtype=torch.float16 if half_precision else None,\n        variant=\"fp16\" if half_weights else None\n    )\n\n\ndef _load_checkpoint(model_class, checkpoint, dtype, **kwargs):\n    from diffusers.pipelines.stable_diffusion.convert_from_ckpt import download_from_original_stable_diffusion_ckpt\n\n    if isinstance(checkpoint, Checkpoint):\n        model = checkpoint.path\n        config = checkpoint.config\n    else:\n        model = checkpoint\n        config = ModelConfig.AUTO_DETECT\n\n    if not os.path.exists(model):\n        raise FileNotFoundError(f\"Can't locate {model}\")\n\n    config_file = config.original_config if isinstance(config, ModelConfig) else config\n    if hasattr(model_class, \"from_single_file\"):\n        return model_class.from_single_file(\n            model,\n            torch_dtype=dtype,\n            original_config_file=config_file,\n            **kwargs\n        )\n    else:\n        # auto pipelines won't support from_single_file() https://github.com/huggingface/diffusers/issues/4367\n        from_pipe = hasattr(model_class, \"from_pipe\")\n        if from_pipe:\n            pipeline_class = config.pipeline if isinstance(config, ModelConfig) else None\n        else:\n            pipeline_class = model_class\n        pipe = download_from_original_stable_diffusion_ckpt(\n            model,\n            from_safetensors=model.endswith(\".safetensors\"),\n            original_config_file=config_file,\n            pipeline_class=pipeline_class,\n            controlnet=kwargs.get(\"controlnet\", None)\n        )\n        if dtype is not None:\n            pipe.to(torch_dtype=dtype)\n        if from_pipe:\n            pipe = model_class.from_pipe(pipe, **kwargs)\n        return pipe\n\n\ndef _convert_pipe(cache, model, pipe, model_class, half_precision, scheduler, **kwargs):\n    if model_class.__name__ not in {\n        # some tasks are not supported by auto pipeline\n        'DreamTexturesDepth2ImgPipeline',\n        'StableDiffusionUpscalePipeline',\n    }:\n        pipe = model_class.from_pipe(pipe, **kwargs)\n    scheduler.create(pipe)\n    return pipe\n\n\n@cache_check(exists_callback=_convert_pipe)\ndef _load_pipeline(cache, model, model_class, half_precision, scheduler, **kwargs):\n    import torch\n\n    dtype = torch.float16 if half_precision else None\n\n    if isinstance(model, Checkpoint) or os.path.splitext(model)[1] in [\".ckpt\", \".safetensors\"]:\n        pipe = _load_checkpoint(model_class, model, dtype, **kwargs)\n        scheduler.create(pipe)\n        return pipe\n\n    revisions = revision_paths(model)\n    strategies = []\n    if \"main\" in revisions:\n        strategies.append({\"model_path\": revisions[\"main\"], \"variant\": \"fp16\" if half_precision else None})\n        if not half_precision:\n            # fp16 variant can automatically use fp32 files, but fp32 won't automatically use fp16 files\n            strategies.append({\"model_path\": revisions[\"main\"], \"variant\": \"fp16\", \"_warn_precision_fallback\": True})\n    if \"fp16\" in revisions:\n        strategies.append({\"model_path\": revisions[\"fp16\"], \"_warn_precision_fallback\": not half_precision})\n\n    if len(strategies) == 0:\n        raise FileNotFoundError(f\"{model} does not contain a main or fp16 revision\")\n\n    exc = None\n    for strat in strategies:\n        if strat.pop(\"_warn_precision_fallback\", False):\n            logger.warning(f\"Can't load fp32 weights for model {model}, attempting to load fp16 instead\")\n        try:\n            pipe = model_class.from_pretrained(strat.pop(\"model_path\"), torch_dtype=dtype, safety_checker=None, requires_safety_checker=False, **strat, **kwargs)\n            pipe.scheduler = scheduler.create(pipe)\n            return pipe\n        except Exception as e:\n            if exc is None:\n                exc = e\n    raise exc\n\n\ndef load_model(self, model_class, model, optimizations, scheduler, controlnet=None, sdxl_refiner_model=None, **kwargs):\n    import torch\n    from diffusers import StableDiffusionXLPipeline, AutoPipelineForImage2Image\n    from diffusers.pipelines.controlnet.multicontrolnet import MultiControlNetModel\n\n    device = self.choose_device(optimizations)\n    half_precision = optimizations.can_use_half(device)\n    invalidation_properties = (device, half_precision, optimizations.cpu_offloading(device), controlnet is not None)\n\n    # determine models to be removed from cache\n    if not hasattr(self, \"_pipe\") or self._pipe is None or self._pipe[0] != invalidation_properties:\n        model_cache = {}\n        self._pipe = (invalidation_properties, model_cache)\n        gc.collect()\n        torch.cuda.empty_cache()\n    else:\n        model_cache = self._pipe[1]\n        expected_models = {model}\n        if sdxl_refiner_model is not None:\n            expected_models.add(sdxl_refiner_model)\n        if controlnet is not None:\n            expected_models.update(name for name in controlnet)\n        clear_models = set(model_cache).difference(expected_models)\n        for name in clear_models:\n            model_cache.pop(name)\n        for pipe in model_cache.items():\n            if isinstance(getattr(pipe, \"controlnet\", None), MultiControlNetModel):\n                # make sure no longer needed ControlNetModels are cleared\n                # the MultiControlNetModel container will be remade\n                pipe.controlnet = None\n        if len(clear_models) > 0:\n            gc.collect()\n            torch.cuda.empty_cache()\n\n    # load or obtain models from cache\n    if controlnet is not None:\n        kwargs[\"controlnet\"] = MultiControlNetModel([\n            _load_controlnet_model(model_cache, name, half_precision) for name in controlnet\n        ])\n    if not isinstance(scheduler, Scheduler):\n        try:\n            scheduler = Scheduler[scheduler]\n        except KeyError:\n            raise ValueError(f\"scheduler expected one of {[s.name for s in Scheduler]}, got {repr(scheduler)}\")\n    pipe = _load_pipeline(model_cache, model, model_class, half_precision, scheduler, **kwargs)\n    if isinstance(pipe, StableDiffusionXLPipeline) and sdxl_refiner_model is not None:\n        return pipe, _load_pipeline(model_cache, sdxl_refiner_model, AutoPipelineForImage2Image, half_precision, scheduler, **kwargs)\n    elif sdxl_refiner_model is not None:\n        if model_cache.pop(sdxl_refiner_model, None) is not None:\n            # refiner was previously used and left enabled but is not compatible with the now selected model\n            gc.collect()\n            torch.cuda.empty_cache()\n        # the caller expects a tuple since refiner was defined\n        return pipe, None\n    return pipe\n"
  },
  {
    "path": "generator_process/actions/outpaint.py",
    "content": "from typing import Tuple, Generator\nimport numpy as np\nfrom ..future import Future\nfrom ...api.models.generation_result import GenerationResult\nfrom ...image_utils import image_to_np, rgba, ImageOrPath\n\ndef outpaint(\n    self,\n\n    image: ImageOrPath,\n\n    width: int | None,\n    height: int | None,\n\n    outpaint_origin: Tuple[int, int],\n\n    **kwargs\n) -> Generator[Future, None, None]:\n\n    future = Future()\n    yield future\n\n    width = width or 512\n    height = height or 512\n    image = image_to_np(image)\n    \n    if outpaint_origin[0] > image.shape[1] or outpaint_origin[0] < -width:\n        raise ValueError(f\"Outpaint origin X ({outpaint_origin[0]}) must be between {-width} and {image.shape[1]}\")\n    if outpaint_origin[1] > image.shape[0] or outpaint_origin[1] < -height:\n        raise ValueError(f\"Outpaint origin Y ({outpaint_origin[1]}) must be between {-height} and {image.shape[0]}\")\n\n    outpaint_bounds = np.zeros((\n        max(image.shape[0], outpaint_origin[1] + height) - min(0, outpaint_origin[1]),\n        max(image.shape[1], outpaint_origin[0] + width) - min(0, outpaint_origin[0]),\n        4\n    ), dtype=np.float32)\n\n    def paste(under, over, offset):\n        under[offset[0]:offset[0] + over.shape[0], offset[1]:offset[1] + over.shape[1]] = over\n        return under\n\n    paste(outpaint_bounds, image, (\n        0 if outpaint_origin[1] > 0 else -outpaint_origin[1],\n        0 if outpaint_origin[0] > 0 else -outpaint_origin[0]\n    ))\n\n    offset_origin = (\n        max(outpaint_origin[1], 0), # upper\n        max(outpaint_origin[0], 0), # left\n    )\n    # Crop out the area to generate\n    inpaint_tile = outpaint_bounds[offset_origin[0]:offset_origin[0]+height, offset_origin[1]:offset_origin[1]+width]\n\n    def process(_, step: [GenerationResult]):\n        for res in step:\n            res.image = paste(outpaint_bounds.copy(), rgba(res.image), offset_origin)\n        future.add_response(step)\n\n    inpaint_generator = self.inpaint(\n        image=inpaint_tile,\n        width=width,\n        height=height,\n        **kwargs\n    )\n    inpaint_future = next(inpaint_generator)\n    inpaint_future.check_cancelled = future.check_cancelled\n    inpaint_future.add_response_callback(process)\n    inpaint_future.add_exception_callback(future.set_exception)\n    for _ in inpaint_generator:\n        pass\n\n    future.set_done()\n"
  },
  {
    "path": "generator_process/actions/prompt_to_image.py",
    "content": "import functools\nfrom typing import Generator\nfrom contextlib import nullcontext\n\nimport numpy as np\nimport random\nfrom ...api.models.seamless_axes import SeamlessAxes\nfrom ...api.models.step_preview_mode import StepPreviewMode\nfrom ..models import Checkpoint, Optimizations, Scheduler\nfrom ..models.image_generation_result import step_latents, step_images\nfrom ..future import Future\n\ndef prompt_to_image(\n    self,\n    \n    model: str | Checkpoint,\n\n    scheduler: str | Scheduler,\n\n    optimizations: Optimizations,\n\n    prompt: str | list[str],\n    steps: int,\n    width: int | None,\n    height: int | None,\n    seed: int,\n\n    cfg_scale: float,\n    use_negative_prompt: bool,\n    negative_prompt: str,\n    \n    seamless_axes: SeamlessAxes | str | bool | tuple[bool, bool] | None,\n\n    iterations: int,\n\n    step_preview_mode: StepPreviewMode,\n\n    # Stability SDK\n    key: str | None = None,\n\n    sdxl_refiner_model: str | Checkpoint | None = None,\n\n    **kwargs\n) -> Generator[Future, None, None]:\n    future = Future()\n    yield future\n\n    import diffusers\n    import torch\n\n    device = self.choose_device(optimizations)\n\n    # Stable Diffusion pipeline w/ caching\n    if sdxl_refiner_model is not None and device == \"cuda\" and (optimizations.cpu_offloading(device) or torch.cuda.mem_get_info()[1] > 20 * 1024**3 * (1 if optimizations.can_use_half(device) else 2)):\n        pipe, refiner = self.load_model(diffusers.AutoPipelineForText2Image, model, optimizations, scheduler, sdxl_refiner_model=sdxl_refiner_model)\n    else:\n        pipe = self.load_model(diffusers.AutoPipelineForText2Image, model, optimizations, scheduler)\n        refiner = None\n    height = height or pipe.unet.config.sample_size * pipe.vae_scale_factor\n    width = width or pipe.unet.config.sample_size * pipe.vae_scale_factor\n\n    # Optimizations\n    pipe = optimizations.apply(pipe, device)\n\n    # RNG\n    batch_size = len(prompt) if isinstance(prompt, list) else 1\n    generator = []\n    for _ in range(batch_size):\n        gen = torch.Generator(device=\"cpu\" if device in (\"mps\", \"dml\") else device) # MPS and DML do not support the `Generator` API\n        generator.append(gen.manual_seed(random.randrange(0, np.iinfo(np.uint32).max) if seed is None else seed))\n    if batch_size == 1:\n        # Some schedulers don't handle a list of generators: https://github.com/huggingface/diffusers/issues/1909\n        generator = generator[0]\n    \n    # Seamless\n    _configure_model_padding(pipe.unet, seamless_axes)\n    _configure_model_padding(pipe.vae, seamless_axes)\n\n    # Inference\n    with torch.inference_mode() if device not in ('mps', \"dml\") else nullcontext():\n        is_sdxl = isinstance(pipe, diffusers.StableDiffusionXLPipeline)\n        output_type = \"latent\" if is_sdxl and sdxl_refiner_model is not None else \"np\"\n        def callback(pipe, step, timestep, callback_kwargs):\n            if future.check_cancelled():\n                raise InterruptedError()\n            future.add_response(step_latents(pipe, step_preview_mode, callback_kwargs[\"latents\"], generator, step, steps))\n            return callback_kwargs\n        try:\n            result = pipe(\n                prompt=prompt,\n                height=height,\n                width=width,\n                num_inference_steps=steps,\n                guidance_scale=cfg_scale,\n                negative_prompt=negative_prompt if use_negative_prompt else None,\n                num_images_per_prompt=1,\n                eta=0.0,\n                generator=generator,\n                latents=None,\n                output_type=output_type,\n                return_dict=True,\n                callback_on_step_end=callback,\n                callback_steps=1,\n                #cfg_end=optimizations.cfg_end\n            )\n            if is_sdxl and sdxl_refiner_model is not None and refiner is None:\n                # allow load_model() to garbage collect pipe\n                pipe = None\n                refiner = self.load_model(diffusers.AutoPipelineForImage2Image, sdxl_refiner_model, optimizations, scheduler)\n            if refiner is not None:\n                refiner = optimizations.apply(refiner, device)\n                result = refiner(\n                    prompt=prompt,\n                    negative_prompt=[\"\"],\n                    callback_on_step_end=callback,\n                    callback_steps=1,\n                    num_inference_steps=steps,\n                    image=result.images,\n                    output_type=\"np\"\n                )\n\n            future.add_response(step_images(result.images, generator, steps, steps))\n        except InterruptedError:\n            pass\n    \n    future.set_done()\n\ndef _conv_forward_asymmetric(self, input, weight, bias):\n    import torch.nn as nn\n    \"\"\"\n    Patch for Conv2d._conv_forward that supports asymmetric padding\n    \"\"\"\n    if input.device.type == \"dml\":\n        # DML pad() will wrongly fill the tensor in constant mode with the supplied value\n        # (default 0) when padding on both ends of a dimension, can't split to two calls.\n        working = nn.functional.pad(input, self._reversed_padding_repeated_twice, mode='circular')\n        pad_w0, pad_w1, pad_h0, pad_h1 = self._reversed_padding_repeated_twice\n        if self.asymmetric_padding_mode[0] == 'constant':\n            working[:, :, :, :pad_w0] = 0\n            if pad_w1 > 0:\n                working[:, :, :, -pad_w1:] = 0\n        if self.asymmetric_padding_mode[1] == 'constant':\n            working[:, :, :pad_h0] = 0\n            if pad_h1 > 0:\n                working[:, :, -pad_h1:] = 0\n    else:\n        working = nn.functional.pad(input, self.asymmetric_padding[0], mode=self.asymmetric_padding_mode[0])\n        working = nn.functional.pad(working, self.asymmetric_padding[1], mode=self.asymmetric_padding_mode[1])\n    return nn.functional.conv2d(working, weight, bias, self.stride, nn.modules.utils._pair(0), self.dilation, self.groups)\n\ndef _lora_compatible_conv_forward(self, hidden_states, scale=1.0):\n    return self._conv_forward(hidden_states, self.weight, self.bias)\n\ndef _configure_model_padding(model, seamless_axes):\n    import torch.nn as nn\n    from diffusers.models.lora import LoRACompatibleConv\n    \"\"\"\n    Modifies the 2D convolution layers to use a circular padding mode based on the `seamless` and `seamless_axes` options.\n    \"\"\"\n    seamless_axes = SeamlessAxes(seamless_axes)\n    if seamless_axes == SeamlessAxes.AUTO:\n        seamless_axes = seamless_axes.OFF\n    if getattr(model, \"seamless_axes\", SeamlessAxes.OFF) == seamless_axes:\n        return\n    model.seamless_axes = seamless_axes\n    for m in model.modules():\n        if isinstance(m, (nn.Conv2d, nn.ConvTranspose2d, LoRACompatibleConv)):\n            if seamless_axes.x or seamless_axes.y:\n                if isinstance(m, LoRACompatibleConv):\n                    m.forward = _lora_compatible_conv_forward.__get__(m, LoRACompatibleConv)\n                m.asymmetric_padding_mode = (\n                    'circular' if seamless_axes.x else 'constant',\n                    'circular' if seamless_axes.y else 'constant'\n                )\n                m.asymmetric_padding = (\n                    (m._reversed_padding_repeated_twice[0], m._reversed_padding_repeated_twice[1], 0, 0),\n                    (0, 0, m._reversed_padding_repeated_twice[2], m._reversed_padding_repeated_twice[3])\n                )\n                m._conv_forward = _conv_forward_asymmetric.__get__(m, nn.Conv2d)\n            else:\n                if isinstance(m, LoRACompatibleConv):\n                    m.forward = LoRACompatibleConv.forward.__get__(m, LoRACompatibleConv)\n                m._conv_forward = nn.Conv2d._conv_forward.__get__(m, nn.Conv2d)\n                if hasattr(m, 'asymmetric_padding_mode'):\n                    del m.asymmetric_padding_mode\n                if hasattr(m, 'asymmetric_padding'):\n                    del m.asymmetric_padding\n"
  },
  {
    "path": "generator_process/actions/upscale.py",
    "content": "import numpy as np\nfrom .prompt_to_image import Optimizations, Scheduler, StepPreviewMode, _configure_model_padding\nfrom ...api.models.seamless_axes import SeamlessAxes\nimport random\nfrom numpy.typing import NDArray\nfrom ..models import Checkpoint, Optimizations, Scheduler, UpscaleTiler, step_images\nfrom ..future import Future\nfrom contextlib import nullcontext\nfrom ...image_utils import rgb, rgba\n\ndef upscale(\n    self,\n    image: NDArray,\n\n    model: str | Checkpoint,\n    \n    prompt: str,\n    steps: int,\n    seed: int,\n    cfg_scale: float,\n    scheduler: Scheduler,\n    \n    tile_size: int,\n    blend: int,\n    seamless_axes: SeamlessAxes | str | bool | tuple[bool, bool] | None,\n\n    optimizations: Optimizations,\n\n    step_preview_mode: StepPreviewMode,\n\n    **kwargs\n):\n    future = Future()\n    yield future\n\n    import torch\n    import diffusers\n\n    device = self.choose_device(optimizations)\n\n    pipe = self.load_model(diffusers.StableDiffusionUpscalePipeline, model, optimizations, scheduler)\n\n    # Optimizations\n    pipe = optimizations.apply(pipe, device)\n\n    # RNG\n    batch_size = len(prompt) if isinstance(prompt, list) else 1\n    generator = []\n    for _ in range(batch_size):\n        gen = torch.Generator(device=\"cpu\" if device in (\"mps\", \"dml\") else device) # MPS and DML do not support the `Generator` API\n        generator.append(gen.manual_seed(random.randrange(0, np.iinfo(np.uint32).max) if seed is None else seed))\n    if batch_size == 1:\n        # Some schedulers don't handle a list of generators: https://github.com/huggingface/diffusers/issues/1909\n        generator = generator[0]\n    \n    # Seamless\n    tiler = UpscaleTiler(image, 4, tile_size, blend, seamless_axes)\n    _configure_model_padding(pipe.unet, seamless_axes & ~tiler.seamless_axes)\n    _configure_model_padding(pipe.vae, seamless_axes & ~tiler.seamless_axes)\n\n    for i in range(0, len(tiler), optimizations.batch_size):\n        if future.check_cancelled():\n            future.set_done()\n            return\n        batch_size = min(len(tiler)-i, optimizations.batch_size)\n        ids = list(range(i, i+batch_size))\n        low_res_tiles = [rgb(tiler[id]) for id in ids]\n        # Inference\n        with torch.inference_mode() if device not in ('mps', \"dml\") else nullcontext():\n            high_res_tiles = pipe(\n                prompt=[prompt[0] if isinstance(prompt, list) else prompt] * batch_size,\n                image=low_res_tiles,\n                num_inference_steps=steps,\n                generator=generator,\n                guidance_scale=cfg_scale,\n                output_type=\"np\"\n            ).images\n\n        for id, tile in zip(ids, high_res_tiles):\n            tiler[id] = rgba(tile)\n\n        if step_preview_mode != StepPreviewMode.NONE:\n            future.add_response(step_images(\n                [tiler.combined()],\n                generator,\n                i + batch_size,\n                len(tiler),\n            ))\n    if step_preview_mode == StepPreviewMode.NONE:\n        future.add_response(step_images(\n            [tiler.combined()],\n            generator,\n            len(tiler),\n            len(tiler)\n        ))\n    future.set_done()\n"
  },
  {
    "path": "generator_process/actor.py",
    "content": "from multiprocessing import Queue, Lock, current_process, get_context\nimport multiprocessing.synchronize\nimport enum\nimport traceback\nimport threading\nfrom typing import Type, TypeVar, Generator\nimport site\nimport sys\nimport os\nfrom ..absolute_path import absolute_path\nfrom .future import Future\n\ndef _patch_zip_direct_transformers_import():\n    # direct_transformers_import() implementation doesn't work when transformers is in a zip archive\n    # since it relies on existing file paths. The function appears to ensure the correct root module\n    # is obtained when there could be another loadable transformers module or it isn't in any sys.path\n    # directory during development testing, both not being a concern in this environment.\n    def direct_transformers_import(*_, **__):\n        import transformers\n        return transformers\n    from transformers.utils import import_utils\n    import_utils.direct_transformers_import = direct_transformers_import\n    from transformers import utils\n    utils.direct_transformers_import = direct_transformers_import\n\ndef _load_dependencies():\n    site.addsitedir(absolute_path(\".python_dependencies\"))\n    deps = sys.path.pop(-1)\n    sys.path.insert(0, deps)\n    if sys.platform == 'win32':\n        # fix for ImportError: DLL load failed while importing cv2: The specified module could not be found.\n        # cv2 needs python3.dll, which is stored in Blender's root directory instead of its python directory.\n        python3_path = os.path.abspath(os.path.join(sys.executable, \"..\\\\..\\\\..\\\\..\\\\python3.dll\"))\n        if os.path.exists(python3_path):\n            os.add_dll_directory(os.path.dirname(python3_path))\n\n        # fix for OSError: [WinError 126] The specified module could not be found. Error loading \"...\\dream_textures\\.python_dependencies\\torch\\lib\\shm.dll\" or one of its dependencies.\n        # Allows for shm.dll from torch==2.3.0 to access dependencies from mkl==2021.4.0\n        # These DLL dependencies are not in the usual places that torch would look at due to being pip installed to a target directory.\n        mkl_bin = absolute_path(\".python_dependencies\\\\Library\\\\bin\")\n        if os.path.exists(mkl_bin):\n            os.add_dll_directory(mkl_bin)\n    \n    if os.path.exists(absolute_path(\".python_dependencies.zip\")):\n        sys.path.insert(1, absolute_path(\".python_dependencies.zip\"))\n        _patch_zip_direct_transformers_import()\n\nmain_thread_rendering = False\nis_actor_process = current_process().name == \"__actor__\"\nif is_actor_process:\n    _load_dependencies()\nelif {\"-b\", \"-f\", \"-a\"}.intersection(sys.argv):\n    main_thread_rendering = True\n    import bpy\n    def main_thread_rendering_finished():\n        # starting without -b will allow Blender to continue running with UI after rendering is complete\n        global main_thread_rendering\n        main_thread_rendering = False\n    bpy.app.timers.register(main_thread_rendering_finished, persistent=True)\n\nclass ActorContext(enum.IntEnum):\n    \"\"\"\n    The context of an `Actor` object.\n    \n    One `Actor` instance is the `FRONTEND`, while the other instance is the backend, which runs in a separate process.\n    The `FRONTEND` sends messages to the `BACKEND`, which does work and returns a result.\n    \"\"\"\n    FRONTEND = 0\n    BACKEND = 1\n\nclass Message:\n    \"\"\"\n    Represents a function signature with a method name, positonal arguments, and keyword arguments.\n\n    Note: All arguments must be picklable.\n    \"\"\"\n\n    def __init__(self, method_name, args, kwargs):\n        self.method_name = method_name\n        self.args = args\n        self.kwargs = kwargs\n    \n    CANCEL = \"__cancel__\"\n    END = \"__end__\"\n\ndef _start_backend(cls, message_queue, response_queue):\n    cls(\n        ActorContext.BACKEND,\n        message_queue=message_queue,\n        response_queue=response_queue\n    ).start()\n\nclass TracedError(BaseException):\n    def __init__(self, base: BaseException, trace: str):\n        self.base = base\n        self.trace = trace\n\nT = TypeVar('T', bound='Actor')\n\nclass Actor:\n    \"\"\"\n    Base class for specialized actors.\n    \n    Uses queues to send actions to a background process and receive a response.\n    Calls to any method declared by the frontend are automatically dispatched to the backend.\n\n    All function arguments must be picklable.\n    \"\"\"\n\n    _message_queue: Queue\n    _response_queue: Queue\n    _lock: multiprocessing.synchronize.Lock\n\n    _shared_instance = None\n\n    # Methods that are not used for message passing, and should not be overridden in `_setup`.\n    _protected_methods = {\n        \"start\",\n        \"close\",\n        \"is_alive\",\n        \"can_use\",\n        \"shared\"\n    }\n\n    def __init__(self, context: ActorContext, message_queue: Queue = None, response_queue: Queue = None):\n        self.context = context\n        self._message_queue = message_queue if message_queue is not None else get_context('spawn').Queue(maxsize=1)\n        self._response_queue = response_queue if response_queue is not None else get_context('spawn').Queue(maxsize=1)\n        self._setup()\n        self.__class__._shared_instance = self\n    \n    def _setup(self):\n        \"\"\"\n        Setup the Actor after initialization.\n        \"\"\"\n        match self.context:\n            case ActorContext.FRONTEND:\n                self._lock = Lock()\n                for name in filter(lambda name: callable(getattr(self, name)) and not name.startswith(\"_\") and name not in self._protected_methods, dir(self)):\n                    setattr(self, name, self._send(name))\n            case ActorContext.BACKEND:\n                pass\n\n    @classmethod\n    def shared(cls: Type[T]) -> T:\n        return cls._shared_instance or cls(ActorContext.FRONTEND).start()\n\n    def start(self: T) -> T:\n        \"\"\"\n        Start the actor process.\n        \"\"\"\n        match self.context:\n            case ActorContext.FRONTEND:\n                self.process = get_context('spawn').Process(target=_start_backend, args=(self.__class__, self._message_queue, self._response_queue), name=\"__actor__\", daemon=True)\n                main_module = sys.modules[\"__main__\"]\n                main_file = getattr(main_module, \"__file__\", None)\n                if main_file == \"<blender string>\":\n                    # Fix for Blender 4.0 not being able to start a subprocess\n                    # while previously installed addons are being initialized.\n                    try:\n                        main_module.__file__ = None\n                        self.process.start()\n                    finally:\n                        main_module.__file__ = main_file\n                else:\n                    self.process.start()\n            case ActorContext.BACKEND:\n                os.environ[\"PYTORCH_ENABLE_MPS_FALLBACK\"] = \"1\"\n                self._backend_loop()\n        return self\n    \n    def close(self):\n        \"\"\"\n        Stop the actor process.\n        \"\"\"\n        match self.context:\n            case ActorContext.FRONTEND:\n                self.process.terminate()\n                self._message_queue.close()\n                self._response_queue.close()\n            case ActorContext.BACKEND:\n                pass\n    \n    @classmethod\n    def shared_close(cls: Type[T]):\n        if cls._shared_instance is None:\n            return\n        cls._shared_instance.close()\n        cls._shared_instance = None\n    \n    def is_alive(self):\n        match self.context:\n            case ActorContext.FRONTEND:\n                return self.process.is_alive()\n            case ActorContext.BACKEND:\n                return True\n\n    def can_use(self):\n        if result := self._lock.acquire(block=False):\n            self._lock.release()\n        return result\n\n    def _backend_loop(self):\n        while True:\n            self._receive(self._message_queue.get())\n\n    def _receive(self, message: Message):\n        try:\n            response = getattr(self, message.method_name)(*message.args, **message.kwargs)\n            if isinstance(response, Generator):\n                for res in iter(response):\n                    extra_message = None\n                    try:\n                        extra_message = self._message_queue.get(block=False)\n                    except:\n                        pass\n                    if extra_message == Message.CANCEL:\n                        break\n                    if isinstance(res, Future):\n                        def check_cancelled():\n                            try:\n                                return self._message_queue.get(block=False) == Message.CANCEL\n                            except:\n                                return False\n                        res.check_cancelled = check_cancelled\n                        res.add_response_callback(lambda _, res: self._response_queue.put(res))\n                        res.add_exception_callback(lambda _, e: self._response_queue.put(RuntimeError(repr(e))))\n                        res.add_done_callback(lambda _: None)\n                    else:\n                        self._response_queue.put(res)\n            else:\n                self._response_queue.put(response)\n        except Exception as e:\n            trace = traceback.format_exc()\n            try:\n                if sys.modules[e.__module__].__file__.startswith(absolute_path(\".python_dependencies\")):\n                    e = RuntimeError(repr(e))\n                    # might be more suitable to have specific substitute exceptions for cases\n                    # like torch.cuda.OutOfMemoryError for frontend handling in the future\n            except (AttributeError, KeyError):\n                pass\n            self._response_queue.put(TracedError(e, trace))\n        self._response_queue.put(Message.END)\n\n    def _send(self, name):\n        def _send(*args, _block=False, **kwargs):\n            if main_thread_rendering:\n                _block = True\n            future = Future()\n            def _send_thread(future: Future):\n                self._lock.acquire()\n                self._message_queue.put(Message(name, args, kwargs))\n\n                while not future.done:\n                    if future.cancelled:\n                        self._message_queue.put(Message.CANCEL)\n                    response = self._response_queue.get()\n                    if response == Message.END:\n                        future.set_done()\n                    elif isinstance(response, TracedError):\n                        response.base.__cause__ = Exception(response.trace)\n                        future.set_exception(response.base)\n                    elif isinstance(response, Exception):\n                        future.set_exception(response)\n                    else:\n                        future.add_response(response)\n                \n                self._lock.release()\n            if _block:\n                _send_thread(future)\n            else:\n                thread = threading.Thread(target=_send_thread, args=(future,), daemon=True)\n                thread.start()\n            return future\n        return _send\n    \n    def __del__(self):\n        self.close()"
  },
  {
    "path": "generator_process/block_in_use.py",
    "content": "def block_in_use(func):\n    def block(self, *args, **kwargs):\n        if self.in_use:\n            raise RuntimeError(f\"Can't call {func.__qualname__} while process is in use\")\n        self.in_use = True\n\n        # generator function is separate so in_use gets set immediately rather than waiting for first next() call\n        def sub():\n            try:\n                yield from func(self, *args, **kwargs)\n            finally:\n                self.in_use = False\n        return sub()\n\n    # Pass the name through so we can use it in `setattr` on `GeneratorProcess`.\n    block.__name__ = func.__name__\n    return block"
  },
  {
    "path": "generator_process/directml_patches.py",
    "content": "import functools\nimport gc\n\nimport torch\nfrom torch import Tensor\n\nactive_dml_patches: list | None = None\n\n\ndef pad(input, pad, mode=\"constant\", value=None, *, pre_patch):\n    if input.device.type == \"dml\" and mode == \"constant\":\n        pad_dims = torch.tensor(pad, dtype=torch.int32).view(-1, 2).flip(0)\n        both_ends = False\n        for pre, post in pad_dims:\n            if pre != 0 and post != 0:\n                both_ends = True\n                break\n        if both_ends:\n            if value is None:\n                value = 0\n            if pad_dims.size(0) < input.ndim:\n                pad_dims = pre_patch(pad_dims, (0, 0, input.ndim-pad_dims.size(0), 0))\n            ret = torch.full(torch.Size(torch.tensor(input.size(), dtype=pad_dims.dtype) + pad_dims.sum(dim=1)),\n                             fill_value=value, dtype=input.dtype, device=input.device)\n            assign_slices = [slice(max(0, int(pre)), None if post <= 0 else -max(0, int(post))) for pre, post in pad_dims]\n            index_slices = [slice(max(0, -int(pre)), None if post >= 0 else -max(0, -int(post))) for pre, post in pad_dims]\n            ret[assign_slices] = input[index_slices]\n            return ret\n    return pre_patch(input, pad, mode=mode, value=value)\n\n\ndef layer_norm(input, normalized_shape, weight = None, bias = None, eps = 1e-05, *, pre_patch):\n    if input.device.type == \"dml\":\n        return pre_patch(input.contiguous(), normalized_shape, weight, bias, eps)\n    return pre_patch(input, normalized_shape, weight, bias, eps)\n\n\ndef retry_OOM(module):\n    if hasattr(module, \"_retry_OOM\"):\n        return\n    forward = module.forward\n\n    def is_OOM(e: RuntimeError):\n        if hasattr(e, \"_retry_OOM\"):\n            return False\n        if len(e.args) == 0:\n            return False\n        if not isinstance(e.args[0], str):\n            return False\n        return (\n            e.args[0].startswith(\"Could not allocate tensor with\") and\n            e.args[0].endswith(\"bytes. There is not enough GPU video memory available!\")\n        )\n\n    def wrapper(*args, **kwargs):\n        try:\n            try:\n                return forward(*args, **kwargs)\n            except RuntimeError as e:\n                if is_OOM(e):\n                    tb = e.__traceback__.tb_next\n                    while tb is not None:\n                        # clear locals from traceback so that intermediate tensors can be garbage collected\n                        # helps recover from Attention blocks more often\n                        tb.tb_frame.clear()\n                        tb = tb.tb_next\n                    # print(\"retrying!\", type(module).__name__)\n                    gc.collect()\n                    return forward(*args, **kwargs)\n                raise\n        except RuntimeError as e:\n            if is_OOM(e):\n                # only retry leaf modules\n                e._retry_OOM = True\n            raise\n\n    module.forward = wrapper\n    module._retry_OOM = True\n\n\ndef enable(pipe):\n    for comp in pipe.components.values():\n        if not isinstance(comp, torch.nn.Module):\n            continue\n        for module in comp.modules():\n            retry_OOM(module)\n\n    global active_dml_patches\n    if active_dml_patches is not None:\n        return\n    active_dml_patches = []\n\n    def dml_patch(object, name, patched):\n        original = getattr(object, name)\n        setattr(object, name, functools.partial(patched, pre_patch=original))\n        active_dml_patches.append({\"object\": object, \"name\": name, \"original\": original})\n\n    def dml_patch_method(object, name, patched):\n        original = getattr(object, name)\n        setattr(object, name, functools.partialmethod(patched, pre_patch=original))\n        active_dml_patches.append({\"object\": object, \"name\": name, \"original\": original})\n\n    dml_patch(torch.nn.functional, \"pad\", pad)\n\n    dml_patch(torch.nn.functional, \"layer_norm\", layer_norm)\n\n    def decorate_forward(name, module):\n        \"\"\"Helper function to better find which modules DML fails in as it often does\n        not raise an exception and immediately crashes the python interpreter.\"\"\"\n        original = module.forward\n\n        def func(self, *args, **kwargs):\n            print(f\"{name} in module {type(self)}\")\n\n            def nan_check(key, x):\n                if isinstance(x, Tensor) and x.dtype in [torch.float16, torch.float32] and x.isnan().any():\n                    raise RuntimeError(f\"{key} got NaN!\")\n\n            for i, v in enumerate(args):\n                nan_check(i, v)\n            for k, v in kwargs.items():\n                nan_check(k, v)\n            r = original(*args, **kwargs)\n            nan_check(\"return\", r)\n            return r\n        module.forward = func.__get__(module)\n\n    # only enable when testing\n    # for name, model in [(\"text_encoder\", pipe.text_encoder), (\"unet\", pipe.unet), (\"vae\", pipe.vae)]:\n    #     for module in model.modules():\n    #         decorate_forward(name, module)\n\n\ndef disable(pipe):\n    global active_dml_patches\n    if active_dml_patches is None:\n        return\n    for patch in active_dml_patches:\n        setattr(patch[\"object\"], patch[\"name\"], patch[\"original\"])\n    active_dml_patches = None\n"
  },
  {
    "path": "generator_process/future.py",
    "content": "import functools\nimport threading\nfrom typing import Callable, Any, MutableSet\n\nclass Future:\n    \"\"\"\n    Object that represents a value that has not completed processing, but will in the future.\n\n    Add callbacks to be notified when values become available, or use `.result()` and `.exception()` to wait for the value.\n    \"\"\"\n    _response_callbacks: MutableSet[Callable[['Future', Any], None]] = set()\n    _exception_callbacks: MutableSet[Callable[['Future', BaseException], None]] = set()\n    _done_callbacks: MutableSet[Callable[['Future'], None]] = set()\n    _responses: list = []\n    _exception: BaseException | None = None\n    _done_event: threading.Event\n    done: bool = False\n    cancelled: bool = False\n    check_cancelled: Callable[[], bool] = lambda: False\n    call_done_on_exception: bool = True\n\n    def __init__(self):\n        self._response_callbacks = set()\n        self._exception_callbacks = set()\n        self._done_callbacks = set()\n        self._responses = []\n        self._exception = None\n        self._done_event = threading.Event()\n        self.done = False\n        self.cancelled = False\n        self.call_done_on_exception = True\n\n    def result(self, last_only=False):\n        \"\"\"\n        Get the result value (blocking).\n        \"\"\"\n        def _response():\n            match len(self._responses):\n                case 0:\n                    return None\n                case 1:\n                    return self._responses[0]\n                case _:\n                    return self._responses[-1] if last_only else self._responses\n        if self._exception is not None:\n            raise self._exception\n        if self.done:\n            return _response()\n        else:\n            self._done_event.wait()\n            if self._exception is not None:\n                raise self._exception\n            return _response()\n    \n    def exception(self):\n        if self.done:\n            return self._exception\n        else:\n            self._done_event.wait()\n            return self._exception\n    \n    def cancel(self):\n        self.cancelled = True\n\n    def _run_on_main_thread(self, func):\n        if threading.current_thread() == threading.main_thread():\n            func()\n            return\n        try:\n            import bpy\n            bpy.app.timers.register(func, persistent=True)\n        except:\n            func()\n\n    def add_response(self, response):\n        \"\"\"\n        Add a response value and notify all consumers.\n        \"\"\"\n        self._responses.append(response)\n        def run_callbacks():\n            for response_callback in self._response_callbacks:\n                response_callback(self, response)\n        self._run_on_main_thread(run_callbacks)\n\n    def set_exception(self, exception: BaseException):\n        \"\"\"\n        Set the exception.\n        \"\"\"\n        self._exception = exception\n        def run_callbacks():\n            for exception_callback in self._exception_callbacks:\n                exception_callback(self, exception)\n        self._run_on_main_thread(run_callbacks)\n\n    def set_done(self):\n        \"\"\"\n        Mark the future as done.\n        \"\"\"\n        assert not self.done\n        self.done = True\n        self._done_event.set()\n        if self._exception is None or self.call_done_on_exception:\n            def run_callbacks():\n                for done_callback in self._done_callbacks:\n                    done_callback(self)\n            self._run_on_main_thread(run_callbacks)\n\n    def add_response_callback(self, callback: Callable[['Future', Any], None]):\n        \"\"\"\n        Add a callback to run whenever a response is received.\n        Will be called multiple times by generator functions.\n        \"\"\"\n        self._response_callbacks.add(callback)\n    \n    def add_exception_callback(self, callback: Callable[['Future', BaseException], None]):\n        \"\"\"\n        Add a callback to run when the future errors.\n        Will only be called once at the first exception.\n        \"\"\"\n        self._exception_callbacks.add(callback)\n        if self._exception is not None:\n            self._run_on_main_thread(functools.partial(callback, self, self._exception))\n\n    def add_done_callback(self, callback: Callable[['Future'], None]):\n        \"\"\"\n        Add a callback to run when the future is marked as done.\n        Will only be called once.\n        \"\"\"\n        self._done_callbacks.add(callback)\n        if self.done:\n            self._run_on_main_thread(functools.partial(callback, self))\n"
  },
  {
    "path": "generator_process/models/__init__.py",
    "content": "from .checkpoint import *\nfrom .image_generation_result import *\nfrom .model_config import *\nfrom .model_type import *\nfrom .optimizations import *\nfrom .scheduler import *\nfrom .upscale_tiler import *"
  },
  {
    "path": "generator_process/models/checkpoint.py",
    "content": "from dataclasses import dataclass\n\nfrom .model_config import ModelConfig\n\n\n@dataclass(frozen=True)\nclass Checkpoint:\n    path: str\n    config: ModelConfig | str | None\n"
  },
  {
    "path": "generator_process/models/image_generation_result.py",
    "content": "from ...api.models.step_preview_mode import StepPreviewMode\nfrom ...api.models.generation_result import GenerationResult\n\ndef step_latents(pipe, mode, latents, generator, iteration, steps):\n    seeds = [gen.initial_seed() for gen in generator] if isinstance(generator, list) else [generator.initial_seed()]\n    scale = 2 ** (len(pipe.vae.config.block_out_channels) - 1)\n    match mode:\n        case StepPreviewMode.FAST:\n            return [\n                GenerationResult(\n                    progress=iteration,\n                    total=steps,\n                    seed=seeds[-1],\n                    image=approximate_decoded_latents(latents[-1:], scale)\n                )\n            ]\n        case StepPreviewMode.FAST_BATCH:\n            return [\n                GenerationResult(\n                    progress=iteration,\n                    total=steps,\n                    seed=seed,\n                    image=approximate_decoded_latents(latent, scale)\n                )\n                for latent, seed in zip(latents[:, None], seeds)\n            ]\n        case StepPreviewMode.ACCURATE:\n            return [\n                GenerationResult(\n                    progress=iteration,\n                    total=steps,\n                    seed=seeds[-1],\n                    image=decode_latents(pipe, latents[-1:])\n                )\n            ]\n        case StepPreviewMode.ACCURATE_BATCH:\n            return [\n                GenerationResult(\n                    progress=iteration,\n                    total=steps,\n                    seed=seed,\n                    image=decode_latents(pipe, latent)\n                )\n                for latent, seed in zip(latents[:, None], seeds)\n            ]\n    return [\n        GenerationResult(\n            progress=iteration,\n            total=steps,\n            seed=seeds[-1]\n        )\n    ]\n\ndef step_images(images, generator, iteration, steps):\n    if not isinstance(images, list) and images.ndim == 3:\n        images = images[None]\n    seeds = [gen.initial_seed() for gen in generator] if isinstance(generator, list) else [generator.initial_seed()]\n    return [\n        GenerationResult(\n            progress=iteration,\n            total=steps,\n            seed=seed,\n            image=image\n        )\n        for image, seed in zip(images, seeds)\n    ]\n\ndef decode_latents(pipe, latents):\n    return pipe.image_processor.postprocess(pipe.vae.decode(latents / pipe.vae.config.scaling_factor).sample, output_type=\"np\")\n\ndef approximate_decoded_latents(latents, scale=None):\n    \"\"\"\n    Approximate the decoded latents without using the VAE.\n    \"\"\"\n    import torch\n    # origingally adapted from code by @erucipe and @keturn here:\n    # https://discuss.huggingface.co/t/decoding-latents-to-rgb-without-upscaling/23204/7\n\n    # these updated numbers for v1.5 are from @torridgristle\n    v1_5_latent_rgb_factors = torch.tensor([\n        #    R        G        B\n        [ 0.3444,  0.1385,  0.0670], # L1\n        [ 0.1247,  0.4027,  0.1494], # L2\n        [-0.3192,  0.2513,  0.2103], # L3\n        [-0.1307, -0.1874, -0.7445]  # L4\n    ], dtype=latents.dtype, device=latents.device)\n\n    latent_image = latents[0].permute(1, 2, 0) @ v1_5_latent_rgb_factors\n    if scale is not None:\n        latent_image = torch.nn.functional.interpolate(\n            latent_image.permute(2, 0, 1).unsqueeze(0), scale_factor=scale, mode=\"nearest\"\n        ).squeeze(0).permute(1, 2, 0)\n    latent_image = ((latent_image + 1) / 2).clamp(0, 1).cpu()\n    return latent_image.numpy()\n"
  },
  {
    "path": "generator_process/models/model_config.py",
    "content": "import enum\n\nfrom ...absolute_path import absolute_path\n\n\nclass ModelConfig(enum.Enum):\n    AUTO_DETECT = \"auto-detect\"\n    STABLE_DIFFUSION_1 = \"v1\"\n    STABLE_DIFFUSION_2_BASE = \"v2 (512, epsilon)\"\n    STABLE_DIFFUSION_2 = \"v2 (768, v_prediction)\"\n    STABLE_DIFFUSION_2_DEPTH = \"v2 (depth)\"\n    STABLE_DIFFUSION_2_INPAINTING = \"v2 (inpainting)\"\n    STABLE_DIFFUSION_XL_BASE = \"XL (base)\"\n    STABLE_DIFFUSION_XL_REFINER = \"XL (refiner)\"\n    CONTROL_NET_1_5 = \"1.5 (ControlNet)\"\n    CONTROL_NET_2_1 = \"2.1 (ControlNet)\"\n\n    @property\n    def original_config(self):\n        match self:\n            case ModelConfig.AUTO_DETECT:\n                return None\n            case ModelConfig.STABLE_DIFFUSION_1:\n                return absolute_path(\"sd_configs/v1-inference.yaml\")\n            case ModelConfig.STABLE_DIFFUSION_2_BASE:\n                return absolute_path(\"sd_configs/v2-inference.yaml\")\n            case ModelConfig.STABLE_DIFFUSION_2:\n                return absolute_path(\"sd_configs/v2-inference-v.yaml\")\n            case ModelConfig.STABLE_DIFFUSION_2_DEPTH:\n                return absolute_path(\"sd_configs/v2-midas-inference.yaml\")\n            case ModelConfig.STABLE_DIFFUSION_2_INPAINTING:\n                return absolute_path(\"sd_configs/v2-inpainting-inference.yaml\")\n            case ModelConfig.STABLE_DIFFUSION_XL_BASE:\n                return absolute_path(\"sd_configs/sd_xl_base.yaml\")\n            case ModelConfig.STABLE_DIFFUSION_XL_REFINER:\n                return absolute_path(\"sd_configs/sd_xl_refiner.yaml\")\n            case ModelConfig.CONTROL_NET_1_5:\n                return absolute_path(\"sd_configs/cldm_v15.yaml\")\n            case ModelConfig.CONTROL_NET_2_1:\n                return absolute_path(\"sd_configs/cldm_v21.yaml\")\n\n    @property\n    def pipeline(self):\n        # allows for saving with correct _class_name in model_index.json and necessary for some models to import\n        import diffusers\n        match self:\n            case ModelConfig.AUTO_DETECT:\n                return None\n            case ModelConfig.STABLE_DIFFUSION_2_DEPTH:\n                return diffusers.StableDiffusionDepth2ImgPipeline\n            case ModelConfig.STABLE_DIFFUSION_2_INPAINTING:\n                return diffusers.StableDiffusionInpaintPipeline\n            case ModelConfig.STABLE_DIFFUSION_XL_BASE:\n                return diffusers.StableDiffusionXLPipeline\n            case ModelConfig.STABLE_DIFFUSION_XL_REFINER:\n                return diffusers.StableDiffusionXLImg2ImgPipeline\n            case ModelConfig.CONTROL_NET_1_5 | ModelConfig.CONTROL_NET_2_1:\n                return diffusers.ControlNetModel\n            case _:\n                return diffusers.StableDiffusionPipeline\n"
  },
  {
    "path": "generator_process/models/model_type.py",
    "content": "import enum\n\nfrom ...api.models.task import *\nfrom .model_config import ModelConfig\n\n\nclass ModelType(enum.IntEnum):\n    \"\"\"\n    Inferred model type from the U-Net `in_channels`.\n    \"\"\"\n    UNKNOWN = 0\n    PROMPT_TO_IMAGE = 4\n    DEPTH = 5\n    UPSCALING = 7\n    INPAINTING = 9\n\n    CONTROL_NET = -1\n    UNSPECIFIED_CHECKPOINT = -2\n\n    @classmethod\n    def _missing_(cls, _):\n        return cls.UNKNOWN\n\n    def recommended_model(self) -> str:\n        \"\"\"Provides a recommended model for a given task.\n\n        This method has a bias towards the latest version of official Stability AI models.\n        \"\"\"\n        match self:\n            case ModelType.PROMPT_TO_IMAGE:\n                return \"stabilityai/stable-diffusion-2-1\"\n            case ModelType.DEPTH:\n                return \"stabilityai/stable-diffusion-2-depth\"\n            case ModelType.UPSCALING:\n                return \"stabilityai/stable-diffusion-x4-upscaler\"\n            case ModelType.INPAINTING:\n                return \"stabilityai/stable-diffusion-2-inpainting\"\n            case _:\n                return \"stabilityai/stable-diffusion-2-1\"\n\n    def matches_task(self, task: Task) -> bool:\n        \"\"\"Indicates if the model type is correct for a given `Task`.\n\n        If not an error should be shown to the user to select a different model.\n        \"\"\"\n        if self == ModelType.UNSPECIFIED_CHECKPOINT:\n            return True\n        match task:\n            case PromptToImage():\n                return self == ModelType.PROMPT_TO_IMAGE\n            case Inpaint():\n                return self == ModelType.INPAINTING\n            case DepthToImage():\n                return self == ModelType.DEPTH\n            case Outpaint():\n                return self == ModelType.INPAINTING\n            case ImageToImage():\n                return self == ModelType.PROMPT_TO_IMAGE\n            case _:\n                return False\n\n    @staticmethod\n    def from_task(task: Task) -> 'ModelType | None':\n        match task:\n            case PromptToImage():\n                return ModelType.PROMPT_TO_IMAGE\n            case Inpaint():\n                return ModelType.INPAINTING\n            case DepthToImage():\n                return ModelType.DEPTH\n            case Outpaint():\n                return ModelType.INPAINTING\n            case ImageToImage():\n                return ModelType.PROMPT_TO_IMAGE\n            case _:\n                return None\n\n    @staticmethod\n    def from_config(config: ModelConfig):\n        match config:\n            case ModelConfig.AUTO_DETECT:\n                return ModelType.UNSPECIFIED_CHECKPOINT\n            case ModelConfig.STABLE_DIFFUSION_2_DEPTH:\n                return ModelType.DEPTH\n            case ModelConfig.STABLE_DIFFUSION_2_INPAINTING:\n                return ModelType.INPAINTING\n            case ModelConfig.CONTROL_NET_1_5 | ModelConfig.CONTROL_NET_2_1:\n                return ModelType.CONTROL_NET\n            case _:\n                return ModelType.PROMPT_TO_IMAGE\n"
  },
  {
    "path": "generator_process/models/optimizations.py",
    "content": "from enum import Enum\nfrom typing import Annotated, Union, _AnnotatedAlias\nimport functools\nimport os\nimport sys\nfrom dataclasses import dataclass\n\nfrom .upscale_tiler import tiled_decode_latents\n\n\nclass CPUOffload(Enum):\n    OFF = \"off\"\n    MODEL = \"model\"\n    SUBMODULE = \"submodule\"\n\n    def __bool__(self):\n        return self != CPUOffload.OFF\n\n\n@dataclass(eq=True)\nclass Optimizations:\n    attention_slicing: bool = True\n    attention_slice_size: Union[str, int] = \"auto\"\n    cudnn_benchmark: Annotated[bool, \"cuda\"] = False\n    tf32: Annotated[bool, \"cuda\"] = False\n    amp: Annotated[bool, \"cuda\"] = False\n    half_precision: Annotated[bool, {\"cuda\", \"dml\"}] = True\n    cpu_offload: Annotated[str, {\"cuda\", \"dml\"}] = CPUOffload.OFF\n    channels_last_memory_format: bool = False\n    sdp_attention: bool = True\n    batch_size: int = 1\n    vae_slicing: bool = True\n    vae_tiling: str = \"off\"\n    vae_tile_size: int = 512\n    vae_tile_blend: int = 64\n    cfg_end: float = 1.0\n\n    cpu_only: bool = False\n\n    @staticmethod\n    def infer_device() -> str:\n        from ...absolute_path import absolute_path\n        if sys.platform == \"darwin\":\n            return \"mps\"\n        elif os.path.exists(absolute_path(\".python_dependencies/torch_directml\")):\n            return \"dml\"\n        else:\n            return \"cuda\"\n\n    @classmethod\n    def device_supports(cls, property, device) -> bool:\n        annotation = cls.__annotations__.get(property, None)\n        if isinstance(annotation, _AnnotatedAlias):\n            opt_dev = annotation.__metadata__[0]\n            if isinstance(opt_dev, str):\n                return opt_dev == device\n            return device in opt_dev\n        return annotation is not None\n\n    def can_use(self, property, device) -> bool:\n        return self.device_supports(property, device) and getattr(self, property)\n\n    def can_use_half(self, device):\n        if self.half_precision and device == \"cuda\":\n            import torch\n            name = torch.cuda.get_device_name()\n            return not (\"GTX 1650\" in name or \"GTX 1660\" in name)\n        return self.can_use(\"half_precision\", device)\n\n    def cpu_offloading(self, device):\n        return self.cpu_offload if self.device_supports(\"cpu_offload\", device) else CPUOffload.OFF\n    \n    def apply(self, pipeline, device):\n        \"\"\"\n        Apply the optimizations to a diffusers pipeline.\n\n        All exceptions are ignored to make this more general purpose across different pipelines.\n        \"\"\"\n        import torch\n\n        if not self.cpu_offloading(device):\n            pipeline = pipeline.to(device)\n\n        torch.backends.cudnn.benchmark = self.can_use(\"cudnn_benchmark\", device)\n        torch.backends.cuda.matmul.allow_tf32 = self.can_use(\"tf32\", device)\n\n        try:\n            if self.can_use(\"sdp_attention\", device):\n                from diffusers.models.attention_processor import AttnProcessor2_0\n                pipeline.unet.set_attn_processor(AttnProcessor2_0())\n            elif self.can_use(\"attention_slicing\", device):\n                pipeline.enable_attention_slicing(self.attention_slice_size)\n            else:\n                pipeline.disable_attention_slicing()  # will also disable AttnProcessor2_0\n        except: pass\n        \n        try:\n            if pipeline.device != pipeline._execution_device:\n                pass # pipeline is already offloaded, offloading again can cause `pipeline._execution_device` to be incorrect\n            elif self.cpu_offloading(device) == CPUOffload.MODEL:\n                # adapted from diffusers.StableDiffusionPipeline.enable_model_cpu_offload() to allow DirectML device and unimplemented pipelines\n                from accelerate import cpu_offload_with_hook\n\n                hook = None\n                models = []\n                # text_encoder can be None in SDXL Pipeline but not text_encoder_2\n                if pipeline.text_encoder is not None:\n                    models.append(pipeline.text_encoder)\n                if hasattr(pipeline, \"text_encoder_2\"):\n                    models.append(pipeline.text_encoder_2)\n                models.extend([pipeline.unet, pipeline.vae])\n                if hasattr(pipeline, \"controlnet\"):\n                    models.append(pipeline.controlnet)\n                for cpu_offloaded_model in models:\n                    _, hook = cpu_offload_with_hook(cpu_offloaded_model, device, prev_module_hook=hook)\n\n                if getattr(pipeline, \"safety_checker\", None) is not None:\n                    _, hook = cpu_offload_with_hook(pipeline.safety_checker, device, prev_module_hook=hook)\n\n                # We'll offload the last model manually.\n                pipeline.final_offload_hook = hook\n            elif self.cpu_offloading(device) == CPUOffload.SUBMODULE:\n                # adapted from diffusers.StableDiffusionPipeline.enable_sequential_cpu_offload() to allow DirectML device and unimplemented pipelines\n                from accelerate import cpu_offload\n\n                models = []\n                # text_encoder can be None in SDXL Pipeline but not text_encoder_2\n                if pipeline.text_encoder is not None:\n                    models.append(pipeline.text_encoder)\n                if hasattr(pipeline, \"text_encoder_2\"):\n                    models.append(pipeline.text_encoder_2)\n                models.extend([pipeline.unet, pipeline.vae])\n                if hasattr(pipeline, \"controlnet\"):\n                    models.append(pipeline.controlnet)\n                for cpu_offloaded_model in models:\n                    cpu_offload(cpu_offloaded_model, device)\n\n                if getattr(pipeline, \"safety_checker\", None) is not None:\n                    cpu_offload(pipeline.safety_checker, device, offload_buffers=True)\n        except: pass\n        \n        try:\n            if self.can_use(\"channels_last_memory_format\", device):\n                pipeline.unet.to(memory_format=torch.channels_last)\n            else:\n                pipeline.unet.to(memory_format=torch.contiguous_format)\n        except: pass\n\n        try:\n            if self.can_use(\"vae_slicing\", device):\n                # Not many pipelines implement the enable_vae_slicing()/disable_vae_slicing()\n                # methods but all they do is forward their call to the vae anyway.\n                pipeline.vae.enable_slicing()\n            else:\n                pipeline.vae.disable_slicing()\n        except: pass\n\n        try:\n            if self.vae_tiling != \"off\":\n                if not isinstance(pipeline.vae.decode, functools.partial):\n                    pipeline.vae.decode = functools.partial(tiled_decode_latents.__get__(pipeline), pre_patch=pipeline.vae.decode)\n                pipeline.vae.decode.keywords['optimizations'] = self\n            elif self.vae_tiling == \"off\" and isinstance(pipeline.vae.decode, functools.partial):\n                pipeline.vae.decode = pipeline.vae.decode.keywords[\"pre_patch\"]\n        except: pass\n        \n        from .. import directml_patches\n        if device == \"dml\":\n            directml_patches.enable(pipeline)\n        else:\n            directml_patches.disable(pipeline)\n\n        return pipeline"
  },
  {
    "path": "generator_process/models/scheduler.py",
    "content": "import enum\n\nclass Scheduler(enum.Enum):\n    DDIM = \"DDIM\"\n    DDPM = \"DDPM\"\n    DEIS_MULTISTEP = \"DEIS Multistep\"\n    DPM_SOLVER_MULTISTEP = \"DPM Solver Multistep\"\n    DPM_SOLVER_MULTISTEP_KARRAS = \"DPM Solver Multistep Karras\"\n    DPM_SOLVER_SINGLESTEP = \"DPM Solver Singlestep\"\n    DPM_SOLVER_SINGLESTEP_KARRAS = \"DPM Solver Singlestep Karras\"\n    EULER_DISCRETE = \"Euler Discrete\"\n    EULER_DISCRETE_KARRAS = \"Euler Discrete Karras\"\n    EULER_ANCESTRAL_DISCRETE = \"Euler Ancestral Discrete\"\n    HEUN_DISCRETE = \"Heun Discrete\"\n    HEUN_DISCRETE_KARRAS = \"Heun Discrete Karras\"\n    KDPM2_DISCRETE = \"KDPM2 Discrete\" # Non-functional on mps\n    KDPM2_ANCESTRAL_DISCRETE = \"KDPM2 Ancestral Discrete\"\n    LMS_DISCRETE = \"LMS Discrete\"\n    LMS_DISCRETE_KARRAS = \"LMS Discrete Karras\"\n    PNDM = \"PNDM\"\n    UNIPC_MULTISTEP = \"UniPC Multistep\"\n\n    def create(self, pipeline):\n        import diffusers\n        def scheduler_class():\n            match self:\n                case Scheduler.DDIM:\n                    return diffusers.schedulers.DDIMScheduler\n                case Scheduler.DDPM:\n                    return diffusers.schedulers.DDPMScheduler\n                case Scheduler.DEIS_MULTISTEP:\n                    return diffusers.schedulers.DEISMultistepScheduler\n                case Scheduler.DPM_SOLVER_MULTISTEP | Scheduler.DPM_SOLVER_MULTISTEP_KARRAS:\n                    return diffusers.schedulers.DPMSolverMultistepScheduler\n                case Scheduler.DPM_SOLVER_SINGLESTEP | Scheduler.DPM_SOLVER_SINGLESTEP_KARRAS:\n                    return diffusers.schedulers.DPMSolverSinglestepScheduler\n                case Scheduler.EULER_DISCRETE | Scheduler.EULER_DISCRETE_KARRAS:\n                    return diffusers.schedulers.EulerDiscreteScheduler\n                case Scheduler.EULER_ANCESTRAL_DISCRETE:\n                    return diffusers.schedulers.EulerAncestralDiscreteScheduler\n                case Scheduler.HEUN_DISCRETE | Scheduler.HEUN_DISCRETE_KARRAS:\n                    return diffusers.schedulers.HeunDiscreteScheduler\n                case Scheduler.KDPM2_DISCRETE:\n                    return diffusers.schedulers.KDPM2DiscreteScheduler\n                case Scheduler.KDPM2_ANCESTRAL_DISCRETE:\n                    return diffusers.schedulers.KDPM2AncestralDiscreteScheduler\n                case Scheduler.LMS_DISCRETE | Scheduler.LMS_DISCRETE_KARRAS:\n                    return diffusers.schedulers.LMSDiscreteScheduler\n                case Scheduler.PNDM:\n                    return diffusers.schedulers.PNDMScheduler\n                case Scheduler.UNIPC_MULTISTEP:\n                    return diffusers.schedulers.UniPCMultistepScheduler\n        original_config = getattr(pipeline.scheduler, \"_original_config\", pipeline.scheduler.config)\n        scheduler = scheduler_class().from_config(original_config, use_karras_sigmas=self.name.endswith(\"KARRAS\"))\n        scheduler._original_config = original_config\n        pipeline.scheduler = scheduler\n        return scheduler\n"
  },
  {
    "path": "generator_process/models/upscale_tiler.py",
    "content": "import math\nfrom typing import Optional\n\nimport numpy as np\nfrom ..actions.detect_seamless import SeamlessAxes\nfrom numpy.typing import NDArray\n\n\nclass UpscaleTiler:\n    def __init__(\n            self,\n            image: NDArray,\n            scale: int,\n            tile_size: int | tuple[int, int],\n            blend: int | tuple[int, int],\n            seamless_axes: SeamlessAxes,\n            defer_seamless: bool = True,\n            out_channels: Optional[int] = None\n    ):\n        height, width = image.shape[:2]\n        if scale < 1:\n            raise ValueError(\"scale must be 1 or higher\")\n        if isinstance(tile_size, int):\n            tile_size = (tile_size, tile_size)\n        if tile_size[0] <= 0 or tile_size[1] <= 0:\n            raise ValueError(\"tile size must be 1 or higher\")\n        if isinstance(blend, int):\n            blend = (blend, blend)\n        if blend[0] < 0 or blend[1] < 0:\n            raise ValueError(\"blend must be 0 or higher\")\n        seamless_axes = SeamlessAxes(seamless_axes)\n        if defer_seamless:\n            # Seamless handling may be deferred to upscaler model or VAE rather than using larger or multiple tiles\n            seamless_axes = SeamlessAxes((seamless_axes.x and width > tile_size[0], seamless_axes.y and height > tile_size[1]))\n        max_width = width*2 if seamless_axes.x else width\n        max_height = height*2 if seamless_axes.y else height\n        tile_size = (min(tile_size[0], max_width), min(tile_size[1], max_height))\n        blend = (min(blend[0], math.ceil(tile_size[0]/2)), min(blend[1], math.ceil(tile_size[1]/2)))\n        self.image = image\n        self.scale = scale\n        self.tile_size = tile_size\n        self.blend = blend\n        self.seamless_axes = seamless_axes\n        self.x_tiles = self.axis_tiles(width, tile_size[0], blend[0], seamless_axes.x)\n        self.y_tiles = self.axis_tiles(height, tile_size[1], blend[1], seamless_axes.y)\n        if out_channels is None:\n            out_channels = image.shape[2]\n        # combined image with last channel containing pixel weights\n        self.canvas = np.zeros((image.shape[0] * scale, image.shape[1] * scale, out_channels + 1), dtype=np.float32)\n\n        scaled_tile_size = (tile_size[0] * scale, tile_size[1] * scale)\n        weight_gradient_y = [min(i + 1, scaled_tile_size[1] - i) for i in range(scaled_tile_size[1])]\n        weight_gradient_x = [min(i + 1, scaled_tile_size[0] - i) for i in range(scaled_tile_size[0])]\n        tile_weight = np.zeros(scaled_tile_size, dtype=np.float32)\n        tile_weight[:] = weight_gradient_y\n        # determines how much each pixel in a blended area influences the final color, basically a pyramid\n        self.tile_weight = np.minimum(tile_weight, np.reshape(weight_gradient_x, (scaled_tile_size[0], 1)))\n\n    @staticmethod\n    def axis_tiles(axis_size: int, tile_size: int, blend: int, seamless: bool) -> list[int]:\n        \"\"\"\n        Returns a list of values where each tile starts on an axis.\n        Blend is only guaranteed as a minimum and may vary by a pixel between tiles.\n        \"\"\"\n        if seamless:\n            count = math.ceil(axis_size / (tile_size - blend))\n            blend_balance = math.ceil(tile_size - axis_size / count)\n            final = min(axis_size - tile_size + blend_balance, axis_size * 2 - tile_size)\n        else:\n            count = math.ceil((axis_size - tile_size) / (tile_size - blend)) + 1\n            final = axis_size - tile_size\n        if count == 1:\n            return [0]\n        return [i * final // (count - 1) for i in range(count)]\n\n    def combined(self) -> NDArray:\n        return self.canvas[:, :, :-1]\n\n    def index_to_xy(self, index: int):\n        key_y = index % len(self.y_tiles)\n        key_x = (index - key_y) // len(self.y_tiles)\n        return key_x, key_y\n\n    def __getitem__(self, key: int | tuple[int, int]) -> NDArray:\n        if isinstance(key, int):\n            key = self.index_to_xy(key)\n        image = self.image\n        tile_size = self.tile_size\n        x0 = self.x_tiles[key[0]]\n        x1 = x0 + tile_size[0]\n        x2 = image.shape[1] - x0\n        y0 = self.y_tiles[key[1]]\n        y1 = y0 + tile_size[1]\n        y2 = image.shape[0] - y0\n        if x2 >= tile_size[0] and y2 >= tile_size[1]:\n            return image[y0:y1, x0:x1]\n        # seamless axis wrapping\n        if isinstance(image, np.ndarray):\n            tile = np.empty((tile_size[0], tile_size[1], image.shape[2]), dtype=image.dtype)\n        else:\n            import torch\n            tile = torch.empty((tile_size[0], tile_size[1], image.shape[2]), dtype=image.dtype, device=image.device)\n        if x2 < tile_size[0]:\n            if y2 < tile_size[1]:\n                # wrap bottom/right to top/left\n                tile[:y2, :x2] = image[y0:, x0:]\n                tile[y2:, :x2] = image[:tile_size[1] - y2, x0:]\n                tile[:y2, x2:] = image[y0:, :tile_size[0] - x2]\n                tile[y2:, x2:] = image[:tile_size[1] - y2, :tile_size[0] - x2]\n            else:\n                # wrap right to left\n                tile[:, :x2] = image[y0:y1, x0:]\n                tile[:, x2:] = image[y0:y1, :tile_size[0] - x2]\n        else:\n            # wrap bottom to top\n            tile[:y2] = image[y0:, x0:x1]\n            tile[y2:] = image[:tile_size[1] - y2, x0:x1]\n        return tile\n\n    def __setitem__(self, key: int | tuple[int, int], tile: NDArray):\n        if isinstance(key, int):\n            key = self.index_to_xy(key)\n        canvas = self.canvas\n        scale = self.scale\n        tile_size = (self.tile_size[0] * scale, self.tile_size[1] * scale)\n        tile_weight = self.tile_weight\n        x0 = self.x_tiles[key[0]] * scale\n        x1 = x0 + tile_size[0]\n        x2 = canvas.shape[1] - x0\n        y0 = self.y_tiles[key[1]] * scale\n        y1 = y0 + tile_size[1]\n        y2 = canvas.shape[0] - y0\n\n        def update(canvas_slice, tile_slice, weight_slice):\n            weight_slice = weight_slice.reshape(weight_slice.shape[0], weight_slice.shape[1], 1)\n            # undo weighted average, then add new tile with its weights applied and average again\n            canvas_slice[:, :, :-1] *= canvas_slice[:, :, -1:]\n            canvas_slice[:, :, :-1] += tile_slice * weight_slice\n            canvas_slice[:, :, -1:] += weight_slice\n            canvas_slice[:, :, :-1] /= canvas_slice[:, :, -1:]\n\n        if x2 >= tile_size[0] and y2 >= tile_size[1]:\n            update(canvas[y0:y1, x0:x1], tile, tile_weight)\n        elif x2 < tile_size[0]:\n            if y2 < tile_size[1]:\n                update(canvas[y0:, x0:], tile[:y2, :x2], tile_weight[:y2, :x2])\n                update(canvas[:tile_size[1] - y2, x0:], tile[y2:, :x2], tile_weight[y2:, :x2])\n                update(canvas[y0:, :tile_size[0] - x2], tile[:y2, x2:], tile_weight[:y2, x2:])\n                update(canvas[:tile_size[1] - y2, :tile_size[0] - x2], tile[y2:, x2:], tile_weight[y2:, x2:])\n            else:\n                update(canvas[y0:y1, x0:], tile[:, :x2], tile_weight[:, :x2])\n                update(canvas[y0:y1, :tile_size[0] - x2], tile[:, x2:], tile_weight[:, x2:])\n        else:\n            update(canvas[y0:, x0:x1], tile[:y2], tile_weight[:y2])\n            update(canvas[:tile_size[1] - y2, x0:x1], tile[y2:], tile_weight[y2:])\n\n    def __iter__(self):\n        for x in range(len(self.x_tiles)):\n            for y in range(len(self.y_tiles)):\n                yield (x, y), self[x, y]\n\n    def __len__(self):\n        return len(self.x_tiles) * len(self.y_tiles)\n\n\ndef tiled_decode_latents(self, latents, return_dict=False, *, pre_patch, optimizations):\n    # not all pipelines (namely upscale) have the vae_scale_factor attribute\n    vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)\n    default_size = self.unet.config.sample_size * vae_scale_factor\n    match optimizations.vae_tiling:\n        case \"full\":\n            tile_size = default_size\n            blend = math.ceil(tile_size / 8)\n        case \"half\":\n            tile_size = math.ceil(default_size / 2)\n            blend = math.ceil(tile_size / 8)\n        case \"manual\":\n            tile_size = optimizations.vae_tile_size\n            blend = optimizations.vae_tile_blend\n        case _:\n            return pre_patch(latents)\n\n    seamless_axes = getattr(self.vae, \"seamless_axes\", SeamlessAxes.OFF)\n\n    images = []\n    for image_latents in latents.split(1, dim=0):\n        tiler = UpscaleTiler(\n            image_latents.squeeze(0).permute(1, 2, 0),\n            vae_scale_factor,\n            math.ceil(tile_size / vae_scale_factor),\n            math.ceil(blend / vae_scale_factor),\n            seamless_axes,\n            out_channels=self.vae.config.out_channels\n        )\n\n        configure_model_padding(self.vae, seamless_axes & ~tiler.seamless_axes)\n\n        for id, tile in tiler:\n            tiler[id] = pre_patch(tile.permute(2, 0, 1).unsqueeze(0)).sample.squeeze(0).permute(1, 2, 0).cpu().numpy()\n        images.append(np.expand_dims(tiler.combined(), 0).transpose(0, 3, 1, 2))\n    configure_model_padding(self.vae, seamless_axes)\n    images = np.concatenate(images)\n    import torch\n    images = torch.from_numpy(images)\n    if not return_dict:\n        return (images,)\n    from diffusers.models.vae import DecoderOutput\n    return DecoderOutput(images)\n\ndef configure_model_padding(model, seamless_axes):\n    import torch.nn as nn\n    \"\"\"\n    Modifies the 2D convolution layers to use a circular padding mode based on the `seamless_axes` option.\n    \"\"\"\n    seamless_axes = SeamlessAxes(seamless_axes)\n    if seamless_axes == SeamlessAxes.AUTO:\n        seamless_axes = seamless_axes.OFF\n    if getattr(model, \"seamless_axes\", SeamlessAxes.OFF) == seamless_axes:\n        return\n    model.seamless_axes = seamless_axes\n    for m in model.modules():\n        if isinstance(m, (nn.Conv2d, nn.ConvTranspose2d)):\n            if seamless_axes.x or seamless_axes.y:\n                m.asymmetric_padding_mode = (\n                    'circular' if seamless_axes.x else 'constant',\n                    'circular' if seamless_axes.y else 'constant'\n                )\n                m.asymmetric_padding = (\n                    (m._reversed_padding_repeated_twice[0], m._reversed_padding_repeated_twice[1], 0, 0),\n                    (0, 0, m._reversed_padding_repeated_twice[2], m._reversed_padding_repeated_twice[3])\n                )\n                m._conv_forward = _conv_forward_asymmetric.__get__(m, nn.Conv2d)\n            else:\n                m._conv_forward = nn.Conv2d._conv_forward.__get__(m, nn.Conv2d)\n                if hasattr(m, 'asymmetric_padding_mode'):\n                    del m.asymmetric_padding_mode\n                if hasattr(m, 'asymmetric_padding'):\n                    del m.asymmetric_padding\n\ndef _conv_forward_asymmetric(self, input, weight, bias):\n    import torch.nn as nn\n    \"\"\"\n    Patch for Conv2d._conv_forward that supports asymmetric padding\n    \"\"\"\n    if input.device.type == \"dml\":\n        # DML pad() will wrongly fill the tensor in constant mode with the supplied value\n        # (default 0) when padding on both ends of a dimension, can't split to two calls.\n        working = nn.functional.pad(input, self._reversed_padding_repeated_twice, mode='circular')\n        pad_w0, pad_w1, pad_h0, pad_h1 = self._reversed_padding_repeated_twice\n        if self.asymmetric_padding_mode[0] == 'constant':\n            working[:, :, :, :pad_w0] = 0\n            if pad_w1 > 0:\n                working[:, :, :, -pad_w1:] = 0\n        if self.asymmetric_padding_mode[1] == 'constant':\n            working[:, :, :pad_h0] = 0\n            if pad_h1 > 0:\n                working[:, :, -pad_h1:] = 0\n    else:\n        working = nn.functional.pad(input, self.asymmetric_padding[0], mode=self.asymmetric_padding_mode[0])\n        working = nn.functional.pad(working, self.asymmetric_padding[1], mode=self.asymmetric_padding_mode[1])\n    return nn.functional.conv2d(working, weight, bias, self.stride, nn.modules.utils._pair(0), self.dilation, self.groups)\n"
  },
  {
    "path": "image_utils.py",
    "content": "import importlib.util\nimport os\nimport sys\nfrom os import PathLike\nfrom typing import Tuple, Literal, Union, TYPE_CHECKING\n\nimport numpy as np\nfrom numpy.typing import NDArray, DTypeLike\n\nfrom .generator_process import RunInSubprocess\n\n\n\"\"\"\nThis module allows for simple handling of image data in numpy ndarrays in some common formats.\n\nDimensions:\n    2: HW - L\n    3: HWC - L/LA/RGB/RGBA\n    4: NHWC - batched HWC\n\nChannels:\n    1: L\n    2: LA\n    3: RGB\n    4: RGBA\n\"\"\"\n\n\ndef version_str(version):\n    return \".\".join(str(x) for x in version)\n\n\n# find_spec(\"bpy\") will never return None\nhas_bpy = sys.modules.get(\"bpy\", None) is not None\nhas_ocio = importlib.util.find_spec(\"PyOpenColorIO\") is not None\nhas_oiio = importlib.util.find_spec(\"OpenImageIO\") is not None\nhas_pil = importlib.util.find_spec(\"PIL\") is not None\n\nif has_bpy:\n    # frontend\n    import bpy\n    BLENDER_VERSION = bpy.app.version\n    OCIO_CONFIG = os.path.join(bpy.utils.resource_path('LOCAL'), 'datafiles/colormanagement/config.ocio')\n    # Easier to share via environment variables than to enforce backends with subprocesses to use their own methods of sharing.\n    os.environ[\"BLENDER_VERSION\"] = version_str(BLENDER_VERSION)\n    os.environ[\"BLENDER_OCIO_CONFIG\"] = OCIO_CONFIG\nelse:\n    # backend\n    BLENDER_VERSION = tuple(int(x) for x in os.environ[\"BLENDER_VERSION\"].split(\".\"))\n    OCIO_CONFIG = os.environ[\"BLENDER_OCIO_CONFIG\"]\n\nif TYPE_CHECKING:\n    import bpy\n    import PIL.Image\n\n\ndef _bpy_version_error(required_version, feature, module):\n    if BLENDER_VERSION >= required_version:\n        return Exception(f\"{module} is unexpectedly missing in Blender {version_str(BLENDER_VERSION)}\")\n    return Exception(f\"{feature} requires Blender {version_str(required_version)} or higher, you are using {version_str(BLENDER_VERSION)}\")\n\n\ndef size(array: NDArray) -> Tuple[int, int]:\n    if array.ndim == 2:\n        return array.shape[1], array.shape[0]\n    if array.ndim in [3, 4]:\n        return array.shape[-2], array.shape[-3]\n    raise ValueError(f\"Can't determine size from {array.ndim} dimensions\")\n\n\ndef channels(array: NDArray) -> int:\n    if array.ndim == 2:\n        return 1\n    if array.ndim in [3, 4]:\n        return array.shape[-1]\n    raise ValueError(f\"Can't determine channels from {array.ndim} dimensions\")\n\n\ndef ensure_alpha(array: NDArray, alpha=None) -> NDArray:\n    \"\"\"\n    Args:\n        array: Image pixels values.\n        alpha: Default alpha value if an alpha channel will be made. Will be inferred from `array.dtype` if None.\n\n    Returns: The converted image or the original image if it already had alpha.\n    \"\"\"\n    c = channels(array)\n    if c in [2, 4]:\n        return array\n    if c not in [1, 3]:\n        raise ValueError(f\"Can't ensure alpha from {c} channels\")\n\n    if alpha is None:\n        alpha = 0\n        if np.issubdtype(array.dtype, np.floating):\n            alpha = 1\n        elif np.issubdtype(array.dtype, np.integer):\n            alpha = np.iinfo(array.dtype).max\n    array = ensure_channel_dim(array)\n    return np.pad(array, [*[(0, 0)]*(array.ndim-1), (0, 1)], constant_values=alpha)\n\n\ndef ensure_opaque(array: NDArray) -> NDArray:\n    \"\"\"\n    Removes the alpha channel if it exists.\n    \"\"\"\n    if channels(array) in [2, 4]:\n        return array[..., :-1]\n    return array\n\n\ndef ensure_channel_dim(array: NDArray) -> NDArray:\n    \"\"\"\n    Expands a HW grayscale image to HWC.\n    \"\"\"\n    if array.ndim == 2:\n        return array[..., np.newaxis]\n    return array\n\n\ndef rgb(array: NDArray) -> NDArray:\n    \"\"\"\n    Converts a grayscale image to RGB or removes the alpha channel from an RGBA image.\n    If the image was already RGB the original array will be returned.\n    \"\"\"\n    c = channels(array)\n    match channels(array):\n        case 1:\n            return np.concatenate([ensure_channel_dim(array)] * 3, axis=-1)\n        case 2:\n            return np.concatenate([array[..., :1]] * 3, axis=-1)\n        case 3:\n            return array\n        case 4:\n            return array[..., :3]\n    raise ValueError(f\"Can't make {c} channels RGB\")\n\n\ndef rgba(array: NDArray, alpha=None) -> NDArray:\n    \"\"\"\n    Args:\n        array: Image pixels values.\n        alpha: Default alpha value if an alpha channel will be made. Will be inferred from `array.dtype` if None.\n\n    Returns: The converted image or the original image if it already was RGBA.\n    \"\"\"\n    c = channels(array)\n    if c == 4:\n        return array\n    if c == 2:\n        l, a = np.split(array, 2, axis=-1)\n        return np.concatenate([l, l, l, a], axis=-1)\n    return ensure_alpha(rgb(array), alpha)\n\n\ndef grayscale(array: NDArray) -> NDArray:\n    \"\"\"\n    Converts `array` into HW or NHWC grayscale. This is intended for converting an\n    RGB image that is already visibly grayscale, such as a depth map. It will not\n    make a good approximation of perceived lightness of an otherwise colored image.\n    \"\"\"\n    if array.ndim == 2:\n        return array\n    c = channels(array)\n    if array.ndim == 3:\n        if c in [1, 2]:\n            return array[..., 0]\n        elif c in [3, 4]:\n            return np.max(array[..., :3], axis=-1)\n        raise ValueError(f\"Can't make {c} channels grayscale\")\n    elif array.ndim == 4:\n        if c in [1, 2]:\n            return array[..., :1]\n        elif c in [3, 4]:\n            return np.max(array[..., :3], axis=-1, keepdims=True)\n        raise ValueError(f\"Can't make {c} channels grayscale\")\n    raise ValueError(f\"Can't make {array.ndim} dimensions grayscale\")\n\n\ndef _passthrough_alpha(from_array, to_array):\n    if channels(from_array) not in [2, 4]:\n        return to_array\n    to_array = np.concatenate([ensure_channel_dim(to_array), from_array[..., -1:]], axis=-1)\n    return to_array\n\n\ndef linear_to_srgb(array: NDArray, clamp=True) -> NDArray:\n    \"\"\"\n    Args:\n        array: Image to convert from linear to sRGB color space. Will be converted to float32 if it isn't already a float dtype.\n        clamp: whether to restrict the result between 0..1\n    \"\"\"\n    if not np.issubdtype(array.dtype, np.floating):\n        array = to_dtype(array, np.float32)\n    srgb = ensure_opaque(array)\n    srgb = np.where(\n        srgb <= 0.0031308,\n        srgb * 12.92,\n        (np.abs(srgb) ** (1/2.4) * 1.055) - 0.055\n        # abs() to suppress `RuntimeWarning: invalid value encountered in power` for negative values\n    )\n    if clamp:\n        # conversion may produce values outside standard range, usually >1\n        srgb = np.clip(srgb, 0, 1)\n    srgb = _passthrough_alpha(array, srgb)\n    return srgb\n\n\ndef srgb_to_linear(array: NDArray) -> NDArray:\n    \"\"\"\n    Converts from sRGB to linear color space. Will be converted to float32 if it isn't already a float dtype.\n    \"\"\"\n    if not np.issubdtype(array.dtype, np.floating):\n        array = to_dtype(array, np.float32)\n    linear = ensure_opaque(array)\n    linear = np.where(\n        linear <= 0.04045,\n        linear / 12.92,\n        ((linear + 0.055) / 1.055) ** 2.4\n    )\n    linear = _passthrough_alpha(array, linear)\n    return linear\n\n\n@RunInSubprocess.when_raised\ndef color_transform(array: NDArray, from_color_space: str, to_color_space: str, *, clamp_srgb=True) -> NDArray:\n    \"\"\"\n    Args:\n        array: Pixel values in `from_color_space`\n        from_color_space: Color space of `array`\n        to_color_space: Desired color space\n        clamp_srgb: Restrict values inside the standard range when converting to sRGB.\n\n    Returns: Pixel values in `to_color_space`. The image will be converted to RGB/RGBA float32 for most transforms.\n        Transforms between linear and sRGB may remain grayscale and keep the original DType if it was floating point.\n    \"\"\"\n    # Blender handles Raw and Non-Color images as if they were in Linear color space.\n    if from_color_space in [\"Raw\", \"Non-Color\"]:\n        from_color_space = \"Linear\"\n    if to_color_space in [\"Raw\", \"Non-Color\"]:\n        to_color_space = \"Linear\"\n\n    if from_color_space == to_color_space:\n        return array\n    elif from_color_space == \"Linear\" and to_color_space == \"sRGB\":\n        return linear_to_srgb(array, clamp_srgb)\n    elif from_color_space == \"sRGB\" and to_color_space == \"Linear\":\n        return srgb_to_linear(array)\n\n    if not has_ocio:\n        raise RunInSubprocess\n\n    import PyOpenColorIO as OCIO\n    config = OCIO.Config.CreateFromFile(OCIO_CONFIG)\n    proc = config.getProcessor(from_color_space, to_color_space).getDefaultCPUProcessor()\n    # OCIO requires RGB/RGBA float32.\n    # There is a channel agnostic apply(), but I can't seem to get it to work.\n    # getOptimizedCPUProcessor() can handle different precisions, but I doubt it would have meaningful use.\n    array = to_dtype(array, np.float32)\n    c = channels(array)\n    if c in [1, 3]:\n        array = rgb(array)\n        proc.applyRGB(array)\n        if clamp_srgb and to_color_space == \"sRGB\":\n            array = np.clip(array, 0, 1)\n        return array\n    elif c in [2, 4]:\n        array = rgba(array)\n        proc.applyRGBA(array)\n        if clamp_srgb and to_color_space == \"sRGB\":\n            array = np.clip(array, 0, 1)\n        return array\n    raise ValueError(f\"Can't color transform {c} channels\")\n\n\n# inverse=True is often crashing from EXCEPTION_ACCESS_VIOLATION while on frontend.\n# Normally this is caused by not running on the main thread or accessing a deleted\n# object, neither seem to be the issue here. Doesn't matter if the backend imports\n# its own OCIO or the one packaged with Blender.\n# Stack trace:\n# OpenColorIO_2_2.dll :0x00007FFDE8961160  OpenColorIO_v2_2::GradingTone::validate\n# OpenColorIO_2_2.dll :0x00007FFDE8A2BD40  OpenColorIO_v2_2::Processor::isNoOp\n# OpenColorIO_2_2.dll :0x00007FFDE882EA00  OpenColorIO_v2_2::CPUProcessor::apply\n# PyOpenColorIO.pyd   :0x00007FFDEB0F0E40  pybind11::error_already_set::what\n# PyOpenColorIO.pyd   :0x00007FFDEB0F0E40  pybind11::error_already_set::what\n# PyOpenColorIO.pyd   :0x00007FFDEB0F0E40  pybind11::error_already_set::what\n# PyOpenColorIO.pyd   :0x00007FFDEB0E7510  pybind11::error_already_set::discard_as_unraisable\n@RunInSubprocess.when(lambda *_, inverse=False, **__: inverse or not has_ocio)\ndef render_color_transform(\n    array: NDArray,\n    exposure: float,\n    gamma: float,\n    view_transform: str,\n    display_device: str,\n    look: str,\n    *,\n    inverse: bool = False,\n    color_space: str | None = None,\n    clamp_srgb: bool = True,\n) -> NDArray:\n    import PyOpenColorIO as OCIO\n\n    ocio_config = OCIO.Config.CreateFromFile(OCIO_CONFIG)\n\n    # A reimplementation of `OCIOImpl::createDisplayProcessor` from the Blender source.\n    # https://github.com/blender/blender/blob/3816fcd8611bc2836ee8b2a5225b378a02141ce4/intern/opencolorio/ocio_impl.cc#L666\n    # Modified to support a final color space transform.\n    def create_display_processor(\n        config,\n        input_colorspace,\n        view,\n        display,\n        look,\n        scale,  # Exposure\n        exponent,  # Gamma\n        inverse,\n        color_space\n    ):\n        group = OCIO.GroupTransform()\n\n        # Exposure\n        if scale != 1:\n            # Always apply exposure in scene linear.\n            color_space_transform = OCIO.ColorSpaceTransform()\n            color_space_transform.setSrc(input_colorspace)\n            color_space_transform.setDst(OCIO.ROLE_SCENE_LINEAR)\n            group.appendTransform(color_space_transform)\n\n            # Make further transforms aware of the color space change\n            input_colorspace = OCIO.ROLE_SCENE_LINEAR\n\n            # Apply scale\n            matrix_transform = OCIO.MatrixTransform(\n                [scale, 0.0, 0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0, 0.0, 1.0])\n            group.appendTransform(matrix_transform)\n\n        # Add look transform\n        use_look = look is not None and len(look) > 0\n        if use_look:\n            look_output = config.getLook(look).getProcessSpace()\n            if look_output is not None and len(look_output) > 0:\n                look_transform = OCIO.LookTransform()\n                look_transform.setSrc(input_colorspace)\n                look_transform.setDst(look_output)\n                look_transform.setLooks(look)\n                group.appendTransform(look_transform)\n                # Make further transforms aware of the color space change.\n                input_colorspace = look_output\n            else:\n                # For empty looks, no output color space is returned.\n                use_look = False\n\n        # Add view and display transform\n        display_view_transform = OCIO.DisplayViewTransform()\n        display_view_transform.setSrc(input_colorspace)\n        display_view_transform.setLooksBypass(use_look)\n        display_view_transform.setView(view)\n        display_view_transform.setDisplay(display)\n        group.appendTransform(display_view_transform)\n\n        if color_space is not None:\n            group.appendTransform(OCIO.ColorSpaceTransform(input_colorspace if display == \"None\" else display, color_space))\n\n        # Gamma\n        if exponent != 1:\n            exponent_transform = OCIO.ExponentTransform([exponent, exponent, exponent, 1.0])\n            group.appendTransform(exponent_transform)\n\n        if inverse:\n            group.setDirection(OCIO.TransformDirection.TRANSFORM_DIR_INVERSE)\n\n        # Create processor from transform. This is the moment were OCIO validates\n        # the entire transform, no need to check for the validity of inputs above.\n        return config.getProcessor(group)\n\n    # Exposure and gamma transformations derived from Blender source:\n    # https://github.com/blender/blender/blob/3816fcd8611bc2836ee8b2a5225b378a02141ce4/source/blender/imbuf/intern/colormanagement.cc#L867\n    scale = 2 ** exposure\n    exponent = 1 / max(gamma, np.finfo(np.float32).eps)\n    processor = create_display_processor(ocio_config, OCIO.ROLE_SCENE_LINEAR, view_transform, display_device, look if look != 'None' else None, scale, exponent, inverse, color_space)\n    array = to_dtype(array, np.float32)\n    c = channels(array)\n    if c in [1, 3]:\n        array = rgb(array)\n        processor.getDefaultCPUProcessor().applyRGB(array)\n    elif c in [2, 4]:\n        array = rgba(array)\n        processor.getDefaultCPUProcessor().applyRGBA(array)\n    else:\n        raise ValueError(f\"Can't color transform {c} channels\")\n    if clamp_srgb and (color_space == \"sRGB\" or (display_device == \"sRGB\" and color_space is None)) and not inverse:\n        array = np.clip(array, 0, 1)\n    return array\n\n\ndef scene_color_transform(array: NDArray, scene: Union[\"bpy.types.Scene\", None] = None, *, inverse: bool = False, color_space: str | None = None, clamp_srgb=True) -> NDArray:\n    if scene is None:\n        import bpy\n        scene = bpy.context.scene\n    view = scene.view_settings\n    display = scene.display_settings.display_device\n    return render_color_transform(\n        array,\n        view.exposure,\n        view.gamma,\n        view.view_transform,\n        display,\n        view.look,\n        inverse=inverse,\n        clamp_srgb=clamp_srgb,\n        color_space=color_space\n    )\n\n\ndef _unsigned(dtype: DTypeLike) -> DTypeLike:\n    match bits := np.iinfo(dtype).bits:\n        case 8:\n            return np.uint8\n        case 16:\n            return np.uint16\n        case 32:\n            return np.uint32\n        case 64:\n            return np.uint64\n    raise ValueError(f\"unexpected bit depth {bits} from {repr(dtype)}\")\n\n\ndef to_dtype(array: NDArray, dtype: DTypeLike) -> NDArray:\n    \"\"\"\n    Remaps values with respect to ranges rather than simply casting for integer DTypes.\n    `integer(0)=float(0)`, `integer.MAX=float(1)`, and signed `integer.MIN+1=float(-1)`\n    \"\"\"\n    dtype = np.dtype(dtype)\n    from_dtype = array.dtype\n    if dtype == from_dtype:\n        return array\n    from_floating = np.issubdtype(from_dtype, np.floating)\n    from_integer = np.issubdtype(from_dtype, np.integer)\n    to_floating = np.issubdtype(dtype, np.floating)\n    to_integer = np.issubdtype(dtype, np.integer)\n    if from_floating and to_floating:\n        array = array.astype(dtype)\n        if np.finfo(from_dtype).bits > np.finfo(dtype).bits:\n            # prevent inf when lowering precision\n            array = np.nan_to_num(array)\n    elif from_floating and to_integer:\n        iinfo = np.iinfo(dtype)\n        array = (array.clip(-1 if iinfo.min < 0 else 0, 1) * iinfo.max).round().astype(dtype)\n    elif from_integer and to_floating:\n        iinfo = np.iinfo(from_dtype)\n        array = (array / iinfo.max).astype(dtype)\n    elif from_integer and to_integer:\n        from_signed = np.issubdtype(from_dtype, np.signedinteger)\n        to_signed = np.issubdtype(dtype, np.signedinteger)\n        from_bits = np.iinfo(from_dtype).bits\n        to_bits = np.iinfo(dtype).bits\n        if from_signed:\n            from_bits -= 1\n        if to_signed:\n            to_bits -= 1\n        bit_diff = to_bits - from_bits\n\n        if from_signed and not to_signed:\n            # unsigned output does not support negative\n            array = np.maximum(array, 0)\n        if from_signed and to_signed:\n            # simpler to handle bit manipulation in unsigned\n            sign = np.sign(array)\n            array = np.abs(array)\n\n        if bit_diff > 0:\n            # Repeat bits rather than using a single left shift\n            # so that from_iinfo.max turns into to_iinfo.max\n            # and all values remain equally spaced.\n            # Example 8 to 16 bits:\n            # (incorrect)        0x00FF << 8 = 0xFF00\n            # (correct) 0x00FF << 8 | 0x00FF = 0xFFFF\n            # Implementation uses multiplication instead of potentially multiple left shifts and ors:\n            # 0x00FF * 0x0101 = 0xFFFF\n            base = array.astype(_unsigned(dtype))\n            m = 0\n            for i in range(bit_diff, -1, -from_bits):\n                m += 2 ** i\n            array = base * m\n            remaining_bits = bit_diff % from_bits\n            if remaining_bits > 0:\n                # when changing between signed and unsigned bit_diff is not a multiple of from_bits\n                array |= base >> (from_bits-remaining_bits)\n        elif bit_diff < 0:\n            array = array.astype(_unsigned(from_dtype), copy=False) >> -bit_diff\n\n        if from_signed and to_signed:\n            array = np.multiply(array, sign, dtype=dtype)\n        array = array.astype(dtype, copy=False)\n    else:\n        raise TypeError(f\"Unable to convert from {array.dtype} to {dtype}\")\n    return array\n\n\n@RunInSubprocess.when(not has_oiio)\ndef resize(array: NDArray, size: Tuple[int, int], clamp=True):\n    no_channels = array.ndim == 2\n    if no_channels:\n        array = array[..., np.newaxis]\n    no_batch = array.ndim < 4\n    if no_batch:\n        array = array[np.newaxis, ...]\n    if clamp:\n        c_min = np.min(array, axis=(1, 2), keepdims=True)\n        c_max = np.max(array, axis=(1, 2), keepdims=True)\n\n    if has_oiio:\n        import OpenImageIO as oiio\n        resized = []\n        for unbatched in array:\n            # OpenImageIO can have batched images, but doesn't support resizing them\n            image_in = oiio.ImageBuf(unbatched)\n            image_out = oiio.ImageBufAlgo.resize(image_in, roi=oiio.ROI(0, int(size[0]), 0, int(size[1])))\n            if image_out.has_error:\n                raise Exception(image_out.geterror())\n            resized.append(image_out.get_pixels(image_in.spec().format))\n        array = np.stack(resized)\n    else:\n        original_dtype = array.dtype\n        if np.issubdtype(original_dtype, np.floating):\n            if original_dtype == np.float16:\n                # interpolation not implemented for float16 on CPU\n                array = to_dtype(array, np.float32)\n        elif np.issubdtype(original_dtype, np.integer):\n            # integer interpolation only supported for uint8 nearest, nearest-exact or bilinear\n            bits = np.iinfo(original_dtype).bits\n            array = to_dtype(array, np.float64 if bits >= 32 else np.float32)\n\n        import torch\n        array = torch.from_numpy(np.transpose(array, (0, 3, 1, 2)))\n        array = torch.nn.functional.interpolate(array, size=(size[1], size[0]), mode=\"bilinear\")\n        array = np.transpose(array, (0, 2, 3, 1)).numpy()\n        array = to_dtype(array, original_dtype)\n\n    if clamp:\n        array = np.clip(array, c_min, c_max)\n    if no_batch:\n        array = np.squeeze(array, 0)\n    if no_channels:\n        array = np.squeeze(array, -1)\n    return array\n\n\ndef bpy_to_np(image: \"bpy.types.Image\", *, color_space: str | None = \"sRGB\", clamp_srgb=True, top_to_bottom=True) -> NDArray:\n    \"\"\"\n    Args:\n        image: Image to extract pixels values from.\n        color_space: The color space to convert to. `None` will apply no color transform.\n            Keep in mind that Raw/Non-Color images are handled as if they were in Linear color space.\n        clamp_srgb: Restrict values inside the standard range when converting to sRGB.\n        top_to_bottom: The y-axis is flipped to a more common standard of `top=0` to `bottom=height-1`.\n\n    Returns: A ndarray copy of `image.pixels` in RGBA float32 format.\n    \"\"\"\n    if image.type == \"RENDER_RESULT\":\n        # can't get pixels automatically without rendering again and freezing Blender until it finishes, or saving to disk\n        raise ValueError(f\"{image.name} image can't be used directly, alternatively use a compositor viewer node\")\n    array = np.empty((image.size[1], image.size[0], image.channels), dtype=np.float32)\n    # foreach_get/set is extremely fast to read/write an entire image compared to alternatives\n    # see https://projects.blender.org/blender/blender/commit/9075ec8269e7cb029f4fab6c1289eb2f1ae2858a\n    image.pixels.foreach_get(array.ravel())\n    if color_space is not None:\n        if image.type == \"COMPOSITING\":\n            # Viewer Node\n            array = scene_color_transform(array, color_space=color_space, clamp_srgb=clamp_srgb)\n        else:\n            array = color_transform(array, image.colorspace_settings.name, color_space, clamp_srgb=clamp_srgb)\n    if top_to_bottom:\n        array = np.flipud(array)\n    return rgba(array)\n\n\ndef np_to_bpy(array: NDArray, name=None, existing_image=None, float_buffer=None, color_space: str = \"sRGB\", top_to_bottom=True) -> \"bpy.types.Image\":\n    \"\"\"\n    Args:\n        array: Image pixel values. The y-axis is expected to be ordered `top=0` to `bottom=height-1`.\n        name: Name of the image data-block. If None it will be `existing_image.name` or \"Untitled\".\n        existing_image: Image data-block to overwrite.\n        float_buffer:\n            Make Blender keep data in (`True`) 32-bit float values, or (`False`) 8-bit integer values.\n            `None` won't invalidate `existing_image`, but if a new image is created it will be `False`.\n        color_space: Color space of `array`.\n\n    Returns: A new Blender image or `existing_image` if it didn't require replacement.\n    \"\"\"\n    if array.ndim == 4 and array.shape[0] > 1:\n        raise ValueError(f\"Can't convert a batched array of {array.shape[0]} images to a Blender image\")\n\n    # create or replace image\n    import bpy\n    width, height = size(array)\n    if name is None:\n        name = \"Untitled\" if existing_image is None else existing_image.name\n    if existing_image is not None and existing_image.type in [\"RENDER_RESULT\", \"COMPOSITING\"]:\n        existing_image = None\n    elif existing_image is not None and (\n            existing_image.size[0] != width\n            or existing_image.size[1] != height\n            or (existing_image.channels != channels(array) and existing_image.channels != 4)\n            or (existing_image.is_float != float_buffer and float_buffer is not None)\n    ):\n        bpy.data.images.remove(existing_image)\n        existing_image = None\n    if existing_image is None:\n        image = bpy.data.images.new(\n            name,\n            width=width,\n            height=height,\n            alpha=channels(array) == 4,\n            float_buffer=False if float_buffer is None else float_buffer\n        )\n    else:\n        image = existing_image\n        image.name = name\n    image.colorspace_settings.name = color_space\n\n    # adjust array pixels to fit into image\n    if array.ndim == 4:\n        array = array[0]\n    if top_to_bottom:\n        array = np.flipud(array)\n    array = to_dtype(array, np.float32)\n    if image.channels == 4:\n        array = rgba(array)\n    elif image.channels == 3:\n        # I believe image.channels only exists for backwards compatibility and modern versions of Blender\n        # will always handle images as RGBA. I can't manage to make or import an image and end up with\n        # anything but 4 channels. Support for images with 3 channels will be kept just in case.\n        array = rgb(array)\n    else:\n        raise NotImplementedError(f\"Blender image unexpectedly has {image.channels} channels\")\n\n    # apply pixels to image\n    image.pixels.foreach_set(array.ravel())\n    image.pack()\n    image.update()\n    return image\n\n\ndef render_pass_to_np(\n    render_pass: \"bpy.types.RenderPass\",\n    size: Tuple[int, int],\n    *,\n    color_management: bool = False,\n    color_space: str | None = None,\n    clamp_srgb: bool = True,\n    top_to_bottom: bool = True\n):\n    array = np.empty((*reversed(size), render_pass.channels), dtype=np.float32)\n    if BLENDER_VERSION >= (4, 1, 0):\n        render_pass.rect.foreach_get(array.reshape(-1))\n    else:\n        render_pass.rect.foreach_get(array.reshape(-1, render_pass.channels))\n    if color_management:\n        array = scene_color_transform(array, color_space=color_space, clamp_srgb=clamp_srgb)\n    elif color_space is not None:\n        array = color_transform(array, \"Linear\", color_space, clamp_srgb=clamp_srgb)\n    if top_to_bottom:\n        array = np.flipud(array)\n    return array\n\n\ndef np_to_render_pass(\n    array: NDArray,\n    render_pass: \"bpy.types.RenderPass\",\n    *,\n    inverse_color_management: bool = False,\n    color_space: str | None = None,\n    dtype: DTypeLike = np.float32,\n    top_to_bottom: bool = True\n):\n    if inverse_color_management:\n        array = scene_color_transform(array, inverse=True, color_space=color_space)\n    elif color_space is not None:\n        array = color_transform(color_space, \"Linear\")\n    if channels(array) != render_pass.channels:\n        match render_pass.channels:\n            case 1:\n                array = grayscale(array)\n            case 3:\n                array = rgb(array)\n            case 4:\n                array = rgba(array)\n            case _:\n                raise NotImplementedError(f\"Render pass {render_pass.name} unexpectedly requires {render_pass.channels} channels\")\n    if dtype is not None:\n        array = to_dtype(array, dtype)\n    if top_to_bottom:\n        array = np.flipud(array)\n    if BLENDER_VERSION >= (4, 1, 0):\n        render_pass.rect.foreach_set(array.reshape(-1))\n    else:\n        render_pass.rect.foreach_set(array.reshape(-1, render_pass.channels))\n\n\ndef _mode(array, mode):\n    if mode is None:\n        return array\n    elif mode == \"RGBA\":\n        return rgba(array)\n    elif mode == \"RGB\":\n        return rgb(array)\n    elif mode == \"L\":\n        return grayscale(array)\n    elif mode == \"LA\":\n        return ensure_alpha(_passthrough_alpha(array, grayscale(array)))\n    raise ValueError(f\"mode expected one of {['RGB', 'RGBA', 'L', 'LA', None]}, got {repr(mode)}\")\n\n\ndef pil_to_np(image, *, dtype: DTypeLike | None = np.float32, mode: Literal[\"RGB\", \"RGBA\", \"L\", \"LA\"] | None = None) -> NDArray:\n    # some modes don't require being converted to RGBA for proper handling in other module functions\n    # see for other modes https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-modes\n    if image.mode not in [\"RGB\", \"RGBA\", \"L\", \"LA\", \"I\", \"F\", \"I;16\"]:\n        image = image.convert(\"RGBA\")\n    array = np.array(image)\n    if dtype is not None:\n        array = to_dtype(array, dtype)\n    array = _mode(array, mode)\n    return array\n\n\ndef np_to_pil(array: NDArray, *, mode: Literal[\"RGB\", \"RGBA\", \"L\", \"LA\"] | None = None):\n    from PIL import Image\n    array = to_dtype(array, np.uint8)\n    if mode is None:\n        if channels(array) == 1 and array.ndim == 3:\n            # PIL L mode can't have a channel dimension\n            array = array[..., 1]\n    else:\n        array = _mode(array, mode)\n    # PIL does support higher precision modes for a single channel, but I don't see a need for supporting them yet.\n    # uint16=\"I;16\", int32=\"I\", float32=\"F\"\n    return Image.fromarray(array, mode=mode)\n\n\ndef _dtype_to_type_desc(dtype):\n    import OpenImageIO as oiio\n    dtype = np.dtype(dtype)\n    match dtype:\n        case np.uint8:\n            return oiio.TypeUInt8\n        case np.uint16:\n            return oiio.TypeUInt16\n        case np.uint32:\n            return oiio.TypeUInt32\n        case np.uint64:\n            return oiio.TypeUInt64\n        case np.int8:\n            return oiio.TypeInt8\n        case np.int16:\n            return oiio.TypeInt16\n        case np.int32:\n            return oiio.TypeInt32\n        case np.int64:\n            return oiio.TypeInt64\n        case np.float16:\n            return oiio.TypeHalf\n        case np.float32:\n            return oiio.TypeFloat\n        case np.float64:\n            # no oiio.TypeDouble\n            return oiio.TypeDesc(oiio.BASETYPE.DOUBLE)\n    raise TypeError(f\"can't convert {dtype} to OpenImageIO.TypeDesc\")\n\n\n@RunInSubprocess.when(not has_oiio)\ndef path_to_np(\n        path: str | PathLike,\n        *,\n        dtype: DTypeLike | None = np.float32,\n        default_color_space: str | None = None,\n        to_color_space: str | None = \"sRGB\"\n) -> NDArray:\n    \"\"\"\n    Args:\n        path: Path to an image file.\n        dtype: Data type of the returned array. `None` won't change the data type. The data type may still change if a color transform occurs.\n        default_color_space: The color space that `image_or_path` will be handled as when it can't be determined automatically.\n        to_color_space: Color space of the returned array. `None` won't apply a color transform.\n    \"\"\"\n    if has_oiio:\n        import OpenImageIO as oiio\n        image = oiio.ImageInput.open(str(path))\n        if image is None:\n            raise IOError(oiio.geterror())\n        type_desc = image.spec().format\n        if dtype is not None:\n            type_desc = _dtype_to_type_desc(dtype)\n        array = image.read_image(type_desc)\n        from_color_space = image.spec().get_string_attribute(\"oiio:ColorSpace\", default_color_space)\n        image.close()\n    else:\n        from PIL import Image\n        array = pil_to_np(Image.open(path))\n        if dtype is not None:\n            array = to_dtype(array, dtype)\n        from_color_space = \"sRGB\"\n    if from_color_space is not None and to_color_space is not None:\n        array = color_transform(array, from_color_space, to_color_space)\n    return array\n\n\nImageOrPath = Union[NDArray, \"PIL.Image.Image\", str, PathLike]\n\"\"\"Backend compatible image types\"\"\"\n\n\ndef image_to_np(\n        image_or_path: ImageOrPath | \"bpy.types.Image\" | None,\n        *,\n        dtype: DTypeLike | None = np.float32,\n        mode: Literal[\"RGB\", \"RGBA\", \"L\", \"LA\"] | None = \"RGBA\",\n        default_color_space: str | None = None,\n        to_color_space: str | None = \"sRGB\",\n        size: Tuple[int, int] | None = None,\n        top_to_bottom: bool = True\n) -> NDArray:\n    \"\"\"\n    Opens an image from disk or takes an image object and converts it to `numpy.ndarray`.\n    Usable for image argument sanitization when the source can vary in type or format.\n\n    Args:\n        image_or_path: Either a file path or an instance of `bpy.types.Image`, `PIL.Image.Image`, or `numpy.ndarray`. `None` will return `None`.\n        dtype: Data type of the returned array. `None` won't change the data type. The data type may still change if a color transform occurs.\n        mode: Channel mode of the returned array. `None` won't change the mode. The mode may still change if a color transform occurs.\n        default_color_space: The color space that `image_or_path` will be handled as when it can't be determined automatically.\n        to_color_space: Color space of the returned array. `None` won't apply a color transform.\n        size: Resize to specific dimensions. `None` won't change the size.\n        top_to_bottom: Flips the image like `bpy_to_np(top_to_bottom=True)` does when `True` and `image_or_path` is a Blender image. Other image sources will only be flipped when `False`.\n    \"\"\"\n\n    if image_or_path is None:\n        return None\n\n    # convert image_or_path to numpy.ndarray\n    match image_or_path:\n        case PathLike() | str():\n            array = path_to_np(image_or_path, dtype=dtype, default_color_space=default_color_space, to_color_space=to_color_space)\n            from_color_space = None\n        case object(__module__=\"PIL.Image\", __class__=type(__name__=\"Image\")):\n            # abnormal class check because PIL cannot be imported on frontend\n            array = pil_to_np(image_or_path)\n            from_color_space = \"sRGB\"\n        case object(__module__=\"bpy.types\", __class__=type(__name__=\"Image\")):\n            # abnormal class check because bpy cannot be imported on backend\n            array = bpy_to_np(image_or_path, color_space=to_color_space)\n            from_color_space = None\n        case np.ndarray():\n            array = image_or_path\n            from_color_space = default_color_space\n        case _:\n            raise TypeError(f\"not an image or path {repr(type(image_or_path))}\")\n\n    # apply image requirements\n    if not top_to_bottom:\n        array = np.flipud(array)\n    if from_color_space is not None and to_color_space is not None:\n        array = color_transform(array, from_color_space, to_color_space)\n    if dtype is not None:\n        array = to_dtype(array, dtype)\n    array = _mode(array, mode)\n    if size is not None:\n        array = resize(array, size)\n\n    return array\n"
  },
  {
    "path": "operators/dream_texture.py",
    "content": "import bpy\nimport hashlib\nimport numpy as np\nfrom typing import List, Literal\n\nfrom .notify_result import NotifyResult\nfrom ..prompt_engineering import *\nfrom ..generator_process import Generator\nfrom .. import api\nfrom .. import image_utils\nfrom ..generator_process.models.optimizations import Optimizations\nfrom ..diffusers_backend import DiffusersBackend\nimport time\nimport math\n\ndef get_source_image(context, source: Literal['file', 'open_editor']):\n    match source:\n        case 'file':\n            return context.scene.init_img\n        case 'open_editor':\n            if context.area.type == 'IMAGE_EDITOR':\n                return context.area.spaces.active.image\n            else:\n                init_image = None\n                for area in context.screen.areas:\n                    if area.type == 'IMAGE_EDITOR':\n                        if area.spaces.active.image is not None:\n                            init_image = area.spaces.active.image\n                return init_image\n        case _:\n            raise ValueError(f\"unsupported source {repr(source)}\")\n\nclass DreamTexture(bpy.types.Operator):\n    bl_idname = \"shade.dream_texture\"\n    bl_label = \"Dream Texture\"\n    bl_description = \"Generate a texture with AI\"\n    bl_options = {'REGISTER'}\n\n    @classmethod\n    def poll(cls, context):\n        try:\n            prompt = context.scene.dream_textures_prompt\n            backend: api.Backend = prompt.get_backend()\n            backend.validate(prompt.generate_args(context))\n        except:\n            return False\n        return Generator.shared().can_use()\n\n    def execute(self, context):\n        screen = context.screen\n        scene = context.scene\n        prompt = scene.dream_textures_prompt\n        backend: api.Backend = prompt.get_backend()\n\n        history_template = {prop: getattr(context.scene.dream_textures_prompt, prop) for prop in context.scene.dream_textures_prompt.__annotations__.keys()}\n        history_template[\"iterations\"] = 1\n        history_template[\"random_seed\"] = False\n        \n        is_file_batch = context.scene.dream_textures_prompt.prompt_structure == file_batch_structure.id\n        file_batch_lines = []\n        if is_file_batch:\n            context.scene.dream_textures_prompt.iterations = 1\n            file_batch_lines = [line.body for line in context.scene.dream_textures_prompt_file.lines if len(line.body.strip()) > 0]\n            history_template[\"prompt_structure\"] = custom_structure.id\n\n        node_tree = context.material.node_tree if hasattr(context, 'material') and hasattr(context.material, 'node_tree') else None\n        node_tree_center = np.array(node_tree.view_center) if node_tree is not None else None\n        node_tree_top_left = np.array(context.region.view2d.region_to_view(0, context.region.height)) if node_tree is not None else None\n        screen = context.screen\n        scene = context.scene\n\n        generated_args = scene.dream_textures_prompt.generate_args(context)\n        context.scene.seamless_result.update_args(generated_args)\n        context.scene.seamless_result.update_args(history_template, as_id=True)\n\n        def execute_backend(control_images):\n            # Setup the progress indicator\n            bpy.types.Scene.dream_textures_progress = bpy.props.IntProperty(name=\"\", default=0, min=0, max=generated_args.steps)\n            scene.dream_textures_info = \"Starting...\"\n\n            # Get any init images\n            try:\n                init_image = get_source_image(context, prompt.init_img_src)\n            except ValueError:\n                init_image = None\n            if init_image is not None:\n                init_image_color_space = \"sRGB\"\n                if scene.dream_textures_prompt.use_init_img and scene.dream_textures_prompt.modify_action_source_type in ['depth_map', 'depth']:\n                    init_image_color_space = None\n                init_image = image_utils.bpy_to_np(init_image, color_space=init_image_color_space)\n\n            # Callbacks\n            last_data_block = None\n            execution_start = time.time()\n            def step_callback(progress: List[api.GenerationResult]) -> bool:\n                nonlocal last_data_block\n                scene.dream_textures_last_execution_time = f\"{time.time() - execution_start:.2f} seconds\"\n                scene.dream_textures_progress = progress[-1].progress\n                for area in context.screen.areas:\n                    for region in area.regions:\n                        if region.type == \"UI\":\n                            region.tag_redraw()\n                image = api.GenerationResult.tile_images(progress)\n                if image is None:\n                    return CancelGenerator.should_continue\n                last_data_block = image_utils.np_to_bpy(image, f\"Step {progress[-1].progress}/{progress[-1].total}\", last_data_block)\n                for area in screen.areas:\n                    if area.type == 'IMAGE_EDITOR' and not area.spaces.active.use_image_pin:\n                        area.spaces.active.image = last_data_block\n                return CancelGenerator.should_continue\n\n            iteration = 0\n            iteration_limit = len(file_batch_lines) if is_file_batch else generated_args.iterations\n            iteration_square = math.ceil(math.sqrt(iteration_limit))\n            node_pad = np.array((20, 20))\n            node_size = np.array((240, 277)) + node_pad\n            if node_tree is not None:\n                # keep image nodes grid centered but don't go beyond top and left sides of nodes editor\n                node_anchor = node_tree_center + node_size * 0.5 * (-iteration_square, (iteration_limit-1) // iteration_square + 1)\n                node_anchor = np.array((np.maximum(node_tree_top_left[0], node_anchor[0]), np.minimum(node_tree_top_left[1], node_anchor[1]))) + node_pad * (0.5, -0.5)\n            \n            def callback(results: List[api.GenerationResult] | Exception):\n                if isinstance(results, Exception):\n                    scene.dream_textures_info = \"\"\n                    scene.dream_textures_progress = 0\n                    CancelGenerator.should_continue = None\n                    if not isinstance(results, InterruptedError): # this is a user-initiated cancellation\n                        eval('bpy.ops.' + NotifyResult.bl_idname)('INVOKE_DEFAULT', exception=repr(results))\n                    raise results\n                else:\n                    nonlocal last_data_block\n                    nonlocal iteration\n                    for result in results:\n                        if result.image is None or result.seed is None:\n                            continue\n                        \n                        # Create a trimmed image name\n                        prompt_string = context.scene.dream_textures_prompt.prompt_structure_token_subject\n                        seed_str_length = len(str(result.seed))\n                        trim_aware_name = (prompt_string[:54 - seed_str_length] + '..') if len(prompt_string) > 54 else prompt_string\n                        name_with_trimmed_prompt = f\"{trim_aware_name} ({result.seed})\"\n                        image = image_utils.np_to_bpy(result.image, name_with_trimmed_prompt, last_data_block)\n                        last_data_block = None\n                        if node_tree is not None:\n                            nodes = node_tree.nodes\n                            texture_node = nodes.new(\"ShaderNodeTexImage\")\n                            texture_node.image = image\n                            texture_node.location = node_anchor + node_size * ((iteration % iteration_square), -(iteration // iteration_square))\n                            nodes.active = texture_node\n                        for area in screen.areas:\n                            if area.type == 'IMAGE_EDITOR' and not area.spaces.active.use_image_pin:\n                                area.spaces.active.image = image\n                        scene.dream_textures_prompt.seed = str(result.seed) # update property in case seed was sourced randomly or from hash\n                        # create a hash from the Blender image datablock to use as unique ID of said image and store it in the prompt history\n                        # and as custom property of the image. Needs to be a string because the int from the hash function is too large\n                        image_hash = hashlib.sha256((np.array(image.pixels) * 255).tobytes()).hexdigest()\n                        image['dream_textures_hash'] = image_hash\n                        scene.dream_textures_prompt.hash = image_hash\n                        history_entry = context.scene.dream_textures_history.add()\n                        for key, value in history_template.items():\n                            match key:\n                                case 'control_nets':\n                                    for net in value:\n                                        n = history_entry.control_nets.add()\n                                        for prop in n.__annotations__.keys():\n                                            setattr(n, prop, getattr(net, prop))\n                                case _:\n                                    setattr(history_entry, key, value)\n                        history_entry.seed = str(result.seed)\n                        history_entry.hash = image_hash\n                        history_entry.width = result.image.shape[1]\n                        history_entry.height = result.image.shape[0]\n                        if is_file_batch:\n                            history_entry.prompt_structure_token_subject = file_batch_lines[iteration]\n                        iteration += 1\n                    if iteration < iteration_limit:\n                        generate_next()\n                    else:\n                        scene.dream_textures_info = \"\"\n                        scene.dream_textures_progress = 0\n                        CancelGenerator.should_continue = None\n        \n            # Call the backend\n            CancelGenerator.should_continue = True # reset global cancellation state\n            def generate_next():\n                args = prompt.generate_args(context, iteration=iteration, init_image=init_image, control_images=control_images)\n                backend.generate(args, step_callback=step_callback, callback=callback)\n            \n            generate_next()\n        \n        # Prepare ControlNet images\n        if len(prompt.control_nets) > 0:\n            bpy.types.Scene.dream_textures_progress = bpy.props.IntProperty(name=\"\", default=0, min=0, max=len(prompt.control_nets))\n            scene.dream_textures_info = \"Processing Control Images...\"\n            context.scene.dream_textures_progress = 0\n\n            gen = Generator.shared()\n            optimizations = backend.optimizations() if isinstance(backend, DiffusersBackend) else Optimizations()\n\n            control_images = []\n            def process_next(i):\n                if i >= len(prompt.control_nets):\n                    execute_backend(control_images)\n                    return\n                net = prompt.control_nets[i]\n                future = gen.controlnet_aux(\n                    processor_id=net.processor_id,\n                    image=image_utils.bpy_to_np(net.control_image, color_space=None),\n                    optimizations=optimizations\n                )\n                def on_response(future):\n                    control_images.append(future.result(last_only=True))\n                    context.scene.dream_textures_progress = i + 1\n                    process_next(i + 1)\n                future.add_done_callback(on_response)\n            process_next(0)\n        else:\n            execute_backend(None)\n\n        return {\"FINISHED\"}\n\ndef kill_generator(context=bpy.context):\n    Generator.shared_close()\n    try:\n        context.scene.dream_textures_info = \"\"\n        context.scene.dream_textures_progress = 0\n        CancelGenerator.should_continue = None\n    except:\n        pass\n\nclass ReleaseGenerator(bpy.types.Operator):\n    bl_idname = \"shade.dream_textures_release_generator\"\n    bl_label = \"Release Generator\"\n    bl_description = \"Releases the generator class to free up VRAM\"\n    bl_options = {'REGISTER'}\n\n    def execute(self, context):\n        kill_generator(context)\n        return {'FINISHED'}\n\nclass CancelGenerator(bpy.types.Operator):\n    bl_idname = \"shade.dream_textures_stop_generator\"\n    bl_label = \"Cancel Generator\"\n    bl_description = \"Stops the generator without reloading everything next time\"\n    bl_options = {'REGISTER'}\n\n    should_continue = None\n\n    @classmethod\n    def poll(cls, context):\n        return cls.should_continue is not None\n\n    def execute(self, context):\n        CancelGenerator.should_continue = False\n        return {'FINISHED'}\n"
  },
  {
    "path": "operators/inpaint_area_brush.py",
    "content": "import bpy\n\nreset_blend_mode = 'MIX'\nreset_curve_preset = 'CUSTOM'\nreset_strength = 1.0\nclass InpaintAreaBrushActivated(bpy.types.GizmoGroup):\n    bl_idname = \"dream_textures.inpaint_area_brush_activated\"\n    bl_label = \"Inpaint Area Brush Activated\"\n    bl_space_type = 'IMAGE_EDITOR'\n    bl_context_mode = 'PAINT'\n    bl_region_type = 'WINDOW'\n\n    def setup(self, context):\n        global reset_blend_mode\n        global reset_curve_preset\n        global reset_strength\n        reset_blend_mode = bpy.data.brushes[\"TexDraw\"].blend\n        reset_curve_preset = bpy.data.brushes[\"TexDraw\"].curve_preset\n        reset_strength = bpy.data.brushes[\"TexDraw\"].strength\n        def set_blend():\n            bpy.data.brushes[\"TexDraw\"].blend = \"ERASE_ALPHA\"\n            bpy.data.brushes[\"TexDraw\"].curve_preset = \"CONSTANT\"\n            bpy.data.brushes[\"TexDraw\"].strength = 1.0\n            bpy.ops.paint.brush_select(image_tool='DRAW', toggle=False)\n        bpy.app.timers.register(set_blend)\n\n    def __del__(self):\n        bpy.data.brushes[\"TexDraw\"].blend = reset_blend_mode\n        bpy.data.brushes[\"TexDraw\"].curve_preset = reset_curve_preset\n        bpy.data.brushes[\"TexDraw\"].strength = reset_strength\n\nclass InpaintAreaBrush(bpy.types.WorkSpaceTool):\n    bl_space_type = 'IMAGE_EDITOR'\n    bl_context_mode = 'PAINT'\n\n    bl_idname = \"dream_textures.inpaint_area_brush\"\n    bl_label = \"Mark Inpaint Area\"\n    bl_description = \"Mark an area for inpainting\"\n    bl_icon = \"brush.gpencil_draw.tint\"\n    bl_widget = InpaintAreaBrushActivated.bl_idname\n\n    def draw_settings(self, layout, tool):\n        layout.prop(bpy.context.scene.tool_settings.unified_paint_settings, 'size')"
  },
  {
    "path": "operators/install_dependencies.py",
    "content": "import bpy\nimport os\nimport site\nimport sys\nimport sysconfig\nimport subprocess\nimport requests\nimport tarfile\nfrom enum import IntEnum\n\nfrom ..absolute_path import absolute_path\nfrom ..generator_process import Generator\n\nclass PipInstall(IntEnum):\n    DEPENDENCIES = 1\n    STANDARD = 2\n    USER_SITE = 3\n\ndef install_pip(method = PipInstall.STANDARD):\n    \"\"\"\n    Installs pip if not already present. Please note that ensurepip.bootstrap() also calls pip, which adds the\n    environment variable PIP_REQ_TRACKER. After ensurepip.bootstrap() finishes execution, the directory doesn't exist\n    anymore. However, when subprocess is used to call pip, in order to install a package, the environment variables\n    still contain PIP_REQ_TRACKER with the now nonexistent path. This is a problem since pip checks if PIP_REQ_TRACKER\n    is set and if it is, attempts to use it as temp directory. This would result in an error because the\n    directory can't be found. Therefore, PIP_REQ_TRACKER needs to be removed from environment variables.\n    :return:\n    \"\"\"\n\n    import ensurepip\n\n    if method == PipInstall.DEPENDENCIES:\n        # ensurepip doesn't have a useful way of installing to a specific directory.\n        # root parameter can be used, but it just concatenates that to the beginning of\n        # where it decides to install to, causing a more complicated path to where it installs.\n        wheels = {}\n        for name, package in ensurepip._get_packages().items():\n            if package.wheel_name:\n                whl = os.path.join(os.path.dirname(ensurepip.__file__), \"_bundled\", package.wheel_name)\n            else:\n                whl = package.wheel_path\n            wheels[name] = whl\n        pip_whl = os.path.join(wheels['pip'], 'pip')\n        subprocess.run([sys.executable, pip_whl, \"install\", *wheels.values(), \"--upgrade\", \"--no-index\", \"--no-deps\", \"--no-cache-dir\", \"--target\", absolute_path(\".python_dependencies\")], check=True)\n        return\n    \n    # STANDARD or USER_SITE\n    no_user = os.environ.get(\"PYTHONNOUSERSITE\", None)\n    if method == PipInstall.STANDARD:\n        os.environ[\"PYTHONNOUSERSITE\"] = \"1\"\n    else:\n        os.environ.pop(\"PYTHONNOUSERSITE\", None)\n    try:\n        ensurepip.bootstrap(user=method==PipInstall.USER_SITE)\n    finally:\n        os.environ.pop(\"PIP_REQ_TRACKER\", None)\n        if no_user:\n            os.environ[\"PYTHONNOUSERSITE\"] = no_user\n        else:\n            os.environ.pop(\"PYTHONNOUSERSITE\", None)\n\ndef install_pip_any(*methods):\n    methods = methods or PipInstall\n    for method in methods:\n        print(f\"Attempting to install pip: {PipInstall(method).name}\")\n        try:\n            install_pip(method)\n            return method\n        except:\n            import traceback\n            traceback.print_exc()\n\ndef get_pip_install():\n    def run(pip):\n        if os.path.exists(pip):\n            try:\n                subprocess.run([sys.executable, pip, \"--version\"], check=True)\n                return True\n            except subprocess.CalledProcessError:\n                pass\n        return False\n\n    if run(absolute_path(\".python_dependencies/pip\")):\n        return PipInstall.DEPENDENCIES\n    \n    # This seems to not raise CalledProcessError while debugging in vscode, but works fine in normal use.\n    # subprocess.run([sys.executable, \"-s\", \"-m\", \"pip\", \"--version\"], check=True)\n    # Best to check if the module directory exists first.\n    for path in site.getsitepackages():\n        if run(os.path.join(path,\"pip\")):\n            return PipInstall.STANDARD\n\n    if run(os.path.join(site.getusersitepackages(),\"pip\")):\n        return PipInstall.USER_SITE\n\n\ndef install_and_import_requirements(requirements_txt=None, pip_install=PipInstall.STANDARD):\n    \"\"\"\n    Installs all modules in the 'requirements.txt' file.\n    \"\"\"\n    environ_copy = dict(os.environ)\n    if pip_install != PipInstall.USER_SITE:\n        environ_copy[\"PYTHONNOUSERSITE\"] = \"1\"\n    if pip_install == PipInstall.DEPENDENCIES:\n        environ_copy[\"PYTHONPATH\"] = absolute_path(\".python_dependencies\")\n    python_include_dir = sysconfig.get_paths()['include']\n    if not os.path.exists(python_include_dir):\n        try:\n            os.makedirs(python_include_dir)\n        finally:\n            pass\n    if os.access(python_include_dir, os.W_OK):\n        print(\"downloading additional include files\")\n        python_devel_tgz_path = absolute_path('python-devel.tgz')\n        response = requests.get(f\"https://www.python.org/ftp/python/{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}/Python-{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}.tgz\")\n        with open(python_devel_tgz_path, 'wb') as f:\n            f.write(response.content)\n        with tarfile.open(python_devel_tgz_path) as python_devel_tgz:\n            def members(tf):\n                prefix = f\"Python-{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}/Include/\"\n                l = len(prefix)\n                for member in tf.getmembers():\n                    if member.path.startswith(prefix):\n                        member.path = member.path[l:]\n                        yield member\n            python_devel_tgz.extractall(path=python_include_dir, members=members(python_devel_tgz))\n        os.remove(python_devel_tgz_path)\n    else:\n        print(f\"skipping include files, can't write to {python_include_dir}\",file=sys.stderr)\n\n    subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-r\", absolute_path(requirements_txt), \"--upgrade\", \"--no-cache-dir\", \"--target\", absolute_path('.python_dependencies')], check=True, env=environ_copy, cwd=absolute_path(\"\"))\n\nclass InstallDependencies(bpy.types.Operator):\n    bl_idname = \"stable_diffusion.install_dependencies\"\n    bl_label = \"Install Dependencies\"\n    bl_description = (\"Downloads and installs the required python packages into the '.python_dependencies' directory of the addon.\")\n    bl_options = {\"REGISTER\", \"INTERNAL\"}\n\n    def invoke(self, context, event):\n        return context.window_manager.invoke_confirm(self, event)\n\n    def execute(self, context):\n        # Open the console so we can watch the progress.\n        if sys.platform == 'win32':\n            bpy.ops.wm.console_toggle()\n\n        Generator.shared_close()\n        try:\n            pip_install = get_pip_install()\n            if pip_install is None:\n                pip_install = install_pip_any()\n            if pip_install is None:\n                raise ImportError(f'Pip could not be installed. You may have to manually install pip into {absolute_path(\".python_dependencies\")}')\n\n            install_and_import_requirements(requirements_txt=context.scene.dream_textures_requirements_path, pip_install=pip_install)\n        except (subprocess.CalledProcessError, ImportError) as err:\n            self.report({\"ERROR\"}, str(err))\n            return {\"CANCELLED\"}\n\n        return {\"FINISHED\"}\n\nclass UninstallDependencies(bpy.types.Operator):\n    bl_idname = \"stable_diffusion.uninstall_dependencies\"\n    bl_label = \"Uninstall Dependencies\"\n    bl_description = (\"Uninstalls specific dependencies from Blender's site-packages\")\n    bl_options = {\"REGISTER\", \"INTERNAL\"}\n\n    conflicts: bpy.props.StringProperty(name=\"Conflicts\")\n\n    def execute(self, context):\n        # Open the console so we can watch the progress.\n        if sys.platform == 'win32':\n            bpy.ops.wm.console_toggle()\n\n        environ_copy = dict(os.environ)\n        environ_copy[\"PYTHONNOUSERSITE\"] = \"1\"\n        subprocess.run([sys.executable, \"-m\", \"pip\", \"uninstall\", \"-y\", *self.conflicts.split(' ')], check=True, env=environ_copy, cwd=absolute_path(\"\"))\n\n        return {\"FINISHED\"}"
  },
  {
    "path": "operators/notify_result.py",
    "content": "import bpy\nimport os\nimport sys\n\nclass NotifyResult(bpy.types.Operator):\n    bl_idname = \"shade.dream_textures_notify_result\"\n    bl_label = \"Notify Result\"\n    bl_description = \"Notifies of a generation completion or any error messages\"\n    bl_options = {'REGISTER'}\n\n    exception: bpy.props.StringProperty(name=\"Exception\", default=\"\")\n\n    def modal(self, context, event):\n        if self.exception != \"\":\n            self.report({'ERROR'}, f\"\"\"An error occurred while generating. Check the issues tab on GitHub to see if this has been reported before:\n\n{self.exception}\"\"\")\n            return {'CANCELLED'}\n        else:\n            return {'FINISHED'}\n\n    def invoke(self, context, event):\n        context.window_manager.modal_handler_add(self)\n        return {'RUNNING_MODAL'}\n\n    def execute(self, context):\n        return {'FINISHED'}"
  },
  {
    "path": "operators/open_latest_version.py",
    "content": "import requests\nimport bpy\nimport webbrowser\nfrom ..version import VERSION, version_tag, version_tuple\n\nREPO_OWNER = \"carson-katri\"\nREPO_NAME = \"dream-textures\"\n\nlatest_version = VERSION\ndef check_for_updates():\n    try:\n        global latest_version\n        response = requests.get(f\"https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/releases\")\n        releases = response.json()\n        latest_version = version_tuple(releases[0]['tag_name'])\n    except:\n        pass\n\ndef new_version_available():\n    return not latest_version == VERSION\n\nforce_show_download = False\ndef do_force_show_download():\n    global force_show_download\n    force_show_download = True\ndef is_force_show_download():\n    return force_show_download\n\nclass OpenLatestVersion(bpy.types.Operator):\n    bl_idname = \"stable_diffusion.open_latest_version\"\n    bl_label = f\"Update Available...\"\n    bl_description = (\"Opens a window to download the latest release from GitHub\")\n    bl_options = {\"REGISTER\", \"INTERNAL\"}\n\n    @classmethod\n    def poll(cls, context):\n        return True\n\n    def execute(self, context):\n        webbrowser.open(f'https://github.com/carson-katri/dream-textures/releases/tag/{version_tag(latest_version)}')\n\n        return {\"FINISHED\"}"
  },
  {
    "path": "operators/project.py",
    "content": "import bpy\nimport gpu\nimport gpu.texture\nfrom gpu_extras.batch import batch_for_shader\nimport bmesh\nfrom bpy_extras import view3d_utils\nimport mathutils\nimport numpy as np\nfrom typing import List\n\nfrom .view_history import ImportPromptFile\nfrom .open_latest_version import OpenLatestVersion, is_force_show_download, new_version_available\n\nfrom ..ui.panels.dream_texture import advanced_panel, create_panel, prompt_panel, size_panel\nfrom .dream_texture import CancelGenerator, ReleaseGenerator\nfrom .notify_result import NotifyResult\n\nfrom ..generator_process import Generator\nfrom ..generator_process.models import ModelType\nfrom ..api.models import FixItError\nimport tempfile\n\nfrom ..engine.annotations.depth import render_depth_map\n\nfrom .. import api\nfrom .. import image_utils\n\nframebuffer_arguments = [\n    ('depth', 'Depth', 'Only provide the scene depth as input'),\n    ('color', 'Depth and Color', 'Provide the scene depth and color as input'),\n]\n\ndef _validate_projection(context):\n    if len(context.selected_objects) == 0:\n        def object_mode_operator(operator):\n            operator.mode = 'OBJECT'\n        def select_by_type_operator(operator):\n            operator.type = 'MESH'\n        raise FixItError(\n            \"\"\"No objects selected\nSelect at least one object to project onto.\"\"\",\n            FixItError.RunOperator(\"Switch to Object Mode\", \"object.mode_set\", object_mode_operator)\n            if context.object.mode != 'OBJECT'\n            else FixItError.RunOperator(\"Select All Meshes\", \"object.select_by_type\", select_by_type_operator)\n        )\n    if context.object is not None and context.object.mode != 'EDIT':\n        def fix_mode(operator):\n            operator.mode = 'EDIT'\n        raise FixItError(\n            \"\"\"Enter edit mode\nIn edit mode, select the faces to project onto.\"\"\",\n            FixItError.RunOperator(\"Switch to Edit Mode\", \"object.mode_set\", fix_mode)\n        )\n    has_selection = False\n    for obj in context.selected_objects:\n        if not hasattr(obj, \"data\"):\n            continue\n        mesh = bmesh.from_edit_mesh(obj.data)\n        bm = mesh.copy()\n        bm.select_mode = {'FACE'}\n        for f in bm.faces:\n            if f.select:\n                has_selection = True\n                break\n    if not has_selection:\n        raise FixItError(\n            \"\"\"No faces selected.\nSelect at least one face to project onto.\"\"\",\n            FixItError.RunOperator(\"Select All Faces\", \"mesh.select_all\", lambda _: None)\n        )\n\ndef dream_texture_projection_panels():\n    class DREAM_PT_dream_panel_projection(bpy.types.Panel):\n        \"\"\"Creates a Dream Textures panel for projection\"\"\"\n        bl_label = \"Dream Texture Projection\"\n        bl_idname = f\"DREAM_PT_dream_panel_projection\"\n        bl_category = \"Dream\"\n        bl_space_type = 'VIEW_3D'\n        bl_region_type = 'UI'\n\n        @classmethod\n        def poll(cls, context):\n            if cls.bl_space_type == 'NODE_EDITOR':\n                return context.area.ui_type == \"ShaderNodeTree\" or context.area.ui_type == \"CompositorNodeTree\"\n            else:\n                return True\n        \n        def draw_header_preset(self, context):\n            layout = self.layout\n            layout.operator(ImportPromptFile.bl_idname, text=\"\", icon=\"IMPORT\")\n            layout.separator()\n\n        def draw(self, context):\n            layout = self.layout\n            layout.use_property_split = True\n            layout.use_property_decorate = False\n            \n            if is_force_show_download():\n                layout.operator(OpenLatestVersion.bl_idname, icon=\"IMPORT\", text=\"Download Latest Release\")\n            elif new_version_available():\n                layout.operator(OpenLatestVersion.bl_idname, icon=\"IMPORT\")\n\n            layout.prop(context.scene.dream_textures_project_prompt, \"backend\")\n            layout.prop(context.scene.dream_textures_project_prompt, 'model')\n\n    yield DREAM_PT_dream_panel_projection\n\n    def get_prompt(context):\n        return context.scene.dream_textures_project_prompt\n    yield from create_panel('VIEW_3D', 'UI', DREAM_PT_dream_panel_projection.bl_idname, prompt_panel, get_prompt)\n    yield create_panel('VIEW_3D', 'UI', DREAM_PT_dream_panel_projection.bl_idname, size_panel, get_prompt)\n    yield from create_panel('VIEW_3D', 'UI', DREAM_PT_dream_panel_projection.bl_idname, advanced_panel, get_prompt)\n    def actions_panel(sub_panel, space_type, get_prompt):\n        class ActionsPanel(sub_panel):\n            \"\"\"Create a subpanel for actions\"\"\"\n            bl_idname = f\"DREAM_PT_dream_panel_projection_actions\"\n            bl_label = \"Actions\"\n            bl_options = {'HIDE_HEADER'}\n\n            def draw(self, context):\n                super().draw(context)\n                layout = self.layout\n                layout.use_property_split = True\n\n                prompt = get_prompt(context)\n\n                layout.prop(context.scene, \"dream_textures_project_framebuffer_arguments\")\n                if context.scene.dream_textures_project_framebuffer_arguments == 'color':\n                    layout.prop(prompt, \"strength\")\n                \n                col = layout.column()\n                \n                col.prop(context.scene, \"dream_textures_project_use_control_net\")\n                if context.scene.dream_textures_project_use_control_net and len(prompt.control_nets) > 0:\n                    col.prop(prompt.control_nets[0], \"control_net\", text=\"Depth ControlNet\")\n                    col.prop(prompt.control_nets[0], \"conditioning_scale\", text=\"ControlNet Conditioning Scale\")\n\n                col.prop(context.scene, \"dream_textures_project_bake\")\n                if context.scene.dream_textures_project_bake:\n                    for obj in context.selected_objects:\n                        col.prop_search(obj.data.uv_layers, \"active\", obj.data, \"uv_layers\", text=f\"{obj.name} Target UVs\")\n\n                row = layout.row(align=True)\n                row.scale_y = 1.5\n                if CancelGenerator.poll(context):\n                    row.operator(CancelGenerator.bl_idname, icon=\"SNAP_FACE\", text=\"\")\n                if context.scene.dream_textures_progress <= 0:\n                    if context.scene.dream_textures_info != \"\":\n                        disabled_row = row.row(align=True)\n                        disabled_row.operator(ProjectDreamTexture.bl_idname, text=context.scene.dream_textures_info, icon=\"INFO\")\n                        disabled_row.enabled = False\n                    else:\n                        r = row.row(align=True)\n                        r.operator(ProjectDreamTexture.bl_idname, icon=\"MOD_UVPROJECT\")\n                        r.enabled = context.object is not None and context.object.mode == 'EDIT'\n                else:\n                    disabled_row = row.row(align=True)\n                    disabled_row.use_property_split = True\n                    disabled_row.prop(context.scene, 'dream_textures_progress', slider=True)\n                    disabled_row.enabled = False\n                row.operator(ReleaseGenerator.bl_idname, icon=\"X\", text=\"\")\n                \n                # Validation\n                try:\n                    _validate_projection(context)\n                    prompt = context.scene.dream_textures_project_prompt\n                    backend: api.Backend = prompt.get_backend()\n                    args = prompt.generate_args(context)\n                    args.task = api.task.PromptToImage() if context.scene.dream_textures_project_use_control_net else api.task.DepthToImage(None, None, 0)\n                    backend.validate(args)\n                except FixItError as e:\n                    error_box = layout.box()\n                    error_box.use_property_split = False\n                    for i, line in enumerate(e.args[0].split('\\n')):\n                        error_box.label(text=line, icon=\"ERROR\" if i == 0 else \"NONE\")\n                    e._draw(context.scene.dream_textures_project_prompt, context, error_box)\n                except Exception as e:\n                    print(e)\n        return ActionsPanel\n    yield create_panel('VIEW_3D', 'UI', DREAM_PT_dream_panel_projection.bl_idname, actions_panel, get_prompt)\n\ndef bake(context, mesh, src, dest, src_uv, dest_uv):\n    def bake_shader():\n        vert_out = gpu.types.GPUStageInterfaceInfo(\"my_interface\")\n        vert_out.smooth('VEC2', \"uvInterp\")\n\n        shader_info = gpu.types.GPUShaderCreateInfo()\n        shader_info.sampler(0, 'FLOAT_2D', \"image\")\n        shader_info.vertex_in(0, 'VEC2', \"src_uv\")\n        shader_info.vertex_in(1, 'VEC2', \"dest_uv\")\n        shader_info.vertex_out(vert_out)\n        shader_info.fragment_out(0, 'VEC4', \"fragColor\")\n\n        shader_info.vertex_source(\"\"\"\nvoid main()\n{\n    gl_Position = vec4(dest_uv * 2 - 1, 0.0, 1.0);\n    uvInterp = src_uv;\n}\n\"\"\")\n\n        shader_info.fragment_source(\"\"\"\nvoid main()\n{\n    fragColor = texture(image, uvInterp);\n}\n\"\"\")\n\n        return gpu.shader.create_from_info(shader_info)\n\n    width, height = dest.size[0], dest.size[1]\n    offscreen = gpu.types.GPUOffScreen(width, height)\n\n    buffer = gpu.types.Buffer('FLOAT', width * height * 4, src)\n    texture = gpu.types.GPUTexture(size=(width, height), data=buffer, format='RGBA16F')\n    \n    with offscreen.bind():\n        fb = gpu.state.active_framebuffer_get()\n        fb.clear(color=(0.0, 0.0, 0.0, 0.0))\n        with gpu.matrix.push_pop():\n            gpu.matrix.load_matrix(mathutils.Matrix.Identity(4))\n            gpu.matrix.load_projection_matrix(mathutils.Matrix.Identity(4))\n\n            vertices = np.array([[l.vert.index for l in loop] for loop in mesh.calc_loop_triangles()], dtype='i')\n\n            shader = bake_shader()\n            batch = batch_for_shader(\n                shader, 'TRIS',\n                {\"src_uv\": src_uv, \"dest_uv\": dest_uv},\n                indices=vertices,\n            )\n            shader.uniform_sampler(\"image\", texture)\n            batch.draw(shader)\n        projected = np.array(fb.read_color(0, 0, width, height, 4, 0, 'FLOAT').to_list())\n    offscreen.free()\n    dest.pixels[:] = projected.ravel()\n\nclass ProjectDreamTexture(bpy.types.Operator):\n    bl_idname = \"shade.dream_texture_project\"\n    bl_label = \"Project Dream Texture\"\n    bl_description = \"Automatically texture all selected objects using the depth buffer and Stable Diffusion\"\n    bl_options = {'REGISTER'}\n\n    @classmethod\n    def poll(cls, context):\n        try:\n            _validate_projection(context)\n            prompt = context.scene.dream_textures_project_prompt\n            backend: api.Backend = prompt.get_backend()\n            args = prompt.generate_args(context)\n            args.task = api.task.PromptToImage() if context.scene.dream_textures_project_use_control_net else api.task.DepthToImage(None, None, 0)\n            backend.validate(args)\n        except:\n            return False\n        return Generator.shared().can_use()\n\n    @classmethod\n    def get_uv_layer(cls, mesh: bmesh.types.BMesh):\n        for i in range(len(mesh.loops.layers.uv)):\n            uv = mesh.loops.layers.uv[i]\n            if uv.name.lower() == \"projected uvs\":\n                return uv, i\n\n        return mesh.loops.layers.uv.new(\"Projected UVs\"), len(mesh.loops.layers.uv) - 1\n\n    def execute(self, context):\n        # Setup the progress indicator\n        def step_progress_update(self, context):\n            if hasattr(context.area, \"regions\"):\n                for region in context.area.regions:\n                    if region.type == \"UI\":\n                        region.tag_redraw()\n            return None\n        bpy.types.Scene.dream_textures_progress = bpy.props.IntProperty(name=\"\", default=0, min=0, max=context.scene.dream_textures_project_prompt.steps, update=step_progress_update)\n        context.scene.dream_textures_info = \"Starting...\"\n\n        # Get region size\n        region_width = region_height = None\n        for area in context.screen.areas:\n            if area.type == 'VIEW_3D':\n                for region in area.regions:\n                    if region.type == 'WINDOW':\n                        region_width, region_height = region.width, region.height\n\n        if region_width is None or region_height is None:\n            self.report({'ERROR'}, \"Could not determine region size.\")\n\n        # Render the viewport\n        if context.scene.dream_textures_project_framebuffer_arguments == 'color':\n            context.scene.dream_textures_info = \"Rendering viewport color...\"\n            res_x, res_y = context.scene.render.resolution_x, context.scene.render.resolution_y\n            view3d_spaces = []\n            for area in context.screen.areas:\n                if area.type == 'VIEW_3D':\n                    for region in area.regions:\n                        if region.type == 'WINDOW':\n                            context.scene.render.resolution_x, context.scene.render.resolution_y = region.width, region.height\n                    for space in area.spaces:\n                        if space.type == 'VIEW_3D':\n                            if space.overlay.show_overlays:\n                                view3d_spaces.append(space)\n                                space.overlay.show_overlays = False\n            init_img_path = tempfile.NamedTemporaryFile(suffix='.png').name\n            render_filepath, file_format = context.scene.render.filepath, context.scene.render.image_settings.file_format\n            context.scene.render.image_settings.file_format = 'PNG'\n            context.scene.render.filepath = init_img_path\n            bpy.ops.render.opengl(write_still=True, view_context=True)\n            for space in view3d_spaces:\n                space.overlay.show_overlays = True\n            context.scene.render.resolution_x, context.scene.render.resolution_y = res_x, res_y\n            context.scene.render.filepath, context.scene.render.image_settings.file_format = render_filepath, file_format\n        else:\n            init_img_path = None\n\n        context.scene.dream_textures_info = \"Generating UVs and materials...\"\n        \n        material = bpy.data.materials.new(name=\"diffused-material\")\n        material.use_nodes = True\n        image_texture_node = material.node_tree.nodes.new(\"ShaderNodeTexImage\")\n        principled_node = next((n for n in material.node_tree.nodes if n.type == 'BSDF_PRINCIPLED'))\n        material.node_tree.links.new(image_texture_node.outputs[0], principled_node.inputs[0])\n        uv_map_node = material.node_tree.nodes.new(\"ShaderNodeUVMap\")\n        uv_map_node.uv_map = bpy.context.selected_objects[0].data.uv_layers.active.name if context.scene.dream_textures_project_bake else \"Projected UVs\"\n        material.node_tree.links.new(uv_map_node.outputs[0], image_texture_node.inputs[0])\n        target_objects = []\n        for obj in bpy.context.selected_objects:\n            if not hasattr(obj, \"data\") or not hasattr(obj.data, \"materials\"):\n                continue\n            material_index = len(obj.material_slots)\n            obj.data.materials.append(material)\n            mesh = bmesh.from_edit_mesh(obj.data)\n            # Project from UVs view and update material index\n            mesh.verts.ensure_lookup_table()\n            mesh.verts.index_update()\n            def vert_to_uv(v):\n                screen_space = view3d_utils.location_3d_to_region_2d(context.region, context.space_data.region_3d, obj.matrix_world @ v.co)\n                if screen_space is None:\n                    return None\n                return (screen_space[0] / context.region.width, screen_space[1] / context.region.height)\n            uv_layer, uv_layer_index = ProjectDreamTexture.get_uv_layer(mesh)\n\n            bm = mesh.copy()\n            bm.select_mode = {'FACE'}\n            bmesh.ops.split_edges(bm, edges=bm.edges)\n            bmesh.ops.delete(bm, geom=[f for f in bm.faces if not f.select], context='FACES')\n            target_objects.append((bm, bm.loops.layers.uv[uv_layer_index]))\n\n            mesh.faces.ensure_lookup_table()\n            for face in mesh.faces:\n                if face.select:\n                    for loop in face.loops:\n                        uv = vert_to_uv(mesh.verts[loop.vert.index])\n                        if uv is None:\n                            continue\n                        loop[uv_layer].uv = uv\n                    face.material_index = material_index\n            bmesh.update_edit_mesh(obj.data)\n\n        context.scene.dream_textures_info = \"Rendering viewport depth...\"\n\n        depth = np.flipud(render_depth_map(\n            context.evaluated_depsgraph_get(),\n            collection=None,\n            width=region_width,\n            height=region_height,\n            matrix=context.space_data.region_3d.view_matrix,\n            projection_matrix=context.space_data.region_3d.window_matrix,\n            main_thread=True\n        ))\n        \n        texture = None\n\n        def step_callback(progress: List[api.GenerationResult]) -> bool:\n            nonlocal texture\n            context.scene.dream_textures_progress = progress[-1].progress\n            image = api.GenerationResult.tile_images(progress)\n            texture = image_utils.np_to_bpy(image, f\"Step {progress[-1].progress}/{progress[-1].total}\", texture)\n            image_texture_node.image = texture\n            return CancelGenerator.should_continue\n\n        def callback(results: List[api.GenerationResult] | Exception):\n            CancelGenerator.should_continue = None\n            if isinstance(results, Exception):\n                context.scene.dream_textures_info = \"\"\n                context.scene.dream_textures_progress = 0\n                if not isinstance(results, InterruptedError): # this is a user-initiated cancellation\n                    eval('bpy.ops.' + NotifyResult.bl_idname)('INVOKE_DEFAULT', exception=repr(results))\n                raise results\n            else:\n                nonlocal texture\n                context.scene.dream_textures_info = \"\"\n                context.scene.dream_textures_progress = 0\n                result = results[-1]\n                prompt_subject = context.scene.dream_textures_project_prompt.prompt_structure_token_subject\n                seed_str_length = len(str(result.seed))\n                trim_aware_name = (prompt_subject[:54 - seed_str_length] + '..') if len(prompt_subject) > 54 else prompt_subject\n                name_with_trimmed_prompt = f\"{trim_aware_name} ({result.seed})\"\n\n                texture = image_utils.np_to_bpy(result.image, name_with_trimmed_prompt, texture)\n                image_texture_node.image = texture\n                if context.scene.dream_textures_project_bake:\n                    for bm, src_uv_layer in target_objects:\n                        dest = bpy.data.images.new(name=f\"{texture.name} (Baked)\", width=texture.size[0], height=texture.size[1])\n                        \n                        dest_uv_layer = bm.loops.layers.uv.active\n                        src_uvs = np.empty((len(bm.verts), 2), dtype=np.float32)\n                        dest_uvs = np.empty((len(bm.verts), 2), dtype=np.float32)\n                        for face in bm.faces:\n                            for loop in face.loops:\n                                src_uvs[loop.vert.index] = loop[src_uv_layer].uv\n                                dest_uvs[loop.vert.index] = loop[dest_uv_layer].uv\n                        bake(context, bm, result.image.ravel(), dest, src_uvs, dest_uvs)\n                        dest.update()\n                        dest.pack()\n                        image_texture_node.image = dest\n        \n        backend: api.Backend = context.scene.dream_textures_project_prompt.get_backend()\n\n        context.scene.dream_textures_info = \"Starting...\"\n        CancelGenerator.should_continue = True # reset global cancellation state\n        image_data = bpy.data.images.load(init_img_path) if init_img_path is not None else None\n        image = np.asarray(image_data.pixels).reshape((*depth.shape, image_data.channels)) if image_data is not None else None\n        if context.scene.dream_textures_project_use_control_net:\n            generated_args: api.GenerationArguments = context.scene.dream_textures_project_prompt.generate_args(context, init_image=image, control_images=[image_utils.rgba(depth)])\n            backend.generate(generated_args, step_callback=step_callback, callback=callback)\n        else:\n            generated_args: api.GenerationArguments = context.scene.dream_textures_project_prompt.generate_args(context)\n            generated_args.task = api.DepthToImage(depth, image, context.scene.dream_textures_project_prompt.strength)\n            backend.generate(generated_args, step_callback=step_callback, callback=callback)\n\n        for area in context.screen.areas:\n            if area.type == 'VIEW_3D':\n                area.tag_redraw()\n                return {'FINISHED'}\n\n        return {'FINISHED'}\n"
  },
  {
    "path": "operators/upscale.py",
    "content": "import bpy\nimport numpy as np\nfrom typing import List, Literal\nfrom .. import api\nfrom ..prompt_engineering import custom_structure\nfrom ..generator_process import Generator\nfrom .dream_texture import CancelGenerator\nfrom .. import image_utils\n\nupscale_options = [\n    (\"2\", \"2x\", \"\", 2),\n    (\"4\", \"4x\", \"\", 4),\n    (\"8\", \"8x\", \"\", 8),\n]\n\ndef get_source_image(context):\n    node_tree = context.material.node_tree if hasattr(context, 'material') else None\n    active_node = next((node for node in node_tree.nodes if node.select and node.bl_idname == 'ShaderNodeTexImage'), None) if node_tree is not None else None\n    if active_node is not None and active_node.image is not None:\n        return active_node.image\n    elif context.area.type == 'IMAGE_EDITOR':\n        return context.area.spaces.active.image\n    else:\n        input_image = None\n        for area in context.screen.areas:\n            if area.type == 'IMAGE_EDITOR':\n                if area.spaces.active.image is not None:\n                    input_image = area.spaces.active.image\n        return input_image\n\nclass Upscale(bpy.types.Operator):\n    bl_idname = \"shade.dream_textures_upscale\"\n    bl_label = \"Upscale\"\n    bl_description = (\"Upscale with Stable Diffusion x4 Upscaler\")\n    bl_options = {\"REGISTER\"}\n\n    @classmethod\n    def poll(cls, context):\n        return Generator.shared().can_use()\n\n    def execute(self, context):\n        screen = context.screen\n        scene = context.scene\n        node_tree = context.material.node_tree if hasattr(context, 'material') else None\n        active_node = next((node for node in node_tree.nodes if node.select and node.bl_idname == 'ShaderNodeTexImage'), None) if node_tree is not None else None\n\n        def step_progress_update(self, context):\n            if hasattr(context.area, \"regions\"):\n                for region in context.area.regions:\n                    if region.type == \"UI\":\n                        region.tag_redraw()\n            return None\n\n        bpy.types.Scene.dream_textures_info = bpy.props.StringProperty(name=\"Info\", update=step_progress_update)\n\n        input_image = get_source_image(context)\n        if input_image is None:\n            self.report({\"ERROR\"}, \"No open image in the Image Editor space, or selected Image Texture node.\")\n            return {\"FINISHED\"}\n        image_pixels = image_utils.bpy_to_np(input_image)\n\n        generated_args = context.scene.dream_textures_upscale_prompt.generate_args(context)\n        context.scene.dream_textures_upscale_seamless_result.update_args(generated_args)\n\n        # Setup the progress indicator\n        def step_progress_update(self, context):\n            if hasattr(context.area, \"regions\"):\n                for region in context.area.regions:\n                    if region.type == \"UI\":\n                        region.tag_redraw()\n            return None\n        bpy.types.Scene.dream_textures_progress = bpy.props.IntProperty(name=\"\", default=0, min=0, max=generated_args.steps, update=step_progress_update)\n        scene.dream_textures_info = \"Starting...\"\n\n        last_data_block = None\n        def step_callback(progress: List[api.GenerationResult]) -> bool:\n            nonlocal last_data_block\n            if last_data_block is None:\n                bpy.types.Scene.dream_textures_progress = bpy.props.IntProperty(name=\"\", default=progress[-1].progress, min=0, max=progress[-1].total, update=step_progress_update)\n            \n            scene.dream_textures_progress = progress[-1].progress\n            if progress[-1].image is not None:\n                last_data_block = image_utils.np_to_bpy(progress[-1].image, f\"Tile {progress[-1].progress}/{progress[-1].total}\", last_data_block)\n            for area in screen.areas:\n                if area.type == 'IMAGE_EDITOR' and not area.spaces.active.use_image_pin:\n                    area.spaces.active.image = last_data_block\n            return CancelGenerator.should_continue\n\n        def callback(results: List[api.GenerationResult] | Exception):\n            if isinstance(results, Exception):\n                scene.dream_textures_info = \"\"\n                scene.dream_textures_progress = 0\n                CancelGenerator.should_continue = None\n            else:\n                nonlocal last_data_block\n                if last_data_block is not None:\n                    bpy.data.images.remove(last_data_block)\n                    last_data_block = None\n                if results[-1].image is None:\n                    return\n                image = image_utils.np_to_bpy(results[-1].image, f\"{input_image.name} (Upscaled)\", last_data_block)\n                for area in screen.areas:\n                    if area.type == 'IMAGE_EDITOR' and not area.spaces.active.use_image_pin:\n                        area.spaces.active.image = image\n                if active_node is not None:\n                    active_node.image = image\n                scene.dream_textures_info = \"\"\n                scene.dream_textures_progress = 0\n                CancelGenerator.should_continue = None\n        \n        prompt = context.scene.dream_textures_upscale_prompt\n        prompt.prompt_structure = custom_structure.id\n        backend: api.Backend = prompt.get_backend()\n        generated_args.task = api.models.task.Upscale(image=image_pixels, tile_size=context.scene.dream_textures_upscale_tile_size, blend=context.scene.dream_textures_upscale_blend)\n        CancelGenerator.should_continue = True\n        backend.generate(\n            generated_args, step_callback=step_callback, callback=callback\n        )\n        \n        return {\"FINISHED\"}"
  },
  {
    "path": "operators/view_history.py",
    "content": "import bpy\nfrom bpy_extras.io_utils import ImportHelper, ExportHelper\nimport json\nimport os\nfrom ..property_groups.dream_prompt import DreamPrompt, scheduler_options\nfrom ..preferences import StableDiffusionPreferences\n    \nclass SCENE_UL_HistoryList(bpy.types.UIList):\n    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):\n        if self.layout_type in {'DEFAULT', 'COMPACT'}:\n            layout.label(text=item.get_prompt_subject(), translate=False, icon_value=icon)\n            layout.label(text=f\"{item.seed}\", translate=False)\n            layout.label(text=f\"{item.width}x{item.height}\", translate=False)\n            layout.label(text=f\"{item.steps} steps\", translate=False)\n            layout.label(text=item.scheduler, translate=False)\n        elif self.layout_type == 'GRID':\n            layout.alignment = 'CENTER'\n            layout.label(text=\"\", icon_value=icon)\n\nclass RecallHistoryEntry(bpy.types.Operator):\n    bl_idname = \"shade.dream_textures_history_recall\"\n    bl_label = \"Recall Prompt\"\n    bl_description = \"Open the Dream Textures dialog with the historical properties filled in\"\n    bl_options = {'REGISTER'}\n\n    @classmethod\n    def poll(self, context):\n        return context.scene.dream_textures_history_selection is not None\n    \n    def execute(self, context):\n        selection = context.scene.dream_textures_history[context.scene.dream_textures_history_selection]\n        for prop in selection.__annotations__.keys():\n            if hasattr(context.scene.dream_textures_prompt, prop):\n                match prop:\n                    case 'control_nets':\n                        context.scene.dream_textures_prompt.control_nets.clear()\n                        for net in selection.control_nets:\n                            n = context.scene.dream_textures_prompt.control_nets.add()\n                            for k in n.__annotations__.keys():\n                                setattr(n, k, getattr(net, k))\n                    case _:\n                        setattr(context.scene.dream_textures_prompt, prop, getattr(selection, prop))\n            # when the seed of the promt is found in the available image datablocks, use that one in the open image editor\n            # note: when there is more than one image with the seed in it's name, do nothing. Same when no image with that seed is available.\n            if prop == 'hash':\n                hash_string = str(getattr(selection, prop))\n                existing_image = None\n                # accessing custom properties for image datablocks in Blender is still a bit cumbersome\n                for i in bpy.data.images:\n                    if i.get('dream_textures_hash', None) == hash_string:\n                        existing_image = i\n                        break\n                if existing_image is not None:\n                    for area in context.screen.areas:\n                        if area.type != 'IMAGE_EDITOR':\n                            continue\n                        area.spaces.active.image = existing_image\n\n        return {\"FINISHED\"}\n\nclass ClearHistory(bpy.types.Operator):\n    bl_idname = \"shade.dream_textures_history_clear\"\n    bl_label = \"Clear History\"\n    bl_description = \"Removes all history entries\"\n    bl_options = {'REGISTER'}\n    \n    def execute(self, context):\n        context.scene.dream_textures_history.clear()\n\n        return {\"FINISHED\"}\n\nclass RemoveHistorySelection(bpy.types.Operator):\n    bl_idname = \"shade.dream_textures_history_remove_selection\"\n    bl_label = \"Remove History Selection\"\n    bl_description = \"Removes the selected history entry\"\n    bl_options = {'REGISTER'}\n\n    @classmethod\n    def poll(self, context):\n        return context.scene.dream_textures_history_selection is not None\n    \n    def execute(self, context):\n        context.scene.dream_textures_history.remove(context.scene.dream_textures_history_selection)\n\n        return {\"FINISHED\"}\n\nclass ExportHistorySelection(bpy.types.Operator, ExportHelper):\n    bl_idname = \"shade.dream_textures_history_export\"\n    bl_label = \"Export Prompt\"\n    bl_description = \"Exports the selected history entry to a JSON file\"\n    \n    filename_ext = \".json\"\n\n    filter_glob: bpy.props.StringProperty(\n        default=\"*.json\",\n        options={'HIDDEN'},\n        maxlen=255,\n    )\n\n    @classmethod\n    def poll(self, context):\n        return context.scene.dream_textures_history_selection is not None\n    \n    def invoke(self, context, event):\n        selection = context.scene.dream_textures_history[context.scene.dream_textures_history_selection]\n        self.filepath = \"untitled\" if selection is None else selection.get_prompt_subject()\n        context.window_manager.fileselect_add(self)\n        return {'RUNNING_MODAL'}\n\n    def execute(self, context):\n        selection = context.scene.dream_textures_history[context.scene.dream_textures_history_selection]\n        if selection is None:\n            self.report({\"ERROR\"}, \"No valid selection to export.\")\n            return {\"FINISHED\"}\n        with open(self.filepath, 'w', encoding='utf-8') as target:\n            args = {key: getattr(selection, key) for key in DreamPrompt.__annotations__}\n            args[\"outpaint_origin\"] = list(args[\"outpaint_origin\"])\n            json.dump(args, target, indent=4)\n\n        return {\"FINISHED\"}\n\nclass ImportPromptFile(bpy.types.Operator, ImportHelper):\n    bl_idname = \"shade.dream_textures_import_prompt\"\n    bl_label = \"Import Prompt\"\n    bl_description = \"Imports a JSON file as a prompt\"\n    \n    filename_ext = \".json\"\n\n    filter_glob: bpy.props.StringProperty(\n        default=\"*.json\",\n        options={'HIDDEN'},\n        maxlen=255,\n    )\n    \n    def execute(self, context):\n        _, extension = os.path.splitext(self.filepath)\n        if extension != \".json\":\n            self.report({\"ERROR\"}, \"Invalid prompt JSON file selected.\")\n            return {\"FINISHED\"}\n        \n        with open(self.filepath, 'r', encoding='utf-8') as target:\n            args = json.load(target)\n            for key, value in args.items():\n                if hasattr(context.scene.dream_textures_prompt, key) and value is not None:\n                    setattr(context.scene.dream_textures_prompt, key, value)\n\n        return {\"FINISHED\"}"
  },
  {
    "path": "preferences.py",
    "content": "import bpy\nfrom bpy.props import CollectionProperty, StringProperty\nfrom bpy_extras.io_utils import ImportHelper\nimport os\nimport webbrowser\nimport importlib.util\nimport site\n\nfrom .absolute_path import absolute_path\nfrom .operators.install_dependencies import InstallDependencies, UninstallDependencies\nfrom .operators.open_latest_version import OpenLatestVersion\nfrom .ui.presets import RestoreDefaultPresets, default_presets_missing\nfrom .generator_process import Generator\nfrom .generator_process.actions.huggingface_hub import DownloadStatus, Model as HubModel\nfrom .generator_process.models import Checkpoint, ModelConfig, ModelType\n\nis_downloading = False\n\nclass OpenURL(bpy.types.Operator):\n    bl_idname = \"dream_textures.open_url\"\n    bl_label = \"Get Access Token\"\n    bl_description = (\"Opens huggingface.co to the tokens page\")\n    bl_options = {\"REGISTER\", \"INTERNAL\"}\n\n    url: bpy.props.StringProperty(name=\"URL\")\n\n    def execute(self, context):\n        webbrowser.open(self.url)\n        return {\"FINISHED\"}\n\n_model_config_options = [(m.name, m.value, '') for m in ModelConfig]\nimport_extensions = ['.ckpt', '.safetensors', '.pth']\nimport_extensions_glob = \";\".join(import_extensions).replace(\".\", \"*.\")\nclass ImportWeights(bpy.types.Operator, ImportHelper):\n    bl_idname = \"dream_textures.import_weights\"\n    bl_label = \"Import Checkpoint File\"\n    filename_ext = \".ckpt\"\n    filter_glob: bpy.props.StringProperty(\n        default=import_extensions_glob,\n        options={'HIDDEN'},\n        maxlen=255,\n    )\n    model_config: bpy.props.EnumProperty(\n        name=\"Model Config\",\n        items=_model_config_options\n    )\n    prefer_fp16_variant: bpy.props.BoolProperty(\n        name=\"Save Half Precision Weights\",\n        default=True\n    )\n\n    def execute(self, context):\n        global is_downloading\n        is_downloading = True\n        f = Generator.shared().convert_original_stable_diffusion_to_diffusers(self.filepath, ModelConfig[self.model_config], self.prefer_fp16_variant)\n        def on_progress(_, response: DownloadStatus):\n            bpy.context.preferences.addons[__package__].preferences.download_file = response.file\n            bpy.context.preferences.addons[__package__].preferences.download_progress = int((response.index / response.total) * 100)\n        def on_done(future):\n            global is_downloading\n            is_downloading = False\n            fetch_installed_models()\n        def on_exception(_, exception):\n            self.report({\"ERROR\"}, str(exception))\n            raise exception\n        f.add_response_callback(on_progress)\n        f.add_done_callback(on_done)\n        f.add_exception_callback(on_exception)\n        return {\"FINISHED\"}\n\nclass Model(bpy.types.PropertyGroup):\n    bl_label = \"Model\"\n    bl_idname = \"dream_textures.Model\"\n\n    model: bpy.props.StringProperty(name=\"Model\")\n    model_base: bpy.props.StringProperty()\n    downloads: bpy.props.IntProperty(name=\"Downloads\")\n    likes: bpy.props.IntProperty(name=\"Likes\")\n    model_type: bpy.props.EnumProperty(name=\"Model Type\", items=[(t.name, t.name, '') for t in ModelType])\n\nclass PREFERENCES_UL_ModelList(bpy.types.UIList):\n    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):\n        model_name = item.model\n        is_installed = False\n        if os.path.exists(item.model):\n            model_name = os.path.basename(item.model).replace('models--', '').replace('--', '/')\n            is_installed = True\n        split = layout.split(factor=0.75)\n        split.label(text=model_name)\n        if item.downloads != -1:\n            split.label(text=str(item.downloads), icon=\"IMPORT\")\n        if item.downloads != -1:\n            split.label(text=str(item.likes), icon=\"HEART\")\n        if ModelType[item.model_type] != ModelType.UNKNOWN:\n            split.label(text=item.model_type.replace('_', ' ').title())\n        install_model = layout.operator(InstallModel.bl_idname, text=\"\", icon=\"FILE_FOLDER\" if is_installed else \"IMPORT\")\n        install_model.model = item.model\n        install_model.prefer_fp16_variant = data.prefer_fp16_variant\n        install_model.resume_download = data.resume_download\n\ndef set_model_list(model_list: str, models: list):\n    getattr(bpy.context.preferences.addons[__package__].preferences, model_list).clear()\n    for model in models:\n        m = getattr(bpy.context.preferences.addons[__package__].preferences, model_list).add()\n        m.model = model.id\n        m.model_base = os.path.basename(model.id)\n        m.downloads = model.downloads\n        m.likes = model.likes\n        try:\n            m.model_type = model.model_type.name\n        except:\n            pass\n\nclass checkpoint_lookup:\n    _checkpoints = {}\n\n    @classmethod\n    def get(cls, item):\n        return cls._checkpoints.get(item, item)\n\nclass model_lookup:\n    _models = {}\n\n    @classmethod\n    def get(cls, item):\n        return cls._models.get(item, None)\n\ndef fetch_installed_models(blocking=True):\n    def on_done(future):\n        model_list = future.result()\n\n        model_lookup._models = { os.path.basename(model.id).replace('models--', '').replace('--', '/'): model for model in model_list }\n\n        pref = bpy.context.preferences.addons[__package__].preferences\n        checkpoint_links = ((link.path, ModelConfig[link.model_config]) for link in pref.linked_checkpoints)\n        checkpoints = {}\n        for path, config in checkpoint_links:\n            if not os.path.exists(path):\n                continue\n            if os.path.isfile(path):\n                checkpoints[os.path.basename(path)] = (path, config)\n                continue\n            for name in os.listdir(path):\n                if os.path.splitext(name)[1] not in import_extensions:\n                    continue\n                if name in checkpoints:\n                    # file linked config takes precedence over folder linked config\n                    continue\n                checkpoints[name] = (os.path.join(path, name), config)\n        checkpoint_lookup._checkpoints.clear()\n        for path, config in checkpoints.values():\n            model = HubModel(path, \"\", [], -1, -1, ModelType.from_config(config))\n            model_list.append(model)\n            checkpoint_lookup._checkpoints[os.path.basename(path)] = Checkpoint(path, config)\n            model_lookup._models[os.path.basename(path)] = model\n\n        set_model_list('installed_models', model_list)\n\n    future = Generator.shared().hf_list_installed_models()\n    if blocking:\n        on_done(future)\n    else:\n        future.add_done_callback(on_done)\n\n\nclass ModelSearch(bpy.types.Operator):\n    bl_idname = \"dream_textures.model_search\"\n    bl_label = \"Search\"\n    bl_description = (\"Searches Hugging Face Hub for models\")\n    bl_options = {\"REGISTER\", \"INTERNAL\"}\n\n    def execute(self, context):\n        \n        return {\"FINISHED\"}\n\nclass InstallModel(bpy.types.Operator):\n    bl_idname = \"dream_textures.install_model\"\n    bl_label = \"Install or Open\"\n    bl_description = (\"Install or open a model from the cache\")\n    bl_options = {\"REGISTER\", \"INTERNAL\"}\n\n    model: StringProperty(name=\"Model ID\")\n    prefer_fp16_variant: bpy.props.BoolProperty(name=\"\", default=True)\n    resume_download: bpy.props.BoolProperty(name=\"\", default=True)\n\n    def execute(self, context):\n        if os.path.exists(self.model):\n            if os.path.isfile(self.model):\n                webbrowser.open(f\"file://{os.path.dirname(self.model)}\")\n            else:\n                webbrowser.open(f\"file://{self.model}\")\n        else:\n            global is_downloading\n            is_downloading = True\n            f = Generator.shared().hf_snapshot_download(\n                self.model,\n                bpy.context.preferences.addons[__package__].preferences.hf_token,\n                \"fp16\" if self.prefer_fp16_variant else None,\n                self.resume_download\n            )\n            def on_progress(_, response: DownloadStatus):\n                bpy.context.preferences.addons[__package__].preferences.download_file = response.file\n                bpy.context.preferences.addons[__package__].preferences.download_progress = int((response.index / response.total) * 100)\n            def on_done(future):\n                global is_downloading\n                is_downloading = False\n                fetch_installed_models()\n            def on_exception(_, exception):\n                self.report({\"ERROR\"}, str(exception))\n                raise exception\n            f.add_response_callback(on_progress)\n            f.add_done_callback(on_done)\n            f.add_exception_callback(on_exception)\n        return {\"FINISHED\"}\n\ndef _model_search(self, context):\n    def on_done(future):\n        set_model_list('model_results', future.result())\n    Generator.shared().hf_list_models(self.model_query, self.hf_token).add_done_callback(on_done)\n\ndef _update_ui(self, context):\n    if hasattr(context.area, \"regions\"):\n        for region in context.area.regions:\n            if region.type == \"UI\":\n                region.tag_redraw()\n    return None\n\ndef _template_model_download_progress(context, layout):\n    global is_downloading\n    preferences = context.preferences.addons[StableDiffusionPreferences.bl_idname].preferences\n    if is_downloading:\n        progress_col = layout.column()\n        progress_col.label(text=preferences.download_file)\n        progress_col.prop(preferences, \"download_progress\", slider=True)\n        progress_col.enabled = False\n    return is_downloading\n\nclass CheckpointGroup(bpy.types.PropertyGroup):\n    bl_label = \"Model\"\n    bl_idname = \"dream_textures.checkpoint\"\n\n    path: bpy.props.StringProperty(name=\"Checkpoint\")\n    model_config: bpy.props.EnumProperty(\n        name=\"Model Config\",\n        items=_model_config_options\n    )\n\nclass LinkCheckpoint(bpy.types.Operator, ImportHelper):\n    bl_idname = \"dream_textures.link_checkpoint\"\n    bl_label = \"Link Checkpoint File or Folder\"\n    filename_ext = \".ckpt\"\n    files: CollectionProperty(\n        type=bpy.types.OperatorFileListElement,\n        options={'HIDDEN', 'SKIP_SAVE'}\n    )\n    filter_glob: bpy.props.StringProperty(\n        default=import_extensions_glob,\n        options={'HIDDEN'},\n        maxlen=255,\n    )\n    model_config: bpy.props.EnumProperty(\n        name=\"Model Config\",\n        items=_model_config_options\n    )\n\n    def invoke(self, context, _event):\n        if os.path.isfile(self.filepath):\n            # Reset to a directory, otherwise the filename remains populated and can cause issues to select a directory if gone unnoticed.\n            self.filepath = os.path.dirname(self.filepath) + os.path.sep\n        return super().invoke(context, _event)\n\n    def execute(self, context):\n        pref = context.preferences.addons[__package__].preferences\n        for file in self.files:\n            path = self.filepath\n            if file.name != \"\":\n                path = os.path.join(os.path.dirname(path), file.name)\n\n            if not os.path.exists(path):\n                self.report({\"ERROR\"}, f\"{path} does not exist\")\n                continue\n            if os.path.isfile(path) and os.path.splitext(path)[1] not in import_extensions:\n                self.report({\"ERROR\"}, f\"{os.path.basename(path)} is not a checkpoint\")\n                continue\n\n            link = next((link for link in pref.linked_checkpoints if link.path == path), None)\n            if link is None:\n                link = pref.linked_checkpoints.add()\n                link.path = path\n            link.model_config = self.model_config\n\n        fetch_installed_models()\n\n        return {\"FINISHED\"}\n\nclass UnlinkCheckpoint(bpy.types.Operator):\n    bl_idname = \"dream_textures.unlink_checkpoint\"\n    bl_label = \"Unlink Checkpoint File\"\n\n    path: bpy.props.StringProperty()\n    def execute(self, context):\n        pref = context.preferences.addons[__package__].preferences\n        index = next((i for i, link in enumerate(pref.linked_checkpoints) if link.path == self.path), -1)\n        if index != -1:\n            pref.linked_checkpoints.remove(index)\n\n        fetch_installed_models()\n\n        return {\"FINISHED\"}\n\nclass PREFERENCES_UL_CheckpointList(bpy.types.UIList):\n    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):\n        split = layout.split(factor=0.75)\n        split.label(text=item.path)\n        split.label(text=ModelConfig[item.model_config].value)\n        install_model = layout.operator(InstallModel.bl_idname, text=\"\", icon=\"FILE_FOLDER\")\n        install_model.model = item.path\n        unlink = layout.operator(UnlinkCheckpoint.bl_idname, text=\"\", icon=\"TRASH\")\n        unlink.path = item.path\n\nclass StableDiffusionPreferences(bpy.types.AddonPreferences):\n    bl_idname = __package__\n\n    dream_studio_key: StringProperty(name=\"DreamStudio Key\")\n\n    model_query: StringProperty(name=\"Search\", update=_model_search)\n    model_results: CollectionProperty(type=Model)\n    active_model_result: bpy.props.IntProperty(name=\"Active Model\", default=0)\n    hf_token: StringProperty(name=\"HuggingFace Token\")\n    prefer_fp16_variant: bpy.props.BoolProperty(name=\"Prefer Half Precision Weights\", description=\"Download fp16 weights if available for smaller file size. If you run with 'Half Precision' disabled, you should not use this setting\", default=True)\n    resume_download: bpy.props.BoolProperty(name=\"Resume Incomplete Download\", description=\"Continue an in-progress download in case if Blender was closed or connection was interrupted, otherwise incomplete files will be entirely redownloaded\", default=True)\n\n    installed_models: CollectionProperty(type=Model)\n    active_installed_model: bpy.props.IntProperty(name=\"Active Model\", default=0)\n\n    linked_checkpoints: CollectionProperty(type=CheckpointGroup)\n    active_linked_checkpoint: bpy.props.IntProperty(name=\"Active Checkpoint\", default=0)\n\n    download_file: bpy.props.StringProperty(name=\"\")\n    download_progress: bpy.props.IntProperty(name=\"\", min=0, max=100, subtype=\"PERCENTAGE\", update=_update_ui)\n\n    model_cache = []\n\n    @staticmethod\n    def register():\n        fetch_installed_models(False)\n\n    def draw(self, context):\n        layout = self.layout\n\n        weights_installed = len(self.installed_models) > 0\n\n        if not weights_installed:\n            layout.label(text=\"Complete the following steps to finish setting up the addon:\")\n\n        has_dependencies = len(os.listdir(absolute_path(\".python_dependencies\"))) > 2\n        if has_dependencies:\n            if not _template_model_download_progress(context, layout):\n                conflicting_packages = [\"wandb\", \"k_diffusion\"]\n                conflicting_package_specs = {}\n                for package in conflicting_packages:\n                    spec = importlib.util.find_spec(package)\n                    if spec is not None:\n                        conflicting_package_specs[package] = spec\n                if len(conflicting_package_specs) > 0:\n                    conflicts_box = layout.box()\n                    conflicts_box.label(text=\"WARNING\", icon=\"ERROR\")\n                    conflicts_box.label(text=f\"The following packages conflict with Dream Textures: {', '.join(conflicting_packages)}\")\n                    conflicts_box.label(text=f\"You may need to run Blender as an administrator to remove these packages\")\n                    conflicts_box.operator(UninstallDependencies.bl_idname, text=\"Uninstall Conflicting Packages\", icon=\"CANCEL\").conflicts = ' '.join(conflicting_packages)\n                    conflicts_box.label(text=f\"If the button above fails, you can remove the following folders manually:\")\n                    for package in conflicting_packages:\n                        if package not in conflicting_package_specs:\n                            continue\n                        location = conflicting_package_specs[package].submodule_search_locations[0]\n                        conflicts_box.operator(OpenURL.bl_idname, text=f\"Open '{location}'\").url = f\"file://{location}\"\n\n                if not weights_installed:\n                    default_weights_box = layout.box()\n                    default_weights_box.label(text=\"You need to download at least one model.\")\n                    install_model = default_weights_box.operator(InstallModel.bl_idname, text=\"Download Stable Diffusion v2.1 (Recommended)\", icon=\"IMPORT\")\n                    install_model.model = \"stabilityai/stable-diffusion-2-1\"\n                    install_model.prefer_fp16_variant = self.prefer_fp16_variant\n                    install_model.resume_download = self.resume_download\n\n                search_box = layout.box()\n                search_box.label(text=\"Find Models\", icon=\"SETTINGS\")\n                search_box.label(text=\"Search Hugging Face Hub for more compatible models.\")\n\n                search_box.prop(self, \"model_query\", text=\"\", icon=\"VIEWZOOM\")\n                \n                if len(self.model_results) > 0:\n                    search_box.template_list(PREFERENCES_UL_ModelList.__name__, \"dream_textures_model_results\", self, \"model_results\", self, \"active_model_result\")\n\n                search_box.label(text=\"Some models require authentication. Provide a token to download gated models.\")\n\n                auth_row = search_box.row()\n                auth_row.prop(self, \"hf_token\", text=\"Token\")\n                auth_row.operator(OpenURL.bl_idname, text=\"Get Your Token\", icon=\"KEYINGSET\").url = \"https://huggingface.co/settings/tokens\"\n                \n                search_box.prop(self, \"prefer_fp16_variant\")\n                search_box.prop(self, \"resume_download\")\n\n            layout.template_list(PREFERENCES_UL_ModelList.__name__, \"dream_textures_installed_models\", self, \"installed_models\", self, \"active_installed_model\")\n            import_weights = layout.operator(ImportWeights.bl_idname, icon='IMPORT')\n            import_weights.prefer_fp16_variant = self.prefer_fp16_variant\n            layout.template_list(PREFERENCES_UL_CheckpointList.__name__, \"dream_textures_linked_checkpoints\", self, \"linked_checkpoints\", self, \"active_linked_checkpoint\")\n            layout.operator(LinkCheckpoint.bl_idname, icon='FOLDER_REDIRECT')\n\n            if weights_installed or len(self.dream_studio_key) > 0:\n                complete_box = layout.box()\n                complete_box.label(text=\"Addon Setup Complete\", icon=\"CHECKMARK\")\n                complete_box.label(text=\"To locate the interface:\")\n                complete_box.label(text=\"1. Open an Image Editor or Shader Editor space\")\n                complete_box.label(text=\"2. Enable 'View' > 'Sidebar'\")\n                complete_box.label(text=\"3. Select the 'Dream' tab\")\n            \n            if default_presets_missing():\n                presets_box = layout.box()\n                presets_box.label(text=\"Default Presets\", icon=\"PRESET\")\n                presets_box.label(text=\"It looks like you removed some of the default presets.\")\n                presets_box.label(text=\"You can restore them here.\")\n                presets_box.operator(RestoreDefaultPresets.bl_idname, icon=\"RECOVER_LAST\")\n        else:\n            missing_dependencies_box = layout.box()\n            missing_dependencies_box.label(text=\"Dependencies Missing\", icon=\"ERROR\")\n            missing_dependencies_box.label(text=\"You've likely downloaded source instead of release by accident.\")\n            missing_dependencies_box.label(text=\"Follow the instructions to install for your platform.\")\n            missing_dependencies_box.operator(OpenLatestVersion.bl_idname, text=\"Download Latest Release\")\n        \n        contributors_box = layout.box()\n        contributors_box.label(text=\"Contributors\", icon=\"COMMUNITY\")\n        contributors_box.label(text=\"Dream Textures is made possible by the contributors on GitHub.\")\n        contributors_box.operator(OpenURL.bl_idname, text=\"See All Contributors\", icon=\"URL\").url = \"https://github.com/carson-katri/dream-textures/graphs/contributors\"\n\n        if context.preferences.view.show_developer_ui: # If 'Developer Extras' is enabled, show addon development tools\n            developer_box = layout.box()\n            developer_box.label(text=\"Development Tools\", icon=\"CONSOLE\")\n            warn_box = developer_box.box()\n            warn_box.label(text=\"WARNING\", icon=\"ERROR\")\n            warn_box.label(text=\"This section is for addon development only.\")\n            warn_box.label(text=\"Do not use any operators in this section unless you are setting up a development environment.\")\n            if has_dependencies:\n                warn_box = developer_box.box()\n                warn_box.label(text=\"Dependencies already installed. Only install below if you developing the addon\", icon=\"CHECKMARK\")\n            developer_box.prop(context.scene, 'dream_textures_requirements_path')\n            developer_box.operator_context = 'INVOKE_DEFAULT'\n            developer_box.operator(InstallDependencies.bl_idname, icon=\"CONSOLE\")\n"
  },
  {
    "path": "prompt_engineering.py",
    "content": "from collections import namedtuple\n\nPromptToken = namedtuple('PromptToken', ['id', 'label', 'values'])\nPromptStructure = namedtuple('PromptStructure', ['id', 'label', 'structure', 'generate'])\n\nframing_token = PromptToken('framing', 'Framing', (\n    ('ecu', 'Extreme Close-up'),\n    ('cu', 'Close-up'),\n    ('mcu', 'Medium Close Up'),\n    ('ms', 'Medium Shot'),\n    ('ls', 'Long Shot'),\n    ('els', 'Extra Long Shot'),\n))\n\nposition_token = PromptToken('position', 'Position', (\n    ('overhead', 'Overhead View'),\n    ('aerial', 'Aerial View'),\n    ('low', 'Low Angle'),\n    ('dutch', 'Dutch Angle'),\n    ('ots', 'Over-the-shoulder shot'),\n))\n\nfilm_type_token = PromptToken('film_type', 'Film Type', (\n    ('bw', 'Black & White'),\n    ('fc', 'Full Color'),\n    ('cine', 'Cinematic'),\n    ('polaroid', 'Polaroid'),\n    ('anaglyph', 'Anaglyph'),\n    ('double', 'Double Exposure'),\n))\n\ncamera_settings_token = PromptToken('camera_settings', 'Camera Settings', (\n    ('high_speed', 'Fast Shutter Speed'),\n    ('long_exposure', 'Long Exposure'),\n    ('bokeh', 'Shallow Depth of Field'),\n    ('deep_dof', 'Deep Depth of Field'),\n    ('tilt_shift', 'Tilt Shift'),\n    ('motion_blur', 'Motion Blur'),\n    ('telephoto', 'Telephoto Lens'),\n    ('macro', 'Macro Lens'),\n    ('wide_angle', 'Wide Angle Lens'),\n    ('fish_eye', 'Fish-Eye Lens'),\n))\n\nshooting_context_token = PromptToken('shooting_context', 'Shooting Context', (\n    ('film_still', 'Film Still'),\n    ('photograph', 'Photograph'),\n    ('studio_portrait', 'Studio Portrait Photograph'),\n    ('outdoor', 'Outdoor Photograph'),\n    ('cctv', 'Surveillance Footage'),\n))\n\nsubject_token = PromptToken('subject', 'Subject', ())\n\nlighting_token = PromptToken('lighting', 'Lighting', (\n    ('golden_hour', 'Golden Hour'),\n    ('blur_hour', 'Blue Hour'),\n    ('midday', 'Midday'),\n    ('overcast', 'Overcast'),\n    ('silhouette', 'Mostly Silhouetted'),\n    \n    ('warm', 'Warm Lighting, 2700K'),\n    ('cold', 'Flourescent Lighting, 4800K'),\n    ('flash', 'Harsh Flash'),\n    ('ambient', 'Ambient Lighting'),\n    ('dramatic', 'Dramatic Lighting'),\n    ('backlit', 'Backlit'),\n    ('studio', 'Studio Lighting'),\n    ('above', 'Lit from Above'),\n    ('below', 'Lit from Below'),\n    ('left', 'Lit from the Left'),\n    ('right', 'Lit from the Right'),\n))\n\ndef texture_prompt(tokens):\n    return f\"{tokens.subject} texture\"\ntexture_structure = PromptStructure(\n    'texture',\n    'Texture',\n    [subject_token],\n    texture_prompt\n)\n\ndef photography_prompt(tokens):\n    return f\"A {tokens.framing} {tokens.position} {tokens.film_type} {tokens.camera_settings} {tokens.shooting_context} of {tokens.subject}, {tokens.lighting}\"\n\nphotography_structure = PromptStructure(\n    'photography',\n    'Photography',\n    (subject_token, framing_token, position_token, film_type_token, camera_settings_token, shooting_context_token, lighting_token),\n    photography_prompt\n)\n\nsubject_type_token = PromptToken('subject_type', 'Subject Type', (\n    ('environment', 'Environment'),\n    ('character', 'Character'),\n    ('weapon', 'Weapon'),\n    ('vehicle', 'Vehicle'),\n))\n\ngenre_token = PromptToken('genre', 'Genre', (\n    ('scifi', 'Sci-Fi'),\n    ('fantasy', 'Fantasy'),\n    ('cyberpunk', 'Cyberpunk'),\n    ('cinematic', 'Cinematic'),\n))\n\ndef concept_art_prompt(tokens):\n    return f\"{tokens.subject}, {tokens.subject_type} concept art, {tokens.genre} digital painting, trending on ArtStation\"\n\nconcept_art_structure = PromptStructure(\n    'concept_art',\n    'Concept Art',\n    (subject_token, subject_type_token, genre_token),\n    concept_art_prompt\n)\n\ndef custom_prompt(tokens):\n    return f\"{tokens.subject}\"\ncustom_structure = PromptStructure(\n    'custom',\n    'Custom',\n    [subject_token],\n    custom_prompt\n)\n\ndef file_batch_prompt(tokens):\n    return f\"\"\nfile_batch_structure = PromptStructure(\n    'file_batch',\n    \"File Batch\",\n    [],\n    file_batch_prompt\n)\n\nprompt_structures = [\n    custom_structure,\n    texture_structure,\n    photography_structure,\n    concept_art_structure,\n    file_batch_structure\n]\n\ndef map_structure(x):\n    return (x.id, x.label, '')\nprompt_structures_items = list(map(map_structure, prompt_structures))"
  },
  {
    "path": "property_groups/control_net.py",
    "content": "import bpy\nfrom bpy.props import FloatProperty, EnumProperty, PointerProperty, IntProperty, BoolProperty\n\nfrom .. import api, image_utils\nfrom ..diffusers_backend import DiffusersBackend\nfrom ..generator_process import Generator\nfrom ..generator_process.models.optimizations import Optimizations\n\ndef control_net_options(self, context):\n    return [\n        None if model is None else (model.id, model.name, model.description)\n        for model in context.scene.dream_textures_prompt.get_backend().list_controlnet_models(context)\n    ]\n\nPROCESSOR_IDS = [\n    (\"none\", \"None\", \"No pre-processing\"),\n    None,\n    (\"depth_leres\", \"Depth (LeRes)\", \"\"),\n    (\"depth_leres++\", \"Depth (LeRes++)\", \"\"),\n    (\"depth_midas\", \"Depth (MiDaS)\", \"\"),\n    (\"depth_zoe\", \"Depth (Zoe)\", \"\"),\n    None,\n    (\"canny\", \"Canny\", \"Canny edge detection\"),\n    (\"mlsd\", \"M-LSD\", \"\"),\n    (\"softedge_hed\", \"Soft Edge (HED)\", \"\"),\n    (\"softedge_hedsafe\", \"Soft Edge (HED-Safe)\", \"\"),\n    (\"softedge_pidinet\", \"Soft Edge (PidiNet)\", \"\"),\n    (\"softedge_pidsafe\", \"Soft Edge (Pidsafe)\", \"\"),\n    None,\n    (\"lineart_anime\", \"Lineart (Anime)\", \"\"),\n    (\"lineart_coarse\", \"Lineart (Coarse)\", \"\"),\n    (\"lineart_realistic\", \"Lineart (Realistic)\", \"\"),\n    None,\n    (\"normal_bae\", \"Normal (BAE)\", \"\"),\n    None,\n    (\"openpose\", \"OpenPose\", \"\"),\n    (\"openpose_face\", \"OpenPose (Face)\", \"\"),\n    (\"openpose_faceonly\", \"OpenPose (Face Only)\", \"\"),\n    (\"openpose_full\", \"OpenPose (Full)\", \"\"),\n    (\"openpose_hand\", \"OpenPose (Hand)\", \"\"),\n    # (\"dwpose\", \"DWPose\", \"\"), # requires additional dependencies\n    # (\"mediapipe_face\", \"MediaPipe Face\", \"\"), # requires additional dependencies\n    None,\n    (\"scribble_hed\", \"Scribble (HED)\", \"\"),\n    (\"scribble_pidinet\", \"Scribble (PidiNet)\", \"\"),\n    None,\n    (\"shuffle\", \"Shuffle\", \"\"),\n]\n\nclass ControlNet(bpy.types.PropertyGroup):\n    control_net: EnumProperty(name=\"ControlNet\", items=control_net_options, description=\"Specify which ControlNet to use\")\n    conditioning_scale: FloatProperty(name=\"Conditioning Scale\", default=1.0, description=\"Increases the strength of the ControlNet's effect\")\n    control_image: PointerProperty(type=bpy.types.Image)\n    processor_id: EnumProperty(\n        name=\"Processor\",\n        items=PROCESSOR_IDS,\n        description=\"Pre-process the control image\"\n    )\n    enabled: BoolProperty(name=\"Enabled\", default=True)\n\nclass ControlNetsAddMenu(bpy.types.Menu):\n    bl_idname = \"DREAM_MT_control_nets_add\"\n    bl_label = \"Add ControlNet\"\n\n    def draw(self, context):\n        layout = self.layout\n\n        for model in control_net_options(self, context):\n            if model is None:\n                layout.separator()\n            else:\n                layout.operator(\"dream_textures.control_nets_add\", text=model[1]).control_net = model[0]\n\nclass ControlNetsAdd(bpy.types.Operator):\n    bl_idname = \"dream_textures.control_nets_add\"\n    bl_label = \"Add ControlNet\"\n\n    control_net: EnumProperty(name=\"ControlNet\", items=control_net_options)\n\n    def execute(self, context):\n        net = context.scene.dream_textures_prompt.control_nets.add()\n        net.control_net = self.control_net\n        return {'FINISHED'}\nclass ControlNetsRemove(bpy.types.Operator):\n    bl_idname = \"dream_textures.control_nets_remove\"\n    bl_label = \"Remove ControlNet\"\n\n    index: IntProperty(name=\"Index\")\n\n    def execute(self, context):\n        context.scene.dream_textures_prompt.control_nets.remove(self.index)\n        return {'FINISHED'}\n\nclass BakeControlNetImage(bpy.types.Operator):\n    bl_idname = \"dream_textures.control_net_bake\"\n    bl_label = \"Bake Control Image\"\n    bl_description = \"Runs the selected processor, and bakes the result to an image datablock\"\n\n    index: IntProperty(name=\"Index\")\n\n    def execute(self, context):\n        prompt = context.scene.dream_textures_prompt\n\n        net = prompt.control_nets[self.index]\n\n        gen = Generator.shared()\n        backend: api.Backend = prompt.get_backend()\n        optimizations = backend.optimizations() if isinstance(backend, DiffusersBackend) else Optimizations()\n\n        future = gen.controlnet_aux(\n            processor_id=net.processor_id,\n            image=image_utils.bpy_to_np(net.control_image, color_space=None),\n            optimizations=optimizations\n        )\n        \n        control_image = image_utils.np_to_bpy(\n            future.result(last_only=True),\n            f\"{net.control_image.name} ({next(processor[1] for processor in PROCESSOR_IDS if processor != None and processor[0] == net.processor_id)})\",\n        )\n        \n        net.control_image = control_image\n        net.processor_id = \"none\"\n\n        for area in context.screen.areas:\n            if area.type == 'IMAGE_EDITOR' and not area.spaces.active.use_image_pin:\n                area.spaces.active.image = control_image\n\n        return {'FINISHED'}"
  },
  {
    "path": "property_groups/dream_prompt.py",
    "content": "import bpy\nfrom bpy.props import FloatProperty, IntProperty, EnumProperty, BoolProperty, StringProperty, IntVectorProperty, CollectionProperty\nimport os\nimport sys\nfrom typing import _AnnotatedAlias\n\nfrom ..generator_process.actions.detect_seamless import SeamlessAxes\nfrom ..generator_process.actions.prompt_to_image import Optimizations, Scheduler, StepPreviewMode\nfrom ..prompt_engineering import *\nfrom ..preferences import StableDiffusionPreferences\nfrom .control_net import ControlNet\n\nimport numpy as np\n\nfrom functools import reduce\n\nfrom .. import api\nfrom ..image_utils import bpy_to_np, grayscale\n\ndef scheduler_options(self, context):\n    return [\n        (scheduler, scheduler, '')\n        for scheduler in self.get_backend().list_schedulers(context)\n    ]\n\nstep_preview_mode_options = [(mode.value, mode.value, '') for mode in StepPreviewMode]\n\nprecision_options = [\n    ('auto', 'Automatic', \"\", 1),\n    ('float32', 'Full Precision (float32)', \"\", 2),\n    ('autocast', 'Autocast', \"\", 3),\n    ('float16', 'Half Precision (float16)', \"\", 4),\n]\n\ninit_image_sources = [\n    ('file', 'File', '', 'IMAGE_DATA', 1),\n    ('open_editor', 'Open Image', '', 'TPAINT_HLT', 2),\n]\n\ninit_image_actions = [\n    ('modify', 'Modify', 'Combine the initial image with noise to influence the output', 'IMAGE', 1),\n    ('inpaint', 'Inpaint', 'Fill in any masked areas', 'TPAINT_HLT', 2),\n    ('outpaint', 'Outpaint', 'Extend the image in a specific direction', 'FULLSCREEN_ENTER', 3),\n]\n\ndef init_image_actions_filtered(self, context):\n    available = ['modify', 'inpaint', 'outpaint']\n    return list(filter(lambda x: x[0] in available, init_image_actions))\n\ninpaint_mask_sources = [\n    ('alpha', 'Alpha Channel', '', 1),\n    ('prompt', 'Prompt', '', 2),\n]\n\ndef inpaint_mask_sources_filtered(self, context):\n    available = ['alpha', 'prompt']\n    return list(filter(lambda x: x[0] in available, inpaint_mask_sources))\n\nseamless_axes = [\n    SeamlessAxes.AUTO.bpy_enum('Detect from source image when modifying or inpainting, off otherwise', -1),\n    SeamlessAxes.OFF.bpy_enum('', 0),\n    SeamlessAxes.HORIZONTAL.bpy_enum('', 1),\n    SeamlessAxes.VERTICAL.bpy_enum('', 2),\n    SeamlessAxes.BOTH.bpy_enum('', 3),\n]\n\ndef modify_action_source_type(self, context):\n    return [\n        ('color', 'Color', 'Use the color information from the image', 1),\n        None,\n        ('depth_generated', 'Color and Generated Depth', 'Use MiDaS to infer the depth of the initial image and include it in the conditioning. Can give results that more closely match the composition of the source image', 2),\n        ('depth_map', 'Color and Depth Map', 'Specify a secondary image to use as the depth map. Can give results that closely match the composition of the depth map', 3),\n        ('depth', 'Depth', 'Treat the initial image as a depth map, and ignore any color. Matches the composition of the source image without any color influence', 4),\n    ]\n\ndef model_options(self, context):\n    return [\n        None if model is None else (model.id, model.name, model.description)\n        for model in self.get_backend().list_models(context)\n    ]\n\ndef _model_update(self, context):\n    options = [m for m in model_options(self, context) if m is not None]\n    if self.model == '' and len(options) > 0:\n        self.model = options[0]\n\ndef backend_options(self, context):\n    return [\n        (backend._id(), backend.name if hasattr(backend, \"name\") else backend.__name__, backend.description if hasattr(backend, \"description\") else \"\")\n        for backend in api.Backend._list_backends()\n    ]\n\ndef seed_clamp(self, ctx):\n    # clamp seed right after input to make it clear what the limits are\n    try:\n        s = str(max(0,min(int(float(self.seed)),2**32-1))) # float() first to make sure any seed that is a number gets clamped, not just ints\n        if s != self.seed:\n            self.seed = s\n    except (ValueError, OverflowError):\n        pass # will get hashed once generated\n\nattributes = {\n    \"backend\": EnumProperty(name=\"Backend\", items=backend_options, description=\"Specify which generation backend to use\"),\n    \"model\": EnumProperty(name=\"Model\", items=model_options, description=\"Specify which model to use for inference\", update=_model_update),\n    \n    \"control_nets\": CollectionProperty(type=ControlNet),\n    \"active_control_net\": IntProperty(name=\"Active ControlNet\"),\n\n    # Prompt\n    \"prompt_structure\": EnumProperty(name=\"Preset\", items=prompt_structures_items, description=\"Fill in a few simple options to create interesting images quickly\"),\n    \"use_negative_prompt\": BoolProperty(name=\"Use Negative Prompt\", default=False),\n    \"negative_prompt\": StringProperty(name=\"Negative Prompt\", description=\"The model will avoid aspects of the negative prompt\"),\n\n    # Size\n    \"use_size\": BoolProperty(name=\"Manual Size\", default=False),\n    \"width\": IntProperty(name=\"Width\", default=512, min=64, step=64),\n    \"height\": IntProperty(name=\"Height\", default=512, min=64, step=64),\n\n    # Simple Options\n    \"seamless_axes\": EnumProperty(name=\"Seamless Axes\", items=seamless_axes, default=SeamlessAxes.AUTO.id, description=\"Specify which axes should be seamless/tilable\"),\n\n    # Advanced\n    \"show_advanced\": BoolProperty(name=\"\", default=False),\n    \"random_seed\": BoolProperty(name=\"Random Seed\", default=True, description=\"Randomly pick a seed\"),\n    \"seed\": StringProperty(name=\"Seed\", default=\"0\", description=\"Manually pick a seed\", update=seed_clamp),\n    \"iterations\": IntProperty(name=\"Iterations\", default=1, min=1, description=\"How many images to generate\"),\n    \"steps\": IntProperty(name=\"Steps\", default=25, min=1),\n    \"cfg_scale\": FloatProperty(name=\"CFG Scale\", default=7.5, min=0, description=\"How strongly the prompt influences the image\"),\n    \"scheduler\": EnumProperty(name=\"Scheduler\", items=scheduler_options, default=3), # defaults to \"DPM Solver Multistep\"\n    \"step_preview_mode\": EnumProperty(name=\"Step Preview\", description=\"Displays intermediate steps in the Image Viewer. Disabling can speed up generation\", items=step_preview_mode_options, default=1),\n\n    # Init Image\n    \"use_init_img\": BoolProperty(name=\"Use Init Image\", default=False),\n    \"init_img_src\": EnumProperty(name=\" \", items=init_image_sources, default=\"file\"),\n    \"init_img_action\": EnumProperty(name=\"Action\", items=init_image_actions_filtered, default=1),\n    \"strength\": FloatProperty(name=\"Noise Strength\", description=\"The ratio of noise:image. A higher value gives more 'creative' results\", default=0.75, min=0, max=1, soft_min=0.01, soft_max=0.99),\n    \"fit\": BoolProperty(name=\"Fit to width/height\", description=\"Resize the source image to match the specified size\", default=True),\n    \"use_init_img_color\": BoolProperty(name=\"Color Correct\", default=True),\n    \"modify_action_source_type\": EnumProperty(name=\"Image Type\", items=modify_action_source_type, default=1, description=\"What kind of data is the source image\"),\n    \n    # Inpaint\n    \"inpaint_mask_src\": EnumProperty(name=\"Mask Source\", items=inpaint_mask_sources_filtered, default=1),\n    \"inpaint_replace\": FloatProperty(name=\"Replace\", description=\"Replaces the masked area with a specified amount of noise, can create more extreme changes. Values of 0 or 1 will give the best results\", min=0, max=1, default=0),\n    \"text_mask\": StringProperty(name=\"Mask Prompt\"),\n    \"text_mask_confidence\": FloatProperty(name=\"Confidence Threshold\", description=\"How confident the segmentation model needs to be\", default=0.5, min=0),\n\n    # Outpaint\n    \"outpaint_origin\": IntVectorProperty(name=\"Origin\", default=(0, 448), size=2, description=\"The position of the outpaint area relative to the top left corner of the image. A value of (0, 512) will outpaint from the bottom of a 512x512 image\"),\n\n    # Resulting image\n    \"hash\": StringProperty(name=\"Image Hash\"),\n}\n\ndef map_structure_token_items(value):\n    return (value[0], value[1], '')\nfor structure in prompt_structures:\n    for token in structure.structure:\n        if not isinstance(token, str):\n            attributes['prompt_structure_token_' + token.id] = StringProperty(name=token.label)\n            attributes['prompt_structure_token_' + token.id + '_enum'] = EnumProperty(\n                name=token.label,\n                items=[('custom', 'Custom', '')] + list(map(map_structure_token_items, token.values)),\n                default='custom' if len(token.values) == 0 else token.values[0][0],\n            )\n\nDreamPrompt = type('DreamPrompt', (bpy.types.PropertyGroup,), {\n    \"bl_label\": \"DreamPrompt\",\n    \"bl_idname\": \"dream_textures.DreamPrompt\",\n    \"__annotations__\": attributes,\n})\n\ndef generate_prompt(self):\n    structure = next(x for x in prompt_structures if x.id == self.prompt_structure)\n    class dotdict(dict):\n        \"\"\"dot.notation access to dictionary attributes\"\"\"\n        __getattr__ = dict.get\n        __setattr__ = dict.__setitem__\n        __delattr__ = dict.__delitem__\n    tokens = {}\n    for segment in structure.structure:\n        enum_value = getattr(self, 'prompt_structure_token_' + segment.id + '_enum')\n        if enum_value == 'custom':\n            tokens[segment.id] = getattr(self, 'prompt_structure_token_' + segment.id)\n        else:\n            tokens[segment.id] = next(x for x in segment.values if x[0] == enum_value)[1]\n    return structure.generate(dotdict(tokens))\n\ndef get_prompt_subject(self):\n    structure = next(x for x in prompt_structures if x.id == self.prompt_structure)\n    for segment in structure.structure:\n        if segment.id == 'subject':\n            return getattr(self, 'prompt_structure_token_' + segment.id)\n    return self.generate_prompt()\n\ndef get_seed(self):\n    import numpy\n    numpy.random.randn()\n    if self.random_seed:\n        return None # let stable diffusion automatically pick one\n    try:\n        return max(0,min(int(float(self.seed)),2**32-1)) # clamp int\n    except (ValueError, OverflowError):\n        h = hash(self.seed) # not an int, let's hash it!\n        if h < 0:\n            h = ~h\n        return (h & 0xFFFFFFFF) ^ (h >> 32) # 64 bit hash down to 32 bits\n\ndef generate_args(self, context, iteration=0, init_image=None, control_images=None) -> api.GenerationArguments:\n    is_file_batch = self.prompt_structure == file_batch_structure.id\n    file_batch_lines = []\n    file_batch_lines_negative = []\n    if is_file_batch:\n        file_batch_lines = [line.body for line in context.scene.dream_textures_prompt_file.lines if len(line.body.strip()) > 0]\n        file_batch_lines_negative = [\"\"] * len(file_batch_lines)\n    \n    backend: api.Backend = self.get_backend()\n    batch_size = backend.get_batch_size(context)\n    iteration_limit = len(file_batch_lines) if is_file_batch else self.iterations\n    batch_size = min(batch_size, iteration_limit-iteration)\n\n    task: api.Task = api.PromptToImage()\n    if self.use_init_img:\n        match self.init_img_action:\n            case 'modify':\n                match self.modify_action_source_type:\n                    case 'color':\n                        task = api.ImageToImage(\n                            image=init_image,\n                            strength=self.strength,\n                            fit=self.fit\n                        )\n                    case 'depth_generated':\n                        task = api.DepthToImage(\n                            depth=None,\n                            image=init_image,\n                            strength=self.strength\n                        )\n                    case 'depth_map':\n                        task = api.DepthToImage(\n                            depth=None if init_image is None else grayscale(bpy_to_np(context.scene.init_depth, color_space=None)),\n                            image=init_image,\n                            strength=self.strength\n                        )\n                    case 'depth':\n                        task = api.DepthToImage(\n                            image=None,\n                            depth=None if init_image is None else grayscale(init_image),\n                            strength=self.strength\n                        )\n            case 'inpaint':\n                task = api.Inpaint(\n                    image=init_image,\n                    strength=self.strength,\n                    fit=self.fit,\n                    mask_source=api.Inpaint.MaskSource.ALPHA if self.inpaint_mask_src == 'alpha' else api.Inpaint.MaskSource.PROMPT,\n                    mask_prompt=self.text_mask,\n                    confidence=self.text_mask_confidence\n                )\n            case 'outpaint':\n                task = api.Outpaint(\n                    image=init_image,\n                    origin=(self.outpaint_origin[0], self.outpaint_origin[1])\n                )\n\n    return api.GenerationArguments(\n        task=task,\n        model=next((model for model in self.get_backend().list_models(context) if model is not None and model.id == self.model), None),\n        prompt=api.Prompt(\n            file_batch_lines[iteration:iteration+batch_size] if is_file_batch else [self.generate_prompt()] * batch_size,\n            file_batch_lines_negative[iteration:iteration+batch_size] if is_file_batch else ([self.negative_prompt] * batch_size if self.use_negative_prompt else None)\n        ),\n        size=(self.width, self.height) if self.use_size else None,\n        seed=self.get_seed(),\n        steps=self.steps,\n        guidance_scale=self.cfg_scale,\n        scheduler=self.scheduler,\n        seamless_axes=SeamlessAxes(self.seamless_axes),\n        step_preview_mode=StepPreviewMode(self.step_preview_mode),\n        iterations=self.iterations,\n        control_nets=[\n            api.models.control_net.ControlNet(\n                net.control_net,\n                control_images[i] if control_images is not None else None,\n                net.conditioning_scale\n            )\n            for i, net in enumerate(self.control_nets)\n            if net.enabled\n        ]\n    )\n\ndef get_backend(self) -> api.Backend:\n    return getattr(self, api.Backend._lookup(self.backend)._attribute())\n\nDreamPrompt.generate_prompt = generate_prompt\nDreamPrompt.get_prompt_subject = get_prompt_subject\nDreamPrompt.get_seed = get_seed\nDreamPrompt.generate_args = generate_args\nDreamPrompt.get_backend = get_backend"
  },
  {
    "path": "property_groups/seamless_result.py",
    "content": "import bpy\n\nimport numpy as np\nfrom ..generator_process.actions.detect_seamless import SeamlessAxes\nfrom ..generator_process import Generator\nfrom ..preferences import StableDiffusionPreferences\nfrom ..api.models import GenerationArguments\nfrom .. import image_utils\n\ndef update(self, context):\n    if hasattr(context.area, \"regions\"):\n        for region in context.area.regions:\n            if region.type == \"UI\":\n                region.tag_redraw()\n\n\nclass SeamlessResult(bpy.types.PropertyGroup):\n    bl_label = \"SeamlessResult\"\n    bl_idname = \"dream_textures.SeamlessResult\"\n\n    image: bpy.props.PointerProperty(type=bpy.types.Image)\n    result: bpy.props.StringProperty(name=\"Auto-detected\", update=update, default=SeamlessAxes.OFF.text)\n\n    def check(self, image):\n        if image == self.image or not Generator.shared().can_use():\n            return\n\n        if image is not None and (hash_string := image.get('dream_textures_hash', None)) is not None:\n            res = None\n            def hash_init():\n                self.image = image\n                self.result = res\n            for args in bpy.context.scene.dream_textures_history:\n                if args.get('hash', None) == hash_string and args.seamless_axes != SeamlessAxes.AUTO:\n                    res = SeamlessAxes(args.seamless_axes).text\n                    bpy.app.timers.register(hash_init)\n                    return\n\n        can_process = image is not None and image.size[0] >= 8 and image.size[1] >= 8\n\n        def init():\n            self.image = image\n            self.result = 'Processing' if can_process else SeamlessAxes.OFF.text\n        bpy.app.timers.register(init)\n\n        if not can_process:\n            return\n        pixels = image_utils.bpy_to_np(image)\n\n        def result(future):\n            self.result = future.result().text\n        Generator.shared().detect_seamless(pixels).add_done_callback(result)\n\n    def update_args(self, args, as_id=False):\n        if isinstance(args, GenerationArguments):\n            if args.seamless_axes == SeamlessAxes.AUTO and self.result != 'Processing':\n                if as_id:\n                    args.seamless_axes = SeamlessAxes(self.result).id\n                else:\n                    args.seamless_axes = SeamlessAxes(self.result)\n        else:\n            if args['seamless_axes'] == SeamlessAxes.AUTO and self.result != 'Processing':\n                if as_id:\n                    args['seamless_axes'] = SeamlessAxes(self.result).id\n                else:\n                    args['seamless_axes'] = SeamlessAxes(self.result)\n"
  },
  {
    "path": "realtime_viewport.py",
    "content": "# Realtime Viewport is still under development, and is not currently used.\nimport bpy\nimport cycles\nimport time\nimport threading\nimport gpu\nfrom gpu_extras.batch import batch_for_shader\nimport numpy as np\nfrom multiprocessing.shared_memory import SharedMemory\nfrom .operators.dream_texture import dream_texture\n\nview_update_original = cycles.CyclesRender.view_update\nview_draw_original = cycles.CyclesRender.view_draw\n\ndef debounce(wait_time):\n    \"\"\"\n    Decorator that will debounce a function so that it is called after wait_time seconds\n    If it is called multiple times, will wait for the last call to be debounced and run only this one.\n    \"\"\"\n\n    def decorator(function):\n        def debounced(*args, **kwargs):\n            def call_function():\n                debounced._timer = None\n                return function(*args, **kwargs)\n            # if we already have a call to the function currently waiting to be executed, reset the timer\n            if debounced._timer is not None:\n                debounced._timer.cancel()\n\n            # after wait_time, call the function provided to the decorator with its arguments\n            debounced._timer = threading.Timer(wait_time, call_function)\n            debounced._timer.start()\n\n        debounced._timer = None\n        return debounced\n\n    return decorator\n\ndef DREAMTEXTURES_HT_viewport_enabled(self, context):\n    self.layout.prop(context.scene, \"dream_textures_viewport_enabled\", text=\"\", icon=\"OUTLINER_OB_VOLUME\" if context.scene.dream_textures_viewport_enabled else \"VOLUME_DATA\", toggle=True)\n\nis_rendering_viewport = False\nlast_viewport_update = time.time()\nlast_viewport_pixel_buffer_update = time.time()\ndream_viewport = None\nis_rendering_dream = False\nrender_dream_flag = False\nviewport_pixel_buffer = None\nviewport_size = (0, 0)\nignore_next = 0\ndef create_image():\n    print(\"Create image\")\n    global dream_viewport\n    dream_viewport = bpy.data.images.new('Dream Viewport', width=32, height=32)\n\ndef register_realtime_viewport():\n    bpy.app.timers.register(create_image)\n\n    def view_update_decorator(original):\n        def view_update(self, context, depsgraph):\n            result = original(self, context, depsgraph)\n            global last_viewport_update\n            global ignore_next\n            if ignore_next <= 0:\n                last_viewport_update = time.time()\n                print(\"View Update\")\n            ignore_next -= 1\n            return result\n        return view_update\n    cycles.CyclesRender.view_update = view_update_decorator(cycles.CyclesRender.view_update)\n    \n    def updates_stopped():\n        global last_viewport_update\n        global is_rendering_viewport\n        global is_rendering_dream\n        threshold_reached = (time.time() - last_viewport_update) < 0.5\n        if threshold_reached != is_rendering_viewport:\n            is_rendering_viewport = threshold_reached\n            global viewport_pixel_buffer\n            if not is_rendering_viewport and not is_rendering_dream and viewport_pixel_buffer is not None:\n                print(\"Stopped rendering viewport\")\n                is_rendering_dream = True\n                array = np.flipud((np.array(viewport_pixel_buffer) * 255).astype(np.int8))\n                pixels_memory = SharedMemory(create=True, size=array.nbytes)\n                pixels_memory_array = np.ndarray(array.shape, dtype=array.dtype, buffer=pixels_memory.buf)\n                pixels_memory_array[:] = array[:]\n\n                def image_callback(shared_memory_name, seed, width, height, upscaled=False):\n                    if not upscaled:\n                        shared_memory = SharedMemory(shared_memory_name)\n                        pixels = np.frombuffer(shared_memory.buf, dtype=np.float32).copy()\n\n                        global ignore_next\n                        ignore_next = 5\n                        global dream_viewport\n                        dream_viewport.scale(width, height)\n                        dream_viewport.pixels[:] = pixels\n\n                        shared_memory.close()\n                        pixels_memory.close()\n\n                        print(\"Done\")\n                        global is_rendering_dream\n                        is_rendering_dream = False\n                        # for area in bpy.context.screen.areas:\n                        #     if area.type == 'VIEW_3D':\n                                # area.tag_redraw()\n                \n                def step_callback(step, width=None, height=None, shared_memory_name=None):\n                    pass\n\n                dream_texture(bpy.context.scene.dream_textures_render_properties_prompt, step_callback, image_callback, init_img_shared_memory=pixels_memory.name, init_img_shared_memory_width=viewport_size[0], init_img_shared_memory_height=viewport_size[1])\n        return 0.5\n    bpy.app.timers.register(updates_stopped)\n\n    def draw():\n        global last_viewport_pixel_buffer_update\n        if not bpy.context.scene.dream_textures_viewport_enabled:\n            return\n        if (time.time() - last_viewport_pixel_buffer_update) < 0.5:\n            return\n        last_viewport_pixel_buffer_update = time.time()\n        # get currently bound framebuffer\n        framebuffer = gpu.state.active_framebuffer_get()\n\n        # get information on current viewport \n        viewport_info = gpu.state.viewport_get()\n        width = viewport_info[2]\n        height = viewport_info[3]\n        \n        global viewport_pixel_buffer\n        global viewport_size\n        viewport_pixel_buffer = framebuffer.read_color(0, 0, width, height, 4, 0, 'FLOAT').to_list()\n        viewport_size = (width, height)\n\n    bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'PRE_VIEW')\n    def draw_dream():\n        global is_rendering_dream\n        global is_rendering_viewport\n        global dream_viewport\n        if not bpy.context.scene.dream_textures_viewport_enabled or is_rendering_viewport:\n            return\n        texture = gpu.texture.from_image(dream_viewport)\n        viewport_info = gpu.state.viewport_get()\n        width = viewport_info[2]\n        height = viewport_info[3]\n        shader = gpu.shader.from_builtin(\"2D_IMAGE\")\n        shader.bind()\n        shader.uniform_sampler(\"image\", texture)\n        batch = batch_for_shader(shader, 'TRI_FAN', {\n            'pos': ((0, 0), (width, 0), (width, height), (0, height)),\n            'texCoord': ((0, 0), (1, 0), (1, 1), (0, 1)),\n        })\n        batch.draw(shader)\n    bpy.types.SpaceView3D.draw_handler_add(draw_dream, (), 'WINDOW', 'POST_PIXEL')\n\n    bpy.types.VIEW3D_HT_header.append(DREAMTEXTURES_HT_viewport_enabled)\n\ndef unregister_realtime_viewport():\n    global view_update_original\n    cycles.CyclesRender.view_update = view_update_original\n    global view_draw_original\n    cycles.CyclesRender.view_draw = view_draw_original\n    \n    bpy.types.VIEW3D_HT_header.remove(DREAMTEXTURES_HT_viewport_enabled)"
  },
  {
    "path": "render_pass.py",
    "content": "import bpy\nimport cycles\nimport numpy as np\nimport os\nfrom typing import List\nimport threading\nfrom .generator_process import Generator\nfrom . import api\nfrom . import image_utils\n\npass_inputs = [\n    ('color', 'Color', 'Provide the scene color as input'),\n    ('depth', 'Depth', 'Provide the Z pass as depth input'),\n    ('color_depth', 'Color and Depth', 'Provide the scene color and depth as input'),\n]\n\nupdate_render_passes_original = cycles.CyclesRender.update_render_passes\nrender_original = cycles.CyclesRender.render\n# del_original = cycles.CyclesRender.__del__\n\ndef register_render_pass():\n    def update_render_passes_decorator(original):\n        def update_render_passes(self, scene=None, renderlayer=None):\n            result = original(self, scene, renderlayer)\n            self.register_pass(scene, renderlayer, \"Dream Textures\", 4, \"RGBA\", 'COLOR')\n            return result\n        return update_render_passes\n    cycles.CyclesRender.update_render_passes = update_render_passes_decorator(cycles.CyclesRender.update_render_passes)\n    def render_decorator(original):\n        def render(self, depsgraph):\n            scene = depsgraph.scene if hasattr(depsgraph, \"scene\") else depsgraph\n            if not scene.dream_textures_render_properties_enabled:\n                return original(self, depsgraph)\n            result = original(self, depsgraph)\n            try:\n                original_result = self.get_result()\n                self.add_pass(\"Dream Textures\", 4, \"RGBA\")\n                scale = scene.render.resolution_percentage / 100.0\n                size_x = int(scene.render.resolution_x * scale)\n                size_y = int(scene.render.resolution_y * scale)\n                if size_x % 64 != 0 or size_y % 64 != 0:\n                    self.report({\"ERROR\"}, f\"Image dimensions must be multiples of 64 (e.x. 512x512, 512x768, ...) closest is {round(size_x/64)*64}x{round(size_y/64)*64}\")\n                    return result\n                render_result = self.begin_result(0, 0, size_x, size_y)\n                for layer in render_result.layers:\n                    for render_pass in layer.passes:\n                        if render_pass.name == \"Dream Textures\":\n                            try:\n                                self._render_dream_textures_pass(layer, (size_x, size_y), scene, render_pass, render_result)\n                            except Exception as e:\n                                self.error_set(str(e))\n                        else:\n                            source_pass = None\n                            for original_layer in original_result.layers:\n                                if layer.name == original_layer.name:\n                                    for original_pass in original_layer.passes:\n                                        if original_pass.name == render_pass.name:\n                                            source_pass = original_pass\n                            pixels = image_utils.render_pass_to_np(source_pass, size=(size_x, size_y))\n                            image_utils.np_to_render_pass(pixels, render_pass)\n                self.end_result(render_result)\n            except Exception as e:\n                print(e)\n            return result\n        return render\n    cycles.CyclesRender.render = render_decorator(cycles.CyclesRender.render)\n    cycles.CyclesRender._render_dream_textures_pass = _render_dream_textures_pass\n\n    # def del_decorator(original):\n    #     def del_patch(self):\n    #         result = original(self)\n    #         kill_generator()\n    #         return result\n    #     return del_patch\n    # cycles.CyclesRender.__del__ = del_decorator(cycles.CyclesRender.__del__)\n\ndef unregister_render_pass():\n    global update_render_passes_original\n    cycles.CyclesRender.update_render_passes = update_render_passes_original\n    global render_original\n    cycles.CyclesRender.render = render_original\n    del cycles.CyclesRender._render_dream_textures_pass\n    # global del_original\n    # cycles.CyclesRender.__del__ = del_original\n\ndef _render_dream_textures_pass(self, layer, size, scene, render_pass, render_result):\n    def combined():\n        self.update_stats(\"Dream Textures\", \"Applying color management transforms\")\n        return image_utils.render_pass_to_np(layer.passes[\"Combined\"], size, color_management=True, color_space=\"sRGB\")\n\n    def depth():\n        d = image_utils.render_pass_to_np(layer.passes[\"Depth\"], size).squeeze(2)\n        return (1 - np.interp(d, [0, np.ma.masked_equal(d, d.max(), copy=False).max()], [0, 1]))\n\n    self.update_stats(\"Dream Textures\", \"Starting\")\n    \n    prompt = scene.dream_textures_render_properties_prompt\n    match scene.dream_textures_render_properties_pass_inputs:\n        case 'color':\n            task = api.ImageToImage(\n                combined(),\n                prompt.strength,\n                True\n            )\n        case 'depth':\n            task = api.DepthToImage(\n                depth(),\n                None,\n                prompt.strength\n            )\n        case 'color_depth':\n            task = api.DepthToImage(\n                depth(),\n                combined(),\n                prompt.strength\n            )\n    event = threading.Event()\n    dream_pixels = None\n    def step_callback(progress: List[api.GenerationResult]) -> bool:\n        self.update_progress(progress[-1].progress / progress[-1].total)\n        image_utils.np_to_render_pass(progress[-1].image, render_pass)\n        self.update_result(render_result) # This does not seem to have an effect.\n        return True\n    def callback(results: List[api.GenerationResult] | Exception):\n        nonlocal dream_pixels\n        dream_pixels = results[-1].image\n        event.set()\n    \n    backend: api.Backend = prompt.get_backend()\n    generated_args: api.GenerationArguments = prompt.generate_args(bpy.context)\n    generated_args.task = task\n    generated_args.size = size\n    self.update_stats(\"Dream Textures\", \"Generating...\")\n    backend.generate(\n        generated_args,\n        step_callback=step_callback,\n        callback=callback\n    )\n\n    event.wait()\n\n    # Perform an inverse transform so when Blender applies its transform everything looks correct.\n    self.update_stats(\"Dream Textures\", \"Applying inverse color management transforms\")\n    image_utils.np_to_render_pass(dream_pixels, render_pass, inverse_color_management=True, color_space=\"sRGB\")\n\n    self.update_stats(\"Dream Textures\", \"Finished\")"
  },
  {
    "path": "requirements/linux-rocm.txt",
    "content": "diffusers==0.27.2\ninvisible-watermark\ntransformers\naccelerate\nhuggingface_hub\ncontrolnet-aux==0.0.7\n\n--extra-index-url https://download.pytorch.org/whl/rocm6.1/\ntorch==2.3.1\n\n# Original SD checkpoint conversion\npytorch-lightning\ntensorboard\nomegaconf\n\nscipy # LMSDiscreteScheduler\n\nopencolorio==2.3.2 # color management\nmatplotlib\n"
  },
  {
    "path": "requirements/mac-mps-cpu.txt",
    "content": "diffusers==0.27.2\ninvisible-watermark\ntransformers\naccelerate\nhuggingface_hub>=0.19.3\ncontrolnet-aux==0.0.7\n\ntorch==2.3.1\n\n# Original SD checkpoint conversion\npytorch-lightning\ntensorboard\nomegaconf\n\nscipy # LMSDiscreteScheduler\n\nopencolorio==2.3.2 # color management\nmatplotlib"
  },
  {
    "path": "requirements/win-dml.txt",
    "content": "diffusers==0.27.2\ninvisible-watermark\ntransformers\naccelerate\nhuggingface_hub\ncontrolnet-aux==0.0.7\n\ntorch-directml\ntorch==2.3.1\n\n# Original SD checkpoint conversion\npytorch-lightning\ntensorboard\nomegaconf\n\nscipy # LMSDiscreteScheduler\n\nopencolorio==2.3.2 # color management\nmatplotlib"
  },
  {
    "path": "requirements/win-linux-cuda.txt",
    "content": "diffusers==0.27.2\ninvisible-watermark\ntransformers\naccelerate\nhuggingface_hub\ncontrolnet-aux==0.0.7\n\n--extra-index-url https://download.pytorch.org/whl/cu118\ntorch==2.3.1\n\n# Original SD checkpoint conversion\npytorch-lightning\ntensorboard\nomegaconf\n\nscipy # LMSDiscreteScheduler\n\nopencolorio==2.3.2 # color management\n\nmatplotlib # OpenPose"
  },
  {
    "path": "scripts/train_detect_seamless.py",
    "content": "\"\"\"\nIt's recommended to copy this script to its own project folder to\nkeep it with your own image samples and trained models.\n\nEach dataset should have images of the same square dimensions for batched training and validation.\nYou can train with multiple datasets in the same session.\n\ndataset_layout/\n    imagesNone/\n        [sample_images]\n    imagesX/\n        [sample_images]\n    imagesY/\n        [sample_images]\n    imagesXY/\n        [sample_images]\n\"\"\"\n\n# if torch, numpy, and cv2 are not installed to site-packages\n# import site\n# site.addsitedir(r\"path/to/dream_textures/.python_dependencies\")\n\nimport itertools\nimport os\nfrom datetime import datetime\n\nimport cv2\nimport numpy as np\nimport torch\nfrom numpy._typing import NDArray\nfrom torch import nn\nfrom torch.utils.data import Dataset, DataLoader\n\nEDGE_SLICE = 8\nif torch.cuda.is_available():\n    DEVICE = 'cuda'\nelif torch.backends.mps.is_available():\n    DEVICE = 'mps'\nelse:\n    DEVICE = 'cpu'\n\n\nclass SeamlessModel(nn.Module):\n    def __init__(self):\n        super(SeamlessModel, self).__init__()\n        self.conv = nn.Sequential(\n            nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),\n            nn.Dropout(.2),\n            nn.PReLU(64),\n            nn.Conv2d(64, 16, kernel_size=3, stride=1, padding=1),\n            nn.Dropout(.2),\n            nn.PReLU(16),\n            nn.Conv2d(16, 64, kernel_size=8, stride=4, padding=0),\n            nn.Dropout(.2),\n            nn.PReLU(64),\n            nn.Conv2d(64, 64, kernel_size=(1, 3), stride=1, padding=0),\n            nn.Dropout(.2),\n        )\n        self.gru = nn.GRU(64, 32, batch_first=True)\n        self.fc = nn.Linear(32, 1)\n\n    def forward(self, x: torch.Tensor):\n        if len(x.size()) == 3:\n            x = x.unsqueeze(0)\n        # x[batch, channels, height, EDGE_SLICE*2]\n        x = self.conv(x)\n        # x[batch, features, height/4, 1]\n        h = torch.zeros(self.gru.num_layers, x.size()[0], self.gru.hidden_size,\n                        dtype=x.dtype, device=x.device)\n        x = x.squeeze(3).transpose(2, 1)\n        # x[batch, height/4, features]\n        x, h = self.gru(x, h)\n        return torch.tanh(self.fc(x[:, -1]))\n\n\ndef image_edges(path):\n    image: NDArray = cv2.imread(path)\n    # Pretty sure loading images is a bottleneck and makes the first epoch incredibly slow until fully in RAM.\n    # Might be worth caching the edges in an easier to deserialize format with np.savez()\n\n    edge_x = np.zeros((image.shape[0], EDGE_SLICE * 2, 3), dtype=np.float32)\n    edge_x[:, :EDGE_SLICE] = image[:, -EDGE_SLICE:]\n    edge_x[:, EDGE_SLICE:] = image[:, :EDGE_SLICE]\n\n    edge_y = np.zeros((EDGE_SLICE * 2, image.shape[1], 3), dtype=np.float32)\n    edge_y[:EDGE_SLICE] = image[-EDGE_SLICE:]\n    edge_y[EDGE_SLICE:] = image[:EDGE_SLICE]\n\n    return edge_x, edge_y\n\n\ndef prepare_edge(edge: NDArray, axis: str) -> torch.Tensor:\n    edge = (edge * 2 / 255 - 1)\n    if axis == 'x':\n        edge = edge.transpose(2, 0, 1)\n    elif axis == 'y':\n        edge = edge.transpose(2, 1, 0)\n    else:\n        raise ValueError('axis should be \"x\" or \"y\"')\n    return torch.as_tensor(edge, dtype=torch.float32)\n\n\ndef prepare_edges(edge_x: NDArray, edge_y: NDArray) -> tuple[torch.Tensor, torch.Tensor]:\n    edge_x = edge_x * 2 / 255 - 1\n    edge_y = edge_y * 2 / 255 - 1\n    edge_x = edge_x.transpose(2, 0, 1)\n    edge_y = edge_y.transpose(2, 1, 0)\n    return torch.as_tensor(edge_x, dtype=torch.float32), torch.as_tensor(edge_y, dtype=torch.float32)\n\n\ndef seamless_tensor(seamless):\n    return torch.tensor([1 if seamless else -1], dtype=torch.float32)\n\n\nclass EdgeDataset(Dataset):\n    def __init__(self, path):\n        self.data = []\n        self._load_dir(os.path.join(path, 'imagesNone'), (False, False))\n        self._load_dir(os.path.join(path, 'imagesX'), (True, False))\n        self._load_dir(os.path.join(path, 'imagesY'), (False, True))\n        self._load_dir(os.path.join(path, 'imagesXY'), (True, True))\n        print(f'dataset loaded {path} contains {len(self)}')\n\n    def _load_dir(self, imdir, seamless):\n        if not os.path.exists(imdir):\n            print(f'skipping {imdir}, does not exist')\n            return\n        if not os.path.isdir(imdir):\n            print(f'skipping {imdir}, not a directory')\n            return\n        print(f'loading {imdir}')\n\n        for image in sorted(os.listdir(imdir)):\n            path = os.path.join(imdir, image)\n            if not os.path.isfile(path):\n                continue\n            self.data.append((seamless_tensor(seamless[0]), None, 'x', path))\n            self.data.append((seamless_tensor(seamless[1]), None, 'y', path))\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx) -> tuple[torch.Tensor, torch.Tensor, str, str]:\n        ret = self.data[idx]\n        if ret[1] is not None:\n            return ret\n        path = ret[3]\n        edge_x, edge_y = prepare_edges(*image_edges(path))\n        # Edges will be cached in cpu when first requested. Might not be desirable with a large enough dataset.\n        if idx % 2 == 0:\n            ret = (ret[0], edge_x, 'x', path)\n            self.data[idx] = ret\n            self.data[idx + 1] = (self.data[idx + 1][0], edge_y, 'y', path)\n        else:\n            self.data[idx - 1] = (self.data[idx - 1][0], edge_x, 'x', path)\n            ret = (ret[0], edge_y, 'y', path)\n            self.data[idx] = ret\n        return ret\n\n\nCHANNEL_PERMUTATIONS = [*itertools.permutations((0, 1, 2))]\n\n\nclass PermutedEdgeDataset(Dataset):\n    \"\"\"Permutes the channels to better generalize color data.\"\"\"\n\n    def __init__(self, dataset: EdgeDataset | str):\n        if isinstance(dataset, str):\n            dataset = EdgeDataset(dataset)\n        self.base_dataset = dataset\n\n    def __len__(self):\n        return len(self.base_dataset) * len(CHANNEL_PERMUTATIONS)\n\n    def __getitem__(self, idx):\n        perm = CHANNEL_PERMUTATIONS[idx % len(CHANNEL_PERMUTATIONS)]\n        result, edge, edge_type, path = self.base_dataset[idx // len(CHANNEL_PERMUTATIONS)]\n        edge_perm = torch.zeros(edge.size(), dtype=edge.dtype)\n        edge_perm[0] = edge[perm[0]]\n        edge_perm[1] = edge[perm[1]]\n        edge_perm[2] = edge[perm[2]]\n        return result, edge_perm, edge_type, path, perm\n\n\ndef mix_iter(*iterables):\n    \"\"\"Iterates through multiple objects while attempting to balance\n    by yielding from which one has the highest of remaining/length\"\"\"\n    iterables = [x for x in iterables if len(x) > 0]\n    lengths = [len(x) for x in iterables]\n    counts = lengths.copy()\n    ratios = [1.] * len(iterables)\n    iters = [x.__iter__() for x in iterables]\n    while True:\n        idx = -1\n        max_ratio = 0\n        for i, ratio in enumerate(ratios):\n            if ratio > max_ratio:\n                idx = i\n                max_ratio = ratio\n        if idx == -1:\n            return\n        c = counts[idx] - 1\n        counts[idx] = c\n        ratios[idx] = c / lengths[idx]\n        yield next(iters[idx])\n\n\ndef train(model: nn.Module, train_datasets, valid_datasets, epochs=1000, training_rate=0.0001, batch=50):\n    train_loaders = [DataLoader(PermutedEdgeDataset(ds), batch_size=batch, shuffle=True, num_workers=0, pin_memory=True)\n                     for ds in train_datasets]\n    valid_loaders = [DataLoader(ds, batch_size=batch, num_workers=0, pin_memory=True)\n                     for ds in valid_datasets]\n\n    criterion = nn.MSELoss()\n    criterion.to(DEVICE)\n    optimizer = torch.optim.SGD(model.parameters(), training_rate, .9)\n\n    def train_one_epoch():\n        running_loss = 0.\n        print_rate = 5000\n        print_after = print_rate\n        for i, data in enumerate(mix_iter(*train_loaders)):\n            seamless = data[0].to(DEVICE)\n            edge = data[1].to(DEVICE)\n\n            optimizer.zero_grad()\n\n            output = model(edge)\n\n            loss: torch.Tensor = criterion(output, seamless)\n            loss.backward()\n\n            torch.nn.utils.clip_grad_norm_(model.parameters(), 5)\n\n            optimizer.step()\n\n            running_loss += loss.item()\n\n            if i * batch > print_after:\n                print_after += print_rate\n                print(f\"LOSS train {running_loss / (i + 1)}\")\n\n        return running_loss / (i + 1)\n\n    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')\n\n    best_vloss = 1_000_000.\n\n    for epoch in range(epochs):\n        print(f'EPOCH {epoch}:')\n\n        model.train(True)\n        avg_loss = train_one_epoch()\n\n        model.train(False)\n\n        running_vloss = 0.0\n        with torch.no_grad():\n            for i, vdata in enumerate(mix_iter(valid_loaders)):\n                expected_results = vdata[0].to(DEVICE)\n                inputs = vdata[1].to(DEVICE)\n                outputs = model(inputs)\n                vloss = criterion(outputs, expected_results)\n                running_vloss += vloss\n\n        avg_vloss = running_vloss / (i + 1)\n        print(f'LOSS train {avg_loss} valid {avg_vloss}')\n\n        # Track best performance, and save the model's state\n        if avg_vloss < best_vloss:\n            best_vloss = avg_vloss\n            model_path = f'model/model_{timestamp}_{epoch}_{int(avg_vloss * 1000)}.pt'\n            torch.save(model.state_dict(), model_path)\n\n\n@torch.no_grad()\ndef validate(model, datasets):\n    # datasets here do not need images of equal sizes or to be square as there is no batching\n    passes = 0\n    fails = 0\n    print_limit = 100\n    print_count = 0\n\n    def do_print(result, path, axis):\n        nonlocal print_count\n        if print_count < print_limit:\n            print(f'{path} {axis} {result}')\n        print_count += 1\n\n    for valid_dataset in datasets:\n        for data in valid_dataset:\n            expected_result = data[0]\n            tensor = data[1]\n            axis = data[2]\n            path = data[3]\n            result = model(tensor.to(DEVICE))[0].item()\n            if expected_result.item() == 1:\n                if result >= 0:\n                    passes += 1\n                else:\n                    fails += 1\n                    do_print(result, path, axis)\n            elif expected_result.item() == -1:\n                if result < 0:\n                    passes += 1\n                else:\n                    fails += 1\n                    do_print(result, path, axis)\n            else:\n                raise RuntimeError(f'Unexpected result target {expected_result.item()}')\n    if print_count > print_limit:\n        print(f\"{print_count - print_limit} more\")\n    total = passes + fails\n    print(f\"Passed: {passes} | {passes / total * 100:.2f}%\")  # edge accuracy\n    print(f\"Failed: {fails} | {fails / total * 100:.2f}%\")\n    print(f\"Passed²: {(passes / total) ** 2 * 100:.2f}%\")  # image accuracy\n\n\n# I prefer to not perpetuate the public distribution of torch.save() pickled files.\ndef save_npz(path, state_dict):\n    np.savez(path, **state_dict)\n\n\ndef load_npz(path):\n    state_dict_np: dict[str, NDArray] = np.load(path, allow_pickle=False)\n    state_dict_torch = dict()\n    for name, arr in state_dict_np.items():\n        state_dict_torch[name] = torch.from_numpy(arr)\n    return state_dict_torch\n\n\ndef main():\n    model = SeamlessModel()\n\n    # resume training or validate a saved model\n    # model.load_state_dict(load_npz(\"model.npz\"))\n    # model.load_state_dict(torch.load(\"model/model_20221203_162623_26_10.pt\"))\n\n    model.to(DEVICE)\n\n    datasets = [\n        (EdgeDataset('train/samples'), EdgeDataset('valid/samples')),\n        (EdgeDataset('train/samples2x'), EdgeDataset('valid/samples2x')),\n        (EdgeDataset('train/samples4x'), EdgeDataset('valid/samples4x'))\n    ]\n\n    # Though it's possible to keep training and validation samples in the same dataset, you really shouldn't.\n    # If you add new samples to a dataset that's being used like this DO NOT resume a previously trained model.\n    # Training and validation samples will get reshuffled and your validation samples will likely be overfit.\n    # gen = torch.Generator().manual_seed(132)\n    # datasets = [\n    #     torch.utils.data.random_split(EdgeDataset('samples'), [.8, .2], gen),\n    #     torch.utils.data.random_split(EdgeDataset('samples2x'), [.8, .2], gen),\n    #     torch.utils.data.random_split(EdgeDataset('samples4x'), [.8, .2], gen)\n    # ]\n\n    # If you're generating new samples it can be useful to modify generator_process/actions/prompt_to_image.py\n    # to automatically save images to the dataset. It's best to keep them separate at first and run a previously\n    # trained model on them to help find bad samples. Stable diffusion can at times add solid colored borders to\n    # the edges of images that are not meant to be seamless. I recommend deleting all samples where an edge\n    # appears seamless with scrutiny but was not generated to be, don't move it to another folder in the dataset.\n    # datasets = [\n    #     (None, EdgeDataset('tmp'))\n    # ]\n\n    # If you only want to validate a saved model.\n    # datasets = [(None, valid) for _, valid in datasets]\n\n    train_datasets = []\n    valid_datasets = []\n    for t, v in datasets:\n        if t is not None:\n            train_datasets.append(t)\n        if v is not None:\n            valid_datasets.append(v)\n\n    try:\n        if len(train_datasets) > 0:\n            train(model, train_datasets, valid_datasets, epochs=50, training_rate=0.001)\n            # It should easily converge in under 50 epochs.\n            # Training rate is a little high, but I've never managed better\n            # results with a lower rate and several times more epochs.\n    except KeyboardInterrupt:\n        pass\n\n    # As long as your images have meaningful names you can get feedback on\n    # what kind of images aren't detecting well to add similar samples.\n    model.train(False)\n    validate(model, valid_datasets)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/zip_dependencies.py",
    "content": "import shutil\nimport zipfile\nfrom pathlib import Path\n\n\ndef main():\n    root = Path(__file__).parent.parent\n    deps = root / '.python_dependencies'\n    deps_to_zip = [deps / 'transformers']\n\n    for dep in deps_to_zip:\n        if not dep.exists():\n            raise FileNotFoundError(dep)\n        elif not dep.is_dir():\n            raise EnvironmentError(f\"not a directory {dep}\")\n\n    zip_deps_path = root / '.python_dependencies.zip'\n    zip_deps_path.unlink(True)\n    with zipfile.PyZipFile(str(zip_deps_path), mode='x') as zip_deps:\n        for dep in deps_to_zip:\n            zip_deps.writepy(str(dep))\n            shutil.rmtree(str(dep))\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "sd_configs/cldm_v15.yaml",
    "content": "model:\n  target: cldm.cldm.ControlLDM\n  params:\n    linear_start: 0.00085\n    linear_end: 0.0120\n    num_timesteps_cond: 1\n    log_every_t: 200\n    timesteps: 1000\n    first_stage_key: \"jpg\"\n    cond_stage_key: \"txt\"\n    control_key: \"hint\"\n    image_size: 64\n    channels: 4\n    cond_stage_trainable: false\n    conditioning_key: crossattn\n    monitor: val/loss_simple_ema\n    scale_factor: 0.18215\n    use_ema: False\n    only_mid_control: False\n\n    control_stage_config:\n      target: cldm.cldm.ControlNet\n      params:\n        image_size: 32 # unused\n        in_channels: 4\n        hint_channels: 3\n        model_channels: 320\n        attention_resolutions: [ 4, 2, 1 ]\n        num_res_blocks: 2\n        channel_mult: [ 1, 2, 4, 4 ]\n        num_heads: 8\n        use_spatial_transformer: True\n        transformer_depth: 1\n        context_dim: 768\n        use_checkpoint: True\n        legacy: False\n\n    unet_config:\n      target: cldm.cldm.ControlledUnetModel\n      params:\n        image_size: 32 # unused\n        in_channels: 4\n        out_channels: 4\n        model_channels: 320\n        attention_resolutions: [ 4, 2, 1 ]\n        num_res_blocks: 2\n        channel_mult: [ 1, 2, 4, 4 ]\n        num_heads: 8\n        use_spatial_transformer: True\n        transformer_depth: 1\n        context_dim: 768\n        use_checkpoint: True\n        legacy: False\n\n    first_stage_config:\n      target: ldm.models.autoencoder.AutoencoderKL\n      params:\n        embed_dim: 4\n        monitor: val/rec_loss\n        ddconfig:\n          double_z: true\n          z_channels: 4\n          resolution: 256\n          in_channels: 3\n          out_ch: 3\n          ch: 128\n          ch_mult:\n          - 1\n          - 2\n          - 4\n          - 4\n          num_res_blocks: 2\n          attn_resolutions: []\n          dropout: 0.0\n        lossconfig:\n          target: torch.nn.Identity\n\n    cond_stage_config:\n      target: ldm.modules.encoders.modules.FrozenCLIPEmbedder\n"
  },
  {
    "path": "sd_configs/cldm_v21.yaml",
    "content": "model:\n  target: cldm.cldm.ControlLDM\n  params:\n    linear_start: 0.00085\n    linear_end: 0.0120\n    num_timesteps_cond: 1\n    log_every_t: 200\n    timesteps: 1000\n    first_stage_key: \"jpg\"\n    cond_stage_key: \"txt\"\n    control_key: \"hint\"\n    image_size: 64\n    channels: 4\n    cond_stage_trainable: false\n    conditioning_key: crossattn\n    monitor: val/loss_simple_ema\n    scale_factor: 0.18215\n    use_ema: False\n    only_mid_control: False\n\n    control_stage_config:\n      target: cldm.cldm.ControlNet\n      params:\n        use_checkpoint: True\n        image_size: 32 # unused\n        in_channels: 4\n        hint_channels: 3\n        model_channels: 320\n        attention_resolutions: [ 4, 2, 1 ]\n        num_res_blocks: 2\n        channel_mult: [ 1, 2, 4, 4 ]\n        num_head_channels: 64 # need to fix for flash-attn\n        use_spatial_transformer: True\n        use_linear_in_transformer: True\n        transformer_depth: 1\n        context_dim: 1024\n        legacy: False\n\n    unet_config:\n      target: cldm.cldm.ControlledUnetModel\n      params:\n        use_checkpoint: True\n        image_size: 32 # unused\n        in_channels: 4\n        out_channels: 4\n        model_channels: 320\n        attention_resolutions: [ 4, 2, 1 ]\n        num_res_blocks: 2\n        channel_mult: [ 1, 2, 4, 4 ]\n        num_head_channels: 64 # need to fix for flash-attn\n        use_spatial_transformer: True\n        use_linear_in_transformer: True\n        transformer_depth: 1\n        context_dim: 1024\n        legacy: False\n\n    first_stage_config:\n      target: ldm.models.autoencoder.AutoencoderKL\n      params:\n        embed_dim: 4\n        monitor: val/rec_loss\n        ddconfig:\n          #attn_type: \"vanilla-xformers\"\n          double_z: true\n          z_channels: 4\n          resolution: 256\n          in_channels: 3\n          out_ch: 3\n          ch: 128\n          ch_mult:\n          - 1\n          - 2\n          - 4\n          - 4\n          num_res_blocks: 2\n          attn_resolutions: []\n          dropout: 0.0\n        lossconfig:\n          target: torch.nn.Identity\n\n    cond_stage_config:\n      target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder\n      params:\n        freeze: True\n        layer: \"penultimate\"\n"
  },
  {
    "path": "sd_configs/sd_xl_base.yaml",
    "content": "model:\n  target: sgm.models.diffusion.DiffusionEngine\n  params:\n    scale_factor: 0.13025\n    disable_first_stage_autocast: True\n\n    denoiser_config:\n      target: sgm.modules.diffusionmodules.denoiser.DiscreteDenoiser\n      params:\n        num_idx: 1000\n\n        weighting_config:\n          target: sgm.modules.diffusionmodules.denoiser_weighting.EpsWeighting\n        scaling_config:\n          target: sgm.modules.diffusionmodules.denoiser_scaling.EpsScaling\n        discretization_config:\n          target: sgm.modules.diffusionmodules.discretizer.LegacyDDPMDiscretization\n\n    network_config:\n      target: sgm.modules.diffusionmodules.openaimodel.UNetModel\n      params:\n        adm_in_channels: 2816\n        num_classes: sequential\n        use_checkpoint: True\n        in_channels: 4\n        out_channels: 4\n        model_channels: 320\n        attention_resolutions: [4, 2]\n        num_res_blocks: 2\n        channel_mult: [1, 2, 4]\n        num_head_channels: 64\n        use_spatial_transformer: True\n        use_linear_in_transformer: True\n        transformer_depth: [1, 2, 10]  # note: the first is unused (due to attn_res starting at 2) 32, 16, 8 --> 64, 32, 16\n        context_dim: 2048\n        spatial_transformer_attn_type: softmax-xformers\n        legacy: False\n\n    conditioner_config:\n      target: sgm.modules.GeneralConditioner\n      params:\n        emb_models:\n          # crossattn cond\n          - is_trainable: False\n            input_key: txt\n            target: sgm.modules.encoders.modules.FrozenCLIPEmbedder\n            params:\n              layer: hidden\n              layer_idx: 11\n          # crossattn and vector cond\n          - is_trainable: False\n            input_key: txt\n            target: sgm.modules.encoders.modules.FrozenOpenCLIPEmbedder2\n            params:\n              arch: ViT-bigG-14\n              version: laion2b_s39b_b160k\n              freeze: True\n              layer: penultimate\n              always_return_pooled: True\n              legacy: False\n          # vector cond\n          - is_trainable: False\n            input_key: original_size_as_tuple\n            target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND\n            params:\n              outdim: 256  # multiplied by two\n          # vector cond\n          - is_trainable: False\n            input_key: crop_coords_top_left\n            target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND\n            params:\n              outdim: 256  # multiplied by two\n          # vector cond\n          - is_trainable: False\n            input_key: target_size_as_tuple\n            target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND\n            params:\n              outdim: 256  # multiplied by two\n\n    first_stage_config:\n      target: sgm.models.autoencoder.AutoencoderKLInferenceWrapper\n      params:\n        embed_dim: 4\n        monitor: val/rec_loss\n        ddconfig:\n          attn_type: vanilla-xformers\n          double_z: true\n          z_channels: 4\n          resolution: 256\n          in_channels: 3\n          out_ch: 3\n          ch: 128\n          ch_mult: [1, 2, 4, 4]\n          num_res_blocks: 2\n          attn_resolutions: []\n          dropout: 0.0\n        lossconfig:\n          target: torch.nn.Identity\n"
  },
  {
    "path": "sd_configs/sd_xl_refiner.yaml",
    "content": "model:\n  target: sgm.models.diffusion.DiffusionEngine\n  params:\n    scale_factor: 0.13025\n    disable_first_stage_autocast: True\n\n    denoiser_config:\n      target: sgm.modules.diffusionmodules.denoiser.DiscreteDenoiser\n      params:\n        num_idx: 1000\n\n        weighting_config:\n          target: sgm.modules.diffusionmodules.denoiser_weighting.EpsWeighting\n        scaling_config:\n          target: sgm.modules.diffusionmodules.denoiser_scaling.EpsScaling\n        discretization_config:\n          target: sgm.modules.diffusionmodules.discretizer.LegacyDDPMDiscretization\n\n    network_config:\n      target: sgm.modules.diffusionmodules.openaimodel.UNetModel\n      params:\n        adm_in_channels: 2560\n        num_classes: sequential\n        use_checkpoint: True\n        in_channels: 4\n        out_channels: 4\n        model_channels: 384\n        attention_resolutions: [4, 2]\n        num_res_blocks: 2\n        channel_mult: [1, 2, 4, 4]\n        num_head_channels: 64\n        use_spatial_transformer: True\n        use_linear_in_transformer: True\n        transformer_depth: 4\n        context_dim: [1280, 1280, 1280, 1280]  # 1280\n        spatial_transformer_attn_type: softmax-xformers\n        legacy: False\n\n    conditioner_config:\n      target: sgm.modules.GeneralConditioner\n      params:\n        emb_models:\n          # crossattn and vector cond\n          - is_trainable: False\n            input_key: txt\n            target: sgm.modules.encoders.modules.FrozenOpenCLIPEmbedder2\n            params:\n              arch: ViT-bigG-14\n              version: laion2b_s39b_b160k\n              legacy: False\n              freeze: True\n              layer: penultimate\n              always_return_pooled: True\n          # vector cond\n          - is_trainable: False\n            input_key: original_size_as_tuple\n            target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND\n            params:\n              outdim: 256  # multiplied by two\n          # vector cond\n          - is_trainable: False\n            input_key: crop_coords_top_left\n            target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND\n            params:\n              outdim: 256  # multiplied by two\n          # vector cond\n          - is_trainable: False\n            input_key: aesthetic_score\n            target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND\n            params:\n              outdim: 256  # multiplied by one\n\n    first_stage_config:\n      target: sgm.models.autoencoder.AutoencoderKLInferenceWrapper\n      params:\n        embed_dim: 4\n        monitor: val/rec_loss\n        ddconfig:\n          attn_type: vanilla-xformers\n          double_z: true\n          z_channels: 4\n          resolution: 256\n          in_channels: 3\n          out_ch: 3\n          ch: 128\n          ch_mult: [1, 2, 4, 4]\n          num_res_blocks: 2\n          attn_resolutions: []\n          dropout: 0.0\n        lossconfig:\n          target: torch.nn.Identity\n"
  },
  {
    "path": "sd_configs/v1-inference.yaml",
    "content": "model:\n  base_learning_rate: 1.0e-04\n  target: ldm.models.diffusion.ddpm.LatentDiffusion\n  params:\n    linear_start: 0.00085\n    linear_end: 0.0120\n    num_timesteps_cond: 1\n    log_every_t: 200\n    timesteps: 1000\n    first_stage_key: \"jpg\"\n    cond_stage_key: \"txt\"\n    image_size: 64\n    channels: 4\n    cond_stage_trainable: false   # Note: different from the one we trained before\n    conditioning_key: crossattn\n    monitor: val/loss_simple_ema\n    scale_factor: 0.18215\n    use_ema: False\n\n    scheduler_config: # 10000 warmup steps\n      target: ldm.lr_scheduler.LambdaLinearScheduler\n      params:\n        warm_up_steps: [ 10000 ]\n        cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases\n        f_start: [ 1.e-6 ]\n        f_max: [ 1. ]\n        f_min: [ 1. ]\n\n    unet_config:\n      target: ldm.modules.diffusionmodules.openaimodel.UNetModel\n      params:\n        image_size: 32 # unused\n        in_channels: 4\n        out_channels: 4\n        model_channels: 320\n        attention_resolutions: [ 4, 2, 1 ]\n        num_res_blocks: 2\n        channel_mult: [ 1, 2, 4, 4 ]\n        num_heads: 8\n        use_spatial_transformer: True\n        transformer_depth: 1\n        context_dim: 768\n        use_checkpoint: True\n        legacy: False\n\n    first_stage_config:\n      target: ldm.models.autoencoder.AutoencoderKL\n      params:\n        embed_dim: 4\n        monitor: val/rec_loss\n        ddconfig:\n          double_z: true\n          z_channels: 4\n          resolution: 256\n          in_channels: 3\n          out_ch: 3\n          ch: 128\n          ch_mult:\n          - 1\n          - 2\n          - 4\n          - 4\n          num_res_blocks: 2\n          attn_resolutions: []\n          dropout: 0.0\n        lossconfig:\n          target: torch.nn.Identity\n\n    cond_stage_config:\n      target: ldm.modules.encoders.modules.FrozenCLIPEmbedder\n"
  },
  {
    "path": "sd_configs/v2-inference-v.yaml",
    "content": "model:\n  base_learning_rate: 1.0e-4\n  target: ldm.models.diffusion.ddpm.LatentDiffusion\n  params:\n    parameterization: \"v\"\n    linear_start: 0.00085\n    linear_end: 0.0120\n    num_timesteps_cond: 1\n    log_every_t: 200\n    timesteps: 1000\n    first_stage_key: \"jpg\"\n    cond_stage_key: \"txt\"\n    image_size: 64\n    channels: 4\n    cond_stage_trainable: false\n    conditioning_key: crossattn\n    monitor: val/loss_simple_ema\n    scale_factor: 0.18215\n    use_ema: False # we set this to false because this is an inference only config\n\n    unet_config:\n      target: ldm.modules.diffusionmodules.openaimodel.UNetModel\n      params:\n        use_checkpoint: True\n        use_fp16: True\n        image_size: 32 # unused\n        in_channels: 4\n        out_channels: 4\n        model_channels: 320\n        attention_resolutions: [ 4, 2, 1 ]\n        num_res_blocks: 2\n        channel_mult: [ 1, 2, 4, 4 ]\n        num_head_channels: 64 # need to fix for flash-attn\n        use_spatial_transformer: True\n        use_linear_in_transformer: True\n        transformer_depth: 1\n        context_dim: 1024\n        legacy: False\n\n    first_stage_config:\n      target: ldm.models.autoencoder.AutoencoderKL\n      params:\n        embed_dim: 4\n        monitor: val/rec_loss\n        ddconfig:\n          #attn_type: \"vanilla-xformers\"\n          double_z: true\n          z_channels: 4\n          resolution: 256\n          in_channels: 3\n          out_ch: 3\n          ch: 128\n          ch_mult:\n          - 1\n          - 2\n          - 4\n          - 4\n          num_res_blocks: 2\n          attn_resolutions: []\n          dropout: 0.0\n        lossconfig:\n          target: torch.nn.Identity\n\n    cond_stage_config:\n      target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder\n      params:\n        freeze: True\n        layer: \"penultimate\"\n"
  },
  {
    "path": "sd_configs/v2-inference.yaml",
    "content": "model:\n  base_learning_rate: 1.0e-4\n  target: ldm.models.diffusion.ddpm.LatentDiffusion\n  params:\n    linear_start: 0.00085\n    linear_end: 0.0120\n    num_timesteps_cond: 1\n    log_every_t: 200\n    timesteps: 1000\n    first_stage_key: \"jpg\"\n    cond_stage_key: \"txt\"\n    image_size: 64\n    channels: 4\n    cond_stage_trainable: false\n    conditioning_key: crossattn\n    monitor: val/loss_simple_ema\n    scale_factor: 0.18215\n    use_ema: False # we set this to false because this is an inference only config\n\n    unet_config:\n      target: ldm.modules.diffusionmodules.openaimodel.UNetModel\n      params:\n        use_checkpoint: True\n        use_fp16: True\n        image_size: 32 # unused\n        in_channels: 4\n        out_channels: 4\n        model_channels: 320\n        attention_resolutions: [ 4, 2, 1 ]\n        num_res_blocks: 2\n        channel_mult: [ 1, 2, 4, 4 ]\n        num_head_channels: 64 # need to fix for flash-attn\n        use_spatial_transformer: True\n        use_linear_in_transformer: True\n        transformer_depth: 1\n        context_dim: 1024\n        legacy: False\n\n    first_stage_config:\n      target: ldm.models.autoencoder.AutoencoderKL\n      params:\n        embed_dim: 4\n        monitor: val/rec_loss\n        ddconfig:\n          #attn_type: \"vanilla-xformers\"\n          double_z: true\n          z_channels: 4\n          resolution: 256\n          in_channels: 3\n          out_ch: 3\n          ch: 128\n          ch_mult:\n          - 1\n          - 2\n          - 4\n          - 4\n          num_res_blocks: 2\n          attn_resolutions: []\n          dropout: 0.0\n        lossconfig:\n          target: torch.nn.Identity\n\n    cond_stage_config:\n      target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder\n      params:\n        freeze: True\n        layer: \"penultimate\"\n"
  },
  {
    "path": "sd_configs/v2-inpainting-inference.yaml",
    "content": "model:\n  base_learning_rate: 5.0e-05\n  target: ldm.models.diffusion.ddpm.LatentInpaintDiffusion\n  params:\n    linear_start: 0.00085\n    linear_end: 0.0120\n    num_timesteps_cond: 1\n    log_every_t: 200\n    timesteps: 1000\n    first_stage_key: \"jpg\"\n    cond_stage_key: \"txt\"\n    image_size: 64\n    channels: 4\n    cond_stage_trainable: false\n    conditioning_key: hybrid\n    scale_factor: 0.18215\n    monitor: val/loss_simple_ema\n    finetune_keys: null\n    use_ema: False\n\n    unet_config:\n      target: ldm.modules.diffusionmodules.openaimodel.UNetModel\n      params:\n        use_checkpoint: True\n        image_size: 32 # unused\n        in_channels: 9\n        out_channels: 4\n        model_channels: 320\n        attention_resolutions: [ 4, 2, 1 ]\n        num_res_blocks: 2\n        channel_mult: [ 1, 2, 4, 4 ]\n        num_head_channels: 64 # need to fix for flash-attn\n        use_spatial_transformer: True\n        use_linear_in_transformer: True\n        transformer_depth: 1\n        context_dim: 1024\n        legacy: False\n\n    first_stage_config:\n      target: ldm.models.autoencoder.AutoencoderKL\n      params:\n        embed_dim: 4\n        monitor: val/rec_loss\n        ddconfig:\n          #attn_type: \"vanilla-xformers\"\n          double_z: true\n          z_channels: 4\n          resolution: 256\n          in_channels: 3\n          out_ch: 3\n          ch: 128\n          ch_mult:\n            - 1\n            - 2\n            - 4\n            - 4\n          num_res_blocks: 2\n          attn_resolutions: [ ]\n          dropout: 0.0\n        lossconfig:\n          target: torch.nn.Identity\n\n    cond_stage_config:\n      target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder\n      params:\n        freeze: True\n        layer: \"penultimate\"\n\n\ndata:\n  target: ldm.data.laion.WebDataModuleFromConfig\n  params:\n    tar_base: null  # for concat as in LAION-A\n    p_unsafe_threshold: 0.1\n    filter_word_list: \"data/filters.yaml\"\n    max_pwatermark: 0.45\n    batch_size: 8\n    num_workers: 6\n    multinode: True\n    min_size: 512\n    train:\n      shards:\n        - \"pipe:aws s3 cp s3://stability-aws/laion-a-native/part-0/{00000..18699}.tar -\"\n        - \"pipe:aws s3 cp s3://stability-aws/laion-a-native/part-1/{00000..18699}.tar -\"\n        - \"pipe:aws s3 cp s3://stability-aws/laion-a-native/part-2/{00000..18699}.tar -\"\n        - \"pipe:aws s3 cp s3://stability-aws/laion-a-native/part-3/{00000..18699}.tar -\"\n        - \"pipe:aws s3 cp s3://stability-aws/laion-a-native/part-4/{00000..18699}.tar -\"  #{00000-94333}.tar\"\n      shuffle: 10000\n      image_key: jpg\n      image_transforms:\n      - target: torchvision.transforms.Resize\n        params:\n          size: 512\n          interpolation: 3\n      - target: torchvision.transforms.RandomCrop\n        params:\n          size: 512\n      postprocess:\n        target: ldm.data.laion.AddMask\n        params:\n          mode: \"512train-large\"\n          p_drop: 0.25\n    # NOTE use enough shards to avoid empty validation loops in workers\n    validation:\n      shards:\n        - \"pipe:aws s3 cp s3://deep-floyd-s3/datasets/laion_cleaned-part5/{93001..94333}.tar - \"\n      shuffle: 0\n      image_key: jpg\n      image_transforms:\n      - target: torchvision.transforms.Resize\n        params:\n          size: 512\n          interpolation: 3\n      - target: torchvision.transforms.CenterCrop\n        params:\n          size: 512\n      postprocess:\n        target: ldm.data.laion.AddMask\n        params:\n          mode: \"512train-large\"\n          p_drop: 0.25\n\nlightning:\n  find_unused_parameters: True\n  modelcheckpoint:\n    params:\n      every_n_train_steps: 5000\n\n  callbacks:\n    metrics_over_trainsteps_checkpoint:\n      params:\n        every_n_train_steps: 10000\n\n    image_logger:\n      target: main.ImageLogger\n      params:\n        enable_autocast: False\n        disabled: False\n        batch_frequency: 1000\n        max_images: 4\n        increase_log_steps: False\n        log_first_step: False\n        log_images_kwargs:\n          use_ema_scope: False\n          inpaint: False\n          plot_progressive_rows: False\n          plot_diffusion_rows: False\n          N: 4\n          unconditional_guidance_scale: 5.0\n          unconditional_guidance_label: [\"\"]\n          ddim_steps: 50  # todo check these out for depth2img,\n          ddim_eta: 0.0   # todo check these out for depth2img,\n\n  trainer:\n    benchmark: True\n    val_check_interval: 5000000\n    num_sanity_val_steps: 0\n    accumulate_grad_batches: 1\n"
  },
  {
    "path": "sd_configs/v2-midas-inference.yaml",
    "content": "model:\n  base_learning_rate: 5.0e-07\n  target: ldm.models.diffusion.ddpm.LatentDepth2ImageDiffusion\n  params:\n    linear_start: 0.00085\n    linear_end: 0.0120\n    num_timesteps_cond: 1\n    log_every_t: 200\n    timesteps: 1000\n    first_stage_key: \"jpg\"\n    cond_stage_key: \"txt\"\n    image_size: 64\n    channels: 4\n    cond_stage_trainable: false\n    conditioning_key: hybrid\n    scale_factor: 0.18215\n    monitor: val/loss_simple_ema\n    finetune_keys: null\n    use_ema: False\n\n    depth_stage_config:\n      target: ldm.modules.midas.api.MiDaSInference\n      params:\n        model_type: \"dpt_hybrid\"\n\n    unet_config:\n      target: ldm.modules.diffusionmodules.openaimodel.UNetModel\n      params:\n        use_checkpoint: True\n        image_size: 32 # unused\n        in_channels: 5\n        out_channels: 4\n        model_channels: 320\n        attention_resolutions: [ 4, 2, 1 ]\n        num_res_blocks: 2\n        channel_mult: [ 1, 2, 4, 4 ]\n        num_head_channels: 64 # need to fix for flash-attn\n        use_spatial_transformer: True\n        use_linear_in_transformer: True\n        transformer_depth: 1\n        context_dim: 1024\n        legacy: False\n\n    first_stage_config:\n      target: ldm.models.autoencoder.AutoencoderKL\n      params:\n        embed_dim: 4\n        monitor: val/rec_loss\n        ddconfig:\n          #attn_type: \"vanilla-xformers\"\n          double_z: true\n          z_channels: 4\n          resolution: 256\n          in_channels: 3\n          out_ch: 3\n          ch: 128\n          ch_mult:\n            - 1\n            - 2\n            - 4\n            - 4\n          num_res_blocks: 2\n          attn_resolutions: [ ]\n          dropout: 0.0\n        lossconfig:\n          target: torch.nn.Identity\n\n    cond_stage_config:\n      target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder\n      params:\n        freeze: True\n        layer: \"penultimate\"\n\n\n"
  },
  {
    "path": "tools.py",
    "content": "from .operators.inpaint_area_brush import InpaintAreaBrush\n\nTOOLS = (\n    InpaintAreaBrush,\n)"
  },
  {
    "path": "ui/panels/dream_texture.py",
    "content": "import bpy\nfrom bpy.types import Panel\nfrom ..presets import DREAM_PT_AdvancedPresets\nfrom ...prompt_engineering import *\nfrom ...operators.dream_texture import DreamTexture, ReleaseGenerator, CancelGenerator, get_source_image\nfrom ...operators.open_latest_version import OpenLatestVersion, is_force_show_download, new_version_available\nfrom ...operators.view_history import ImportPromptFile\nfrom ..space_types import SPACE_TYPES\nfrom ...generator_process.actions.detect_seamless import SeamlessAxes\nfrom ...api.models import FixItError\nfrom ...property_groups.dream_prompt import DreamPrompt\nfrom ...property_groups.control_net import BakeControlNetImage\nfrom ... import api\n\ndef dream_texture_panels():\n    for space_type in SPACE_TYPES:\n        class DreamTexturePanel(Panel):\n            \"\"\"Creates a Panel in the scene context of the properties editor\"\"\"\n            bl_label = \"Dream Texture\"\n            bl_idname = f\"DREAM_PT_dream_panel_{space_type}\"\n            bl_category = \"Dream\"\n            bl_space_type = space_type\n            bl_region_type = 'UI'\n\n            @classmethod\n            def poll(cls, context):\n                if cls.bl_space_type == 'NODE_EDITOR':\n                    return context.area.ui_type == \"ShaderNodeTree\" or context.area.ui_type == \"CompositorNodeTree\"\n                else:\n                    return True\n            \n            def draw_header_preset(self, context):\n                layout = self.layout\n                layout.operator(ImportPromptFile.bl_idname, text=\"\", icon=\"IMPORT\")\n                layout.separator()\n\n            def draw(self, context):\n                layout = self.layout\n                layout.use_property_split = True\n                layout.use_property_decorate = False\n\n                if is_force_show_download():\n                    layout.operator(OpenLatestVersion.bl_idname, icon=\"IMPORT\", text=\"Download Latest Release\")\n                elif new_version_available():\n                    layout.operator(OpenLatestVersion.bl_idname, icon=\"IMPORT\")\n\n                layout.prop(context.scene.dream_textures_prompt, \"backend\")\n                layout.prop(context.scene.dream_textures_prompt, 'model')\n\n        DreamTexturePanel.__name__ = f\"DREAM_PT_dream_panel_{space_type}\"\n        yield DreamTexturePanel\n\n        def get_prompt(context):\n            return context.scene.dream_textures_prompt\n\n        def get_seamless_result(context, prompt):\n            init_image = None\n            if prompt.use_init_img and prompt.init_img_action in ['modify', 'inpaint']:\n                init_image = get_source_image(context, prompt.init_img_src)\n            context.scene.seamless_result.check(init_image)\n            return context.scene.seamless_result\n\n        yield from create_panel(space_type, 'UI', DreamTexturePanel.bl_idname, prompt_panel, get_prompt,\n                                get_seamless_result=get_seamless_result)\n        yield create_panel(space_type, 'UI', DreamTexturePanel.bl_idname, size_panel, get_prompt)\n        yield from create_panel(space_type, 'UI', DreamTexturePanel.bl_idname, init_image_panels, get_prompt)\n        yield create_panel(space_type, 'UI', DreamTexturePanel.bl_idname, control_net_panel, get_prompt)\n        yield from create_panel(space_type, 'UI', DreamTexturePanel.bl_idname, advanced_panel, get_prompt)\n        yield create_panel(space_type, 'UI', DreamTexturePanel.bl_idname, actions_panel, get_prompt)\n\ndef create_panel(space_type, region_type, parent_id, ctor, get_prompt, use_property_decorate=False, **kwargs):\n    class BasePanel(Panel):\n        bl_category = \"Dream\"\n        bl_space_type = space_type\n        bl_region_type = region_type\n\n    class SubPanel(BasePanel):\n        bl_category = \"Dream\"\n        bl_space_type = space_type\n        bl_region_type = region_type\n        bl_parent_id = parent_id\n\n        def draw(self, context):\n            self.layout.use_property_decorate = use_property_decorate\n\n    return ctor(kwargs.pop('base_panel', SubPanel), space_type, get_prompt, **kwargs)\n\ndef prompt_panel(sub_panel, space_type, get_prompt, get_seamless_result=None):\n    class PromptPanel(sub_panel):\n        \"\"\"Create a subpanel for prompt input\"\"\"\n        bl_label = \"Prompt\"\n        bl_idname = f\"DREAM_PT_dream_panel_prompt_{space_type}\"\n\n        def draw_header_preset(self, context):\n            layout = self.layout\n            layout.prop(get_prompt(context), \"prompt_structure\", text=\"\")\n\n        def draw(self, context):\n            super().draw(context)\n            layout = self.layout\n            layout.use_property_split = True\n            prompt = get_prompt(context)\n\n            structure = next(x for x in prompt_structures if x.id == prompt.prompt_structure)\n            for segment in structure.structure:\n                segment_row = layout.row()\n                enum_prop = 'prompt_structure_token_' + segment.id + '_enum'\n                is_custom = getattr(prompt, enum_prop) == 'custom'\n                if is_custom:\n                    segment_row.prop(prompt, 'prompt_structure_token_' + segment.id)\n                enum_cases = DreamPrompt.__annotations__[enum_prop].keywords['items']\n                if len(enum_cases) != 1 or enum_cases[0][0] != 'custom':\n                    segment_row.prop(prompt, enum_prop, icon_only=is_custom)\n            if prompt.prompt_structure == file_batch_structure.id:\n                layout.template_ID(context.scene, \"dream_textures_prompt_file\", open=\"text.open\")\n            \n            layout.prop(prompt, \"seamless_axes\")\n            if prompt.seamless_axes == SeamlessAxes.AUTO and get_seamless_result is not None:\n                auto_row = self.layout.row()\n                auto_row.enabled = False\n                auto_row.prop(get_seamless_result(context, prompt), \"result\")\n\n    yield PromptPanel\n\n    class NegativePromptPanel(sub_panel):\n        \"\"\"Create a subpanel for negative prompt input\"\"\"\n        bl_idname = f\"DREAM_PT_dream_panel_negative_prompt_{space_type}\"\n        bl_label = \"Negative\"\n        bl_parent_id = PromptPanel.bl_idname\n\n        @classmethod\n        def poll(cls, context):\n            return get_prompt(context).prompt_structure != file_batch_structure.id\n\n        def draw_header(self, context):\n            layout = self.layout\n            layout.prop(get_prompt(context), \"use_negative_prompt\", text=\"\")\n\n        def draw(self, context):\n            super().draw(context)\n            layout = self.layout\n            layout.use_property_split = True\n            layout.enabled = layout.enabled and get_prompt(context).use_negative_prompt\n            scene = context.scene\n\n            layout.prop(get_prompt(context), \"negative_prompt\")\n    yield NegativePromptPanel\n\ndef size_panel(sub_panel, space_type, get_prompt):\n    class SizePanel(sub_panel):\n        \"\"\"Create a subpanel for size options\"\"\"\n        bl_idname = f\"DREAM_PT_dream_panel_size_{space_type}\"\n        bl_label = \"Size\"\n        bl_options = {'DEFAULT_CLOSED'}\n\n        def draw_header(self, context):\n            self.layout.prop(get_prompt(context), \"use_size\", text=\"\")\n\n        def draw(self, context):\n            super().draw(context)\n            layout = self.layout\n            layout.use_property_split = True\n            layout.enabled = layout.enabled and get_prompt(context).use_size\n\n            layout.prop(get_prompt(context), \"width\")\n            layout.prop(get_prompt(context), \"height\")\n    return SizePanel\n\ndef init_image_panels(sub_panel, space_type, get_prompt):\n    class InitImagePanel(sub_panel):\n        \"\"\"Create a subpanel for init image options\"\"\"\n        bl_idname = f\"DREAM_PT_dream_panel_init_image_{space_type}\"\n        bl_label = \"Source Image\"\n        bl_options = {'DEFAULT_CLOSED'}\n\n        def draw_header(self, context):\n            self.layout.prop(get_prompt(context), \"use_init_img\", text=\"\")\n\n        def draw(self, context):\n            super().draw(context)\n            layout = self.layout\n            prompt = get_prompt(context)\n            layout.enabled = prompt.use_init_img\n            \n            layout.prop(prompt, \"init_img_src\", expand=True)\n            if prompt.init_img_src == 'file':\n                layout.template_ID(context.scene, \"init_img\", open=\"image.open\")\n            layout.prop(prompt, \"init_img_action\", expand=True)\n            \n            layout.use_property_split = True\n\n            if prompt.init_img_action == 'inpaint':\n                layout.prop(prompt, \"inpaint_mask_src\")\n                if prompt.inpaint_mask_src == 'prompt':\n                    layout.prop(prompt, \"text_mask\")\n                    layout.prop(prompt, \"text_mask_confidence\")\n                layout.prop(prompt, \"inpaint_replace\")\n            elif prompt.init_img_action == 'outpaint':\n                layout.prop(prompt, \"outpaint_origin\")\n                def _outpaint_warning_box(warning):\n                    box = layout.box()\n                    box.label(text=warning, icon=\"ERROR\")\n                if prompt.outpaint_origin[0] <= -prompt.width or prompt.outpaint_origin[1] <= -prompt.height:\n                    _outpaint_warning_box(\"Outpaint has no overlap, so the result will not blend\")\n                init_img = context.scene.init_img if prompt.init_img_src == 'file' else None\n                if init_img is None:\n                    for area in context.screen.areas:\n                        if area.type == 'IMAGE_EDITOR':\n                            if area.spaces.active.image is not None:\n                                init_img = area.spaces.active.image\n                if init_img is not None:\n                    if prompt.outpaint_origin[0] >= init_img.size[0] or \\\n                        prompt.outpaint_origin[1] >= init_img.size[1]:\n                        _outpaint_warning_box(\"Outpaint has no overlap, so the result will not blend\")\n            elif prompt.init_img_action == 'modify':\n                layout.prop(prompt, \"fit\")\n            if prompt.init_img_action != 'outpaint':\n                layout.prop(prompt, \"strength\")\n            layout.prop(prompt, \"use_init_img_color\")\n            if prompt.init_img_action == 'modify':\n                layout.prop(prompt, \"modify_action_source_type\")\n                if prompt.modify_action_source_type == 'depth_map':\n                    layout.template_ID(context.scene, \"init_depth\", open=\"image.open\")\n    yield InitImagePanel\n\ndef control_net_panel(sub_panel, space_type, get_prompt):\n    class ControlNetPanel(sub_panel):\n        \"\"\"Create a subpanel for ControlNet options\"\"\"\n        bl_idname = f\"DREAM_PT_dream_panel_control_net_{space_type}\"\n        bl_label = \"ControlNet\"\n        bl_options = {'DEFAULT_CLOSED'}\n\n        def draw(self, context):\n            layout = self.layout\n            prompt = get_prompt(context)\n            \n            layout.operator(\"wm.call_menu\", text=\"Add ControlNet\", icon='ADD').name = \"DREAM_MT_control_nets_add\"\n            for i, control_net in enumerate(prompt.control_nets):\n                box = layout.box()\n                box.use_property_split = False\n                box.use_property_decorate = False\n                \n                row = box.row()\n                row.prop(control_net, \"enabled\", icon=\"MODIFIER_ON\" if control_net.enabled else \"MODIFIER_OFF\", icon_only=True, emboss=False)\n                row.prop(control_net, \"control_net\", text=\"\")\n                row.operator(\"dream_textures.control_nets_remove\", icon='X', emboss=False, text=\"\").index = i\n\n                col = box.column()\n                col.use_property_split = True\n                col.template_ID(control_net, \"control_image\", open=\"image.open\", text=\"Image\")\n                processor_row = col.row()\n                processor_row.prop(control_net, \"processor_id\")\n                if control_net.processor_id != \"none\":\n                    processor_row.operator(BakeControlNetImage.bl_idname, icon='RENDER_STILL', text='').index = i\n                col.prop(control_net, \"conditioning_scale\")\n                \n    return ControlNetPanel\n\ndef advanced_panel(sub_panel, space_type, get_prompt):\n    class AdvancedPanel(sub_panel):\n        \"\"\"Create a subpanel for advanced options\"\"\"\n        bl_idname = f\"DREAM_PT_dream_panel_advanced_{space_type}\"\n        bl_label = \"Advanced\"\n        bl_options = {'DEFAULT_CLOSED'}\n\n        def draw_header_preset(self, context):\n            DREAM_PT_AdvancedPresets.draw_panel_header(self.layout)\n\n        def draw(self, context):\n            super().draw(context)\n            layout = self.layout\n            layout.use_property_split = True\n            \n            prompt = get_prompt(context)\n            layout.prop(prompt, \"random_seed\")\n            if not prompt.random_seed:\n                layout.prop(prompt, \"seed\")\n            # advanced_box.prop(self, \"iterations\") # Disabled until supported by the addon.\n            layout.prop(prompt, \"steps\")\n            layout.prop(prompt, \"cfg_scale\")\n            layout.prop(prompt, \"scheduler\")\n            layout.prop(prompt, \"step_preview_mode\")\n\n            backend: api.Backend = prompt.get_backend()\n            backend.draw_advanced(layout, context)\n\n    yield AdvancedPanel\n\n    yield from optimization_panels(sub_panel, space_type, get_prompt, AdvancedPanel.bl_idname)\n\ndef optimization_panels(sub_panel, space_type, get_prompt, parent_id=\"\"):\n    class SpeedOptimizationPanel(sub_panel):\n        \"\"\"Create a subpanel for speed optimizations\"\"\"\n        bl_idname = f\"DREAM_PT_dream_panel_speed_optimizations_{space_type}\"\n        bl_label = \"Speed Optimizations\"\n        bl_parent_id = parent_id\n\n        def draw(self, context):\n            super().draw(context)\n            layout = self.layout\n            layout.use_property_split = True\n            prompt = get_prompt(context)\n\n            backend: api.Backend = prompt.get_backend()\n            backend.draw_speed_optimizations(layout, context)\n    yield SpeedOptimizationPanel\n\n    class MemoryOptimizationPanel(sub_panel):\n        \"\"\"Create a subpanel for memory optimizations\"\"\"\n        bl_idname = f\"DREAM_PT_dream_panel_memory_optimizations_{space_type}\"\n        bl_label = \"Memory Optimizations\"\n        bl_parent_id = parent_id\n\n        def draw(self, context):\n            super().draw(context)\n            layout = self.layout\n            layout.use_property_split = True\n            prompt = get_prompt(context)\n\n            backend: api.Backend = prompt.get_backend()\n            backend.draw_memory_optimizations(layout, context)\n    yield MemoryOptimizationPanel\n\ndef actions_panel(sub_panel, space_type, get_prompt):\n    class ActionsPanel(sub_panel):\n        \"\"\"Create a subpanel for actions\"\"\"\n        bl_idname = f\"DREAM_PT_dream_panel_actions_{space_type}\"\n        bl_label = \"Advanced\"\n        bl_options = {'HIDE_HEADER'}\n\n        def draw(self, context):\n            super().draw(context)\n            layout = self.layout\n            layout.use_property_split = True\n\n            prompt = get_prompt(context)\n\n            iterations_row = layout.row()\n            iterations_row.enabled = prompt.prompt_structure != file_batch_structure.id\n            iterations_row.prop(prompt, \"iterations\")\n            \n            row = layout.row(align=True)\n            row.scale_y = 1.5\n            if CancelGenerator.poll(context):\n                row.operator(CancelGenerator.bl_idname, icon=\"SNAP_FACE\", text=\"\")\n            if context.scene.dream_textures_progress <= 0:\n                if context.scene.dream_textures_info != \"\":\n                    disabled_row = row.row(align=True)\n                    disabled_row.operator(DreamTexture.bl_idname, text=context.scene.dream_textures_info, icon=\"INFO\")\n                    disabled_row.enabled = False\n                else:\n                    row.operator(DreamTexture.bl_idname, icon=\"PLAY\", text=\"Generate\")\n            else:\n                if bpy.app.version[0] >= 4:\n                    progress = context.scene.dream_textures_progress\n                    progress_max = bpy.types.Scene.dream_textures_progress.keywords['max']\n                    row.progress(text=f\"{progress} / {progress_max}\", factor=progress / progress_max)\n                else:\n                    disabled_row = row.row(align=True)\n                    disabled_row.use_property_split = True\n                    disabled_row.prop(context.scene, 'dream_textures_progress', slider=True)\n                    disabled_row.enabled = False\n            row.operator(ReleaseGenerator.bl_idname, icon=\"X\", text=\"\")\n\n            if context.scene.dream_textures_last_execution_time != \"\":\n                r = layout.row()\n                r.scale_x = 0.5\n                r.scale_y = 0.5\n                r.label(text=context.scene.dream_textures_last_execution_time, icon=\"SORTTIME\")\n\n            # Validation\n            try:\n                backend: api.Backend = prompt.get_backend()\n                backend.validate(prompt.generate_args(context))\n            except FixItError as e:\n                error_box = layout.box()\n                error_box.use_property_split = False\n                for i, line in enumerate(e.args[0].split('\\n')):\n                    error_box.label(text=line, icon=\"ERROR\" if i == 0 else \"NONE\")\n                e._draw(prompt, context, error_box)\n    return ActionsPanel"
  },
  {
    "path": "ui/panels/history.py",
    "content": "import bpy\nfrom bpy.types import Panel\nfrom ...prompt_engineering import *\nfrom ...operators.dream_texture import DreamTexture, ReleaseGenerator\nfrom ...operators.view_history import ExportHistorySelection, ImportPromptFile, RecallHistoryEntry, ClearHistory, RemoveHistorySelection\nfrom ...operators.open_latest_version import OpenLatestVersion, is_force_show_download, new_version_available\nfrom ...preferences import StableDiffusionPreferences\nfrom ..space_types import SPACE_TYPES\n\ndef history_panels():\n    for space_type in SPACE_TYPES:\n        class HistoryPanel(Panel):\n            \"\"\"Panel for Dream Textures History\"\"\"\n            bl_label = \"History\"\n            bl_category = \"Dream\"\n            bl_idname = f\"DREAM_PT_dream_history_panel_{space_type}\"\n            bl_space_type = space_type\n            bl_region_type = 'UI'\n\n            @classmethod\n            def poll(cls, context):\n                if cls.bl_space_type == 'NODE_EDITOR':\n                    return context.area.ui_type == \"ShaderNodeTree\" or context.area.ui_type == \"CompositorNodeTree\"\n                else:\n                    return True\n\n            def draw(self, context):\n                self.layout.template_list(\"SCENE_UL_HistoryList\", \"\", context.scene, \"dream_textures_history\", context.scene, \"dream_textures_history_selection\")\n                \n                row = self.layout.row()\n                row.prop(context.scene, \"dream_textures_history_selection_preview\")\n                row.operator(RemoveHistorySelection.bl_idname, text=\"\", icon=\"X\")\n                row.operator(ExportHistorySelection.bl_idname, text=\"\", icon=\"EXPORT\")\n\n                self.layout.operator(RecallHistoryEntry.bl_idname)\n                self.layout.operator(ClearHistory.bl_idname)\n        HistoryPanel.__name__ = f\"DREAM_PT_dream_history_panel_{space_type}\"\n        yield HistoryPanel"
  },
  {
    "path": "ui/panels/render_properties.py",
    "content": "import bpy\nfrom .dream_texture import create_panel, prompt_panel, advanced_panel\nfrom ...property_groups.dream_prompt import backend_options\nfrom ...generator_process.models import ModelType\nfrom ...preferences import StableDiffusionPreferences\n\nclass RenderPropertiesPanel(bpy.types.Panel):\n    \"\"\"Panel for Dream Textures render properties\"\"\"\n    bl_label = \"Dream Textures\"\n    bl_idname = \"DREAM_PT_dream_render_properties_panel\"\n    bl_space_type = 'PROPERTIES'\n    bl_region_type = 'WINDOW'\n    bl_context = 'render'\n    bl_options = {'DEFAULT_CLOSED'}\n\n    @classmethod\n    def poll(self, context):\n        return context.scene.render.engine == 'CYCLES'\n\n    def draw_header(self, context):\n        self.layout.prop(context.scene, \"dream_textures_render_properties_enabled\", text=\"\")\n\n    def draw(self, context):\n        layout = self.layout\n        layout.use_property_split = True\n        layout.use_property_decorate = False\n        layout.active = context.scene.dream_textures_render_properties_enabled\n\n        if len(backend_options(self, context)) > 1:\n            layout.prop(context.scene.dream_textures_render_properties_prompt, \"backend\")\n        layout.prop(context.scene.dream_textures_render_properties_prompt, 'model')\n        layout.prop(context.scene.dream_textures_render_properties_prompt, \"strength\")\n        layout.prop(context.scene, \"dream_textures_render_properties_pass_inputs\")\n        if context.scene.dream_textures_render_properties_pass_inputs != 'color':\n            if not bpy.context.view_layer.use_pass_z:\n                box = layout.box()\n                box.label(text=\"Z Pass Disabled\", icon=\"ERROR\")\n                box.label(text=\"Enable the Z pass to use depth pass inputs\")\n                box.use_property_split = False\n                box.prop(context.view_layer, \"use_pass_z\")\n            \n            models = list(filter(\n                lambda m: m.model_base == context.scene.dream_textures_render_properties_prompt.model,\n                context.preferences.addons[StableDiffusionPreferences.bl_idname].preferences.installed_models\n            ))\n            if len(models) > 0 and ModelType[models[0].model_type] != ModelType.DEPTH:\n                box = layout.box()\n                box.label(text=\"Unsupported model\", icon=\"ERROR\")\n                box.label(text=\"Select a depth model, such as 'stabilityai/stable-diffusion-2-depth'\")\n\ndef render_properties_panels():\n    yield RenderPropertiesPanel\n    def get_prompt(context):\n        return context.scene.dream_textures_render_properties_prompt\n    space_type = RenderPropertiesPanel.bl_space_type\n    region_type = RenderPropertiesPanel.bl_region_type\n    panels = [\n        *create_panel(space_type, region_type, RenderPropertiesPanel.bl_idname, prompt_panel, get_prompt, True),\n        *create_panel(space_type, region_type, RenderPropertiesPanel.bl_idname, advanced_panel, get_prompt, True),\n    ]\n    for panel in panels:\n        def draw_decorator(original):\n            def draw(self, context):\n                self.layout.enabled = context.scene.dream_textures_render_properties_enabled\n                return original(self, context)\n            return draw\n        panel.draw = draw_decorator(panel.draw)\n        if hasattr(panel, 'draw_header_preset'):\n            panel.draw_header_preset = draw_decorator(panel.draw_header_preset)\n        if hasattr(panel, 'draw_header'):\n            panel.draw_header = draw_decorator(panel.draw_header)\n        yield panel"
  },
  {
    "path": "ui/panels/upscaling.py",
    "content": "from bpy.types import Panel\nfrom ...prompt_engineering import *\nfrom ...operators.upscale import Upscale, get_source_image\nfrom ...operators.dream_texture import CancelGenerator, ReleaseGenerator\nfrom ...generator_process.actions.detect_seamless import SeamlessAxes\nfrom .dream_texture import create_panel, advanced_panel\nfrom ..space_types import SPACE_TYPES\n\ndef upscaling_panels():\n    for space_type in SPACE_TYPES:\n        class UpscalingPanel(Panel):\n            \"\"\"Panel for AI Upscaling\"\"\"\n            bl_label = \"AI Upscaling\"\n            bl_category = \"Dream\"\n            bl_idname = f\"DREAM_PT_dream_upscaling_panel_{space_type}\"\n            bl_space_type = space_type\n            bl_region_type = 'UI'\n            bl_options = {'DEFAULT_CLOSED'}\n\n            @classmethod\n            def poll(cls, context):\n                if cls.bl_space_type == 'NODE_EDITOR':\n                    return context.area.ui_type == \"ShaderNodeTree\" or context.area.ui_type == \"CompositorNodeTree\"\n                else:\n                    return True\n\n            def draw(self, context):\n                layout = self.layout\n                layout.use_property_split = True\n                layout.use_property_decorate = False\n\n                prompt = context.scene.dream_textures_upscale_prompt\n\n                layout.prop(prompt, \"backend\")\n                layout.prop(prompt, \"model\")\n                \n                layout.prop(prompt, \"prompt_structure_token_subject\")\n                layout.prop(context.scene, \"dream_textures_upscale_tile_size\")\n                layout.prop(context.scene, \"dream_textures_upscale_blend\")\n\n                layout.prop(prompt, \"seamless_axes\")\n\n                if prompt.seamless_axes == SeamlessAxes.AUTO:\n                    node_tree = context.material.node_tree if hasattr(context, 'material') else None\n                    active_node = next((node for node in node_tree.nodes if node.select and node.bl_idname == 'ShaderNodeTexImage'), None) if node_tree is not None else None\n                    init_image = get_source_image(context)\n                    context.scene.dream_textures_upscale_seamless_result.check(init_image)\n                    auto_row = layout.row()\n                    auto_row.enabled = False\n                    auto_row.prop(context.scene.dream_textures_upscale_seamless_result, \"result\")\n\n                if context.scene.dream_textures_upscale_tile_size > 128:\n                    warning_box = layout.box()\n                    warning_box.label(text=\"Warning\", icon=\"ERROR\")\n                    warning_box.label(text=\"Large tile sizes consume more VRAM.\")\n\n        UpscalingPanel.__name__ = UpscalingPanel.bl_idname\n        class ActionsPanel(Panel):\n            \"\"\"Panel for AI Upscaling Actions\"\"\"\n            bl_category = \"Dream\"\n            bl_label = \"Actions\"\n            bl_idname = f\"DREAM_PT_dream_upscaling_actions_panel_{space_type}\"\n            bl_space_type = space_type\n            bl_region_type = 'UI'\n            bl_parent_id = UpscalingPanel.bl_idname\n            bl_options = {'HIDE_HEADER'}\n\n            @classmethod\n            def poll(cls, context):\n                if cls.bl_space_type == 'NODE_EDITOR':\n                    return context.area.ui_type == \"ShaderNodeTree\" or context.area.ui_type == \"CompositorNodeTree\"\n                else:\n                    return True\n\n            def draw(self, context):\n                layout = self.layout\n                layout.use_property_split = True\n                layout.use_property_decorate = False\n                \n                image = get_source_image(context)\n                row = layout.row(align=True)\n                row.scale_y = 1.5\n                if CancelGenerator.poll(context):\n                    row.operator(CancelGenerator.bl_idname, icon=\"SNAP_FACE\", text=\"\")\n                if context.scene.dream_textures_progress <= 0:\n                    if context.scene.dream_textures_info != \"\":\n                        disabled_row = row.row(align=True)\n                        disabled_row.operator(Upscale.bl_idname, text=context.scene.dream_textures_info, icon=\"INFO\")\n                        disabled_row.enabled = False\n                    else:\n                        row.operator(\n                            Upscale.bl_idname,\n                            text=f\"Upscale to {image.size[0] * 4}x{image.size[1] * 4}\" if image is not None else \"Upscale\",\n                            icon=\"FULLSCREEN_ENTER\"\n                        )\n                else:\n                    disabled_row = row.row(align=True)\n                    disabled_row.use_property_split = True\n                    disabled_row.prop(context.scene, 'dream_textures_progress', slider=True)\n                    disabled_row.enabled = False\n                row.operator(ReleaseGenerator.bl_idname, icon=\"X\", text=\"\")\n        yield UpscalingPanel\n        advanced_panels = [*create_panel(space_type, 'UI', UpscalingPanel.bl_idname, advanced_panel, lambda context: context.scene.dream_textures_upscale_prompt)]\n        outer_panel = advanced_panels[0]\n        outer_original_idname = outer_panel.bl_idname\n        outer_panel.bl_idname += \"_upscaling\"\n        for panel in advanced_panels:\n            panel.bl_idname += \"_upscaling\"\n            if panel.bl_parent_id == outer_original_idname:\n                panel.bl_parent_id = outer_panel.bl_idname\n            yield panel\n        yield ActionsPanel"
  },
  {
    "path": "ui/presets.py",
    "content": "import bpy\nfrom bpy.types import Panel, Operator, Menu\nfrom bl_operators.presets import AddPresetBase\nfrom bl_ui.utils import PresetPanel\nfrom typing import _AnnotatedAlias\nimport os\nimport shutil\nfrom ..absolute_path import absolute_path\nfrom ..generator_process.actions.prompt_to_image import Optimizations\n\nclass DreamTexturesPresetPanel(PresetPanel, Panel):\n    preset_operator = \"script.execute_preset\"\n\nclass DREAM_PT_AdvancedPresets(DreamTexturesPresetPanel):\n    bl_label = \"Advanced Presets\"\n    preset_subdir = \"dream_textures/advanced\"\n    preset_add_operator = \"dream_textures.advanced_preset_add\"\n\nclass DREAM_MT_AdvancedPresets(Menu):\n    bl_label = 'Advanced Presets'\n    preset_subdir = 'dream_textures/advanced'\n    preset_operator = 'script.execute_preset'\n    draw = Menu.draw_preset\n\nclass AddAdvancedPreset(AddPresetBase, Operator):\n    bl_idname = 'dream_textures.advanced_preset_add'\n    bl_label = 'Add Advanced Preset'\n    preset_menu = 'DREAM_MT_AdvancedPresets'\n    \n    preset_subdir = 'dream_textures/advanced'\n    \n    preset_defines = ['prompt = bpy.context.scene.dream_textures_prompt']\n    preset_values = [\n        \"prompt.steps\",\n        \"prompt.cfg_scale\",\n        \"prompt.scheduler\",\n        \"prompt.step_preview_mode\",\n\n        \"prompt.optimizations_attention_slicing\",\n        \"prompt.optimizations_attention_slice_size_src\",\n        \"prompt.optimizations_attention_slice_size\",\n        \"prompt.optimizations_cudnn_benchmark\",\n        \"prompt.optimizations_tf32\",\n        \"prompt.optimizations_amp\",\n        \"prompt.optimizations_half_precision\",\n        \"prompt.optimizations_sequential_cpu_offload\",\n        \"prompt.optimizations_channels_last_memory_format\",\n        \"prompt.optimizations_batch_size\",\n        \"prompt.optimizations_vae_slicing\",\n        \"prompt.optimizations_cpu_only\",\n    ]\n\nclass RestoreDefaultPresets(Operator):\n    bl_idname = \"dream_textures.restore_default_presets\"\n    bl_label = \"Restore Default Presets\"\n    bl_description = (\"Restores all default presets provided by the addon.\")\n    bl_options = {\"REGISTER\", \"INTERNAL\"}\n\n    def execute(self, context):\n        register_default_presets(force=True)\n        return {\"FINISHED\"}\n\nPRESETS_PATH = os.path.join(bpy.utils.user_resource('SCRIPTS'), 'presets/dream_textures/advanced')\nDEFAULT_PRESETS_PATH = absolute_path('builtin_presets')\ndef register_default_presets(force=False):\n    presets_path_exists = os.path.isdir(PRESETS_PATH)\n    if not presets_path_exists or force:\n        if not presets_path_exists:\n            os.makedirs(PRESETS_PATH)\n        for default_preset in os.listdir(DEFAULT_PRESETS_PATH):\n            if not os.path.exists(os.path.join(PRESETS_PATH, default_preset)):\n                shutil.copy(os.path.join(DEFAULT_PRESETS_PATH, default_preset), PRESETS_PATH)\n\ndef default_presets_missing():\n    if not os.path.isdir(PRESETS_PATH):\n        return True\n    for default_preset in os.listdir(DEFAULT_PRESETS_PATH):\n        if not os.path.exists(os.path.join(PRESETS_PATH, default_preset)):\n            return True"
  },
  {
    "path": "ui/space_types.py",
    "content": "SPACE_TYPES = {'IMAGE_EDITOR', 'NODE_EDITOR'}"
  },
  {
    "path": "version.py",
    "content": "VERSION = (0, 4, 1)\ndef version_tag(version):\n    return f\"{version[0]}.{version[1]}.{version[2]}\"\n\ndef version_tuple(tag):\n    return tuple(map(lambda x: int(x), tag.split('.')))\n"
  }
]