diff --git a/.gitattributes b/.gitattributes index c7d9f3332a950355d5a77d85000f05e6f45435ea..a4a193fafe33e0e1f93eb5181e2d7abe4fb9b85f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -32,3 +32,31 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-inpainting/merged-leopards.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/depth2img/d2i.gif filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/depth2img/depth2img01.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/depth2img/depth2img02.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/depth2img/merged-0000.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/depth2img/merged-0004.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/depth2img/merged-0005.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/img2img/upscaling-in.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/img2img/upscaling-out.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0001.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0002.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0004.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0005.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0006.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/txt2img/merged-0001.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/txt2img/merged-0003.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/txt2img/merged-0005.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/txt2img/merged-0006.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/txt2img/merged-0007.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/upscaling/merged-dog.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/upscaling/sampled-bear-x4.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/assets/stable-samples/upscaling/snow-leopard-x4.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/src/blip/BLIP.gif filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/src/taming-transformers/assets/birddrawnbyachild.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/src/taming-transformers/assets/first_stage_mushrooms.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/src/taming-transformers/assets/first_stage_squirrels.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/src/taming-transformers/assets/imagenet.png filter=lfs diff=lfs merge=lfs -text +sd/stablediffusion/src/taming-transformers/scripts/reconstruction_usage.ipynb filter=lfs diff=lfs merge=lfs -text diff --git a/README.md b/README.md index 127e26e1e62ce345a1d9b8426030ee05206f9abc..b44062ca509a687fb0004e226e444094f9eba15c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ --- -title: Stable Diffusion -emoji: 💻 -colorFrom: green -colorTo: green -sdk: gradio -sdk_version: 3.20.1 -app_file: app.py -pinned: false license: apache-2.0 +title: Automatic Stable Diffusion +sdk: gradio +sdk_version: 3.16.2 +app_file: sd/stable-diffusion-webui/webui.py +emoji: 🚀 +colorFrom: indigo +colorTo: purple +pinned: true --- - -Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..6243ca93646d5ba6c22b6ac5b18c072feb15ea23 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,32 @@ +blendmodes +accelerate +basicsr +fonts +font-roboto +gfpgan +gradio==3.16.2 +invisible-watermark +numpy +omegaconf +opencv-contrib-python +requests +piexif +Pillow +pytorch_lightning==1.7.7 +realesrgan +scikit-image>=0.19 +timm==0.4.12 +transformers==4.25.1 +torch +einops +jsonmerge +clean-fid +resize-right +torchdiffeq +kornia +lark +inflection +GitPython +torchsde +safetensors +psutil diff --git a/sd/stable-diffusion-webui/CODEOWNERS b/sd/stable-diffusion-webui/CODEOWNERS new file mode 100644 index 0000000000000000000000000000000000000000..2c937f6f1e519f864d15d5233e1fb86c6cdfac2f --- /dev/null +++ b/sd/stable-diffusion-webui/CODEOWNERS @@ -0,0 +1,12 @@ +* @AUTOMATIC1111 + +# if you were managing a localization and were removed from this file, this is because +# the intended way to do localizations now is via extensions. See: +# https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Developing-extensions +# Make a repo with your localization and since you are still listed as a collaborator +# you can add it to the wiki page yourself. This change is because some people complained +# the git commit log is cluttered with things unrelated to almost everyone and +# because I believe this is the best overall for the project to handle localizations almost +# entirely without my oversight. + + diff --git a/sd/stable-diffusion-webui/LICENSE.txt b/sd/stable-diffusion-webui/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..211d32e752cb61bd056436e8f7a806f12a626bb7 --- /dev/null +++ b/sd/stable-diffusion-webui/LICENSE.txt @@ -0,0 +1,663 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (c) 2023 AUTOMATIC1111 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/sd/stable-diffusion-webui/configs/alt-diffusion-inference.yaml b/sd/stable-diffusion-webui/configs/alt-diffusion-inference.yaml new file mode 100644 index 0000000000000000000000000000000000000000..cfbee72d71bfd7deed2075e423ca51bd1da0521c --- /dev/null +++ b/sd/stable-diffusion-webui/configs/alt-diffusion-inference.yaml @@ -0,0 +1,72 @@ +model: + base_learning_rate: 1.0e-04 + target: ldm.models.diffusion.ddpm.LatentDiffusion + params: + linear_start: 0.00085 + linear_end: 0.0120 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + image_size: 64 + channels: 4 + cond_stage_trainable: false # Note: different from the one we trained before + conditioning_key: crossattn + monitor: val/loss_simple_ema + scale_factor: 0.18215 + use_ema: False + + scheduler_config: # 10000 warmup steps + target: ldm.lr_scheduler.LambdaLinearScheduler + params: + warm_up_steps: [ 10000 ] + cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases + f_start: [ 1.e-6 ] + f_max: [ 1. ] + f_min: [ 1. ] + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + image_size: 32 # unused + in_channels: 4 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_heads: 8 + use_spatial_transformer: True + transformer_depth: 1 + context_dim: 768 + use_checkpoint: True + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: modules.xlmr.BertSeriesModelWithTransformation + params: + name: "XLMR-Large" \ No newline at end of file diff --git a/sd/stable-diffusion-webui/configs/instruct-pix2pix.yaml b/sd/stable-diffusion-webui/configs/instruct-pix2pix.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4e896879dd7ac5697b89cb323ec43eb41c03596c --- /dev/null +++ b/sd/stable-diffusion-webui/configs/instruct-pix2pix.yaml @@ -0,0 +1,98 @@ +# File modified by authors of InstructPix2Pix from original (https://github.com/CompVis/stable-diffusion). +# See more details in LICENSE. + +model: + base_learning_rate: 1.0e-04 + target: modules.models.diffusion.ddpm_edit.LatentDiffusion + params: + linear_start: 0.00085 + linear_end: 0.0120 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: edited + cond_stage_key: edit + # image_size: 64 + # image_size: 32 + image_size: 16 + channels: 4 + cond_stage_trainable: false # Note: different from the one we trained before + conditioning_key: hybrid + monitor: val/loss_simple_ema + scale_factor: 0.18215 + use_ema: false + + scheduler_config: # 10000 warmup steps + target: ldm.lr_scheduler.LambdaLinearScheduler + params: + warm_up_steps: [ 0 ] + cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases + f_start: [ 1.e-6 ] + f_max: [ 1. ] + f_min: [ 1. ] + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + image_size: 32 # unused + in_channels: 8 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_heads: 8 + use_spatial_transformer: True + transformer_depth: 1 + context_dim: 768 + use_checkpoint: True + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenCLIPEmbedder + +data: + target: main.DataModuleFromConfig + params: + batch_size: 128 + num_workers: 1 + wrap: false + validation: + target: edit_dataset.EditDataset + params: + path: data/clip-filtered-dataset + cache_dir: data/ + cache_name: data_10k + split: val + min_text_sim: 0.2 + min_image_sim: 0.75 + min_direction_sim: 0.2 + max_samples_per_prompt: 1 + min_resize_res: 512 + max_resize_res: 512 + crop_res: 512 + output_as_edit: False + real_input: True diff --git a/sd/stable-diffusion-webui/configs/v1-inference.yaml b/sd/stable-diffusion-webui/configs/v1-inference.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d4effe569e897369918625f9d8be5603a0e6a0d6 --- /dev/null +++ b/sd/stable-diffusion-webui/configs/v1-inference.yaml @@ -0,0 +1,70 @@ +model: + base_learning_rate: 1.0e-04 + target: ldm.models.diffusion.ddpm.LatentDiffusion + params: + linear_start: 0.00085 + linear_end: 0.0120 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + image_size: 64 + channels: 4 + cond_stage_trainable: false # Note: different from the one we trained before + conditioning_key: crossattn + monitor: val/loss_simple_ema + scale_factor: 0.18215 + use_ema: False + + scheduler_config: # 10000 warmup steps + target: ldm.lr_scheduler.LambdaLinearScheduler + params: + warm_up_steps: [ 10000 ] + cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases + f_start: [ 1.e-6 ] + f_max: [ 1. ] + f_min: [ 1. ] + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + image_size: 32 # unused + in_channels: 4 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_heads: 8 + use_spatial_transformer: True + transformer_depth: 1 + context_dim: 768 + use_checkpoint: True + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenCLIPEmbedder diff --git a/sd/stable-diffusion-webui/configs/v1-inpainting-inference.yaml b/sd/stable-diffusion-webui/configs/v1-inpainting-inference.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f9eec37d24bce33ce92320a782d16ae72308190a --- /dev/null +++ b/sd/stable-diffusion-webui/configs/v1-inpainting-inference.yaml @@ -0,0 +1,70 @@ +model: + base_learning_rate: 7.5e-05 + target: ldm.models.diffusion.ddpm.LatentInpaintDiffusion + params: + linear_start: 0.00085 + linear_end: 0.0120 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + image_size: 64 + channels: 4 + cond_stage_trainable: false # Note: different from the one we trained before + conditioning_key: hybrid # important + monitor: val/loss_simple_ema + scale_factor: 0.18215 + finetune_keys: null + + scheduler_config: # 10000 warmup steps + target: ldm.lr_scheduler.LambdaLinearScheduler + params: + warm_up_steps: [ 2500 ] # NOTE for resuming. use 10000 if starting from scratch + cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases + f_start: [ 1.e-6 ] + f_max: [ 1. ] + f_min: [ 1. ] + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + image_size: 32 # unused + in_channels: 9 # 4 data + 4 downscaled image + 1 mask + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_heads: 8 + use_spatial_transformer: True + transformer_depth: 1 + context_dim: 768 + use_checkpoint: True + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenCLIPEmbedder diff --git a/sd/stable-diffusion-webui/embeddings/Place Textual Inversion embeddings here.txt b/sd/stable-diffusion-webui/embeddings/Place Textual Inversion embeddings here.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stable-diffusion-webui/environment-wsl2.yaml b/sd/stable-diffusion-webui/environment-wsl2.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f88727507835d96bfbbfae3ece2996e8506e3760 --- /dev/null +++ b/sd/stable-diffusion-webui/environment-wsl2.yaml @@ -0,0 +1,11 @@ +name: automatic +channels: + - pytorch + - defaults +dependencies: + - python=3.10 + - pip=22.2.2 + - cudatoolkit=11.3 + - pytorch=1.12.1 + - torchvision=0.13.1 + - numpy=1.23.1 \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions-builtin/LDSR/ldsr_model_arch.py b/sd/stable-diffusion-webui/extensions-builtin/LDSR/ldsr_model_arch.py new file mode 100644 index 0000000000000000000000000000000000000000..bc11cc6e4821bf110ada3c61c0fef35be7f548e8 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/LDSR/ldsr_model_arch.py @@ -0,0 +1,253 @@ +import os +import gc +import time + +import numpy as np +import torch +import torchvision +from PIL import Image +from einops import rearrange, repeat +from omegaconf import OmegaConf +import safetensors.torch + +from ldm.models.diffusion.ddim import DDIMSampler +from ldm.util import instantiate_from_config, ismap +from modules import shared, sd_hijack + +cached_ldsr_model: torch.nn.Module = None + + +# Create LDSR Class +class LDSR: + def load_model_from_config(self, half_attention): + global cached_ldsr_model + + if shared.opts.ldsr_cached and cached_ldsr_model is not None: + print("Loading model from cache") + model: torch.nn.Module = cached_ldsr_model + else: + print(f"Loading model from {self.modelPath}") + _, extension = os.path.splitext(self.modelPath) + if extension.lower() == ".safetensors": + pl_sd = safetensors.torch.load_file(self.modelPath, device="cpu") + else: + pl_sd = torch.load(self.modelPath, map_location="cpu") + sd = pl_sd["state_dict"] if "state_dict" in pl_sd else pl_sd + config = OmegaConf.load(self.yamlPath) + config.model.target = "ldm.models.diffusion.ddpm.LatentDiffusionV1" + model: torch.nn.Module = instantiate_from_config(config.model) + model.load_state_dict(sd, strict=False) + model = model.to(shared.device) + if half_attention: + model = model.half() + if shared.cmd_opts.opt_channelslast: + model = model.to(memory_format=torch.channels_last) + + sd_hijack.model_hijack.hijack(model) # apply optimization + model.eval() + + if shared.opts.ldsr_cached: + cached_ldsr_model = model + + return {"model": model} + + def __init__(self, model_path, yaml_path): + self.modelPath = model_path + self.yamlPath = yaml_path + + @staticmethod + def run(model, selected_path, custom_steps, eta): + example = get_cond(selected_path) + + n_runs = 1 + guider = None + ckwargs = None + ddim_use_x0_pred = False + temperature = 1. + eta = eta + custom_shape = None + + height, width = example["image"].shape[1:3] + split_input = height >= 128 and width >= 128 + + if split_input: + ks = 128 + stride = 64 + vqf = 4 # + model.split_input_params = {"ks": (ks, ks), "stride": (stride, stride), + "vqf": vqf, + "patch_distributed_vq": True, + "tie_braker": False, + "clip_max_weight": 0.5, + "clip_min_weight": 0.01, + "clip_max_tie_weight": 0.5, + "clip_min_tie_weight": 0.01} + else: + if hasattr(model, "split_input_params"): + delattr(model, "split_input_params") + + x_t = None + logs = None + for n in range(n_runs): + if custom_shape is not None: + x_t = torch.randn(1, custom_shape[1], custom_shape[2], custom_shape[3]).to(model.device) + x_t = repeat(x_t, '1 c h w -> b c h w', b=custom_shape[0]) + + logs = make_convolutional_sample(example, model, + custom_steps=custom_steps, + eta=eta, quantize_x0=False, + custom_shape=custom_shape, + temperature=temperature, noise_dropout=0., + corrector=guider, corrector_kwargs=ckwargs, x_T=x_t, + ddim_use_x0_pred=ddim_use_x0_pred + ) + return logs + + def super_resolution(self, image, steps=100, target_scale=2, half_attention=False): + model = self.load_model_from_config(half_attention) + + # Run settings + diffusion_steps = int(steps) + eta = 1.0 + + down_sample_method = 'Lanczos' + + gc.collect() + if torch.cuda.is_available: + torch.cuda.empty_cache() + + im_og = image + width_og, height_og = im_og.size + # If we can adjust the max upscale size, then the 4 below should be our variable + down_sample_rate = target_scale / 4 + wd = width_og * down_sample_rate + hd = height_og * down_sample_rate + width_downsampled_pre = int(np.ceil(wd)) + height_downsampled_pre = int(np.ceil(hd)) + + if down_sample_rate != 1: + print( + f'Downsampling from [{width_og}, {height_og}] to [{width_downsampled_pre}, {height_downsampled_pre}]') + im_og = im_og.resize((width_downsampled_pre, height_downsampled_pre), Image.LANCZOS) + else: + print(f"Down sample rate is 1 from {target_scale} / 4 (Not downsampling)") + + # pad width and height to multiples of 64, pads with the edge values of image to avoid artifacts + pad_w, pad_h = np.max(((2, 2), np.ceil(np.array(im_og.size) / 64).astype(int)), axis=0) * 64 - im_og.size + im_padded = Image.fromarray(np.pad(np.array(im_og), ((0, pad_h), (0, pad_w), (0, 0)), mode='edge')) + + logs = self.run(model["model"], im_padded, diffusion_steps, eta) + + sample = logs["sample"] + sample = sample.detach().cpu() + sample = torch.clamp(sample, -1., 1.) + sample = (sample + 1.) / 2. * 255 + sample = sample.numpy().astype(np.uint8) + sample = np.transpose(sample, (0, 2, 3, 1)) + a = Image.fromarray(sample[0]) + + # remove padding + a = a.crop((0, 0) + tuple(np.array(im_og.size) * 4)) + + del model + gc.collect() + if torch.cuda.is_available: + torch.cuda.empty_cache() + + return a + + +def get_cond(selected_path): + example = dict() + up_f = 4 + c = selected_path.convert('RGB') + c = torch.unsqueeze(torchvision.transforms.ToTensor()(c), 0) + c_up = torchvision.transforms.functional.resize(c, size=[up_f * c.shape[2], up_f * c.shape[3]], + antialias=True) + c_up = rearrange(c_up, '1 c h w -> 1 h w c') + c = rearrange(c, '1 c h w -> 1 h w c') + c = 2. * c - 1. + + c = c.to(shared.device) + example["LR_image"] = c + example["image"] = c_up + + return example + + +@torch.no_grad() +def convsample_ddim(model, cond, steps, shape, eta=1.0, callback=None, normals_sequence=None, + mask=None, x0=None, quantize_x0=False, temperature=1., score_corrector=None, + corrector_kwargs=None, x_t=None + ): + ddim = DDIMSampler(model) + bs = shape[0] + shape = shape[1:] + print(f"Sampling with eta = {eta}; steps: {steps}") + samples, intermediates = ddim.sample(steps, batch_size=bs, shape=shape, conditioning=cond, callback=callback, + normals_sequence=normals_sequence, quantize_x0=quantize_x0, eta=eta, + mask=mask, x0=x0, temperature=temperature, verbose=False, + score_corrector=score_corrector, + corrector_kwargs=corrector_kwargs, x_t=x_t) + + return samples, intermediates + + +@torch.no_grad() +def make_convolutional_sample(batch, model, custom_steps=None, eta=1.0, quantize_x0=False, custom_shape=None, temperature=1., noise_dropout=0., corrector=None, + corrector_kwargs=None, x_T=None, ddim_use_x0_pred=False): + log = dict() + + z, c, x, xrec, xc = model.get_input(batch, model.first_stage_key, + return_first_stage_outputs=True, + force_c_encode=not (hasattr(model, 'split_input_params') + and model.cond_stage_key == 'coordinates_bbox'), + return_original_cond=True) + + if custom_shape is not None: + z = torch.randn(custom_shape) + print(f"Generating {custom_shape[0]} samples of shape {custom_shape[1:]}") + + z0 = None + + log["input"] = x + log["reconstruction"] = xrec + + if ismap(xc): + log["original_conditioning"] = model.to_rgb(xc) + if hasattr(model, 'cond_stage_key'): + log[model.cond_stage_key] = model.to_rgb(xc) + + else: + log["original_conditioning"] = xc if xc is not None else torch.zeros_like(x) + if model.cond_stage_model: + log[model.cond_stage_key] = xc if xc is not None else torch.zeros_like(x) + if model.cond_stage_key == 'class_label': + log[model.cond_stage_key] = xc[model.cond_stage_key] + + with model.ema_scope("Plotting"): + t0 = time.time() + + sample, intermediates = convsample_ddim(model, c, steps=custom_steps, shape=z.shape, + eta=eta, + quantize_x0=quantize_x0, mask=None, x0=z0, + temperature=temperature, score_corrector=corrector, corrector_kwargs=corrector_kwargs, + x_t=x_T) + t1 = time.time() + + if ddim_use_x0_pred: + sample = intermediates['pred_x0'][-1] + + x_sample = model.decode_first_stage(sample) + + try: + x_sample_noquant = model.decode_first_stage(sample, force_not_quantize=True) + log["sample_noquant"] = x_sample_noquant + log["sample_diff"] = torch.abs(x_sample_noquant - x_sample) + except: + pass + + log["sample"] = x_sample + log["time"] = t1 - t0 + + return log diff --git a/sd/stable-diffusion-webui/extensions-builtin/LDSR/preload.py b/sd/stable-diffusion-webui/extensions-builtin/LDSR/preload.py new file mode 100644 index 0000000000000000000000000000000000000000..cfd478d545ed12ef74e73fa40b6defe0156859da --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/LDSR/preload.py @@ -0,0 +1,6 @@ +import os +from modules import paths + + +def preload(parser): + parser.add_argument("--ldsr-models-path", type=str, help="Path to directory with LDSR model file(s).", default=os.path.join(paths.models_path, 'LDSR')) diff --git a/sd/stable-diffusion-webui/extensions-builtin/LDSR/scripts/ldsr_model.py b/sd/stable-diffusion-webui/extensions-builtin/LDSR/scripts/ldsr_model.py new file mode 100644 index 0000000000000000000000000000000000000000..b8cff29b9f4ca56e3a9f4b1ac8e150abb1a0ff30 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/LDSR/scripts/ldsr_model.py @@ -0,0 +1,69 @@ +import os +import sys +import traceback + +from basicsr.utils.download_util import load_file_from_url + +from modules.upscaler import Upscaler, UpscalerData +from ldsr_model_arch import LDSR +from modules import shared, script_callbacks +import sd_hijack_autoencoder, sd_hijack_ddpm_v1 + + +class UpscalerLDSR(Upscaler): + def __init__(self, user_path): + self.name = "LDSR" + self.user_path = user_path + self.model_url = "https://heibox.uni-heidelberg.de/f/578df07c8fc04ffbadf3/?dl=1" + self.yaml_url = "https://heibox.uni-heidelberg.de/f/31a76b13ea27482981b4/?dl=1" + super().__init__() + scaler_data = UpscalerData("LDSR", None, self) + self.scalers = [scaler_data] + + def load_model(self, path: str): + # Remove incorrect project.yaml file if too big + yaml_path = os.path.join(self.model_path, "project.yaml") + old_model_path = os.path.join(self.model_path, "model.pth") + new_model_path = os.path.join(self.model_path, "model.ckpt") + safetensors_model_path = os.path.join(self.model_path, "model.safetensors") + if os.path.exists(yaml_path): + statinfo = os.stat(yaml_path) + if statinfo.st_size >= 10485760: + print("Removing invalid LDSR YAML file.") + os.remove(yaml_path) + if os.path.exists(old_model_path): + print("Renaming model from model.pth to model.ckpt") + os.rename(old_model_path, new_model_path) + if os.path.exists(safetensors_model_path): + model = safetensors_model_path + else: + model = load_file_from_url(url=self.model_url, model_dir=self.model_path, + file_name="model.ckpt", progress=True) + yaml = load_file_from_url(url=self.yaml_url, model_dir=self.model_path, + file_name="project.yaml", progress=True) + + try: + return LDSR(model, yaml) + + except Exception: + print("Error importing LDSR:", file=sys.stderr) + print(traceback.format_exc(), file=sys.stderr) + return None + + def do_upscale(self, img, path): + ldsr = self.load_model(path) + if ldsr is None: + print("NO LDSR!") + return img + ddim_steps = shared.opts.ldsr_steps + return ldsr.super_resolution(img, ddim_steps, self.scale) + + +def on_ui_settings(): + import gradio as gr + + shared.opts.add_option("ldsr_steps", shared.OptionInfo(100, "LDSR processing steps. Lower = faster", gr.Slider, {"minimum": 1, "maximum": 200, "step": 1}, section=('upscaling', "Upscaling"))) + shared.opts.add_option("ldsr_cached", shared.OptionInfo(False, "Cache LDSR model in memory", gr.Checkbox, {"interactive": True}, section=('upscaling', "Upscaling"))) + + +script_callbacks.on_ui_settings(on_ui_settings) diff --git a/sd/stable-diffusion-webui/extensions-builtin/LDSR/sd_hijack_autoencoder.py b/sd/stable-diffusion-webui/extensions-builtin/LDSR/sd_hijack_autoencoder.py new file mode 100644 index 0000000000000000000000000000000000000000..8e03c7f898988c237c714ed949610f5035b30b50 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/LDSR/sd_hijack_autoencoder.py @@ -0,0 +1,286 @@ +# The content of this file comes from the ldm/models/autoencoder.py file of the compvis/stable-diffusion repo +# The VQModel & VQModelInterface were subsequently removed from ldm/models/autoencoder.py when we moved to the stability-ai/stablediffusion repo +# As the LDSR upscaler relies on VQModel & VQModelInterface, the hijack aims to put them back into the ldm.models.autoencoder + +import torch +import pytorch_lightning as pl +import torch.nn.functional as F +from contextlib import contextmanager +from taming.modules.vqvae.quantize import VectorQuantizer2 as VectorQuantizer +from ldm.modules.diffusionmodules.model import Encoder, Decoder +from ldm.util import instantiate_from_config + +import ldm.models.autoencoder + +class VQModel(pl.LightningModule): + def __init__(self, + ddconfig, + lossconfig, + n_embed, + embed_dim, + ckpt_path=None, + ignore_keys=[], + image_key="image", + colorize_nlabels=None, + monitor=None, + batch_resize_range=None, + scheduler_config=None, + lr_g_factor=1.0, + remap=None, + sane_index_shape=False, # tell vector quantizer to return indices as bhw + use_ema=False + ): + super().__init__() + self.embed_dim = embed_dim + self.n_embed = n_embed + self.image_key = image_key + self.encoder = Encoder(**ddconfig) + self.decoder = Decoder(**ddconfig) + self.loss = instantiate_from_config(lossconfig) + self.quantize = VectorQuantizer(n_embed, embed_dim, beta=0.25, + remap=remap, + sane_index_shape=sane_index_shape) + self.quant_conv = torch.nn.Conv2d(ddconfig["z_channels"], embed_dim, 1) + self.post_quant_conv = torch.nn.Conv2d(embed_dim, ddconfig["z_channels"], 1) + if colorize_nlabels is not None: + assert type(colorize_nlabels)==int + self.register_buffer("colorize", torch.randn(3, colorize_nlabels, 1, 1)) + if monitor is not None: + self.monitor = monitor + self.batch_resize_range = batch_resize_range + if self.batch_resize_range is not None: + print(f"{self.__class__.__name__}: Using per-batch resizing in range {batch_resize_range}.") + + self.use_ema = use_ema + if self.use_ema: + self.model_ema = LitEma(self) + print(f"Keeping EMAs of {len(list(self.model_ema.buffers()))}.") + + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys) + self.scheduler_config = scheduler_config + self.lr_g_factor = lr_g_factor + + @contextmanager + def ema_scope(self, context=None): + if self.use_ema: + self.model_ema.store(self.parameters()) + self.model_ema.copy_to(self) + if context is not None: + print(f"{context}: Switched to EMA weights") + try: + yield None + finally: + if self.use_ema: + self.model_ema.restore(self.parameters()) + if context is not None: + print(f"{context}: Restored training weights") + + def init_from_ckpt(self, path, ignore_keys=list()): + sd = torch.load(path, map_location="cpu")["state_dict"] + keys = list(sd.keys()) + for k in keys: + for ik in ignore_keys: + if k.startswith(ik): + print("Deleting key {} from state_dict.".format(k)) + del sd[k] + missing, unexpected = self.load_state_dict(sd, strict=False) + print(f"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys") + if len(missing) > 0: + print(f"Missing Keys: {missing}") + print(f"Unexpected Keys: {unexpected}") + + def on_train_batch_end(self, *args, **kwargs): + if self.use_ema: + self.model_ema(self) + + def encode(self, x): + h = self.encoder(x) + h = self.quant_conv(h) + quant, emb_loss, info = self.quantize(h) + return quant, emb_loss, info + + def encode_to_prequant(self, x): + h = self.encoder(x) + h = self.quant_conv(h) + return h + + def decode(self, quant): + quant = self.post_quant_conv(quant) + dec = self.decoder(quant) + return dec + + def decode_code(self, code_b): + quant_b = self.quantize.embed_code(code_b) + dec = self.decode(quant_b) + return dec + + def forward(self, input, return_pred_indices=False): + quant, diff, (_,_,ind) = self.encode(input) + dec = self.decode(quant) + if return_pred_indices: + return dec, diff, ind + return dec, diff + + def get_input(self, batch, k): + x = batch[k] + if len(x.shape) == 3: + x = x[..., None] + x = x.permute(0, 3, 1, 2).to(memory_format=torch.contiguous_format).float() + if self.batch_resize_range is not None: + lower_size = self.batch_resize_range[0] + upper_size = self.batch_resize_range[1] + if self.global_step <= 4: + # do the first few batches with max size to avoid later oom + new_resize = upper_size + else: + new_resize = np.random.choice(np.arange(lower_size, upper_size+16, 16)) + if new_resize != x.shape[2]: + x = F.interpolate(x, size=new_resize, mode="bicubic") + x = x.detach() + return x + + def training_step(self, batch, batch_idx, optimizer_idx): + # https://github.com/pytorch/pytorch/issues/37142 + # try not to fool the heuristics + x = self.get_input(batch, self.image_key) + xrec, qloss, ind = self(x, return_pred_indices=True) + + if optimizer_idx == 0: + # autoencode + aeloss, log_dict_ae = self.loss(qloss, x, xrec, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train", + predicted_indices=ind) + + self.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return aeloss + + if optimizer_idx == 1: + # discriminator + discloss, log_dict_disc = self.loss(qloss, x, xrec, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + self.log_dict(log_dict_disc, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return discloss + + def validation_step(self, batch, batch_idx): + log_dict = self._validation_step(batch, batch_idx) + with self.ema_scope(): + log_dict_ema = self._validation_step(batch, batch_idx, suffix="_ema") + return log_dict + + def _validation_step(self, batch, batch_idx, suffix=""): + x = self.get_input(batch, self.image_key) + xrec, qloss, ind = self(x, return_pred_indices=True) + aeloss, log_dict_ae = self.loss(qloss, x, xrec, 0, + self.global_step, + last_layer=self.get_last_layer(), + split="val"+suffix, + predicted_indices=ind + ) + + discloss, log_dict_disc = self.loss(qloss, x, xrec, 1, + self.global_step, + last_layer=self.get_last_layer(), + split="val"+suffix, + predicted_indices=ind + ) + rec_loss = log_dict_ae[f"val{suffix}/rec_loss"] + self.log(f"val{suffix}/rec_loss", rec_loss, + prog_bar=True, logger=True, on_step=False, on_epoch=True, sync_dist=True) + self.log(f"val{suffix}/aeloss", aeloss, + prog_bar=True, logger=True, on_step=False, on_epoch=True, sync_dist=True) + if version.parse(pl.__version__) >= version.parse('1.4.0'): + del log_dict_ae[f"val{suffix}/rec_loss"] + self.log_dict(log_dict_ae) + self.log_dict(log_dict_disc) + return self.log_dict + + def configure_optimizers(self): + lr_d = self.learning_rate + lr_g = self.lr_g_factor*self.learning_rate + print("lr_d", lr_d) + print("lr_g", lr_g) + opt_ae = torch.optim.Adam(list(self.encoder.parameters())+ + list(self.decoder.parameters())+ + list(self.quantize.parameters())+ + list(self.quant_conv.parameters())+ + list(self.post_quant_conv.parameters()), + lr=lr_g, betas=(0.5, 0.9)) + opt_disc = torch.optim.Adam(self.loss.discriminator.parameters(), + lr=lr_d, betas=(0.5, 0.9)) + + if self.scheduler_config is not None: + scheduler = instantiate_from_config(self.scheduler_config) + + print("Setting up LambdaLR scheduler...") + scheduler = [ + { + 'scheduler': LambdaLR(opt_ae, lr_lambda=scheduler.schedule), + 'interval': 'step', + 'frequency': 1 + }, + { + 'scheduler': LambdaLR(opt_disc, lr_lambda=scheduler.schedule), + 'interval': 'step', + 'frequency': 1 + }, + ] + return [opt_ae, opt_disc], scheduler + return [opt_ae, opt_disc], [] + + def get_last_layer(self): + return self.decoder.conv_out.weight + + def log_images(self, batch, only_inputs=False, plot_ema=False, **kwargs): + log = dict() + x = self.get_input(batch, self.image_key) + x = x.to(self.device) + if only_inputs: + log["inputs"] = x + return log + xrec, _ = self(x) + if x.shape[1] > 3: + # colorize with random projection + assert xrec.shape[1] > 3 + x = self.to_rgb(x) + xrec = self.to_rgb(xrec) + log["inputs"] = x + log["reconstructions"] = xrec + if plot_ema: + with self.ema_scope(): + xrec_ema, _ = self(x) + if x.shape[1] > 3: xrec_ema = self.to_rgb(xrec_ema) + log["reconstructions_ema"] = xrec_ema + return log + + def to_rgb(self, x): + assert self.image_key == "segmentation" + if not hasattr(self, "colorize"): + self.register_buffer("colorize", torch.randn(3, x.shape[1], 1, 1).to(x)) + x = F.conv2d(x, weight=self.colorize) + x = 2.*(x-x.min())/(x.max()-x.min()) - 1. + return x + + +class VQModelInterface(VQModel): + def __init__(self, embed_dim, *args, **kwargs): + super().__init__(embed_dim=embed_dim, *args, **kwargs) + self.embed_dim = embed_dim + + def encode(self, x): + h = self.encoder(x) + h = self.quant_conv(h) + return h + + def decode(self, h, force_not_quantize=False): + # also go through quantization layer + if not force_not_quantize: + quant, emb_loss, info = self.quantize(h) + else: + quant = h + quant = self.post_quant_conv(quant) + dec = self.decoder(quant) + return dec + +setattr(ldm.models.autoencoder, "VQModel", VQModel) +setattr(ldm.models.autoencoder, "VQModelInterface", VQModelInterface) diff --git a/sd/stable-diffusion-webui/extensions-builtin/LDSR/sd_hijack_ddpm_v1.py b/sd/stable-diffusion-webui/extensions-builtin/LDSR/sd_hijack_ddpm_v1.py new file mode 100644 index 0000000000000000000000000000000000000000..5c0488e5f6fcea41e7f9fa25070e38fbfe656478 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/LDSR/sd_hijack_ddpm_v1.py @@ -0,0 +1,1449 @@ +# This script is copied from the compvis/stable-diffusion repo (aka the SD V1 repo) +# Original filename: ldm/models/diffusion/ddpm.py +# The purpose to reinstate the old DDPM logic which works with VQ, whereas the V2 one doesn't +# Some models such as LDSR require VQ to work correctly +# The classes are suffixed with "V1" and added back to the "ldm.models.diffusion.ddpm" module + +import torch +import torch.nn as nn +import numpy as np +import pytorch_lightning as pl +from torch.optim.lr_scheduler import LambdaLR +from einops import rearrange, repeat +from contextlib import contextmanager +from functools import partial +from tqdm import tqdm +from torchvision.utils import make_grid +from pytorch_lightning.utilities.distributed import rank_zero_only + +from ldm.util import log_txt_as_img, exists, default, ismap, isimage, mean_flat, count_params, instantiate_from_config +from ldm.modules.ema import LitEma +from ldm.modules.distributions.distributions import normal_kl, DiagonalGaussianDistribution +from ldm.models.autoencoder import VQModelInterface, IdentityFirstStage, AutoencoderKL +from ldm.modules.diffusionmodules.util import make_beta_schedule, extract_into_tensor, noise_like +from ldm.models.diffusion.ddim import DDIMSampler + +import ldm.models.diffusion.ddpm + +__conditioning_keys__ = {'concat': 'c_concat', + 'crossattn': 'c_crossattn', + 'adm': 'y'} + + +def disabled_train(self, mode=True): + """Overwrite model.train with this function to make sure train/eval mode + does not change anymore.""" + return self + + +def uniform_on_device(r1, r2, shape, device): + return (r1 - r2) * torch.rand(*shape, device=device) + r2 + + +class DDPMV1(pl.LightningModule): + # classic DDPM with Gaussian diffusion, in image space + def __init__(self, + unet_config, + timesteps=1000, + beta_schedule="linear", + loss_type="l2", + ckpt_path=None, + ignore_keys=[], + load_only_unet=False, + monitor="val/loss", + use_ema=True, + first_stage_key="image", + image_size=256, + channels=3, + log_every_t=100, + clip_denoised=True, + linear_start=1e-4, + linear_end=2e-2, + cosine_s=8e-3, + given_betas=None, + original_elbo_weight=0., + v_posterior=0., # weight for choosing posterior variance as sigma = (1-v) * beta_tilde + v * beta + l_simple_weight=1., + conditioning_key=None, + parameterization="eps", # all assuming fixed variance schedules + scheduler_config=None, + use_positional_encodings=False, + learn_logvar=False, + logvar_init=0., + ): + super().__init__() + assert parameterization in ["eps", "x0"], 'currently only supporting "eps" and "x0"' + self.parameterization = parameterization + print(f"{self.__class__.__name__}: Running in {self.parameterization}-prediction mode") + self.cond_stage_model = None + self.clip_denoised = clip_denoised + self.log_every_t = log_every_t + self.first_stage_key = first_stage_key + self.image_size = image_size # try conv? + self.channels = channels + self.use_positional_encodings = use_positional_encodings + self.model = DiffusionWrapperV1(unet_config, conditioning_key) + count_params(self.model, verbose=True) + self.use_ema = use_ema + if self.use_ema: + self.model_ema = LitEma(self.model) + print(f"Keeping EMAs of {len(list(self.model_ema.buffers()))}.") + + self.use_scheduler = scheduler_config is not None + if self.use_scheduler: + self.scheduler_config = scheduler_config + + self.v_posterior = v_posterior + self.original_elbo_weight = original_elbo_weight + self.l_simple_weight = l_simple_weight + + if monitor is not None: + self.monitor = monitor + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys, only_model=load_only_unet) + + self.register_schedule(given_betas=given_betas, beta_schedule=beta_schedule, timesteps=timesteps, + linear_start=linear_start, linear_end=linear_end, cosine_s=cosine_s) + + self.loss_type = loss_type + + self.learn_logvar = learn_logvar + self.logvar = torch.full(fill_value=logvar_init, size=(self.num_timesteps,)) + if self.learn_logvar: + self.logvar = nn.Parameter(self.logvar, requires_grad=True) + + + def register_schedule(self, given_betas=None, beta_schedule="linear", timesteps=1000, + linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + if exists(given_betas): + betas = given_betas + else: + betas = make_beta_schedule(beta_schedule, timesteps, linear_start=linear_start, linear_end=linear_end, + cosine_s=cosine_s) + alphas = 1. - betas + alphas_cumprod = np.cumprod(alphas, axis=0) + alphas_cumprod_prev = np.append(1., alphas_cumprod[:-1]) + + timesteps, = betas.shape + self.num_timesteps = int(timesteps) + self.linear_start = linear_start + self.linear_end = linear_end + assert alphas_cumprod.shape[0] == self.num_timesteps, 'alphas have to be defined for each timestep' + + to_torch = partial(torch.tensor, dtype=torch.float32) + + self.register_buffer('betas', to_torch(betas)) + self.register_buffer('alphas_cumprod', to_torch(alphas_cumprod)) + self.register_buffer('alphas_cumprod_prev', to_torch(alphas_cumprod_prev)) + + # calculations for diffusion q(x_t | x_{t-1}) and others + self.register_buffer('sqrt_alphas_cumprod', to_torch(np.sqrt(alphas_cumprod))) + self.register_buffer('sqrt_one_minus_alphas_cumprod', to_torch(np.sqrt(1. - alphas_cumprod))) + self.register_buffer('log_one_minus_alphas_cumprod', to_torch(np.log(1. - alphas_cumprod))) + self.register_buffer('sqrt_recip_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod))) + self.register_buffer('sqrt_recipm1_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod - 1))) + + # calculations for posterior q(x_{t-1} | x_t, x_0) + posterior_variance = (1 - self.v_posterior) * betas * (1. - alphas_cumprod_prev) / ( + 1. - alphas_cumprod) + self.v_posterior * betas + # above: equal to 1. / (1. / (1. - alpha_cumprod_tm1) + alpha_t / beta_t) + self.register_buffer('posterior_variance', to_torch(posterior_variance)) + # below: log calculation clipped because the posterior variance is 0 at the beginning of the diffusion chain + self.register_buffer('posterior_log_variance_clipped', to_torch(np.log(np.maximum(posterior_variance, 1e-20)))) + self.register_buffer('posterior_mean_coef1', to_torch( + betas * np.sqrt(alphas_cumprod_prev) / (1. - alphas_cumprod))) + self.register_buffer('posterior_mean_coef2', to_torch( + (1. - alphas_cumprod_prev) * np.sqrt(alphas) / (1. - alphas_cumprod))) + + if self.parameterization == "eps": + lvlb_weights = self.betas ** 2 / ( + 2 * self.posterior_variance * to_torch(alphas) * (1 - self.alphas_cumprod)) + elif self.parameterization == "x0": + lvlb_weights = 0.5 * np.sqrt(torch.Tensor(alphas_cumprod)) / (2. * 1 - torch.Tensor(alphas_cumprod)) + else: + raise NotImplementedError("mu not supported") + # TODO how to choose this term + lvlb_weights[0] = lvlb_weights[1] + self.register_buffer('lvlb_weights', lvlb_weights, persistent=False) + assert not torch.isnan(self.lvlb_weights).all() + + @contextmanager + def ema_scope(self, context=None): + if self.use_ema: + self.model_ema.store(self.model.parameters()) + self.model_ema.copy_to(self.model) + if context is not None: + print(f"{context}: Switched to EMA weights") + try: + yield None + finally: + if self.use_ema: + self.model_ema.restore(self.model.parameters()) + if context is not None: + print(f"{context}: Restored training weights") + + def init_from_ckpt(self, path, ignore_keys=list(), only_model=False): + sd = torch.load(path, map_location="cpu") + if "state_dict" in list(sd.keys()): + sd = sd["state_dict"] + keys = list(sd.keys()) + for k in keys: + for ik in ignore_keys: + if k.startswith(ik): + print("Deleting key {} from state_dict.".format(k)) + del sd[k] + missing, unexpected = self.load_state_dict(sd, strict=False) if not only_model else self.model.load_state_dict( + sd, strict=False) + print(f"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys") + if len(missing) > 0: + print(f"Missing Keys: {missing}") + if len(unexpected) > 0: + print(f"Unexpected Keys: {unexpected}") + + def q_mean_variance(self, x_start, t): + """ + Get the distribution q(x_t | x_0). + :param x_start: the [N x C x ...] tensor of noiseless inputs. + :param t: the number of diffusion steps (minus 1). Here, 0 means one step. + :return: A tuple (mean, variance, log_variance), all of x_start's shape. + """ + mean = (extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start) + variance = extract_into_tensor(1.0 - self.alphas_cumprod, t, x_start.shape) + log_variance = extract_into_tensor(self.log_one_minus_alphas_cumprod, t, x_start.shape) + return mean, variance, log_variance + + def predict_start_from_noise(self, x_t, t, noise): + return ( + extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - + extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) * noise + ) + + def q_posterior(self, x_start, x_t, t): + posterior_mean = ( + extract_into_tensor(self.posterior_mean_coef1, t, x_t.shape) * x_start + + extract_into_tensor(self.posterior_mean_coef2, t, x_t.shape) * x_t + ) + posterior_variance = extract_into_tensor(self.posterior_variance, t, x_t.shape) + posterior_log_variance_clipped = extract_into_tensor(self.posterior_log_variance_clipped, t, x_t.shape) + return posterior_mean, posterior_variance, posterior_log_variance_clipped + + def p_mean_variance(self, x, t, clip_denoised: bool): + model_out = self.model(x, t) + if self.parameterization == "eps": + x_recon = self.predict_start_from_noise(x, t=t, noise=model_out) + elif self.parameterization == "x0": + x_recon = model_out + if clip_denoised: + x_recon.clamp_(-1., 1.) + + model_mean, posterior_variance, posterior_log_variance = self.q_posterior(x_start=x_recon, x_t=x, t=t) + return model_mean, posterior_variance, posterior_log_variance + + @torch.no_grad() + def p_sample(self, x, t, clip_denoised=True, repeat_noise=False): + b, *_, device = *x.shape, x.device + model_mean, _, model_log_variance = self.p_mean_variance(x=x, t=t, clip_denoised=clip_denoised) + noise = noise_like(x.shape, device, repeat_noise) + # no noise when t == 0 + nonzero_mask = (1 - (t == 0).float()).reshape(b, *((1,) * (len(x.shape) - 1))) + return model_mean + nonzero_mask * (0.5 * model_log_variance).exp() * noise + + @torch.no_grad() + def p_sample_loop(self, shape, return_intermediates=False): + device = self.betas.device + b = shape[0] + img = torch.randn(shape, device=device) + intermediates = [img] + for i in tqdm(reversed(range(0, self.num_timesteps)), desc='Sampling t', total=self.num_timesteps): + img = self.p_sample(img, torch.full((b,), i, device=device, dtype=torch.long), + clip_denoised=self.clip_denoised) + if i % self.log_every_t == 0 or i == self.num_timesteps - 1: + intermediates.append(img) + if return_intermediates: + return img, intermediates + return img + + @torch.no_grad() + def sample(self, batch_size=16, return_intermediates=False): + image_size = self.image_size + channels = self.channels + return self.p_sample_loop((batch_size, channels, image_size, image_size), + return_intermediates=return_intermediates) + + def q_sample(self, x_start, t, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + return (extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start + + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape) * noise) + + def get_loss(self, pred, target, mean=True): + if self.loss_type == 'l1': + loss = (target - pred).abs() + if mean: + loss = loss.mean() + elif self.loss_type == 'l2': + if mean: + loss = torch.nn.functional.mse_loss(target, pred) + else: + loss = torch.nn.functional.mse_loss(target, pred, reduction='none') + else: + raise NotImplementedError("unknown loss type '{loss_type}'") + + return loss + + def p_losses(self, x_start, t, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + x_noisy = self.q_sample(x_start=x_start, t=t, noise=noise) + model_out = self.model(x_noisy, t) + + loss_dict = {} + if self.parameterization == "eps": + target = noise + elif self.parameterization == "x0": + target = x_start + else: + raise NotImplementedError(f"Paramterization {self.parameterization} not yet supported") + + loss = self.get_loss(model_out, target, mean=False).mean(dim=[1, 2, 3]) + + log_prefix = 'train' if self.training else 'val' + + loss_dict.update({f'{log_prefix}/loss_simple': loss.mean()}) + loss_simple = loss.mean() * self.l_simple_weight + + loss_vlb = (self.lvlb_weights[t] * loss).mean() + loss_dict.update({f'{log_prefix}/loss_vlb': loss_vlb}) + + loss = loss_simple + self.original_elbo_weight * loss_vlb + + loss_dict.update({f'{log_prefix}/loss': loss}) + + return loss, loss_dict + + def forward(self, x, *args, **kwargs): + # b, c, h, w, device, img_size, = *x.shape, x.device, self.image_size + # assert h == img_size and w == img_size, f'height and width of image must be {img_size}' + t = torch.randint(0, self.num_timesteps, (x.shape[0],), device=self.device).long() + return self.p_losses(x, t, *args, **kwargs) + + def get_input(self, batch, k): + x = batch[k] + if len(x.shape) == 3: + x = x[..., None] + x = rearrange(x, 'b h w c -> b c h w') + x = x.to(memory_format=torch.contiguous_format).float() + return x + + def shared_step(self, batch): + x = self.get_input(batch, self.first_stage_key) + loss, loss_dict = self(x) + return loss, loss_dict + + def training_step(self, batch, batch_idx): + loss, loss_dict = self.shared_step(batch) + + self.log_dict(loss_dict, prog_bar=True, + logger=True, on_step=True, on_epoch=True) + + self.log("global_step", self.global_step, + prog_bar=True, logger=True, on_step=True, on_epoch=False) + + if self.use_scheduler: + lr = self.optimizers().param_groups[0]['lr'] + self.log('lr_abs', lr, prog_bar=True, logger=True, on_step=True, on_epoch=False) + + return loss + + @torch.no_grad() + def validation_step(self, batch, batch_idx): + _, loss_dict_no_ema = self.shared_step(batch) + with self.ema_scope(): + _, loss_dict_ema = self.shared_step(batch) + loss_dict_ema = {key + '_ema': loss_dict_ema[key] for key in loss_dict_ema} + self.log_dict(loss_dict_no_ema, prog_bar=False, logger=True, on_step=False, on_epoch=True) + self.log_dict(loss_dict_ema, prog_bar=False, logger=True, on_step=False, on_epoch=True) + + def on_train_batch_end(self, *args, **kwargs): + if self.use_ema: + self.model_ema(self.model) + + def _get_rows_from_list(self, samples): + n_imgs_per_row = len(samples) + denoise_grid = rearrange(samples, 'n b c h w -> b n c h w') + denoise_grid = rearrange(denoise_grid, 'b n c h w -> (b n) c h w') + denoise_grid = make_grid(denoise_grid, nrow=n_imgs_per_row) + return denoise_grid + + @torch.no_grad() + def log_images(self, batch, N=8, n_row=2, sample=True, return_keys=None, **kwargs): + log = dict() + x = self.get_input(batch, self.first_stage_key) + N = min(x.shape[0], N) + n_row = min(x.shape[0], n_row) + x = x.to(self.device)[:N] + log["inputs"] = x + + # get diffusion row + diffusion_row = list() + x_start = x[:n_row] + + for t in range(self.num_timesteps): + if t % self.log_every_t == 0 or t == self.num_timesteps - 1: + t = repeat(torch.tensor([t]), '1 -> b', b=n_row) + t = t.to(self.device).long() + noise = torch.randn_like(x_start) + x_noisy = self.q_sample(x_start=x_start, t=t, noise=noise) + diffusion_row.append(x_noisy) + + log["diffusion_row"] = self._get_rows_from_list(diffusion_row) + + if sample: + # get denoise row + with self.ema_scope("Plotting"): + samples, denoise_row = self.sample(batch_size=N, return_intermediates=True) + + log["samples"] = samples + log["denoise_row"] = self._get_rows_from_list(denoise_row) + + if return_keys: + if np.intersect1d(list(log.keys()), return_keys).shape[0] == 0: + return log + else: + return {key: log[key] for key in return_keys} + return log + + def configure_optimizers(self): + lr = self.learning_rate + params = list(self.model.parameters()) + if self.learn_logvar: + params = params + [self.logvar] + opt = torch.optim.AdamW(params, lr=lr) + return opt + + +class LatentDiffusionV1(DDPMV1): + """main class""" + def __init__(self, + first_stage_config, + cond_stage_config, + num_timesteps_cond=None, + cond_stage_key="image", + cond_stage_trainable=False, + concat_mode=True, + cond_stage_forward=None, + conditioning_key=None, + scale_factor=1.0, + scale_by_std=False, + *args, **kwargs): + self.num_timesteps_cond = default(num_timesteps_cond, 1) + self.scale_by_std = scale_by_std + assert self.num_timesteps_cond <= kwargs['timesteps'] + # for backwards compatibility after implementation of DiffusionWrapper + if conditioning_key is None: + conditioning_key = 'concat' if concat_mode else 'crossattn' + if cond_stage_config == '__is_unconditional__': + conditioning_key = None + ckpt_path = kwargs.pop("ckpt_path", None) + ignore_keys = kwargs.pop("ignore_keys", []) + super().__init__(conditioning_key=conditioning_key, *args, **kwargs) + self.concat_mode = concat_mode + self.cond_stage_trainable = cond_stage_trainable + self.cond_stage_key = cond_stage_key + try: + self.num_downs = len(first_stage_config.params.ddconfig.ch_mult) - 1 + except: + self.num_downs = 0 + if not scale_by_std: + self.scale_factor = scale_factor + else: + self.register_buffer('scale_factor', torch.tensor(scale_factor)) + self.instantiate_first_stage(first_stage_config) + self.instantiate_cond_stage(cond_stage_config) + self.cond_stage_forward = cond_stage_forward + self.clip_denoised = False + self.bbox_tokenizer = None + + self.restarted_from_ckpt = False + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys) + self.restarted_from_ckpt = True + + def make_cond_schedule(self, ): + self.cond_ids = torch.full(size=(self.num_timesteps,), fill_value=self.num_timesteps - 1, dtype=torch.long) + ids = torch.round(torch.linspace(0, self.num_timesteps - 1, self.num_timesteps_cond)).long() + self.cond_ids[:self.num_timesteps_cond] = ids + + @rank_zero_only + @torch.no_grad() + def on_train_batch_start(self, batch, batch_idx, dataloader_idx): + # only for very first batch + if self.scale_by_std and self.current_epoch == 0 and self.global_step == 0 and batch_idx == 0 and not self.restarted_from_ckpt: + assert self.scale_factor == 1., 'rather not use custom rescaling and std-rescaling simultaneously' + # set rescale weight to 1./std of encodings + print("### USING STD-RESCALING ###") + x = super().get_input(batch, self.first_stage_key) + x = x.to(self.device) + encoder_posterior = self.encode_first_stage(x) + z = self.get_first_stage_encoding(encoder_posterior).detach() + del self.scale_factor + self.register_buffer('scale_factor', 1. / z.flatten().std()) + print(f"setting self.scale_factor to {self.scale_factor}") + print("### USING STD-RESCALING ###") + + def register_schedule(self, + given_betas=None, beta_schedule="linear", timesteps=1000, + linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + super().register_schedule(given_betas, beta_schedule, timesteps, linear_start, linear_end, cosine_s) + + self.shorten_cond_schedule = self.num_timesteps_cond > 1 + if self.shorten_cond_schedule: + self.make_cond_schedule() + + def instantiate_first_stage(self, config): + model = instantiate_from_config(config) + self.first_stage_model = model.eval() + self.first_stage_model.train = disabled_train + for param in self.first_stage_model.parameters(): + param.requires_grad = False + + def instantiate_cond_stage(self, config): + if not self.cond_stage_trainable: + if config == "__is_first_stage__": + print("Using first stage also as cond stage.") + self.cond_stage_model = self.first_stage_model + elif config == "__is_unconditional__": + print(f"Training {self.__class__.__name__} as an unconditional model.") + self.cond_stage_model = None + # self.be_unconditional = True + else: + model = instantiate_from_config(config) + self.cond_stage_model = model.eval() + self.cond_stage_model.train = disabled_train + for param in self.cond_stage_model.parameters(): + param.requires_grad = False + else: + assert config != '__is_first_stage__' + assert config != '__is_unconditional__' + model = instantiate_from_config(config) + self.cond_stage_model = model + + def _get_denoise_row_from_list(self, samples, desc='', force_no_decoder_quantization=False): + denoise_row = [] + for zd in tqdm(samples, desc=desc): + denoise_row.append(self.decode_first_stage(zd.to(self.device), + force_not_quantize=force_no_decoder_quantization)) + n_imgs_per_row = len(denoise_row) + denoise_row = torch.stack(denoise_row) # n_log_step, n_row, C, H, W + denoise_grid = rearrange(denoise_row, 'n b c h w -> b n c h w') + denoise_grid = rearrange(denoise_grid, 'b n c h w -> (b n) c h w') + denoise_grid = make_grid(denoise_grid, nrow=n_imgs_per_row) + return denoise_grid + + def get_first_stage_encoding(self, encoder_posterior): + if isinstance(encoder_posterior, DiagonalGaussianDistribution): + z = encoder_posterior.sample() + elif isinstance(encoder_posterior, torch.Tensor): + z = encoder_posterior + else: + raise NotImplementedError(f"encoder_posterior of type '{type(encoder_posterior)}' not yet implemented") + return self.scale_factor * z + + def get_learned_conditioning(self, c): + if self.cond_stage_forward is None: + if hasattr(self.cond_stage_model, 'encode') and callable(self.cond_stage_model.encode): + c = self.cond_stage_model.encode(c) + if isinstance(c, DiagonalGaussianDistribution): + c = c.mode() + else: + c = self.cond_stage_model(c) + else: + assert hasattr(self.cond_stage_model, self.cond_stage_forward) + c = getattr(self.cond_stage_model, self.cond_stage_forward)(c) + return c + + def meshgrid(self, h, w): + y = torch.arange(0, h).view(h, 1, 1).repeat(1, w, 1) + x = torch.arange(0, w).view(1, w, 1).repeat(h, 1, 1) + + arr = torch.cat([y, x], dim=-1) + return arr + + def delta_border(self, h, w): + """ + :param h: height + :param w: width + :return: normalized distance to image border, + wtith min distance = 0 at border and max dist = 0.5 at image center + """ + lower_right_corner = torch.tensor([h - 1, w - 1]).view(1, 1, 2) + arr = self.meshgrid(h, w) / lower_right_corner + dist_left_up = torch.min(arr, dim=-1, keepdims=True)[0] + dist_right_down = torch.min(1 - arr, dim=-1, keepdims=True)[0] + edge_dist = torch.min(torch.cat([dist_left_up, dist_right_down], dim=-1), dim=-1)[0] + return edge_dist + + def get_weighting(self, h, w, Ly, Lx, device): + weighting = self.delta_border(h, w) + weighting = torch.clip(weighting, self.split_input_params["clip_min_weight"], + self.split_input_params["clip_max_weight"], ) + weighting = weighting.view(1, h * w, 1).repeat(1, 1, Ly * Lx).to(device) + + if self.split_input_params["tie_braker"]: + L_weighting = self.delta_border(Ly, Lx) + L_weighting = torch.clip(L_weighting, + self.split_input_params["clip_min_tie_weight"], + self.split_input_params["clip_max_tie_weight"]) + + L_weighting = L_weighting.view(1, 1, Ly * Lx).to(device) + weighting = weighting * L_weighting + return weighting + + def get_fold_unfold(self, x, kernel_size, stride, uf=1, df=1): # todo load once not every time, shorten code + """ + :param x: img of size (bs, c, h, w) + :return: n img crops of size (n, bs, c, kernel_size[0], kernel_size[1]) + """ + bs, nc, h, w = x.shape + + # number of crops in image + Ly = (h - kernel_size[0]) // stride[0] + 1 + Lx = (w - kernel_size[1]) // stride[1] + 1 + + if uf == 1 and df == 1: + fold_params = dict(kernel_size=kernel_size, dilation=1, padding=0, stride=stride) + unfold = torch.nn.Unfold(**fold_params) + + fold = torch.nn.Fold(output_size=x.shape[2:], **fold_params) + + weighting = self.get_weighting(kernel_size[0], kernel_size[1], Ly, Lx, x.device).to(x.dtype) + normalization = fold(weighting).view(1, 1, h, w) # normalizes the overlap + weighting = weighting.view((1, 1, kernel_size[0], kernel_size[1], Ly * Lx)) + + elif uf > 1 and df == 1: + fold_params = dict(kernel_size=kernel_size, dilation=1, padding=0, stride=stride) + unfold = torch.nn.Unfold(**fold_params) + + fold_params2 = dict(kernel_size=(kernel_size[0] * uf, kernel_size[0] * uf), + dilation=1, padding=0, + stride=(stride[0] * uf, stride[1] * uf)) + fold = torch.nn.Fold(output_size=(x.shape[2] * uf, x.shape[3] * uf), **fold_params2) + + weighting = self.get_weighting(kernel_size[0] * uf, kernel_size[1] * uf, Ly, Lx, x.device).to(x.dtype) + normalization = fold(weighting).view(1, 1, h * uf, w * uf) # normalizes the overlap + weighting = weighting.view((1, 1, kernel_size[0] * uf, kernel_size[1] * uf, Ly * Lx)) + + elif df > 1 and uf == 1: + fold_params = dict(kernel_size=kernel_size, dilation=1, padding=0, stride=stride) + unfold = torch.nn.Unfold(**fold_params) + + fold_params2 = dict(kernel_size=(kernel_size[0] // df, kernel_size[0] // df), + dilation=1, padding=0, + stride=(stride[0] // df, stride[1] // df)) + fold = torch.nn.Fold(output_size=(x.shape[2] // df, x.shape[3] // df), **fold_params2) + + weighting = self.get_weighting(kernel_size[0] // df, kernel_size[1] // df, Ly, Lx, x.device).to(x.dtype) + normalization = fold(weighting).view(1, 1, h // df, w // df) # normalizes the overlap + weighting = weighting.view((1, 1, kernel_size[0] // df, kernel_size[1] // df, Ly * Lx)) + + else: + raise NotImplementedError + + return fold, unfold, normalization, weighting + + @torch.no_grad() + def get_input(self, batch, k, return_first_stage_outputs=False, force_c_encode=False, + cond_key=None, return_original_cond=False, bs=None): + x = super().get_input(batch, k) + if bs is not None: + x = x[:bs] + x = x.to(self.device) + encoder_posterior = self.encode_first_stage(x) + z = self.get_first_stage_encoding(encoder_posterior).detach() + + if self.model.conditioning_key is not None: + if cond_key is None: + cond_key = self.cond_stage_key + if cond_key != self.first_stage_key: + if cond_key in ['caption', 'coordinates_bbox']: + xc = batch[cond_key] + elif cond_key == 'class_label': + xc = batch + else: + xc = super().get_input(batch, cond_key).to(self.device) + else: + xc = x + if not self.cond_stage_trainable or force_c_encode: + if isinstance(xc, dict) or isinstance(xc, list): + # import pudb; pudb.set_trace() + c = self.get_learned_conditioning(xc) + else: + c = self.get_learned_conditioning(xc.to(self.device)) + else: + c = xc + if bs is not None: + c = c[:bs] + + if self.use_positional_encodings: + pos_x, pos_y = self.compute_latent_shifts(batch) + ckey = __conditioning_keys__[self.model.conditioning_key] + c = {ckey: c, 'pos_x': pos_x, 'pos_y': pos_y} + + else: + c = None + xc = None + if self.use_positional_encodings: + pos_x, pos_y = self.compute_latent_shifts(batch) + c = {'pos_x': pos_x, 'pos_y': pos_y} + out = [z, c] + if return_first_stage_outputs: + xrec = self.decode_first_stage(z) + out.extend([x, xrec]) + if return_original_cond: + out.append(xc) + return out + + @torch.no_grad() + def decode_first_stage(self, z, predict_cids=False, force_not_quantize=False): + if predict_cids: + if z.dim() == 4: + z = torch.argmax(z.exp(), dim=1).long() + z = self.first_stage_model.quantize.get_codebook_entry(z, shape=None) + z = rearrange(z, 'b h w c -> b c h w').contiguous() + + z = 1. / self.scale_factor * z + + if hasattr(self, "split_input_params"): + if self.split_input_params["patch_distributed_vq"]: + ks = self.split_input_params["ks"] # eg. (128, 128) + stride = self.split_input_params["stride"] # eg. (64, 64) + uf = self.split_input_params["vqf"] + bs, nc, h, w = z.shape + if ks[0] > h or ks[1] > w: + ks = (min(ks[0], h), min(ks[1], w)) + print("reducing Kernel") + + if stride[0] > h or stride[1] > w: + stride = (min(stride[0], h), min(stride[1], w)) + print("reducing stride") + + fold, unfold, normalization, weighting = self.get_fold_unfold(z, ks, stride, uf=uf) + + z = unfold(z) # (bn, nc * prod(**ks), L) + # 1. Reshape to img shape + z = z.view((z.shape[0], -1, ks[0], ks[1], z.shape[-1])) # (bn, nc, ks[0], ks[1], L ) + + # 2. apply model loop over last dim + if isinstance(self.first_stage_model, VQModelInterface): + output_list = [self.first_stage_model.decode(z[:, :, :, :, i], + force_not_quantize=predict_cids or force_not_quantize) + for i in range(z.shape[-1])] + else: + + output_list = [self.first_stage_model.decode(z[:, :, :, :, i]) + for i in range(z.shape[-1])] + + o = torch.stack(output_list, axis=-1) # # (bn, nc, ks[0], ks[1], L) + o = o * weighting + # Reverse 1. reshape to img shape + o = o.view((o.shape[0], -1, o.shape[-1])) # (bn, nc * ks[0] * ks[1], L) + # stitch crops together + decoded = fold(o) + decoded = decoded / normalization # norm is shape (1, 1, h, w) + return decoded + else: + if isinstance(self.first_stage_model, VQModelInterface): + return self.first_stage_model.decode(z, force_not_quantize=predict_cids or force_not_quantize) + else: + return self.first_stage_model.decode(z) + + else: + if isinstance(self.first_stage_model, VQModelInterface): + return self.first_stage_model.decode(z, force_not_quantize=predict_cids or force_not_quantize) + else: + return self.first_stage_model.decode(z) + + # same as above but without decorator + def differentiable_decode_first_stage(self, z, predict_cids=False, force_not_quantize=False): + if predict_cids: + if z.dim() == 4: + z = torch.argmax(z.exp(), dim=1).long() + z = self.first_stage_model.quantize.get_codebook_entry(z, shape=None) + z = rearrange(z, 'b h w c -> b c h w').contiguous() + + z = 1. / self.scale_factor * z + + if hasattr(self, "split_input_params"): + if self.split_input_params["patch_distributed_vq"]: + ks = self.split_input_params["ks"] # eg. (128, 128) + stride = self.split_input_params["stride"] # eg. (64, 64) + uf = self.split_input_params["vqf"] + bs, nc, h, w = z.shape + if ks[0] > h or ks[1] > w: + ks = (min(ks[0], h), min(ks[1], w)) + print("reducing Kernel") + + if stride[0] > h or stride[1] > w: + stride = (min(stride[0], h), min(stride[1], w)) + print("reducing stride") + + fold, unfold, normalization, weighting = self.get_fold_unfold(z, ks, stride, uf=uf) + + z = unfold(z) # (bn, nc * prod(**ks), L) + # 1. Reshape to img shape + z = z.view((z.shape[0], -1, ks[0], ks[1], z.shape[-1])) # (bn, nc, ks[0], ks[1], L ) + + # 2. apply model loop over last dim + if isinstance(self.first_stage_model, VQModelInterface): + output_list = [self.first_stage_model.decode(z[:, :, :, :, i], + force_not_quantize=predict_cids or force_not_quantize) + for i in range(z.shape[-1])] + else: + + output_list = [self.first_stage_model.decode(z[:, :, :, :, i]) + for i in range(z.shape[-1])] + + o = torch.stack(output_list, axis=-1) # # (bn, nc, ks[0], ks[1], L) + o = o * weighting + # Reverse 1. reshape to img shape + o = o.view((o.shape[0], -1, o.shape[-1])) # (bn, nc * ks[0] * ks[1], L) + # stitch crops together + decoded = fold(o) + decoded = decoded / normalization # norm is shape (1, 1, h, w) + return decoded + else: + if isinstance(self.first_stage_model, VQModelInterface): + return self.first_stage_model.decode(z, force_not_quantize=predict_cids or force_not_quantize) + else: + return self.first_stage_model.decode(z) + + else: + if isinstance(self.first_stage_model, VQModelInterface): + return self.first_stage_model.decode(z, force_not_quantize=predict_cids or force_not_quantize) + else: + return self.first_stage_model.decode(z) + + @torch.no_grad() + def encode_first_stage(self, x): + if hasattr(self, "split_input_params"): + if self.split_input_params["patch_distributed_vq"]: + ks = self.split_input_params["ks"] # eg. (128, 128) + stride = self.split_input_params["stride"] # eg. (64, 64) + df = self.split_input_params["vqf"] + self.split_input_params['original_image_size'] = x.shape[-2:] + bs, nc, h, w = x.shape + if ks[0] > h or ks[1] > w: + ks = (min(ks[0], h), min(ks[1], w)) + print("reducing Kernel") + + if stride[0] > h or stride[1] > w: + stride = (min(stride[0], h), min(stride[1], w)) + print("reducing stride") + + fold, unfold, normalization, weighting = self.get_fold_unfold(x, ks, stride, df=df) + z = unfold(x) # (bn, nc * prod(**ks), L) + # Reshape to img shape + z = z.view((z.shape[0], -1, ks[0], ks[1], z.shape[-1])) # (bn, nc, ks[0], ks[1], L ) + + output_list = [self.first_stage_model.encode(z[:, :, :, :, i]) + for i in range(z.shape[-1])] + + o = torch.stack(output_list, axis=-1) + o = o * weighting + + # Reverse reshape to img shape + o = o.view((o.shape[0], -1, o.shape[-1])) # (bn, nc * ks[0] * ks[1], L) + # stitch crops together + decoded = fold(o) + decoded = decoded / normalization + return decoded + + else: + return self.first_stage_model.encode(x) + else: + return self.first_stage_model.encode(x) + + def shared_step(self, batch, **kwargs): + x, c = self.get_input(batch, self.first_stage_key) + loss = self(x, c) + return loss + + def forward(self, x, c, *args, **kwargs): + t = torch.randint(0, self.num_timesteps, (x.shape[0],), device=self.device).long() + if self.model.conditioning_key is not None: + assert c is not None + if self.cond_stage_trainable: + c = self.get_learned_conditioning(c) + if self.shorten_cond_schedule: # TODO: drop this option + tc = self.cond_ids[t].to(self.device) + c = self.q_sample(x_start=c, t=tc, noise=torch.randn_like(c.float())) + return self.p_losses(x, c, t, *args, **kwargs) + + def _rescale_annotations(self, bboxes, crop_coordinates): # TODO: move to dataset + def rescale_bbox(bbox): + x0 = clamp((bbox[0] - crop_coordinates[0]) / crop_coordinates[2]) + y0 = clamp((bbox[1] - crop_coordinates[1]) / crop_coordinates[3]) + w = min(bbox[2] / crop_coordinates[2], 1 - x0) + h = min(bbox[3] / crop_coordinates[3], 1 - y0) + return x0, y0, w, h + + return [rescale_bbox(b) for b in bboxes] + + def apply_model(self, x_noisy, t, cond, return_ids=False): + + if isinstance(cond, dict): + # hybrid case, cond is exptected to be a dict + pass + else: + if not isinstance(cond, list): + cond = [cond] + key = 'c_concat' if self.model.conditioning_key == 'concat' else 'c_crossattn' + cond = {key: cond} + + if hasattr(self, "split_input_params"): + assert len(cond) == 1 # todo can only deal with one conditioning atm + assert not return_ids + ks = self.split_input_params["ks"] # eg. (128, 128) + stride = self.split_input_params["stride"] # eg. (64, 64) + + h, w = x_noisy.shape[-2:] + + fold, unfold, normalization, weighting = self.get_fold_unfold(x_noisy, ks, stride) + + z = unfold(x_noisy) # (bn, nc * prod(**ks), L) + # Reshape to img shape + z = z.view((z.shape[0], -1, ks[0], ks[1], z.shape[-1])) # (bn, nc, ks[0], ks[1], L ) + z_list = [z[:, :, :, :, i] for i in range(z.shape[-1])] + + if self.cond_stage_key in ["image", "LR_image", "segmentation", + 'bbox_img'] and self.model.conditioning_key: # todo check for completeness + c_key = next(iter(cond.keys())) # get key + c = next(iter(cond.values())) # get value + assert (len(c) == 1) # todo extend to list with more than one elem + c = c[0] # get element + + c = unfold(c) + c = c.view((c.shape[0], -1, ks[0], ks[1], c.shape[-1])) # (bn, nc, ks[0], ks[1], L ) + + cond_list = [{c_key: [c[:, :, :, :, i]]} for i in range(c.shape[-1])] + + elif self.cond_stage_key == 'coordinates_bbox': + assert 'original_image_size' in self.split_input_params, 'BoudingBoxRescaling is missing original_image_size' + + # assuming padding of unfold is always 0 and its dilation is always 1 + n_patches_per_row = int((w - ks[0]) / stride[0] + 1) + full_img_h, full_img_w = self.split_input_params['original_image_size'] + # as we are operating on latents, we need the factor from the original image size to the + # spatial latent size to properly rescale the crops for regenerating the bbox annotations + num_downs = self.first_stage_model.encoder.num_resolutions - 1 + rescale_latent = 2 ** (num_downs) + + # get top left postions of patches as conforming for the bbbox tokenizer, therefore we + # need to rescale the tl patch coordinates to be in between (0,1) + tl_patch_coordinates = [(rescale_latent * stride[0] * (patch_nr % n_patches_per_row) / full_img_w, + rescale_latent * stride[1] * (patch_nr // n_patches_per_row) / full_img_h) + for patch_nr in range(z.shape[-1])] + + # patch_limits are tl_coord, width and height coordinates as (x_tl, y_tl, h, w) + patch_limits = [(x_tl, y_tl, + rescale_latent * ks[0] / full_img_w, + rescale_latent * ks[1] / full_img_h) for x_tl, y_tl in tl_patch_coordinates] + # patch_values = [(np.arange(x_tl,min(x_tl+ks, 1.)),np.arange(y_tl,min(y_tl+ks, 1.))) for x_tl, y_tl in tl_patch_coordinates] + + # tokenize crop coordinates for the bounding boxes of the respective patches + patch_limits_tknzd = [torch.LongTensor(self.bbox_tokenizer._crop_encoder(bbox))[None].to(self.device) + for bbox in patch_limits] # list of length l with tensors of shape (1, 2) + print(patch_limits_tknzd[0].shape) + # cut tknzd crop position from conditioning + assert isinstance(cond, dict), 'cond must be dict to be fed into model' + cut_cond = cond['c_crossattn'][0][..., :-2].to(self.device) + print(cut_cond.shape) + + adapted_cond = torch.stack([torch.cat([cut_cond, p], dim=1) for p in patch_limits_tknzd]) + adapted_cond = rearrange(adapted_cond, 'l b n -> (l b) n') + print(adapted_cond.shape) + adapted_cond = self.get_learned_conditioning(adapted_cond) + print(adapted_cond.shape) + adapted_cond = rearrange(adapted_cond, '(l b) n d -> l b n d', l=z.shape[-1]) + print(adapted_cond.shape) + + cond_list = [{'c_crossattn': [e]} for e in adapted_cond] + + else: + cond_list = [cond for i in range(z.shape[-1])] # Todo make this more efficient + + # apply model by loop over crops + output_list = [self.model(z_list[i], t, **cond_list[i]) for i in range(z.shape[-1])] + assert not isinstance(output_list[0], + tuple) # todo cant deal with multiple model outputs check this never happens + + o = torch.stack(output_list, axis=-1) + o = o * weighting + # Reverse reshape to img shape + o = o.view((o.shape[0], -1, o.shape[-1])) # (bn, nc * ks[0] * ks[1], L) + # stitch crops together + x_recon = fold(o) / normalization + + else: + x_recon = self.model(x_noisy, t, **cond) + + if isinstance(x_recon, tuple) and not return_ids: + return x_recon[0] + else: + return x_recon + + def _predict_eps_from_xstart(self, x_t, t, pred_xstart): + return (extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - pred_xstart) / \ + extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) + + def _prior_bpd(self, x_start): + """ + Get the prior KL term for the variational lower-bound, measured in + bits-per-dim. + This term can't be optimized, as it only depends on the encoder. + :param x_start: the [N x C x ...] tensor of inputs. + :return: a batch of [N] KL values (in bits), one per batch element. + """ + batch_size = x_start.shape[0] + t = torch.tensor([self.num_timesteps - 1] * batch_size, device=x_start.device) + qt_mean, _, qt_log_variance = self.q_mean_variance(x_start, t) + kl_prior = normal_kl(mean1=qt_mean, logvar1=qt_log_variance, mean2=0.0, logvar2=0.0) + return mean_flat(kl_prior) / np.log(2.0) + + def p_losses(self, x_start, cond, t, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + x_noisy = self.q_sample(x_start=x_start, t=t, noise=noise) + model_output = self.apply_model(x_noisy, t, cond) + + loss_dict = {} + prefix = 'train' if self.training else 'val' + + if self.parameterization == "x0": + target = x_start + elif self.parameterization == "eps": + target = noise + else: + raise NotImplementedError() + + loss_simple = self.get_loss(model_output, target, mean=False).mean([1, 2, 3]) + loss_dict.update({f'{prefix}/loss_simple': loss_simple.mean()}) + + logvar_t = self.logvar[t].to(self.device) + loss = loss_simple / torch.exp(logvar_t) + logvar_t + # loss = loss_simple / torch.exp(self.logvar) + self.logvar + if self.learn_logvar: + loss_dict.update({f'{prefix}/loss_gamma': loss.mean()}) + loss_dict.update({'logvar': self.logvar.data.mean()}) + + loss = self.l_simple_weight * loss.mean() + + loss_vlb = self.get_loss(model_output, target, mean=False).mean(dim=(1, 2, 3)) + loss_vlb = (self.lvlb_weights[t] * loss_vlb).mean() + loss_dict.update({f'{prefix}/loss_vlb': loss_vlb}) + loss += (self.original_elbo_weight * loss_vlb) + loss_dict.update({f'{prefix}/loss': loss}) + + return loss, loss_dict + + def p_mean_variance(self, x, c, t, clip_denoised: bool, return_codebook_ids=False, quantize_denoised=False, + return_x0=False, score_corrector=None, corrector_kwargs=None): + t_in = t + model_out = self.apply_model(x, t_in, c, return_ids=return_codebook_ids) + + if score_corrector is not None: + assert self.parameterization == "eps" + model_out = score_corrector.modify_score(self, model_out, x, t, c, **corrector_kwargs) + + if return_codebook_ids: + model_out, logits = model_out + + if self.parameterization == "eps": + x_recon = self.predict_start_from_noise(x, t=t, noise=model_out) + elif self.parameterization == "x0": + x_recon = model_out + else: + raise NotImplementedError() + + if clip_denoised: + x_recon.clamp_(-1., 1.) + if quantize_denoised: + x_recon, _, [_, _, indices] = self.first_stage_model.quantize(x_recon) + model_mean, posterior_variance, posterior_log_variance = self.q_posterior(x_start=x_recon, x_t=x, t=t) + if return_codebook_ids: + return model_mean, posterior_variance, posterior_log_variance, logits + elif return_x0: + return model_mean, posterior_variance, posterior_log_variance, x_recon + else: + return model_mean, posterior_variance, posterior_log_variance + + @torch.no_grad() + def p_sample(self, x, c, t, clip_denoised=False, repeat_noise=False, + return_codebook_ids=False, quantize_denoised=False, return_x0=False, + temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None): + b, *_, device = *x.shape, x.device + outputs = self.p_mean_variance(x=x, c=c, t=t, clip_denoised=clip_denoised, + return_codebook_ids=return_codebook_ids, + quantize_denoised=quantize_denoised, + return_x0=return_x0, + score_corrector=score_corrector, corrector_kwargs=corrector_kwargs) + if return_codebook_ids: + raise DeprecationWarning("Support dropped.") + model_mean, _, model_log_variance, logits = outputs + elif return_x0: + model_mean, _, model_log_variance, x0 = outputs + else: + model_mean, _, model_log_variance = outputs + + noise = noise_like(x.shape, device, repeat_noise) * temperature + if noise_dropout > 0.: + noise = torch.nn.functional.dropout(noise, p=noise_dropout) + # no noise when t == 0 + nonzero_mask = (1 - (t == 0).float()).reshape(b, *((1,) * (len(x.shape) - 1))) + + if return_codebook_ids: + return model_mean + nonzero_mask * (0.5 * model_log_variance).exp() * noise, logits.argmax(dim=1) + if return_x0: + return model_mean + nonzero_mask * (0.5 * model_log_variance).exp() * noise, x0 + else: + return model_mean + nonzero_mask * (0.5 * model_log_variance).exp() * noise + + @torch.no_grad() + def progressive_denoising(self, cond, shape, verbose=True, callback=None, quantize_denoised=False, + img_callback=None, mask=None, x0=None, temperature=1., noise_dropout=0., + score_corrector=None, corrector_kwargs=None, batch_size=None, x_T=None, start_T=None, + log_every_t=None): + if not log_every_t: + log_every_t = self.log_every_t + timesteps = self.num_timesteps + if batch_size is not None: + b = batch_size if batch_size is not None else shape[0] + shape = [batch_size] + list(shape) + else: + b = batch_size = shape[0] + if x_T is None: + img = torch.randn(shape, device=self.device) + else: + img = x_T + intermediates = [] + if cond is not None: + if isinstance(cond, dict): + cond = {key: cond[key][:batch_size] if not isinstance(cond[key], list) else + list(map(lambda x: x[:batch_size], cond[key])) for key in cond} + else: + cond = [c[:batch_size] for c in cond] if isinstance(cond, list) else cond[:batch_size] + + if start_T is not None: + timesteps = min(timesteps, start_T) + iterator = tqdm(reversed(range(0, timesteps)), desc='Progressive Generation', + total=timesteps) if verbose else reversed( + range(0, timesteps)) + if type(temperature) == float: + temperature = [temperature] * timesteps + + for i in iterator: + ts = torch.full((b,), i, device=self.device, dtype=torch.long) + if self.shorten_cond_schedule: + assert self.model.conditioning_key != 'hybrid' + tc = self.cond_ids[ts].to(cond.device) + cond = self.q_sample(x_start=cond, t=tc, noise=torch.randn_like(cond)) + + img, x0_partial = self.p_sample(img, cond, ts, + clip_denoised=self.clip_denoised, + quantize_denoised=quantize_denoised, return_x0=True, + temperature=temperature[i], noise_dropout=noise_dropout, + score_corrector=score_corrector, corrector_kwargs=corrector_kwargs) + if mask is not None: + assert x0 is not None + img_orig = self.q_sample(x0, ts) + img = img_orig * mask + (1. - mask) * img + + if i % log_every_t == 0 or i == timesteps - 1: + intermediates.append(x0_partial) + if callback: callback(i) + if img_callback: img_callback(img, i) + return img, intermediates + + @torch.no_grad() + def p_sample_loop(self, cond, shape, return_intermediates=False, + x_T=None, verbose=True, callback=None, timesteps=None, quantize_denoised=False, + mask=None, x0=None, img_callback=None, start_T=None, + log_every_t=None): + + if not log_every_t: + log_every_t = self.log_every_t + device = self.betas.device + b = shape[0] + if x_T is None: + img = torch.randn(shape, device=device) + else: + img = x_T + + intermediates = [img] + if timesteps is None: + timesteps = self.num_timesteps + + if start_T is not None: + timesteps = min(timesteps, start_T) + iterator = tqdm(reversed(range(0, timesteps)), desc='Sampling t', total=timesteps) if verbose else reversed( + range(0, timesteps)) + + if mask is not None: + assert x0 is not None + assert x0.shape[2:3] == mask.shape[2:3] # spatial size has to match + + for i in iterator: + ts = torch.full((b,), i, device=device, dtype=torch.long) + if self.shorten_cond_schedule: + assert self.model.conditioning_key != 'hybrid' + tc = self.cond_ids[ts].to(cond.device) + cond = self.q_sample(x_start=cond, t=tc, noise=torch.randn_like(cond)) + + img = self.p_sample(img, cond, ts, + clip_denoised=self.clip_denoised, + quantize_denoised=quantize_denoised) + if mask is not None: + img_orig = self.q_sample(x0, ts) + img = img_orig * mask + (1. - mask) * img + + if i % log_every_t == 0 or i == timesteps - 1: + intermediates.append(img) + if callback: callback(i) + if img_callback: img_callback(img, i) + + if return_intermediates: + return img, intermediates + return img + + @torch.no_grad() + def sample(self, cond, batch_size=16, return_intermediates=False, x_T=None, + verbose=True, timesteps=None, quantize_denoised=False, + mask=None, x0=None, shape=None,**kwargs): + if shape is None: + shape = (batch_size, self.channels, self.image_size, self.image_size) + if cond is not None: + if isinstance(cond, dict): + cond = {key: cond[key][:batch_size] if not isinstance(cond[key], list) else + list(map(lambda x: x[:batch_size], cond[key])) for key in cond} + else: + cond = [c[:batch_size] for c in cond] if isinstance(cond, list) else cond[:batch_size] + return self.p_sample_loop(cond, + shape, + return_intermediates=return_intermediates, x_T=x_T, + verbose=verbose, timesteps=timesteps, quantize_denoised=quantize_denoised, + mask=mask, x0=x0) + + @torch.no_grad() + def sample_log(self,cond,batch_size,ddim, ddim_steps,**kwargs): + + if ddim: + ddim_sampler = DDIMSampler(self) + shape = (self.channels, self.image_size, self.image_size) + samples, intermediates =ddim_sampler.sample(ddim_steps,batch_size, + shape,cond,verbose=False,**kwargs) + + else: + samples, intermediates = self.sample(cond=cond, batch_size=batch_size, + return_intermediates=True,**kwargs) + + return samples, intermediates + + + @torch.no_grad() + def log_images(self, batch, N=8, n_row=4, sample=True, ddim_steps=200, ddim_eta=1., return_keys=None, + quantize_denoised=True, inpaint=True, plot_denoise_rows=False, plot_progressive_rows=True, + plot_diffusion_rows=True, **kwargs): + + use_ddim = ddim_steps is not None + + log = dict() + z, c, x, xrec, xc = self.get_input(batch, self.first_stage_key, + return_first_stage_outputs=True, + force_c_encode=True, + return_original_cond=True, + bs=N) + N = min(x.shape[0], N) + n_row = min(x.shape[0], n_row) + log["inputs"] = x + log["reconstruction"] = xrec + if self.model.conditioning_key is not None: + if hasattr(self.cond_stage_model, "decode"): + xc = self.cond_stage_model.decode(c) + log["conditioning"] = xc + elif self.cond_stage_key in ["caption"]: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch["caption"]) + log["conditioning"] = xc + elif self.cond_stage_key == 'class_label': + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch["human_label"]) + log['conditioning'] = xc + elif isimage(xc): + log["conditioning"] = xc + if ismap(xc): + log["original_conditioning"] = self.to_rgb(xc) + + if plot_diffusion_rows: + # get diffusion row + diffusion_row = list() + z_start = z[:n_row] + for t in range(self.num_timesteps): + if t % self.log_every_t == 0 or t == self.num_timesteps - 1: + t = repeat(torch.tensor([t]), '1 -> b', b=n_row) + t = t.to(self.device).long() + noise = torch.randn_like(z_start) + z_noisy = self.q_sample(x_start=z_start, t=t, noise=noise) + diffusion_row.append(self.decode_first_stage(z_noisy)) + + diffusion_row = torch.stack(diffusion_row) # n_log_step, n_row, C, H, W + diffusion_grid = rearrange(diffusion_row, 'n b c h w -> b n c h w') + diffusion_grid = rearrange(diffusion_grid, 'b n c h w -> (b n) c h w') + diffusion_grid = make_grid(diffusion_grid, nrow=diffusion_row.shape[0]) + log["diffusion_row"] = diffusion_grid + + if sample: + # get denoise row + with self.ema_scope("Plotting"): + samples, z_denoise_row = self.sample_log(cond=c,batch_size=N,ddim=use_ddim, + ddim_steps=ddim_steps,eta=ddim_eta) + # samples, z_denoise_row = self.sample(cond=c, batch_size=N, return_intermediates=True) + x_samples = self.decode_first_stage(samples) + log["samples"] = x_samples + if plot_denoise_rows: + denoise_grid = self._get_denoise_row_from_list(z_denoise_row) + log["denoise_row"] = denoise_grid + + if quantize_denoised and not isinstance(self.first_stage_model, AutoencoderKL) and not isinstance( + self.first_stage_model, IdentityFirstStage): + # also display when quantizing x0 while sampling + with self.ema_scope("Plotting Quantized Denoised"): + samples, z_denoise_row = self.sample_log(cond=c,batch_size=N,ddim=use_ddim, + ddim_steps=ddim_steps,eta=ddim_eta, + quantize_denoised=True) + # samples, z_denoise_row = self.sample(cond=c, batch_size=N, return_intermediates=True, + # quantize_denoised=True) + x_samples = self.decode_first_stage(samples.to(self.device)) + log["samples_x0_quantized"] = x_samples + + if inpaint: + # make a simple center square + b, h, w = z.shape[0], z.shape[2], z.shape[3] + mask = torch.ones(N, h, w).to(self.device) + # zeros will be filled in + mask[:, h // 4:3 * h // 4, w // 4:3 * w // 4] = 0. + mask = mask[:, None, ...] + with self.ema_scope("Plotting Inpaint"): + + samples, _ = self.sample_log(cond=c,batch_size=N,ddim=use_ddim, eta=ddim_eta, + ddim_steps=ddim_steps, x0=z[:N], mask=mask) + x_samples = self.decode_first_stage(samples.to(self.device)) + log["samples_inpainting"] = x_samples + log["mask"] = mask + + # outpaint + with self.ema_scope("Plotting Outpaint"): + samples, _ = self.sample_log(cond=c, batch_size=N, ddim=use_ddim,eta=ddim_eta, + ddim_steps=ddim_steps, x0=z[:N], mask=mask) + x_samples = self.decode_first_stage(samples.to(self.device)) + log["samples_outpainting"] = x_samples + + if plot_progressive_rows: + with self.ema_scope("Plotting Progressives"): + img, progressives = self.progressive_denoising(c, + shape=(self.channels, self.image_size, self.image_size), + batch_size=N) + prog_row = self._get_denoise_row_from_list(progressives, desc="Progressive Generation") + log["progressive_row"] = prog_row + + if return_keys: + if np.intersect1d(list(log.keys()), return_keys).shape[0] == 0: + return log + else: + return {key: log[key] for key in return_keys} + return log + + def configure_optimizers(self): + lr = self.learning_rate + params = list(self.model.parameters()) + if self.cond_stage_trainable: + print(f"{self.__class__.__name__}: Also optimizing conditioner params!") + params = params + list(self.cond_stage_model.parameters()) + if self.learn_logvar: + print('Diffusion model optimizing logvar') + params.append(self.logvar) + opt = torch.optim.AdamW(params, lr=lr) + if self.use_scheduler: + assert 'target' in self.scheduler_config + scheduler = instantiate_from_config(self.scheduler_config) + + print("Setting up LambdaLR scheduler...") + scheduler = [ + { + 'scheduler': LambdaLR(opt, lr_lambda=scheduler.schedule), + 'interval': 'step', + 'frequency': 1 + }] + return [opt], scheduler + return opt + + @torch.no_grad() + def to_rgb(self, x): + x = x.float() + if not hasattr(self, "colorize"): + self.colorize = torch.randn(3, x.shape[1], 1, 1).to(x) + x = nn.functional.conv2d(x, weight=self.colorize) + x = 2. * (x - x.min()) / (x.max() - x.min()) - 1. + return x + + +class DiffusionWrapperV1(pl.LightningModule): + def __init__(self, diff_model_config, conditioning_key): + super().__init__() + self.diffusion_model = instantiate_from_config(diff_model_config) + self.conditioning_key = conditioning_key + assert self.conditioning_key in [None, 'concat', 'crossattn', 'hybrid', 'adm'] + + def forward(self, x, t, c_concat: list = None, c_crossattn: list = None): + if self.conditioning_key is None: + out = self.diffusion_model(x, t) + elif self.conditioning_key == 'concat': + xc = torch.cat([x] + c_concat, dim=1) + out = self.diffusion_model(xc, t) + elif self.conditioning_key == 'crossattn': + cc = torch.cat(c_crossattn, 1) + out = self.diffusion_model(x, t, context=cc) + elif self.conditioning_key == 'hybrid': + xc = torch.cat([x] + c_concat, dim=1) + cc = torch.cat(c_crossattn, 1) + out = self.diffusion_model(xc, t, context=cc) + elif self.conditioning_key == 'adm': + cc = c_crossattn[0] + out = self.diffusion_model(x, t, y=cc) + else: + raise NotImplementedError() + + return out + + +class Layout2ImgDiffusionV1(LatentDiffusionV1): + # TODO: move all layout-specific hacks to this class + def __init__(self, cond_stage_key, *args, **kwargs): + assert cond_stage_key == 'coordinates_bbox', 'Layout2ImgDiffusion only for cond_stage_key="coordinates_bbox"' + super().__init__(cond_stage_key=cond_stage_key, *args, **kwargs) + + def log_images(self, batch, N=8, *args, **kwargs): + logs = super().log_images(batch=batch, N=N, *args, **kwargs) + + key = 'train' if self.training else 'validation' + dset = self.trainer.datamodule.datasets[key] + mapper = dset.conditional_builders[self.cond_stage_key] + + bbox_imgs = [] + map_fn = lambda catno: dset.get_textual_label(dset.get_category_id(catno)) + for tknzd_bbox in batch[self.cond_stage_key][:N]: + bboximg = mapper.plot(tknzd_bbox.detach().cpu(), map_fn, (256, 256)) + bbox_imgs.append(bboximg) + + cond_img = torch.stack(bbox_imgs, dim=0) + logs['bbox_image'] = cond_img + return logs + +setattr(ldm.models.diffusion.ddpm, "DDPMV1", DDPMV1) +setattr(ldm.models.diffusion.ddpm, "LatentDiffusionV1", LatentDiffusionV1) +setattr(ldm.models.diffusion.ddpm, "DiffusionWrapperV1", DiffusionWrapperV1) +setattr(ldm.models.diffusion.ddpm, "Layout2ImgDiffusionV1", Layout2ImgDiffusionV1) diff --git a/sd/stable-diffusion-webui/extensions-builtin/Lora/extra_networks_lora.py b/sd/stable-diffusion-webui/extensions-builtin/Lora/extra_networks_lora.py new file mode 100644 index 0000000000000000000000000000000000000000..db63a4bb819b91ba363396a0777f7af52f9713ae --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/Lora/extra_networks_lora.py @@ -0,0 +1,26 @@ +from modules import extra_networks, shared +import lora + +class ExtraNetworkLora(extra_networks.ExtraNetwork): + def __init__(self): + super().__init__('lora') + + def activate(self, p, params_list): + additional = shared.opts.sd_lora + + if additional != "" and additional in lora.available_loras and len([x for x in params_list if x.items[0] == additional]) == 0: + p.all_prompts = [x + f"" for x in p.all_prompts] + params_list.append(extra_networks.ExtraNetworkParams(items=[additional, shared.opts.extra_networks_default_multiplier])) + + names = [] + multipliers = [] + for params in params_list: + assert len(params.items) > 0 + + names.append(params.items[0]) + multipliers.append(float(params.items[1]) if len(params.items) > 1 else 1.0) + + lora.load_loras(names, multipliers) + + def deactivate(self, p): + pass diff --git a/sd/stable-diffusion-webui/extensions-builtin/Lora/lora.py b/sd/stable-diffusion-webui/extensions-builtin/Lora/lora.py new file mode 100644 index 0000000000000000000000000000000000000000..42b9eb56aaf5c1f5c8d2a6a97a144680faee78a7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/Lora/lora.py @@ -0,0 +1,207 @@ +import glob +import os +import re +import torch + +from modules import shared, devices, sd_models + +re_digits = re.compile(r"\d+") +re_unet_down_blocks = re.compile(r"lora_unet_down_blocks_(\d+)_attentions_(\d+)_(.+)") +re_unet_mid_blocks = re.compile(r"lora_unet_mid_block_attentions_(\d+)_(.+)") +re_unet_up_blocks = re.compile(r"lora_unet_up_blocks_(\d+)_attentions_(\d+)_(.+)") +re_text_block = re.compile(r"lora_te_text_model_encoder_layers_(\d+)_(.+)") + + +def convert_diffusers_name_to_compvis(key): + def match(match_list, regex): + r = re.match(regex, key) + if not r: + return False + + match_list.clear() + match_list.extend([int(x) if re.match(re_digits, x) else x for x in r.groups()]) + return True + + m = [] + + if match(m, re_unet_down_blocks): + return f"diffusion_model_input_blocks_{1 + m[0] * 3 + m[1]}_1_{m[2]}" + + if match(m, re_unet_mid_blocks): + return f"diffusion_model_middle_block_1_{m[1]}" + + if match(m, re_unet_up_blocks): + return f"diffusion_model_output_blocks_{m[0] * 3 + m[1]}_1_{m[2]}" + + if match(m, re_text_block): + return f"transformer_text_model_encoder_layers_{m[0]}_{m[1]}" + + return key + + +class LoraOnDisk: + def __init__(self, name, filename): + self.name = name + self.filename = filename + + +class LoraModule: + def __init__(self, name): + self.name = name + self.multiplier = 1.0 + self.modules = {} + self.mtime = None + + +class LoraUpDownModule: + def __init__(self): + self.up = None + self.down = None + self.alpha = None + + +def assign_lora_names_to_compvis_modules(sd_model): + lora_layer_mapping = {} + + for name, module in shared.sd_model.cond_stage_model.wrapped.named_modules(): + lora_name = name.replace(".", "_") + lora_layer_mapping[lora_name] = module + module.lora_layer_name = lora_name + + for name, module in shared.sd_model.model.named_modules(): + lora_name = name.replace(".", "_") + lora_layer_mapping[lora_name] = module + module.lora_layer_name = lora_name + + sd_model.lora_layer_mapping = lora_layer_mapping + + +def load_lora(name, filename): + lora = LoraModule(name) + lora.mtime = os.path.getmtime(filename) + + sd = sd_models.read_state_dict(filename) + + keys_failed_to_match = [] + + for key_diffusers, weight in sd.items(): + fullkey = convert_diffusers_name_to_compvis(key_diffusers) + key, lora_key = fullkey.split(".", 1) + + sd_module = shared.sd_model.lora_layer_mapping.get(key, None) + if sd_module is None: + keys_failed_to_match.append(key_diffusers) + continue + + lora_module = lora.modules.get(key, None) + if lora_module is None: + lora_module = LoraUpDownModule() + lora.modules[key] = lora_module + + if lora_key == "alpha": + lora_module.alpha = weight.item() + continue + + if type(sd_module) == torch.nn.Linear: + module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False) + elif type(sd_module) == torch.nn.Conv2d: + module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], (1, 1), bias=False) + else: + assert False, f'Lora layer {key_diffusers} matched a layer with unsupported type: {type(sd_module).__name__}' + + with torch.no_grad(): + module.weight.copy_(weight) + + module.to(device=devices.device, dtype=devices.dtype) + + if lora_key == "lora_up.weight": + lora_module.up = module + elif lora_key == "lora_down.weight": + lora_module.down = module + else: + assert False, f'Bad Lora layer name: {key_diffusers} - must end in lora_up.weight, lora_down.weight or alpha' + + if len(keys_failed_to_match) > 0: + print(f"Failed to match keys when loading Lora {filename}: {keys_failed_to_match}") + + return lora + + +def load_loras(names, multipliers=None): + already_loaded = {} + + for lora in loaded_loras: + if lora.name in names: + already_loaded[lora.name] = lora + + loaded_loras.clear() + + loras_on_disk = [available_loras.get(name, None) for name in names] + if any([x is None for x in loras_on_disk]): + list_available_loras() + + loras_on_disk = [available_loras.get(name, None) for name in names] + + for i, name in enumerate(names): + lora = already_loaded.get(name, None) + + lora_on_disk = loras_on_disk[i] + if lora_on_disk is not None: + if lora is None or os.path.getmtime(lora_on_disk.filename) > lora.mtime: + lora = load_lora(name, lora_on_disk.filename) + + if lora is None: + print(f"Couldn't find Lora with name {name}") + continue + + lora.multiplier = multipliers[i] if multipliers else 1.0 + loaded_loras.append(lora) + + +def lora_forward(module, input, res): + if len(loaded_loras) == 0: + return res + + lora_layer_name = getattr(module, 'lora_layer_name', None) + for lora in loaded_loras: + module = lora.modules.get(lora_layer_name, None) + if module is not None: + if shared.opts.lora_apply_to_outputs and res.shape == input.shape: + res = res + module.up(module.down(res)) * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0) + else: + res = res + module.up(module.down(input)) * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0) + + return res + + +def lora_Linear_forward(self, input): + return lora_forward(self, input, torch.nn.Linear_forward_before_lora(self, input)) + + +def lora_Conv2d_forward(self, input): + return lora_forward(self, input, torch.nn.Conv2d_forward_before_lora(self, input)) + + +def list_available_loras(): + available_loras.clear() + + os.makedirs(shared.cmd_opts.lora_dir, exist_ok=True) + + candidates = \ + glob.glob(os.path.join(shared.cmd_opts.lora_dir, '**/*.pt'), recursive=True) + \ + glob.glob(os.path.join(shared.cmd_opts.lora_dir, '**/*.safetensors'), recursive=True) + \ + glob.glob(os.path.join(shared.cmd_opts.lora_dir, '**/*.ckpt'), recursive=True) + + for filename in sorted(candidates): + if os.path.isdir(filename): + continue + + name = os.path.splitext(os.path.basename(filename))[0] + + available_loras[name] = LoraOnDisk(name, filename) + + +available_loras = {} +loaded_loras = [] + +list_available_loras() diff --git a/sd/stable-diffusion-webui/extensions-builtin/Lora/preload.py b/sd/stable-diffusion-webui/extensions-builtin/Lora/preload.py new file mode 100644 index 0000000000000000000000000000000000000000..c47d7ef4e24893953e51b8dfb5a7ccf88c574546 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/Lora/preload.py @@ -0,0 +1,6 @@ +import os +from modules import paths + + +def preload(parser): + parser.add_argument("--lora-dir", type=str, help="Path to directory with Lora networks.", default=os.path.join(paths.models_path, 'Lora')) diff --git a/sd/stable-diffusion-webui/extensions-builtin/Lora/scripts/lora_script.py b/sd/stable-diffusion-webui/extensions-builtin/Lora/scripts/lora_script.py new file mode 100644 index 0000000000000000000000000000000000000000..29ec16018858f4210f00c83a6e18c0cb7adb5e40 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/Lora/scripts/lora_script.py @@ -0,0 +1,38 @@ +import torch +import gradio as gr + +import lora +import extra_networks_lora +import ui_extra_networks_lora +from modules import script_callbacks, ui_extra_networks, extra_networks, shared + + +def unload(): + torch.nn.Linear.forward = torch.nn.Linear_forward_before_lora + torch.nn.Conv2d.forward = torch.nn.Conv2d_forward_before_lora + + +def before_ui(): + ui_extra_networks.register_page(ui_extra_networks_lora.ExtraNetworksPageLora()) + extra_networks.register_extra_network(extra_networks_lora.ExtraNetworkLora()) + + +if not hasattr(torch.nn, 'Linear_forward_before_lora'): + torch.nn.Linear_forward_before_lora = torch.nn.Linear.forward + +if not hasattr(torch.nn, 'Conv2d_forward_before_lora'): + torch.nn.Conv2d_forward_before_lora = torch.nn.Conv2d.forward + +torch.nn.Linear.forward = lora.lora_Linear_forward +torch.nn.Conv2d.forward = lora.lora_Conv2d_forward + +script_callbacks.on_model_loaded(lora.assign_lora_names_to_compvis_modules) +script_callbacks.on_script_unloaded(unload) +script_callbacks.on_before_ui(before_ui) + + +shared.options_templates.update(shared.options_section(('extra_networks', "Extra Networks"), { + "sd_lora": shared.OptionInfo("None", "Add Lora to prompt", gr.Dropdown, lambda: {"choices": [""] + [x for x in lora.available_loras]}, refresh=lora.list_available_loras), + "lora_apply_to_outputs": shared.OptionInfo(False, "Apply Lora to outputs rather than inputs when possible (experimental)"), + +})) diff --git a/sd/stable-diffusion-webui/extensions-builtin/Lora/ui_extra_networks_lora.py b/sd/stable-diffusion-webui/extensions-builtin/Lora/ui_extra_networks_lora.py new file mode 100644 index 0000000000000000000000000000000000000000..d2dca927b0a84f28ceeffa7347c8b60ec88698a6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/Lora/ui_extra_networks_lora.py @@ -0,0 +1,37 @@ +import json +import os +import lora + +from modules import shared, ui_extra_networks + + +class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): + def __init__(self): + super().__init__('Lora') + + def refresh(self): + lora.list_available_loras() + + def list_items(self): + for name, lora_on_disk in lora.available_loras.items(): + path, ext = os.path.splitext(lora_on_disk.filename) + previews = [path + ".png", path + ".preview.png"] + + preview = None + for file in previews: + if os.path.isfile(file): + preview = self.link_preview(file) + break + + yield { + "name": name, + "filename": path, + "preview": preview, + "search_term": self.search_terms_from_path(lora_on_disk.filename), + "prompt": json.dumps(f""), + "local_preview": path + ".png", + } + + def allowed_directories_for_previews(self): + return [shared.cmd_opts.lora_dir] + diff --git a/sd/stable-diffusion-webui/extensions-builtin/ScuNET/preload.py b/sd/stable-diffusion-webui/extensions-builtin/ScuNET/preload.py new file mode 100644 index 0000000000000000000000000000000000000000..4ce82b1d4349b24192b1915d022ed4fda9f31e5c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/ScuNET/preload.py @@ -0,0 +1,6 @@ +import os +from modules import paths + + +def preload(parser): + parser.add_argument("--scunet-models-path", type=str, help="Path to directory with ScuNET model file(s).", default=os.path.join(paths.models_path, 'ScuNET')) diff --git a/sd/stable-diffusion-webui/extensions-builtin/ScuNET/scripts/scunet_model.py b/sd/stable-diffusion-webui/extensions-builtin/ScuNET/scripts/scunet_model.py new file mode 100644 index 0000000000000000000000000000000000000000..e0fbf3a33747f447d396dd0d564e92c904cfabac --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/ScuNET/scripts/scunet_model.py @@ -0,0 +1,87 @@ +import os.path +import sys +import traceback + +import PIL.Image +import numpy as np +import torch +from basicsr.utils.download_util import load_file_from_url + +import modules.upscaler +from modules import devices, modelloader +from scunet_model_arch import SCUNet as net + + +class UpscalerScuNET(modules.upscaler.Upscaler): + def __init__(self, dirname): + self.name = "ScuNET" + self.model_name = "ScuNET GAN" + self.model_name2 = "ScuNET PSNR" + self.model_url = "https://github.com/cszn/KAIR/releases/download/v1.0/scunet_color_real_gan.pth" + self.model_url2 = "https://github.com/cszn/KAIR/releases/download/v1.0/scunet_color_real_psnr.pth" + self.user_path = dirname + super().__init__() + model_paths = self.find_models(ext_filter=[".pth"]) + scalers = [] + add_model2 = True + for file in model_paths: + if "http" in file: + name = self.model_name + else: + name = modelloader.friendly_name(file) + if name == self.model_name2 or file == self.model_url2: + add_model2 = False + try: + scaler_data = modules.upscaler.UpscalerData(name, file, self, 4) + scalers.append(scaler_data) + except Exception: + print(f"Error loading ScuNET model: {file}", file=sys.stderr) + print(traceback.format_exc(), file=sys.stderr) + if add_model2: + scaler_data2 = modules.upscaler.UpscalerData(self.model_name2, self.model_url2, self) + scalers.append(scaler_data2) + self.scalers = scalers + + def do_upscale(self, img: PIL.Image, selected_file): + torch.cuda.empty_cache() + + model = self.load_model(selected_file) + if model is None: + return img + + device = devices.get_device_for('scunet') + img = np.array(img) + img = img[:, :, ::-1] + img = np.moveaxis(img, 2, 0) / 255 + img = torch.from_numpy(img).float() + img = img.unsqueeze(0).to(device) + + with torch.no_grad(): + output = model(img) + output = output.squeeze().float().cpu().clamp_(0, 1).numpy() + output = 255. * np.moveaxis(output, 0, 2) + output = output.astype(np.uint8) + output = output[:, :, ::-1] + torch.cuda.empty_cache() + return PIL.Image.fromarray(output, 'RGB') + + def load_model(self, path: str): + device = devices.get_device_for('scunet') + if "http" in path: + filename = load_file_from_url(url=self.model_url, model_dir=self.model_path, file_name="%s.pth" % self.name, + progress=True) + else: + filename = path + if not os.path.exists(os.path.join(self.model_path, filename)) or filename is None: + print(f"ScuNET: Unable to load model from {filename}", file=sys.stderr) + return None + + model = net(in_nc=3, config=[4, 4, 4, 4, 4, 4, 4], dim=64) + model.load_state_dict(torch.load(filename), strict=True) + model.eval() + for k, v in model.named_parameters(): + v.requires_grad = False + model = model.to(device) + + return model + diff --git a/sd/stable-diffusion-webui/extensions-builtin/ScuNET/scunet_model_arch.py b/sd/stable-diffusion-webui/extensions-builtin/ScuNET/scunet_model_arch.py new file mode 100644 index 0000000000000000000000000000000000000000..43ca8d36fe57a12dcad58e8b06ee2e0774494b0e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/ScuNET/scunet_model_arch.py @@ -0,0 +1,265 @@ +# -*- coding: utf-8 -*- +import numpy as np +import torch +import torch.nn as nn +from einops import rearrange +from einops.layers.torch import Rearrange +from timm.models.layers import trunc_normal_, DropPath + + +class WMSA(nn.Module): + """ Self-attention module in Swin Transformer + """ + + def __init__(self, input_dim, output_dim, head_dim, window_size, type): + super(WMSA, self).__init__() + self.input_dim = input_dim + self.output_dim = output_dim + self.head_dim = head_dim + self.scale = self.head_dim ** -0.5 + self.n_heads = input_dim // head_dim + self.window_size = window_size + self.type = type + self.embedding_layer = nn.Linear(self.input_dim, 3 * self.input_dim, bias=True) + + self.relative_position_params = nn.Parameter( + torch.zeros((2 * window_size - 1) * (2 * window_size - 1), self.n_heads)) + + self.linear = nn.Linear(self.input_dim, self.output_dim) + + trunc_normal_(self.relative_position_params, std=.02) + self.relative_position_params = torch.nn.Parameter( + self.relative_position_params.view(2 * window_size - 1, 2 * window_size - 1, self.n_heads).transpose(1, + 2).transpose( + 0, 1)) + + def generate_mask(self, h, w, p, shift): + """ generating the mask of SW-MSA + Args: + shift: shift parameters in CyclicShift. + Returns: + attn_mask: should be (1 1 w p p), + """ + # supporting square. + attn_mask = torch.zeros(h, w, p, p, p, p, dtype=torch.bool, device=self.relative_position_params.device) + if self.type == 'W': + return attn_mask + + s = p - shift + attn_mask[-1, :, :s, :, s:, :] = True + attn_mask[-1, :, s:, :, :s, :] = True + attn_mask[:, -1, :, :s, :, s:] = True + attn_mask[:, -1, :, s:, :, :s] = True + attn_mask = rearrange(attn_mask, 'w1 w2 p1 p2 p3 p4 -> 1 1 (w1 w2) (p1 p2) (p3 p4)') + return attn_mask + + def forward(self, x): + """ Forward pass of Window Multi-head Self-attention module. + Args: + x: input tensor with shape of [b h w c]; + attn_mask: attention mask, fill -inf where the value is True; + Returns: + output: tensor shape [b h w c] + """ + if self.type != 'W': x = torch.roll(x, shifts=(-(self.window_size // 2), -(self.window_size // 2)), dims=(1, 2)) + x = rearrange(x, 'b (w1 p1) (w2 p2) c -> b w1 w2 p1 p2 c', p1=self.window_size, p2=self.window_size) + h_windows = x.size(1) + w_windows = x.size(2) + # square validation + # assert h_windows == w_windows + + x = rearrange(x, 'b w1 w2 p1 p2 c -> b (w1 w2) (p1 p2) c', p1=self.window_size, p2=self.window_size) + qkv = self.embedding_layer(x) + q, k, v = rearrange(qkv, 'b nw np (threeh c) -> threeh b nw np c', c=self.head_dim).chunk(3, dim=0) + sim = torch.einsum('hbwpc,hbwqc->hbwpq', q, k) * self.scale + # Adding learnable relative embedding + sim = sim + rearrange(self.relative_embedding(), 'h p q -> h 1 1 p q') + # Using Attn Mask to distinguish different subwindows. + if self.type != 'W': + attn_mask = self.generate_mask(h_windows, w_windows, self.window_size, shift=self.window_size // 2) + sim = sim.masked_fill_(attn_mask, float("-inf")) + + probs = nn.functional.softmax(sim, dim=-1) + output = torch.einsum('hbwij,hbwjc->hbwic', probs, v) + output = rearrange(output, 'h b w p c -> b w p (h c)') + output = self.linear(output) + output = rearrange(output, 'b (w1 w2) (p1 p2) c -> b (w1 p1) (w2 p2) c', w1=h_windows, p1=self.window_size) + + if self.type != 'W': output = torch.roll(output, shifts=(self.window_size // 2, self.window_size // 2), + dims=(1, 2)) + return output + + def relative_embedding(self): + cord = torch.tensor(np.array([[i, j] for i in range(self.window_size) for j in range(self.window_size)])) + relation = cord[:, None, :] - cord[None, :, :] + self.window_size - 1 + # negative is allowed + return self.relative_position_params[:, relation[:, :, 0].long(), relation[:, :, 1].long()] + + +class Block(nn.Module): + def __init__(self, input_dim, output_dim, head_dim, window_size, drop_path, type='W', input_resolution=None): + """ SwinTransformer Block + """ + super(Block, self).__init__() + self.input_dim = input_dim + self.output_dim = output_dim + assert type in ['W', 'SW'] + self.type = type + if input_resolution <= window_size: + self.type = 'W' + + self.ln1 = nn.LayerNorm(input_dim) + self.msa = WMSA(input_dim, input_dim, head_dim, window_size, self.type) + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + self.ln2 = nn.LayerNorm(input_dim) + self.mlp = nn.Sequential( + nn.Linear(input_dim, 4 * input_dim), + nn.GELU(), + nn.Linear(4 * input_dim, output_dim), + ) + + def forward(self, x): + x = x + self.drop_path(self.msa(self.ln1(x))) + x = x + self.drop_path(self.mlp(self.ln2(x))) + return x + + +class ConvTransBlock(nn.Module): + def __init__(self, conv_dim, trans_dim, head_dim, window_size, drop_path, type='W', input_resolution=None): + """ SwinTransformer and Conv Block + """ + super(ConvTransBlock, self).__init__() + self.conv_dim = conv_dim + self.trans_dim = trans_dim + self.head_dim = head_dim + self.window_size = window_size + self.drop_path = drop_path + self.type = type + self.input_resolution = input_resolution + + assert self.type in ['W', 'SW'] + if self.input_resolution <= self.window_size: + self.type = 'W' + + self.trans_block = Block(self.trans_dim, self.trans_dim, self.head_dim, self.window_size, self.drop_path, + self.type, self.input_resolution) + self.conv1_1 = nn.Conv2d(self.conv_dim + self.trans_dim, self.conv_dim + self.trans_dim, 1, 1, 0, bias=True) + self.conv1_2 = nn.Conv2d(self.conv_dim + self.trans_dim, self.conv_dim + self.trans_dim, 1, 1, 0, bias=True) + + self.conv_block = nn.Sequential( + nn.Conv2d(self.conv_dim, self.conv_dim, 3, 1, 1, bias=False), + nn.ReLU(True), + nn.Conv2d(self.conv_dim, self.conv_dim, 3, 1, 1, bias=False) + ) + + def forward(self, x): + conv_x, trans_x = torch.split(self.conv1_1(x), (self.conv_dim, self.trans_dim), dim=1) + conv_x = self.conv_block(conv_x) + conv_x + trans_x = Rearrange('b c h w -> b h w c')(trans_x) + trans_x = self.trans_block(trans_x) + trans_x = Rearrange('b h w c -> b c h w')(trans_x) + res = self.conv1_2(torch.cat((conv_x, trans_x), dim=1)) + x = x + res + + return x + + +class SCUNet(nn.Module): + # def __init__(self, in_nc=3, config=[2, 2, 2, 2, 2, 2, 2], dim=64, drop_path_rate=0.0, input_resolution=256): + def __init__(self, in_nc=3, config=None, dim=64, drop_path_rate=0.0, input_resolution=256): + super(SCUNet, self).__init__() + if config is None: + config = [2, 2, 2, 2, 2, 2, 2] + self.config = config + self.dim = dim + self.head_dim = 32 + self.window_size = 8 + + # drop path rate for each layer + dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(config))] + + self.m_head = [nn.Conv2d(in_nc, dim, 3, 1, 1, bias=False)] + + begin = 0 + self.m_down1 = [ConvTransBlock(dim // 2, dim // 2, self.head_dim, self.window_size, dpr[i + begin], + 'W' if not i % 2 else 'SW', input_resolution) + for i in range(config[0])] + \ + [nn.Conv2d(dim, 2 * dim, 2, 2, 0, bias=False)] + + begin += config[0] + self.m_down2 = [ConvTransBlock(dim, dim, self.head_dim, self.window_size, dpr[i + begin], + 'W' if not i % 2 else 'SW', input_resolution // 2) + for i in range(config[1])] + \ + [nn.Conv2d(2 * dim, 4 * dim, 2, 2, 0, bias=False)] + + begin += config[1] + self.m_down3 = [ConvTransBlock(2 * dim, 2 * dim, self.head_dim, self.window_size, dpr[i + begin], + 'W' if not i % 2 else 'SW', input_resolution // 4) + for i in range(config[2])] + \ + [nn.Conv2d(4 * dim, 8 * dim, 2, 2, 0, bias=False)] + + begin += config[2] + self.m_body = [ConvTransBlock(4 * dim, 4 * dim, self.head_dim, self.window_size, dpr[i + begin], + 'W' if not i % 2 else 'SW', input_resolution // 8) + for i in range(config[3])] + + begin += config[3] + self.m_up3 = [nn.ConvTranspose2d(8 * dim, 4 * dim, 2, 2, 0, bias=False), ] + \ + [ConvTransBlock(2 * dim, 2 * dim, self.head_dim, self.window_size, dpr[i + begin], + 'W' if not i % 2 else 'SW', input_resolution // 4) + for i in range(config[4])] + + begin += config[4] + self.m_up2 = [nn.ConvTranspose2d(4 * dim, 2 * dim, 2, 2, 0, bias=False), ] + \ + [ConvTransBlock(dim, dim, self.head_dim, self.window_size, dpr[i + begin], + 'W' if not i % 2 else 'SW', input_resolution // 2) + for i in range(config[5])] + + begin += config[5] + self.m_up1 = [nn.ConvTranspose2d(2 * dim, dim, 2, 2, 0, bias=False), ] + \ + [ConvTransBlock(dim // 2, dim // 2, self.head_dim, self.window_size, dpr[i + begin], + 'W' if not i % 2 else 'SW', input_resolution) + for i in range(config[6])] + + self.m_tail = [nn.Conv2d(dim, in_nc, 3, 1, 1, bias=False)] + + self.m_head = nn.Sequential(*self.m_head) + self.m_down1 = nn.Sequential(*self.m_down1) + self.m_down2 = nn.Sequential(*self.m_down2) + self.m_down3 = nn.Sequential(*self.m_down3) + self.m_body = nn.Sequential(*self.m_body) + self.m_up3 = nn.Sequential(*self.m_up3) + self.m_up2 = nn.Sequential(*self.m_up2) + self.m_up1 = nn.Sequential(*self.m_up1) + self.m_tail = nn.Sequential(*self.m_tail) + # self.apply(self._init_weights) + + def forward(self, x0): + + h, w = x0.size()[-2:] + paddingBottom = int(np.ceil(h / 64) * 64 - h) + paddingRight = int(np.ceil(w / 64) * 64 - w) + x0 = nn.ReplicationPad2d((0, paddingRight, 0, paddingBottom))(x0) + + x1 = self.m_head(x0) + x2 = self.m_down1(x1) + x3 = self.m_down2(x2) + x4 = self.m_down3(x3) + x = self.m_body(x4) + x = self.m_up3(x + x4) + x = self.m_up2(x + x3) + x = self.m_up1(x + x2) + x = self.m_tail(x + x1) + + x = x[..., :h, :w] + + return x + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight, std=.02) + if m.bias is not None: + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.LayerNorm): + nn.init.constant_(m.bias, 0) + nn.init.constant_(m.weight, 1.0) \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions-builtin/SwinIR/preload.py b/sd/stable-diffusion-webui/extensions-builtin/SwinIR/preload.py new file mode 100644 index 0000000000000000000000000000000000000000..e912c6402bc80faa797cf2e95183101fb9a10286 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/SwinIR/preload.py @@ -0,0 +1,6 @@ +import os +from modules import paths + + +def preload(parser): + parser.add_argument("--swinir-models-path", type=str, help="Path to directory with SwinIR model file(s).", default=os.path.join(paths.models_path, 'SwinIR')) diff --git a/sd/stable-diffusion-webui/extensions-builtin/SwinIR/scripts/swinir_model.py b/sd/stable-diffusion-webui/extensions-builtin/SwinIR/scripts/swinir_model.py new file mode 100644 index 0000000000000000000000000000000000000000..e8783bca153954afd086536a6dee854ec5e17ba9 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/SwinIR/scripts/swinir_model.py @@ -0,0 +1,178 @@ +import contextlib +import os + +import numpy as np +import torch +from PIL import Image +from basicsr.utils.download_util import load_file_from_url +from tqdm import tqdm + +from modules import modelloader, devices, script_callbacks, shared +from modules.shared import cmd_opts, opts, state +from swinir_model_arch import SwinIR as net +from swinir_model_arch_v2 import Swin2SR as net2 +from modules.upscaler import Upscaler, UpscalerData + + +device_swinir = devices.get_device_for('swinir') + + +class UpscalerSwinIR(Upscaler): + def __init__(self, dirname): + self.name = "SwinIR" + self.model_url = "https://github.com/JingyunLiang/SwinIR/releases/download/v0.0" \ + "/003_realSR_BSRGAN_DFOWMFC_s64w8_SwinIR" \ + "-L_x4_GAN.pth " + self.model_name = "SwinIR 4x" + self.user_path = dirname + super().__init__() + scalers = [] + model_files = self.find_models(ext_filter=[".pt", ".pth"]) + for model in model_files: + if "http" in model: + name = self.model_name + else: + name = modelloader.friendly_name(model) + model_data = UpscalerData(name, model, self) + scalers.append(model_data) + self.scalers = scalers + + def do_upscale(self, img, model_file): + model = self.load_model(model_file) + if model is None: + return img + model = model.to(device_swinir, dtype=devices.dtype) + img = upscale(img, model) + try: + torch.cuda.empty_cache() + except: + pass + return img + + def load_model(self, path, scale=4): + if "http" in path: + dl_name = "%s%s" % (self.model_name.replace(" ", "_"), ".pth") + filename = load_file_from_url(url=path, model_dir=self.model_path, file_name=dl_name, progress=True) + else: + filename = path + if filename is None or not os.path.exists(filename): + return None + if filename.endswith(".v2.pth"): + model = net2( + upscale=scale, + in_chans=3, + img_size=64, + window_size=8, + img_range=1.0, + depths=[6, 6, 6, 6, 6, 6], + embed_dim=180, + num_heads=[6, 6, 6, 6, 6, 6], + mlp_ratio=2, + upsampler="nearest+conv", + resi_connection="1conv", + ) + params = None + else: + model = net( + upscale=scale, + in_chans=3, + img_size=64, + window_size=8, + img_range=1.0, + depths=[6, 6, 6, 6, 6, 6, 6, 6, 6], + embed_dim=240, + num_heads=[8, 8, 8, 8, 8, 8, 8, 8, 8], + mlp_ratio=2, + upsampler="nearest+conv", + resi_connection="3conv", + ) + params = "params_ema" + + pretrained_model = torch.load(filename) + if params is not None: + model.load_state_dict(pretrained_model[params], strict=True) + else: + model.load_state_dict(pretrained_model, strict=True) + return model + + +def upscale( + img, + model, + tile=None, + tile_overlap=None, + window_size=8, + scale=4, +): + tile = tile or opts.SWIN_tile + tile_overlap = tile_overlap or opts.SWIN_tile_overlap + + + img = np.array(img) + img = img[:, :, ::-1] + img = np.moveaxis(img, 2, 0) / 255 + img = torch.from_numpy(img).float() + img = img.unsqueeze(0).to(device_swinir, dtype=devices.dtype) + with torch.no_grad(), devices.autocast(): + _, _, h_old, w_old = img.size() + h_pad = (h_old // window_size + 1) * window_size - h_old + w_pad = (w_old // window_size + 1) * window_size - w_old + img = torch.cat([img, torch.flip(img, [2])], 2)[:, :, : h_old + h_pad, :] + img = torch.cat([img, torch.flip(img, [3])], 3)[:, :, :, : w_old + w_pad] + output = inference(img, model, tile, tile_overlap, window_size, scale) + output = output[..., : h_old * scale, : w_old * scale] + output = output.data.squeeze().float().cpu().clamp_(0, 1).numpy() + if output.ndim == 3: + output = np.transpose( + output[[2, 1, 0], :, :], (1, 2, 0) + ) # CHW-RGB to HCW-BGR + output = (output * 255.0).round().astype(np.uint8) # float32 to uint8 + return Image.fromarray(output, "RGB") + + +def inference(img, model, tile, tile_overlap, window_size, scale): + # test the image tile by tile + b, c, h, w = img.size() + tile = min(tile, h, w) + assert tile % window_size == 0, "tile size should be a multiple of window_size" + sf = scale + + stride = tile - tile_overlap + h_idx_list = list(range(0, h - tile, stride)) + [h - tile] + w_idx_list = list(range(0, w - tile, stride)) + [w - tile] + E = torch.zeros(b, c, h * sf, w * sf, dtype=devices.dtype, device=device_swinir).type_as(img) + W = torch.zeros_like(E, dtype=devices.dtype, device=device_swinir) + + with tqdm(total=len(h_idx_list) * len(w_idx_list), desc="SwinIR tiles") as pbar: + for h_idx in h_idx_list: + if state.interrupted or state.skipped: + break + + for w_idx in w_idx_list: + if state.interrupted or state.skipped: + break + + in_patch = img[..., h_idx: h_idx + tile, w_idx: w_idx + tile] + out_patch = model(in_patch) + out_patch_mask = torch.ones_like(out_patch) + + E[ + ..., h_idx * sf: (h_idx + tile) * sf, w_idx * sf: (w_idx + tile) * sf + ].add_(out_patch) + W[ + ..., h_idx * sf: (h_idx + tile) * sf, w_idx * sf: (w_idx + tile) * sf + ].add_(out_patch_mask) + pbar.update(1) + output = E.div_(W) + + return output + + +def on_ui_settings(): + import gradio as gr + + shared.opts.add_option("SWIN_tile", shared.OptionInfo(192, "Tile size for all SwinIR.", gr.Slider, {"minimum": 16, "maximum": 512, "step": 16}, section=('upscaling', "Upscaling"))) + shared.opts.add_option("SWIN_tile_overlap", shared.OptionInfo(8, "Tile overlap, in pixels for SwinIR. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}, section=('upscaling', "Upscaling"))) + + +script_callbacks.on_ui_settings(on_ui_settings) diff --git a/sd/stable-diffusion-webui/extensions-builtin/SwinIR/swinir_model_arch.py b/sd/stable-diffusion-webui/extensions-builtin/SwinIR/swinir_model_arch.py new file mode 100644 index 0000000000000000000000000000000000000000..863f42db6f50e5eac70931b8c0e6443f831a6018 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/SwinIR/swinir_model_arch.py @@ -0,0 +1,867 @@ +# ----------------------------------------------------------------------------------- +# SwinIR: Image Restoration Using Swin Transformer, https://arxiv.org/abs/2108.10257 +# Originally Written by Ze Liu, Modified by Jingyun Liang. +# ----------------------------------------------------------------------------------- + +import math +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.utils.checkpoint as checkpoint +from timm.models.layers import DropPath, to_2tuple, trunc_normal_ + + +class Mlp(nn.Module): + def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +def window_partition(x, window_size): + """ + Args: + x: (B, H, W, C) + window_size (int): window size + + Returns: + windows: (num_windows*B, window_size, window_size, C) + """ + B, H, W, C = x.shape + x = x.view(B, H // window_size, window_size, W // window_size, window_size, C) + windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C) + return windows + + +def window_reverse(windows, window_size, H, W): + """ + Args: + windows: (num_windows*B, window_size, window_size, C) + window_size (int): Window size + H (int): Height of image + W (int): Width of image + + Returns: + x: (B, H, W, C) + """ + B = int(windows.shape[0] / (H * W / window_size / window_size)) + x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1) + x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1) + return x + + +class WindowAttention(nn.Module): + r""" Window based multi-head self attention (W-MSA) module with relative position bias. + It supports both of shifted and non-shifted window. + + Args: + dim (int): Number of input channels. + window_size (tuple[int]): The height and width of the window. + num_heads (int): Number of attention heads. + qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True + qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set + attn_drop (float, optional): Dropout ratio of attention weight. Default: 0.0 + proj_drop (float, optional): Dropout ratio of output. Default: 0.0 + """ + + def __init__(self, dim, window_size, num_heads, qkv_bias=True, qk_scale=None, attn_drop=0., proj_drop=0.): + + super().__init__() + self.dim = dim + self.window_size = window_size # Wh, Ww + self.num_heads = num_heads + head_dim = dim // num_heads + self.scale = qk_scale or head_dim ** -0.5 + + # define a parameter table of relative position bias + self.relative_position_bias_table = nn.Parameter( + torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads)) # 2*Wh-1 * 2*Ww-1, nH + + # get pair-wise relative position index for each token inside the window + coords_h = torch.arange(self.window_size[0]) + coords_w = torch.arange(self.window_size[1]) + coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww + coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww + relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww + relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2 + relative_coords[:, :, 0] += self.window_size[0] - 1 # shift to start from 0 + relative_coords[:, :, 1] += self.window_size[1] - 1 + relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1 + relative_position_index = relative_coords.sum(-1) # Wh*Ww, Wh*Ww + self.register_buffer("relative_position_index", relative_position_index) + + self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + + self.proj_drop = nn.Dropout(proj_drop) + + trunc_normal_(self.relative_position_bias_table, std=.02) + self.softmax = nn.Softmax(dim=-1) + + def forward(self, x, mask=None): + """ + Args: + x: input features with shape of (num_windows*B, N, C) + mask: (0/-inf) mask with shape of (num_windows, Wh*Ww, Wh*Ww) or None + """ + B_, N, C = x.shape + qkv = self.qkv(x).reshape(B_, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4) + q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) + + q = q * self.scale + attn = (q @ k.transpose(-2, -1)) + + relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view( + self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1) # Wh*Ww,Wh*Ww,nH + relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww + attn = attn + relative_position_bias.unsqueeze(0) + + if mask is not None: + nW = mask.shape[0] + attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0) + attn = attn.view(-1, self.num_heads, N, N) + attn = self.softmax(attn) + else: + attn = self.softmax(attn) + + attn = self.attn_drop(attn) + + x = (attn @ v).transpose(1, 2).reshape(B_, N, C) + x = self.proj(x) + x = self.proj_drop(x) + return x + + def extra_repr(self) -> str: + return f'dim={self.dim}, window_size={self.window_size}, num_heads={self.num_heads}' + + def flops(self, N): + # calculate flops for 1 window with token length of N + flops = 0 + # qkv = self.qkv(x) + flops += N * self.dim * 3 * self.dim + # attn = (q @ k.transpose(-2, -1)) + flops += self.num_heads * N * (self.dim // self.num_heads) * N + # x = (attn @ v) + flops += self.num_heads * N * N * (self.dim // self.num_heads) + # x = self.proj(x) + flops += N * self.dim * self.dim + return flops + + +class SwinTransformerBlock(nn.Module): + r""" Swin Transformer Block. + + Args: + dim (int): Number of input channels. + input_resolution (tuple[int]): Input resolution. + num_heads (int): Number of attention heads. + window_size (int): Window size. + shift_size (int): Shift size for SW-MSA. + mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. + qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True + qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set. + drop (float, optional): Dropout rate. Default: 0.0 + attn_drop (float, optional): Attention dropout rate. Default: 0.0 + drop_path (float, optional): Stochastic depth rate. Default: 0.0 + act_layer (nn.Module, optional): Activation layer. Default: nn.GELU + norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm + """ + + def __init__(self, dim, input_resolution, num_heads, window_size=7, shift_size=0, + mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., drop_path=0., + act_layer=nn.GELU, norm_layer=nn.LayerNorm): + super().__init__() + self.dim = dim + self.input_resolution = input_resolution + self.num_heads = num_heads + self.window_size = window_size + self.shift_size = shift_size + self.mlp_ratio = mlp_ratio + if min(self.input_resolution) <= self.window_size: + # if window size is larger than input resolution, we don't partition windows + self.shift_size = 0 + self.window_size = min(self.input_resolution) + assert 0 <= self.shift_size < self.window_size, "shift_size must in 0-window_size" + + self.norm1 = norm_layer(dim) + self.attn = WindowAttention( + dim, window_size=to_2tuple(self.window_size), num_heads=num_heads, + qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop) + + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) + + if self.shift_size > 0: + attn_mask = self.calculate_mask(self.input_resolution) + else: + attn_mask = None + + self.register_buffer("attn_mask", attn_mask) + + def calculate_mask(self, x_size): + # calculate attention mask for SW-MSA + H, W = x_size + img_mask = torch.zeros((1, H, W, 1)) # 1 H W 1 + h_slices = (slice(0, -self.window_size), + slice(-self.window_size, -self.shift_size), + slice(-self.shift_size, None)) + w_slices = (slice(0, -self.window_size), + slice(-self.window_size, -self.shift_size), + slice(-self.shift_size, None)) + cnt = 0 + for h in h_slices: + for w in w_slices: + img_mask[:, h, w, :] = cnt + cnt += 1 + + mask_windows = window_partition(img_mask, self.window_size) # nW, window_size, window_size, 1 + mask_windows = mask_windows.view(-1, self.window_size * self.window_size) + attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2) + attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0)) + + return attn_mask + + def forward(self, x, x_size): + H, W = x_size + B, L, C = x.shape + # assert L == H * W, "input feature has wrong size" + + shortcut = x + x = self.norm1(x) + x = x.view(B, H, W, C) + + # cyclic shift + if self.shift_size > 0: + shifted_x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2)) + else: + shifted_x = x + + # partition windows + x_windows = window_partition(shifted_x, self.window_size) # nW*B, window_size, window_size, C + x_windows = x_windows.view(-1, self.window_size * self.window_size, C) # nW*B, window_size*window_size, C + + # W-MSA/SW-MSA (to be compatible for testing on images whose shapes are the multiple of window size + if self.input_resolution == x_size: + attn_windows = self.attn(x_windows, mask=self.attn_mask) # nW*B, window_size*window_size, C + else: + attn_windows = self.attn(x_windows, mask=self.calculate_mask(x_size).to(x.device)) + + # merge windows + attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C) + shifted_x = window_reverse(attn_windows, self.window_size, H, W) # B H' W' C + + # reverse cyclic shift + if self.shift_size > 0: + x = torch.roll(shifted_x, shifts=(self.shift_size, self.shift_size), dims=(1, 2)) + else: + x = shifted_x + x = x.view(B, H * W, C) + + # FFN + x = shortcut + self.drop_path(x) + x = x + self.drop_path(self.mlp(self.norm2(x))) + + return x + + def extra_repr(self) -> str: + return f"dim={self.dim}, input_resolution={self.input_resolution}, num_heads={self.num_heads}, " \ + f"window_size={self.window_size}, shift_size={self.shift_size}, mlp_ratio={self.mlp_ratio}" + + def flops(self): + flops = 0 + H, W = self.input_resolution + # norm1 + flops += self.dim * H * W + # W-MSA/SW-MSA + nW = H * W / self.window_size / self.window_size + flops += nW * self.attn.flops(self.window_size * self.window_size) + # mlp + flops += 2 * H * W * self.dim * self.dim * self.mlp_ratio + # norm2 + flops += self.dim * H * W + return flops + + +class PatchMerging(nn.Module): + r""" Patch Merging Layer. + + Args: + input_resolution (tuple[int]): Resolution of input feature. + dim (int): Number of input channels. + norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm + """ + + def __init__(self, input_resolution, dim, norm_layer=nn.LayerNorm): + super().__init__() + self.input_resolution = input_resolution + self.dim = dim + self.reduction = nn.Linear(4 * dim, 2 * dim, bias=False) + self.norm = norm_layer(4 * dim) + + def forward(self, x): + """ + x: B, H*W, C + """ + H, W = self.input_resolution + B, L, C = x.shape + assert L == H * W, "input feature has wrong size" + assert H % 2 == 0 and W % 2 == 0, f"x size ({H}*{W}) are not even." + + x = x.view(B, H, W, C) + + x0 = x[:, 0::2, 0::2, :] # B H/2 W/2 C + x1 = x[:, 1::2, 0::2, :] # B H/2 W/2 C + x2 = x[:, 0::2, 1::2, :] # B H/2 W/2 C + x3 = x[:, 1::2, 1::2, :] # B H/2 W/2 C + x = torch.cat([x0, x1, x2, x3], -1) # B H/2 W/2 4*C + x = x.view(B, -1, 4 * C) # B H/2*W/2 4*C + + x = self.norm(x) + x = self.reduction(x) + + return x + + def extra_repr(self) -> str: + return f"input_resolution={self.input_resolution}, dim={self.dim}" + + def flops(self): + H, W = self.input_resolution + flops = H * W * self.dim + flops += (H // 2) * (W // 2) * 4 * self.dim * 2 * self.dim + return flops + + +class BasicLayer(nn.Module): + """ A basic Swin Transformer layer for one stage. + + Args: + dim (int): Number of input channels. + input_resolution (tuple[int]): Input resolution. + depth (int): Number of blocks. + num_heads (int): Number of attention heads. + window_size (int): Local window size. + mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. + qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True + qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set. + drop (float, optional): Dropout rate. Default: 0.0 + attn_drop (float, optional): Attention dropout rate. Default: 0.0 + drop_path (float | tuple[float], optional): Stochastic depth rate. Default: 0.0 + norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm + downsample (nn.Module | None, optional): Downsample layer at the end of the layer. Default: None + use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False. + """ + + def __init__(self, dim, input_resolution, depth, num_heads, window_size, + mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., + drop_path=0., norm_layer=nn.LayerNorm, downsample=None, use_checkpoint=False): + + super().__init__() + self.dim = dim + self.input_resolution = input_resolution + self.depth = depth + self.use_checkpoint = use_checkpoint + + # build blocks + self.blocks = nn.ModuleList([ + SwinTransformerBlock(dim=dim, input_resolution=input_resolution, + num_heads=num_heads, window_size=window_size, + shift_size=0 if (i % 2 == 0) else window_size // 2, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop, attn_drop=attn_drop, + drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path, + norm_layer=norm_layer) + for i in range(depth)]) + + # patch merging layer + if downsample is not None: + self.downsample = downsample(input_resolution, dim=dim, norm_layer=norm_layer) + else: + self.downsample = None + + def forward(self, x, x_size): + for blk in self.blocks: + if self.use_checkpoint: + x = checkpoint.checkpoint(blk, x, x_size) + else: + x = blk(x, x_size) + if self.downsample is not None: + x = self.downsample(x) + return x + + def extra_repr(self) -> str: + return f"dim={self.dim}, input_resolution={self.input_resolution}, depth={self.depth}" + + def flops(self): + flops = 0 + for blk in self.blocks: + flops += blk.flops() + if self.downsample is not None: + flops += self.downsample.flops() + return flops + + +class RSTB(nn.Module): + """Residual Swin Transformer Block (RSTB). + + Args: + dim (int): Number of input channels. + input_resolution (tuple[int]): Input resolution. + depth (int): Number of blocks. + num_heads (int): Number of attention heads. + window_size (int): Local window size. + mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. + qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True + qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set. + drop (float, optional): Dropout rate. Default: 0.0 + attn_drop (float, optional): Attention dropout rate. Default: 0.0 + drop_path (float | tuple[float], optional): Stochastic depth rate. Default: 0.0 + norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm + downsample (nn.Module | None, optional): Downsample layer at the end of the layer. Default: None + use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False. + img_size: Input image size. + patch_size: Patch size. + resi_connection: The convolutional block before residual connection. + """ + + def __init__(self, dim, input_resolution, depth, num_heads, window_size, + mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., + drop_path=0., norm_layer=nn.LayerNorm, downsample=None, use_checkpoint=False, + img_size=224, patch_size=4, resi_connection='1conv'): + super(RSTB, self).__init__() + + self.dim = dim + self.input_resolution = input_resolution + + self.residual_group = BasicLayer(dim=dim, + input_resolution=input_resolution, + depth=depth, + num_heads=num_heads, + window_size=window_size, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop, attn_drop=attn_drop, + drop_path=drop_path, + norm_layer=norm_layer, + downsample=downsample, + use_checkpoint=use_checkpoint) + + if resi_connection == '1conv': + self.conv = nn.Conv2d(dim, dim, 3, 1, 1) + elif resi_connection == '3conv': + # to save parameters and memory + self.conv = nn.Sequential(nn.Conv2d(dim, dim // 4, 3, 1, 1), nn.LeakyReLU(negative_slope=0.2, inplace=True), + nn.Conv2d(dim // 4, dim // 4, 1, 1, 0), + nn.LeakyReLU(negative_slope=0.2, inplace=True), + nn.Conv2d(dim // 4, dim, 3, 1, 1)) + + self.patch_embed = PatchEmbed( + img_size=img_size, patch_size=patch_size, in_chans=0, embed_dim=dim, + norm_layer=None) + + self.patch_unembed = PatchUnEmbed( + img_size=img_size, patch_size=patch_size, in_chans=0, embed_dim=dim, + norm_layer=None) + + def forward(self, x, x_size): + return self.patch_embed(self.conv(self.patch_unembed(self.residual_group(x, x_size), x_size))) + x + + def flops(self): + flops = 0 + flops += self.residual_group.flops() + H, W = self.input_resolution + flops += H * W * self.dim * self.dim * 9 + flops += self.patch_embed.flops() + flops += self.patch_unembed.flops() + + return flops + + +class PatchEmbed(nn.Module): + r""" Image to Patch Embedding + + Args: + img_size (int): Image size. Default: 224. + patch_size (int): Patch token size. Default: 4. + in_chans (int): Number of input image channels. Default: 3. + embed_dim (int): Number of linear projection output channels. Default: 96. + norm_layer (nn.Module, optional): Normalization layer. Default: None + """ + + def __init__(self, img_size=224, patch_size=4, in_chans=3, embed_dim=96, norm_layer=None): + super().__init__() + img_size = to_2tuple(img_size) + patch_size = to_2tuple(patch_size) + patches_resolution = [img_size[0] // patch_size[0], img_size[1] // patch_size[1]] + self.img_size = img_size + self.patch_size = patch_size + self.patches_resolution = patches_resolution + self.num_patches = patches_resolution[0] * patches_resolution[1] + + self.in_chans = in_chans + self.embed_dim = embed_dim + + if norm_layer is not None: + self.norm = norm_layer(embed_dim) + else: + self.norm = None + + def forward(self, x): + x = x.flatten(2).transpose(1, 2) # B Ph*Pw C + if self.norm is not None: + x = self.norm(x) + return x + + def flops(self): + flops = 0 + H, W = self.img_size + if self.norm is not None: + flops += H * W * self.embed_dim + return flops + + +class PatchUnEmbed(nn.Module): + r""" Image to Patch Unembedding + + Args: + img_size (int): Image size. Default: 224. + patch_size (int): Patch token size. Default: 4. + in_chans (int): Number of input image channels. Default: 3. + embed_dim (int): Number of linear projection output channels. Default: 96. + norm_layer (nn.Module, optional): Normalization layer. Default: None + """ + + def __init__(self, img_size=224, patch_size=4, in_chans=3, embed_dim=96, norm_layer=None): + super().__init__() + img_size = to_2tuple(img_size) + patch_size = to_2tuple(patch_size) + patches_resolution = [img_size[0] // patch_size[0], img_size[1] // patch_size[1]] + self.img_size = img_size + self.patch_size = patch_size + self.patches_resolution = patches_resolution + self.num_patches = patches_resolution[0] * patches_resolution[1] + + self.in_chans = in_chans + self.embed_dim = embed_dim + + def forward(self, x, x_size): + B, HW, C = x.shape + x = x.transpose(1, 2).view(B, self.embed_dim, x_size[0], x_size[1]) # B Ph*Pw C + return x + + def flops(self): + flops = 0 + return flops + + +class Upsample(nn.Sequential): + """Upsample module. + + Args: + scale (int): Scale factor. Supported scales: 2^n and 3. + num_feat (int): Channel number of intermediate features. + """ + + def __init__(self, scale, num_feat): + m = [] + if (scale & (scale - 1)) == 0: # scale = 2^n + for _ in range(int(math.log(scale, 2))): + m.append(nn.Conv2d(num_feat, 4 * num_feat, 3, 1, 1)) + m.append(nn.PixelShuffle(2)) + elif scale == 3: + m.append(nn.Conv2d(num_feat, 9 * num_feat, 3, 1, 1)) + m.append(nn.PixelShuffle(3)) + else: + raise ValueError(f'scale {scale} is not supported. ' 'Supported scales: 2^n and 3.') + super(Upsample, self).__init__(*m) + + +class UpsampleOneStep(nn.Sequential): + """UpsampleOneStep module (the difference with Upsample is that it always only has 1conv + 1pixelshuffle) + Used in lightweight SR to save parameters. + + Args: + scale (int): Scale factor. Supported scales: 2^n and 3. + num_feat (int): Channel number of intermediate features. + + """ + + def __init__(self, scale, num_feat, num_out_ch, input_resolution=None): + self.num_feat = num_feat + self.input_resolution = input_resolution + m = [] + m.append(nn.Conv2d(num_feat, (scale ** 2) * num_out_ch, 3, 1, 1)) + m.append(nn.PixelShuffle(scale)) + super(UpsampleOneStep, self).__init__(*m) + + def flops(self): + H, W = self.input_resolution + flops = H * W * self.num_feat * 3 * 9 + return flops + + +class SwinIR(nn.Module): + r""" SwinIR + A PyTorch impl of : `SwinIR: Image Restoration Using Swin Transformer`, based on Swin Transformer. + + Args: + img_size (int | tuple(int)): Input image size. Default 64 + patch_size (int | tuple(int)): Patch size. Default: 1 + in_chans (int): Number of input image channels. Default: 3 + embed_dim (int): Patch embedding dimension. Default: 96 + depths (tuple(int)): Depth of each Swin Transformer layer. + num_heads (tuple(int)): Number of attention heads in different layers. + window_size (int): Window size. Default: 7 + mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. Default: 4 + qkv_bias (bool): If True, add a learnable bias to query, key, value. Default: True + qk_scale (float): Override default qk scale of head_dim ** -0.5 if set. Default: None + drop_rate (float): Dropout rate. Default: 0 + attn_drop_rate (float): Attention dropout rate. Default: 0 + drop_path_rate (float): Stochastic depth rate. Default: 0.1 + norm_layer (nn.Module): Normalization layer. Default: nn.LayerNorm. + ape (bool): If True, add absolute position embedding to the patch embedding. Default: False + patch_norm (bool): If True, add normalization after patch embedding. Default: True + use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False + upscale: Upscale factor. 2/3/4/8 for image SR, 1 for denoising and compress artifact reduction + img_range: Image range. 1. or 255. + upsampler: The reconstruction reconstruction module. 'pixelshuffle'/'pixelshuffledirect'/'nearest+conv'/None + resi_connection: The convolutional block before residual connection. '1conv'/'3conv' + """ + + def __init__(self, img_size=64, patch_size=1, in_chans=3, + embed_dim=96, depths=[6, 6, 6, 6], num_heads=[6, 6, 6, 6], + window_size=7, mlp_ratio=4., qkv_bias=True, qk_scale=None, + drop_rate=0., attn_drop_rate=0., drop_path_rate=0.1, + norm_layer=nn.LayerNorm, ape=False, patch_norm=True, + use_checkpoint=False, upscale=2, img_range=1., upsampler='', resi_connection='1conv', + **kwargs): + super(SwinIR, self).__init__() + num_in_ch = in_chans + num_out_ch = in_chans + num_feat = 64 + self.img_range = img_range + if in_chans == 3: + rgb_mean = (0.4488, 0.4371, 0.4040) + self.mean = torch.Tensor(rgb_mean).view(1, 3, 1, 1) + else: + self.mean = torch.zeros(1, 1, 1, 1) + self.upscale = upscale + self.upsampler = upsampler + self.window_size = window_size + + ##################################################################################################### + ################################### 1, shallow feature extraction ################################### + self.conv_first = nn.Conv2d(num_in_ch, embed_dim, 3, 1, 1) + + ##################################################################################################### + ################################### 2, deep feature extraction ###################################### + self.num_layers = len(depths) + self.embed_dim = embed_dim + self.ape = ape + self.patch_norm = patch_norm + self.num_features = embed_dim + self.mlp_ratio = mlp_ratio + + # split image into non-overlapping patches + self.patch_embed = PatchEmbed( + img_size=img_size, patch_size=patch_size, in_chans=embed_dim, embed_dim=embed_dim, + norm_layer=norm_layer if self.patch_norm else None) + num_patches = self.patch_embed.num_patches + patches_resolution = self.patch_embed.patches_resolution + self.patches_resolution = patches_resolution + + # merge non-overlapping patches into image + self.patch_unembed = PatchUnEmbed( + img_size=img_size, patch_size=patch_size, in_chans=embed_dim, embed_dim=embed_dim, + norm_layer=norm_layer if self.patch_norm else None) + + # absolute position embedding + if self.ape: + self.absolute_pos_embed = nn.Parameter(torch.zeros(1, num_patches, embed_dim)) + trunc_normal_(self.absolute_pos_embed, std=.02) + + self.pos_drop = nn.Dropout(p=drop_rate) + + # stochastic depth + dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] # stochastic depth decay rule + + # build Residual Swin Transformer blocks (RSTB) + self.layers = nn.ModuleList() + for i_layer in range(self.num_layers): + layer = RSTB(dim=embed_dim, + input_resolution=(patches_resolution[0], + patches_resolution[1]), + depth=depths[i_layer], + num_heads=num_heads[i_layer], + window_size=window_size, + mlp_ratio=self.mlp_ratio, + qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop_rate, attn_drop=attn_drop_rate, + drop_path=dpr[sum(depths[:i_layer]):sum(depths[:i_layer + 1])], # no impact on SR results + norm_layer=norm_layer, + downsample=None, + use_checkpoint=use_checkpoint, + img_size=img_size, + patch_size=patch_size, + resi_connection=resi_connection + + ) + self.layers.append(layer) + self.norm = norm_layer(self.num_features) + + # build the last conv layer in deep feature extraction + if resi_connection == '1conv': + self.conv_after_body = nn.Conv2d(embed_dim, embed_dim, 3, 1, 1) + elif resi_connection == '3conv': + # to save parameters and memory + self.conv_after_body = nn.Sequential(nn.Conv2d(embed_dim, embed_dim // 4, 3, 1, 1), + nn.LeakyReLU(negative_slope=0.2, inplace=True), + nn.Conv2d(embed_dim // 4, embed_dim // 4, 1, 1, 0), + nn.LeakyReLU(negative_slope=0.2, inplace=True), + nn.Conv2d(embed_dim // 4, embed_dim, 3, 1, 1)) + + ##################################################################################################### + ################################ 3, high quality image reconstruction ################################ + if self.upsampler == 'pixelshuffle': + # for classical SR + self.conv_before_upsample = nn.Sequential(nn.Conv2d(embed_dim, num_feat, 3, 1, 1), + nn.LeakyReLU(inplace=True)) + self.upsample = Upsample(upscale, num_feat) + self.conv_last = nn.Conv2d(num_feat, num_out_ch, 3, 1, 1) + elif self.upsampler == 'pixelshuffledirect': + # for lightweight SR (to save parameters) + self.upsample = UpsampleOneStep(upscale, embed_dim, num_out_ch, + (patches_resolution[0], patches_resolution[1])) + elif self.upsampler == 'nearest+conv': + # for real-world SR (less artifacts) + self.conv_before_upsample = nn.Sequential(nn.Conv2d(embed_dim, num_feat, 3, 1, 1), + nn.LeakyReLU(inplace=True)) + self.conv_up1 = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + if self.upscale == 4: + self.conv_up2 = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + self.conv_hr = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + self.conv_last = nn.Conv2d(num_feat, num_out_ch, 3, 1, 1) + self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) + else: + # for image denoising and JPEG compression artifact reduction + self.conv_last = nn.Conv2d(embed_dim, num_out_ch, 3, 1, 1) + + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight, std=.02) + if isinstance(m, nn.Linear) and m.bias is not None: + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.LayerNorm): + nn.init.constant_(m.bias, 0) + nn.init.constant_(m.weight, 1.0) + + @torch.jit.ignore + def no_weight_decay(self): + return {'absolute_pos_embed'} + + @torch.jit.ignore + def no_weight_decay_keywords(self): + return {'relative_position_bias_table'} + + def check_image_size(self, x): + _, _, h, w = x.size() + mod_pad_h = (self.window_size - h % self.window_size) % self.window_size + mod_pad_w = (self.window_size - w % self.window_size) % self.window_size + x = F.pad(x, (0, mod_pad_w, 0, mod_pad_h), 'reflect') + return x + + def forward_features(self, x): + x_size = (x.shape[2], x.shape[3]) + x = self.patch_embed(x) + if self.ape: + x = x + self.absolute_pos_embed + x = self.pos_drop(x) + + for layer in self.layers: + x = layer(x, x_size) + + x = self.norm(x) # B L C + x = self.patch_unembed(x, x_size) + + return x + + def forward(self, x): + H, W = x.shape[2:] + x = self.check_image_size(x) + + self.mean = self.mean.type_as(x) + x = (x - self.mean) * self.img_range + + if self.upsampler == 'pixelshuffle': + # for classical SR + x = self.conv_first(x) + x = self.conv_after_body(self.forward_features(x)) + x + x = self.conv_before_upsample(x) + x = self.conv_last(self.upsample(x)) + elif self.upsampler == 'pixelshuffledirect': + # for lightweight SR + x = self.conv_first(x) + x = self.conv_after_body(self.forward_features(x)) + x + x = self.upsample(x) + elif self.upsampler == 'nearest+conv': + # for real-world SR + x = self.conv_first(x) + x = self.conv_after_body(self.forward_features(x)) + x + x = self.conv_before_upsample(x) + x = self.lrelu(self.conv_up1(torch.nn.functional.interpolate(x, scale_factor=2, mode='nearest'))) + if self.upscale == 4: + x = self.lrelu(self.conv_up2(torch.nn.functional.interpolate(x, scale_factor=2, mode='nearest'))) + x = self.conv_last(self.lrelu(self.conv_hr(x))) + else: + # for image denoising and JPEG compression artifact reduction + x_first = self.conv_first(x) + res = self.conv_after_body(self.forward_features(x_first)) + x_first + x = x + self.conv_last(res) + + x = x / self.img_range + self.mean + + return x[:, :, :H*self.upscale, :W*self.upscale] + + def flops(self): + flops = 0 + H, W = self.patches_resolution + flops += H * W * 3 * self.embed_dim * 9 + flops += self.patch_embed.flops() + for i, layer in enumerate(self.layers): + flops += layer.flops() + flops += H * W * 3 * self.embed_dim * self.embed_dim + flops += self.upsample.flops() + return flops + + +if __name__ == '__main__': + upscale = 4 + window_size = 8 + height = (1024 // upscale // window_size + 1) * window_size + width = (720 // upscale // window_size + 1) * window_size + model = SwinIR(upscale=2, img_size=(height, width), + window_size=window_size, img_range=1., depths=[6, 6, 6, 6], + embed_dim=60, num_heads=[6, 6, 6, 6], mlp_ratio=2, upsampler='pixelshuffledirect') + print(model) + print(height, width, model.flops() / 1e9) + + x = torch.randn((1, 3, height, width)) + x = model(x) + print(x.shape) diff --git a/sd/stable-diffusion-webui/extensions-builtin/SwinIR/swinir_model_arch_v2.py b/sd/stable-diffusion-webui/extensions-builtin/SwinIR/swinir_model_arch_v2.py new file mode 100644 index 0000000000000000000000000000000000000000..a1255881a13b480d1b7564d7474e8bbb5fd7ee76 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/SwinIR/swinir_model_arch_v2.py @@ -0,0 +1,1017 @@ +# ----------------------------------------------------------------------------------- +# Swin2SR: Swin2SR: SwinV2 Transformer for Compressed Image Super-Resolution and Restoration, https://arxiv.org/abs/ +# Written by Conde and Choi et al. +# ----------------------------------------------------------------------------------- + +import math +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.utils.checkpoint as checkpoint +from timm.models.layers import DropPath, to_2tuple, trunc_normal_ + + +class Mlp(nn.Module): + def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +def window_partition(x, window_size): + """ + Args: + x: (B, H, W, C) + window_size (int): window size + Returns: + windows: (num_windows*B, window_size, window_size, C) + """ + B, H, W, C = x.shape + x = x.view(B, H // window_size, window_size, W // window_size, window_size, C) + windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C) + return windows + + +def window_reverse(windows, window_size, H, W): + """ + Args: + windows: (num_windows*B, window_size, window_size, C) + window_size (int): Window size + H (int): Height of image + W (int): Width of image + Returns: + x: (B, H, W, C) + """ + B = int(windows.shape[0] / (H * W / window_size / window_size)) + x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1) + x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1) + return x + +class WindowAttention(nn.Module): + r""" Window based multi-head self attention (W-MSA) module with relative position bias. + It supports both of shifted and non-shifted window. + Args: + dim (int): Number of input channels. + window_size (tuple[int]): The height and width of the window. + num_heads (int): Number of attention heads. + qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True + attn_drop (float, optional): Dropout ratio of attention weight. Default: 0.0 + proj_drop (float, optional): Dropout ratio of output. Default: 0.0 + pretrained_window_size (tuple[int]): The height and width of the window in pre-training. + """ + + def __init__(self, dim, window_size, num_heads, qkv_bias=True, attn_drop=0., proj_drop=0., + pretrained_window_size=[0, 0]): + + super().__init__() + self.dim = dim + self.window_size = window_size # Wh, Ww + self.pretrained_window_size = pretrained_window_size + self.num_heads = num_heads + + self.logit_scale = nn.Parameter(torch.log(10 * torch.ones((num_heads, 1, 1))), requires_grad=True) + + # mlp to generate continuous relative position bias + self.cpb_mlp = nn.Sequential(nn.Linear(2, 512, bias=True), + nn.ReLU(inplace=True), + nn.Linear(512, num_heads, bias=False)) + + # get relative_coords_table + relative_coords_h = torch.arange(-(self.window_size[0] - 1), self.window_size[0], dtype=torch.float32) + relative_coords_w = torch.arange(-(self.window_size[1] - 1), self.window_size[1], dtype=torch.float32) + relative_coords_table = torch.stack( + torch.meshgrid([relative_coords_h, + relative_coords_w])).permute(1, 2, 0).contiguous().unsqueeze(0) # 1, 2*Wh-1, 2*Ww-1, 2 + if pretrained_window_size[0] > 0: + relative_coords_table[:, :, :, 0] /= (pretrained_window_size[0] - 1) + relative_coords_table[:, :, :, 1] /= (pretrained_window_size[1] - 1) + else: + relative_coords_table[:, :, :, 0] /= (self.window_size[0] - 1) + relative_coords_table[:, :, :, 1] /= (self.window_size[1] - 1) + relative_coords_table *= 8 # normalize to -8, 8 + relative_coords_table = torch.sign(relative_coords_table) * torch.log2( + torch.abs(relative_coords_table) + 1.0) / np.log2(8) + + self.register_buffer("relative_coords_table", relative_coords_table) + + # get pair-wise relative position index for each token inside the window + coords_h = torch.arange(self.window_size[0]) + coords_w = torch.arange(self.window_size[1]) + coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww + coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww + relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww + relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2 + relative_coords[:, :, 0] += self.window_size[0] - 1 # shift to start from 0 + relative_coords[:, :, 1] += self.window_size[1] - 1 + relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1 + relative_position_index = relative_coords.sum(-1) # Wh*Ww, Wh*Ww + self.register_buffer("relative_position_index", relative_position_index) + + self.qkv = nn.Linear(dim, dim * 3, bias=False) + if qkv_bias: + self.q_bias = nn.Parameter(torch.zeros(dim)) + self.v_bias = nn.Parameter(torch.zeros(dim)) + else: + self.q_bias = None + self.v_bias = None + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + self.softmax = nn.Softmax(dim=-1) + + def forward(self, x, mask=None): + """ + Args: + x: input features with shape of (num_windows*B, N, C) + mask: (0/-inf) mask with shape of (num_windows, Wh*Ww, Wh*Ww) or None + """ + B_, N, C = x.shape + qkv_bias = None + if self.q_bias is not None: + qkv_bias = torch.cat((self.q_bias, torch.zeros_like(self.v_bias, requires_grad=False), self.v_bias)) + qkv = F.linear(input=x, weight=self.qkv.weight, bias=qkv_bias) + qkv = qkv.reshape(B_, N, 3, self.num_heads, -1).permute(2, 0, 3, 1, 4) + q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) + + # cosine attention + attn = (F.normalize(q, dim=-1) @ F.normalize(k, dim=-1).transpose(-2, -1)) + logit_scale = torch.clamp(self.logit_scale, max=torch.log(torch.tensor(1. / 0.01)).to(self.logit_scale.device)).exp() + attn = attn * logit_scale + + relative_position_bias_table = self.cpb_mlp(self.relative_coords_table).view(-1, self.num_heads) + relative_position_bias = relative_position_bias_table[self.relative_position_index.view(-1)].view( + self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1) # Wh*Ww,Wh*Ww,nH + relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww + relative_position_bias = 16 * torch.sigmoid(relative_position_bias) + attn = attn + relative_position_bias.unsqueeze(0) + + if mask is not None: + nW = mask.shape[0] + attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0) + attn = attn.view(-1, self.num_heads, N, N) + attn = self.softmax(attn) + else: + attn = self.softmax(attn) + + attn = self.attn_drop(attn) + + x = (attn @ v).transpose(1, 2).reshape(B_, N, C) + x = self.proj(x) + x = self.proj_drop(x) + return x + + def extra_repr(self) -> str: + return f'dim={self.dim}, window_size={self.window_size}, ' \ + f'pretrained_window_size={self.pretrained_window_size}, num_heads={self.num_heads}' + + def flops(self, N): + # calculate flops for 1 window with token length of N + flops = 0 + # qkv = self.qkv(x) + flops += N * self.dim * 3 * self.dim + # attn = (q @ k.transpose(-2, -1)) + flops += self.num_heads * N * (self.dim // self.num_heads) * N + # x = (attn @ v) + flops += self.num_heads * N * N * (self.dim // self.num_heads) + # x = self.proj(x) + flops += N * self.dim * self.dim + return flops + +class SwinTransformerBlock(nn.Module): + r""" Swin Transformer Block. + Args: + dim (int): Number of input channels. + input_resolution (tuple[int]): Input resulotion. + num_heads (int): Number of attention heads. + window_size (int): Window size. + shift_size (int): Shift size for SW-MSA. + mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. + qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True + drop (float, optional): Dropout rate. Default: 0.0 + attn_drop (float, optional): Attention dropout rate. Default: 0.0 + drop_path (float, optional): Stochastic depth rate. Default: 0.0 + act_layer (nn.Module, optional): Activation layer. Default: nn.GELU + norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm + pretrained_window_size (int): Window size in pre-training. + """ + + def __init__(self, dim, input_resolution, num_heads, window_size=7, shift_size=0, + mlp_ratio=4., qkv_bias=True, drop=0., attn_drop=0., drop_path=0., + act_layer=nn.GELU, norm_layer=nn.LayerNorm, pretrained_window_size=0): + super().__init__() + self.dim = dim + self.input_resolution = input_resolution + self.num_heads = num_heads + self.window_size = window_size + self.shift_size = shift_size + self.mlp_ratio = mlp_ratio + if min(self.input_resolution) <= self.window_size: + # if window size is larger than input resolution, we don't partition windows + self.shift_size = 0 + self.window_size = min(self.input_resolution) + assert 0 <= self.shift_size < self.window_size, "shift_size must in 0-window_size" + + self.norm1 = norm_layer(dim) + self.attn = WindowAttention( + dim, window_size=to_2tuple(self.window_size), num_heads=num_heads, + qkv_bias=qkv_bias, attn_drop=attn_drop, proj_drop=drop, + pretrained_window_size=to_2tuple(pretrained_window_size)) + + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) + + if self.shift_size > 0: + attn_mask = self.calculate_mask(self.input_resolution) + else: + attn_mask = None + + self.register_buffer("attn_mask", attn_mask) + + def calculate_mask(self, x_size): + # calculate attention mask for SW-MSA + H, W = x_size + img_mask = torch.zeros((1, H, W, 1)) # 1 H W 1 + h_slices = (slice(0, -self.window_size), + slice(-self.window_size, -self.shift_size), + slice(-self.shift_size, None)) + w_slices = (slice(0, -self.window_size), + slice(-self.window_size, -self.shift_size), + slice(-self.shift_size, None)) + cnt = 0 + for h in h_slices: + for w in w_slices: + img_mask[:, h, w, :] = cnt + cnt += 1 + + mask_windows = window_partition(img_mask, self.window_size) # nW, window_size, window_size, 1 + mask_windows = mask_windows.view(-1, self.window_size * self.window_size) + attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2) + attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0)) + + return attn_mask + + def forward(self, x, x_size): + H, W = x_size + B, L, C = x.shape + #assert L == H * W, "input feature has wrong size" + + shortcut = x + x = x.view(B, H, W, C) + + # cyclic shift + if self.shift_size > 0: + shifted_x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2)) + else: + shifted_x = x + + # partition windows + x_windows = window_partition(shifted_x, self.window_size) # nW*B, window_size, window_size, C + x_windows = x_windows.view(-1, self.window_size * self.window_size, C) # nW*B, window_size*window_size, C + + # W-MSA/SW-MSA (to be compatible for testing on images whose shapes are the multiple of window size + if self.input_resolution == x_size: + attn_windows = self.attn(x_windows, mask=self.attn_mask) # nW*B, window_size*window_size, C + else: + attn_windows = self.attn(x_windows, mask=self.calculate_mask(x_size).to(x.device)) + + # merge windows + attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C) + shifted_x = window_reverse(attn_windows, self.window_size, H, W) # B H' W' C + + # reverse cyclic shift + if self.shift_size > 0: + x = torch.roll(shifted_x, shifts=(self.shift_size, self.shift_size), dims=(1, 2)) + else: + x = shifted_x + x = x.view(B, H * W, C) + x = shortcut + self.drop_path(self.norm1(x)) + + # FFN + x = x + self.drop_path(self.norm2(self.mlp(x))) + + return x + + def extra_repr(self) -> str: + return f"dim={self.dim}, input_resolution={self.input_resolution}, num_heads={self.num_heads}, " \ + f"window_size={self.window_size}, shift_size={self.shift_size}, mlp_ratio={self.mlp_ratio}" + + def flops(self): + flops = 0 + H, W = self.input_resolution + # norm1 + flops += self.dim * H * W + # W-MSA/SW-MSA + nW = H * W / self.window_size / self.window_size + flops += nW * self.attn.flops(self.window_size * self.window_size) + # mlp + flops += 2 * H * W * self.dim * self.dim * self.mlp_ratio + # norm2 + flops += self.dim * H * W + return flops + +class PatchMerging(nn.Module): + r""" Patch Merging Layer. + Args: + input_resolution (tuple[int]): Resolution of input feature. + dim (int): Number of input channels. + norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm + """ + + def __init__(self, input_resolution, dim, norm_layer=nn.LayerNorm): + super().__init__() + self.input_resolution = input_resolution + self.dim = dim + self.reduction = nn.Linear(4 * dim, 2 * dim, bias=False) + self.norm = norm_layer(2 * dim) + + def forward(self, x): + """ + x: B, H*W, C + """ + H, W = self.input_resolution + B, L, C = x.shape + assert L == H * W, "input feature has wrong size" + assert H % 2 == 0 and W % 2 == 0, f"x size ({H}*{W}) are not even." + + x = x.view(B, H, W, C) + + x0 = x[:, 0::2, 0::2, :] # B H/2 W/2 C + x1 = x[:, 1::2, 0::2, :] # B H/2 W/2 C + x2 = x[:, 0::2, 1::2, :] # B H/2 W/2 C + x3 = x[:, 1::2, 1::2, :] # B H/2 W/2 C + x = torch.cat([x0, x1, x2, x3], -1) # B H/2 W/2 4*C + x = x.view(B, -1, 4 * C) # B H/2*W/2 4*C + + x = self.reduction(x) + x = self.norm(x) + + return x + + def extra_repr(self) -> str: + return f"input_resolution={self.input_resolution}, dim={self.dim}" + + def flops(self): + H, W = self.input_resolution + flops = (H // 2) * (W // 2) * 4 * self.dim * 2 * self.dim + flops += H * W * self.dim // 2 + return flops + +class BasicLayer(nn.Module): + """ A basic Swin Transformer layer for one stage. + Args: + dim (int): Number of input channels. + input_resolution (tuple[int]): Input resolution. + depth (int): Number of blocks. + num_heads (int): Number of attention heads. + window_size (int): Local window size. + mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. + qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True + drop (float, optional): Dropout rate. Default: 0.0 + attn_drop (float, optional): Attention dropout rate. Default: 0.0 + drop_path (float | tuple[float], optional): Stochastic depth rate. Default: 0.0 + norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm + downsample (nn.Module | None, optional): Downsample layer at the end of the layer. Default: None + use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False. + pretrained_window_size (int): Local window size in pre-training. + """ + + def __init__(self, dim, input_resolution, depth, num_heads, window_size, + mlp_ratio=4., qkv_bias=True, drop=0., attn_drop=0., + drop_path=0., norm_layer=nn.LayerNorm, downsample=None, use_checkpoint=False, + pretrained_window_size=0): + + super().__init__() + self.dim = dim + self.input_resolution = input_resolution + self.depth = depth + self.use_checkpoint = use_checkpoint + + # build blocks + self.blocks = nn.ModuleList([ + SwinTransformerBlock(dim=dim, input_resolution=input_resolution, + num_heads=num_heads, window_size=window_size, + shift_size=0 if (i % 2 == 0) else window_size // 2, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + drop=drop, attn_drop=attn_drop, + drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path, + norm_layer=norm_layer, + pretrained_window_size=pretrained_window_size) + for i in range(depth)]) + + # patch merging layer + if downsample is not None: + self.downsample = downsample(input_resolution, dim=dim, norm_layer=norm_layer) + else: + self.downsample = None + + def forward(self, x, x_size): + for blk in self.blocks: + if self.use_checkpoint: + x = checkpoint.checkpoint(blk, x, x_size) + else: + x = blk(x, x_size) + if self.downsample is not None: + x = self.downsample(x) + return x + + def extra_repr(self) -> str: + return f"dim={self.dim}, input_resolution={self.input_resolution}, depth={self.depth}" + + def flops(self): + flops = 0 + for blk in self.blocks: + flops += blk.flops() + if self.downsample is not None: + flops += self.downsample.flops() + return flops + + def _init_respostnorm(self): + for blk in self.blocks: + nn.init.constant_(blk.norm1.bias, 0) + nn.init.constant_(blk.norm1.weight, 0) + nn.init.constant_(blk.norm2.bias, 0) + nn.init.constant_(blk.norm2.weight, 0) + +class PatchEmbed(nn.Module): + r""" Image to Patch Embedding + Args: + img_size (int): Image size. Default: 224. + patch_size (int): Patch token size. Default: 4. + in_chans (int): Number of input image channels. Default: 3. + embed_dim (int): Number of linear projection output channels. Default: 96. + norm_layer (nn.Module, optional): Normalization layer. Default: None + """ + + def __init__(self, img_size=224, patch_size=4, in_chans=3, embed_dim=96, norm_layer=None): + super().__init__() + img_size = to_2tuple(img_size) + patch_size = to_2tuple(patch_size) + patches_resolution = [img_size[0] // patch_size[0], img_size[1] // patch_size[1]] + self.img_size = img_size + self.patch_size = patch_size + self.patches_resolution = patches_resolution + self.num_patches = patches_resolution[0] * patches_resolution[1] + + self.in_chans = in_chans + self.embed_dim = embed_dim + + self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size) + if norm_layer is not None: + self.norm = norm_layer(embed_dim) + else: + self.norm = None + + def forward(self, x): + B, C, H, W = x.shape + # FIXME look at relaxing size constraints + # assert H == self.img_size[0] and W == self.img_size[1], + # f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})." + x = self.proj(x).flatten(2).transpose(1, 2) # B Ph*Pw C + if self.norm is not None: + x = self.norm(x) + return x + + def flops(self): + Ho, Wo = self.patches_resolution + flops = Ho * Wo * self.embed_dim * self.in_chans * (self.patch_size[0] * self.patch_size[1]) + if self.norm is not None: + flops += Ho * Wo * self.embed_dim + return flops + +class RSTB(nn.Module): + """Residual Swin Transformer Block (RSTB). + + Args: + dim (int): Number of input channels. + input_resolution (tuple[int]): Input resolution. + depth (int): Number of blocks. + num_heads (int): Number of attention heads. + window_size (int): Local window size. + mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. + qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True + drop (float, optional): Dropout rate. Default: 0.0 + attn_drop (float, optional): Attention dropout rate. Default: 0.0 + drop_path (float | tuple[float], optional): Stochastic depth rate. Default: 0.0 + norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm + downsample (nn.Module | None, optional): Downsample layer at the end of the layer. Default: None + use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False. + img_size: Input image size. + patch_size: Patch size. + resi_connection: The convolutional block before residual connection. + """ + + def __init__(self, dim, input_resolution, depth, num_heads, window_size, + mlp_ratio=4., qkv_bias=True, drop=0., attn_drop=0., + drop_path=0., norm_layer=nn.LayerNorm, downsample=None, use_checkpoint=False, + img_size=224, patch_size=4, resi_connection='1conv'): + super(RSTB, self).__init__() + + self.dim = dim + self.input_resolution = input_resolution + + self.residual_group = BasicLayer(dim=dim, + input_resolution=input_resolution, + depth=depth, + num_heads=num_heads, + window_size=window_size, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + drop=drop, attn_drop=attn_drop, + drop_path=drop_path, + norm_layer=norm_layer, + downsample=downsample, + use_checkpoint=use_checkpoint) + + if resi_connection == '1conv': + self.conv = nn.Conv2d(dim, dim, 3, 1, 1) + elif resi_connection == '3conv': + # to save parameters and memory + self.conv = nn.Sequential(nn.Conv2d(dim, dim // 4, 3, 1, 1), nn.LeakyReLU(negative_slope=0.2, inplace=True), + nn.Conv2d(dim // 4, dim // 4, 1, 1, 0), + nn.LeakyReLU(negative_slope=0.2, inplace=True), + nn.Conv2d(dim // 4, dim, 3, 1, 1)) + + self.patch_embed = PatchEmbed( + img_size=img_size, patch_size=patch_size, in_chans=dim, embed_dim=dim, + norm_layer=None) + + self.patch_unembed = PatchUnEmbed( + img_size=img_size, patch_size=patch_size, in_chans=dim, embed_dim=dim, + norm_layer=None) + + def forward(self, x, x_size): + return self.patch_embed(self.conv(self.patch_unembed(self.residual_group(x, x_size), x_size))) + x + + def flops(self): + flops = 0 + flops += self.residual_group.flops() + H, W = self.input_resolution + flops += H * W * self.dim * self.dim * 9 + flops += self.patch_embed.flops() + flops += self.patch_unembed.flops() + + return flops + +class PatchUnEmbed(nn.Module): + r""" Image to Patch Unembedding + + Args: + img_size (int): Image size. Default: 224. + patch_size (int): Patch token size. Default: 4. + in_chans (int): Number of input image channels. Default: 3. + embed_dim (int): Number of linear projection output channels. Default: 96. + norm_layer (nn.Module, optional): Normalization layer. Default: None + """ + + def __init__(self, img_size=224, patch_size=4, in_chans=3, embed_dim=96, norm_layer=None): + super().__init__() + img_size = to_2tuple(img_size) + patch_size = to_2tuple(patch_size) + patches_resolution = [img_size[0] // patch_size[0], img_size[1] // patch_size[1]] + self.img_size = img_size + self.patch_size = patch_size + self.patches_resolution = patches_resolution + self.num_patches = patches_resolution[0] * patches_resolution[1] + + self.in_chans = in_chans + self.embed_dim = embed_dim + + def forward(self, x, x_size): + B, HW, C = x.shape + x = x.transpose(1, 2).view(B, self.embed_dim, x_size[0], x_size[1]) # B Ph*Pw C + return x + + def flops(self): + flops = 0 + return flops + + +class Upsample(nn.Sequential): + """Upsample module. + + Args: + scale (int): Scale factor. Supported scales: 2^n and 3. + num_feat (int): Channel number of intermediate features. + """ + + def __init__(self, scale, num_feat): + m = [] + if (scale & (scale - 1)) == 0: # scale = 2^n + for _ in range(int(math.log(scale, 2))): + m.append(nn.Conv2d(num_feat, 4 * num_feat, 3, 1, 1)) + m.append(nn.PixelShuffle(2)) + elif scale == 3: + m.append(nn.Conv2d(num_feat, 9 * num_feat, 3, 1, 1)) + m.append(nn.PixelShuffle(3)) + else: + raise ValueError(f'scale {scale} is not supported. ' 'Supported scales: 2^n and 3.') + super(Upsample, self).__init__(*m) + +class Upsample_hf(nn.Sequential): + """Upsample module. + + Args: + scale (int): Scale factor. Supported scales: 2^n and 3. + num_feat (int): Channel number of intermediate features. + """ + + def __init__(self, scale, num_feat): + m = [] + if (scale & (scale - 1)) == 0: # scale = 2^n + for _ in range(int(math.log(scale, 2))): + m.append(nn.Conv2d(num_feat, 4 * num_feat, 3, 1, 1)) + m.append(nn.PixelShuffle(2)) + elif scale == 3: + m.append(nn.Conv2d(num_feat, 9 * num_feat, 3, 1, 1)) + m.append(nn.PixelShuffle(3)) + else: + raise ValueError(f'scale {scale} is not supported. ' 'Supported scales: 2^n and 3.') + super(Upsample_hf, self).__init__(*m) + + +class UpsampleOneStep(nn.Sequential): + """UpsampleOneStep module (the difference with Upsample is that it always only has 1conv + 1pixelshuffle) + Used in lightweight SR to save parameters. + + Args: + scale (int): Scale factor. Supported scales: 2^n and 3. + num_feat (int): Channel number of intermediate features. + + """ + + def __init__(self, scale, num_feat, num_out_ch, input_resolution=None): + self.num_feat = num_feat + self.input_resolution = input_resolution + m = [] + m.append(nn.Conv2d(num_feat, (scale ** 2) * num_out_ch, 3, 1, 1)) + m.append(nn.PixelShuffle(scale)) + super(UpsampleOneStep, self).__init__(*m) + + def flops(self): + H, W = self.input_resolution + flops = H * W * self.num_feat * 3 * 9 + return flops + + + +class Swin2SR(nn.Module): + r""" Swin2SR + A PyTorch impl of : `Swin2SR: SwinV2 Transformer for Compressed Image Super-Resolution and Restoration`. + + Args: + img_size (int | tuple(int)): Input image size. Default 64 + patch_size (int | tuple(int)): Patch size. Default: 1 + in_chans (int): Number of input image channels. Default: 3 + embed_dim (int): Patch embedding dimension. Default: 96 + depths (tuple(int)): Depth of each Swin Transformer layer. + num_heads (tuple(int)): Number of attention heads in different layers. + window_size (int): Window size. Default: 7 + mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. Default: 4 + qkv_bias (bool): If True, add a learnable bias to query, key, value. Default: True + drop_rate (float): Dropout rate. Default: 0 + attn_drop_rate (float): Attention dropout rate. Default: 0 + drop_path_rate (float): Stochastic depth rate. Default: 0.1 + norm_layer (nn.Module): Normalization layer. Default: nn.LayerNorm. + ape (bool): If True, add absolute position embedding to the patch embedding. Default: False + patch_norm (bool): If True, add normalization after patch embedding. Default: True + use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False + upscale: Upscale factor. 2/3/4/8 for image SR, 1 for denoising and compress artifact reduction + img_range: Image range. 1. or 255. + upsampler: The reconstruction reconstruction module. 'pixelshuffle'/'pixelshuffledirect'/'nearest+conv'/None + resi_connection: The convolutional block before residual connection. '1conv'/'3conv' + """ + + def __init__(self, img_size=64, patch_size=1, in_chans=3, + embed_dim=96, depths=[6, 6, 6, 6], num_heads=[6, 6, 6, 6], + window_size=7, mlp_ratio=4., qkv_bias=True, + drop_rate=0., attn_drop_rate=0., drop_path_rate=0.1, + norm_layer=nn.LayerNorm, ape=False, patch_norm=True, + use_checkpoint=False, upscale=2, img_range=1., upsampler='', resi_connection='1conv', + **kwargs): + super(Swin2SR, self).__init__() + num_in_ch = in_chans + num_out_ch = in_chans + num_feat = 64 + self.img_range = img_range + if in_chans == 3: + rgb_mean = (0.4488, 0.4371, 0.4040) + self.mean = torch.Tensor(rgb_mean).view(1, 3, 1, 1) + else: + self.mean = torch.zeros(1, 1, 1, 1) + self.upscale = upscale + self.upsampler = upsampler + self.window_size = window_size + + ##################################################################################################### + ################################### 1, shallow feature extraction ################################### + self.conv_first = nn.Conv2d(num_in_ch, embed_dim, 3, 1, 1) + + ##################################################################################################### + ################################### 2, deep feature extraction ###################################### + self.num_layers = len(depths) + self.embed_dim = embed_dim + self.ape = ape + self.patch_norm = patch_norm + self.num_features = embed_dim + self.mlp_ratio = mlp_ratio + + # split image into non-overlapping patches + self.patch_embed = PatchEmbed( + img_size=img_size, patch_size=patch_size, in_chans=embed_dim, embed_dim=embed_dim, + norm_layer=norm_layer if self.patch_norm else None) + num_patches = self.patch_embed.num_patches + patches_resolution = self.patch_embed.patches_resolution + self.patches_resolution = patches_resolution + + # merge non-overlapping patches into image + self.patch_unembed = PatchUnEmbed( + img_size=img_size, patch_size=patch_size, in_chans=embed_dim, embed_dim=embed_dim, + norm_layer=norm_layer if self.patch_norm else None) + + # absolute position embedding + if self.ape: + self.absolute_pos_embed = nn.Parameter(torch.zeros(1, num_patches, embed_dim)) + trunc_normal_(self.absolute_pos_embed, std=.02) + + self.pos_drop = nn.Dropout(p=drop_rate) + + # stochastic depth + dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] # stochastic depth decay rule + + # build Residual Swin Transformer blocks (RSTB) + self.layers = nn.ModuleList() + for i_layer in range(self.num_layers): + layer = RSTB(dim=embed_dim, + input_resolution=(patches_resolution[0], + patches_resolution[1]), + depth=depths[i_layer], + num_heads=num_heads[i_layer], + window_size=window_size, + mlp_ratio=self.mlp_ratio, + qkv_bias=qkv_bias, + drop=drop_rate, attn_drop=attn_drop_rate, + drop_path=dpr[sum(depths[:i_layer]):sum(depths[:i_layer + 1])], # no impact on SR results + norm_layer=norm_layer, + downsample=None, + use_checkpoint=use_checkpoint, + img_size=img_size, + patch_size=patch_size, + resi_connection=resi_connection + + ) + self.layers.append(layer) + + if self.upsampler == 'pixelshuffle_hf': + self.layers_hf = nn.ModuleList() + for i_layer in range(self.num_layers): + layer = RSTB(dim=embed_dim, + input_resolution=(patches_resolution[0], + patches_resolution[1]), + depth=depths[i_layer], + num_heads=num_heads[i_layer], + window_size=window_size, + mlp_ratio=self.mlp_ratio, + qkv_bias=qkv_bias, + drop=drop_rate, attn_drop=attn_drop_rate, + drop_path=dpr[sum(depths[:i_layer]):sum(depths[:i_layer + 1])], # no impact on SR results + norm_layer=norm_layer, + downsample=None, + use_checkpoint=use_checkpoint, + img_size=img_size, + patch_size=patch_size, + resi_connection=resi_connection + + ) + self.layers_hf.append(layer) + + self.norm = norm_layer(self.num_features) + + # build the last conv layer in deep feature extraction + if resi_connection == '1conv': + self.conv_after_body = nn.Conv2d(embed_dim, embed_dim, 3, 1, 1) + elif resi_connection == '3conv': + # to save parameters and memory + self.conv_after_body = nn.Sequential(nn.Conv2d(embed_dim, embed_dim // 4, 3, 1, 1), + nn.LeakyReLU(negative_slope=0.2, inplace=True), + nn.Conv2d(embed_dim // 4, embed_dim // 4, 1, 1, 0), + nn.LeakyReLU(negative_slope=0.2, inplace=True), + nn.Conv2d(embed_dim // 4, embed_dim, 3, 1, 1)) + + ##################################################################################################### + ################################ 3, high quality image reconstruction ################################ + if self.upsampler == 'pixelshuffle': + # for classical SR + self.conv_before_upsample = nn.Sequential(nn.Conv2d(embed_dim, num_feat, 3, 1, 1), + nn.LeakyReLU(inplace=True)) + self.upsample = Upsample(upscale, num_feat) + self.conv_last = nn.Conv2d(num_feat, num_out_ch, 3, 1, 1) + elif self.upsampler == 'pixelshuffle_aux': + self.conv_bicubic = nn.Conv2d(num_in_ch, num_feat, 3, 1, 1) + self.conv_before_upsample = nn.Sequential( + nn.Conv2d(embed_dim, num_feat, 3, 1, 1), + nn.LeakyReLU(inplace=True)) + self.conv_aux = nn.Conv2d(num_feat, num_out_ch, 3, 1, 1) + self.conv_after_aux = nn.Sequential( + nn.Conv2d(3, num_feat, 3, 1, 1), + nn.LeakyReLU(inplace=True)) + self.upsample = Upsample(upscale, num_feat) + self.conv_last = nn.Conv2d(num_feat, num_out_ch, 3, 1, 1) + + elif self.upsampler == 'pixelshuffle_hf': + self.conv_before_upsample = nn.Sequential(nn.Conv2d(embed_dim, num_feat, 3, 1, 1), + nn.LeakyReLU(inplace=True)) + self.upsample = Upsample(upscale, num_feat) + self.upsample_hf = Upsample_hf(upscale, num_feat) + self.conv_last = nn.Conv2d(num_feat, num_out_ch, 3, 1, 1) + self.conv_first_hf = nn.Sequential(nn.Conv2d(num_feat, embed_dim, 3, 1, 1), + nn.LeakyReLU(inplace=True)) + self.conv_after_body_hf = nn.Conv2d(embed_dim, embed_dim, 3, 1, 1) + self.conv_before_upsample_hf = nn.Sequential( + nn.Conv2d(embed_dim, num_feat, 3, 1, 1), + nn.LeakyReLU(inplace=True)) + self.conv_last_hf = nn.Conv2d(num_feat, num_out_ch, 3, 1, 1) + + elif self.upsampler == 'pixelshuffledirect': + # for lightweight SR (to save parameters) + self.upsample = UpsampleOneStep(upscale, embed_dim, num_out_ch, + (patches_resolution[0], patches_resolution[1])) + elif self.upsampler == 'nearest+conv': + # for real-world SR (less artifacts) + assert self.upscale == 4, 'only support x4 now.' + self.conv_before_upsample = nn.Sequential(nn.Conv2d(embed_dim, num_feat, 3, 1, 1), + nn.LeakyReLU(inplace=True)) + self.conv_up1 = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + self.conv_up2 = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + self.conv_hr = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + self.conv_last = nn.Conv2d(num_feat, num_out_ch, 3, 1, 1) + self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) + else: + # for image denoising and JPEG compression artifact reduction + self.conv_last = nn.Conv2d(embed_dim, num_out_ch, 3, 1, 1) + + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight, std=.02) + if isinstance(m, nn.Linear) and m.bias is not None: + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.LayerNorm): + nn.init.constant_(m.bias, 0) + nn.init.constant_(m.weight, 1.0) + + @torch.jit.ignore + def no_weight_decay(self): + return {'absolute_pos_embed'} + + @torch.jit.ignore + def no_weight_decay_keywords(self): + return {'relative_position_bias_table'} + + def check_image_size(self, x): + _, _, h, w = x.size() + mod_pad_h = (self.window_size - h % self.window_size) % self.window_size + mod_pad_w = (self.window_size - w % self.window_size) % self.window_size + x = F.pad(x, (0, mod_pad_w, 0, mod_pad_h), 'reflect') + return x + + def forward_features(self, x): + x_size = (x.shape[2], x.shape[3]) + x = self.patch_embed(x) + if self.ape: + x = x + self.absolute_pos_embed + x = self.pos_drop(x) + + for layer in self.layers: + x = layer(x, x_size) + + x = self.norm(x) # B L C + x = self.patch_unembed(x, x_size) + + return x + + def forward_features_hf(self, x): + x_size = (x.shape[2], x.shape[3]) + x = self.patch_embed(x) + if self.ape: + x = x + self.absolute_pos_embed + x = self.pos_drop(x) + + for layer in self.layers_hf: + x = layer(x, x_size) + + x = self.norm(x) # B L C + x = self.patch_unembed(x, x_size) + + return x + + def forward(self, x): + H, W = x.shape[2:] + x = self.check_image_size(x) + + self.mean = self.mean.type_as(x) + x = (x - self.mean) * self.img_range + + if self.upsampler == 'pixelshuffle': + # for classical SR + x = self.conv_first(x) + x = self.conv_after_body(self.forward_features(x)) + x + x = self.conv_before_upsample(x) + x = self.conv_last(self.upsample(x)) + elif self.upsampler == 'pixelshuffle_aux': + bicubic = F.interpolate(x, size=(H * self.upscale, W * self.upscale), mode='bicubic', align_corners=False) + bicubic = self.conv_bicubic(bicubic) + x = self.conv_first(x) + x = self.conv_after_body(self.forward_features(x)) + x + x = self.conv_before_upsample(x) + aux = self.conv_aux(x) # b, 3, LR_H, LR_W + x = self.conv_after_aux(aux) + x = self.upsample(x)[:, :, :H * self.upscale, :W * self.upscale] + bicubic[:, :, :H * self.upscale, :W * self.upscale] + x = self.conv_last(x) + aux = aux / self.img_range + self.mean + elif self.upsampler == 'pixelshuffle_hf': + # for classical SR with HF + x = self.conv_first(x) + x = self.conv_after_body(self.forward_features(x)) + x + x_before = self.conv_before_upsample(x) + x_out = self.conv_last(self.upsample(x_before)) + + x_hf = self.conv_first_hf(x_before) + x_hf = self.conv_after_body_hf(self.forward_features_hf(x_hf)) + x_hf + x_hf = self.conv_before_upsample_hf(x_hf) + x_hf = self.conv_last_hf(self.upsample_hf(x_hf)) + x = x_out + x_hf + x_hf = x_hf / self.img_range + self.mean + + elif self.upsampler == 'pixelshuffledirect': + # for lightweight SR + x = self.conv_first(x) + x = self.conv_after_body(self.forward_features(x)) + x + x = self.upsample(x) + elif self.upsampler == 'nearest+conv': + # for real-world SR + x = self.conv_first(x) + x = self.conv_after_body(self.forward_features(x)) + x + x = self.conv_before_upsample(x) + x = self.lrelu(self.conv_up1(torch.nn.functional.interpolate(x, scale_factor=2, mode='nearest'))) + x = self.lrelu(self.conv_up2(torch.nn.functional.interpolate(x, scale_factor=2, mode='nearest'))) + x = self.conv_last(self.lrelu(self.conv_hr(x))) + else: + # for image denoising and JPEG compression artifact reduction + x_first = self.conv_first(x) + res = self.conv_after_body(self.forward_features(x_first)) + x_first + x = x + self.conv_last(res) + + x = x / self.img_range + self.mean + if self.upsampler == "pixelshuffle_aux": + return x[:, :, :H*self.upscale, :W*self.upscale], aux + + elif self.upsampler == "pixelshuffle_hf": + x_out = x_out / self.img_range + self.mean + return x_out[:, :, :H*self.upscale, :W*self.upscale], x[:, :, :H*self.upscale, :W*self.upscale], x_hf[:, :, :H*self.upscale, :W*self.upscale] + + else: + return x[:, :, :H*self.upscale, :W*self.upscale] + + def flops(self): + flops = 0 + H, W = self.patches_resolution + flops += H * W * 3 * self.embed_dim * 9 + flops += self.patch_embed.flops() + for i, layer in enumerate(self.layers): + flops += layer.flops() + flops += H * W * 3 * self.embed_dim * self.embed_dim + flops += self.upsample.flops() + return flops + + +if __name__ == '__main__': + upscale = 4 + window_size = 8 + height = (1024 // upscale // window_size + 1) * window_size + width = (720 // upscale // window_size + 1) * window_size + model = Swin2SR(upscale=2, img_size=(height, width), + window_size=window_size, img_range=1., depths=[6, 6, 6, 6], + embed_dim=60, num_heads=[6, 6, 6, 6], mlp_ratio=2, upsampler='pixelshuffledirect') + print(model) + print(height, width, model.flops() / 1e9) + + x = torch.randn((1, 3, height, width)) + x = model(x) + print(x.shape) \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions-builtin/prompt-bracket-checker/javascript/prompt-bracket-checker.js b/sd/stable-diffusion-webui/extensions-builtin/prompt-bracket-checker/javascript/prompt-bracket-checker.js new file mode 100644 index 0000000000000000000000000000000000000000..4a85c8ebf25110e911a6a1021fae6a014aa11000 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions-builtin/prompt-bracket-checker/javascript/prompt-bracket-checker.js @@ -0,0 +1,110 @@ +// Stable Diffusion WebUI - Bracket checker +// Version 1.0 +// By Hingashi no Florin/Bwin4L +// Counts open and closed brackets (round, square, curly) in the prompt and negative prompt text boxes in the txt2img and img2img tabs. +// If there's a mismatch, the keyword counter turns red and if you hover on it, a tooltip tells you what's wrong. + +function checkBrackets(evt, textArea, counterElt) { + errorStringParen = '(...) - Different number of opening and closing parentheses detected.\n'; + errorStringSquare = '[...] - Different number of opening and closing square brackets detected.\n'; + errorStringCurly = '{...} - Different number of opening and closing curly brackets detected.\n'; + + openBracketRegExp = /\(/g; + closeBracketRegExp = /\)/g; + + openSquareBracketRegExp = /\[/g; + closeSquareBracketRegExp = /\]/g; + + openCurlyBracketRegExp = /\{/g; + closeCurlyBracketRegExp = /\}/g; + + totalOpenBracketMatches = 0; + totalCloseBracketMatches = 0; + totalOpenSquareBracketMatches = 0; + totalCloseSquareBracketMatches = 0; + totalOpenCurlyBracketMatches = 0; + totalCloseCurlyBracketMatches = 0; + + openBracketMatches = textArea.value.match(openBracketRegExp); + if(openBracketMatches) { + totalOpenBracketMatches = openBracketMatches.length; + } + + closeBracketMatches = textArea.value.match(closeBracketRegExp); + if(closeBracketMatches) { + totalCloseBracketMatches = closeBracketMatches.length; + } + + openSquareBracketMatches = textArea.value.match(openSquareBracketRegExp); + if(openSquareBracketMatches) { + totalOpenSquareBracketMatches = openSquareBracketMatches.length; + } + + closeSquareBracketMatches = textArea.value.match(closeSquareBracketRegExp); + if(closeSquareBracketMatches) { + totalCloseSquareBracketMatches = closeSquareBracketMatches.length; + } + + openCurlyBracketMatches = textArea.value.match(openCurlyBracketRegExp); + if(openCurlyBracketMatches) { + totalOpenCurlyBracketMatches = openCurlyBracketMatches.length; + } + + closeCurlyBracketMatches = textArea.value.match(closeCurlyBracketRegExp); + if(closeCurlyBracketMatches) { + totalCloseCurlyBracketMatches = closeCurlyBracketMatches.length; + } + + if(totalOpenBracketMatches != totalCloseBracketMatches) { + if(!counterElt.title.includes(errorStringParen)) { + counterElt.title += errorStringParen; + } + } else { + counterElt.title = counterElt.title.replace(errorStringParen, ''); + } + + if(totalOpenSquareBracketMatches != totalCloseSquareBracketMatches) { + if(!counterElt.title.includes(errorStringSquare)) { + counterElt.title += errorStringSquare; + } + } else { + counterElt.title = counterElt.title.replace(errorStringSquare, ''); + } + + if(totalOpenCurlyBracketMatches != totalCloseCurlyBracketMatches) { + if(!counterElt.title.includes(errorStringCurly)) { + counterElt.title += errorStringCurly; + } + } else { + counterElt.title = counterElt.title.replace(errorStringCurly, ''); + } + + if(counterElt.title != '') { + counterElt.classList.add('error'); + } else { + counterElt.classList.remove('error'); + } +} + +function setupBracketChecking(id_prompt, id_counter){ + var textarea = gradioApp().querySelector("#" + id_prompt + " > label > textarea"); + var counter = gradioApp().getElementById(id_counter) + textarea.addEventListener("input", function(evt){ + checkBrackets(evt, textarea, counter) + }); +} + +var shadowRootLoaded = setInterval(function() { + var shadowRoot = document.querySelector('gradio-app').shadowRoot; + if(! shadowRoot) return false; + + var shadowTextArea = shadowRoot.querySelectorAll('#txt2img_prompt > label > textarea'); + if(shadowTextArea.length < 1) return false; + + clearInterval(shadowRootLoaded); + + setupBracketChecking('txt2img_prompt', 'txt2img_token_counter') + setupBracketChecking('txt2img_neg_prompt', 'txt2img_negative_token_counter') + setupBracketChecking('img2img_prompt', 'imgimg_token_counter') + setupBracketChecking('img2img_neg_prompt', 'img2img_negative_token_counter') +}, 1000); diff --git a/sd/stable-diffusion-webui/extensions/put extensions here.txt b/sd/stable-diffusion-webui/extensions/put extensions here.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/.github/ISSUE_TEMPLATE/bug_report.yml b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000000000000000000000000000000000..88339e16e7b9b05f688aa50a86471586292aa2d6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,84 @@ +name: Bug Report +description: Create a report +title: "[Bug]: " +labels: ["bug-report"] + +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered, and that it hasn't been fixed in a recent build/commit. + options: + - label: I have searched the existing issues and checked the recent builds/commits of both this extension and the webui + required: true + - type: markdown + attributes: + value: | + *Please fill this form with as much information as possible, don't forget to fill "What OS..." and "What browsers" and *provide screenshots if possible** + - type: textarea + id: what-did + attributes: + label: What happened? + description: Tell us what happened in a very clear and simple way + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to reproduce the problem + description: Please provide us with precise step by step information on how to reproduce the bug + value: | + 1. Go to .... + 2. Press .... + 3. ... + validations: + required: true + - type: textarea + id: what-should + attributes: + label: What should have happened? + description: Tell what you think the normal behavior should be + validations: + required: true + - type: textarea + id: commits + attributes: + label: Commit where the problem happens + description: Which commit of the extension are you running on? Please include the commit of both the extension and the webui (Do not write *Latest version/repo/commit*, as this means nothing and will have changed by the time we read your issue. Rather, copy the **Commit** link at the bottom of the UI, or from the cmd/terminal if you can't launch it.) + value: | + webui: + controlnet: + validations: + required: true + - type: dropdown + id: browsers + attributes: + label: What browsers do you use to access the UI ? + multiple: true + options: + - Mozilla Firefox + - Google Chrome + - Brave + - Apple Safari + - Microsoft Edge + - type: textarea + id: cmdargs + attributes: + label: Command Line Arguments + description: Are you using any launching parameters/command line arguments (modified webui-user .bat/.sh) ? If yes, please write them below. Write "No" otherwise. + render: Shell + validations: + required: true + - type: textarea + id: logs + attributes: + label: Console logs + description: Please provide **full** cmd/terminal logs from the moment you started UI to the end of it, after your bug happened. If it's very long, provide a link to pastebin or similar service. + render: Shell + validations: + required: true + - type: textarea + id: misc + attributes: + label: Additional information + description: Please provide us with any relevant additional info or context. diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/.github/ISSUE_TEMPLATE/config.yml b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000000000000000000000000000000000..0086358db1eb971c0cfa8739c27518bbc18a5ff4 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: true diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/.gitignore b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..72f2de9684a05ff65779c87575621ed7bccedca7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/.gitignore @@ -0,0 +1,166 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea +*.pt +*.pth +*.ckpt +*.safetensors +models/control_sd15_scribble.pth +detected_maps/ \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/LICENSE b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..9c10fff77734b9debaa02a563360e49de20b8ad8 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Kakigōri Maker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/README.md b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e5e7b72b32740ac727c8f6202f93219b8d5c3cc3 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/README.md @@ -0,0 +1,160 @@ +## sd-webui-controlnet +(WIP) WebUI extension for ControlNet and T2I-Adapter + +This extension is for AUTOMATIC1111's [Stable Diffusion web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui), allows the Web UI to add [ControlNet](https://github.com/lllyasviel/ControlNet) to the original Stable Diffusion model to generate images. The addition is on-the-fly, the merging is not required. + +ControlNet is a neural network structure to control diffusion models by adding extra conditions. + +Thanks & Inspired: kohya-ss/sd-webui-additional-networks + +### Limits + +* Dragging large file on the Web UI may freeze the entire page. It is better to use the upload file option instead. +* Just like WebUI's [hijack](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/3715ece0adce7bf7c5e9c5ab3710b2fdc3848f39/modules/sd_hijack_unet.py#L27), we used some interpolate to accept arbitrary size configure (see `scripts/cldm.py`) + +### Install + +1. Open "Extensions" tab. +2. Open "Install from URL" tab in the tab. +3. Enter URL of this repo to "URL for extension's git repository". +4. Press "Install" button. +5. Reload/Restart Web UI. + +Upgrade gradio if any ui issues occured: `pip install gradio==3.16.2` + +### Usage + +1. Put the ControlNet models (`.pt`, `.pth`, `.ckpt` or `.safetensors`) inside the `models/ControlNet` folder. +2. Open "txt2img" or "img2img" tab, write your prompts. +3. Press "Refresh models" and select the model you want to use. (If nothing appears, try reload/restart the webui) +4. Upload your image and select preprocessor, done. + +Currently it supports both full models and trimmed models. Use `extract_controlnet.py` to extract controlnet from original `.pth` file. + +Pretrained Models: https://huggingface.co/lllyasviel/ControlNet/tree/main/models + +### Extraction + +Two methods can be used to reduce the model's filesize: + +1. Directly extract controlnet from original .pth file using `extract_controlnet.py`. + +2. Transfer control from original checkpoint by making difference using `extract_controlnet_diff.py`. + +All type of models can be correctly recognized and loaded. The results of different extraction methods are discussed in https://github.com/lllyasviel/ControlNet/discussions/12 and https://github.com/Mikubill/sd-webui-controlnet/issues/73. + +Pre-extracted model: https://huggingface.co/webui/ControlNet-modules-safetensors + +Pre-extracted difference model: https://huggingface.co/kohya-ss/ControlNet-diff-modules + +### T2I-Adapter Support + +Note that the impl is experimental, result may differ from original repo. See `Adapter Examples` for reference. + +To use T2I-Adapter models: +1. Download files from https://huggingface.co/TencentARC/T2I-Adapter +2. Copy corresponding config file and rename it to the same name as the model - see list below. +3. It's better to use a slightly lower strength (t) when generating images with sketch model, such as 0.6-0.8. (ref: [ldm/models/diffusion/plms.py](https://github.com/TencentARC/T2I-Adapter/blob/5f41a0e38fc6eac90d04bc4cede85a2bc4570653/ldm/models/diffusion/plms.py#L158)) + +| Adapter | Config | +|:-------------------------:|:-------------------------:| +| t2iadapter_canny_sd14v1.pth | sketch_adapter_v14.yaml | +| t2iadapter_sketch_sd14v1.pth | sketch_adapter_v14.yaml | +| t2iadapter_seg_sd14v1.pth | image_adapter_v14.yaml | +| t2iadapter_keypose_sd14v1.pth | image_adapter_v14.yaml | +| t2iadapter_openpose_sd14v1.pth | image_adapter_v14.yaml | +| t2iadapter_color_sd14v1.pth | t2iadapter_color_sd14v1.yaml | +| t2iadapter_style_sd14v1.pth | t2iadapter_style_sd14v1.yaml | + +### Tips + +* Don't forget to add some negative prompt, default negative prompt in ControlNet repo is "longbody, lowres, bad anatomy, bad hands, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality". +* Regarding canvas height/width: they are designed for canvas generation. If you want to upload images directly, you can safely ignore them. + +### Examples + +| Source | Input | Output | +|:-------------------------:|:-------------------------:|:-------------------------:| +| (no preprocessor) | | | +| (no preprocessor) | | | +| | | | +| | | | +| | | | +| | | | +| | | | + +### Adapter Examples + +| Source | Input | Output | +|:-------------------------:|:-------------------------:|:-------------------------:| +| (no preprocessor) | | | +| (no preprocessor) | | | +| (no preprocessor) | | | +| (no preprocessor) | | | +| | | | +| | (clip, non-image) | | + +Examples by catboxanon, no tweaking or cherrypicking. (Color Guidance) + +| Image | Disabled | Enabled | +|:-------------------------:|:-------------------------:|:-------------------------:| +| | | | +| | | | + +### Minimum Requirements + +* (Windows) (NVIDIA: Ampere) 4gb - with `--xformers` enabled, and `Low VRAM` mode ticked in the UI, goes up to 768x832 + +### CFG Based ControlNet (Experimental) + +The original ControlNet applies control to both conditional (cond) and unconditional (uncond) parts. Enabling this option will make the control only apply to the cond part. Some experiments indicate that this approach improves image quality. + +To enable this option, tick `Enable CFG-Based guidance for ControlNet` in the settings. + +Note that you need to use a low cfg scale/guidance scale (such as 3-5) and proper weight tuning to get good result. + +### Guess Mode (Non-Prompt Mode, Experimental) + +Guess Mode is CFG Based ControlNet + Exponential decay in weighting. + +See issue https://github.com/Mikubill/sd-webui-controlnet/issues/236 for more details. + +Original introduction from controlnet: + +The "guess mode" (or called non-prompt mode) will completely unleash all the power of the very powerful ControlNet encoder. + +In this mode, you can just remove all prompts, and then the ControlNet encoder will recognize the content of the input control map, like depth map, edge map, scribbles, etc. + +This mode is very suitable for comparing different methods to control stable diffusion because the non-prompted generating task is significantly more difficult than prompted task. In this mode, different methods' performance will be very salient. + +For this mode, we recommend to **use 50 steps and guidance scale between 3 and 5.** + +### Multi-ControlNet / Joint Conditioning (Experimental) + +This option allows multiple ControlNet inputs for a single generation. To enable this option, change `Multi ControlNet: Max models amount (requires restart)` in the settings. Note that you will need to restart the WebUI for changes to take effect. + +* Guess Mode will apply to all ControlNet if any of them are enabled. + +| Source A | Source B | Output | +|:-------------------------:|:-------------------------:|:-------------------------:| +| | | | + +### Weight and Guidance Strength/Start/End + +Weight is the weight of the controlnet "influence". It's analogous to prompt attention/emphasis. E.g. (myprompt: 1.2). Technically, it's the factor by which to multiply the ControlNet outputs before merging them with original SD Unet. + +Guidance Start/End is the percentage of total steps the controlnet applies (guidance strength = guidance end). It's analogous to prompt editing/shifting. E.g. \[myprompt::0.8\] (It applies from the beginning until 80% of total steps) + +### API/Script Access + +This extension can accept txt2img or img2img tasks via API or external extension call. Note that you may need to enable `Allow other scripts to control this extension` in settings for external calls. + +To use the API: start WebUI with argument `--api` and go to `http://webui-address/docs` for documents or checkout [examples](https://github.com/Mikubill/sd-webui-controlnet/blob/main/example/api_txt2img.ipynb). + +To use external call: Checkout [Wiki](https://github.com/Mikubill/sd-webui-controlnet/wiki/API) + +### MacOS Support + +Tested with pytorch nightly: https://github.com/Mikubill/sd-webui-controlnet/pull/143#issuecomment-1435058285 + +To use this extension with mps and normal pytorch, currently you may need to start WebUI with `--no-half`. diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/binary/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/binary/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2d13ad692ffc109ad95789334bb5524d52794acc --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/binary/__init__.py @@ -0,0 +1,14 @@ +import cv2 + + +def apply_binary(img, bin_threshold): + img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) + + if bin_threshold == 0 or bin_threshold == 255: + # Otsu's threshold + otsu_threshold, img_bin = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) + print("Otsu threshold:", otsu_threshold) + else: + _, img_bin = cv2.threshold(img_gray, bin_threshold, 255, cv2.THRESH_BINARY_INV) + + return cv2.cvtColor(img_bin, cv2.COLOR_GRAY2RGB) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/canny/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/canny/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ace985839d3fc18dd4947f6c38e9f5d5a2625aca --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/canny/__init__.py @@ -0,0 +1,5 @@ +import cv2 + + +def apply_canny(img, low_threshold, high_threshold): + return cv2.Canny(img, low_threshold, high_threshold) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/clip/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/clip/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..997e90edcf6efcfbd2b4065be2d8de9ced7abb06 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/clip/__init__.py @@ -0,0 +1,23 @@ +from transformers import CLIPProcessor, CLIPVisionModel +from modules import devices + +version = 'openai/clip-vit-large-patch14' +clip_proc = None +clip_vision_model = None + +def apply_clip(img): + global clip_proc, clip_vision_model + + if clip_vision_model is None: + clip_proc = CLIPProcessor.from_pretrained(version) + clip_vision_model = CLIPVisionModel.from_pretrained(version) + + clip_vision_model = clip_vision_model.to(devices.get_device_for("controlnet")) + style_for_clip = clip_proc(images=img, return_tensors="pt")['pixel_values'] + style_feat = clip_vision_model(style_for_clip.to(devices.get_device_for("controlnet")))['last_hidden_state'] + return style_feat + +def unload_clip_model(): + global clip_proc, clip_vision_model + if clip_vision_model is not None: + clip_vision_model.cpu() \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/color/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/color/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f9453add3fb690ef903f0e0d445d09a6085d4858 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/color/__init__.py @@ -0,0 +1,6 @@ +import cv2 + +def apply_color(img, res=512): + input_img_color = cv2.resize(img, (res//64, res//64), interpolation=cv2.INTER_CUBIC) + input_img_color = cv2.resize(input_img_color, (res, res), interpolation=cv2.INTER_NEAREST) + return input_img_color \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/hed/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/hed/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8a37efbdcb58e94b32c91035f2446de837b86aa2 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/hed/__init__.py @@ -0,0 +1,148 @@ +from distutils import extension +import numpy as np +import cv2 +import torch +from einops import rearrange + +import os +from modules import devices +from modules.paths import models_path + +class Network(torch.nn.Module): + def __init__(self, model_path): + super().__init__() + + self.netVggOne = torch.nn.Sequential( + torch.nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False) + ) + + self.netVggTwo = torch.nn.Sequential( + torch.nn.MaxPool2d(kernel_size=2, stride=2), + torch.nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False) + ) + + self.netVggThr = torch.nn.Sequential( + torch.nn.MaxPool2d(kernel_size=2, stride=2), + torch.nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False) + ) + + self.netVggFou = torch.nn.Sequential( + torch.nn.MaxPool2d(kernel_size=2, stride=2), + torch.nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False) + ) + + self.netVggFiv = torch.nn.Sequential( + torch.nn.MaxPool2d(kernel_size=2, stride=2), + torch.nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False) + ) + + self.netScoreOne = torch.nn.Conv2d(in_channels=64, out_channels=1, kernel_size=1, stride=1, padding=0) + self.netScoreTwo = torch.nn.Conv2d(in_channels=128, out_channels=1, kernel_size=1, stride=1, padding=0) + self.netScoreThr = torch.nn.Conv2d(in_channels=256, out_channels=1, kernel_size=1, stride=1, padding=0) + self.netScoreFou = torch.nn.Conv2d(in_channels=512, out_channels=1, kernel_size=1, stride=1, padding=0) + self.netScoreFiv = torch.nn.Conv2d(in_channels=512, out_channels=1, kernel_size=1, stride=1, padding=0) + + self.netCombine = torch.nn.Sequential( + torch.nn.Conv2d(in_channels=5, out_channels=1, kernel_size=1, stride=1, padding=0), + torch.nn.Sigmoid() + ) + + self.load_state_dict({strKey.replace('module', 'net'): tenWeight for strKey, tenWeight in torch.load(model_path).items()}) + # end + + def forward(self, tenInput): + tenInput = tenInput * 255.0 + tenInput = tenInput - torch.tensor(data=[104.00698793, 116.66876762, 122.67891434], dtype=tenInput.dtype, device=tenInput.device).view(1, 3, 1, 1) + + tenVggOne = self.netVggOne(tenInput) + tenVggTwo = self.netVggTwo(tenVggOne) + tenVggThr = self.netVggThr(tenVggTwo) + tenVggFou = self.netVggFou(tenVggThr) + tenVggFiv = self.netVggFiv(tenVggFou) + + tenScoreOne = self.netScoreOne(tenVggOne) + tenScoreTwo = self.netScoreTwo(tenVggTwo) + tenScoreThr = self.netScoreThr(tenVggThr) + tenScoreFou = self.netScoreFou(tenVggFou) + tenScoreFiv = self.netScoreFiv(tenVggFiv) + + tenScoreOne = torch.nn.functional.interpolate(input=tenScoreOne, size=(tenInput.shape[2], tenInput.shape[3]), mode='bilinear', align_corners=False) + tenScoreTwo = torch.nn.functional.interpolate(input=tenScoreTwo, size=(tenInput.shape[2], tenInput.shape[3]), mode='bilinear', align_corners=False) + tenScoreThr = torch.nn.functional.interpolate(input=tenScoreThr, size=(tenInput.shape[2], tenInput.shape[3]), mode='bilinear', align_corners=False) + tenScoreFou = torch.nn.functional.interpolate(input=tenScoreFou, size=(tenInput.shape[2], tenInput.shape[3]), mode='bilinear', align_corners=False) + tenScoreFiv = torch.nn.functional.interpolate(input=tenScoreFiv, size=(tenInput.shape[2], tenInput.shape[3]), mode='bilinear', align_corners=False) + + return self.netCombine(torch.cat([ tenScoreOne, tenScoreTwo, tenScoreThr, tenScoreFou, tenScoreFiv ], 1)) + # end +# end + +netNetwork = None +remote_model_path = "https://huggingface.co/lllyasviel/ControlNet/resolve/main/annotator/ckpts/network-bsds500.pth" +modeldir = os.path.join(models_path, "hed") +old_modeldir = os.path.dirname(os.path.realpath(__file__)) + +def apply_hed(input_image): + global netNetwork + if netNetwork is None: + modelpath = os.path.join(modeldir, "network-bsds500.pth") + old_modelpath = os.path.join(old_modeldir, "network-bsds500.pth") + if os.path.exists(old_modelpath): + modelpath = old_modelpath + elif not os.path.exists(modelpath): + from basicsr.utils.download_util import load_file_from_url + load_file_from_url(remote_model_path, model_dir=modeldir) + netNetwork = Network(modelpath) + netNetwork.to(devices.get_device_for("controlnet")).eval() + + assert input_image.ndim == 3 + input_image = input_image[:, :, ::-1].copy() + with torch.no_grad(): + image_hed = torch.from_numpy(input_image).float().to(devices.get_device_for("controlnet")) + image_hed = image_hed / 255.0 + image_hed = rearrange(image_hed, 'h w c -> 1 c h w') + edge = netNetwork(image_hed)[0] + edge = (edge.cpu().numpy() * 255.0).clip(0, 255).astype(np.uint8) + return edge[0] + +def unload_hed_model(): + global netNetwork + if netNetwork is not None: + netNetwork.cpu() + +def nms(x, t, s): + x = cv2.GaussianBlur(x.astype(np.float32), (0, 0), s) + + f1 = np.array([[0, 0, 0], [1, 1, 1], [0, 0, 0]], dtype=np.uint8) + f2 = np.array([[0, 1, 0], [0, 1, 0], [0, 1, 0]], dtype=np.uint8) + f3 = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=np.uint8) + f4 = np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]], dtype=np.uint8) + + y = np.zeros_like(x) + + for f in [f1, f2, f3, f4]: + np.putmask(y, cv2.dilate(x, kernel=f) == x, x) + + z = np.zeros_like(y, dtype=np.uint8) + z[y > t] = 255 + return z diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/informative/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/informative/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fa7697f6e68a1e2899c290242900a750acd98c5e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/informative/__init__.py @@ -0,0 +1,131 @@ +from distutils import extension +import numpy as np +import cv2 +import torch +from einops import rearrange + +import os +from modules import devices +from modules.paths import models_path + +class Network(torch.nn.Module): + def __init__(self, model_path): + super().__init__() + + self.netVggOne = torch.nn.Sequential( + torch.nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False) + ) + + self.netVggTwo = torch.nn.Sequential( + torch.nn.MaxPool2d(kernel_size=2, stride=2), + torch.nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False) + ) + + self.netVggThr = torch.nn.Sequential( + torch.nn.MaxPool2d(kernel_size=2, stride=2), + torch.nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False) + ) + + self.netVggFou = torch.nn.Sequential( + torch.nn.MaxPool2d(kernel_size=2, stride=2), + torch.nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False) + ) + + self.netVggFiv = torch.nn.Sequential( + torch.nn.MaxPool2d(kernel_size=2, stride=2), + torch.nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False), + torch.nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1), + torch.nn.ReLU(inplace=False) + ) + + self.netScoreOne = torch.nn.Conv2d(in_channels=64, out_channels=1, kernel_size=1, stride=1, padding=0) + self.netScoreTwo = torch.nn.Conv2d(in_channels=128, out_channels=1, kernel_size=1, stride=1, padding=0) + self.netScoreThr = torch.nn.Conv2d(in_channels=256, out_channels=1, kernel_size=1, stride=1, padding=0) + self.netScoreFou = torch.nn.Conv2d(in_channels=512, out_channels=1, kernel_size=1, stride=1, padding=0) + self.netScoreFiv = torch.nn.Conv2d(in_channels=512, out_channels=1, kernel_size=1, stride=1, padding=0) + + self.netCombine = torch.nn.Sequential( + torch.nn.Conv2d(in_channels=5, out_channels=1, kernel_size=1, stride=1, padding=0), + torch.nn.Sigmoid() + ) + + self.load_state_dict({strKey.replace('module', 'net'): tenWeight for strKey, tenWeight in torch.load(model_path).items()}) + # end + + def forward(self, tenInput): + tenInput = tenInput * 255.0 + tenInput = tenInput - torch.tensor(data=[104.00698793, 116.66876762, 122.67891434], dtype=tenInput.dtype, device=tenInput.device).view(1, 3, 1, 1) + + tenVggOne = self.netVggOne(tenInput) + tenVggTwo = self.netVggTwo(tenVggOne) + tenVggThr = self.netVggThr(tenVggTwo) + tenVggFou = self.netVggFou(tenVggThr) + tenVggFiv = self.netVggFiv(tenVggFou) + + tenScoreOne = self.netScoreOne(tenVggOne) + tenScoreTwo = self.netScoreTwo(tenVggTwo) + tenScoreThr = self.netScoreThr(tenVggThr) + tenScoreFou = self.netScoreFou(tenVggFou) + tenScoreFiv = self.netScoreFiv(tenVggFiv) + + tenScoreOne = torch.nn.functional.interpolate(input=tenScoreOne, size=(tenInput.shape[2], tenInput.shape[3]), mode='bilinear', align_corners=False) + tenScoreTwo = torch.nn.functional.interpolate(input=tenScoreTwo, size=(tenInput.shape[2], tenInput.shape[3]), mode='bilinear', align_corners=False) + tenScoreThr = torch.nn.functional.interpolate(input=tenScoreThr, size=(tenInput.shape[2], tenInput.shape[3]), mode='bilinear', align_corners=False) + tenScoreFou = torch.nn.functional.interpolate(input=tenScoreFou, size=(tenInput.shape[2], tenInput.shape[3]), mode='bilinear', align_corners=False) + tenScoreFiv = torch.nn.functional.interpolate(input=tenScoreFiv, size=(tenInput.shape[2], tenInput.shape[3]), mode='bilinear', align_corners=False) + + return self.netCombine(torch.cat([ tenScoreOne, tenScoreTwo, tenScoreThr, tenScoreFou, tenScoreFiv ], 1)) + # end +# end + +netNetwork = None +remote_model_path = "https://huggingface.co/lllyasviel/ControlNet/resolve/main/annotator/ckpts/network-bsds500.pth" +modeldir = os.path.join(models_path, "hed") +old_modeldir = os.path.dirname(os.path.realpath(__file__)) + +def apply_hed(input_image): + global netNetwork + if netNetwork is None: + modelpath = os.path.join(modeldir, "network-bsds500.pth") + old_modelpath = os.path.join(old_modeldir, "network-bsds500.pth") + if os.path.exists(old_modelpath): + modelpath = old_modelpath + elif not os.path.exists(modelpath): + from basicsr.utils.download_util import load_file_from_url + load_file_from_url(remote_model_path, model_dir=modeldir) + netNetwork = Network(modelpath) + netNetwork.to(devices.get_device_for("controlnet")).eval() + + assert input_image.ndim == 3 + input_image = input_image[:, :, ::-1].copy() + with torch.no_grad(): + image_hed = torch.from_numpy(input_image).float().to(devices.get_device_for("controlnet")) + image_hed = image_hed / 255.0 + image_hed = rearrange(image_hed, 'h w c -> 1 c h w') + edge = netNetwork(image_hed)[0] + edge = (edge.cpu().numpy() * 255.0).clip(0, 255).astype(np.uint8) + return edge[0] + +def unload_hed_model(): + global netNetwork + if netNetwork is not None: + netNetwork.cpu() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/keypose/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/keypose/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2550fe50b9afe0c40effe102ab32c8d82bde310d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/keypose/__init__.py @@ -0,0 +1,212 @@ +import numpy as np +import cv2 +import torch + +import os +from modules import devices +from modules.paths import models_path + +import mmcv +from mmdet.apis import inference_detector, init_detector +from mmpose.apis import inference_top_down_pose_model +from mmpose.apis import init_pose_model, process_mmdet_results, vis_pose_result + + +def preprocessing(image, device): + # Resize + scale = 640 / max(image.shape[:2]) + image = cv2.resize(image, dsize=None, fx=scale, fy=scale) + raw_image = image.astype(np.uint8) + + # Subtract mean values + image = image.astype(np.float32) + image -= np.array( + [ + float(104.008), + float(116.669), + float(122.675), + ] + ) + + # Convert to torch.Tensor and add "batch" axis + image = torch.from_numpy(image.transpose(2, 0, 1)).float().unsqueeze(0) + image = image.to(device) + + return image, raw_image + + +def imshow_keypoints(img, + pose_result, + skeleton=None, + kpt_score_thr=0.1, + pose_kpt_color=None, + pose_link_color=None, + radius=4, + thickness=1): + """Draw keypoints and links on an image. + Args: + img (ndarry): The image to draw poses on. + pose_result (list[kpts]): The poses to draw. Each element kpts is + a set of K keypoints as an Kx3 numpy.ndarray, where each + keypoint is represented as x, y, score. + kpt_score_thr (float, optional): Minimum score of keypoints + to be shown. Default: 0.3. + pose_kpt_color (np.array[Nx3]`): Color of N keypoints. If None, + the keypoint will not be drawn. + pose_link_color (np.array[Mx3]): Color of M links. If None, the + links will not be drawn. + thickness (int): Thickness of lines. + """ + + img_h, img_w, _ = img.shape + img = np.zeros(img.shape) + + for idx, kpts in enumerate(pose_result): + if idx > 1: + continue + kpts = kpts['keypoints'] + # print(kpts) + kpts = np.array(kpts, copy=False) + + # draw each point on image + if pose_kpt_color is not None: + assert len(pose_kpt_color) == len(kpts) + + for kid, kpt in enumerate(kpts): + x_coord, y_coord, kpt_score = int(kpt[0]), int(kpt[1]), kpt[2] + + if kpt_score < kpt_score_thr or pose_kpt_color[kid] is None: + # skip the point that should not be drawn + continue + + color = tuple(int(c) for c in pose_kpt_color[kid]) + cv2.circle(img, (int(x_coord), int(y_coord)), + radius, color, -1) + + # draw links + if skeleton is not None and pose_link_color is not None: + assert len(pose_link_color) == len(skeleton) + + for sk_id, sk in enumerate(skeleton): + pos1 = (int(kpts[sk[0], 0]), int(kpts[sk[0], 1])) + pos2 = (int(kpts[sk[1], 0]), int(kpts[sk[1], 1])) + + if (pos1[0] <= 0 or pos1[0] >= img_w or pos1[1] <= 0 or pos1[1] >= img_h or pos2[0] <= 0 + or pos2[0] >= img_w or pos2[1] <= 0 or pos2[1] >= img_h or kpts[sk[0], 2] < kpt_score_thr + or kpts[sk[1], 2] < kpt_score_thr or pose_link_color[sk_id] is None): + # skip the link that should not be drawn + continue + color = tuple(int(c) for c in pose_link_color[sk_id]) + cv2.line(img, pos1, pos2, color, thickness=thickness) + + return img + + +human_det, pose_model = None, None +det_model_path = "https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth" +pose_model_path = "https://download.openmmlab.com/mmpose/top_down/hrnet/hrnet_w48_coco_256x192-b9e0b3ab_20200708.pth" + +modeldir = os.path.join(models_path, "keypose") +old_modeldir = os.path.dirname(os.path.realpath(__file__)) + +det_config = 'faster_rcnn_r50_fpn_coco.py' +pose_config = 'hrnet_w48_coco_256x192.py' + +det_checkpoint = 'faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth' +pose_checkpoint = 'hrnet_w48_coco_256x192-b9e0b3ab_20200708.pth' +det_cat_id = 1 +bbox_thr = 0.2 + +skeleton = [ + [15, 13], [13, 11], [16, 14], [14, 12], [11, 12], [5, 11], [6, 12], [5, 6], [5, 7], [6, 8], + [7, 9], [8, 10], + [1, 2], [0, 1], [0, 2], [1, 3], [2, 4], [3, 5], [4, 6] +] + +pose_kpt_color = [ + [51, 153, 255], [51, 153, 255], [51, 153, 255], [51, 153, 255], [51, 153, 255], + [0, 255, 0], + [255, 128, 0], [0, 255, 0], [255, 128, 0], [0, 255, 0], [255, 128, 0], [0, 255, 0], + [255, 128, 0], + [0, 255, 0], [255, 128, 0], [0, 255, 0], [255, 128, 0] +] + +pose_link_color = [ + [0, 255, 0], [0, 255, 0], [255, 128, 0], [255, 128, 0], + [51, 153, 255], [51, 153, 255], [51, 153, 255], [51, 153, 255], [0, 255, 0], + [255, 128, 0], + [0, 255, 0], [255, 128, 0], [51, 153, 255], [51, 153, 255], [51, 153, 255], + [51, 153, 255], + [51, 153, 255], [51, 153, 255], [51, 153, 255] +] + +def find_download_model(checkpoint, remote_path): + modelpath = os.path.join(modeldir, checkpoint) + old_modelpath = os.path.join(old_modeldir, checkpoint) + + if os.path.exists(old_modelpath): + modelpath = old_modelpath + elif not os.path.exists(modelpath): + from basicsr.utils.download_util import load_file_from_url + load_file_from_url(remote_path, model_dir=modeldir) + + return modelpath + +def apply_keypose(input_image): + global human_det, pose_model + if netNetwork is None: + det_model_local = find_download_model(det_checkpoint, det_model_path) + hrnet_model_local = find_download_model(pose_checkpoint, pose_model_path) + det_config_mmcv = mmcv.Config.fromfile(det_config) + pose_config_mmcv = mmcv.Config.fromfile(pose_config) + human_det = init_detector(det_config_mmcv, det_model_local, device=devices.get_device_for("controlnet")) + pose_model = init_pose_model(pose_config_mmcv, hrnet_model_local, device=devices.get_device_for("controlnet")) + + assert input_image.ndim == 3 + input_image = input_image.copy() + with torch.no_grad(): + image = torch.from_numpy(input_image).float().to(devices.get_device_for("controlnet")) + image = image / 255.0 + mmdet_results = inference_detector(human_det, image) + + # keep the person class bounding boxes. + person_results = process_mmdet_results(mmdet_results, det_cat_id) + + return_heatmap = False + dataset = pose_model.cfg.data['test']['type'] + + # e.g. use ('backbone', ) to return backbone feature + output_layer_names = None + pose_results, _ = inference_top_down_pose_model( + pose_model, + image, + person_results, + bbox_thr=bbox_thr, + format='xyxy', + dataset=dataset, + dataset_info=None, + return_heatmap=return_heatmap, + outputs=output_layer_names + ) + + im_keypose_out = imshow_keypoints( + image, + pose_results, + skeleton=skeleton, + pose_kpt_color=pose_kpt_color, + pose_link_color=pose_link_color, + radius=2, + thickness=2 + ) + im_keypose_out = im_keypose_out.astype(np.uint8) + + # image_hed = rearrange(image_hed, 'h w c -> 1 c h w') + # edge = netNetwork(image_hed)[0] + # edge = (edge.cpu().numpy() * 255.0).clip(0, 255).astype(np.uint8) + return im_keypose_out + + +def unload_hed_model(): + global netNetwork + if netNetwork is not None: + netNetwork.cpu() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/keypose/faster_rcnn_r50_fpn_coco.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/keypose/faster_rcnn_r50_fpn_coco.py new file mode 100644 index 0000000000000000000000000000000000000000..a9ad9528b22163ae7ce1390375b69227fd6eafd9 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/keypose/faster_rcnn_r50_fpn_coco.py @@ -0,0 +1,182 @@ +checkpoint_config = dict(interval=1) +# yapf:disable +log_config = dict( + interval=50, + hooks=[ + dict(type='TextLoggerHook'), + # dict(type='TensorboardLoggerHook') + ]) +# yapf:enable +dist_params = dict(backend='nccl') +log_level = 'INFO' +load_from = None +resume_from = None +workflow = [('train', 1)] +# optimizer +optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001) +optimizer_config = dict(grad_clip=None) +# learning policy +lr_config = dict( + policy='step', + warmup='linear', + warmup_iters=500, + warmup_ratio=0.001, + step=[8, 11]) +total_epochs = 12 + +model = dict( + type='FasterRCNN', + pretrained='torchvision://resnet50', + backbone=dict( + type='ResNet', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + frozen_stages=1, + norm_cfg=dict(type='BN', requires_grad=True), + norm_eval=True, + style='pytorch'), + neck=dict( + type='FPN', + in_channels=[256, 512, 1024, 2048], + out_channels=256, + num_outs=5), + rpn_head=dict( + type='RPNHead', + in_channels=256, + feat_channels=256, + anchor_generator=dict( + type='AnchorGenerator', + scales=[8], + ratios=[0.5, 1.0, 2.0], + strides=[4, 8, 16, 32, 64]), + bbox_coder=dict( + type='DeltaXYWHBBoxCoder', + target_means=[.0, .0, .0, .0], + target_stds=[1.0, 1.0, 1.0, 1.0]), + loss_cls=dict( + type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0), + loss_bbox=dict(type='L1Loss', loss_weight=1.0)), + roi_head=dict( + type='StandardRoIHead', + bbox_roi_extractor=dict( + type='SingleRoIExtractor', + roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0), + out_channels=256, + featmap_strides=[4, 8, 16, 32]), + bbox_head=dict( + type='Shared2FCBBoxHead', + in_channels=256, + fc_out_channels=1024, + roi_feat_size=7, + num_classes=80, + bbox_coder=dict( + type='DeltaXYWHBBoxCoder', + target_means=[0., 0., 0., 0.], + target_stds=[0.1, 0.1, 0.2, 0.2]), + reg_class_agnostic=False, + loss_cls=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0), + loss_bbox=dict(type='L1Loss', loss_weight=1.0))), + # model training and testing settings + train_cfg=dict( + rpn=dict( + assigner=dict( + type='MaxIoUAssigner', + pos_iou_thr=0.7, + neg_iou_thr=0.3, + min_pos_iou=0.3, + match_low_quality=True, + ignore_iof_thr=-1), + sampler=dict( + type='RandomSampler', + num=256, + pos_fraction=0.5, + neg_pos_ub=-1, + add_gt_as_proposals=False), + allowed_border=-1, + pos_weight=-1, + debug=False), + rpn_proposal=dict( + nms_pre=2000, + max_per_img=1000, + nms=dict(type='nms', iou_threshold=0.7), + min_bbox_size=0), + rcnn=dict( + assigner=dict( + type='MaxIoUAssigner', + pos_iou_thr=0.5, + neg_iou_thr=0.5, + min_pos_iou=0.5, + match_low_quality=False, + ignore_iof_thr=-1), + sampler=dict( + type='RandomSampler', + num=512, + pos_fraction=0.25, + neg_pos_ub=-1, + add_gt_as_proposals=True), + pos_weight=-1, + debug=False)), + test_cfg=dict( + rpn=dict( + nms_pre=1000, + max_per_img=1000, + nms=dict(type='nms', iou_threshold=0.7), + min_bbox_size=0), + rcnn=dict( + score_thr=0.05, + nms=dict(type='nms', iou_threshold=0.5), + max_per_img=100) + # soft-nms is also supported for rcnn testing + # e.g., nms=dict(type='soft_nms', iou_threshold=0.5, min_score=0.05) + )) + +dataset_type = 'CocoDataset' +data_root = 'data/coco' +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='LoadAnnotations', with_bbox=True), + dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), + dict(type='RandomFlip', flip_ratio=0.5), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size_divisor=32), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']), +] +test_pipeline = [ + dict(type='LoadImageFromFile'), + dict( + type='MultiScaleFlipAug', + img_scale=(1333, 800), + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size_divisor=32), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img']), + ]) +] +data = dict( + samples_per_gpu=2, + workers_per_gpu=2, + train=dict( + type=dataset_type, + ann_file=f'{data_root}/annotations/instances_train2017.json', + img_prefix=f'{data_root}/train2017/', + pipeline=train_pipeline), + val=dict( + type=dataset_type, + ann_file=f'{data_root}/annotations/instances_val2017.json', + img_prefix=f'{data_root}/val2017/', + pipeline=test_pipeline), + test=dict( + type=dataset_type, + ann_file=f'{data_root}/annotations/instances_val2017.json', + img_prefix=f'{data_root}/val2017/', + pipeline=test_pipeline)) +evaluation = dict(interval=1, metric='bbox') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/keypose/hrnet_w48_coco_256x192.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/keypose/hrnet_w48_coco_256x192.py new file mode 100644 index 0000000000000000000000000000000000000000..9755e6773cd3a8c0d2ac684c612d716cfd44b0ca --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/keypose/hrnet_w48_coco_256x192.py @@ -0,0 +1,169 @@ +# _base_ = [ +# '../../../../_base_/default_runtime.py', +# '../../../../_base_/datasets/coco.py' +# ] +evaluation = dict(interval=10, metric='mAP', save_best='AP') + +optimizer = dict( + type='Adam', + lr=5e-4, +) +optimizer_config = dict(grad_clip=None) +# learning policy +lr_config = dict( + policy='step', + warmup='linear', + warmup_iters=500, + warmup_ratio=0.001, + step=[170, 200]) +total_epochs = 210 +channel_cfg = dict( + num_output_channels=17, + dataset_joints=17, + dataset_channel=[ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + ], + inference_channel=[ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + ]) + +# model settings +model = dict( + type='TopDown', + pretrained='https://download.openmmlab.com/mmpose/' + 'pretrain_models/hrnet_w48-8ef0771d.pth', + backbone=dict( + type='HRNet', + in_channels=3, + extra=dict( + stage1=dict( + num_modules=1, + num_branches=1, + block='BOTTLENECK', + num_blocks=(4, ), + num_channels=(64, )), + stage2=dict( + num_modules=1, + num_branches=2, + block='BASIC', + num_blocks=(4, 4), + num_channels=(48, 96)), + stage3=dict( + num_modules=4, + num_branches=3, + block='BASIC', + num_blocks=(4, 4, 4), + num_channels=(48, 96, 192)), + stage4=dict( + num_modules=3, + num_branches=4, + block='BASIC', + num_blocks=(4, 4, 4, 4), + num_channels=(48, 96, 192, 384))), + ), + keypoint_head=dict( + type='TopdownHeatmapSimpleHead', + in_channels=48, + out_channels=channel_cfg['num_output_channels'], + num_deconv_layers=0, + extra=dict(final_conv_kernel=1, ), + loss_keypoint=dict(type='JointsMSELoss', use_target_weight=True)), + train_cfg=dict(), + test_cfg=dict( + flip_test=True, + post_process='default', + shift_heatmap=True, + modulate_kernel=11)) + +data_cfg = dict( + image_size=[192, 256], + heatmap_size=[48, 64], + num_output_channels=channel_cfg['num_output_channels'], + num_joints=channel_cfg['dataset_joints'], + dataset_channel=channel_cfg['dataset_channel'], + inference_channel=channel_cfg['inference_channel'], + soft_nms=False, + nms_thr=1.0, + oks_thr=0.9, + vis_thr=0.2, + use_gt_bbox=False, + det_bbox_thr=0.0, + bbox_file='data/coco/person_detection_results/' + 'COCO_val2017_detections_AP_H_56_person.json', +) + +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='TopDownGetBboxCenterScale', padding=1.25), + dict(type='TopDownRandomShiftBboxCenter', shift_factor=0.16, prob=0.3), + dict(type='TopDownRandomFlip', flip_prob=0.5), + dict( + type='TopDownHalfBodyTransform', + num_joints_half_body=8, + prob_half_body=0.3), + dict( + type='TopDownGetRandomScaleRotation', rot_factor=40, scale_factor=0.5), + dict(type='TopDownAffine'), + dict(type='ToTensor'), + dict( + type='NormalizeTensor', + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]), + dict(type='TopDownGenerateTarget', sigma=2), + dict( + type='Collect', + keys=['img', 'target', 'target_weight'], + meta_keys=[ + 'image_file', 'joints_3d', 'joints_3d_visible', 'center', 'scale', + 'rotation', 'bbox_score', 'flip_pairs' + ]), +] + +val_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='TopDownGetBboxCenterScale', padding=1.25), + dict(type='TopDownAffine'), + dict(type='ToTensor'), + dict( + type='NormalizeTensor', + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]), + dict( + type='Collect', + keys=['img'], + meta_keys=[ + 'image_file', 'center', 'scale', 'rotation', 'bbox_score', + 'flip_pairs' + ]), +] + +test_pipeline = val_pipeline + +data_root = 'data/coco' +data = dict( + samples_per_gpu=32, + workers_per_gpu=2, + val_dataloader=dict(samples_per_gpu=32), + test_dataloader=dict(samples_per_gpu=32), + train=dict( + type='TopDownCocoDataset', + ann_file=f'{data_root}/annotations/person_keypoints_train2017.json', + img_prefix=f'{data_root}/train2017/', + data_cfg=data_cfg, + pipeline=train_pipeline, + dataset_info={{_base_.dataset_info}}), + val=dict( + type='TopDownCocoDataset', + ann_file=f'{data_root}/annotations/person_keypoints_val2017.json', + img_prefix=f'{data_root}/val2017/', + data_cfg=data_cfg, + pipeline=val_pipeline, + dataset_info={{_base_.dataset_info}}), + test=dict( + type='TopDownCocoDataset', + ann_file=f'{data_root}/annotations/person_keypoints_val2017.json', + img_prefix=f'{data_root}/val2017/', + data_cfg=data_cfg, + pipeline=test_pipeline, + dataset_info={{_base_.dataset_info}}), +) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ce75553daf861f95dd4e3707b0c9b2ed5de4b1a9 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/__init__.py @@ -0,0 +1,115 @@ +import cv2 +import numpy as np +import torch +import os +from modules import devices, shared +from modules.paths import models_path +from torchvision.transforms import transforms + +# AdelaiDepth/LeReS imports +from .leres.depthmap import estimateleres, estimateboost +from .leres.multi_depth_model_woauxi import RelDepthModel +from .leres.net_tools import strip_prefix_if_present + +# pix2pix/merge net imports +from .pix2pix.options.test_options import TestOptions +from .pix2pix.models.pix2pix4depth_model import Pix2Pix4DepthModel + +base_model_path = os.path.join(models_path, "leres") +old_modeldir = os.path.dirname(os.path.realpath(__file__)) + +remote_model_path_leres = "https://cloudstor.aarnet.edu.au/plus/s/lTIJF4vrvHCAI31/download" +remote_model_path_pix2pix = "https://sfu.ca/~yagiz/CVPR21/latest_net_G.pth" + +model = None +pix2pixmodel = None + +def unload_leres_model(): + global model, pix2pixmodel + if model is not None: + model = model.cpu() + if pix2pixmodel is not None: + pix2pixmodel = pix2pixmodel.unload_network('G') + +def apply_leres(input_image, thr_a, thr_b): + global model, pix2pixmodel + boost = shared.opts.data.get("control_net_monocular_depth_optim", False) + + if model is None: + model_path = os.path.join(base_model_path, "res101.pth") + old_model_path = os.path.join(old_modeldir, "res101.pth") + + if os.path.exists(old_model_path): + model_path = old_model_path + elif not os.path.exists(model_path): + from basicsr.utils.download_util import load_file_from_url + load_file_from_url(remote_model_path_leres, model_dir=base_model_path) + os.rename(os.path.join(base_model_path, 'download'), model_path) + + if torch.cuda.is_available(): + checkpoint = torch.load(model_path) + else: + checkpoint = torch.load(model_path,map_location=torch.device('cpu')) + + model = RelDepthModel(backbone='resnext101') + model.load_state_dict(strip_prefix_if_present(checkpoint['depth_model'], "module."), strict=True) + del checkpoint + + if boost and pix2pixmodel is None: + pix2pixmodel_path = os.path.join(base_model_path, "latest_net_G.pth") + if not os.path.exists(pix2pixmodel_path): + from basicsr.utils.download_util import load_file_from_url + load_file_from_url(remote_model_path_pix2pix, model_dir=base_model_path) + + opt = TestOptions().parse() + if not torch.cuda.is_available(): + opt.gpu_ids = [] # cpu mode + pix2pixmodel = Pix2Pix4DepthModel(opt) + pix2pixmodel.save_dir = base_model_path + pix2pixmodel.load_networks('latest') + pix2pixmodel.eval() + + if devices.get_device_for("controlnet").type != 'mps': + model = model.to(devices.get_device_for("controlnet")) + + assert input_image.ndim == 3 + height, width, dim = input_image.shape + + with torch.no_grad(): + + if boost: + depth = estimateboost(input_image, model, 0, pix2pixmodel, max(width, height)) + else: + depth = estimateleres(input_image, model, width, height) + + numbytes=2 + depth_min = depth.min() + depth_max = depth.max() + max_val = (2**(8*numbytes))-1 + + # check output before normalizing and mapping to 16 bit + if depth_max - depth_min > np.finfo("float").eps: + out = max_val * (depth - depth_min) / (depth_max - depth_min) + else: + out = np.zeros(depth.shape) + + # single channel, 16 bit image + depth_image = out.astype("uint16") + + # convert to uint8 + depth_image = cv2.convertScaleAbs(depth_image, alpha=(255.0/65535.0)) + + # remove near + if thr_a != 0: + thr_a = ((thr_a/100)*255) + depth_image = cv2.threshold(depth_image, thr_a, 255, cv2.THRESH_TOZERO)[1] + + # invert image + depth_image = cv2.bitwise_not(depth_image) + + # remove bg + if thr_b != 0: + thr_b = ((thr_b/100)*255) + depth_image = cv2.threshold(depth_image, thr_b, 255, cv2.THRESH_TOZERO)[1] + + return depth_image diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/LICENSE b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..e0f1d07d98d4e85e684734d058dfe2515d215405 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/LICENSE @@ -0,0 +1,23 @@ +https://github.com/thygate/stable-diffusion-webui-depthmap-script + +MIT License + +Copyright (c) 2023 Bob Thiry + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/Resnet.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/Resnet.py new file mode 100644 index 0000000000000000000000000000000000000000..f12c9975c1aa05401269be3ca3dbaa56bde55581 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/Resnet.py @@ -0,0 +1,199 @@ +import torch.nn as nn +import torch.nn as NN + +__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', + 'resnet152'] + + +model_urls = { + 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', + 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', + 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', + 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', + 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', +} + + +def conv3x3(in_planes, out_planes, stride=1): + """3x3 convolution with padding""" + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, + padding=1, bias=False) + + +class BasicBlock(nn.Module): + expansion = 1 + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(BasicBlock, self).__init__() + self.conv1 = conv3x3(inplanes, planes, stride) + self.bn1 = NN.BatchNorm2d(planes) #NN.BatchNorm2d + self.relu = nn.ReLU(inplace=True) + self.conv2 = conv3x3(planes, planes) + self.bn2 = NN.BatchNorm2d(planes) #NN.BatchNorm2d + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class Bottleneck(nn.Module): + expansion = 4 + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(Bottleneck, self).__init__() + self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) + self.bn1 = NN.BatchNorm2d(planes) #NN.BatchNorm2d + self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, + padding=1, bias=False) + self.bn2 = NN.BatchNorm2d(planes) #NN.BatchNorm2d + self.conv3 = nn.Conv2d(planes, planes * self.expansion, kernel_size=1, bias=False) + self.bn3 = NN.BatchNorm2d(planes * self.expansion) #NN.BatchNorm2d + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class ResNet(nn.Module): + + def __init__(self, block, layers, num_classes=1000): + self.inplanes = 64 + super(ResNet, self).__init__() + self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, + bias=False) + self.bn1 = NN.BatchNorm2d(64) #NN.BatchNorm2d + self.relu = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + self.layer1 = self._make_layer(block, 64, layers[0]) + self.layer2 = self._make_layer(block, 128, layers[1], stride=2) + self.layer3 = self._make_layer(block, 256, layers[2], stride=2) + self.layer4 = self._make_layer(block, 512, layers[3], stride=2) + #self.avgpool = nn.AvgPool2d(7, stride=1) + #self.fc = nn.Linear(512 * block.expansion, num_classes) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') + elif isinstance(m, nn.BatchNorm2d): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + + def _make_layer(self, block, planes, blocks, stride=1): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2d(self.inplanes, planes * block.expansion, + kernel_size=1, stride=stride, bias=False), + NN.BatchNorm2d(planes * block.expansion), #NN.BatchNorm2d + ) + + layers = [] + layers.append(block(self.inplanes, planes, stride, downsample)) + self.inplanes = planes * block.expansion + for i in range(1, blocks): + layers.append(block(self.inplanes, planes)) + + return nn.Sequential(*layers) + + def forward(self, x): + features = [] + + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + x = self.maxpool(x) + + x = self.layer1(x) + features.append(x) + x = self.layer2(x) + features.append(x) + x = self.layer3(x) + features.append(x) + x = self.layer4(x) + features.append(x) + + return features + + +def resnet18(pretrained=True, **kwargs): + """Constructs a ResNet-18 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) + return model + + +def resnet34(pretrained=True, **kwargs): + """Constructs a ResNet-34 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs) + return model + + +def resnet50(pretrained=True, **kwargs): + """Constructs a ResNet-50 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) + + return model + + +def resnet101(pretrained=True, **kwargs): + """Constructs a ResNet-101 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) + + return model + + +def resnet152(pretrained=True, **kwargs): + """Constructs a ResNet-152 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs) + return model diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/Resnext_torch.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/Resnext_torch.py new file mode 100644 index 0000000000000000000000000000000000000000..e5ce4c50a4975acf02079488e42cfd9d686572d3 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/Resnext_torch.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python +# coding: utf-8 +import torch.nn as nn + +try: + from urllib import urlretrieve +except ImportError: + from urllib.request import urlretrieve + +__all__ = ['resnext101_32x8d'] + + +model_urls = { + 'resnext50_32x4d': 'https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth', + 'resnext101_32x8d': 'https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth', +} + + +def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1): + """3x3 convolution with padding""" + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, + padding=dilation, groups=groups, bias=False, dilation=dilation) + + +def conv1x1(in_planes, out_planes, stride=1): + """1x1 convolution""" + return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False) + + +class BasicBlock(nn.Module): + expansion = 1 + + def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1, + base_width=64, dilation=1, norm_layer=None): + super(BasicBlock, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2d + if groups != 1 or base_width != 64: + raise ValueError('BasicBlock only supports groups=1 and base_width=64') + if dilation > 1: + raise NotImplementedError("Dilation > 1 not supported in BasicBlock") + # Both self.conv1 and self.downsample layers downsample the input when stride != 1 + self.conv1 = conv3x3(inplanes, planes, stride) + self.bn1 = norm_layer(planes) + self.relu = nn.ReLU(inplace=True) + self.conv2 = conv3x3(planes, planes) + self.bn2 = norm_layer(planes) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + identity = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + out = self.relu(out) + + return out + + +class Bottleneck(nn.Module): + # Bottleneck in torchvision places the stride for downsampling at 3x3 convolution(self.conv2) + # while original implementation places the stride at the first 1x1 convolution(self.conv1) + # according to "Deep residual learning for image recognition"https://arxiv.org/abs/1512.03385. + # This variant is also known as ResNet V1.5 and improves accuracy according to + # https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch. + + expansion = 4 + + def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1, + base_width=64, dilation=1, norm_layer=None): + super(Bottleneck, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2d + width = int(planes * (base_width / 64.)) * groups + # Both self.conv2 and self.downsample layers downsample the input when stride != 1 + self.conv1 = conv1x1(inplanes, width) + self.bn1 = norm_layer(width) + self.conv2 = conv3x3(width, width, stride, groups, dilation) + self.bn2 = norm_layer(width) + self.conv3 = conv1x1(width, planes * self.expansion) + self.bn3 = norm_layer(planes * self.expansion) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + identity = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + out = self.relu(out) + + return out + + +class ResNet(nn.Module): + + def __init__(self, block, layers, num_classes=1000, zero_init_residual=False, + groups=1, width_per_group=64, replace_stride_with_dilation=None, + norm_layer=None): + super(ResNet, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2d + self._norm_layer = norm_layer + + self.inplanes = 64 + self.dilation = 1 + if replace_stride_with_dilation is None: + # each element in the tuple indicates if we should replace + # the 2x2 stride with a dilated convolution instead + replace_stride_with_dilation = [False, False, False] + if len(replace_stride_with_dilation) != 3: + raise ValueError("replace_stride_with_dilation should be None " + "or a 3-element tuple, got {}".format(replace_stride_with_dilation)) + self.groups = groups + self.base_width = width_per_group + self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, padding=3, + bias=False) + self.bn1 = norm_layer(self.inplanes) + self.relu = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + self.layer1 = self._make_layer(block, 64, layers[0]) + self.layer2 = self._make_layer(block, 128, layers[1], stride=2, + dilate=replace_stride_with_dilation[0]) + self.layer3 = self._make_layer(block, 256, layers[2], stride=2, + dilate=replace_stride_with_dilation[1]) + self.layer4 = self._make_layer(block, 512, layers[3], stride=2, + dilate=replace_stride_with_dilation[2]) + #self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) + #self.fc = nn.Linear(512 * block.expansion, num_classes) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') + elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + + # Zero-initialize the last BN in each residual branch, + # so that the residual branch starts with zeros, and each residual block behaves like an identity. + # This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677 + if zero_init_residual: + for m in self.modules(): + if isinstance(m, Bottleneck): + nn.init.constant_(m.bn3.weight, 0) + elif isinstance(m, BasicBlock): + nn.init.constant_(m.bn2.weight, 0) + + def _make_layer(self, block, planes, blocks, stride=1, dilate=False): + norm_layer = self._norm_layer + downsample = None + previous_dilation = self.dilation + if dilate: + self.dilation *= stride + stride = 1 + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + conv1x1(self.inplanes, planes * block.expansion, stride), + norm_layer(planes * block.expansion), + ) + + layers = [] + layers.append(block(self.inplanes, planes, stride, downsample, self.groups, + self.base_width, previous_dilation, norm_layer)) + self.inplanes = planes * block.expansion + for _ in range(1, blocks): + layers.append(block(self.inplanes, planes, groups=self.groups, + base_width=self.base_width, dilation=self.dilation, + norm_layer=norm_layer)) + + return nn.Sequential(*layers) + + def _forward_impl(self, x): + # See note [TorchScript super()] + features = [] + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + x = self.maxpool(x) + + x = self.layer1(x) + features.append(x) + + x = self.layer2(x) + features.append(x) + + x = self.layer3(x) + features.append(x) + + x = self.layer4(x) + features.append(x) + + #x = self.avgpool(x) + #x = torch.flatten(x, 1) + #x = self.fc(x) + + return features + + def forward(self, x): + return self._forward_impl(x) + + + +def resnext101_32x8d(pretrained=True, **kwargs): + """Constructs a ResNet-152 model. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + """ + kwargs['groups'] = 32 + kwargs['width_per_group'] = 8 + + model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) + return model + + + +if __name__ == '__main__': + import torch + model = resnext101_32x8d(True).cuda() + + rgb = torch.rand((2, 3, 256, 256)).cuda() + out = model(rgb) + print(len(out)) + diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/depthmap.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/depthmap.py new file mode 100644 index 0000000000000000000000000000000000000000..ebceecbe28ec248f6f96bb65b1c53bdbaf393ecc --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/depthmap.py @@ -0,0 +1,546 @@ +# Author: thygate +# https://github.com/thygate/stable-diffusion-webui-depthmap-script + +from modules import devices +from modules.shared import opts +from torchvision.transforms import transforms +from operator import getitem + +import torch, gc +import cv2 +import numpy as np +import skimage.measure + +whole_size_threshold = 1600 # R_max from the paper +pix2pixsize = 1024 + +def scale_torch(img): + """ + Scale the image and output it in torch.tensor. + :param img: input rgb is in shape [H, W, C], input depth/disp is in shape [H, W] + :param scale: the scale factor. float + :return: img. [C, H, W] + """ + if len(img.shape) == 2: + img = img[np.newaxis, :, :] + if img.shape[2] == 3: + transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.485, 0.456, 0.406) , (0.229, 0.224, 0.225) )]) + img = transform(img.astype(np.float32)) + else: + img = img.astype(np.float32) + img = torch.from_numpy(img) + return img + +def estimateleres(img, model, w, h): + # leres transform input + rgb_c = img[:, :, ::-1].copy() + A_resize = cv2.resize(rgb_c, (w, h)) + img_torch = scale_torch(A_resize)[None, :, :, :] + + # compute + with torch.no_grad(): + img_torch = img_torch.to(devices.get_device_for("controlnet")) + prediction = model.depth_model(img_torch) + + prediction = prediction.squeeze().cpu().numpy() + prediction = cv2.resize(prediction, (img.shape[1], img.shape[0]), interpolation=cv2.INTER_CUBIC) + + return prediction + +def generatemask(size): + # Generates a Guassian mask + mask = np.zeros(size, dtype=np.float32) + sigma = int(size[0]/16) + k_size = int(2 * np.ceil(2 * int(size[0]/16)) + 1) + mask[int(0.15*size[0]):size[0] - int(0.15*size[0]), int(0.15*size[1]): size[1] - int(0.15*size[1])] = 1 + mask = cv2.GaussianBlur(mask, (int(k_size), int(k_size)), sigma) + mask = (mask - mask.min()) / (mask.max() - mask.min()) + mask = mask.astype(np.float32) + return mask + +def resizewithpool(img, size): + i_size = img.shape[0] + n = int(np.floor(i_size/size)) + + out = skimage.measure.block_reduce(img, (n, n), np.max) + return out + +def rgb2gray(rgb): + # Converts rgb to gray + return np.dot(rgb[..., :3], [0.2989, 0.5870, 0.1140]) + +def calculateprocessingres(img, basesize, confidence=0.1, scale_threshold=3, whole_size_threshold=3000): + # Returns the R_x resolution described in section 5 of the main paper. + + # Parameters: + # img :input rgb image + # basesize : size the dilation kernel which is equal to receptive field of the network. + # confidence: value of x in R_x; allowed percentage of pixels that are not getting any contextual cue. + # scale_threshold: maximum allowed upscaling on the input image ; it has been set to 3. + # whole_size_threshold: maximum allowed resolution. (R_max from section 6 of the main paper) + + # Returns: + # outputsize_scale*speed_scale :The computed R_x resolution + # patch_scale: K parameter from section 6 of the paper + + # speed scale parameter is to process every image in a smaller size to accelerate the R_x resolution search + speed_scale = 32 + image_dim = int(min(img.shape[0:2])) + + gray = rgb2gray(img) + grad = np.abs(cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)) + np.abs(cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)) + grad = cv2.resize(grad, (image_dim, image_dim), cv2.INTER_AREA) + + # thresholding the gradient map to generate the edge-map as a proxy of the contextual cues + m = grad.min() + M = grad.max() + middle = m + (0.4 * (M - m)) + grad[grad < middle] = 0 + grad[grad >= middle] = 1 + + # dilation kernel with size of the receptive field + kernel = np.ones((int(basesize/speed_scale), int(basesize/speed_scale)), float) + # dilation kernel with size of the a quarter of receptive field used to compute k + # as described in section 6 of main paper + kernel2 = np.ones((int(basesize / (4*speed_scale)), int(basesize / (4*speed_scale))), float) + + # Output resolution limit set by the whole_size_threshold and scale_threshold. + threshold = min(whole_size_threshold, scale_threshold * max(img.shape[:2])) + + outputsize_scale = basesize / speed_scale + for p_size in range(int(basesize/speed_scale), int(threshold/speed_scale), int(basesize / (2*speed_scale))): + grad_resized = resizewithpool(grad, p_size) + grad_resized = cv2.resize(grad_resized, (p_size, p_size), cv2.INTER_NEAREST) + grad_resized[grad_resized >= 0.5] = 1 + grad_resized[grad_resized < 0.5] = 0 + + dilated = cv2.dilate(grad_resized, kernel, iterations=1) + meanvalue = (1-dilated).mean() + if meanvalue > confidence: + break + else: + outputsize_scale = p_size + + grad_region = cv2.dilate(grad_resized, kernel2, iterations=1) + patch_scale = grad_region.mean() + + return int(outputsize_scale*speed_scale), patch_scale + +# Generate a double-input depth estimation +def doubleestimate(img, size1, size2, pix2pixsize, model, net_type, pix2pixmodel): + # Generate the low resolution estimation + estimate1 = singleestimate(img, size1, model, net_type) + # Resize to the inference size of merge network. + estimate1 = cv2.resize(estimate1, (pix2pixsize, pix2pixsize), interpolation=cv2.INTER_CUBIC) + + # Generate the high resolution estimation + estimate2 = singleestimate(img, size2, model, net_type) + # Resize to the inference size of merge network. + estimate2 = cv2.resize(estimate2, (pix2pixsize, pix2pixsize), interpolation=cv2.INTER_CUBIC) + + # Inference on the merge model + pix2pixmodel.set_input(estimate1, estimate2) + pix2pixmodel.test() + visuals = pix2pixmodel.get_current_visuals() + prediction_mapped = visuals['fake_B'] + prediction_mapped = (prediction_mapped+1)/2 + prediction_mapped = (prediction_mapped - torch.min(prediction_mapped)) / ( + torch.max(prediction_mapped) - torch.min(prediction_mapped)) + prediction_mapped = prediction_mapped.squeeze().cpu().numpy() + + return prediction_mapped + +# Generate a single-input depth estimation +def singleestimate(img, msize, model, net_type): + # if net_type == 0: + return estimateleres(img, model, msize, msize) + # else: + # return estimatemidasBoost(img, model, msize, msize) + +def applyGridpatch(blsize, stride, img, box): + # Extract a simple grid patch. + counter1 = 0 + patch_bound_list = {} + for k in range(blsize, img.shape[1] - blsize, stride): + for j in range(blsize, img.shape[0] - blsize, stride): + patch_bound_list[str(counter1)] = {} + patchbounds = [j - blsize, k - blsize, j - blsize + 2 * blsize, k - blsize + 2 * blsize] + patch_bound = [box[0] + patchbounds[1], box[1] + patchbounds[0], patchbounds[3] - patchbounds[1], + patchbounds[2] - patchbounds[0]] + patch_bound_list[str(counter1)]['rect'] = patch_bound + patch_bound_list[str(counter1)]['size'] = patch_bound[2] + counter1 = counter1 + 1 + return patch_bound_list + +# Generating local patches to perform the local refinement described in section 6 of the main paper. +def generatepatchs(img, base_size): + + # Compute the gradients as a proxy of the contextual cues. + img_gray = rgb2gray(img) + whole_grad = np.abs(cv2.Sobel(img_gray, cv2.CV_64F, 0, 1, ksize=3)) +\ + np.abs(cv2.Sobel(img_gray, cv2.CV_64F, 1, 0, ksize=3)) + + threshold = whole_grad[whole_grad > 0].mean() + whole_grad[whole_grad < threshold] = 0 + + # We use the integral image to speed-up the evaluation of the amount of gradients for each patch. + gf = whole_grad.sum()/len(whole_grad.reshape(-1)) + grad_integral_image = cv2.integral(whole_grad) + + # Variables are selected such that the initial patch size would be the receptive field size + # and the stride is set to 1/3 of the receptive field size. + blsize = int(round(base_size/2)) + stride = int(round(blsize*0.75)) + + # Get initial Grid + patch_bound_list = applyGridpatch(blsize, stride, img, [0, 0, 0, 0]) + + # Refine initial Grid of patches by discarding the flat (in terms of gradients of the rgb image) ones. Refine + # each patch size to ensure that there will be enough depth cues for the network to generate a consistent depth map. + print("Selecting patches ...") + patch_bound_list = adaptiveselection(grad_integral_image, patch_bound_list, gf) + + # Sort the patch list to make sure the merging operation will be done with the correct order: starting from biggest + # patch + patchset = sorted(patch_bound_list.items(), key=lambda x: getitem(x[1], 'size'), reverse=True) + return patchset + +def getGF_fromintegral(integralimage, rect): + # Computes the gradient density of a given patch from the gradient integral image. + x1 = rect[1] + x2 = rect[1]+rect[3] + y1 = rect[0] + y2 = rect[0]+rect[2] + value = integralimage[x2, y2]-integralimage[x1, y2]-integralimage[x2, y1]+integralimage[x1, y1] + return value + +# Adaptively select patches +def adaptiveselection(integral_grad, patch_bound_list, gf): + patchlist = {} + count = 0 + height, width = integral_grad.shape + + search_step = int(32/factor) + + # Go through all patches + for c in range(len(patch_bound_list)): + # Get patch + bbox = patch_bound_list[str(c)]['rect'] + + # Compute the amount of gradients present in the patch from the integral image. + cgf = getGF_fromintegral(integral_grad, bbox)/(bbox[2]*bbox[3]) + + # Check if patching is beneficial by comparing the gradient density of the patch to + # the gradient density of the whole image + if cgf >= gf: + bbox_test = bbox.copy() + patchlist[str(count)] = {} + + # Enlarge each patch until the gradient density of the patch is equal + # to the whole image gradient density + while True: + + bbox_test[0] = bbox_test[0] - int(search_step/2) + bbox_test[1] = bbox_test[1] - int(search_step/2) + + bbox_test[2] = bbox_test[2] + search_step + bbox_test[3] = bbox_test[3] + search_step + + # Check if we are still within the image + if bbox_test[0] < 0 or bbox_test[1] < 0 or bbox_test[1] + bbox_test[3] >= height \ + or bbox_test[0] + bbox_test[2] >= width: + break + + # Compare gradient density + cgf = getGF_fromintegral(integral_grad, bbox_test)/(bbox_test[2]*bbox_test[3]) + if cgf < gf: + break + bbox = bbox_test.copy() + + # Add patch to selected patches + patchlist[str(count)]['rect'] = bbox + patchlist[str(count)]['size'] = bbox[2] + count = count + 1 + + # Return selected patches + return patchlist + +def impatch(image, rect): + # Extract the given patch pixels from a given image. + w1 = rect[0] + h1 = rect[1] + w2 = w1 + rect[2] + h2 = h1 + rect[3] + image_patch = image[h1:h2, w1:w2] + return image_patch + +class ImageandPatchs: + def __init__(self, root_dir, name, patchsinfo, rgb_image, scale=1): + self.root_dir = root_dir + self.patchsinfo = patchsinfo + self.name = name + self.patchs = patchsinfo + self.scale = scale + + self.rgb_image = cv2.resize(rgb_image, (round(rgb_image.shape[1]*scale), round(rgb_image.shape[0]*scale)), + interpolation=cv2.INTER_CUBIC) + + self.do_have_estimate = False + self.estimation_updated_image = None + self.estimation_base_image = None + + def __len__(self): + return len(self.patchs) + + def set_base_estimate(self, est): + self.estimation_base_image = est + if self.estimation_updated_image is not None: + self.do_have_estimate = True + + def set_updated_estimate(self, est): + self.estimation_updated_image = est + if self.estimation_base_image is not None: + self.do_have_estimate = True + + def __getitem__(self, index): + patch_id = int(self.patchs[index][0]) + rect = np.array(self.patchs[index][1]['rect']) + msize = self.patchs[index][1]['size'] + + ## applying scale to rect: + rect = np.round(rect * self.scale) + rect = rect.astype('int') + msize = round(msize * self.scale) + + patch_rgb = impatch(self.rgb_image, rect) + if self.do_have_estimate: + patch_whole_estimate_base = impatch(self.estimation_base_image, rect) + patch_whole_estimate_updated = impatch(self.estimation_updated_image, rect) + return {'patch_rgb': patch_rgb, 'patch_whole_estimate_base': patch_whole_estimate_base, + 'patch_whole_estimate_updated': patch_whole_estimate_updated, 'rect': rect, + 'size': msize, 'id': patch_id} + else: + return {'patch_rgb': patch_rgb, 'rect': rect, 'size': msize, 'id': patch_id} + + def print_options(self, opt): + """Print and save options + + It will print both current options and default values(if different). + It will save options into a text file / [checkpoints_dir] / opt.txt + """ + message = '' + message += '----------------- Options ---------------\n' + for k, v in sorted(vars(opt).items()): + comment = '' + default = self.parser.get_default(k) + if v != default: + comment = '\t[default: %s]' % str(default) + message += '{:>25}: {:<30}{}\n'.format(str(k), str(v), comment) + message += '----------------- End -------------------' + print(message) + + # save to the disk + """ + expr_dir = os.path.join(opt.checkpoints_dir, opt.name) + util.mkdirs(expr_dir) + file_name = os.path.join(expr_dir, '{}_opt.txt'.format(opt.phase)) + with open(file_name, 'wt') as opt_file: + opt_file.write(message) + opt_file.write('\n') + """ + + def parse(self): + """Parse our options, create checkpoints directory suffix, and set up gpu device.""" + opt = self.gather_options() + opt.isTrain = self.isTrain # train or test + + # process opt.suffix + if opt.suffix: + suffix = ('_' + opt.suffix.format(**vars(opt))) if opt.suffix != '' else '' + opt.name = opt.name + suffix + + #self.print_options(opt) + + # set gpu ids + str_ids = opt.gpu_ids.split(',') + opt.gpu_ids = [] + for str_id in str_ids: + id = int(str_id) + if id >= 0: + opt.gpu_ids.append(id) + #if len(opt.gpu_ids) > 0: + # torch.cuda.set_device(opt.gpu_ids[0]) + + self.opt = opt + return self.opt + + +def estimateboost(img, model, model_type, pix2pixmodel, max_res=512): + global whole_size_threshold + + # get settings + if hasattr(opts, 'depthmap_script_boost_rmax'): + whole_size_threshold = opts.depthmap_script_boost_rmax + + if model_type == 0: #leres + net_receptive_field_size = 448 + patch_netsize = 2 * net_receptive_field_size + elif model_type == 1: #dpt_beit_large_512 + net_receptive_field_size = 512 + patch_netsize = 2 * net_receptive_field_size + else: #other midas + net_receptive_field_size = 384 + patch_netsize = 2 * net_receptive_field_size + + gc.collect() + devices.torch_gc() + + # Generate mask used to smoothly blend the local pathc estimations to the base estimate. + # It is arbitrarily large to avoid artifacts during rescaling for each crop. + mask_org = generatemask((3000, 3000)) + mask = mask_org.copy() + + # Value x of R_x defined in the section 5 of the main paper. + r_threshold_value = 0.2 + #if R0: + # r_threshold_value = 0 + + input_resolution = img.shape + scale_threshold = 3 # Allows up-scaling with a scale up to 3 + + # Find the best input resolution R-x. The resolution search described in section 5-double estimation of the main paper and section B of the + # supplementary material. + whole_image_optimal_size, patch_scale = calculateprocessingres(img, net_receptive_field_size, r_threshold_value, scale_threshold, whole_size_threshold) + + # print('wholeImage being processed in :', whole_image_optimal_size) + + # Generate the base estimate using the double estimation. + whole_estimate = doubleestimate(img, net_receptive_field_size, whole_image_optimal_size, pix2pixsize, model, model_type, pix2pixmodel) + + # Compute the multiplier described in section 6 of the main paper to make sure our initial patch can select + # small high-density regions of the image. + global factor + factor = max(min(1, 4 * patch_scale * whole_image_optimal_size / whole_size_threshold), 0.2) + # print('Adjust factor is:', 1/factor) + + # Check if Local boosting is beneficial. + if max_res < whole_image_optimal_size: + # print("No Local boosting. Specified Max Res is smaller than R20, Returning doubleestimate result") + return cv2.resize(whole_estimate, (input_resolution[1], input_resolution[0]), interpolation=cv2.INTER_CUBIC) + + # Compute the default target resolution. + if img.shape[0] > img.shape[1]: + a = 2 * whole_image_optimal_size + b = round(2 * whole_image_optimal_size * img.shape[1] / img.shape[0]) + else: + a = round(2 * whole_image_optimal_size * img.shape[0] / img.shape[1]) + b = 2 * whole_image_optimal_size + b = int(round(b / factor)) + a = int(round(a / factor)) + + """ + # recompute a, b and saturate to max res. + if max(a,b) > max_res: + print('Default Res is higher than max-res: Reducing final resolution') + if img.shape[0] > img.shape[1]: + a = max_res + b = round(max_res * img.shape[1] / img.shape[0]) + else: + a = round(max_res * img.shape[0] / img.shape[1]) + b = max_res + b = int(b) + a = int(a) + """ + + img = cv2.resize(img, (b, a), interpolation=cv2.INTER_CUBIC) + + # Extract selected patches for local refinement + base_size = net_receptive_field_size * 2 + patchset = generatepatchs(img, base_size) + + # print('Target resolution: ', img.shape) + + # Computing a scale in case user prompted to generate the results as the same resolution of the input. + # Notice that our method output resolution is independent of the input resolution and this parameter will only + # enable a scaling operation during the local patch merge implementation to generate results with the same resolution + # as the input. + """ + if output_resolution == 1: + mergein_scale = input_resolution[0] / img.shape[0] + print('Dynamicly change merged-in resolution; scale:', mergein_scale) + else: + mergein_scale = 1 + """ + # always rescale to input res for now + mergein_scale = input_resolution[0] / img.shape[0] + + imageandpatchs = ImageandPatchs('', '', patchset, img, mergein_scale) + whole_estimate_resized = cv2.resize(whole_estimate, (round(img.shape[1]*mergein_scale), + round(img.shape[0]*mergein_scale)), interpolation=cv2.INTER_CUBIC) + imageandpatchs.set_base_estimate(whole_estimate_resized.copy()) + imageandpatchs.set_updated_estimate(whole_estimate_resized.copy()) + + print('Resulting depthmap resolution will be :', whole_estimate_resized.shape[:2]) + print('Patches to process: '+str(len(imageandpatchs))) + + # Enumerate through all patches, generate their estimations and refining the base estimate. + for patch_ind in range(len(imageandpatchs)): + + # Get patch information + patch = imageandpatchs[patch_ind] # patch object + patch_rgb = patch['patch_rgb'] # rgb patch + patch_whole_estimate_base = patch['patch_whole_estimate_base'] # corresponding patch from base + rect = patch['rect'] # patch size and location + patch_id = patch['id'] # patch ID + org_size = patch_whole_estimate_base.shape # the original size from the unscaled input + print('\t Processing patch', patch_ind, '/', len(imageandpatchs)-1, '|', rect) + + # We apply double estimation for patches. The high resolution value is fixed to twice the receptive + # field size of the network for patches to accelerate the process. + patch_estimation = doubleestimate(patch_rgb, net_receptive_field_size, patch_netsize, pix2pixsize, model, model_type, pix2pixmodel) + patch_estimation = cv2.resize(patch_estimation, (pix2pixsize, pix2pixsize), interpolation=cv2.INTER_CUBIC) + patch_whole_estimate_base = cv2.resize(patch_whole_estimate_base, (pix2pixsize, pix2pixsize), interpolation=cv2.INTER_CUBIC) + + # Merging the patch estimation into the base estimate using our merge network: + # We feed the patch estimation and the same region from the updated base estimate to the merge network + # to generate the target estimate for the corresponding region. + pix2pixmodel.set_input(patch_whole_estimate_base, patch_estimation) + + # Run merging network + pix2pixmodel.test() + visuals = pix2pixmodel.get_current_visuals() + + prediction_mapped = visuals['fake_B'] + prediction_mapped = (prediction_mapped+1)/2 + prediction_mapped = prediction_mapped.squeeze().cpu().numpy() + + mapped = prediction_mapped + + # We use a simple linear polynomial to make sure the result of the merge network would match the values of + # base estimate + p_coef = np.polyfit(mapped.reshape(-1), patch_whole_estimate_base.reshape(-1), deg=1) + merged = np.polyval(p_coef, mapped.reshape(-1)).reshape(mapped.shape) + + merged = cv2.resize(merged, (org_size[1],org_size[0]), interpolation=cv2.INTER_CUBIC) + + # Get patch size and location + w1 = rect[0] + h1 = rect[1] + w2 = w1 + rect[2] + h2 = h1 + rect[3] + + # To speed up the implementation, we only generate the Gaussian mask once with a sufficiently large size + # and resize it to our needed size while merging the patches. + if mask.shape != org_size: + mask = cv2.resize(mask_org, (org_size[1],org_size[0]), interpolation=cv2.INTER_LINEAR) + + tobemergedto = imageandpatchs.estimation_updated_image + + # Update the whole estimation: + # We use a simple Gaussian mask to blend the merged patch region with the base estimate to ensure seamless + # blending at the boundaries of the patch region. + tobemergedto[h1:h2, w1:w2] = np.multiply(tobemergedto[h1:h2, w1:w2], 1 - mask) + np.multiply(merged, mask) + imageandpatchs.set_updated_estimate(tobemergedto) + + # output + return cv2.resize(imageandpatchs.estimation_updated_image, (input_resolution[1], input_resolution[0]), interpolation=cv2.INTER_CUBIC) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/multi_depth_model_woauxi.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/multi_depth_model_woauxi.py new file mode 100644 index 0000000000000000000000000000000000000000..822ab0893267042446c2a24ed35b4ea053c9914a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/multi_depth_model_woauxi.py @@ -0,0 +1,34 @@ +from . import network_auxi as network +from .net_tools import get_func +import torch +import torch.nn as nn +from modules import devices + +class RelDepthModel(nn.Module): + def __init__(self, backbone='resnet50'): + super(RelDepthModel, self).__init__() + if backbone == 'resnet50': + encoder = 'resnet50_stride32' + elif backbone == 'resnext101': + encoder = 'resnext101_stride32x8d' + self.depth_model = DepthModel(encoder) + + def inference(self, rgb): + with torch.no_grad(): + input = rgb.to(self.depth_model.device) + depth = self.depth_model(input) + #pred_depth_out = depth - depth.min() + 0.01 + return depth #pred_depth_out + + +class DepthModel(nn.Module): + def __init__(self, encoder): + super(DepthModel, self).__init__() + backbone = network.__name__.split('.')[-1] + '.' + encoder + self.encoder_modules = get_func(backbone)() + self.decoder_modules = network.Decoder() + + def forward(self, x): + lateral_out = self.encoder_modules(x) + out_logit = self.decoder_modules(lateral_out) + return out_logit \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/net_tools.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/net_tools.py new file mode 100644 index 0000000000000000000000000000000000000000..31bb8332b0a1dc16057367f2bed7b145006d5cd8 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/net_tools.py @@ -0,0 +1,53 @@ +import importlib +import torch +import os +from collections import OrderedDict + + +def get_func(func_name): + """Helper to return a function object by name. func_name must identify a + function in this module or the path to a function relative to the base + 'modeling' module. + """ + if func_name == '': + return None + try: + parts = func_name.split('.') + # Refers to a function in this module + if len(parts) == 1: + return globals()[parts[0]] + # Otherwise, assume we're referencing a module under modeling + module_name = 'annotator.leres.leres.' + '.'.join(parts[:-1]) + module = importlib.import_module(module_name) + return getattr(module, parts[-1]) + except Exception: + print('Failed to f1ind function: %s', func_name) + raise + +def load_ckpt(args, depth_model, shift_model, focal_model): + """ + Load checkpoint. + """ + if os.path.isfile(args.load_ckpt): + print("loading checkpoint %s" % args.load_ckpt) + checkpoint = torch.load(args.load_ckpt) + if shift_model is not None: + shift_model.load_state_dict(strip_prefix_if_present(checkpoint['shift_model'], 'module.'), + strict=True) + if focal_model is not None: + focal_model.load_state_dict(strip_prefix_if_present(checkpoint['focal_model'], 'module.'), + strict=True) + depth_model.load_state_dict(strip_prefix_if_present(checkpoint['depth_model'], "module."), + strict=True) + del checkpoint + torch.cuda.empty_cache() + + +def strip_prefix_if_present(state_dict, prefix): + keys = sorted(state_dict.keys()) + if not all(key.startswith(prefix) for key in keys): + return state_dict + stripped_state_dict = OrderedDict() + for key, value in state_dict.items(): + stripped_state_dict[key.replace(prefix, "")] = value + return stripped_state_dict \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/network_auxi.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/network_auxi.py new file mode 100644 index 0000000000000000000000000000000000000000..1bd87011a5339aca632d1a10b217c8737bdc794f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/leres/network_auxi.py @@ -0,0 +1,417 @@ +import torch +import torch.nn as nn +import torch.nn.init as init + +from . import Resnet, Resnext_torch + + +def resnet50_stride32(): + return DepthNet(backbone='resnet', depth=50, upfactors=[2, 2, 2, 2]) + +def resnext101_stride32x8d(): + return DepthNet(backbone='resnext101_32x8d', depth=101, upfactors=[2, 2, 2, 2]) + + +class Decoder(nn.Module): + def __init__(self): + super(Decoder, self).__init__() + self.inchannels = [256, 512, 1024, 2048] + self.midchannels = [256, 256, 256, 512] + self.upfactors = [2,2,2,2] + self.outchannels = 1 + + self.conv = FTB(inchannels=self.inchannels[3], midchannels=self.midchannels[3]) + self.conv1 = nn.Conv2d(in_channels=self.midchannels[3], out_channels=self.midchannels[2], kernel_size=3, padding=1, stride=1, bias=True) + self.upsample = nn.Upsample(scale_factor=self.upfactors[3], mode='bilinear', align_corners=True) + + self.ffm2 = FFM(inchannels=self.inchannels[2], midchannels=self.midchannels[2], outchannels = self.midchannels[2], upfactor=self.upfactors[2]) + self.ffm1 = FFM(inchannels=self.inchannels[1], midchannels=self.midchannels[1], outchannels = self.midchannels[1], upfactor=self.upfactors[1]) + self.ffm0 = FFM(inchannels=self.inchannels[0], midchannels=self.midchannels[0], outchannels = self.midchannels[0], upfactor=self.upfactors[0]) + + self.outconv = AO(inchannels=self.midchannels[0], outchannels=self.outchannels, upfactor=2) + self._init_params() + + def _init_params(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + init.normal_(m.weight, std=0.01) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.ConvTranspose2d): + init.normal_(m.weight, std=0.01) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2d): #NN.BatchNorm2d + init.constant_(m.weight, 1) + init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + init.normal_(m.weight, std=0.01) + if m.bias is not None: + init.constant_(m.bias, 0) + + def forward(self, features): + x_32x = self.conv(features[3]) # 1/32 + x_32 = self.conv1(x_32x) + x_16 = self.upsample(x_32) # 1/16 + + x_8 = self.ffm2(features[2], x_16) # 1/8 + x_4 = self.ffm1(features[1], x_8) # 1/4 + x_2 = self.ffm0(features[0], x_4) # 1/2 + #----------------------------------------- + x = self.outconv(x_2) # original size + return x + +class DepthNet(nn.Module): + __factory = { + 18: Resnet.resnet18, + 34: Resnet.resnet34, + 50: Resnet.resnet50, + 101: Resnet.resnet101, + 152: Resnet.resnet152 + } + def __init__(self, + backbone='resnet', + depth=50, + upfactors=[2, 2, 2, 2]): + super(DepthNet, self).__init__() + self.backbone = backbone + self.depth = depth + self.pretrained = False + self.inchannels = [256, 512, 1024, 2048] + self.midchannels = [256, 256, 256, 512] + self.upfactors = upfactors + self.outchannels = 1 + + # Build model + if self.backbone == 'resnet': + if self.depth not in DepthNet.__factory: + raise KeyError("Unsupported depth:", self.depth) + self.encoder = DepthNet.__factory[depth](pretrained=self.pretrained) + elif self.backbone == 'resnext101_32x8d': + self.encoder = Resnext_torch.resnext101_32x8d(pretrained=self.pretrained) + else: + self.encoder = Resnext_torch.resnext101(pretrained=self.pretrained) + + def forward(self, x): + x = self.encoder(x) # 1/32, 1/16, 1/8, 1/4 + return x + + +class FTB(nn.Module): + def __init__(self, inchannels, midchannels=512): + super(FTB, self).__init__() + self.in1 = inchannels + self.mid = midchannels + self.conv1 = nn.Conv2d(in_channels=self.in1, out_channels=self.mid, kernel_size=3, padding=1, stride=1, + bias=True) + # NN.BatchNorm2d + self.conv_branch = nn.Sequential(nn.ReLU(inplace=True), \ + nn.Conv2d(in_channels=self.mid, out_channels=self.mid, kernel_size=3, + padding=1, stride=1, bias=True), \ + nn.BatchNorm2d(num_features=self.mid), \ + nn.ReLU(inplace=True), \ + nn.Conv2d(in_channels=self.mid, out_channels=self.mid, kernel_size=3, + padding=1, stride=1, bias=True)) + self.relu = nn.ReLU(inplace=True) + + self.init_params() + + def forward(self, x): + x = self.conv1(x) + x = x + self.conv_branch(x) + x = self.relu(x) + + return x + + def init_params(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + init.normal_(m.weight, std=0.01) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.ConvTranspose2d): + # init.kaiming_normal_(m.weight, mode='fan_out') + init.normal_(m.weight, std=0.01) + # init.xavier_normal_(m.weight) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2d): # NN.BatchNorm2d + init.constant_(m.weight, 1) + init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + init.normal_(m.weight, std=0.01) + if m.bias is not None: + init.constant_(m.bias, 0) + + +class ATA(nn.Module): + def __init__(self, inchannels, reduction=8): + super(ATA, self).__init__() + self.inchannels = inchannels + self.avg_pool = nn.AdaptiveAvgPool2d(1) + self.fc = nn.Sequential(nn.Linear(self.inchannels * 2, self.inchannels // reduction), + nn.ReLU(inplace=True), + nn.Linear(self.inchannels // reduction, self.inchannels), + nn.Sigmoid()) + self.init_params() + + def forward(self, low_x, high_x): + n, c, _, _ = low_x.size() + x = torch.cat([low_x, high_x], 1) + x = self.avg_pool(x) + x = x.view(n, -1) + x = self.fc(x).view(n, c, 1, 1) + x = low_x * x + high_x + + return x + + def init_params(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + # init.kaiming_normal_(m.weight, mode='fan_out') + # init.normal(m.weight, std=0.01) + init.xavier_normal_(m.weight) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.ConvTranspose2d): + # init.kaiming_normal_(m.weight, mode='fan_out') + # init.normal_(m.weight, std=0.01) + init.xavier_normal_(m.weight) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2d): # NN.BatchNorm2d + init.constant_(m.weight, 1) + init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + init.normal_(m.weight, std=0.01) + if m.bias is not None: + init.constant_(m.bias, 0) + + +class FFM(nn.Module): + def __init__(self, inchannels, midchannels, outchannels, upfactor=2): + super(FFM, self).__init__() + self.inchannels = inchannels + self.midchannels = midchannels + self.outchannels = outchannels + self.upfactor = upfactor + + self.ftb1 = FTB(inchannels=self.inchannels, midchannels=self.midchannels) + # self.ata = ATA(inchannels = self.midchannels) + self.ftb2 = FTB(inchannels=self.midchannels, midchannels=self.outchannels) + + self.upsample = nn.Upsample(scale_factor=self.upfactor, mode='bilinear', align_corners=True) + + self.init_params() + + def forward(self, low_x, high_x): + x = self.ftb1(low_x) + x = x + high_x + x = self.ftb2(x) + x = self.upsample(x) + + return x + + def init_params(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + # init.kaiming_normal_(m.weight, mode='fan_out') + init.normal_(m.weight, std=0.01) + # init.xavier_normal_(m.weight) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.ConvTranspose2d): + # init.kaiming_normal_(m.weight, mode='fan_out') + init.normal_(m.weight, std=0.01) + # init.xavier_normal_(m.weight) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2d): # NN.Batchnorm2d + init.constant_(m.weight, 1) + init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + init.normal_(m.weight, std=0.01) + if m.bias is not None: + init.constant_(m.bias, 0) + + +class AO(nn.Module): + # Adaptive output module + def __init__(self, inchannels, outchannels, upfactor=2): + super(AO, self).__init__() + self.inchannels = inchannels + self.outchannels = outchannels + self.upfactor = upfactor + + self.adapt_conv = nn.Sequential( + nn.Conv2d(in_channels=self.inchannels, out_channels=self.inchannels // 2, kernel_size=3, padding=1, + stride=1, bias=True), \ + nn.BatchNorm2d(num_features=self.inchannels // 2), \ + nn.ReLU(inplace=True), \ + nn.Conv2d(in_channels=self.inchannels // 2, out_channels=self.outchannels, kernel_size=3, padding=1, + stride=1, bias=True), \ + nn.Upsample(scale_factor=self.upfactor, mode='bilinear', align_corners=True)) + + self.init_params() + + def forward(self, x): + x = self.adapt_conv(x) + return x + + def init_params(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + # init.kaiming_normal_(m.weight, mode='fan_out') + init.normal_(m.weight, std=0.01) + # init.xavier_normal_(m.weight) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.ConvTranspose2d): + # init.kaiming_normal_(m.weight, mode='fan_out') + init.normal_(m.weight, std=0.01) + # init.xavier_normal_(m.weight) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2d): # NN.Batchnorm2d + init.constant_(m.weight, 1) + init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + init.normal_(m.weight, std=0.01) + if m.bias is not None: + init.constant_(m.bias, 0) + + + +# ============================================================================================================== + + +class ResidualConv(nn.Module): + def __init__(self, inchannels): + super(ResidualConv, self).__init__() + # NN.BatchNorm2d + self.conv = nn.Sequential( + # nn.BatchNorm2d(num_features=inchannels), + nn.ReLU(inplace=False), + # nn.Conv2d(in_channels=inchannels, out_channels=inchannels, kernel_size=3, padding=1, stride=1, groups=inchannels,bias=True), + # nn.Conv2d(in_channels=inchannels, out_channels=inchannels, kernel_size=1, padding=0, stride=1, groups=1,bias=True) + nn.Conv2d(in_channels=inchannels, out_channels=inchannels / 2, kernel_size=3, padding=1, stride=1, + bias=False), + nn.BatchNorm2d(num_features=inchannels / 2), + nn.ReLU(inplace=False), + nn.Conv2d(in_channels=inchannels / 2, out_channels=inchannels, kernel_size=3, padding=1, stride=1, + bias=False) + ) + self.init_params() + + def forward(self, x): + x = self.conv(x) + x + return x + + def init_params(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + # init.kaiming_normal_(m.weight, mode='fan_out') + init.normal_(m.weight, std=0.01) + # init.xavier_normal_(m.weight) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.ConvTranspose2d): + # init.kaiming_normal_(m.weight, mode='fan_out') + init.normal_(m.weight, std=0.01) + # init.xavier_normal_(m.weight) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2d): # NN.BatchNorm2d + init.constant_(m.weight, 1) + init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + init.normal_(m.weight, std=0.01) + if m.bias is not None: + init.constant_(m.bias, 0) + + +class FeatureFusion(nn.Module): + def __init__(self, inchannels, outchannels): + super(FeatureFusion, self).__init__() + self.conv = ResidualConv(inchannels=inchannels) + # NN.BatchNorm2d + self.up = nn.Sequential(ResidualConv(inchannels=inchannels), + nn.ConvTranspose2d(in_channels=inchannels, out_channels=outchannels, kernel_size=3, + stride=2, padding=1, output_padding=1), + nn.BatchNorm2d(num_features=outchannels), + nn.ReLU(inplace=True)) + + def forward(self, lowfeat, highfeat): + return self.up(highfeat + self.conv(lowfeat)) + + def init_params(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + # init.kaiming_normal_(m.weight, mode='fan_out') + init.normal_(m.weight, std=0.01) + # init.xavier_normal_(m.weight) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.ConvTranspose2d): + # init.kaiming_normal_(m.weight, mode='fan_out') + init.normal_(m.weight, std=0.01) + # init.xavier_normal_(m.weight) + if m.bias is not None: + init.constant_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2d): # NN.BatchNorm2d + init.constant_(m.weight, 1) + init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + init.normal_(m.weight, std=0.01) + if m.bias is not None: + init.constant_(m.bias, 0) + + +class SenceUnderstand(nn.Module): + def __init__(self, channels): + super(SenceUnderstand, self).__init__() + self.channels = channels + self.conv1 = nn.Sequential(nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=1), + nn.ReLU(inplace=True)) + self.pool = nn.AdaptiveAvgPool2d(8) + self.fc = nn.Sequential(nn.Linear(512 * 8 * 8, self.channels), + nn.ReLU(inplace=True)) + self.conv2 = nn.Sequential( + nn.Conv2d(in_channels=self.channels, out_channels=self.channels, kernel_size=1, padding=0), + nn.ReLU(inplace=True)) + self.initial_params() + + def forward(self, x): + n, c, h, w = x.size() + x = self.conv1(x) + x = self.pool(x) + x = x.view(n, -1) + x = self.fc(x) + x = x.view(n, self.channels, 1, 1) + x = self.conv2(x) + x = x.repeat(1, 1, h, w) + return x + + def initial_params(self, dev=0.01): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + # print torch.sum(m.weight) + m.weight.data.normal_(0, dev) + if m.bias is not None: + m.bias.data.fill_(0) + elif isinstance(m, nn.ConvTranspose2d): + # print torch.sum(m.weight) + m.weight.data.normal_(0, dev) + if m.bias is not None: + m.bias.data.fill_(0) + elif isinstance(m, nn.Linear): + m.weight.data.normal_(0, dev) + + +if __name__ == '__main__': + net = DepthNet(depth=50, pretrained=True) + print(net) + inputs = torch.ones(4,3,128,128) + out = net(inputs) + print(out.size()) + diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/LICENSE b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..38b1a24fd389a138b930dcf1ee606ef97a0186c8 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/LICENSE @@ -0,0 +1,19 @@ +https://github.com/compphoto/BoostingMonocularDepth + +Copyright 2021, Seyed Mahdi Hosseini Miangoleh, Sebastian Dille, Computational Photography Laboratory. All rights reserved. + +This software is for academic use only. A redistribution of this +software, with or without modifications, has to be for academic +use only, while giving the appropriate credit to the original +authors of the software. The methods implemented as a part of +this software may be covered under patents or patent applications. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f96e5c7f032f2154c6bb433b68fc968d0a19b5a8 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/__init__.py @@ -0,0 +1,67 @@ +"""This package contains modules related to objective functions, optimizations, and network architectures. + +To add a custom model class called 'dummy', you need to add a file called 'dummy_model.py' and define a subclass DummyModel inherited from BaseModel. +You need to implement the following five functions: + -- <__init__>: initialize the class; first call BaseModel.__init__(self, opt). + -- : unpack data from dataset and apply preprocessing. + -- : produce intermediate results. + -- : calculate loss, gradients, and update network weights. + -- : (optionally) add model-specific options and set default options. + +In the function <__init__>, you need to define four lists: + -- self.loss_names (str list): specify the training losses that you want to plot and save. + -- self.model_names (str list): define networks used in our training. + -- self.visual_names (str list): specify the images that you want to display and save. + -- self.optimizers (optimizer list): define and initialize optimizers. You can define one optimizer for each network. If two networks are updated at the same time, you can use itertools.chain to group them. See cycle_gan_model.py for an usage. + +Now you can use the model class by specifying flag '--model dummy'. +See our template model class 'template_model.py' for more details. +""" + +import importlib +from .base_model import BaseModel + + +def find_model_using_name(model_name): + """Import the module "models/[model_name]_model.py". + + In the file, the class called DatasetNameModel() will + be instantiated. It has to be a subclass of BaseModel, + and it is case-insensitive. + """ + model_filename = "annotator.leres.pix2pix.models." + model_name + "_model" + modellib = importlib.import_module(model_filename) + model = None + target_model_name = model_name.replace('_', '') + 'model' + for name, cls in modellib.__dict__.items(): + if name.lower() == target_model_name.lower() \ + and issubclass(cls, BaseModel): + model = cls + + if model is None: + print("In %s.py, there should be a subclass of BaseModel with class name that matches %s in lowercase." % (model_filename, target_model_name)) + exit(0) + + return model + + +def get_option_setter(model_name): + """Return the static method of the model class.""" + model_class = find_model_using_name(model_name) + return model_class.modify_commandline_options + + +def create_model(opt): + """Create a model given the option. + + This function warps the class CustomDatasetDataLoader. + This is the main interface between this package and 'train.py'/'test.py' + + Example: + >>> from models import create_model + >>> model = create_model(opt) + """ + model = find_model_using_name(opt.model) + instance = model(opt) + print("model [%s] was created" % type(instance).__name__) + return instance diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/base_model.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/base_model.py new file mode 100644 index 0000000000000000000000000000000000000000..a90c5f832404bc44ef247b42a72988a37fc834cb --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/base_model.py @@ -0,0 +1,241 @@ +import os +import torch, gc +from modules import devices +from collections import OrderedDict +from abc import ABC, abstractmethod +from . import networks + + +class BaseModel(ABC): + """This class is an abstract base class (ABC) for models. + To create a subclass, you need to implement the following five functions: + -- <__init__>: initialize the class; first call BaseModel.__init__(self, opt). + -- : unpack data from dataset and apply preprocessing. + -- : produce intermediate results. + -- : calculate losses, gradients, and update network weights. + -- : (optionally) add model-specific options and set default options. + """ + + def __init__(self, opt): + """Initialize the BaseModel class. + + Parameters: + opt (Option class)-- stores all the experiment flags; needs to be a subclass of BaseOptions + + When creating your custom class, you need to implement your own initialization. + In this function, you should first call + Then, you need to define four lists: + -- self.loss_names (str list): specify the training losses that you want to plot and save. + -- self.model_names (str list): define networks used in our training. + -- self.visual_names (str list): specify the images that you want to display and save. + -- self.optimizers (optimizer list): define and initialize optimizers. You can define one optimizer for each network. If two networks are updated at the same time, you can use itertools.chain to group them. See cycle_gan_model.py for an example. + """ + self.opt = opt + self.gpu_ids = opt.gpu_ids + self.isTrain = opt.isTrain + self.device = torch.device('cuda:{}'.format(self.gpu_ids[0])) if self.gpu_ids else torch.device('cpu') # get device name: CPU or GPU + self.save_dir = os.path.join(opt.checkpoints_dir, opt.name) # save all the checkpoints to save_dir + if opt.preprocess != 'scale_width': # with [scale_width], input images might have different sizes, which hurts the performance of cudnn.benchmark. + torch.backends.cudnn.benchmark = True + self.loss_names = [] + self.model_names = [] + self.visual_names = [] + self.optimizers = [] + self.image_paths = [] + self.metric = 0 # used for learning rate policy 'plateau' + + @staticmethod + def modify_commandline_options(parser, is_train): + """Add new model-specific options, and rewrite default values for existing options. + + Parameters: + parser -- original option parser + is_train (bool) -- whether training phase or test phase. You can use this flag to add training-specific or test-specific options. + + Returns: + the modified parser. + """ + return parser + + @abstractmethod + def set_input(self, input): + """Unpack input data from the dataloader and perform necessary pre-processing steps. + + Parameters: + input (dict): includes the data itself and its metadata information. + """ + pass + + @abstractmethod + def forward(self): + """Run forward pass; called by both functions and .""" + pass + + @abstractmethod + def optimize_parameters(self): + """Calculate losses, gradients, and update network weights; called in every training iteration""" + pass + + def setup(self, opt): + """Load and print networks; create schedulers + + Parameters: + opt (Option class) -- stores all the experiment flags; needs to be a subclass of BaseOptions + """ + if self.isTrain: + self.schedulers = [networks.get_scheduler(optimizer, opt) for optimizer in self.optimizers] + if not self.isTrain or opt.continue_train: + load_suffix = 'iter_%d' % opt.load_iter if opt.load_iter > 0 else opt.epoch + self.load_networks(load_suffix) + self.print_networks(opt.verbose) + + def eval(self): + """Make models eval mode during test time""" + for name in self.model_names: + if isinstance(name, str): + net = getattr(self, 'net' + name) + net.eval() + + def test(self): + """Forward function used in test time. + + This function wraps function in no_grad() so we don't save intermediate steps for backprop + It also calls to produce additional visualization results + """ + with torch.no_grad(): + self.forward() + self.compute_visuals() + + def compute_visuals(self): + """Calculate additional output images for visdom and HTML visualization""" + pass + + def get_image_paths(self): + """ Return image paths that are used to load current data""" + return self.image_paths + + def update_learning_rate(self): + """Update learning rates for all the networks; called at the end of every epoch""" + old_lr = self.optimizers[0].param_groups[0]['lr'] + for scheduler in self.schedulers: + if self.opt.lr_policy == 'plateau': + scheduler.step(self.metric) + else: + scheduler.step() + + lr = self.optimizers[0].param_groups[0]['lr'] + print('learning rate %.7f -> %.7f' % (old_lr, lr)) + + def get_current_visuals(self): + """Return visualization images. train.py will display these images with visdom, and save the images to a HTML""" + visual_ret = OrderedDict() + for name in self.visual_names: + if isinstance(name, str): + visual_ret[name] = getattr(self, name) + return visual_ret + + def get_current_losses(self): + """Return traning losses / errors. train.py will print out these errors on console, and save them to a file""" + errors_ret = OrderedDict() + for name in self.loss_names: + if isinstance(name, str): + errors_ret[name] = float(getattr(self, 'loss_' + name)) # float(...) works for both scalar tensor and float number + return errors_ret + + def save_networks(self, epoch): + """Save all the networks to the disk. + + Parameters: + epoch (int) -- current epoch; used in the file name '%s_net_%s.pth' % (epoch, name) + """ + for name in self.model_names: + if isinstance(name, str): + save_filename = '%s_net_%s.pth' % (epoch, name) + save_path = os.path.join(self.save_dir, save_filename) + net = getattr(self, 'net' + name) + + if len(self.gpu_ids) > 0 and torch.cuda.is_available(): + torch.save(net.module.cpu().state_dict(), save_path) + net.cuda(self.gpu_ids[0]) + else: + torch.save(net.cpu().state_dict(), save_path) + + def unload_network(self, name): + """Unload network and gc. + """ + if isinstance(name, str): + net = getattr(self, 'net' + name) + del net + gc.collect() + devices.torch_gc() + return None + + def __patch_instance_norm_state_dict(self, state_dict, module, keys, i=0): + """Fix InstanceNorm checkpoints incompatibility (prior to 0.4)""" + key = keys[i] + if i + 1 == len(keys): # at the end, pointing to a parameter/buffer + if module.__class__.__name__.startswith('InstanceNorm') and \ + (key == 'running_mean' or key == 'running_var'): + if getattr(module, key) is None: + state_dict.pop('.'.join(keys)) + if module.__class__.__name__.startswith('InstanceNorm') and \ + (key == 'num_batches_tracked'): + state_dict.pop('.'.join(keys)) + else: + self.__patch_instance_norm_state_dict(state_dict, getattr(module, key), keys, i + 1) + + def load_networks(self, epoch): + """Load all the networks from the disk. + + Parameters: + epoch (int) -- current epoch; used in the file name '%s_net_%s.pth' % (epoch, name) + """ + for name in self.model_names: + if isinstance(name, str): + load_filename = '%s_net_%s.pth' % (epoch, name) + load_path = os.path.join(self.save_dir, load_filename) + net = getattr(self, 'net' + name) + if isinstance(net, torch.nn.DataParallel): + net = net.module + # print('Loading depth boost model from %s' % load_path) + # if you are using PyTorch newer than 0.4 (e.g., built from + # GitHub source), you can remove str() on self.device + state_dict = torch.load(load_path, map_location=str(self.device)) + if hasattr(state_dict, '_metadata'): + del state_dict._metadata + + # patch InstanceNorm checkpoints prior to 0.4 + for key in list(state_dict.keys()): # need to copy keys here because we mutate in loop + self.__patch_instance_norm_state_dict(state_dict, net, key.split('.')) + net.load_state_dict(state_dict) + + def print_networks(self, verbose): + """Print the total number of parameters in the network and (if verbose) network architecture + + Parameters: + verbose (bool) -- if verbose: print the network architecture + """ + print('---------- Networks initialized -------------') + for name in self.model_names: + if isinstance(name, str): + net = getattr(self, 'net' + name) + num_params = 0 + for param in net.parameters(): + num_params += param.numel() + if verbose: + print(net) + print('[Network %s] Total number of parameters : %.3f M' % (name, num_params / 1e6)) + print('-----------------------------------------------') + + def set_requires_grad(self, nets, requires_grad=False): + """Set requies_grad=Fasle for all the networks to avoid unnecessary computations + Parameters: + nets (network list) -- a list of networks + requires_grad (bool) -- whether the networks require gradients or not + """ + if not isinstance(nets, list): + nets = [nets] + for net in nets: + if net is not None: + for param in net.parameters(): + param.requires_grad = requires_grad diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/base_model_hg.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/base_model_hg.py new file mode 100644 index 0000000000000000000000000000000000000000..1709accdf0b048b3793dfd1f58d1b06c35f7b907 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/base_model_hg.py @@ -0,0 +1,58 @@ +import os +import torch + +class BaseModelHG(): + def name(self): + return 'BaseModel' + + def initialize(self, opt): + self.opt = opt + self.gpu_ids = opt.gpu_ids + self.isTrain = opt.isTrain + self.Tensor = torch.cuda.FloatTensor if self.gpu_ids else torch.Tensor + self.save_dir = os.path.join(opt.checkpoints_dir, opt.name) + + def set_input(self, input): + self.input = input + + def forward(self): + pass + + # used in test time, no backprop + def test(self): + pass + + def get_image_paths(self): + pass + + def optimize_parameters(self): + pass + + def get_current_visuals(self): + return self.input + + def get_current_errors(self): + return {} + + def save(self, label): + pass + + # helper saving function that can be used by subclasses + def save_network(self, network, network_label, epoch_label, gpu_ids): + save_filename = '_%s_net_%s.pth' % (epoch_label, network_label) + save_path = os.path.join(self.save_dir, save_filename) + torch.save(network.cpu().state_dict(), save_path) + if len(gpu_ids) and torch.cuda.is_available(): + network.cuda(device_id=gpu_ids[0]) + + # helper loading function that can be used by subclasses + def load_network(self, network, network_label, epoch_label): + save_filename = '%s_net_%s.pth' % (epoch_label, network_label) + save_path = os.path.join(self.save_dir, save_filename) + print(save_path) + model = torch.load(save_path) + return model + # network.load_state_dict(torch.load(save_path)) + + def update_learning_rate(): + pass diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/networks.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/networks.py new file mode 100644 index 0000000000000000000000000000000000000000..0cf912b2973721a02deefd042af621e732bad59f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/networks.py @@ -0,0 +1,623 @@ +import torch +import torch.nn as nn +from torch.nn import init +import functools +from torch.optim import lr_scheduler + + +############################################################################### +# Helper Functions +############################################################################### + + +class Identity(nn.Module): + def forward(self, x): + return x + + +def get_norm_layer(norm_type='instance'): + """Return a normalization layer + + Parameters: + norm_type (str) -- the name of the normalization layer: batch | instance | none + + For BatchNorm, we use learnable affine parameters and track running statistics (mean/stddev). + For InstanceNorm, we do not use learnable affine parameters. We do not track running statistics. + """ + if norm_type == 'batch': + norm_layer = functools.partial(nn.BatchNorm2d, affine=True, track_running_stats=True) + elif norm_type == 'instance': + norm_layer = functools.partial(nn.InstanceNorm2d, affine=False, track_running_stats=False) + elif norm_type == 'none': + def norm_layer(x): return Identity() + else: + raise NotImplementedError('normalization layer [%s] is not found' % norm_type) + return norm_layer + + +def get_scheduler(optimizer, opt): + """Return a learning rate scheduler + + Parameters: + optimizer -- the optimizer of the network + opt (option class) -- stores all the experiment flags; needs to be a subclass of BaseOptions.  + opt.lr_policy is the name of learning rate policy: linear | step | plateau | cosine + + For 'linear', we keep the same learning rate for the first epochs + and linearly decay the rate to zero over the next epochs. + For other schedulers (step, plateau, and cosine), we use the default PyTorch schedulers. + See https://pytorch.org/docs/stable/optim.html for more details. + """ + if opt.lr_policy == 'linear': + def lambda_rule(epoch): + lr_l = 1.0 - max(0, epoch + opt.epoch_count - opt.n_epochs) / float(opt.n_epochs_decay + 1) + return lr_l + scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda_rule) + elif opt.lr_policy == 'step': + scheduler = lr_scheduler.StepLR(optimizer, step_size=opt.lr_decay_iters, gamma=0.1) + elif opt.lr_policy == 'plateau': + scheduler = lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.2, threshold=0.01, patience=5) + elif opt.lr_policy == 'cosine': + scheduler = lr_scheduler.CosineAnnealingLR(optimizer, T_max=opt.n_epochs, eta_min=0) + else: + return NotImplementedError('learning rate policy [%s] is not implemented', opt.lr_policy) + return scheduler + + +def init_weights(net, init_type='normal', init_gain=0.02): + """Initialize network weights. + + Parameters: + net (network) -- network to be initialized + init_type (str) -- the name of an initialization method: normal | xavier | kaiming | orthogonal + init_gain (float) -- scaling factor for normal, xavier and orthogonal. + + We use 'normal' in the original pix2pix and CycleGAN paper. But xavier and kaiming might + work better for some applications. Feel free to try yourself. + """ + def init_func(m): # define the initialization function + classname = m.__class__.__name__ + if hasattr(m, 'weight') and (classname.find('Conv') != -1 or classname.find('Linear') != -1): + if init_type == 'normal': + init.normal_(m.weight.data, 0.0, init_gain) + elif init_type == 'xavier': + init.xavier_normal_(m.weight.data, gain=init_gain) + elif init_type == 'kaiming': + init.kaiming_normal_(m.weight.data, a=0, mode='fan_in') + elif init_type == 'orthogonal': + init.orthogonal_(m.weight.data, gain=init_gain) + else: + raise NotImplementedError('initialization method [%s] is not implemented' % init_type) + if hasattr(m, 'bias') and m.bias is not None: + init.constant_(m.bias.data, 0.0) + elif classname.find('BatchNorm2d') != -1: # BatchNorm Layer's weight is not a matrix; only normal distribution applies. + init.normal_(m.weight.data, 1.0, init_gain) + init.constant_(m.bias.data, 0.0) + + # print('initialize network with %s' % init_type) + net.apply(init_func) # apply the initialization function + + +def init_net(net, init_type='normal', init_gain=0.02, gpu_ids=[]): + """Initialize a network: 1. register CPU/GPU device (with multi-GPU support); 2. initialize the network weights + Parameters: + net (network) -- the network to be initialized + init_type (str) -- the name of an initialization method: normal | xavier | kaiming | orthogonal + gain (float) -- scaling factor for normal, xavier and orthogonal. + gpu_ids (int list) -- which GPUs the network runs on: e.g., 0,1,2 + + Return an initialized network. + """ + if len(gpu_ids) > 0: + assert(torch.cuda.is_available()) + net.to(gpu_ids[0]) + net = torch.nn.DataParallel(net, gpu_ids) # multi-GPUs + init_weights(net, init_type, init_gain=init_gain) + return net + + +def define_G(input_nc, output_nc, ngf, netG, norm='batch', use_dropout=False, init_type='normal', init_gain=0.02, gpu_ids=[]): + """Create a generator + + Parameters: + input_nc (int) -- the number of channels in input images + output_nc (int) -- the number of channels in output images + ngf (int) -- the number of filters in the last conv layer + netG (str) -- the architecture's name: resnet_9blocks | resnet_6blocks | unet_256 | unet_128 + norm (str) -- the name of normalization layers used in the network: batch | instance | none + use_dropout (bool) -- if use dropout layers. + init_type (str) -- the name of our initialization method. + init_gain (float) -- scaling factor for normal, xavier and orthogonal. + gpu_ids (int list) -- which GPUs the network runs on: e.g., 0,1,2 + + Returns a generator + + Our current implementation provides two types of generators: + U-Net: [unet_128] (for 128x128 input images) and [unet_256] (for 256x256 input images) + The original U-Net paper: https://arxiv.org/abs/1505.04597 + + Resnet-based generator: [resnet_6blocks] (with 6 Resnet blocks) and [resnet_9blocks] (with 9 Resnet blocks) + Resnet-based generator consists of several Resnet blocks between a few downsampling/upsampling operations. + We adapt Torch code from Justin Johnson's neural style transfer project (https://github.com/jcjohnson/fast-neural-style). + + + The generator has been initialized by . It uses RELU for non-linearity. + """ + net = None + norm_layer = get_norm_layer(norm_type=norm) + + if netG == 'resnet_9blocks': + net = ResnetGenerator(input_nc, output_nc, ngf, norm_layer=norm_layer, use_dropout=use_dropout, n_blocks=9) + elif netG == 'resnet_6blocks': + net = ResnetGenerator(input_nc, output_nc, ngf, norm_layer=norm_layer, use_dropout=use_dropout, n_blocks=6) + elif netG == 'resnet_12blocks': + net = ResnetGenerator(input_nc, output_nc, ngf, norm_layer=norm_layer, use_dropout=use_dropout, n_blocks=12) + elif netG == 'unet_128': + net = UnetGenerator(input_nc, output_nc, 7, ngf, norm_layer=norm_layer, use_dropout=use_dropout) + elif netG == 'unet_256': + net = UnetGenerator(input_nc, output_nc, 8, ngf, norm_layer=norm_layer, use_dropout=use_dropout) + elif netG == 'unet_672': + net = UnetGenerator(input_nc, output_nc, 5, ngf, norm_layer=norm_layer, use_dropout=use_dropout) + elif netG == 'unet_960': + net = UnetGenerator(input_nc, output_nc, 6, ngf, norm_layer=norm_layer, use_dropout=use_dropout) + elif netG == 'unet_1024': + net = UnetGenerator(input_nc, output_nc, 10, ngf, norm_layer=norm_layer, use_dropout=use_dropout) + else: + raise NotImplementedError('Generator model name [%s] is not recognized' % netG) + return init_net(net, init_type, init_gain, gpu_ids) + + +def define_D(input_nc, ndf, netD, n_layers_D=3, norm='batch', init_type='normal', init_gain=0.02, gpu_ids=[]): + """Create a discriminator + + Parameters: + input_nc (int) -- the number of channels in input images + ndf (int) -- the number of filters in the first conv layer + netD (str) -- the architecture's name: basic | n_layers | pixel + n_layers_D (int) -- the number of conv layers in the discriminator; effective when netD=='n_layers' + norm (str) -- the type of normalization layers used in the network. + init_type (str) -- the name of the initialization method. + init_gain (float) -- scaling factor for normal, xavier and orthogonal. + gpu_ids (int list) -- which GPUs the network runs on: e.g., 0,1,2 + + Returns a discriminator + + Our current implementation provides three types of discriminators: + [basic]: 'PatchGAN' classifier described in the original pix2pix paper. + It can classify whether 70×70 overlapping patches are real or fake. + Such a patch-level discriminator architecture has fewer parameters + than a full-image discriminator and can work on arbitrarily-sized images + in a fully convolutional fashion. + + [n_layers]: With this mode, you can specify the number of conv layers in the discriminator + with the parameter (default=3 as used in [basic] (PatchGAN).) + + [pixel]: 1x1 PixelGAN discriminator can classify whether a pixel is real or not. + It encourages greater color diversity but has no effect on spatial statistics. + + The discriminator has been initialized by . It uses Leakly RELU for non-linearity. + """ + net = None + norm_layer = get_norm_layer(norm_type=norm) + + if netD == 'basic': # default PatchGAN classifier + net = NLayerDiscriminator(input_nc, ndf, n_layers=3, norm_layer=norm_layer) + elif netD == 'n_layers': # more options + net = NLayerDiscriminator(input_nc, ndf, n_layers_D, norm_layer=norm_layer) + elif netD == 'pixel': # classify if each pixel is real or fake + net = PixelDiscriminator(input_nc, ndf, norm_layer=norm_layer) + else: + raise NotImplementedError('Discriminator model name [%s] is not recognized' % netD) + return init_net(net, init_type, init_gain, gpu_ids) + + +############################################################################## +# Classes +############################################################################## +class GANLoss(nn.Module): + """Define different GAN objectives. + + The GANLoss class abstracts away the need to create the target label tensor + that has the same size as the input. + """ + + def __init__(self, gan_mode, target_real_label=1.0, target_fake_label=0.0): + """ Initialize the GANLoss class. + + Parameters: + gan_mode (str) - - the type of GAN objective. It currently supports vanilla, lsgan, and wgangp. + target_real_label (bool) - - label for a real image + target_fake_label (bool) - - label of a fake image + + Note: Do not use sigmoid as the last layer of Discriminator. + LSGAN needs no sigmoid. vanilla GANs will handle it with BCEWithLogitsLoss. + """ + super(GANLoss, self).__init__() + self.register_buffer('real_label', torch.tensor(target_real_label)) + self.register_buffer('fake_label', torch.tensor(target_fake_label)) + self.gan_mode = gan_mode + if gan_mode == 'lsgan': + self.loss = nn.MSELoss() + elif gan_mode == 'vanilla': + self.loss = nn.BCEWithLogitsLoss() + elif gan_mode in ['wgangp']: + self.loss = None + else: + raise NotImplementedError('gan mode %s not implemented' % gan_mode) + + def get_target_tensor(self, prediction, target_is_real): + """Create label tensors with the same size as the input. + + Parameters: + prediction (tensor) - - tpyically the prediction from a discriminator + target_is_real (bool) - - if the ground truth label is for real images or fake images + + Returns: + A label tensor filled with ground truth label, and with the size of the input + """ + + if target_is_real: + target_tensor = self.real_label + else: + target_tensor = self.fake_label + return target_tensor.expand_as(prediction) + + def __call__(self, prediction, target_is_real): + """Calculate loss given Discriminator's output and grount truth labels. + + Parameters: + prediction (tensor) - - tpyically the prediction output from a discriminator + target_is_real (bool) - - if the ground truth label is for real images or fake images + + Returns: + the calculated loss. + """ + if self.gan_mode in ['lsgan', 'vanilla']: + target_tensor = self.get_target_tensor(prediction, target_is_real) + loss = self.loss(prediction, target_tensor) + elif self.gan_mode == 'wgangp': + if target_is_real: + loss = -prediction.mean() + else: + loss = prediction.mean() + return loss + + +def cal_gradient_penalty(netD, real_data, fake_data, device, type='mixed', constant=1.0, lambda_gp=10.0): + """Calculate the gradient penalty loss, used in WGAN-GP paper https://arxiv.org/abs/1704.00028 + + Arguments: + netD (network) -- discriminator network + real_data (tensor array) -- real images + fake_data (tensor array) -- generated images from the generator + device (str) -- GPU / CPU: from torch.device('cuda:{}'.format(self.gpu_ids[0])) if self.gpu_ids else torch.device('cpu') + type (str) -- if we mix real and fake data or not [real | fake | mixed]. + constant (float) -- the constant used in formula ( ||gradient||_2 - constant)^2 + lambda_gp (float) -- weight for this loss + + Returns the gradient penalty loss + """ + if lambda_gp > 0.0: + if type == 'real': # either use real images, fake images, or a linear interpolation of two. + interpolatesv = real_data + elif type == 'fake': + interpolatesv = fake_data + elif type == 'mixed': + alpha = torch.rand(real_data.shape[0], 1, device=device) + alpha = alpha.expand(real_data.shape[0], real_data.nelement() // real_data.shape[0]).contiguous().view(*real_data.shape) + interpolatesv = alpha * real_data + ((1 - alpha) * fake_data) + else: + raise NotImplementedError('{} not implemented'.format(type)) + interpolatesv.requires_grad_(True) + disc_interpolates = netD(interpolatesv) + gradients = torch.autograd.grad(outputs=disc_interpolates, inputs=interpolatesv, + grad_outputs=torch.ones(disc_interpolates.size()).to(device), + create_graph=True, retain_graph=True, only_inputs=True) + gradients = gradients[0].view(real_data.size(0), -1) # flat the data + gradient_penalty = (((gradients + 1e-16).norm(2, dim=1) - constant) ** 2).mean() * lambda_gp # added eps + return gradient_penalty, gradients + else: + return 0.0, None + + +class ResnetGenerator(nn.Module): + """Resnet-based generator that consists of Resnet blocks between a few downsampling/upsampling operations. + + We adapt Torch code and idea from Justin Johnson's neural style transfer project(https://github.com/jcjohnson/fast-neural-style) + """ + + def __init__(self, input_nc, output_nc, ngf=64, norm_layer=nn.BatchNorm2d, use_dropout=False, n_blocks=6, padding_type='reflect'): + """Construct a Resnet-based generator + + Parameters: + input_nc (int) -- the number of channels in input images + output_nc (int) -- the number of channels in output images + ngf (int) -- the number of filters in the last conv layer + norm_layer -- normalization layer + use_dropout (bool) -- if use dropout layers + n_blocks (int) -- the number of ResNet blocks + padding_type (str) -- the name of padding layer in conv layers: reflect | replicate | zero + """ + assert(n_blocks >= 0) + super(ResnetGenerator, self).__init__() + if type(norm_layer) == functools.partial: + use_bias = norm_layer.func == nn.InstanceNorm2d + else: + use_bias = norm_layer == nn.InstanceNorm2d + + model = [nn.ReflectionPad2d(3), + nn.Conv2d(input_nc, ngf, kernel_size=7, padding=0, bias=use_bias), + norm_layer(ngf), + nn.ReLU(True)] + + n_downsampling = 2 + for i in range(n_downsampling): # add downsampling layers + mult = 2 ** i + model += [nn.Conv2d(ngf * mult, ngf * mult * 2, kernel_size=3, stride=2, padding=1, bias=use_bias), + norm_layer(ngf * mult * 2), + nn.ReLU(True)] + + mult = 2 ** n_downsampling + for i in range(n_blocks): # add ResNet blocks + + model += [ResnetBlock(ngf * mult, padding_type=padding_type, norm_layer=norm_layer, use_dropout=use_dropout, use_bias=use_bias)] + + for i in range(n_downsampling): # add upsampling layers + mult = 2 ** (n_downsampling - i) + model += [nn.ConvTranspose2d(ngf * mult, int(ngf * mult / 2), + kernel_size=3, stride=2, + padding=1, output_padding=1, + bias=use_bias), + norm_layer(int(ngf * mult / 2)), + nn.ReLU(True)] + model += [nn.ReflectionPad2d(3)] + model += [nn.Conv2d(ngf, output_nc, kernel_size=7, padding=0)] + model += [nn.Tanh()] + + self.model = nn.Sequential(*model) + + def forward(self, input): + """Standard forward""" + return self.model(input) + + +class ResnetBlock(nn.Module): + """Define a Resnet block""" + + def __init__(self, dim, padding_type, norm_layer, use_dropout, use_bias): + """Initialize the Resnet block + + A resnet block is a conv block with skip connections + We construct a conv block with build_conv_block function, + and implement skip connections in function. + Original Resnet paper: https://arxiv.org/pdf/1512.03385.pdf + """ + super(ResnetBlock, self).__init__() + self.conv_block = self.build_conv_block(dim, padding_type, norm_layer, use_dropout, use_bias) + + def build_conv_block(self, dim, padding_type, norm_layer, use_dropout, use_bias): + """Construct a convolutional block. + + Parameters: + dim (int) -- the number of channels in the conv layer. + padding_type (str) -- the name of padding layer: reflect | replicate | zero + norm_layer -- normalization layer + use_dropout (bool) -- if use dropout layers. + use_bias (bool) -- if the conv layer uses bias or not + + Returns a conv block (with a conv layer, a normalization layer, and a non-linearity layer (ReLU)) + """ + conv_block = [] + p = 0 + if padding_type == 'reflect': + conv_block += [nn.ReflectionPad2d(1)] + elif padding_type == 'replicate': + conv_block += [nn.ReplicationPad2d(1)] + elif padding_type == 'zero': + p = 1 + else: + raise NotImplementedError('padding [%s] is not implemented' % padding_type) + + conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=p, bias=use_bias), norm_layer(dim), nn.ReLU(True)] + if use_dropout: + conv_block += [nn.Dropout(0.5)] + + p = 0 + if padding_type == 'reflect': + conv_block += [nn.ReflectionPad2d(1)] + elif padding_type == 'replicate': + conv_block += [nn.ReplicationPad2d(1)] + elif padding_type == 'zero': + p = 1 + else: + raise NotImplementedError('padding [%s] is not implemented' % padding_type) + conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=p, bias=use_bias), norm_layer(dim)] + + return nn.Sequential(*conv_block) + + def forward(self, x): + """Forward function (with skip connections)""" + out = x + self.conv_block(x) # add skip connections + return out + + +class UnetGenerator(nn.Module): + """Create a Unet-based generator""" + + def __init__(self, input_nc, output_nc, num_downs, ngf=64, norm_layer=nn.BatchNorm2d, use_dropout=False): + """Construct a Unet generator + Parameters: + input_nc (int) -- the number of channels in input images + output_nc (int) -- the number of channels in output images + num_downs (int) -- the number of downsamplings in UNet. For example, # if |num_downs| == 7, + image of size 128x128 will become of size 1x1 # at the bottleneck + ngf (int) -- the number of filters in the last conv layer + norm_layer -- normalization layer + + We construct the U-Net from the innermost layer to the outermost layer. + It is a recursive process. + """ + super(UnetGenerator, self).__init__() + # construct unet structure + unet_block = UnetSkipConnectionBlock(ngf * 8, ngf * 8, input_nc=None, submodule=None, norm_layer=norm_layer, innermost=True) # add the innermost layer + for i in range(num_downs - 5): # add intermediate layers with ngf * 8 filters + unet_block = UnetSkipConnectionBlock(ngf * 8, ngf * 8, input_nc=None, submodule=unet_block, norm_layer=norm_layer, use_dropout=use_dropout) + # gradually reduce the number of filters from ngf * 8 to ngf + unet_block = UnetSkipConnectionBlock(ngf * 4, ngf * 8, input_nc=None, submodule=unet_block, norm_layer=norm_layer) + unet_block = UnetSkipConnectionBlock(ngf * 2, ngf * 4, input_nc=None, submodule=unet_block, norm_layer=norm_layer) + unet_block = UnetSkipConnectionBlock(ngf, ngf * 2, input_nc=None, submodule=unet_block, norm_layer=norm_layer) + self.model = UnetSkipConnectionBlock(output_nc, ngf, input_nc=input_nc, submodule=unet_block, outermost=True, norm_layer=norm_layer) # add the outermost layer + + def forward(self, input): + """Standard forward""" + return self.model(input) + + +class UnetSkipConnectionBlock(nn.Module): + """Defines the Unet submodule with skip connection. + X -------------------identity---------------------- + |-- downsampling -- |submodule| -- upsampling --| + """ + + def __init__(self, outer_nc, inner_nc, input_nc=None, + submodule=None, outermost=False, innermost=False, norm_layer=nn.BatchNorm2d, use_dropout=False): + """Construct a Unet submodule with skip connections. + + Parameters: + outer_nc (int) -- the number of filters in the outer conv layer + inner_nc (int) -- the number of filters in the inner conv layer + input_nc (int) -- the number of channels in input images/features + submodule (UnetSkipConnectionBlock) -- previously defined submodules + outermost (bool) -- if this module is the outermost module + innermost (bool) -- if this module is the innermost module + norm_layer -- normalization layer + use_dropout (bool) -- if use dropout layers. + """ + super(UnetSkipConnectionBlock, self).__init__() + self.outermost = outermost + if type(norm_layer) == functools.partial: + use_bias = norm_layer.func == nn.InstanceNorm2d + else: + use_bias = norm_layer == nn.InstanceNorm2d + if input_nc is None: + input_nc = outer_nc + downconv = nn.Conv2d(input_nc, inner_nc, kernel_size=4, + stride=2, padding=1, bias=use_bias) + downrelu = nn.LeakyReLU(0.2, True) + downnorm = norm_layer(inner_nc) + uprelu = nn.ReLU(True) + upnorm = norm_layer(outer_nc) + + if outermost: + upconv = nn.ConvTranspose2d(inner_nc * 2, outer_nc, + kernel_size=4, stride=2, + padding=1) + down = [downconv] + up = [uprelu, upconv, nn.Tanh()] + model = down + [submodule] + up + elif innermost: + upconv = nn.ConvTranspose2d(inner_nc, outer_nc, + kernel_size=4, stride=2, + padding=1, bias=use_bias) + down = [downrelu, downconv] + up = [uprelu, upconv, upnorm] + model = down + up + else: + upconv = nn.ConvTranspose2d(inner_nc * 2, outer_nc, + kernel_size=4, stride=2, + padding=1, bias=use_bias) + down = [downrelu, downconv, downnorm] + up = [uprelu, upconv, upnorm] + + if use_dropout: + model = down + [submodule] + up + [nn.Dropout(0.5)] + else: + model = down + [submodule] + up + + self.model = nn.Sequential(*model) + + def forward(self, x): + if self.outermost: + return self.model(x) + else: # add skip connections + return torch.cat([x, self.model(x)], 1) + + +class NLayerDiscriminator(nn.Module): + """Defines a PatchGAN discriminator""" + + def __init__(self, input_nc, ndf=64, n_layers=3, norm_layer=nn.BatchNorm2d): + """Construct a PatchGAN discriminator + + Parameters: + input_nc (int) -- the number of channels in input images + ndf (int) -- the number of filters in the last conv layer + n_layers (int) -- the number of conv layers in the discriminator + norm_layer -- normalization layer + """ + super(NLayerDiscriminator, self).__init__() + if type(norm_layer) == functools.partial: # no need to use bias as BatchNorm2d has affine parameters + use_bias = norm_layer.func == nn.InstanceNorm2d + else: + use_bias = norm_layer == nn.InstanceNorm2d + + kw = 4 + padw = 1 + sequence = [nn.Conv2d(input_nc, ndf, kernel_size=kw, stride=2, padding=padw), nn.LeakyReLU(0.2, True)] + nf_mult = 1 + nf_mult_prev = 1 + for n in range(1, n_layers): # gradually increase the number of filters + nf_mult_prev = nf_mult + nf_mult = min(2 ** n, 8) + sequence += [ + nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult, kernel_size=kw, stride=2, padding=padw, bias=use_bias), + norm_layer(ndf * nf_mult), + nn.LeakyReLU(0.2, True) + ] + + nf_mult_prev = nf_mult + nf_mult = min(2 ** n_layers, 8) + sequence += [ + nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult, kernel_size=kw, stride=1, padding=padw, bias=use_bias), + norm_layer(ndf * nf_mult), + nn.LeakyReLU(0.2, True) + ] + + sequence += [nn.Conv2d(ndf * nf_mult, 1, kernel_size=kw, stride=1, padding=padw)] # output 1 channel prediction map + self.model = nn.Sequential(*sequence) + + def forward(self, input): + """Standard forward.""" + return self.model(input) + + +class PixelDiscriminator(nn.Module): + """Defines a 1x1 PatchGAN discriminator (pixelGAN)""" + + def __init__(self, input_nc, ndf=64, norm_layer=nn.BatchNorm2d): + """Construct a 1x1 PatchGAN discriminator + + Parameters: + input_nc (int) -- the number of channels in input images + ndf (int) -- the number of filters in the last conv layer + norm_layer -- normalization layer + """ + super(PixelDiscriminator, self).__init__() + if type(norm_layer) == functools.partial: # no need to use bias as BatchNorm2d has affine parameters + use_bias = norm_layer.func == nn.InstanceNorm2d + else: + use_bias = norm_layer == nn.InstanceNorm2d + + self.net = [ + nn.Conv2d(input_nc, ndf, kernel_size=1, stride=1, padding=0), + nn.LeakyReLU(0.2, True), + nn.Conv2d(ndf, ndf * 2, kernel_size=1, stride=1, padding=0, bias=use_bias), + norm_layer(ndf * 2), + nn.LeakyReLU(0.2, True), + nn.Conv2d(ndf * 2, 1, kernel_size=1, stride=1, padding=0, bias=use_bias)] + + self.net = nn.Sequential(*self.net) + + def forward(self, input): + """Standard forward.""" + return self.net(input) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/pix2pix4depth_model.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/pix2pix4depth_model.py new file mode 100644 index 0000000000000000000000000000000000000000..89e89652feb96314973a050c5a2477b474630abb --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/models/pix2pix4depth_model.py @@ -0,0 +1,155 @@ +import torch +from .base_model import BaseModel +from . import networks + + +class Pix2Pix4DepthModel(BaseModel): + """ This class implements the pix2pix model, for learning a mapping from input images to output images given paired data. + + The model training requires '--dataset_mode aligned' dataset. + By default, it uses a '--netG unet256' U-Net generator, + a '--netD basic' discriminator (PatchGAN), + and a '--gan_mode' vanilla GAN loss (the cross-entropy objective used in the orignal GAN paper). + + pix2pix paper: https://arxiv.org/pdf/1611.07004.pdf + """ + @staticmethod + def modify_commandline_options(parser, is_train=True): + """Add new dataset-specific options, and rewrite default values for existing options. + + Parameters: + parser -- original option parser + is_train (bool) -- whether training phase or test phase. You can use this flag to add training-specific or test-specific options. + + Returns: + the modified parser. + + For pix2pix, we do not use image buffer + The training objective is: GAN Loss + lambda_L1 * ||G(A)-B||_1 + By default, we use vanilla GAN loss, UNet with batchnorm, and aligned datasets. + """ + # changing the default values to match the pix2pix paper (https://phillipi.github.io/pix2pix/) + parser.set_defaults(input_nc=2,output_nc=1,norm='none', netG='unet_1024', dataset_mode='depthmerge') + if is_train: + parser.set_defaults(pool_size=0, gan_mode='vanilla',) + parser.add_argument('--lambda_L1', type=float, default=1000, help='weight for L1 loss') + return parser + + def __init__(self, opt): + """Initialize the pix2pix class. + + Parameters: + opt (Option class)-- stores all the experiment flags; needs to be a subclass of BaseOptions + """ + BaseModel.__init__(self, opt) + # specify the training losses you want to print out. The training/test scripts will call + + self.loss_names = ['G_GAN', 'G_L1', 'D_real', 'D_fake'] + # self.loss_names = ['G_L1'] + + # specify the images you want to save/display. The training/test scripts will call + if self.isTrain: + self.visual_names = ['outer','inner', 'fake_B', 'real_B'] + else: + self.visual_names = ['fake_B'] + + # specify the models you want to save to the disk. The training/test scripts will call and + if self.isTrain: + self.model_names = ['G','D'] + else: # during test time, only load G + self.model_names = ['G'] + + # define networks (both generator and discriminator) + self.netG = networks.define_G(opt.input_nc, opt.output_nc, 64, 'unet_1024', 'none', + False, 'normal', 0.02, self.gpu_ids) + + if self.isTrain: # define a discriminator; conditional GANs need to take both input and output images; Therefore, #channels for D is input_nc + output_nc + self.netD = networks.define_D(opt.input_nc + opt.output_nc, opt.ndf, opt.netD, + opt.n_layers_D, opt.norm, opt.init_type, opt.init_gain, self.gpu_ids) + + if self.isTrain: + # define loss functions + self.criterionGAN = networks.GANLoss(opt.gan_mode).to(self.device) + self.criterionL1 = torch.nn.L1Loss() + # initialize optimizers; schedulers will be automatically created by function . + self.optimizer_G = torch.optim.Adam(self.netG.parameters(), lr=1e-4, betas=(opt.beta1, 0.999)) + self.optimizer_D = torch.optim.Adam(self.netD.parameters(), lr=2e-06, betas=(opt.beta1, 0.999)) + self.optimizers.append(self.optimizer_G) + self.optimizers.append(self.optimizer_D) + + def set_input_train(self, input): + self.outer = input['data_outer'].to(self.device) + self.outer = torch.nn.functional.interpolate(self.outer,(1024,1024),mode='bilinear',align_corners=False) + + self.inner = input['data_inner'].to(self.device) + self.inner = torch.nn.functional.interpolate(self.inner,(1024,1024),mode='bilinear',align_corners=False) + + self.image_paths = input['image_path'] + + if self.isTrain: + self.gtfake = input['data_gtfake'].to(self.device) + self.gtfake = torch.nn.functional.interpolate(self.gtfake, (1024, 1024), mode='bilinear', align_corners=False) + self.real_B = self.gtfake + + self.real_A = torch.cat((self.outer, self.inner), 1) + + def set_input(self, outer, inner): + inner = torch.from_numpy(inner).unsqueeze(0).unsqueeze(0) + outer = torch.from_numpy(outer).unsqueeze(0).unsqueeze(0) + + inner = (inner - torch.min(inner))/(torch.max(inner)-torch.min(inner)) + outer = (outer - torch.min(outer))/(torch.max(outer)-torch.min(outer)) + + inner = self.normalize(inner) + outer = self.normalize(outer) + + self.real_A = torch.cat((outer, inner), 1).to(self.device) + + + def normalize(self, input): + input = input * 2 + input = input - 1 + return input + + def forward(self): + """Run forward pass; called by both functions and .""" + self.fake_B = self.netG(self.real_A) # G(A) + + def backward_D(self): + """Calculate GAN loss for the discriminator""" + # Fake; stop backprop to the generator by detaching fake_B + fake_AB = torch.cat((self.real_A, self.fake_B), 1) # we use conditional GANs; we need to feed both input and output to the discriminator + pred_fake = self.netD(fake_AB.detach()) + self.loss_D_fake = self.criterionGAN(pred_fake, False) + # Real + real_AB = torch.cat((self.real_A, self.real_B), 1) + pred_real = self.netD(real_AB) + self.loss_D_real = self.criterionGAN(pred_real, True) + # combine loss and calculate gradients + self.loss_D = (self.loss_D_fake + self.loss_D_real) * 0.5 + self.loss_D.backward() + + def backward_G(self): + """Calculate GAN and L1 loss for the generator""" + # First, G(A) should fake the discriminator + fake_AB = torch.cat((self.real_A, self.fake_B), 1) + pred_fake = self.netD(fake_AB) + self.loss_G_GAN = self.criterionGAN(pred_fake, True) + # Second, G(A) = B + self.loss_G_L1 = self.criterionL1(self.fake_B, self.real_B) * self.opt.lambda_L1 + # combine loss and calculate gradients + self.loss_G = self.loss_G_L1 + self.loss_G_GAN + self.loss_G.backward() + + def optimize_parameters(self): + self.forward() # compute fake images: G(A) + # update D + self.set_requires_grad(self.netD, True) # enable backprop for D + self.optimizer_D.zero_grad() # set D's gradients to zero + self.backward_D() # calculate gradients for D + self.optimizer_D.step() # update D's weights + # update G + self.set_requires_grad(self.netD, False) # D requires no gradients when optimizing G + self.optimizer_G.zero_grad() # set G's gradients to zero + self.backward_G() # calculate graidents for G + self.optimizer_G.step() # udpate G's weights \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/options/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/options/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e7eedebe54aa70169fd25951b3034d819e396c90 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/options/__init__.py @@ -0,0 +1 @@ +"""This package options includes option modules: training options, test options, and basic options (used in both training and test).""" diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/options/base_options.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/options/base_options.py new file mode 100644 index 0000000000000000000000000000000000000000..533a1e88a7e8494223f6994e6861c93667754f83 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/options/base_options.py @@ -0,0 +1,156 @@ +import argparse +import os +from ...pix2pix.util import util +# import torch +from ...pix2pix import models +# import pix2pix.data +import numpy as np + +class BaseOptions(): + """This class defines options used during both training and test time. + + It also implements several helper functions such as parsing, printing, and saving the options. + It also gathers additional options defined in functions in both dataset class and model class. + """ + + def __init__(self): + """Reset the class; indicates the class hasn't been initailized""" + self.initialized = False + + def initialize(self, parser): + """Define the common options that are used in both training and test.""" + # basic parameters + parser.add_argument('--dataroot', help='path to images (should have subfolders trainA, trainB, valA, valB, etc)') + parser.add_argument('--name', type=str, default='void', help='mahdi_unet_new, scaled_unet') + parser.add_argument('--gpu_ids', type=str, default='0', help='gpu ids: e.g. 0 0,1,2, 0,2. use -1 for CPU') + parser.add_argument('--checkpoints_dir', type=str, default='./pix2pix/checkpoints', help='models are saved here') + # model parameters + parser.add_argument('--model', type=str, default='cycle_gan', help='chooses which model to use. [cycle_gan | pix2pix | test | colorization]') + parser.add_argument('--input_nc', type=int, default=2, help='# of input image channels: 3 for RGB and 1 for grayscale') + parser.add_argument('--output_nc', type=int, default=1, help='# of output image channels: 3 for RGB and 1 for grayscale') + parser.add_argument('--ngf', type=int, default=64, help='# of gen filters in the last conv layer') + parser.add_argument('--ndf', type=int, default=64, help='# of discrim filters in the first conv layer') + parser.add_argument('--netD', type=str, default='basic', help='specify discriminator architecture [basic | n_layers | pixel]. The basic model is a 70x70 PatchGAN. n_layers allows you to specify the layers in the discriminator') + parser.add_argument('--netG', type=str, default='resnet_9blocks', help='specify generator architecture [resnet_9blocks | resnet_6blocks | unet_256 | unet_128]') + parser.add_argument('--n_layers_D', type=int, default=3, help='only used if netD==n_layers') + parser.add_argument('--norm', type=str, default='instance', help='instance normalization or batch normalization [instance | batch | none]') + parser.add_argument('--init_type', type=str, default='normal', help='network initialization [normal | xavier | kaiming | orthogonal]') + parser.add_argument('--init_gain', type=float, default=0.02, help='scaling factor for normal, xavier and orthogonal.') + parser.add_argument('--no_dropout', action='store_true', help='no dropout for the generator') + # dataset parameters + parser.add_argument('--dataset_mode', type=str, default='unaligned', help='chooses how datasets are loaded. [unaligned | aligned | single | colorization]') + parser.add_argument('--direction', type=str, default='AtoB', help='AtoB or BtoA') + parser.add_argument('--serial_batches', action='store_true', help='if true, takes images in order to make batches, otherwise takes them randomly') + parser.add_argument('--num_threads', default=4, type=int, help='# threads for loading data') + parser.add_argument('--batch_size', type=int, default=1, help='input batch size') + parser.add_argument('--load_size', type=int, default=672, help='scale images to this size') + parser.add_argument('--crop_size', type=int, default=672, help='then crop to this size') + parser.add_argument('--max_dataset_size', type=int, default=10000, help='Maximum number of samples allowed per dataset. If the dataset directory contains more than max_dataset_size, only a subset is loaded.') + parser.add_argument('--preprocess', type=str, default='resize_and_crop', help='scaling and cropping of images at load time [resize_and_crop | crop | scale_width | scale_width_and_crop | none]') + parser.add_argument('--no_flip', action='store_true', help='if specified, do not flip the images for data augmentation') + parser.add_argument('--display_winsize', type=int, default=256, help='display window size for both visdom and HTML') + # additional parameters + parser.add_argument('--epoch', type=str, default='latest', help='which epoch to load? set to latest to use latest cached model') + parser.add_argument('--load_iter', type=int, default='0', help='which iteration to load? if load_iter > 0, the code will load models by iter_[load_iter]; otherwise, the code will load models by [epoch]') + parser.add_argument('--verbose', action='store_true', help='if specified, print more debugging information') + parser.add_argument('--suffix', default='', type=str, help='customized suffix: opt.name = opt.name + suffix: e.g., {model}_{netG}_size{load_size}') + + parser.add_argument('--data_dir', type=str, required=False, + help='input files directory images can be .png .jpg .tiff') + parser.add_argument('--output_dir', type=str, required=False, + help='result dir. result depth will be png. vides are JMPG as avi') + parser.add_argument('--savecrops', type=int, required=False) + parser.add_argument('--savewholeest', type=int, required=False) + parser.add_argument('--output_resolution', type=int, required=False, + help='0 for no restriction 1 for resize to input size') + parser.add_argument('--net_receptive_field_size', type=int, required=False) + parser.add_argument('--pix2pixsize', type=int, required=False) + parser.add_argument('--generatevideo', type=int, required=False) + parser.add_argument('--depthNet', type=int, required=False, help='0: midas 1:strurturedRL') + parser.add_argument('--R0', action='store_true') + parser.add_argument('--R20', action='store_true') + parser.add_argument('--Final', action='store_true') + parser.add_argument('--colorize_results', action='store_true') + parser.add_argument('--max_res', type=float, default=np.inf) + + self.initialized = True + return parser + + def gather_options(self): + """Initialize our parser with basic options(only once). + Add additional model-specific and dataset-specific options. + These options are defined in the function + in model and dataset classes. + """ + if not self.initialized: # check if it has been initialized + parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser = self.initialize(parser) + + # get the basic options + opt, _ = parser.parse_known_args() + + # modify model-related parser options + model_name = opt.model + model_option_setter = models.get_option_setter(model_name) + parser = model_option_setter(parser, self.isTrain) + opt, _ = parser.parse_known_args() # parse again with new defaults + + # modify dataset-related parser options + # dataset_name = opt.dataset_mode + # dataset_option_setter = pix2pix.data.get_option_setter(dataset_name) + # parser = dataset_option_setter(parser, self.isTrain) + + # save and return the parser + self.parser = parser + #return parser.parse_args() #EVIL + return opt + + def print_options(self, opt): + """Print and save options + + It will print both current options and default values(if different). + It will save options into a text file / [checkpoints_dir] / opt.txt + """ + message = '' + message += '----------------- Options ---------------\n' + for k, v in sorted(vars(opt).items()): + comment = '' + default = self.parser.get_default(k) + if v != default: + comment = '\t[default: %s]' % str(default) + message += '{:>25}: {:<30}{}\n'.format(str(k), str(v), comment) + message += '----------------- End -------------------' + print(message) + + # save to the disk + expr_dir = os.path.join(opt.checkpoints_dir, opt.name) + util.mkdirs(expr_dir) + file_name = os.path.join(expr_dir, '{}_opt.txt'.format(opt.phase)) + with open(file_name, 'wt') as opt_file: + opt_file.write(message) + opt_file.write('\n') + + def parse(self): + """Parse our options, create checkpoints directory suffix, and set up gpu device.""" + opt = self.gather_options() + opt.isTrain = self.isTrain # train or test + + # process opt.suffix + if opt.suffix: + suffix = ('_' + opt.suffix.format(**vars(opt))) if opt.suffix != '' else '' + opt.name = opt.name + suffix + + #self.print_options(opt) + + # set gpu ids + str_ids = opt.gpu_ids.split(',') + opt.gpu_ids = [] + for str_id in str_ids: + id = int(str_id) + if id >= 0: + opt.gpu_ids.append(id) + #if len(opt.gpu_ids) > 0: + # torch.cuda.set_device(opt.gpu_ids[0]) + + self.opt = opt + return self.opt diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/options/test_options.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/options/test_options.py new file mode 100644 index 0000000000000000000000000000000000000000..a3424b5e3b66d6813f74c8cecad691d7488d121c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/options/test_options.py @@ -0,0 +1,22 @@ +from .base_options import BaseOptions + + +class TestOptions(BaseOptions): + """This class includes test options. + + It also includes shared options defined in BaseOptions. + """ + + def initialize(self, parser): + parser = BaseOptions.initialize(self, parser) # define shared options + parser.add_argument('--aspect_ratio', type=float, default=1.0, help='aspect ratio of result images') + parser.add_argument('--phase', type=str, default='test', help='train, val, test, etc') + # Dropout and Batchnorm has different behavioir during training and test. + parser.add_argument('--eval', action='store_true', help='use eval mode during test time.') + parser.add_argument('--num_test', type=int, default=50, help='how many test images to run') + # rewrite devalue values + parser.set_defaults(model='pix2pix4depth') + # To avoid cropping, the load_size should be the same as crop_size + parser.set_defaults(load_size=parser.get_default('crop_size')) + self.isTrain = False + return parser diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ae36f63d8859ec0c60dcbfe67c4ac324e751ddf7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/__init__.py @@ -0,0 +1 @@ +"""This package includes a miscellaneous collection of useful helper functions.""" diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/get_data.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/get_data.py new file mode 100644 index 0000000000000000000000000000000000000000..97edc3ce3c3ab6d6080dca34e73a5fb77bb715fb --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/get_data.py @@ -0,0 +1,110 @@ +from __future__ import print_function +import os +import tarfile +import requests +from warnings import warn +from zipfile import ZipFile +from bs4 import BeautifulSoup +from os.path import abspath, isdir, join, basename + + +class GetData(object): + """A Python script for downloading CycleGAN or pix2pix datasets. + + Parameters: + technique (str) -- One of: 'cyclegan' or 'pix2pix'. + verbose (bool) -- If True, print additional information. + + Examples: + >>> from util.get_data import GetData + >>> gd = GetData(technique='cyclegan') + >>> new_data_path = gd.get(save_path='./datasets') # options will be displayed. + + Alternatively, You can use bash scripts: 'scripts/download_pix2pix_model.sh' + and 'scripts/download_cyclegan_model.sh'. + """ + + def __init__(self, technique='cyclegan', verbose=True): + url_dict = { + 'pix2pix': 'http://efrosgans.eecs.berkeley.edu/pix2pix/datasets/', + 'cyclegan': 'https://people.eecs.berkeley.edu/~taesung_park/CycleGAN/datasets' + } + self.url = url_dict.get(technique.lower()) + self._verbose = verbose + + def _print(self, text): + if self._verbose: + print(text) + + @staticmethod + def _get_options(r): + soup = BeautifulSoup(r.text, 'lxml') + options = [h.text for h in soup.find_all('a', href=True) + if h.text.endswith(('.zip', 'tar.gz'))] + return options + + def _present_options(self): + r = requests.get(self.url) + options = self._get_options(r) + print('Options:\n') + for i, o in enumerate(options): + print("{0}: {1}".format(i, o)) + choice = input("\nPlease enter the number of the " + "dataset above you wish to download:") + return options[int(choice)] + + def _download_data(self, dataset_url, save_path): + if not isdir(save_path): + os.makedirs(save_path) + + base = basename(dataset_url) + temp_save_path = join(save_path, base) + + with open(temp_save_path, "wb") as f: + r = requests.get(dataset_url) + f.write(r.content) + + if base.endswith('.tar.gz'): + obj = tarfile.open(temp_save_path) + elif base.endswith('.zip'): + obj = ZipFile(temp_save_path, 'r') + else: + raise ValueError("Unknown File Type: {0}.".format(base)) + + self._print("Unpacking Data...") + obj.extractall(save_path) + obj.close() + os.remove(temp_save_path) + + def get(self, save_path, dataset=None): + """ + + Download a dataset. + + Parameters: + save_path (str) -- A directory to save the data to. + dataset (str) -- (optional). A specific dataset to download. + Note: this must include the file extension. + If None, options will be presented for you + to choose from. + + Returns: + save_path_full (str) -- the absolute path to the downloaded data. + + """ + if dataset is None: + selected_dataset = self._present_options() + else: + selected_dataset = dataset + + save_path_full = join(save_path, selected_dataset.split('.')[0]) + + if isdir(save_path_full): + warn("\n'{0}' already exists. Voiding Download.".format( + save_path_full)) + else: + self._print('Downloading Data...') + url = "{0}/{1}".format(self.url, selected_dataset) + self._download_data(url, save_path=save_path) + + return abspath(save_path_full) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/guidedfilter.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/guidedfilter.py new file mode 100644 index 0000000000000000000000000000000000000000..d377ff12e078a5f156e9246b63573dae71825fad --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/guidedfilter.py @@ -0,0 +1,47 @@ +import numpy as np + +class GuidedFilter(): + def __init__(self, source, reference, r=64, eps= 0.05**2): + self.source = source; + self.reference = reference; + self.r = r + self.eps = eps + + self.smooth = self.guidedfilter(self.source,self.reference,self.r,self.eps) + + def boxfilter(self,img, r): + (rows, cols) = img.shape + imDst = np.zeros_like(img) + + imCum = np.cumsum(img, 0) + imDst[0 : r+1, :] = imCum[r : 2*r+1, :] + imDst[r+1 : rows-r, :] = imCum[2*r+1 : rows, :] - imCum[0 : rows-2*r-1, :] + imDst[rows-r: rows, :] = np.tile(imCum[rows-1, :], [r, 1]) - imCum[rows-2*r-1 : rows-r-1, :] + + imCum = np.cumsum(imDst, 1) + imDst[:, 0 : r+1] = imCum[:, r : 2*r+1] + imDst[:, r+1 : cols-r] = imCum[:, 2*r+1 : cols] - imCum[:, 0 : cols-2*r-1] + imDst[:, cols-r: cols] = np.tile(imCum[:, cols-1], [r, 1]).T - imCum[:, cols-2*r-1 : cols-r-1] + + return imDst + + def guidedfilter(self,I, p, r, eps): + (rows, cols) = I.shape + N = self.boxfilter(np.ones([rows, cols]), r) + + meanI = self.boxfilter(I, r) / N + meanP = self.boxfilter(p, r) / N + meanIp = self.boxfilter(I * p, r) / N + covIp = meanIp - meanI * meanP + + meanII = self.boxfilter(I * I, r) / N + varI = meanII - meanI * meanI + + a = covIp / (varI + eps) + b = meanP - a * meanI + + meanA = self.boxfilter(a, r) / N + meanB = self.boxfilter(b, r) / N + + q = meanA * I + meanB + return q \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/html.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/html.py new file mode 100644 index 0000000000000000000000000000000000000000..cc3262a1eafda34842e4dbad47bb6ba72f0c5a68 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/html.py @@ -0,0 +1,86 @@ +import dominate +from dominate.tags import meta, h3, table, tr, td, p, a, img, br +import os + + +class HTML: + """This HTML class allows us to save images and write texts into a single HTML file. + + It consists of functions such as (add a text header to the HTML file), + (add a row of images to the HTML file), and (save the HTML to the disk). + It is based on Python library 'dominate', a Python library for creating and manipulating HTML documents using a DOM API. + """ + + def __init__(self, web_dir, title, refresh=0): + """Initialize the HTML classes + + Parameters: + web_dir (str) -- a directory that stores the webpage. HTML file will be created at /index.html; images will be saved at 0: + with self.doc.head: + meta(http_equiv="refresh", content=str(refresh)) + + def get_image_dir(self): + """Return the directory that stores images""" + return self.img_dir + + def add_header(self, text): + """Insert a header to the HTML file + + Parameters: + text (str) -- the header text + """ + with self.doc: + h3(text) + + def add_images(self, ims, txts, links, width=400): + """add images to the HTML file + + Parameters: + ims (str list) -- a list of image paths + txts (str list) -- a list of image names shown on the website + links (str list) -- a list of hyperref links; when you click an image, it will redirect you to a new page + """ + self.t = table(border=1, style="table-layout: fixed;") # Insert a table + self.doc.add(self.t) + with self.t: + with tr(): + for im, txt, link in zip(ims, txts, links): + with td(style="word-wrap: break-word;", halign="center", valign="top"): + with p(): + with a(href=os.path.join('images', link)): + img(style="width:%dpx" % width, src=os.path.join('images', im)) + br() + p(txt) + + def save(self): + """save the current content to the HMTL file""" + html_file = '%s/index.html' % self.web_dir + f = open(html_file, 'wt') + f.write(self.doc.render()) + f.close() + + +if __name__ == '__main__': # we show an example usage here. + html = HTML('web/', 'test_html') + html.add_header('hello world') + + ims, txts, links = [], [], [] + for n in range(4): + ims.append('image_%d.png' % n) + txts.append('text_%d' % n) + links.append('image_%d.png' % n) + html.add_images(ims, txts, links) + html.save() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/image_pool.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/image_pool.py new file mode 100644 index 0000000000000000000000000000000000000000..6d086f882bc3d1b90c529fce6cddaaa75f2005d7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/image_pool.py @@ -0,0 +1,54 @@ +import random +import torch + + +class ImagePool(): + """This class implements an image buffer that stores previously generated images. + + This buffer enables us to update discriminators using a history of generated images + rather than the ones produced by the latest generators. + """ + + def __init__(self, pool_size): + """Initialize the ImagePool class + + Parameters: + pool_size (int) -- the size of image buffer, if pool_size=0, no buffer will be created + """ + self.pool_size = pool_size + if self.pool_size > 0: # create an empty pool + self.num_imgs = 0 + self.images = [] + + def query(self, images): + """Return an image from the pool. + + Parameters: + images: the latest generated images from the generator + + Returns images from the buffer. + + By 50/100, the buffer will return input images. + By 50/100, the buffer will return images previously stored in the buffer, + and insert the current images to the buffer. + """ + if self.pool_size == 0: # if the buffer size is 0, do nothing + return images + return_images = [] + for image in images: + image = torch.unsqueeze(image.data, 0) + if self.num_imgs < self.pool_size: # if the buffer is not full; keep inserting current images to the buffer + self.num_imgs = self.num_imgs + 1 + self.images.append(image) + return_images.append(image) + else: + p = random.uniform(0, 1) + if p > 0.5: # by 50% chance, the buffer will return a previously stored image, and insert the current image into the buffer + random_id = random.randint(0, self.pool_size - 1) # randint is inclusive + tmp = self.images[random_id].clone() + self.images[random_id] = image + return_images.append(tmp) + else: # by another 50% chance, the buffer will return the current image + return_images.append(image) + return_images = torch.cat(return_images, 0) # collect all the images and return + return return_images diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/util.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/util.py new file mode 100644 index 0000000000000000000000000000000000000000..8a7aceaa00681cb76675df7866bf8db58c8d2caf --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/util.py @@ -0,0 +1,105 @@ +"""This module contains simple helper functions """ +from __future__ import print_function +import torch +import numpy as np +from PIL import Image +import os + + +def tensor2im(input_image, imtype=np.uint16): + """"Converts a Tensor array into a numpy image array. + + Parameters: + input_image (tensor) -- the input image tensor array + imtype (type) -- the desired type of the converted numpy array + """ + if not isinstance(input_image, np.ndarray): + if isinstance(input_image, torch.Tensor): # get the data from a variable + image_tensor = input_image.data + else: + return input_image + image_numpy = torch.squeeze(image_tensor).cpu().numpy() # convert it into a numpy array + image_numpy = (image_numpy + 1) / 2.0 * (2**16-1) # + else: # if it is a numpy array, do nothing + image_numpy = input_image + return image_numpy.astype(imtype) + + +def diagnose_network(net, name='network'): + """Calculate and print the mean of average absolute(gradients) + + Parameters: + net (torch network) -- Torch network + name (str) -- the name of the network + """ + mean = 0.0 + count = 0 + for param in net.parameters(): + if param.grad is not None: + mean += torch.mean(torch.abs(param.grad.data)) + count += 1 + if count > 0: + mean = mean / count + print(name) + print(mean) + + +def save_image(image_numpy, image_path, aspect_ratio=1.0): + """Save a numpy image to the disk + + Parameters: + image_numpy (numpy array) -- input numpy array + image_path (str) -- the path of the image + """ + image_pil = Image.fromarray(image_numpy) + + image_pil = image_pil.convert('I;16') + + # image_pil = Image.fromarray(image_numpy) + # h, w, _ = image_numpy.shape + # + # if aspect_ratio > 1.0: + # image_pil = image_pil.resize((h, int(w * aspect_ratio)), Image.BICUBIC) + # if aspect_ratio < 1.0: + # image_pil = image_pil.resize((int(h / aspect_ratio), w), Image.BICUBIC) + + image_pil.save(image_path) + + +def print_numpy(x, val=True, shp=False): + """Print the mean, min, max, median, std, and size of a numpy array + + Parameters: + val (bool) -- if print the values of the numpy array + shp (bool) -- if print the shape of the numpy array + """ + x = x.astype(np.float64) + if shp: + print('shape,', x.shape) + if val: + x = x.flatten() + print('mean = %3.3f, min = %3.3f, max = %3.3f, median = %3.3f, std=%3.3f' % ( + np.mean(x), np.min(x), np.max(x), np.median(x), np.std(x))) + + +def mkdirs(paths): + """create empty directories if they don't exist + + Parameters: + paths (str list) -- a list of directory paths + """ + if isinstance(paths, list) and not isinstance(paths, str): + for path in paths: + mkdir(path) + else: + mkdir(paths) + + +def mkdir(path): + """create a single empty directory if it didn't exist + + Parameters: + path (str) -- a single directory path + """ + if not os.path.exists(path): + os.makedirs(path) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/visualizer.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/visualizer.py new file mode 100644 index 0000000000000000000000000000000000000000..810a0513ab997103ace77b665c9a17f223b173c9 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/leres/pix2pix/util/visualizer.py @@ -0,0 +1,166 @@ +import numpy as np +import os +import sys +import ntpath +import time +from . import util, html +from subprocess import Popen, PIPE +import torch + + +if sys.version_info[0] == 2: + VisdomExceptionBase = Exception +else: + VisdomExceptionBase = ConnectionError + + +def save_images(webpage, visuals, image_path, aspect_ratio=1.0, width=256): + """Save images to the disk. + + Parameters: + webpage (the HTML class) -- the HTML webpage class that stores these imaegs (see html.py for more details) + visuals (OrderedDict) -- an ordered dictionary that stores (name, images (either tensor or numpy) ) pairs + image_path (str) -- the string is used to create image paths + aspect_ratio (float) -- the aspect ratio of saved images + width (int) -- the images will be resized to width x width + + This function will save images stored in 'visuals' to the HTML file specified by 'webpage'. + """ + image_dir = webpage.get_image_dir() + short_path = ntpath.basename(image_path[0]) + name = os.path.splitext(short_path)[0] + + webpage.add_header(name) + ims, txts, links = [], [], [] + + for label, im_data in visuals.items(): + im = util.tensor2im(im_data) + image_name = '%s_%s.png' % (name, label) + save_path = os.path.join(image_dir, image_name) + util.save_image(im, save_path, aspect_ratio=aspect_ratio) + ims.append(image_name) + txts.append(label) + links.append(image_name) + webpage.add_images(ims, txts, links, width=width) + + +class Visualizer(): + """This class includes several functions that can display/save images and print/save logging information. + + It uses a Python library 'visdom' for display, and a Python library 'dominate' (wrapped in 'HTML') for creating HTML files with images. + """ + + def __init__(self, opt): + """Initialize the Visualizer class + + Parameters: + opt -- stores all the experiment flags; needs to be a subclass of BaseOptions + Step 1: Cache the training/test options + Step 2: connect to a visdom server + Step 3: create an HTML object for saveing HTML filters + Step 4: create a logging file to store training losses + """ + self.opt = opt # cache the option + self.display_id = opt.display_id + self.use_html = opt.isTrain and not opt.no_html + self.win_size = opt.display_winsize + self.name = opt.name + self.port = opt.display_port + self.saved = False + + if self.use_html: # create an HTML object at /web/; images will be saved under /web/images/ + self.web_dir = os.path.join(opt.checkpoints_dir, opt.name, 'web') + self.img_dir = os.path.join(self.web_dir, 'images') + print('create web directory %s...' % self.web_dir) + util.mkdirs([self.web_dir, self.img_dir]) + # create a logging file to store training losses + self.log_name = os.path.join(opt.checkpoints_dir, opt.name, 'loss_log.txt') + with open(self.log_name, "a") as log_file: + now = time.strftime("%c") + log_file.write('================ Training Loss (%s) ================\n' % now) + + def reset(self): + """Reset the self.saved status""" + self.saved = False + + def create_visdom_connections(self): + """If the program could not connect to Visdom server, this function will start a new server at port < self.port > """ + cmd = sys.executable + ' -m visdom.server -p %d &>/dev/null &' % self.port + print('\n\nCould not connect to Visdom server. \n Trying to start a server....') + print('Command: %s' % cmd) + Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) + + def display_current_results(self, visuals, epoch, save_result): + """Display current results on visdom; save current results to an HTML file. + + Parameters: + visuals (OrderedDict) - - dictionary of images to display or save + epoch (int) - - the current epoch + save_result (bool) - - if save the current results to an HTML file + """ + if self.use_html and (save_result or not self.saved): # save images to an HTML file if they haven't been saved. + self.saved = True + # save images to the disk + for label, image in visuals.items(): + image_numpy = util.tensor2im(image) + img_path = os.path.join(self.img_dir, 'epoch%.3d_%s.png' % (epoch, label)) + util.save_image(image_numpy, img_path) + + # update website + webpage = html.HTML(self.web_dir, 'Experiment name = %s' % self.name, refresh=1) + for n in range(epoch, 0, -1): + webpage.add_header('epoch [%d]' % n) + ims, txts, links = [], [], [] + + for label, image_numpy in visuals.items(): + # image_numpy = util.tensor2im(image) + img_path = 'epoch%.3d_%s.png' % (n, label) + ims.append(img_path) + txts.append(label) + links.append(img_path) + webpage.add_images(ims, txts, links, width=self.win_size) + webpage.save() + + # def plot_current_losses(self, epoch, counter_ratio, losses): + # """display the current losses on visdom display: dictionary of error labels and values + # + # Parameters: + # epoch (int) -- current epoch + # counter_ratio (float) -- progress (percentage) in the current epoch, between 0 to 1 + # losses (OrderedDict) -- training losses stored in the format of (name, float) pairs + # """ + # if not hasattr(self, 'plot_data'): + # self.plot_data = {'X': [], 'Y': [], 'legend': list(losses.keys())} + # self.plot_data['X'].append(epoch + counter_ratio) + # self.plot_data['Y'].append([losses[k] for k in self.plot_data['legend']]) + # try: + # self.vis.line( + # X=np.stack([np.array(self.plot_data['X'])] * len(self.plot_data['legend']), 1), + # Y=np.array(self.plot_data['Y']), + # opts={ + # 'title': self.name + ' loss over time', + # 'legend': self.plot_data['legend'], + # 'xlabel': 'epoch', + # 'ylabel': 'loss'}, + # win=self.display_id) + # except VisdomExceptionBase: + # self.create_visdom_connections() + + # losses: same format as |losses| of plot_current_losses + def print_current_losses(self, epoch, iters, losses, t_comp, t_data): + """print current losses on console; also save the losses to the disk + + Parameters: + epoch (int) -- current epoch + iters (int) -- current training iteration during this epoch (reset to 0 at the end of every epoch) + losses (OrderedDict) -- training losses stored in the format of (name, float) pairs + t_comp (float) -- computational time per data point (normalized by batch_size) + t_data (float) -- data loading time per data point (normalized by batch_size) + """ + message = '(epoch: %d, iters: %d, time: %.3f, data: %.3f) ' % (epoch, iters, t_comp, t_data) + for k, v in losses.items(): + message += '%s: %.3f ' % (k, v) + + print(message) # print the message + with open(self.log_name, "a") as log_file: + log_file.write('%s\n' % message) # save the message diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..84ad1bc7c0e30bb6f704dba271b5a53c2fef8c55 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/__init__.py @@ -0,0 +1,49 @@ +import cv2 +import numpy as np +import torch + +from einops import rearrange +from .api import MiDaSInference +from modules import devices + +model = None + +def unload_midas_model(): + global model + if model is not None: + model = model.cpu() + +def apply_midas(input_image, a=np.pi * 2.0, bg_th=0.1): + global model + if model is None: + model = MiDaSInference(model_type="dpt_hybrid") + if devices.get_device_for("controlnet").type != 'mps': + model = model.to(devices.get_device_for("controlnet")) + + assert input_image.ndim == 3 + image_depth = input_image + with torch.no_grad(): + image_depth = torch.from_numpy(image_depth).float() + if devices.get_device_for("controlnet").type != 'mps': + image_depth = image_depth.to(devices.get_device_for("controlnet")) + image_depth = image_depth / 127.5 - 1.0 + image_depth = rearrange(image_depth, 'h w c -> 1 c h w') + depth = model(image_depth)[0] + + depth_pt = depth.clone() + depth_pt -= torch.min(depth_pt) + depth_pt /= torch.max(depth_pt) + depth_pt = depth_pt.cpu().numpy() + depth_image = (depth_pt * 255.0).clip(0, 255).astype(np.uint8) + + depth_np = depth.cpu().numpy() + x = cv2.Sobel(depth_np, cv2.CV_32F, 1, 0, ksize=3) + y = cv2.Sobel(depth_np, cv2.CV_32F, 0, 1, ksize=3) + z = np.ones_like(x) * a + x[depth_pt < bg_th] = 0 + y[depth_pt < bg_th] = 0 + normal = np.stack([x, y, z], axis=2) + normal /= np.sum(normal ** 2.0, axis=2, keepdims=True) ** 0.5 + normal_image = (normal * 127.5 + 127.5).clip(0, 255).astype(np.uint8) + + return depth_image, normal_image diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/api.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/api.py new file mode 100644 index 0000000000000000000000000000000000000000..aafb3d02c24f45a3cb0fe9490c885f1d63baa405 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/api.py @@ -0,0 +1,181 @@ +# based on https://github.com/isl-org/MiDaS + +import cv2 +import torch +import torch.nn as nn +import os +from modules.paths import models_path + +from torchvision.transforms import Compose + +from .midas.dpt_depth import DPTDepthModel +from .midas.midas_net import MidasNet +from .midas.midas_net_custom import MidasNet_small +from .midas.transforms import Resize, NormalizeImage, PrepareForNet + +base_model_path = os.path.join(models_path, "midas") +old_modeldir = os.path.dirname(os.path.realpath(__file__)) +remote_model_path = "https://huggingface.co/lllyasviel/ControlNet/resolve/main/annotator/ckpts/dpt_hybrid-midas-501f0c75.pt" + +ISL_PATHS = { + "dpt_large": os.path.join(base_model_path, "dpt_large-midas-2f21e586.pt"), + "dpt_hybrid": os.path.join(base_model_path, "dpt_hybrid-midas-501f0c75.pt"), + "midas_v21": "", + "midas_v21_small": "", +} + +OLD_ISL_PATHS = { + "dpt_large": os.path.join(old_modeldir, "dpt_large-midas-2f21e586.pt"), + "dpt_hybrid": os.path.join(old_modeldir, "dpt_hybrid-midas-501f0c75.pt"), + "midas_v21": "", + "midas_v21_small": "", +} + + +def disabled_train(self, mode=True): + """Overwrite model.train with this function to make sure train/eval mode + does not change anymore.""" + return self + + +def load_midas_transform(model_type): + # https://github.com/isl-org/MiDaS/blob/master/run.py + # load transform only + if model_type == "dpt_large": # DPT-Large + net_w, net_h = 384, 384 + resize_mode = "minimal" + normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + + elif model_type == "dpt_hybrid": # DPT-Hybrid + net_w, net_h = 384, 384 + resize_mode = "minimal" + normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + + elif model_type == "midas_v21": + net_w, net_h = 384, 384 + resize_mode = "upper_bound" + normalization = NormalizeImage(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) + + elif model_type == "midas_v21_small": + net_w, net_h = 256, 256 + resize_mode = "upper_bound" + normalization = NormalizeImage(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) + + else: + assert False, f"model_type '{model_type}' not implemented, use: --model_type large" + + transform = Compose( + [ + Resize( + net_w, + net_h, + resize_target=None, + keep_aspect_ratio=True, + ensure_multiple_of=32, + resize_method=resize_mode, + image_interpolation_method=cv2.INTER_CUBIC, + ), + normalization, + PrepareForNet(), + ] + ) + + return transform + + +def load_model(model_type): + # https://github.com/isl-org/MiDaS/blob/master/run.py + # load network + model_path = ISL_PATHS[model_type] + old_model_path = OLD_ISL_PATHS[model_type] + if model_type == "dpt_large": # DPT-Large + model = DPTDepthModel( + path=model_path, + backbone="vitl16_384", + non_negative=True, + ) + net_w, net_h = 384, 384 + resize_mode = "minimal" + normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + + elif model_type == "dpt_hybrid": # DPT-Hybrid + if os.path.exists(old_model_path): + model_path = old_model_path + elif not os.path.exists(model_path): + from basicsr.utils.download_util import load_file_from_url + load_file_from_url(remote_model_path, model_dir=base_model_path) + + model = DPTDepthModel( + path=model_path, + backbone="vitb_rn50_384", + non_negative=True, + ) + net_w, net_h = 384, 384 + resize_mode = "minimal" + normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + + elif model_type == "midas_v21": + model = MidasNet(model_path, non_negative=True) + net_w, net_h = 384, 384 + resize_mode = "upper_bound" + normalization = NormalizeImage( + mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] + ) + + elif model_type == "midas_v21_small": + model = MidasNet_small(model_path, features=64, backbone="efficientnet_lite3", exportable=True, + non_negative=True, blocks={'expand': True}) + net_w, net_h = 256, 256 + resize_mode = "upper_bound" + normalization = NormalizeImage( + mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] + ) + + else: + print(f"model_type '{model_type}' not implemented, use: --model_type large") + assert False + + transform = Compose( + [ + Resize( + net_w, + net_h, + resize_target=None, + keep_aspect_ratio=True, + ensure_multiple_of=32, + resize_method=resize_mode, + image_interpolation_method=cv2.INTER_CUBIC, + ), + normalization, + PrepareForNet(), + ] + ) + + return model.eval(), transform + + +class MiDaSInference(nn.Module): + MODEL_TYPES_TORCH_HUB = [ + "DPT_Large", + "DPT_Hybrid", + "MiDaS_small" + ] + MODEL_TYPES_ISL = [ + "dpt_large", + "dpt_hybrid", + "midas_v21", + "midas_v21_small", + ] + + def __init__(self, model_type): + super().__init__() + assert (model_type in self.MODEL_TYPES_ISL) + model, _ = load_model(model_type) + self.model = model + self.model.train = disabled_train + + def forward(self, x): + with torch.no_grad(): + prediction = self.model(x) + return prediction + diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/base_model.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/base_model.py new file mode 100644 index 0000000000000000000000000000000000000000..5cf430239b47ec5ec07531263f26f5c24a2311cd --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/base_model.py @@ -0,0 +1,16 @@ +import torch + + +class BaseModel(torch.nn.Module): + def load(self, path): + """Load model from file. + + Args: + path (str): file path + """ + parameters = torch.load(path, map_location=torch.device('cpu')) + + if "optimizer" in parameters: + parameters = parameters["model"] + + self.load_state_dict(parameters) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/blocks.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/blocks.py new file mode 100644 index 0000000000000000000000000000000000000000..2145d18fa98060a618536d9a64fe6589e9be4f78 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/blocks.py @@ -0,0 +1,342 @@ +import torch +import torch.nn as nn + +from .vit import ( + _make_pretrained_vitb_rn50_384, + _make_pretrained_vitl16_384, + _make_pretrained_vitb16_384, + forward_vit, +) + +def _make_encoder(backbone, features, use_pretrained, groups=1, expand=False, exportable=True, hooks=None, use_vit_only=False, use_readout="ignore",): + if backbone == "vitl16_384": + pretrained = _make_pretrained_vitl16_384( + use_pretrained, hooks=hooks, use_readout=use_readout + ) + scratch = _make_scratch( + [256, 512, 1024, 1024], features, groups=groups, expand=expand + ) # ViT-L/16 - 85.0% Top1 (backbone) + elif backbone == "vitb_rn50_384": + pretrained = _make_pretrained_vitb_rn50_384( + use_pretrained, + hooks=hooks, + use_vit_only=use_vit_only, + use_readout=use_readout, + ) + scratch = _make_scratch( + [256, 512, 768, 768], features, groups=groups, expand=expand + ) # ViT-H/16 - 85.0% Top1 (backbone) + elif backbone == "vitb16_384": + pretrained = _make_pretrained_vitb16_384( + use_pretrained, hooks=hooks, use_readout=use_readout + ) + scratch = _make_scratch( + [96, 192, 384, 768], features, groups=groups, expand=expand + ) # ViT-B/16 - 84.6% Top1 (backbone) + elif backbone == "resnext101_wsl": + pretrained = _make_pretrained_resnext101_wsl(use_pretrained) + scratch = _make_scratch([256, 512, 1024, 2048], features, groups=groups, expand=expand) # efficientnet_lite3 + elif backbone == "efficientnet_lite3": + pretrained = _make_pretrained_efficientnet_lite3(use_pretrained, exportable=exportable) + scratch = _make_scratch([32, 48, 136, 384], features, groups=groups, expand=expand) # efficientnet_lite3 + else: + print(f"Backbone '{backbone}' not implemented") + assert False + + return pretrained, scratch + + +def _make_scratch(in_shape, out_shape, groups=1, expand=False): + scratch = nn.Module() + + out_shape1 = out_shape + out_shape2 = out_shape + out_shape3 = out_shape + out_shape4 = out_shape + if expand==True: + out_shape1 = out_shape + out_shape2 = out_shape*2 + out_shape3 = out_shape*4 + out_shape4 = out_shape*8 + + scratch.layer1_rn = nn.Conv2d( + in_shape[0], out_shape1, kernel_size=3, stride=1, padding=1, bias=False, groups=groups + ) + scratch.layer2_rn = nn.Conv2d( + in_shape[1], out_shape2, kernel_size=3, stride=1, padding=1, bias=False, groups=groups + ) + scratch.layer3_rn = nn.Conv2d( + in_shape[2], out_shape3, kernel_size=3, stride=1, padding=1, bias=False, groups=groups + ) + scratch.layer4_rn = nn.Conv2d( + in_shape[3], out_shape4, kernel_size=3, stride=1, padding=1, bias=False, groups=groups + ) + + return scratch + + +def _make_pretrained_efficientnet_lite3(use_pretrained, exportable=False): + efficientnet = torch.hub.load( + "rwightman/gen-efficientnet-pytorch", + "tf_efficientnet_lite3", + pretrained=use_pretrained, + exportable=exportable + ) + return _make_efficientnet_backbone(efficientnet) + + +def _make_efficientnet_backbone(effnet): + pretrained = nn.Module() + + pretrained.layer1 = nn.Sequential( + effnet.conv_stem, effnet.bn1, effnet.act1, *effnet.blocks[0:2] + ) + pretrained.layer2 = nn.Sequential(*effnet.blocks[2:3]) + pretrained.layer3 = nn.Sequential(*effnet.blocks[3:5]) + pretrained.layer4 = nn.Sequential(*effnet.blocks[5:9]) + + return pretrained + + +def _make_resnet_backbone(resnet): + pretrained = nn.Module() + pretrained.layer1 = nn.Sequential( + resnet.conv1, resnet.bn1, resnet.relu, resnet.maxpool, resnet.layer1 + ) + + pretrained.layer2 = resnet.layer2 + pretrained.layer3 = resnet.layer3 + pretrained.layer4 = resnet.layer4 + + return pretrained + + +def _make_pretrained_resnext101_wsl(use_pretrained): + resnet = torch.hub.load("facebookresearch/WSL-Images", "resnext101_32x8d_wsl") + return _make_resnet_backbone(resnet) + + + +class Interpolate(nn.Module): + """Interpolation module. + """ + + def __init__(self, scale_factor, mode, align_corners=False): + """Init. + + Args: + scale_factor (float): scaling + mode (str): interpolation mode + """ + super(Interpolate, self).__init__() + + self.interp = nn.functional.interpolate + self.scale_factor = scale_factor + self.mode = mode + self.align_corners = align_corners + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input + + Returns: + tensor: interpolated data + """ + + x = self.interp( + x, scale_factor=self.scale_factor, mode=self.mode, align_corners=self.align_corners + ) + + return x + + +class ResidualConvUnit(nn.Module): + """Residual convolution module. + """ + + def __init__(self, features): + """Init. + + Args: + features (int): number of features + """ + super().__init__() + + self.conv1 = nn.Conv2d( + features, features, kernel_size=3, stride=1, padding=1, bias=True + ) + + self.conv2 = nn.Conv2d( + features, features, kernel_size=3, stride=1, padding=1, bias=True + ) + + self.relu = nn.ReLU(inplace=True) + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input + + Returns: + tensor: output + """ + out = self.relu(x) + out = self.conv1(out) + out = self.relu(out) + out = self.conv2(out) + + return out + x + + +class FeatureFusionBlock(nn.Module): + """Feature fusion block. + """ + + def __init__(self, features): + """Init. + + Args: + features (int): number of features + """ + super(FeatureFusionBlock, self).__init__() + + self.resConfUnit1 = ResidualConvUnit(features) + self.resConfUnit2 = ResidualConvUnit(features) + + def forward(self, *xs): + """Forward pass. + + Returns: + tensor: output + """ + output = xs[0] + + if len(xs) == 2: + output += self.resConfUnit1(xs[1]) + + output = self.resConfUnit2(output) + + output = nn.functional.interpolate( + output, scale_factor=2, mode="bilinear", align_corners=True + ) + + return output + + + + +class ResidualConvUnit_custom(nn.Module): + """Residual convolution module. + """ + + def __init__(self, features, activation, bn): + """Init. + + Args: + features (int): number of features + """ + super().__init__() + + self.bn = bn + + self.groups=1 + + self.conv1 = nn.Conv2d( + features, features, kernel_size=3, stride=1, padding=1, bias=True, groups=self.groups + ) + + self.conv2 = nn.Conv2d( + features, features, kernel_size=3, stride=1, padding=1, bias=True, groups=self.groups + ) + + if self.bn==True: + self.bn1 = nn.BatchNorm2d(features) + self.bn2 = nn.BatchNorm2d(features) + + self.activation = activation + + self.skip_add = nn.quantized.FloatFunctional() + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input + + Returns: + tensor: output + """ + + out = self.activation(x) + out = self.conv1(out) + if self.bn==True: + out = self.bn1(out) + + out = self.activation(out) + out = self.conv2(out) + if self.bn==True: + out = self.bn2(out) + + if self.groups > 1: + out = self.conv_merge(out) + + return self.skip_add.add(out, x) + + # return out + x + + +class FeatureFusionBlock_custom(nn.Module): + """Feature fusion block. + """ + + def __init__(self, features, activation, deconv=False, bn=False, expand=False, align_corners=True): + """Init. + + Args: + features (int): number of features + """ + super(FeatureFusionBlock_custom, self).__init__() + + self.deconv = deconv + self.align_corners = align_corners + + self.groups=1 + + self.expand = expand + out_features = features + if self.expand==True: + out_features = features//2 + + self.out_conv = nn.Conv2d(features, out_features, kernel_size=1, stride=1, padding=0, bias=True, groups=1) + + self.resConfUnit1 = ResidualConvUnit_custom(features, activation, bn) + self.resConfUnit2 = ResidualConvUnit_custom(features, activation, bn) + + self.skip_add = nn.quantized.FloatFunctional() + + def forward(self, *xs): + """Forward pass. + + Returns: + tensor: output + """ + output = xs[0] + + if len(xs) == 2: + res = self.resConfUnit1(xs[1]) + output = self.skip_add.add(output, res) + # output += res + + output = self.resConfUnit2(output) + + output = nn.functional.interpolate( + output, scale_factor=2, mode="bilinear", align_corners=self.align_corners + ) + + output = self.out_conv(output) + + return output + diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/dpt_depth.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/dpt_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..4e9aab5d2767dffea39da5b3f30e2798688216f1 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/dpt_depth.py @@ -0,0 +1,109 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +from .base_model import BaseModel +from .blocks import ( + FeatureFusionBlock, + FeatureFusionBlock_custom, + Interpolate, + _make_encoder, + forward_vit, +) + + +def _make_fusion_block(features, use_bn): + return FeatureFusionBlock_custom( + features, + nn.ReLU(False), + deconv=False, + bn=use_bn, + expand=False, + align_corners=True, + ) + + +class DPT(BaseModel): + def __init__( + self, + head, + features=256, + backbone="vitb_rn50_384", + readout="project", + channels_last=False, + use_bn=False, + ): + + super(DPT, self).__init__() + + self.channels_last = channels_last + + hooks = { + "vitb_rn50_384": [0, 1, 8, 11], + "vitb16_384": [2, 5, 8, 11], + "vitl16_384": [5, 11, 17, 23], + } + + # Instantiate backbone and reassemble blocks + self.pretrained, self.scratch = _make_encoder( + backbone, + features, + False, # Set to true of you want to train from scratch, uses ImageNet weights + groups=1, + expand=False, + exportable=False, + hooks=hooks[backbone], + use_readout=readout, + ) + + self.scratch.refinenet1 = _make_fusion_block(features, use_bn) + self.scratch.refinenet2 = _make_fusion_block(features, use_bn) + self.scratch.refinenet3 = _make_fusion_block(features, use_bn) + self.scratch.refinenet4 = _make_fusion_block(features, use_bn) + + self.scratch.output_conv = head + + + def forward(self, x): + if self.channels_last == True: + x.contiguous(memory_format=torch.channels_last) + + layer_1, layer_2, layer_3, layer_4 = forward_vit(self.pretrained, x) + + layer_1_rn = self.scratch.layer1_rn(layer_1) + layer_2_rn = self.scratch.layer2_rn(layer_2) + layer_3_rn = self.scratch.layer3_rn(layer_3) + layer_4_rn = self.scratch.layer4_rn(layer_4) + + path_4 = self.scratch.refinenet4(layer_4_rn) + path_3 = self.scratch.refinenet3(path_4, layer_3_rn) + path_2 = self.scratch.refinenet2(path_3, layer_2_rn) + path_1 = self.scratch.refinenet1(path_2, layer_1_rn) + + out = self.scratch.output_conv(path_1) + + return out + + +class DPTDepthModel(DPT): + def __init__(self, path=None, non_negative=True, **kwargs): + features = kwargs["features"] if "features" in kwargs else 256 + + head = nn.Sequential( + nn.Conv2d(features, features // 2, kernel_size=3, stride=1, padding=1), + Interpolate(scale_factor=2, mode="bilinear", align_corners=True), + nn.Conv2d(features // 2, 32, kernel_size=3, stride=1, padding=1), + nn.ReLU(True), + nn.Conv2d(32, 1, kernel_size=1, stride=1, padding=0), + nn.ReLU(True) if non_negative else nn.Identity(), + nn.Identity(), + ) + + super().__init__(head, **kwargs) + + if path is not None: + self.load(path) + + def forward(self, x): + return super().forward(x).squeeze(dim=1) + diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/midas_net.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/midas_net.py new file mode 100644 index 0000000000000000000000000000000000000000..8a954977800b0a0f48807e80fa63041910e33c1f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/midas_net.py @@ -0,0 +1,76 @@ +"""MidashNet: Network for monocular depth estimation trained by mixing several datasets. +This file contains code that is adapted from +https://github.com/thomasjpfan/pytorch_refinenet/blob/master/pytorch_refinenet/refinenet/refinenet_4cascade.py +""" +import torch +import torch.nn as nn + +from .base_model import BaseModel +from .blocks import FeatureFusionBlock, Interpolate, _make_encoder + + +class MidasNet(BaseModel): + """Network for monocular depth estimation. + """ + + def __init__(self, path=None, features=256, non_negative=True): + """Init. + + Args: + path (str, optional): Path to saved model. Defaults to None. + features (int, optional): Number of features. Defaults to 256. + backbone (str, optional): Backbone network for encoder. Defaults to resnet50 + """ + print("Loading weights: ", path) + + super(MidasNet, self).__init__() + + use_pretrained = False if path is None else True + + self.pretrained, self.scratch = _make_encoder(backbone="resnext101_wsl", features=features, use_pretrained=use_pretrained) + + self.scratch.refinenet4 = FeatureFusionBlock(features) + self.scratch.refinenet3 = FeatureFusionBlock(features) + self.scratch.refinenet2 = FeatureFusionBlock(features) + self.scratch.refinenet1 = FeatureFusionBlock(features) + + self.scratch.output_conv = nn.Sequential( + nn.Conv2d(features, 128, kernel_size=3, stride=1, padding=1), + Interpolate(scale_factor=2, mode="bilinear"), + nn.Conv2d(128, 32, kernel_size=3, stride=1, padding=1), + nn.ReLU(True), + nn.Conv2d(32, 1, kernel_size=1, stride=1, padding=0), + nn.ReLU(True) if non_negative else nn.Identity(), + ) + + if path: + self.load(path) + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input data (image) + + Returns: + tensor: depth + """ + + layer_1 = self.pretrained.layer1(x) + layer_2 = self.pretrained.layer2(layer_1) + layer_3 = self.pretrained.layer3(layer_2) + layer_4 = self.pretrained.layer4(layer_3) + + layer_1_rn = self.scratch.layer1_rn(layer_1) + layer_2_rn = self.scratch.layer2_rn(layer_2) + layer_3_rn = self.scratch.layer3_rn(layer_3) + layer_4_rn = self.scratch.layer4_rn(layer_4) + + path_4 = self.scratch.refinenet4(layer_4_rn) + path_3 = self.scratch.refinenet3(path_4, layer_3_rn) + path_2 = self.scratch.refinenet2(path_3, layer_2_rn) + path_1 = self.scratch.refinenet1(path_2, layer_1_rn) + + out = self.scratch.output_conv(path_1) + + return torch.squeeze(out, dim=1) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/midas_net_custom.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/midas_net_custom.py new file mode 100644 index 0000000000000000000000000000000000000000..50e4acb5e53d5fabefe3dde16ab49c33c2b7797c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/midas_net_custom.py @@ -0,0 +1,128 @@ +"""MidashNet: Network for monocular depth estimation trained by mixing several datasets. +This file contains code that is adapted from +https://github.com/thomasjpfan/pytorch_refinenet/blob/master/pytorch_refinenet/refinenet/refinenet_4cascade.py +""" +import torch +import torch.nn as nn + +from .base_model import BaseModel +from .blocks import FeatureFusionBlock, FeatureFusionBlock_custom, Interpolate, _make_encoder + + +class MidasNet_small(BaseModel): + """Network for monocular depth estimation. + """ + + def __init__(self, path=None, features=64, backbone="efficientnet_lite3", non_negative=True, exportable=True, channels_last=False, align_corners=True, + blocks={'expand': True}): + """Init. + + Args: + path (str, optional): Path to saved model. Defaults to None. + features (int, optional): Number of features. Defaults to 256. + backbone (str, optional): Backbone network for encoder. Defaults to resnet50 + """ + print("Loading weights: ", path) + + super(MidasNet_small, self).__init__() + + use_pretrained = False if path else True + + self.channels_last = channels_last + self.blocks = blocks + self.backbone = backbone + + self.groups = 1 + + features1=features + features2=features + features3=features + features4=features + self.expand = False + if "expand" in self.blocks and self.blocks['expand'] == True: + self.expand = True + features1=features + features2=features*2 + features3=features*4 + features4=features*8 + + self.pretrained, self.scratch = _make_encoder(self.backbone, features, use_pretrained, groups=self.groups, expand=self.expand, exportable=exportable) + + self.scratch.activation = nn.ReLU(False) + + self.scratch.refinenet4 = FeatureFusionBlock_custom(features4, self.scratch.activation, deconv=False, bn=False, expand=self.expand, align_corners=align_corners) + self.scratch.refinenet3 = FeatureFusionBlock_custom(features3, self.scratch.activation, deconv=False, bn=False, expand=self.expand, align_corners=align_corners) + self.scratch.refinenet2 = FeatureFusionBlock_custom(features2, self.scratch.activation, deconv=False, bn=False, expand=self.expand, align_corners=align_corners) + self.scratch.refinenet1 = FeatureFusionBlock_custom(features1, self.scratch.activation, deconv=False, bn=False, align_corners=align_corners) + + + self.scratch.output_conv = nn.Sequential( + nn.Conv2d(features, features//2, kernel_size=3, stride=1, padding=1, groups=self.groups), + Interpolate(scale_factor=2, mode="bilinear"), + nn.Conv2d(features//2, 32, kernel_size=3, stride=1, padding=1), + self.scratch.activation, + nn.Conv2d(32, 1, kernel_size=1, stride=1, padding=0), + nn.ReLU(True) if non_negative else nn.Identity(), + nn.Identity(), + ) + + if path: + self.load(path) + + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input data (image) + + Returns: + tensor: depth + """ + if self.channels_last==True: + print("self.channels_last = ", self.channels_last) + x.contiguous(memory_format=torch.channels_last) + + + layer_1 = self.pretrained.layer1(x) + layer_2 = self.pretrained.layer2(layer_1) + layer_3 = self.pretrained.layer3(layer_2) + layer_4 = self.pretrained.layer4(layer_3) + + layer_1_rn = self.scratch.layer1_rn(layer_1) + layer_2_rn = self.scratch.layer2_rn(layer_2) + layer_3_rn = self.scratch.layer3_rn(layer_3) + layer_4_rn = self.scratch.layer4_rn(layer_4) + + + path_4 = self.scratch.refinenet4(layer_4_rn) + path_3 = self.scratch.refinenet3(path_4, layer_3_rn) + path_2 = self.scratch.refinenet2(path_3, layer_2_rn) + path_1 = self.scratch.refinenet1(path_2, layer_1_rn) + + out = self.scratch.output_conv(path_1) + + return torch.squeeze(out, dim=1) + + + +def fuse_model(m): + prev_previous_type = nn.Identity() + prev_previous_name = '' + previous_type = nn.Identity() + previous_name = '' + for name, module in m.named_modules(): + if prev_previous_type == nn.Conv2d and previous_type == nn.BatchNorm2d and type(module) == nn.ReLU: + # print("FUSED ", prev_previous_name, previous_name, name) + torch.quantization.fuse_modules(m, [prev_previous_name, previous_name, name], inplace=True) + elif prev_previous_type == nn.Conv2d and previous_type == nn.BatchNorm2d: + # print("FUSED ", prev_previous_name, previous_name) + torch.quantization.fuse_modules(m, [prev_previous_name, previous_name], inplace=True) + # elif previous_type == nn.Conv2d and type(module) == nn.ReLU: + # print("FUSED ", previous_name, name) + # torch.quantization.fuse_modules(m, [previous_name, name], inplace=True) + + prev_previous_type = previous_type + prev_previous_name = previous_name + previous_type = type(module) + previous_name = name \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/transforms.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/transforms.py new file mode 100644 index 0000000000000000000000000000000000000000..350cbc11662633ad7f8968eb10be2e7de6e384e9 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/transforms.py @@ -0,0 +1,234 @@ +import numpy as np +import cv2 +import math + + +def apply_min_size(sample, size, image_interpolation_method=cv2.INTER_AREA): + """Rezise the sample to ensure the given size. Keeps aspect ratio. + + Args: + sample (dict): sample + size (tuple): image size + + Returns: + tuple: new size + """ + shape = list(sample["disparity"].shape) + + if shape[0] >= size[0] and shape[1] >= size[1]: + return sample + + scale = [0, 0] + scale[0] = size[0] / shape[0] + scale[1] = size[1] / shape[1] + + scale = max(scale) + + shape[0] = math.ceil(scale * shape[0]) + shape[1] = math.ceil(scale * shape[1]) + + # resize + sample["image"] = cv2.resize( + sample["image"], tuple(shape[::-1]), interpolation=image_interpolation_method + ) + + sample["disparity"] = cv2.resize( + sample["disparity"], tuple(shape[::-1]), interpolation=cv2.INTER_NEAREST + ) + sample["mask"] = cv2.resize( + sample["mask"].astype(np.float32), + tuple(shape[::-1]), + interpolation=cv2.INTER_NEAREST, + ) + sample["mask"] = sample["mask"].astype(bool) + + return tuple(shape) + + +class Resize(object): + """Resize sample to given size (width, height). + """ + + def __init__( + self, + width, + height, + resize_target=True, + keep_aspect_ratio=False, + ensure_multiple_of=1, + resize_method="lower_bound", + image_interpolation_method=cv2.INTER_AREA, + ): + """Init. + + Args: + width (int): desired output width + height (int): desired output height + resize_target (bool, optional): + True: Resize the full sample (image, mask, target). + False: Resize image only. + Defaults to True. + keep_aspect_ratio (bool, optional): + True: Keep the aspect ratio of the input sample. + Output sample might not have the given width and height, and + resize behaviour depends on the parameter 'resize_method'. + Defaults to False. + ensure_multiple_of (int, optional): + Output width and height is constrained to be multiple of this parameter. + Defaults to 1. + resize_method (str, optional): + "lower_bound": Output will be at least as large as the given size. + "upper_bound": Output will be at max as large as the given size. (Output size might be smaller than given size.) + "minimal": Scale as least as possible. (Output size might be smaller than given size.) + Defaults to "lower_bound". + """ + self.__width = width + self.__height = height + + self.__resize_target = resize_target + self.__keep_aspect_ratio = keep_aspect_ratio + self.__multiple_of = ensure_multiple_of + self.__resize_method = resize_method + self.__image_interpolation_method = image_interpolation_method + + def constrain_to_multiple_of(self, x, min_val=0, max_val=None): + y = (np.round(x / self.__multiple_of) * self.__multiple_of).astype(int) + + if max_val is not None and y > max_val: + y = (np.floor(x / self.__multiple_of) * self.__multiple_of).astype(int) + + if y < min_val: + y = (np.ceil(x / self.__multiple_of) * self.__multiple_of).astype(int) + + return y + + def get_size(self, width, height): + # determine new height and width + scale_height = self.__height / height + scale_width = self.__width / width + + if self.__keep_aspect_ratio: + if self.__resize_method == "lower_bound": + # scale such that output size is lower bound + if scale_width > scale_height: + # fit width + scale_height = scale_width + else: + # fit height + scale_width = scale_height + elif self.__resize_method == "upper_bound": + # scale such that output size is upper bound + if scale_width < scale_height: + # fit width + scale_height = scale_width + else: + # fit height + scale_width = scale_height + elif self.__resize_method == "minimal": + # scale as least as possbile + if abs(1 - scale_width) < abs(1 - scale_height): + # fit width + scale_height = scale_width + else: + # fit height + scale_width = scale_height + else: + raise ValueError( + f"resize_method {self.__resize_method} not implemented" + ) + + if self.__resize_method == "lower_bound": + new_height = self.constrain_to_multiple_of( + scale_height * height, min_val=self.__height + ) + new_width = self.constrain_to_multiple_of( + scale_width * width, min_val=self.__width + ) + elif self.__resize_method == "upper_bound": + new_height = self.constrain_to_multiple_of( + scale_height * height, max_val=self.__height + ) + new_width = self.constrain_to_multiple_of( + scale_width * width, max_val=self.__width + ) + elif self.__resize_method == "minimal": + new_height = self.constrain_to_multiple_of(scale_height * height) + new_width = self.constrain_to_multiple_of(scale_width * width) + else: + raise ValueError(f"resize_method {self.__resize_method} not implemented") + + return (new_width, new_height) + + def __call__(self, sample): + width, height = self.get_size( + sample["image"].shape[1], sample["image"].shape[0] + ) + + # resize sample + sample["image"] = cv2.resize( + sample["image"], + (width, height), + interpolation=self.__image_interpolation_method, + ) + + if self.__resize_target: + if "disparity" in sample: + sample["disparity"] = cv2.resize( + sample["disparity"], + (width, height), + interpolation=cv2.INTER_NEAREST, + ) + + if "depth" in sample: + sample["depth"] = cv2.resize( + sample["depth"], (width, height), interpolation=cv2.INTER_NEAREST + ) + + sample["mask"] = cv2.resize( + sample["mask"].astype(np.float32), + (width, height), + interpolation=cv2.INTER_NEAREST, + ) + sample["mask"] = sample["mask"].astype(bool) + + return sample + + +class NormalizeImage(object): + """Normlize image by given mean and std. + """ + + def __init__(self, mean, std): + self.__mean = mean + self.__std = std + + def __call__(self, sample): + sample["image"] = (sample["image"] - self.__mean) / self.__std + + return sample + + +class PrepareForNet(object): + """Prepare sample for usage as network input. + """ + + def __init__(self): + pass + + def __call__(self, sample): + image = np.transpose(sample["image"], (2, 0, 1)) + sample["image"] = np.ascontiguousarray(image).astype(np.float32) + + if "mask" in sample: + sample["mask"] = sample["mask"].astype(np.float32) + sample["mask"] = np.ascontiguousarray(sample["mask"]) + + if "disparity" in sample: + disparity = sample["disparity"].astype(np.float32) + sample["disparity"] = np.ascontiguousarray(disparity) + + if "depth" in sample: + depth = sample["depth"].astype(np.float32) + sample["depth"] = np.ascontiguousarray(depth) + + return sample diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/vit.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..ea46b1be88b261b0dec04f3da0256f5f66f88a74 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/midas/vit.py @@ -0,0 +1,491 @@ +import torch +import torch.nn as nn +import timm +import types +import math +import torch.nn.functional as F + + +class Slice(nn.Module): + def __init__(self, start_index=1): + super(Slice, self).__init__() + self.start_index = start_index + + def forward(self, x): + return x[:, self.start_index :] + + +class AddReadout(nn.Module): + def __init__(self, start_index=1): + super(AddReadout, self).__init__() + self.start_index = start_index + + def forward(self, x): + if self.start_index == 2: + readout = (x[:, 0] + x[:, 1]) / 2 + else: + readout = x[:, 0] + return x[:, self.start_index :] + readout.unsqueeze(1) + + +class ProjectReadout(nn.Module): + def __init__(self, in_features, start_index=1): + super(ProjectReadout, self).__init__() + self.start_index = start_index + + self.project = nn.Sequential(nn.Linear(2 * in_features, in_features), nn.GELU()) + + def forward(self, x): + readout = x[:, 0].unsqueeze(1).expand_as(x[:, self.start_index :]) + features = torch.cat((x[:, self.start_index :], readout), -1) + + return self.project(features) + + +class Transpose(nn.Module): + def __init__(self, dim0, dim1): + super(Transpose, self).__init__() + self.dim0 = dim0 + self.dim1 = dim1 + + def forward(self, x): + x = x.transpose(self.dim0, self.dim1) + return x + + +def forward_vit(pretrained, x): + b, c, h, w = x.shape + + glob = pretrained.model.forward_flex(x) + + layer_1 = pretrained.activations["1"] + layer_2 = pretrained.activations["2"] + layer_3 = pretrained.activations["3"] + layer_4 = pretrained.activations["4"] + + layer_1 = pretrained.act_postprocess1[0:2](layer_1) + layer_2 = pretrained.act_postprocess2[0:2](layer_2) + layer_3 = pretrained.act_postprocess3[0:2](layer_3) + layer_4 = pretrained.act_postprocess4[0:2](layer_4) + + unflatten = nn.Sequential( + nn.Unflatten( + 2, + torch.Size( + [ + h // pretrained.model.patch_size[1], + w // pretrained.model.patch_size[0], + ] + ), + ) + ) + + if layer_1.ndim == 3: + layer_1 = unflatten(layer_1) + if layer_2.ndim == 3: + layer_2 = unflatten(layer_2) + if layer_3.ndim == 3: + layer_3 = unflatten(layer_3) + if layer_4.ndim == 3: + layer_4 = unflatten(layer_4) + + layer_1 = pretrained.act_postprocess1[3 : len(pretrained.act_postprocess1)](layer_1) + layer_2 = pretrained.act_postprocess2[3 : len(pretrained.act_postprocess2)](layer_2) + layer_3 = pretrained.act_postprocess3[3 : len(pretrained.act_postprocess3)](layer_3) + layer_4 = pretrained.act_postprocess4[3 : len(pretrained.act_postprocess4)](layer_4) + + return layer_1, layer_2, layer_3, layer_4 + + +def _resize_pos_embed(self, posemb, gs_h, gs_w): + posemb_tok, posemb_grid = ( + posemb[:, : self.start_index], + posemb[0, self.start_index :], + ) + + gs_old = int(math.sqrt(len(posemb_grid))) + + posemb_grid = posemb_grid.reshape(1, gs_old, gs_old, -1).permute(0, 3, 1, 2) + posemb_grid = F.interpolate(posemb_grid, size=(gs_h, gs_w), mode="bilinear") + posemb_grid = posemb_grid.permute(0, 2, 3, 1).reshape(1, gs_h * gs_w, -1) + + posemb = torch.cat([posemb_tok, posemb_grid], dim=1) + + return posemb + + +def forward_flex(self, x): + b, c, h, w = x.shape + + pos_embed = self._resize_pos_embed( + self.pos_embed, h // self.patch_size[1], w // self.patch_size[0] + ) + + B = x.shape[0] + + if hasattr(self.patch_embed, "backbone"): + x = self.patch_embed.backbone(x) + if isinstance(x, (list, tuple)): + x = x[-1] # last feature if backbone outputs list/tuple of features + + x = self.patch_embed.proj(x).flatten(2).transpose(1, 2) + + if getattr(self, "dist_token", None) is not None: + cls_tokens = self.cls_token.expand( + B, -1, -1 + ) # stole cls_tokens impl from Phil Wang, thanks + dist_token = self.dist_token.expand(B, -1, -1) + x = torch.cat((cls_tokens, dist_token, x), dim=1) + else: + cls_tokens = self.cls_token.expand( + B, -1, -1 + ) # stole cls_tokens impl from Phil Wang, thanks + x = torch.cat((cls_tokens, x), dim=1) + + x = x + pos_embed + x = self.pos_drop(x) + + for blk in self.blocks: + x = blk(x) + + x = self.norm(x) + + return x + + +activations = {} + + +def get_activation(name): + def hook(model, input, output): + activations[name] = output + + return hook + + +def get_readout_oper(vit_features, features, use_readout, start_index=1): + if use_readout == "ignore": + readout_oper = [Slice(start_index)] * len(features) + elif use_readout == "add": + readout_oper = [AddReadout(start_index)] * len(features) + elif use_readout == "project": + readout_oper = [ + ProjectReadout(vit_features, start_index) for out_feat in features + ] + else: + assert ( + False + ), "wrong operation for readout token, use_readout can be 'ignore', 'add', or 'project'" + + return readout_oper + + +def _make_vit_b16_backbone( + model, + features=[96, 192, 384, 768], + size=[384, 384], + hooks=[2, 5, 8, 11], + vit_features=768, + use_readout="ignore", + start_index=1, +): + pretrained = nn.Module() + + pretrained.model = model + pretrained.model.blocks[hooks[0]].register_forward_hook(get_activation("1")) + pretrained.model.blocks[hooks[1]].register_forward_hook(get_activation("2")) + pretrained.model.blocks[hooks[2]].register_forward_hook(get_activation("3")) + pretrained.model.blocks[hooks[3]].register_forward_hook(get_activation("4")) + + pretrained.activations = activations + + readout_oper = get_readout_oper(vit_features, features, use_readout, start_index) + + # 32, 48, 136, 384 + pretrained.act_postprocess1 = nn.Sequential( + readout_oper[0], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[0], + kernel_size=1, + stride=1, + padding=0, + ), + nn.ConvTranspose2d( + in_channels=features[0], + out_channels=features[0], + kernel_size=4, + stride=4, + padding=0, + bias=True, + dilation=1, + groups=1, + ), + ) + + pretrained.act_postprocess2 = nn.Sequential( + readout_oper[1], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[1], + kernel_size=1, + stride=1, + padding=0, + ), + nn.ConvTranspose2d( + in_channels=features[1], + out_channels=features[1], + kernel_size=2, + stride=2, + padding=0, + bias=True, + dilation=1, + groups=1, + ), + ) + + pretrained.act_postprocess3 = nn.Sequential( + readout_oper[2], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[2], + kernel_size=1, + stride=1, + padding=0, + ), + ) + + pretrained.act_postprocess4 = nn.Sequential( + readout_oper[3], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[3], + kernel_size=1, + stride=1, + padding=0, + ), + nn.Conv2d( + in_channels=features[3], + out_channels=features[3], + kernel_size=3, + stride=2, + padding=1, + ), + ) + + pretrained.model.start_index = start_index + pretrained.model.patch_size = [16, 16] + + # We inject this function into the VisionTransformer instances so that + # we can use it with interpolated position embeddings without modifying the library source. + pretrained.model.forward_flex = types.MethodType(forward_flex, pretrained.model) + pretrained.model._resize_pos_embed = types.MethodType( + _resize_pos_embed, pretrained.model + ) + + return pretrained + + +def _make_pretrained_vitl16_384(pretrained, use_readout="ignore", hooks=None): + model = timm.create_model("vit_large_patch16_384", pretrained=pretrained) + + hooks = [5, 11, 17, 23] if hooks == None else hooks + return _make_vit_b16_backbone( + model, + features=[256, 512, 1024, 1024], + hooks=hooks, + vit_features=1024, + use_readout=use_readout, + ) + + +def _make_pretrained_vitb16_384(pretrained, use_readout="ignore", hooks=None): + model = timm.create_model("vit_base_patch16_384", pretrained=pretrained) + + hooks = [2, 5, 8, 11] if hooks == None else hooks + return _make_vit_b16_backbone( + model, features=[96, 192, 384, 768], hooks=hooks, use_readout=use_readout + ) + + +def _make_pretrained_deitb16_384(pretrained, use_readout="ignore", hooks=None): + model = timm.create_model("vit_deit_base_patch16_384", pretrained=pretrained) + + hooks = [2, 5, 8, 11] if hooks == None else hooks + return _make_vit_b16_backbone( + model, features=[96, 192, 384, 768], hooks=hooks, use_readout=use_readout + ) + + +def _make_pretrained_deitb16_distil_384(pretrained, use_readout="ignore", hooks=None): + model = timm.create_model( + "vit_deit_base_distilled_patch16_384", pretrained=pretrained + ) + + hooks = [2, 5, 8, 11] if hooks == None else hooks + return _make_vit_b16_backbone( + model, + features=[96, 192, 384, 768], + hooks=hooks, + use_readout=use_readout, + start_index=2, + ) + + +def _make_vit_b_rn50_backbone( + model, + features=[256, 512, 768, 768], + size=[384, 384], + hooks=[0, 1, 8, 11], + vit_features=768, + use_vit_only=False, + use_readout="ignore", + start_index=1, +): + pretrained = nn.Module() + + pretrained.model = model + + if use_vit_only == True: + pretrained.model.blocks[hooks[0]].register_forward_hook(get_activation("1")) + pretrained.model.blocks[hooks[1]].register_forward_hook(get_activation("2")) + else: + pretrained.model.patch_embed.backbone.stages[0].register_forward_hook( + get_activation("1") + ) + pretrained.model.patch_embed.backbone.stages[1].register_forward_hook( + get_activation("2") + ) + + pretrained.model.blocks[hooks[2]].register_forward_hook(get_activation("3")) + pretrained.model.blocks[hooks[3]].register_forward_hook(get_activation("4")) + + pretrained.activations = activations + + readout_oper = get_readout_oper(vit_features, features, use_readout, start_index) + + if use_vit_only == True: + pretrained.act_postprocess1 = nn.Sequential( + readout_oper[0], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[0], + kernel_size=1, + stride=1, + padding=0, + ), + nn.ConvTranspose2d( + in_channels=features[0], + out_channels=features[0], + kernel_size=4, + stride=4, + padding=0, + bias=True, + dilation=1, + groups=1, + ), + ) + + pretrained.act_postprocess2 = nn.Sequential( + readout_oper[1], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[1], + kernel_size=1, + stride=1, + padding=0, + ), + nn.ConvTranspose2d( + in_channels=features[1], + out_channels=features[1], + kernel_size=2, + stride=2, + padding=0, + bias=True, + dilation=1, + groups=1, + ), + ) + else: + pretrained.act_postprocess1 = nn.Sequential( + nn.Identity(), nn.Identity(), nn.Identity() + ) + pretrained.act_postprocess2 = nn.Sequential( + nn.Identity(), nn.Identity(), nn.Identity() + ) + + pretrained.act_postprocess3 = nn.Sequential( + readout_oper[2], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[2], + kernel_size=1, + stride=1, + padding=0, + ), + ) + + pretrained.act_postprocess4 = nn.Sequential( + readout_oper[3], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[3], + kernel_size=1, + stride=1, + padding=0, + ), + nn.Conv2d( + in_channels=features[3], + out_channels=features[3], + kernel_size=3, + stride=2, + padding=1, + ), + ) + + pretrained.model.start_index = start_index + pretrained.model.patch_size = [16, 16] + + # We inject this function into the VisionTransformer instances so that + # we can use it with interpolated position embeddings without modifying the library source. + pretrained.model.forward_flex = types.MethodType(forward_flex, pretrained.model) + + # We inject this function into the VisionTransformer instances so that + # we can use it with interpolated position embeddings without modifying the library source. + pretrained.model._resize_pos_embed = types.MethodType( + _resize_pos_embed, pretrained.model + ) + + return pretrained + + +def _make_pretrained_vitb_rn50_384( + pretrained, use_readout="ignore", hooks=None, use_vit_only=False +): + model = timm.create_model("vit_base_resnet50_384", pretrained=pretrained) + + hooks = [0, 1, 8, 11] if hooks == None else hooks + return _make_vit_b_rn50_backbone( + model, + features=[256, 512, 768, 768], + size=[384, 384], + hooks=hooks, + use_vit_only=use_vit_only, + use_readout=use_readout, + ) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/utils.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..9a9d3b5b66370fa98da9e067ba53ead848ea9a59 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/midas/utils.py @@ -0,0 +1,189 @@ +"""Utils for monoDepth.""" +import sys +import re +import numpy as np +import cv2 +import torch + + +def read_pfm(path): + """Read pfm file. + + Args: + path (str): path to file + + Returns: + tuple: (data, scale) + """ + with open(path, "rb") as file: + + color = None + width = None + height = None + scale = None + endian = None + + header = file.readline().rstrip() + if header.decode("ascii") == "PF": + color = True + elif header.decode("ascii") == "Pf": + color = False + else: + raise Exception("Not a PFM file: " + path) + + dim_match = re.match(r"^(\d+)\s(\d+)\s$", file.readline().decode("ascii")) + if dim_match: + width, height = list(map(int, dim_match.groups())) + else: + raise Exception("Malformed PFM header.") + + scale = float(file.readline().decode("ascii").rstrip()) + if scale < 0: + # little-endian + endian = "<" + scale = -scale + else: + # big-endian + endian = ">" + + data = np.fromfile(file, endian + "f") + shape = (height, width, 3) if color else (height, width) + + data = np.reshape(data, shape) + data = np.flipud(data) + + return data, scale + + +def write_pfm(path, image, scale=1): + """Write pfm file. + + Args: + path (str): pathto file + image (array): data + scale (int, optional): Scale. Defaults to 1. + """ + + with open(path, "wb") as file: + color = None + + if image.dtype.name != "float32": + raise Exception("Image dtype must be float32.") + + image = np.flipud(image) + + if len(image.shape) == 3 and image.shape[2] == 3: # color image + color = True + elif ( + len(image.shape) == 2 or len(image.shape) == 3 and image.shape[2] == 1 + ): # greyscale + color = False + else: + raise Exception("Image must have H x W x 3, H x W x 1 or H x W dimensions.") + + file.write("PF\n" if color else "Pf\n".encode()) + file.write("%d %d\n".encode() % (image.shape[1], image.shape[0])) + + endian = image.dtype.byteorder + + if endian == "<" or endian == "=" and sys.byteorder == "little": + scale = -scale + + file.write("%f\n".encode() % scale) + + image.tofile(file) + + +def read_image(path): + """Read image and output RGB image (0-1). + + Args: + path (str): path to file + + Returns: + array: RGB image (0-1) + """ + img = cv2.imread(path) + + if img.ndim == 2: + img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) + + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) / 255.0 + + return img + + +def resize_image(img): + """Resize image and make it fit for network. + + Args: + img (array): image + + Returns: + tensor: data ready for network + """ + height_orig = img.shape[0] + width_orig = img.shape[1] + + if width_orig > height_orig: + scale = width_orig / 384 + else: + scale = height_orig / 384 + + height = (np.ceil(height_orig / scale / 32) * 32).astype(int) + width = (np.ceil(width_orig / scale / 32) * 32).astype(int) + + img_resized = cv2.resize(img, (width, height), interpolation=cv2.INTER_AREA) + + img_resized = ( + torch.from_numpy(np.transpose(img_resized, (2, 0, 1))).contiguous().float() + ) + img_resized = img_resized.unsqueeze(0) + + return img_resized + + +def resize_depth(depth, width, height): + """Resize depth map and bring to CPU (numpy). + + Args: + depth (tensor): depth + width (int): image width + height (int): image height + + Returns: + array: processed depth + """ + depth = torch.squeeze(depth[0, :, :, :]).to("cpu") + + depth_resized = cv2.resize( + depth.numpy(), (width, height), interpolation=cv2.INTER_CUBIC + ) + + return depth_resized + +def write_depth(path, depth, bits=1): + """Write depth map to pfm and png file. + + Args: + path (str): filepath without extension + depth (array): depth + """ + write_pfm(path + ".pfm", depth.astype(np.float32)) + + depth_min = depth.min() + depth_max = depth.max() + + max_val = (2**(8*bits))-1 + + if depth_max - depth_min > np.finfo("float").eps: + out = max_val * (depth - depth_min) / (depth_max - depth_min) + else: + out = np.zeros(depth.shape, dtype=depth.type) + + if bits == 1: + cv2.imwrite(path + ".png", out.astype("uint8")) + elif bits == 2: + cv2.imwrite(path + ".png", out.astype("uint16")) + + return diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mlsd/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mlsd/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a0260131759fcf7ded674304f5b800ee5726c63b --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mlsd/__init__.py @@ -0,0 +1,49 @@ +import cv2 +import numpy as np +import torch +import os + +from einops import rearrange +from .models.mbv2_mlsd_tiny import MobileV2_MLSD_Tiny +from .models.mbv2_mlsd_large import MobileV2_MLSD_Large +from .utils import pred_lines +from modules import devices +from modules.paths import models_path + +mlsdmodel = None +remote_model_path = "https://huggingface.co/lllyasviel/ControlNet/resolve/main/annotator/ckpts/mlsd_large_512_fp32.pth" +old_modeldir = os.path.dirname(os.path.realpath(__file__)) +modeldir = os.path.join(models_path, "mlsd") + +def unload_mlsd_model(): + global mlsdmodel + if mlsdmodel is not None: + mlsdmodel = mlsdmodel.cpu() + +def apply_mlsd(input_image, thr_v, thr_d): + global modelpath, mlsdmodel + if mlsdmodel is None: + modelpath = os.path.join(modeldir, "mlsd_large_512_fp32.pth") + old_modelpath = os.path.join(old_modeldir, "mlsd_large_512_fp32.pth") + if os.path.exists(old_modelpath): + modelpath = old_modelpath + elif not os.path.exists(modelpath): + from basicsr.utils.download_util import load_file_from_url + load_file_from_url(remote_model_path, model_dir=modeldir) + mlsdmodel = MobileV2_MLSD_Large() + mlsdmodel.load_state_dict(torch.load(modelpath), strict=True) + mlsdmodel = mlsdmodel.to(devices.get_device_for("controlnet")).eval() + + model = mlsdmodel + assert input_image.ndim == 3 + img = input_image + img_output = np.zeros_like(img) + try: + with torch.no_grad(): + lines = pred_lines(img, model, [img.shape[0], img.shape[1]], thr_v, thr_d) + for line in lines: + x_start, y_start, x_end, y_end = [int(val) for val in line] + cv2.line(img_output, (x_start, y_start), (x_end, y_end), [255, 255, 255], 1) + except Exception as e: + pass + return img_output[:, :, 0] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mlsd/models/mbv2_mlsd_large.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mlsd/models/mbv2_mlsd_large.py new file mode 100644 index 0000000000000000000000000000000000000000..5b9799e7573ca41549b3c3b13ac47b906b369603 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mlsd/models/mbv2_mlsd_large.py @@ -0,0 +1,292 @@ +import os +import sys +import torch +import torch.nn as nn +import torch.utils.model_zoo as model_zoo +from torch.nn import functional as F + + +class BlockTypeA(nn.Module): + def __init__(self, in_c1, in_c2, out_c1, out_c2, upscale = True): + super(BlockTypeA, self).__init__() + self.conv1 = nn.Sequential( + nn.Conv2d(in_c2, out_c2, kernel_size=1), + nn.BatchNorm2d(out_c2), + nn.ReLU(inplace=True) + ) + self.conv2 = nn.Sequential( + nn.Conv2d(in_c1, out_c1, kernel_size=1), + nn.BatchNorm2d(out_c1), + nn.ReLU(inplace=True) + ) + self.upscale = upscale + + def forward(self, a, b): + b = self.conv1(b) + a = self.conv2(a) + if self.upscale: + b = F.interpolate(b, scale_factor=2.0, mode='bilinear', align_corners=True) + return torch.cat((a, b), dim=1) + + +class BlockTypeB(nn.Module): + def __init__(self, in_c, out_c): + super(BlockTypeB, self).__init__() + self.conv1 = nn.Sequential( + nn.Conv2d(in_c, in_c, kernel_size=3, padding=1), + nn.BatchNorm2d(in_c), + nn.ReLU() + ) + self.conv2 = nn.Sequential( + nn.Conv2d(in_c, out_c, kernel_size=3, padding=1), + nn.BatchNorm2d(out_c), + nn.ReLU() + ) + + def forward(self, x): + x = self.conv1(x) + x + x = self.conv2(x) + return x + +class BlockTypeC(nn.Module): + def __init__(self, in_c, out_c): + super(BlockTypeC, self).__init__() + self.conv1 = nn.Sequential( + nn.Conv2d(in_c, in_c, kernel_size=3, padding=5, dilation=5), + nn.BatchNorm2d(in_c), + nn.ReLU() + ) + self.conv2 = nn.Sequential( + nn.Conv2d(in_c, in_c, kernel_size=3, padding=1), + nn.BatchNorm2d(in_c), + nn.ReLU() + ) + self.conv3 = nn.Conv2d(in_c, out_c, kernel_size=1) + + def forward(self, x): + x = self.conv1(x) + x = self.conv2(x) + x = self.conv3(x) + return x + +def _make_divisible(v, divisor, min_value=None): + """ + This function is taken from the original tf repo. + It ensures that all layers have a channel number that is divisible by 8 + It can be seen here: + https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py + :param v: + :param divisor: + :param min_value: + :return: + """ + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + # Make sure that round down does not go down by more than 10%. + if new_v < 0.9 * v: + new_v += divisor + return new_v + + +class ConvBNReLU(nn.Sequential): + def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1): + self.channel_pad = out_planes - in_planes + self.stride = stride + #padding = (kernel_size - 1) // 2 + + # TFLite uses slightly different padding than PyTorch + if stride == 2: + padding = 0 + else: + padding = (kernel_size - 1) // 2 + + super(ConvBNReLU, self).__init__( + nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False), + nn.BatchNorm2d(out_planes), + nn.ReLU6(inplace=True) + ) + self.max_pool = nn.MaxPool2d(kernel_size=stride, stride=stride) + + + def forward(self, x): + # TFLite uses different padding + if self.stride == 2: + x = F.pad(x, (0, 1, 0, 1), "constant", 0) + #print(x.shape) + + for module in self: + if not isinstance(module, nn.MaxPool2d): + x = module(x) + return x + + +class InvertedResidual(nn.Module): + def __init__(self, inp, oup, stride, expand_ratio): + super(InvertedResidual, self).__init__() + self.stride = stride + assert stride in [1, 2] + + hidden_dim = int(round(inp * expand_ratio)) + self.use_res_connect = self.stride == 1 and inp == oup + + layers = [] + if expand_ratio != 1: + # pw + layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1)) + layers.extend([ + # dw + ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim), + # pw-linear + nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), + nn.BatchNorm2d(oup), + ]) + self.conv = nn.Sequential(*layers) + + def forward(self, x): + if self.use_res_connect: + return x + self.conv(x) + else: + return self.conv(x) + + +class MobileNetV2(nn.Module): + def __init__(self, pretrained=True): + """ + MobileNet V2 main class + Args: + num_classes (int): Number of classes + width_mult (float): Width multiplier - adjusts number of channels in each layer by this amount + inverted_residual_setting: Network structure + round_nearest (int): Round the number of channels in each layer to be a multiple of this number + Set to 1 to turn off rounding + block: Module specifying inverted residual building block for mobilenet + """ + super(MobileNetV2, self).__init__() + + block = InvertedResidual + input_channel = 32 + last_channel = 1280 + width_mult = 1.0 + round_nearest = 8 + + inverted_residual_setting = [ + # t, c, n, s + [1, 16, 1, 1], + [6, 24, 2, 2], + [6, 32, 3, 2], + [6, 64, 4, 2], + [6, 96, 3, 1], + #[6, 160, 3, 2], + #[6, 320, 1, 1], + ] + + # only check the first element, assuming user knows t,c,n,s are required + if len(inverted_residual_setting) == 0 or len(inverted_residual_setting[0]) != 4: + raise ValueError("inverted_residual_setting should be non-empty " + "or a 4-element list, got {}".format(inverted_residual_setting)) + + # building first layer + input_channel = _make_divisible(input_channel * width_mult, round_nearest) + self.last_channel = _make_divisible(last_channel * max(1.0, width_mult), round_nearest) + features = [ConvBNReLU(4, input_channel, stride=2)] + # building inverted residual blocks + for t, c, n, s in inverted_residual_setting: + output_channel = _make_divisible(c * width_mult, round_nearest) + for i in range(n): + stride = s if i == 0 else 1 + features.append(block(input_channel, output_channel, stride, expand_ratio=t)) + input_channel = output_channel + + self.features = nn.Sequential(*features) + self.fpn_selected = [1, 3, 6, 10, 13] + # weight initialization + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode='fan_out') + if m.bias is not None: + nn.init.zeros_(m.bias) + elif isinstance(m, nn.BatchNorm2d): + nn.init.ones_(m.weight) + nn.init.zeros_(m.bias) + elif isinstance(m, nn.Linear): + nn.init.normal_(m.weight, 0, 0.01) + nn.init.zeros_(m.bias) + if pretrained: + self._load_pretrained_model() + + def _forward_impl(self, x): + # This exists since TorchScript doesn't support inheritance, so the superclass method + # (this one) needs to have a name other than `forward` that can be accessed in a subclass + fpn_features = [] + for i, f in enumerate(self.features): + if i > self.fpn_selected[-1]: + break + x = f(x) + if i in self.fpn_selected: + fpn_features.append(x) + + c1, c2, c3, c4, c5 = fpn_features + return c1, c2, c3, c4, c5 + + + def forward(self, x): + return self._forward_impl(x) + + def _load_pretrained_model(self): + pretrain_dict = model_zoo.load_url('https://download.pytorch.org/models/mobilenet_v2-b0353104.pth') + model_dict = {} + state_dict = self.state_dict() + for k, v in pretrain_dict.items(): + if k in state_dict: + model_dict[k] = v + state_dict.update(model_dict) + self.load_state_dict(state_dict) + + +class MobileV2_MLSD_Large(nn.Module): + def __init__(self): + super(MobileV2_MLSD_Large, self).__init__() + + self.backbone = MobileNetV2(pretrained=False) + ## A, B + self.block15 = BlockTypeA(in_c1= 64, in_c2= 96, + out_c1= 64, out_c2=64, + upscale=False) + self.block16 = BlockTypeB(128, 64) + + ## A, B + self.block17 = BlockTypeA(in_c1 = 32, in_c2 = 64, + out_c1= 64, out_c2= 64) + self.block18 = BlockTypeB(128, 64) + + ## A, B + self.block19 = BlockTypeA(in_c1=24, in_c2=64, + out_c1=64, out_c2=64) + self.block20 = BlockTypeB(128, 64) + + ## A, B, C + self.block21 = BlockTypeA(in_c1=16, in_c2=64, + out_c1=64, out_c2=64) + self.block22 = BlockTypeB(128, 64) + + self.block23 = BlockTypeC(64, 16) + + def forward(self, x): + c1, c2, c3, c4, c5 = self.backbone(x) + + x = self.block15(c4, c5) + x = self.block16(x) + + x = self.block17(c3, x) + x = self.block18(x) + + x = self.block19(c2, x) + x = self.block20(x) + + x = self.block21(c1, x) + x = self.block22(x) + x = self.block23(x) + x = x[:, 7:, :, :] + + return x \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mlsd/models/mbv2_mlsd_tiny.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mlsd/models/mbv2_mlsd_tiny.py new file mode 100644 index 0000000000000000000000000000000000000000..e3ed633f2cc23ea1829a627fdb879ab39f641f83 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mlsd/models/mbv2_mlsd_tiny.py @@ -0,0 +1,275 @@ +import os +import sys +import torch +import torch.nn as nn +import torch.utils.model_zoo as model_zoo +from torch.nn import functional as F + + +class BlockTypeA(nn.Module): + def __init__(self, in_c1, in_c2, out_c1, out_c2, upscale = True): + super(BlockTypeA, self).__init__() + self.conv1 = nn.Sequential( + nn.Conv2d(in_c2, out_c2, kernel_size=1), + nn.BatchNorm2d(out_c2), + nn.ReLU(inplace=True) + ) + self.conv2 = nn.Sequential( + nn.Conv2d(in_c1, out_c1, kernel_size=1), + nn.BatchNorm2d(out_c1), + nn.ReLU(inplace=True) + ) + self.upscale = upscale + + def forward(self, a, b): + b = self.conv1(b) + a = self.conv2(a) + b = F.interpolate(b, scale_factor=2.0, mode='bilinear', align_corners=True) + return torch.cat((a, b), dim=1) + + +class BlockTypeB(nn.Module): + def __init__(self, in_c, out_c): + super(BlockTypeB, self).__init__() + self.conv1 = nn.Sequential( + nn.Conv2d(in_c, in_c, kernel_size=3, padding=1), + nn.BatchNorm2d(in_c), + nn.ReLU() + ) + self.conv2 = nn.Sequential( + nn.Conv2d(in_c, out_c, kernel_size=3, padding=1), + nn.BatchNorm2d(out_c), + nn.ReLU() + ) + + def forward(self, x): + x = self.conv1(x) + x + x = self.conv2(x) + return x + +class BlockTypeC(nn.Module): + def __init__(self, in_c, out_c): + super(BlockTypeC, self).__init__() + self.conv1 = nn.Sequential( + nn.Conv2d(in_c, in_c, kernel_size=3, padding=5, dilation=5), + nn.BatchNorm2d(in_c), + nn.ReLU() + ) + self.conv2 = nn.Sequential( + nn.Conv2d(in_c, in_c, kernel_size=3, padding=1), + nn.BatchNorm2d(in_c), + nn.ReLU() + ) + self.conv3 = nn.Conv2d(in_c, out_c, kernel_size=1) + + def forward(self, x): + x = self.conv1(x) + x = self.conv2(x) + x = self.conv3(x) + return x + +def _make_divisible(v, divisor, min_value=None): + """ + This function is taken from the original tf repo. + It ensures that all layers have a channel number that is divisible by 8 + It can be seen here: + https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py + :param v: + :param divisor: + :param min_value: + :return: + """ + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + # Make sure that round down does not go down by more than 10%. + if new_v < 0.9 * v: + new_v += divisor + return new_v + + +class ConvBNReLU(nn.Sequential): + def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1): + self.channel_pad = out_planes - in_planes + self.stride = stride + #padding = (kernel_size - 1) // 2 + + # TFLite uses slightly different padding than PyTorch + if stride == 2: + padding = 0 + else: + padding = (kernel_size - 1) // 2 + + super(ConvBNReLU, self).__init__( + nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False), + nn.BatchNorm2d(out_planes), + nn.ReLU6(inplace=True) + ) + self.max_pool = nn.MaxPool2d(kernel_size=stride, stride=stride) + + + def forward(self, x): + # TFLite uses different padding + if self.stride == 2: + x = F.pad(x, (0, 1, 0, 1), "constant", 0) + #print(x.shape) + + for module in self: + if not isinstance(module, nn.MaxPool2d): + x = module(x) + return x + + +class InvertedResidual(nn.Module): + def __init__(self, inp, oup, stride, expand_ratio): + super(InvertedResidual, self).__init__() + self.stride = stride + assert stride in [1, 2] + + hidden_dim = int(round(inp * expand_ratio)) + self.use_res_connect = self.stride == 1 and inp == oup + + layers = [] + if expand_ratio != 1: + # pw + layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1)) + layers.extend([ + # dw + ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim), + # pw-linear + nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), + nn.BatchNorm2d(oup), + ]) + self.conv = nn.Sequential(*layers) + + def forward(self, x): + if self.use_res_connect: + return x + self.conv(x) + else: + return self.conv(x) + + +class MobileNetV2(nn.Module): + def __init__(self, pretrained=True): + """ + MobileNet V2 main class + Args: + num_classes (int): Number of classes + width_mult (float): Width multiplier - adjusts number of channels in each layer by this amount + inverted_residual_setting: Network structure + round_nearest (int): Round the number of channels in each layer to be a multiple of this number + Set to 1 to turn off rounding + block: Module specifying inverted residual building block for mobilenet + """ + super(MobileNetV2, self).__init__() + + block = InvertedResidual + input_channel = 32 + last_channel = 1280 + width_mult = 1.0 + round_nearest = 8 + + inverted_residual_setting = [ + # t, c, n, s + [1, 16, 1, 1], + [6, 24, 2, 2], + [6, 32, 3, 2], + [6, 64, 4, 2], + #[6, 96, 3, 1], + #[6, 160, 3, 2], + #[6, 320, 1, 1], + ] + + # only check the first element, assuming user knows t,c,n,s are required + if len(inverted_residual_setting) == 0 or len(inverted_residual_setting[0]) != 4: + raise ValueError("inverted_residual_setting should be non-empty " + "or a 4-element list, got {}".format(inverted_residual_setting)) + + # building first layer + input_channel = _make_divisible(input_channel * width_mult, round_nearest) + self.last_channel = _make_divisible(last_channel * max(1.0, width_mult), round_nearest) + features = [ConvBNReLU(4, input_channel, stride=2)] + # building inverted residual blocks + for t, c, n, s in inverted_residual_setting: + output_channel = _make_divisible(c * width_mult, round_nearest) + for i in range(n): + stride = s if i == 0 else 1 + features.append(block(input_channel, output_channel, stride, expand_ratio=t)) + input_channel = output_channel + self.features = nn.Sequential(*features) + + self.fpn_selected = [3, 6, 10] + # weight initialization + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode='fan_out') + if m.bias is not None: + nn.init.zeros_(m.bias) + elif isinstance(m, nn.BatchNorm2d): + nn.init.ones_(m.weight) + nn.init.zeros_(m.bias) + elif isinstance(m, nn.Linear): + nn.init.normal_(m.weight, 0, 0.01) + nn.init.zeros_(m.bias) + + #if pretrained: + # self._load_pretrained_model() + + def _forward_impl(self, x): + # This exists since TorchScript doesn't support inheritance, so the superclass method + # (this one) needs to have a name other than `forward` that can be accessed in a subclass + fpn_features = [] + for i, f in enumerate(self.features): + if i > self.fpn_selected[-1]: + break + x = f(x) + if i in self.fpn_selected: + fpn_features.append(x) + + c2, c3, c4 = fpn_features + return c2, c3, c4 + + + def forward(self, x): + return self._forward_impl(x) + + def _load_pretrained_model(self): + pretrain_dict = model_zoo.load_url('https://download.pytorch.org/models/mobilenet_v2-b0353104.pth') + model_dict = {} + state_dict = self.state_dict() + for k, v in pretrain_dict.items(): + if k in state_dict: + model_dict[k] = v + state_dict.update(model_dict) + self.load_state_dict(state_dict) + + +class MobileV2_MLSD_Tiny(nn.Module): + def __init__(self): + super(MobileV2_MLSD_Tiny, self).__init__() + + self.backbone = MobileNetV2(pretrained=True) + + self.block12 = BlockTypeA(in_c1= 32, in_c2= 64, + out_c1= 64, out_c2=64) + self.block13 = BlockTypeB(128, 64) + + self.block14 = BlockTypeA(in_c1 = 24, in_c2 = 64, + out_c1= 32, out_c2= 32) + self.block15 = BlockTypeB(64, 64) + + self.block16 = BlockTypeC(64, 16) + + def forward(self, x): + c2, c3, c4 = self.backbone(x) + + x = self.block12(c3, c4) + x = self.block13(x) + x = self.block14(c2, x) + x = self.block15(x) + x = self.block16(x) + x = x[:, 7:, :, :] + #print(x.shape) + x = F.interpolate(x, scale_factor=2.0, mode='bilinear', align_corners=True) + + return x \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mlsd/utils.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mlsd/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..a9cc5d904d9dd34d2ba4c902f3993f7abbb7ac5e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mlsd/utils.py @@ -0,0 +1,581 @@ +''' +modified by lihaoweicv +pytorch version +''' + +''' +M-LSD +Copyright 2021-present NAVER Corp. +Apache License v2.0 +''' + +import os +import numpy as np +import cv2 +import torch +from torch.nn import functional as F +from modules import devices + + +def deccode_output_score_and_ptss(tpMap, topk_n = 200, ksize = 5): + ''' + tpMap: + center: tpMap[1, 0, :, :] + displacement: tpMap[1, 1:5, :, :] + ''' + b, c, h, w = tpMap.shape + assert b==1, 'only support bsize==1' + displacement = tpMap[:, 1:5, :, :][0] + center = tpMap[:, 0, :, :] + heat = torch.sigmoid(center) + hmax = F.max_pool2d( heat, (ksize, ksize), stride=1, padding=(ksize-1)//2) + keep = (hmax == heat).float() + heat = heat * keep + heat = heat.reshape(-1, ) + + scores, indices = torch.topk(heat, topk_n, dim=-1, largest=True) + yy = torch.floor_divide(indices, w).unsqueeze(-1) + xx = torch.fmod(indices, w).unsqueeze(-1) + ptss = torch.cat((yy, xx),dim=-1) + + ptss = ptss.detach().cpu().numpy() + scores = scores.detach().cpu().numpy() + displacement = displacement.detach().cpu().numpy() + displacement = displacement.transpose((1,2,0)) + return ptss, scores, displacement + + +def pred_lines(image, model, + input_shape=[512, 512], + score_thr=0.10, + dist_thr=20.0): + h, w, _ = image.shape + h_ratio, w_ratio = [h / input_shape[0], w / input_shape[1]] + + resized_image = np.concatenate([cv2.resize(image, (input_shape[1], input_shape[0]), interpolation=cv2.INTER_AREA), + np.ones([input_shape[0], input_shape[1], 1])], axis=-1) + + resized_image = resized_image.transpose((2,0,1)) + batch_image = np.expand_dims(resized_image, axis=0).astype('float32') + batch_image = (batch_image / 127.5) - 1.0 + + batch_image = torch.from_numpy(batch_image).float().to(devices.get_device_for("controlnet")) + outputs = model(batch_image) + pts, pts_score, vmap = deccode_output_score_and_ptss(outputs, 200, 3) + start = vmap[:, :, :2] + end = vmap[:, :, 2:] + dist_map = np.sqrt(np.sum((start - end) ** 2, axis=-1)) + + segments_list = [] + for center, score in zip(pts, pts_score): + y, x = center + distance = dist_map[y, x] + if score > score_thr and distance > dist_thr: + disp_x_start, disp_y_start, disp_x_end, disp_y_end = vmap[y, x, :] + x_start = x + disp_x_start + y_start = y + disp_y_start + x_end = x + disp_x_end + y_end = y + disp_y_end + segments_list.append([x_start, y_start, x_end, y_end]) + + lines = 2 * np.array(segments_list) # 256 > 512 + lines[:, 0] = lines[:, 0] * w_ratio + lines[:, 1] = lines[:, 1] * h_ratio + lines[:, 2] = lines[:, 2] * w_ratio + lines[:, 3] = lines[:, 3] * h_ratio + + return lines + + +def pred_squares(image, + model, + input_shape=[512, 512], + params={'score': 0.06, + 'outside_ratio': 0.28, + 'inside_ratio': 0.45, + 'w_overlap': 0.0, + 'w_degree': 1.95, + 'w_length': 0.0, + 'w_area': 1.86, + 'w_center': 0.14}): + ''' + shape = [height, width] + ''' + h, w, _ = image.shape + original_shape = [h, w] + + resized_image = np.concatenate([cv2.resize(image, (input_shape[0], input_shape[1]), interpolation=cv2.INTER_AREA), + np.ones([input_shape[0], input_shape[1], 1])], axis=-1) + resized_image = resized_image.transpose((2, 0, 1)) + batch_image = np.expand_dims(resized_image, axis=0).astype('float32') + batch_image = (batch_image / 127.5) - 1.0 + + batch_image = torch.from_numpy(batch_image).float().to(devices.get_device_for("controlnet")) + outputs = model(batch_image) + + pts, pts_score, vmap = deccode_output_score_and_ptss(outputs, 200, 3) + start = vmap[:, :, :2] # (x, y) + end = vmap[:, :, 2:] # (x, y) + dist_map = np.sqrt(np.sum((start - end) ** 2, axis=-1)) + + junc_list = [] + segments_list = [] + for junc, score in zip(pts, pts_score): + y, x = junc + distance = dist_map[y, x] + if score > params['score'] and distance > 20.0: + junc_list.append([x, y]) + disp_x_start, disp_y_start, disp_x_end, disp_y_end = vmap[y, x, :] + d_arrow = 1.0 + x_start = x + d_arrow * disp_x_start + y_start = y + d_arrow * disp_y_start + x_end = x + d_arrow * disp_x_end + y_end = y + d_arrow * disp_y_end + segments_list.append([x_start, y_start, x_end, y_end]) + + segments = np.array(segments_list) + + ####### post processing for squares + # 1. get unique lines + point = np.array([[0, 0]]) + point = point[0] + start = segments[:, :2] + end = segments[:, 2:] + diff = start - end + a = diff[:, 1] + b = -diff[:, 0] + c = a * start[:, 0] + b * start[:, 1] + + d = np.abs(a * point[0] + b * point[1] - c) / np.sqrt(a ** 2 + b ** 2 + 1e-10) + theta = np.arctan2(diff[:, 0], diff[:, 1]) * 180 / np.pi + theta[theta < 0.0] += 180 + hough = np.concatenate([d[:, None], theta[:, None]], axis=-1) + + d_quant = 1 + theta_quant = 2 + hough[:, 0] //= d_quant + hough[:, 1] //= theta_quant + _, indices, counts = np.unique(hough, axis=0, return_index=True, return_counts=True) + + acc_map = np.zeros([512 // d_quant + 1, 360 // theta_quant + 1], dtype='float32') + idx_map = np.zeros([512 // d_quant + 1, 360 // theta_quant + 1], dtype='int32') - 1 + yx_indices = hough[indices, :].astype('int32') + acc_map[yx_indices[:, 0], yx_indices[:, 1]] = counts + idx_map[yx_indices[:, 0], yx_indices[:, 1]] = indices + + acc_map_np = acc_map + # acc_map = acc_map[None, :, :, None] + # + # ### fast suppression using tensorflow op + # acc_map = tf.constant(acc_map, dtype=tf.float32) + # max_acc_map = tf.keras.layers.MaxPool2D(pool_size=(5, 5), strides=1, padding='same')(acc_map) + # acc_map = acc_map * tf.cast(tf.math.equal(acc_map, max_acc_map), tf.float32) + # flatten_acc_map = tf.reshape(acc_map, [1, -1]) + # topk_values, topk_indices = tf.math.top_k(flatten_acc_map, k=len(pts)) + # _, h, w, _ = acc_map.shape + # y = tf.expand_dims(topk_indices // w, axis=-1) + # x = tf.expand_dims(topk_indices % w, axis=-1) + # yx = tf.concat([y, x], axis=-1) + + ### fast suppression using pytorch op + acc_map = torch.from_numpy(acc_map_np).unsqueeze(0).unsqueeze(0) + _,_, h, w = acc_map.shape + max_acc_map = F.max_pool2d(acc_map,kernel_size=5, stride=1, padding=2) + acc_map = acc_map * ( (acc_map == max_acc_map).float() ) + flatten_acc_map = acc_map.reshape([-1, ]) + + scores, indices = torch.topk(flatten_acc_map, len(pts), dim=-1, largest=True) + yy = torch.div(indices, w, rounding_mode='floor').unsqueeze(-1) + xx = torch.fmod(indices, w).unsqueeze(-1) + yx = torch.cat((yy, xx), dim=-1) + + yx = yx.detach().cpu().numpy() + + topk_values = scores.detach().cpu().numpy() + indices = idx_map[yx[:, 0], yx[:, 1]] + basis = 5 // 2 + + merged_segments = [] + for yx_pt, max_indice, value in zip(yx, indices, topk_values): + y, x = yx_pt + if max_indice == -1 or value == 0: + continue + segment_list = [] + for y_offset in range(-basis, basis + 1): + for x_offset in range(-basis, basis + 1): + indice = idx_map[y + y_offset, x + x_offset] + cnt = int(acc_map_np[y + y_offset, x + x_offset]) + if indice != -1: + segment_list.append(segments[indice]) + if cnt > 1: + check_cnt = 1 + current_hough = hough[indice] + for new_indice, new_hough in enumerate(hough): + if (current_hough == new_hough).all() and indice != new_indice: + segment_list.append(segments[new_indice]) + check_cnt += 1 + if check_cnt == cnt: + break + group_segments = np.array(segment_list).reshape([-1, 2]) + sorted_group_segments = np.sort(group_segments, axis=0) + x_min, y_min = sorted_group_segments[0, :] + x_max, y_max = sorted_group_segments[-1, :] + + deg = theta[max_indice] + if deg >= 90: + merged_segments.append([x_min, y_max, x_max, y_min]) + else: + merged_segments.append([x_min, y_min, x_max, y_max]) + + # 2. get intersections + new_segments = np.array(merged_segments) # (x1, y1, x2, y2) + start = new_segments[:, :2] # (x1, y1) + end = new_segments[:, 2:] # (x2, y2) + new_centers = (start + end) / 2.0 + diff = start - end + dist_segments = np.sqrt(np.sum(diff ** 2, axis=-1)) + + # ax + by = c + a = diff[:, 1] + b = -diff[:, 0] + c = a * start[:, 0] + b * start[:, 1] + pre_det = a[:, None] * b[None, :] + det = pre_det - np.transpose(pre_det) + + pre_inter_y = a[:, None] * c[None, :] + inter_y = (pre_inter_y - np.transpose(pre_inter_y)) / (det + 1e-10) + pre_inter_x = c[:, None] * b[None, :] + inter_x = (pre_inter_x - np.transpose(pre_inter_x)) / (det + 1e-10) + inter_pts = np.concatenate([inter_x[:, :, None], inter_y[:, :, None]], axis=-1).astype('int32') + + # 3. get corner information + # 3.1 get distance + ''' + dist_segments: + | dist(0), dist(1), dist(2), ...| + dist_inter_to_segment1: + | dist(inter,0), dist(inter,0), dist(inter,0), ... | + | dist(inter,1), dist(inter,1), dist(inter,1), ... | + ... + dist_inter_to_semgnet2: + | dist(inter,0), dist(inter,1), dist(inter,2), ... | + | dist(inter,0), dist(inter,1), dist(inter,2), ... | + ... + ''' + + dist_inter_to_segment1_start = np.sqrt( + np.sum(((inter_pts - start[:, None, :]) ** 2), axis=-1, keepdims=True)) # [n_batch, n_batch, 1] + dist_inter_to_segment1_end = np.sqrt( + np.sum(((inter_pts - end[:, None, :]) ** 2), axis=-1, keepdims=True)) # [n_batch, n_batch, 1] + dist_inter_to_segment2_start = np.sqrt( + np.sum(((inter_pts - start[None, :, :]) ** 2), axis=-1, keepdims=True)) # [n_batch, n_batch, 1] + dist_inter_to_segment2_end = np.sqrt( + np.sum(((inter_pts - end[None, :, :]) ** 2), axis=-1, keepdims=True)) # [n_batch, n_batch, 1] + + # sort ascending + dist_inter_to_segment1 = np.sort( + np.concatenate([dist_inter_to_segment1_start, dist_inter_to_segment1_end], axis=-1), + axis=-1) # [n_batch, n_batch, 2] + dist_inter_to_segment2 = np.sort( + np.concatenate([dist_inter_to_segment2_start, dist_inter_to_segment2_end], axis=-1), + axis=-1) # [n_batch, n_batch, 2] + + # 3.2 get degree + inter_to_start = new_centers[:, None, :] - inter_pts + deg_inter_to_start = np.arctan2(inter_to_start[:, :, 1], inter_to_start[:, :, 0]) * 180 / np.pi + deg_inter_to_start[deg_inter_to_start < 0.0] += 360 + inter_to_end = new_centers[None, :, :] - inter_pts + deg_inter_to_end = np.arctan2(inter_to_end[:, :, 1], inter_to_end[:, :, 0]) * 180 / np.pi + deg_inter_to_end[deg_inter_to_end < 0.0] += 360 + + ''' + B -- G + | | + C -- R + B : blue / G: green / C: cyan / R: red + + 0 -- 1 + | | + 3 -- 2 + ''' + # rename variables + deg1_map, deg2_map = deg_inter_to_start, deg_inter_to_end + # sort deg ascending + deg_sort = np.sort(np.concatenate([deg1_map[:, :, None], deg2_map[:, :, None]], axis=-1), axis=-1) + + deg_diff_map = np.abs(deg1_map - deg2_map) + # we only consider the smallest degree of intersect + deg_diff_map[deg_diff_map > 180] = 360 - deg_diff_map[deg_diff_map > 180] + + # define available degree range + deg_range = [60, 120] + + corner_dict = {corner_info: [] for corner_info in range(4)} + inter_points = [] + for i in range(inter_pts.shape[0]): + for j in range(i + 1, inter_pts.shape[1]): + # i, j > line index, always i < j + x, y = inter_pts[i, j, :] + deg1, deg2 = deg_sort[i, j, :] + deg_diff = deg_diff_map[i, j] + + check_degree = deg_diff > deg_range[0] and deg_diff < deg_range[1] + + outside_ratio = params['outside_ratio'] # over ratio >>> drop it! + inside_ratio = params['inside_ratio'] # over ratio >>> drop it! + check_distance = ((dist_inter_to_segment1[i, j, 1] >= dist_segments[i] and \ + dist_inter_to_segment1[i, j, 0] <= dist_segments[i] * outside_ratio) or \ + (dist_inter_to_segment1[i, j, 1] <= dist_segments[i] and \ + dist_inter_to_segment1[i, j, 0] <= dist_segments[i] * inside_ratio)) and \ + ((dist_inter_to_segment2[i, j, 1] >= dist_segments[j] and \ + dist_inter_to_segment2[i, j, 0] <= dist_segments[j] * outside_ratio) or \ + (dist_inter_to_segment2[i, j, 1] <= dist_segments[j] and \ + dist_inter_to_segment2[i, j, 0] <= dist_segments[j] * inside_ratio)) + + if check_degree and check_distance: + corner_info = None + + if (deg1 >= 0 and deg1 <= 45 and deg2 >= 45 and deg2 <= 120) or \ + (deg2 >= 315 and deg1 >= 45 and deg1 <= 120): + corner_info, color_info = 0, 'blue' + elif (deg1 >= 45 and deg1 <= 125 and deg2 >= 125 and deg2 <= 225): + corner_info, color_info = 1, 'green' + elif (deg1 >= 125 and deg1 <= 225 and deg2 >= 225 and deg2 <= 315): + corner_info, color_info = 2, 'black' + elif (deg1 >= 0 and deg1 <= 45 and deg2 >= 225 and deg2 <= 315) or \ + (deg2 >= 315 and deg1 >= 225 and deg1 <= 315): + corner_info, color_info = 3, 'cyan' + else: + corner_info, color_info = 4, 'red' # we don't use it + continue + + corner_dict[corner_info].append([x, y, i, j]) + inter_points.append([x, y]) + + square_list = [] + connect_list = [] + segments_list = [] + for corner0 in corner_dict[0]: + for corner1 in corner_dict[1]: + connect01 = False + for corner0_line in corner0[2:]: + if corner0_line in corner1[2:]: + connect01 = True + break + if connect01: + for corner2 in corner_dict[2]: + connect12 = False + for corner1_line in corner1[2:]: + if corner1_line in corner2[2:]: + connect12 = True + break + if connect12: + for corner3 in corner_dict[3]: + connect23 = False + for corner2_line in corner2[2:]: + if corner2_line in corner3[2:]: + connect23 = True + break + if connect23: + for corner3_line in corner3[2:]: + if corner3_line in corner0[2:]: + # SQUARE!!! + ''' + 0 -- 1 + | | + 3 -- 2 + square_list: + order: 0 > 1 > 2 > 3 + | x0, y0, x1, y1, x2, y2, x3, y3 | + | x0, y0, x1, y1, x2, y2, x3, y3 | + ... + connect_list: + order: 01 > 12 > 23 > 30 + | line_idx01, line_idx12, line_idx23, line_idx30 | + | line_idx01, line_idx12, line_idx23, line_idx30 | + ... + segments_list: + order: 0 > 1 > 2 > 3 + | line_idx0_i, line_idx0_j, line_idx1_i, line_idx1_j, line_idx2_i, line_idx2_j, line_idx3_i, line_idx3_j | + | line_idx0_i, line_idx0_j, line_idx1_i, line_idx1_j, line_idx2_i, line_idx2_j, line_idx3_i, line_idx3_j | + ... + ''' + square_list.append(corner0[:2] + corner1[:2] + corner2[:2] + corner3[:2]) + connect_list.append([corner0_line, corner1_line, corner2_line, corner3_line]) + segments_list.append(corner0[2:] + corner1[2:] + corner2[2:] + corner3[2:]) + + def check_outside_inside(segments_info, connect_idx): + # return 'outside or inside', min distance, cover_param, peri_param + if connect_idx == segments_info[0]: + check_dist_mat = dist_inter_to_segment1 + else: + check_dist_mat = dist_inter_to_segment2 + + i, j = segments_info + min_dist, max_dist = check_dist_mat[i, j, :] + connect_dist = dist_segments[connect_idx] + if max_dist > connect_dist: + return 'outside', min_dist, 0, 1 + else: + return 'inside', min_dist, -1, -1 + + top_square = None + + try: + map_size = input_shape[0] / 2 + squares = np.array(square_list).reshape([-1, 4, 2]) + score_array = [] + connect_array = np.array(connect_list) + segments_array = np.array(segments_list).reshape([-1, 4, 2]) + + # get degree of corners: + squares_rollup = np.roll(squares, 1, axis=1) + squares_rolldown = np.roll(squares, -1, axis=1) + vec1 = squares_rollup - squares + normalized_vec1 = vec1 / (np.linalg.norm(vec1, axis=-1, keepdims=True) + 1e-10) + vec2 = squares_rolldown - squares + normalized_vec2 = vec2 / (np.linalg.norm(vec2, axis=-1, keepdims=True) + 1e-10) + inner_products = np.sum(normalized_vec1 * normalized_vec2, axis=-1) # [n_squares, 4] + squares_degree = np.arccos(inner_products) * 180 / np.pi # [n_squares, 4] + + # get square score + overlap_scores = [] + degree_scores = [] + length_scores = [] + + for connects, segments, square, degree in zip(connect_array, segments_array, squares, squares_degree): + ''' + 0 -- 1 + | | + 3 -- 2 + + # segments: [4, 2] + # connects: [4] + ''' + + ###################################### OVERLAP SCORES + cover = 0 + perimeter = 0 + # check 0 > 1 > 2 > 3 + square_length = [] + + for start_idx in range(4): + end_idx = (start_idx + 1) % 4 + + connect_idx = connects[start_idx] # segment idx of segment01 + start_segments = segments[start_idx] + end_segments = segments[end_idx] + + start_point = square[start_idx] + end_point = square[end_idx] + + # check whether outside or inside + start_position, start_min, start_cover_param, start_peri_param = check_outside_inside(start_segments, + connect_idx) + end_position, end_min, end_cover_param, end_peri_param = check_outside_inside(end_segments, connect_idx) + + cover += dist_segments[connect_idx] + start_cover_param * start_min + end_cover_param * end_min + perimeter += dist_segments[connect_idx] + start_peri_param * start_min + end_peri_param * end_min + + square_length.append( + dist_segments[connect_idx] + start_peri_param * start_min + end_peri_param * end_min) + + overlap_scores.append(cover / perimeter) + ###################################### + ###################################### DEGREE SCORES + ''' + deg0 vs deg2 + deg1 vs deg3 + ''' + deg0, deg1, deg2, deg3 = degree + deg_ratio1 = deg0 / deg2 + if deg_ratio1 > 1.0: + deg_ratio1 = 1 / deg_ratio1 + deg_ratio2 = deg1 / deg3 + if deg_ratio2 > 1.0: + deg_ratio2 = 1 / deg_ratio2 + degree_scores.append((deg_ratio1 + deg_ratio2) / 2) + ###################################### + ###################################### LENGTH SCORES + ''' + len0 vs len2 + len1 vs len3 + ''' + len0, len1, len2, len3 = square_length + len_ratio1 = len0 / len2 if len2 > len0 else len2 / len0 + len_ratio2 = len1 / len3 if len3 > len1 else len3 / len1 + length_scores.append((len_ratio1 + len_ratio2) / 2) + + ###################################### + + overlap_scores = np.array(overlap_scores) + overlap_scores /= np.max(overlap_scores) + + degree_scores = np.array(degree_scores) + # degree_scores /= np.max(degree_scores) + + length_scores = np.array(length_scores) + + ###################################### AREA SCORES + area_scores = np.reshape(squares, [-1, 4, 2]) + area_x = area_scores[:, :, 0] + area_y = area_scores[:, :, 1] + correction = area_x[:, -1] * area_y[:, 0] - area_y[:, -1] * area_x[:, 0] + area_scores = np.sum(area_x[:, :-1] * area_y[:, 1:], axis=-1) - np.sum(area_y[:, :-1] * area_x[:, 1:], axis=-1) + area_scores = 0.5 * np.abs(area_scores + correction) + area_scores /= (map_size * map_size) # np.max(area_scores) + ###################################### + + ###################################### CENTER SCORES + centers = np.array([[256 // 2, 256 // 2]], dtype='float32') # [1, 2] + # squares: [n, 4, 2] + square_centers = np.mean(squares, axis=1) # [n, 2] + center2center = np.sqrt(np.sum((centers - square_centers) ** 2)) + center_scores = center2center / (map_size / np.sqrt(2.0)) + + ''' + score_w = [overlap, degree, area, center, length] + ''' + score_w = [0.0, 1.0, 10.0, 0.5, 1.0] + score_array = params['w_overlap'] * overlap_scores \ + + params['w_degree'] * degree_scores \ + + params['w_area'] * area_scores \ + - params['w_center'] * center_scores \ + + params['w_length'] * length_scores + + best_square = [] + + sorted_idx = np.argsort(score_array)[::-1] + score_array = score_array[sorted_idx] + squares = squares[sorted_idx] + + except Exception as e: + pass + + '''return list + merged_lines, squares, scores + ''' + + try: + new_segments[:, 0] = new_segments[:, 0] * 2 / input_shape[1] * original_shape[1] + new_segments[:, 1] = new_segments[:, 1] * 2 / input_shape[0] * original_shape[0] + new_segments[:, 2] = new_segments[:, 2] * 2 / input_shape[1] * original_shape[1] + new_segments[:, 3] = new_segments[:, 3] * 2 / input_shape[0] * original_shape[0] + except: + new_segments = [] + + try: + squares[:, :, 0] = squares[:, :, 0] * 2 / input_shape[1] * original_shape[1] + squares[:, :, 1] = squares[:, :, 1] * 2 / input_shape[0] * original_shape[0] + except: + squares = [] + score_array = [] + + try: + inter_points = np.array(inter_points) + inter_points[:, 0] = inter_points[:, 0] * 2 / input_shape[1] * original_shape[1] + inter_points[:, 1] = inter_points[:, 1] * 2 / input_shape[0] * original_shape[0] + except: + inter_points = [] + + return new_segments, squares, score_array, inter_points diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..210a2989138380559f23045b568d0fbbeb918c03 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) OpenMMLab. All rights reserved. +# flake8: noqa +from .arraymisc import * +from .fileio import * +from .image import * +from .utils import * +from .version import * +from .video import * +from .visualization import * + +# The following modules are not imported to this level, so mmcv may be used +# without PyTorch. +# - runner +# - parallel +# - op diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/arraymisc/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/arraymisc/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4b4700d6139ae3d604ff6e542468cce4200c020c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/arraymisc/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .quantization import dequantize, quantize + +__all__ = ['quantize', 'dequantize'] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/arraymisc/quantization.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/arraymisc/quantization.py new file mode 100644 index 0000000000000000000000000000000000000000..8e47a3545780cf071a1ef8195efb0b7b662c8186 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/arraymisc/quantization.py @@ -0,0 +1,55 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import numpy as np + + +def quantize(arr, min_val, max_val, levels, dtype=np.int64): + """Quantize an array of (-inf, inf) to [0, levels-1]. + + Args: + arr (ndarray): Input array. + min_val (scalar): Minimum value to be clipped. + max_val (scalar): Maximum value to be clipped. + levels (int): Quantization levels. + dtype (np.type): The type of the quantized array. + + Returns: + tuple: Quantized array. + """ + if not (isinstance(levels, int) and levels > 1): + raise ValueError( + f'levels must be a positive integer, but got {levels}') + if min_val >= max_val: + raise ValueError( + f'min_val ({min_val}) must be smaller than max_val ({max_val})') + + arr = np.clip(arr, min_val, max_val) - min_val + quantized_arr = np.minimum( + np.floor(levels * arr / (max_val - min_val)).astype(dtype), levels - 1) + + return quantized_arr + + +def dequantize(arr, min_val, max_val, levels, dtype=np.float64): + """Dequantize an array. + + Args: + arr (ndarray): Input array. + min_val (scalar): Minimum value to be clipped. + max_val (scalar): Maximum value to be clipped. + levels (int): Quantization levels. + dtype (np.type): The type of the dequantized array. + + Returns: + tuple: Dequantized array. + """ + if not (isinstance(levels, int) and levels > 1): + raise ValueError( + f'levels must be a positive integer, but got {levels}') + if min_val >= max_val: + raise ValueError( + f'min_val ({min_val}) must be smaller than max_val ({max_val})') + + dequantized_arr = (arr + 0.5).astype(dtype) * (max_val - + min_val) / levels + min_val + + return dequantized_arr diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7246c897430f0cc7ce12719ad8608824fc734446 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/__init__.py @@ -0,0 +1,41 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .alexnet import AlexNet +# yapf: disable +from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, + PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, + ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, + ConvTranspose2d, ConvTranspose3d, ConvWS2d, + DepthwiseSeparableConvModule, GeneralizedAttention, + HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, + NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, + build_activation_layer, build_conv_layer, + build_norm_layer, build_padding_layer, build_plugin_layer, + build_upsample_layer, conv_ws_2d, is_norm) +from .builder import MODELS, build_model_from_cfg +# yapf: enable +from .resnet import ResNet, make_res_layer +from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, + NormalInit, PretrainedInit, TruncNormalInit, UniformInit, + XavierInit, bias_init_with_prob, caffe2_xavier_init, + constant_init, fuse_conv_bn, get_model_complexity_info, + initialize, kaiming_init, normal_init, trunc_normal_init, + uniform_init, xavier_init) +from .vgg import VGG, make_vgg_layer + +__all__ = [ + 'AlexNet', 'VGG', 'make_vgg_layer', 'ResNet', 'make_res_layer', + 'constant_init', 'xavier_init', 'normal_init', 'trunc_normal_init', + 'uniform_init', 'kaiming_init', 'caffe2_xavier_init', + 'bias_init_with_prob', 'ConvModule', 'build_activation_layer', + 'build_conv_layer', 'build_norm_layer', 'build_padding_layer', + 'build_upsample_layer', 'build_plugin_layer', 'is_norm', 'NonLocal1d', + 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'HSigmoid', 'Swish', 'HSwish', + 'GeneralizedAttention', 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', + 'PADDING_LAYERS', 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', + 'get_model_complexity_info', 'conv_ws_2d', 'ConvAWS2d', 'ConvWS2d', + 'fuse_conv_bn', 'DepthwiseSeparableConvModule', 'Linear', 'Conv2d', + 'ConvTranspose2d', 'MaxPool2d', 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', + 'initialize', 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', + 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', + 'Caffe2XavierInit', 'MODELS', 'build_model_from_cfg' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/alexnet.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/alexnet.py new file mode 100644 index 0000000000000000000000000000000000000000..89e36b8c7851f895d9ae7f07149f0e707456aab0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/alexnet.py @@ -0,0 +1,61 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import logging + +import torch.nn as nn + + +class AlexNet(nn.Module): + """AlexNet backbone. + + Args: + num_classes (int): number of classes for classification. + """ + + def __init__(self, num_classes=-1): + super(AlexNet, self).__init__() + self.num_classes = num_classes + self.features = nn.Sequential( + nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2), + nn.ReLU(inplace=True), + nn.MaxPool2d(kernel_size=3, stride=2), + nn.Conv2d(64, 192, kernel_size=5, padding=2), + nn.ReLU(inplace=True), + nn.MaxPool2d(kernel_size=3, stride=2), + nn.Conv2d(192, 384, kernel_size=3, padding=1), + nn.ReLU(inplace=True), + nn.Conv2d(384, 256, kernel_size=3, padding=1), + nn.ReLU(inplace=True), + nn.Conv2d(256, 256, kernel_size=3, padding=1), + nn.ReLU(inplace=True), + nn.MaxPool2d(kernel_size=3, stride=2), + ) + if self.num_classes > 0: + self.classifier = nn.Sequential( + nn.Dropout(), + nn.Linear(256 * 6 * 6, 4096), + nn.ReLU(inplace=True), + nn.Dropout(), + nn.Linear(4096, 4096), + nn.ReLU(inplace=True), + nn.Linear(4096, num_classes), + ) + + def init_weights(self, pretrained=None): + if isinstance(pretrained, str): + logger = logging.getLogger() + from ..runner import load_checkpoint + load_checkpoint(self, pretrained, strict=False, logger=logger) + elif pretrained is None: + # use default initializer + pass + else: + raise TypeError('pretrained must be a str or None') + + def forward(self, x): + + x = self.features(x) + if self.num_classes > 0: + x = x.view(x.size(0), 256 * 6 * 6) + x = self.classifier(x) + + return x diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0f33124ed23fc6f27119a37bcb5ab004d3572be0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/__init__.py @@ -0,0 +1,35 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .activation import build_activation_layer +from .context_block import ContextBlock +from .conv import build_conv_layer +from .conv2d_adaptive_padding import Conv2dAdaptivePadding +from .conv_module import ConvModule +from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d +from .depthwise_separable_conv_module import DepthwiseSeparableConvModule +from .drop import Dropout, DropPath +from .generalized_attention import GeneralizedAttention +from .hsigmoid import HSigmoid +from .hswish import HSwish +from .non_local import NonLocal1d, NonLocal2d, NonLocal3d +from .norm import build_norm_layer, is_norm +from .padding import build_padding_layer +from .plugin import build_plugin_layer +from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, + PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) +from .scale import Scale +from .swish import Swish +from .upsample import build_upsample_layer +from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, + Linear, MaxPool2d, MaxPool3d) + +__all__ = [ + 'ConvModule', 'build_activation_layer', 'build_conv_layer', + 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', + 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', + 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', + 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', + 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', + 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', + 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', + 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/activation.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/activation.py new file mode 100644 index 0000000000000000000000000000000000000000..a8951058c8e77eda02c130f3401c9680702e231c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/activation.py @@ -0,0 +1,92 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.nn as nn +import torch.nn.functional as F + +from annotator.mmpkg.mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version +from .registry import ACTIVATION_LAYERS + +for module in [ + nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, + nn.Sigmoid, nn.Tanh +]: + ACTIVATION_LAYERS.register_module(module=module) + + +@ACTIVATION_LAYERS.register_module(name='Clip') +@ACTIVATION_LAYERS.register_module() +class Clamp(nn.Module): + """Clamp activation layer. + + This activation function is to clamp the feature map value within + :math:`[min, max]`. More details can be found in ``torch.clamp()``. + + Args: + min (Number | optional): Lower-bound of the range to be clamped to. + Default to -1. + max (Number | optional): Upper-bound of the range to be clamped to. + Default to 1. + """ + + def __init__(self, min=-1., max=1.): + super(Clamp, self).__init__() + self.min = min + self.max = max + + def forward(self, x): + """Forward function. + + Args: + x (torch.Tensor): The input tensor. + + Returns: + torch.Tensor: Clamped tensor. + """ + return torch.clamp(x, min=self.min, max=self.max) + + +class GELU(nn.Module): + r"""Applies the Gaussian Error Linear Units function: + + .. math:: + \text{GELU}(x) = x * \Phi(x) + where :math:`\Phi(x)` is the Cumulative Distribution Function for + Gaussian Distribution. + + Shape: + - Input: :math:`(N, *)` where `*` means, any number of additional + dimensions + - Output: :math:`(N, *)`, same shape as the input + + .. image:: scripts/activation_images/GELU.png + + Examples:: + + >>> m = nn.GELU() + >>> input = torch.randn(2) + >>> output = m(input) + """ + + def forward(self, input): + return F.gelu(input) + + +if (TORCH_VERSION == 'parrots' + or digit_version(TORCH_VERSION) < digit_version('1.4')): + ACTIVATION_LAYERS.register_module(module=GELU) +else: + ACTIVATION_LAYERS.register_module(module=nn.GELU) + + +def build_activation_layer(cfg): + """Build activation layer. + + Args: + cfg (dict): The activation layer config, which should contain: + - type (str): Layer type. + - layer args: Args needed to instantiate an activation layer. + + Returns: + nn.Module: Created activation layer. + """ + return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/context_block.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/context_block.py new file mode 100644 index 0000000000000000000000000000000000000000..d60fdb904c749ce3b251510dff3cc63cea70d42e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/context_block.py @@ -0,0 +1,125 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +from torch import nn + +from ..utils import constant_init, kaiming_init +from .registry import PLUGIN_LAYERS + + +def last_zero_init(m): + if isinstance(m, nn.Sequential): + constant_init(m[-1], val=0) + else: + constant_init(m, val=0) + + +@PLUGIN_LAYERS.register_module() +class ContextBlock(nn.Module): + """ContextBlock module in GCNet. + + See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' + (https://arxiv.org/abs/1904.11492) for details. + + Args: + in_channels (int): Channels of the input feature map. + ratio (float): Ratio of channels of transform bottleneck + pooling_type (str): Pooling method for context modeling. + Options are 'att' and 'avg', stand for attention pooling and + average pooling respectively. Default: 'att'. + fusion_types (Sequence[str]): Fusion method for feature fusion, + Options are 'channels_add', 'channel_mul', stand for channelwise + addition and multiplication respectively. Default: ('channel_add',) + """ + + _abbr_ = 'context_block' + + def __init__(self, + in_channels, + ratio, + pooling_type='att', + fusion_types=('channel_add', )): + super(ContextBlock, self).__init__() + assert pooling_type in ['avg', 'att'] + assert isinstance(fusion_types, (list, tuple)) + valid_fusion_types = ['channel_add', 'channel_mul'] + assert all([f in valid_fusion_types for f in fusion_types]) + assert len(fusion_types) > 0, 'at least one fusion should be used' + self.in_channels = in_channels + self.ratio = ratio + self.planes = int(in_channels * ratio) + self.pooling_type = pooling_type + self.fusion_types = fusion_types + if pooling_type == 'att': + self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) + self.softmax = nn.Softmax(dim=2) + else: + self.avg_pool = nn.AdaptiveAvgPool2d(1) + if 'channel_add' in fusion_types: + self.channel_add_conv = nn.Sequential( + nn.Conv2d(self.in_channels, self.planes, kernel_size=1), + nn.LayerNorm([self.planes, 1, 1]), + nn.ReLU(inplace=True), # yapf: disable + nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) + else: + self.channel_add_conv = None + if 'channel_mul' in fusion_types: + self.channel_mul_conv = nn.Sequential( + nn.Conv2d(self.in_channels, self.planes, kernel_size=1), + nn.LayerNorm([self.planes, 1, 1]), + nn.ReLU(inplace=True), # yapf: disable + nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) + else: + self.channel_mul_conv = None + self.reset_parameters() + + def reset_parameters(self): + if self.pooling_type == 'att': + kaiming_init(self.conv_mask, mode='fan_in') + self.conv_mask.inited = True + + if self.channel_add_conv is not None: + last_zero_init(self.channel_add_conv) + if self.channel_mul_conv is not None: + last_zero_init(self.channel_mul_conv) + + def spatial_pool(self, x): + batch, channel, height, width = x.size() + if self.pooling_type == 'att': + input_x = x + # [N, C, H * W] + input_x = input_x.view(batch, channel, height * width) + # [N, 1, C, H * W] + input_x = input_x.unsqueeze(1) + # [N, 1, H, W] + context_mask = self.conv_mask(x) + # [N, 1, H * W] + context_mask = context_mask.view(batch, 1, height * width) + # [N, 1, H * W] + context_mask = self.softmax(context_mask) + # [N, 1, H * W, 1] + context_mask = context_mask.unsqueeze(-1) + # [N, 1, C, 1] + context = torch.matmul(input_x, context_mask) + # [N, C, 1, 1] + context = context.view(batch, channel, 1, 1) + else: + # [N, C, 1, 1] + context = self.avg_pool(x) + + return context + + def forward(self, x): + # [N, C, 1, 1] + context = self.spatial_pool(x) + + out = x + if self.channel_mul_conv is not None: + # [N, C, 1, 1] + channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) + out = out * channel_mul_term + if self.channel_add_conv is not None: + # [N, C, 1, 1] + channel_add_term = self.channel_add_conv(context) + out = out + channel_add_term + + return out diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/conv.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/conv.py new file mode 100644 index 0000000000000000000000000000000000000000..cf54491997a48ac3e7fadc4183ab7bf3e831024c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/conv.py @@ -0,0 +1,44 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from torch import nn + +from .registry import CONV_LAYERS + +CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) +CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) +CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) +CONV_LAYERS.register_module('Conv', module=nn.Conv2d) + + +def build_conv_layer(cfg, *args, **kwargs): + """Build convolution layer. + + Args: + cfg (None or dict): The conv layer config, which should contain: + - type (str): Layer type. + - layer args: Args needed to instantiate an conv layer. + args (argument list): Arguments passed to the `__init__` + method of the corresponding conv layer. + kwargs (keyword arguments): Keyword arguments passed to the `__init__` + method of the corresponding conv layer. + + Returns: + nn.Module: Created conv layer. + """ + if cfg is None: + cfg_ = dict(type='Conv2d') + else: + if not isinstance(cfg, dict): + raise TypeError('cfg must be a dict') + if 'type' not in cfg: + raise KeyError('the cfg dict must contain the key "type"') + cfg_ = cfg.copy() + + layer_type = cfg_.pop('type') + if layer_type not in CONV_LAYERS: + raise KeyError(f'Unrecognized norm type {layer_type}') + else: + conv_layer = CONV_LAYERS.get(layer_type) + + layer = conv_layer(*args, **kwargs, **cfg_) + + return layer diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/conv2d_adaptive_padding.py new file mode 100644 index 0000000000000000000000000000000000000000..b45e758ac6cf8dfb0382d072fe09125bc7e9b888 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/conv2d_adaptive_padding.py @@ -0,0 +1,62 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import math + +from torch import nn +from torch.nn import functional as F + +from .registry import CONV_LAYERS + + +@CONV_LAYERS.register_module() +class Conv2dAdaptivePadding(nn.Conv2d): + """Implementation of 2D convolution in tensorflow with `padding` as "same", + which applies padding to input (if needed) so that input image gets fully + covered by filter and stride you specified. For stride 1, this will ensure + that output image size is same as input. For stride of 2, output dimensions + will be half, for example. + + Args: + in_channels (int): Number of channels in the input image + out_channels (int): Number of channels produced by the convolution + kernel_size (int or tuple): Size of the convolving kernel + stride (int or tuple, optional): Stride of the convolution. Default: 1 + padding (int or tuple, optional): Zero-padding added to both sides of + the input. Default: 0 + dilation (int or tuple, optional): Spacing between kernel elements. + Default: 1 + groups (int, optional): Number of blocked connections from input + channels to output channels. Default: 1 + bias (bool, optional): If ``True``, adds a learnable bias to the + output. Default: ``True`` + """ + + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True): + super().__init__(in_channels, out_channels, kernel_size, stride, 0, + dilation, groups, bias) + + def forward(self, x): + img_h, img_w = x.size()[-2:] + kernel_h, kernel_w = self.weight.size()[-2:] + stride_h, stride_w = self.stride + output_h = math.ceil(img_h / stride_h) + output_w = math.ceil(img_w / stride_w) + pad_h = ( + max((output_h - 1) * self.stride[0] + + (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) + pad_w = ( + max((output_w - 1) * self.stride[1] + + (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) + if pad_h > 0 or pad_w > 0: + x = F.pad(x, [ + pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 + ]) + return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, + self.dilation, self.groups) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/conv_module.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/conv_module.py new file mode 100644 index 0000000000000000000000000000000000000000..43cab72624ccc04b2f7877383588a4bbacf9117a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/conv_module.py @@ -0,0 +1,206 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import warnings + +import torch.nn as nn + +from annotator.mmpkg.mmcv.utils import _BatchNorm, _InstanceNorm +from ..utils import constant_init, kaiming_init +from .activation import build_activation_layer +from .conv import build_conv_layer +from .norm import build_norm_layer +from .padding import build_padding_layer +from .registry import PLUGIN_LAYERS + + +@PLUGIN_LAYERS.register_module() +class ConvModule(nn.Module): + """A conv block that bundles conv/norm/activation layers. + + This block simplifies the usage of convolution layers, which are commonly + used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). + It is based upon three build methods: `build_conv_layer()`, + `build_norm_layer()` and `build_activation_layer()`. + + Besides, we add some additional features in this module. + 1. Automatically set `bias` of the conv layer. + 2. Spectral norm is supported. + 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only + supports zero and circular padding, and we add "reflect" padding mode. + + Args: + in_channels (int): Number of channels in the input feature map. + Same as that in ``nn._ConvNd``. + out_channels (int): Number of channels produced by the convolution. + Same as that in ``nn._ConvNd``. + kernel_size (int | tuple[int]): Size of the convolving kernel. + Same as that in ``nn._ConvNd``. + stride (int | tuple[int]): Stride of the convolution. + Same as that in ``nn._ConvNd``. + padding (int | tuple[int]): Zero-padding added to both sides of + the input. Same as that in ``nn._ConvNd``. + dilation (int | tuple[int]): Spacing between kernel elements. + Same as that in ``nn._ConvNd``. + groups (int): Number of blocked connections from input channels to + output channels. Same as that in ``nn._ConvNd``. + bias (bool | str): If specified as `auto`, it will be decided by the + norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise + False. Default: "auto". + conv_cfg (dict): Config dict for convolution layer. Default: None, + which means using conv2d. + norm_cfg (dict): Config dict for normalization layer. Default: None. + act_cfg (dict): Config dict for activation layer. + Default: dict(type='ReLU'). + inplace (bool): Whether to use inplace mode for activation. + Default: True. + with_spectral_norm (bool): Whether use spectral norm in conv module. + Default: False. + padding_mode (str): If the `padding_mode` has not been supported by + current `Conv2d` in PyTorch, we will use our own padding layer + instead. Currently, we support ['zeros', 'circular'] with official + implementation and ['reflect'] with our own implementation. + Default: 'zeros'. + order (tuple[str]): The order of conv/norm/activation layers. It is a + sequence of "conv", "norm" and "act". Common examples are + ("conv", "norm", "act") and ("act", "conv", "norm"). + Default: ('conv', 'norm', 'act'). + """ + + _abbr_ = 'conv_block' + + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias='auto', + conv_cfg=None, + norm_cfg=None, + act_cfg=dict(type='ReLU'), + inplace=True, + with_spectral_norm=False, + padding_mode='zeros', + order=('conv', 'norm', 'act')): + super(ConvModule, self).__init__() + assert conv_cfg is None or isinstance(conv_cfg, dict) + assert norm_cfg is None or isinstance(norm_cfg, dict) + assert act_cfg is None or isinstance(act_cfg, dict) + official_padding_mode = ['zeros', 'circular'] + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + self.inplace = inplace + self.with_spectral_norm = with_spectral_norm + self.with_explicit_padding = padding_mode not in official_padding_mode + self.order = order + assert isinstance(self.order, tuple) and len(self.order) == 3 + assert set(order) == set(['conv', 'norm', 'act']) + + self.with_norm = norm_cfg is not None + self.with_activation = act_cfg is not None + # if the conv layer is before a norm layer, bias is unnecessary. + if bias == 'auto': + bias = not self.with_norm + self.with_bias = bias + + if self.with_explicit_padding: + pad_cfg = dict(type=padding_mode) + self.padding_layer = build_padding_layer(pad_cfg, padding) + + # reset padding to 0 for conv module + conv_padding = 0 if self.with_explicit_padding else padding + # build convolution layer + self.conv = build_conv_layer( + conv_cfg, + in_channels, + out_channels, + kernel_size, + stride=stride, + padding=conv_padding, + dilation=dilation, + groups=groups, + bias=bias) + # export the attributes of self.conv to a higher level for convenience + self.in_channels = self.conv.in_channels + self.out_channels = self.conv.out_channels + self.kernel_size = self.conv.kernel_size + self.stride = self.conv.stride + self.padding = padding + self.dilation = self.conv.dilation + self.transposed = self.conv.transposed + self.output_padding = self.conv.output_padding + self.groups = self.conv.groups + + if self.with_spectral_norm: + self.conv = nn.utils.spectral_norm(self.conv) + + # build normalization layers + if self.with_norm: + # norm layer is after conv layer + if order.index('norm') > order.index('conv'): + norm_channels = out_channels + else: + norm_channels = in_channels + self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) + self.add_module(self.norm_name, norm) + if self.with_bias: + if isinstance(norm, (_BatchNorm, _InstanceNorm)): + warnings.warn( + 'Unnecessary conv bias before batch/instance norm') + else: + self.norm_name = None + + # build activation layer + if self.with_activation: + act_cfg_ = act_cfg.copy() + # nn.Tanh has no 'inplace' argument + if act_cfg_['type'] not in [ + 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish' + ]: + act_cfg_.setdefault('inplace', inplace) + self.activate = build_activation_layer(act_cfg_) + + # Use msra init by default + self.init_weights() + + @property + def norm(self): + if self.norm_name: + return getattr(self, self.norm_name) + else: + return None + + def init_weights(self): + # 1. It is mainly for customized conv layers with their own + # initialization manners by calling their own ``init_weights()``, + # and we do not want ConvModule to override the initialization. + # 2. For customized conv layers without their own initialization + # manners (that is, they don't have their own ``init_weights()``) + # and PyTorch's conv layers, they will be initialized by + # this method with default ``kaiming_init``. + # Note: For PyTorch's conv layers, they will be overwritten by our + # initialization implementation using default ``kaiming_init``. + if not hasattr(self.conv, 'init_weights'): + if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': + nonlinearity = 'leaky_relu' + a = self.act_cfg.get('negative_slope', 0.01) + else: + nonlinearity = 'relu' + a = 0 + kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) + if self.with_norm: + constant_init(self.norm, 1, bias=0) + + def forward(self, x, activate=True, norm=True): + for layer in self.order: + if layer == 'conv': + if self.with_explicit_padding: + x = self.padding_layer(x) + x = self.conv(x) + elif layer == 'norm' and norm and self.with_norm: + x = self.norm(x) + elif layer == 'act' and activate and self.with_activation: + x = self.activate(x) + return x diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/conv_ws.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/conv_ws.py new file mode 100644 index 0000000000000000000000000000000000000000..a3941e27874993418b3b5708d5a7485f175ff9c8 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/conv_ws.py @@ -0,0 +1,148 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.nn as nn +import torch.nn.functional as F + +from .registry import CONV_LAYERS + + +def conv_ws_2d(input, + weight, + bias=None, + stride=1, + padding=0, + dilation=1, + groups=1, + eps=1e-5): + c_in = weight.size(0) + weight_flat = weight.view(c_in, -1) + mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) + std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) + weight = (weight - mean) / (std + eps) + return F.conv2d(input, weight, bias, stride, padding, dilation, groups) + + +@CONV_LAYERS.register_module('ConvWS') +class ConvWS2d(nn.Conv2d): + + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True, + eps=1e-5): + super(ConvWS2d, self).__init__( + in_channels, + out_channels, + kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias=bias) + self.eps = eps + + def forward(self, x): + return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, + self.dilation, self.groups, self.eps) + + +@CONV_LAYERS.register_module(name='ConvAWS') +class ConvAWS2d(nn.Conv2d): + """AWS (Adaptive Weight Standardization) + + This is a variant of Weight Standardization + (https://arxiv.org/pdf/1903.10520.pdf) + It is used in DetectoRS to avoid NaN + (https://arxiv.org/pdf/2006.02334.pdf) + + Args: + in_channels (int): Number of channels in the input image + out_channels (int): Number of channels produced by the convolution + kernel_size (int or tuple): Size of the conv kernel + stride (int or tuple, optional): Stride of the convolution. Default: 1 + padding (int or tuple, optional): Zero-padding added to both sides of + the input. Default: 0 + dilation (int or tuple, optional): Spacing between kernel elements. + Default: 1 + groups (int, optional): Number of blocked connections from input + channels to output channels. Default: 1 + bias (bool, optional): If set True, adds a learnable bias to the + output. Default: True + """ + + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True): + super().__init__( + in_channels, + out_channels, + kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias=bias) + self.register_buffer('weight_gamma', + torch.ones(self.out_channels, 1, 1, 1)) + self.register_buffer('weight_beta', + torch.zeros(self.out_channels, 1, 1, 1)) + + def _get_weight(self, weight): + weight_flat = weight.view(weight.size(0), -1) + mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) + std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) + weight = (weight - mean) / std + weight = self.weight_gamma * weight + self.weight_beta + return weight + + def forward(self, x): + weight = self._get_weight(self.weight) + return F.conv2d(x, weight, self.bias, self.stride, self.padding, + self.dilation, self.groups) + + def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, + missing_keys, unexpected_keys, error_msgs): + """Override default load function. + + AWS overrides the function _load_from_state_dict to recover + weight_gamma and weight_beta if they are missing. If weight_gamma and + weight_beta are found in the checkpoint, this function will return + after super()._load_from_state_dict. Otherwise, it will compute the + mean and std of the pretrained weights and store them in weight_beta + and weight_gamma. + """ + + self.weight_gamma.data.fill_(-1) + local_missing_keys = [] + super()._load_from_state_dict(state_dict, prefix, local_metadata, + strict, local_missing_keys, + unexpected_keys, error_msgs) + if self.weight_gamma.data.mean() > 0: + for k in local_missing_keys: + missing_keys.append(k) + return + weight = self.weight.data + weight_flat = weight.view(weight.size(0), -1) + mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) + std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) + self.weight_beta.data.copy_(mean) + self.weight_gamma.data.copy_(std) + missing_gamma_beta = [ + k for k in local_missing_keys + if k.endswith('weight_gamma') or k.endswith('weight_beta') + ] + for k in missing_gamma_beta: + local_missing_keys.remove(k) + for k in local_missing_keys: + missing_keys.append(k) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/depthwise_separable_conv_module.py new file mode 100644 index 0000000000000000000000000000000000000000..722d5d8d71f75486e2db3008907c4eadfca41d63 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/depthwise_separable_conv_module.py @@ -0,0 +1,96 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch.nn as nn + +from .conv_module import ConvModule + + +class DepthwiseSeparableConvModule(nn.Module): + """Depthwise separable convolution module. + + See https://arxiv.org/pdf/1704.04861.pdf for details. + + This module can replace a ConvModule with the conv block replaced by two + conv block: depthwise conv block and pointwise conv block. The depthwise + conv block contains depthwise-conv/norm/activation layers. The pointwise + conv block contains pointwise-conv/norm/activation layers. It should be + noted that there will be norm/activation layer in the depthwise conv block + if `norm_cfg` and `act_cfg` are specified. + + Args: + in_channels (int): Number of channels in the input feature map. + Same as that in ``nn._ConvNd``. + out_channels (int): Number of channels produced by the convolution. + Same as that in ``nn._ConvNd``. + kernel_size (int | tuple[int]): Size of the convolving kernel. + Same as that in ``nn._ConvNd``. + stride (int | tuple[int]): Stride of the convolution. + Same as that in ``nn._ConvNd``. Default: 1. + padding (int | tuple[int]): Zero-padding added to both sides of + the input. Same as that in ``nn._ConvNd``. Default: 0. + dilation (int | tuple[int]): Spacing between kernel elements. + Same as that in ``nn._ConvNd``. Default: 1. + norm_cfg (dict): Default norm config for both depthwise ConvModule and + pointwise ConvModule. Default: None. + act_cfg (dict): Default activation config for both depthwise ConvModule + and pointwise ConvModule. Default: dict(type='ReLU'). + dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is + 'default', it will be the same as `norm_cfg`. Default: 'default'. + dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is + 'default', it will be the same as `act_cfg`. Default: 'default'. + pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is + 'default', it will be the same as `norm_cfg`. Default: 'default'. + pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is + 'default', it will be the same as `act_cfg`. Default: 'default'. + kwargs (optional): Other shared arguments for depthwise and pointwise + ConvModule. See ConvModule for ref. + """ + + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + norm_cfg=None, + act_cfg=dict(type='ReLU'), + dw_norm_cfg='default', + dw_act_cfg='default', + pw_norm_cfg='default', + pw_act_cfg='default', + **kwargs): + super(DepthwiseSeparableConvModule, self).__init__() + assert 'groups' not in kwargs, 'groups should not be specified' + + # if norm/activation config of depthwise/pointwise ConvModule is not + # specified, use default config. + dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg + dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg + pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg + pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg + + # depthwise convolution + self.depthwise_conv = ConvModule( + in_channels, + in_channels, + kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=in_channels, + norm_cfg=dw_norm_cfg, + act_cfg=dw_act_cfg, + **kwargs) + + self.pointwise_conv = ConvModule( + in_channels, + out_channels, + 1, + norm_cfg=pw_norm_cfg, + act_cfg=pw_act_cfg, + **kwargs) + + def forward(self, x): + x = self.depthwise_conv(x) + x = self.pointwise_conv(x) + return x diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/drop.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/drop.py new file mode 100644 index 0000000000000000000000000000000000000000..465ed38339fe64dde8cdc959451b1236a3a55b95 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/drop.py @@ -0,0 +1,65 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.nn as nn + +from annotator.mmpkg.mmcv import build_from_cfg +from .registry import DROPOUT_LAYERS + + +def drop_path(x, drop_prob=0., training=False): + """Drop paths (Stochastic Depth) per sample (when applied in main path of + residual blocks). + + We follow the implementation + https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 + """ + if drop_prob == 0. or not training: + return x + keep_prob = 1 - drop_prob + # handle tensors with different dimensions, not just 4D tensors. + shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) + random_tensor = keep_prob + torch.rand( + shape, dtype=x.dtype, device=x.device) + output = x.div(keep_prob) * random_tensor.floor() + return output + + +@DROPOUT_LAYERS.register_module() +class DropPath(nn.Module): + """Drop paths (Stochastic Depth) per sample (when applied in main path of + residual blocks). + + We follow the implementation + https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 + + Args: + drop_prob (float): Probability of the path to be zeroed. Default: 0.1 + """ + + def __init__(self, drop_prob=0.1): + super(DropPath, self).__init__() + self.drop_prob = drop_prob + + def forward(self, x): + return drop_path(x, self.drop_prob, self.training) + + +@DROPOUT_LAYERS.register_module() +class Dropout(nn.Dropout): + """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of + ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with + ``DropPath`` + + Args: + drop_prob (float): Probability of the elements to be + zeroed. Default: 0.5. + inplace (bool): Do the operation inplace or not. Default: False. + """ + + def __init__(self, drop_prob=0.5, inplace=False): + super().__init__(p=drop_prob, inplace=inplace) + + +def build_dropout(cfg, default_args=None): + """Builder for drop out layers.""" + return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/generalized_attention.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/generalized_attention.py new file mode 100644 index 0000000000000000000000000000000000000000..988d9adf2f289ef223bd1c680a5ae1d3387f0269 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/generalized_attention.py @@ -0,0 +1,412 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import math + +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F + +from ..utils import kaiming_init +from .registry import PLUGIN_LAYERS + + +@PLUGIN_LAYERS.register_module() +class GeneralizedAttention(nn.Module): + """GeneralizedAttention module. + + See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' + (https://arxiv.org/abs/1711.07971) for details. + + Args: + in_channels (int): Channels of the input feature map. + spatial_range (int): The spatial range. -1 indicates no spatial range + constraint. Default: -1. + num_heads (int): The head number of empirical_attention module. + Default: 9. + position_embedding_dim (int): The position embedding dimension. + Default: -1. + position_magnitude (int): A multiplier acting on coord difference. + Default: 1. + kv_stride (int): The feature stride acting on key/value feature map. + Default: 2. + q_stride (int): The feature stride acting on query feature map. + Default: 1. + attention_type (str): A binary indicator string for indicating which + items in generalized empirical_attention module are used. + Default: '1111'. + + - '1000' indicates 'query and key content' (appr - appr) item, + - '0100' indicates 'query content and relative position' + (appr - position) item, + - '0010' indicates 'key content only' (bias - appr) item, + - '0001' indicates 'relative position only' (bias - position) item. + """ + + _abbr_ = 'gen_attention_block' + + def __init__(self, + in_channels, + spatial_range=-1, + num_heads=9, + position_embedding_dim=-1, + position_magnitude=1, + kv_stride=2, + q_stride=1, + attention_type='1111'): + + super(GeneralizedAttention, self).__init__() + + # hard range means local range for non-local operation + self.position_embedding_dim = ( + position_embedding_dim + if position_embedding_dim > 0 else in_channels) + + self.position_magnitude = position_magnitude + self.num_heads = num_heads + self.in_channels = in_channels + self.spatial_range = spatial_range + self.kv_stride = kv_stride + self.q_stride = q_stride + self.attention_type = [bool(int(_)) for _ in attention_type] + self.qk_embed_dim = in_channels // num_heads + out_c = self.qk_embed_dim * num_heads + + if self.attention_type[0] or self.attention_type[1]: + self.query_conv = nn.Conv2d( + in_channels=in_channels, + out_channels=out_c, + kernel_size=1, + bias=False) + self.query_conv.kaiming_init = True + + if self.attention_type[0] or self.attention_type[2]: + self.key_conv = nn.Conv2d( + in_channels=in_channels, + out_channels=out_c, + kernel_size=1, + bias=False) + self.key_conv.kaiming_init = True + + self.v_dim = in_channels // num_heads + self.value_conv = nn.Conv2d( + in_channels=in_channels, + out_channels=self.v_dim * num_heads, + kernel_size=1, + bias=False) + self.value_conv.kaiming_init = True + + if self.attention_type[1] or self.attention_type[3]: + self.appr_geom_fc_x = nn.Linear( + self.position_embedding_dim // 2, out_c, bias=False) + self.appr_geom_fc_x.kaiming_init = True + + self.appr_geom_fc_y = nn.Linear( + self.position_embedding_dim // 2, out_c, bias=False) + self.appr_geom_fc_y.kaiming_init = True + + if self.attention_type[2]: + stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) + appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv + self.appr_bias = nn.Parameter(appr_bias_value) + + if self.attention_type[3]: + stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) + geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv + self.geom_bias = nn.Parameter(geom_bias_value) + + self.proj_conv = nn.Conv2d( + in_channels=self.v_dim * num_heads, + out_channels=in_channels, + kernel_size=1, + bias=True) + self.proj_conv.kaiming_init = True + self.gamma = nn.Parameter(torch.zeros(1)) + + if self.spatial_range >= 0: + # only works when non local is after 3*3 conv + if in_channels == 256: + max_len = 84 + elif in_channels == 512: + max_len = 42 + + max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) + local_constraint_map = np.ones( + (max_len, max_len, max_len_kv, max_len_kv), dtype=np.int) + for iy in range(max_len): + for ix in range(max_len): + local_constraint_map[ + iy, ix, + max((iy - self.spatial_range) // + self.kv_stride, 0):min((iy + self.spatial_range + + 1) // self.kv_stride + + 1, max_len), + max((ix - self.spatial_range) // + self.kv_stride, 0):min((ix + self.spatial_range + + 1) // self.kv_stride + + 1, max_len)] = 0 + + self.local_constraint_map = nn.Parameter( + torch.from_numpy(local_constraint_map).byte(), + requires_grad=False) + + if self.q_stride > 1: + self.q_downsample = nn.AvgPool2d( + kernel_size=1, stride=self.q_stride) + else: + self.q_downsample = None + + if self.kv_stride > 1: + self.kv_downsample = nn.AvgPool2d( + kernel_size=1, stride=self.kv_stride) + else: + self.kv_downsample = None + + self.init_weights() + + def get_position_embedding(self, + h, + w, + h_kv, + w_kv, + q_stride, + kv_stride, + device, + dtype, + feat_dim, + wave_length=1000): + # the default type of Tensor is float32, leading to type mismatch + # in fp16 mode. Cast it to support fp16 mode. + h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) + h_idxs = h_idxs.view((h, 1)) * q_stride + + w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) + w_idxs = w_idxs.view((w, 1)) * q_stride + + h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( + device=device, dtype=dtype) + h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride + + w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( + device=device, dtype=dtype) + w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride + + # (h, h_kv, 1) + h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) + h_diff *= self.position_magnitude + + # (w, w_kv, 1) + w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) + w_diff *= self.position_magnitude + + feat_range = torch.arange(0, feat_dim / 4).to( + device=device, dtype=dtype) + + dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) + dim_mat = dim_mat**((4. / feat_dim) * feat_range) + dim_mat = dim_mat.view((1, 1, -1)) + + embedding_x = torch.cat( + ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) + + embedding_y = torch.cat( + ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) + + return embedding_x, embedding_y + + def forward(self, x_input): + num_heads = self.num_heads + + # use empirical_attention + if self.q_downsample is not None: + x_q = self.q_downsample(x_input) + else: + x_q = x_input + n, _, h, w = x_q.shape + + if self.kv_downsample is not None: + x_kv = self.kv_downsample(x_input) + else: + x_kv = x_input + _, _, h_kv, w_kv = x_kv.shape + + if self.attention_type[0] or self.attention_type[1]: + proj_query = self.query_conv(x_q).view( + (n, num_heads, self.qk_embed_dim, h * w)) + proj_query = proj_query.permute(0, 1, 3, 2) + + if self.attention_type[0] or self.attention_type[2]: + proj_key = self.key_conv(x_kv).view( + (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) + + if self.attention_type[1] or self.attention_type[3]: + position_embed_x, position_embed_y = self.get_position_embedding( + h, w, h_kv, w_kv, self.q_stride, self.kv_stride, + x_input.device, x_input.dtype, self.position_embedding_dim) + # (n, num_heads, w, w_kv, dim) + position_feat_x = self.appr_geom_fc_x(position_embed_x).\ + view(1, w, w_kv, num_heads, self.qk_embed_dim).\ + permute(0, 3, 1, 2, 4).\ + repeat(n, 1, 1, 1, 1) + + # (n, num_heads, h, h_kv, dim) + position_feat_y = self.appr_geom_fc_y(position_embed_y).\ + view(1, h, h_kv, num_heads, self.qk_embed_dim).\ + permute(0, 3, 1, 2, 4).\ + repeat(n, 1, 1, 1, 1) + + position_feat_x /= math.sqrt(2) + position_feat_y /= math.sqrt(2) + + # accelerate for saliency only + if (np.sum(self.attention_type) == 1) and self.attention_type[2]: + appr_bias = self.appr_bias.\ + view(1, num_heads, 1, self.qk_embed_dim).\ + repeat(n, 1, 1, 1) + + energy = torch.matmul(appr_bias, proj_key).\ + view(n, num_heads, 1, h_kv * w_kv) + + h = 1 + w = 1 + else: + # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for + if not self.attention_type[0]: + energy = torch.zeros( + n, + num_heads, + h, + w, + h_kv, + w_kv, + dtype=x_input.dtype, + device=x_input.device) + + # attention_type[0]: appr - appr + # attention_type[1]: appr - position + # attention_type[2]: bias - appr + # attention_type[3]: bias - position + if self.attention_type[0] or self.attention_type[2]: + if self.attention_type[0] and self.attention_type[2]: + appr_bias = self.appr_bias.\ + view(1, num_heads, 1, self.qk_embed_dim) + energy = torch.matmul(proj_query + appr_bias, proj_key).\ + view(n, num_heads, h, w, h_kv, w_kv) + + elif self.attention_type[0]: + energy = torch.matmul(proj_query, proj_key).\ + view(n, num_heads, h, w, h_kv, w_kv) + + elif self.attention_type[2]: + appr_bias = self.appr_bias.\ + view(1, num_heads, 1, self.qk_embed_dim).\ + repeat(n, 1, 1, 1) + + energy += torch.matmul(appr_bias, proj_key).\ + view(n, num_heads, 1, 1, h_kv, w_kv) + + if self.attention_type[1] or self.attention_type[3]: + if self.attention_type[1] and self.attention_type[3]: + geom_bias = self.geom_bias.\ + view(1, num_heads, 1, self.qk_embed_dim) + + proj_query_reshape = (proj_query + geom_bias).\ + view(n, num_heads, h, w, self.qk_embed_dim) + + energy_x = torch.matmul( + proj_query_reshape.permute(0, 1, 3, 2, 4), + position_feat_x.permute(0, 1, 2, 4, 3)) + energy_x = energy_x.\ + permute(0, 1, 3, 2, 4).unsqueeze(4) + + energy_y = torch.matmul( + proj_query_reshape, + position_feat_y.permute(0, 1, 2, 4, 3)) + energy_y = energy_y.unsqueeze(5) + + energy += energy_x + energy_y + + elif self.attention_type[1]: + proj_query_reshape = proj_query.\ + view(n, num_heads, h, w, self.qk_embed_dim) + proj_query_reshape = proj_query_reshape.\ + permute(0, 1, 3, 2, 4) + position_feat_x_reshape = position_feat_x.\ + permute(0, 1, 2, 4, 3) + position_feat_y_reshape = position_feat_y.\ + permute(0, 1, 2, 4, 3) + + energy_x = torch.matmul(proj_query_reshape, + position_feat_x_reshape) + energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) + + energy_y = torch.matmul(proj_query_reshape, + position_feat_y_reshape) + energy_y = energy_y.unsqueeze(5) + + energy += energy_x + energy_y + + elif self.attention_type[3]: + geom_bias = self.geom_bias.\ + view(1, num_heads, self.qk_embed_dim, 1).\ + repeat(n, 1, 1, 1) + + position_feat_x_reshape = position_feat_x.\ + view(n, num_heads, w*w_kv, self.qk_embed_dim) + + position_feat_y_reshape = position_feat_y.\ + view(n, num_heads, h * h_kv, self.qk_embed_dim) + + energy_x = torch.matmul(position_feat_x_reshape, geom_bias) + energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) + + energy_y = torch.matmul(position_feat_y_reshape, geom_bias) + energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) + + energy += energy_x + energy_y + + energy = energy.view(n, num_heads, h * w, h_kv * w_kv) + + if self.spatial_range >= 0: + cur_local_constraint_map = \ + self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ + contiguous().\ + view(1, 1, h*w, h_kv*w_kv) + + energy = energy.masked_fill_(cur_local_constraint_map, + float('-inf')) + + attention = F.softmax(energy, 3) + + proj_value = self.value_conv(x_kv) + proj_value_reshape = proj_value.\ + view((n, num_heads, self.v_dim, h_kv * w_kv)).\ + permute(0, 1, 3, 2) + + out = torch.matmul(attention, proj_value_reshape).\ + permute(0, 1, 3, 2).\ + contiguous().\ + view(n, self.v_dim * self.num_heads, h, w) + + out = self.proj_conv(out) + + # output is downsampled, upsample back to input size + if self.q_downsample is not None: + out = F.interpolate( + out, + size=x_input.shape[2:], + mode='bilinear', + align_corners=False) + + out = self.gamma * out + x_input + return out + + def init_weights(self): + for m in self.modules(): + if hasattr(m, 'kaiming_init') and m.kaiming_init: + kaiming_init( + m, + mode='fan_in', + nonlinearity='leaky_relu', + bias=0, + distribution='uniform', + a=1) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/hsigmoid.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/hsigmoid.py new file mode 100644 index 0000000000000000000000000000000000000000..30b1a3d6580cf0360710426fbea1f05acdf07b4b --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/hsigmoid.py @@ -0,0 +1,34 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch.nn as nn + +from .registry import ACTIVATION_LAYERS + + +@ACTIVATION_LAYERS.register_module() +class HSigmoid(nn.Module): + """Hard Sigmoid Module. Apply the hard sigmoid function: + Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) + Default: Hsigmoid(x) = min(max((x + 1) / 2, 0), 1) + + Args: + bias (float): Bias of the input feature map. Default: 1.0. + divisor (float): Divisor of the input feature map. Default: 2.0. + min_value (float): Lower bound value. Default: 0.0. + max_value (float): Upper bound value. Default: 1.0. + + Returns: + Tensor: The output tensor. + """ + + def __init__(self, bias=1.0, divisor=2.0, min_value=0.0, max_value=1.0): + super(HSigmoid, self).__init__() + self.bias = bias + self.divisor = divisor + assert self.divisor != 0 + self.min_value = min_value + self.max_value = max_value + + def forward(self, x): + x = (x + self.bias) / self.divisor + + return x.clamp_(self.min_value, self.max_value) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/hswish.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/hswish.py new file mode 100644 index 0000000000000000000000000000000000000000..7e0c090ff037c99ee6c5c84c4592e87beae02208 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/hswish.py @@ -0,0 +1,29 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch.nn as nn + +from .registry import ACTIVATION_LAYERS + + +@ACTIVATION_LAYERS.register_module() +class HSwish(nn.Module): + """Hard Swish Module. + + This module applies the hard swish function: + + .. math:: + Hswish(x) = x * ReLU6(x + 3) / 6 + + Args: + inplace (bool): can optionally do the operation in-place. + Default: False. + + Returns: + Tensor: The output tensor. + """ + + def __init__(self, inplace=False): + super(HSwish, self).__init__() + self.act = nn.ReLU6(inplace) + + def forward(self, x): + return x * self.act(x + 3) / 6 diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/non_local.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/non_local.py new file mode 100644 index 0000000000000000000000000000000000000000..92d00155ef275c1201ea66bba30470a1785cc5d7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/non_local.py @@ -0,0 +1,306 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from abc import ABCMeta + +import torch +import torch.nn as nn + +from ..utils import constant_init, normal_init +from .conv_module import ConvModule +from .registry import PLUGIN_LAYERS + + +class _NonLocalNd(nn.Module, metaclass=ABCMeta): + """Basic Non-local module. + + This module is proposed in + "Non-local Neural Networks" + Paper reference: https://arxiv.org/abs/1711.07971 + Code reference: https://github.com/AlexHex7/Non-local_pytorch + + Args: + in_channels (int): Channels of the input feature map. + reduction (int): Channel reduction ratio. Default: 2. + use_scale (bool): Whether to scale pairwise_weight by + `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. + Default: True. + conv_cfg (None | dict): The config dict for convolution layers. + If not specified, it will use `nn.Conv2d` for convolution layers. + Default: None. + norm_cfg (None | dict): The config dict for normalization layers. + Default: None. (This parameter is only applicable to conv_out.) + mode (str): Options are `gaussian`, `concatenation`, + `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. + """ + + def __init__(self, + in_channels, + reduction=2, + use_scale=True, + conv_cfg=None, + norm_cfg=None, + mode='embedded_gaussian', + **kwargs): + super(_NonLocalNd, self).__init__() + self.in_channels = in_channels + self.reduction = reduction + self.use_scale = use_scale + self.inter_channels = max(in_channels // reduction, 1) + self.mode = mode + + if mode not in [ + 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' + ]: + raise ValueError("Mode should be in 'gaussian', 'concatenation', " + f"'embedded_gaussian' or 'dot_product', but got " + f'{mode} instead.') + + # g, theta, phi are defaulted as `nn.ConvNd`. + # Here we use ConvModule for potential usage. + self.g = ConvModule( + self.in_channels, + self.inter_channels, + kernel_size=1, + conv_cfg=conv_cfg, + act_cfg=None) + self.conv_out = ConvModule( + self.inter_channels, + self.in_channels, + kernel_size=1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=None) + + if self.mode != 'gaussian': + self.theta = ConvModule( + self.in_channels, + self.inter_channels, + kernel_size=1, + conv_cfg=conv_cfg, + act_cfg=None) + self.phi = ConvModule( + self.in_channels, + self.inter_channels, + kernel_size=1, + conv_cfg=conv_cfg, + act_cfg=None) + + if self.mode == 'concatenation': + self.concat_project = ConvModule( + self.inter_channels * 2, + 1, + kernel_size=1, + stride=1, + padding=0, + bias=False, + act_cfg=dict(type='ReLU')) + + self.init_weights(**kwargs) + + def init_weights(self, std=0.01, zeros_init=True): + if self.mode != 'gaussian': + for m in [self.g, self.theta, self.phi]: + normal_init(m.conv, std=std) + else: + normal_init(self.g.conv, std=std) + if zeros_init: + if self.conv_out.norm_cfg is None: + constant_init(self.conv_out.conv, 0) + else: + constant_init(self.conv_out.norm, 0) + else: + if self.conv_out.norm_cfg is None: + normal_init(self.conv_out.conv, std=std) + else: + normal_init(self.conv_out.norm, std=std) + + def gaussian(self, theta_x, phi_x): + # NonLocal1d pairwise_weight: [N, H, H] + # NonLocal2d pairwise_weight: [N, HxW, HxW] + # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] + pairwise_weight = torch.matmul(theta_x, phi_x) + pairwise_weight = pairwise_weight.softmax(dim=-1) + return pairwise_weight + + def embedded_gaussian(self, theta_x, phi_x): + # NonLocal1d pairwise_weight: [N, H, H] + # NonLocal2d pairwise_weight: [N, HxW, HxW] + # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] + pairwise_weight = torch.matmul(theta_x, phi_x) + if self.use_scale: + # theta_x.shape[-1] is `self.inter_channels` + pairwise_weight /= theta_x.shape[-1]**0.5 + pairwise_weight = pairwise_weight.softmax(dim=-1) + return pairwise_weight + + def dot_product(self, theta_x, phi_x): + # NonLocal1d pairwise_weight: [N, H, H] + # NonLocal2d pairwise_weight: [N, HxW, HxW] + # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] + pairwise_weight = torch.matmul(theta_x, phi_x) + pairwise_weight /= pairwise_weight.shape[-1] + return pairwise_weight + + def concatenation(self, theta_x, phi_x): + # NonLocal1d pairwise_weight: [N, H, H] + # NonLocal2d pairwise_weight: [N, HxW, HxW] + # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] + h = theta_x.size(2) + w = phi_x.size(3) + theta_x = theta_x.repeat(1, 1, 1, w) + phi_x = phi_x.repeat(1, 1, h, 1) + + concat_feature = torch.cat([theta_x, phi_x], dim=1) + pairwise_weight = self.concat_project(concat_feature) + n, _, h, w = pairwise_weight.size() + pairwise_weight = pairwise_weight.view(n, h, w) + pairwise_weight /= pairwise_weight.shape[-1] + + return pairwise_weight + + def forward(self, x): + # Assume `reduction = 1`, then `inter_channels = C` + # or `inter_channels = C` when `mode="gaussian"` + + # NonLocal1d x: [N, C, H] + # NonLocal2d x: [N, C, H, W] + # NonLocal3d x: [N, C, T, H, W] + n = x.size(0) + + # NonLocal1d g_x: [N, H, C] + # NonLocal2d g_x: [N, HxW, C] + # NonLocal3d g_x: [N, TxHxW, C] + g_x = self.g(x).view(n, self.inter_channels, -1) + g_x = g_x.permute(0, 2, 1) + + # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] + # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] + # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] + if self.mode == 'gaussian': + theta_x = x.view(n, self.in_channels, -1) + theta_x = theta_x.permute(0, 2, 1) + if self.sub_sample: + phi_x = self.phi(x).view(n, self.in_channels, -1) + else: + phi_x = x.view(n, self.in_channels, -1) + elif self.mode == 'concatenation': + theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) + phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) + else: + theta_x = self.theta(x).view(n, self.inter_channels, -1) + theta_x = theta_x.permute(0, 2, 1) + phi_x = self.phi(x).view(n, self.inter_channels, -1) + + pairwise_func = getattr(self, self.mode) + # NonLocal1d pairwise_weight: [N, H, H] + # NonLocal2d pairwise_weight: [N, HxW, HxW] + # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] + pairwise_weight = pairwise_func(theta_x, phi_x) + + # NonLocal1d y: [N, H, C] + # NonLocal2d y: [N, HxW, C] + # NonLocal3d y: [N, TxHxW, C] + y = torch.matmul(pairwise_weight, g_x) + # NonLocal1d y: [N, C, H] + # NonLocal2d y: [N, C, H, W] + # NonLocal3d y: [N, C, T, H, W] + y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, + *x.size()[2:]) + + output = x + self.conv_out(y) + + return output + + +class NonLocal1d(_NonLocalNd): + """1D Non-local module. + + Args: + in_channels (int): Same as `NonLocalND`. + sub_sample (bool): Whether to apply max pooling after pairwise + function (Note that the `sub_sample` is applied on spatial only). + Default: False. + conv_cfg (None | dict): Same as `NonLocalND`. + Default: dict(type='Conv1d'). + """ + + def __init__(self, + in_channels, + sub_sample=False, + conv_cfg=dict(type='Conv1d'), + **kwargs): + super(NonLocal1d, self).__init__( + in_channels, conv_cfg=conv_cfg, **kwargs) + + self.sub_sample = sub_sample + + if sub_sample: + max_pool_layer = nn.MaxPool1d(kernel_size=2) + self.g = nn.Sequential(self.g, max_pool_layer) + if self.mode != 'gaussian': + self.phi = nn.Sequential(self.phi, max_pool_layer) + else: + self.phi = max_pool_layer + + +@PLUGIN_LAYERS.register_module() +class NonLocal2d(_NonLocalNd): + """2D Non-local module. + + Args: + in_channels (int): Same as `NonLocalND`. + sub_sample (bool): Whether to apply max pooling after pairwise + function (Note that the `sub_sample` is applied on spatial only). + Default: False. + conv_cfg (None | dict): Same as `NonLocalND`. + Default: dict(type='Conv2d'). + """ + + _abbr_ = 'nonlocal_block' + + def __init__(self, + in_channels, + sub_sample=False, + conv_cfg=dict(type='Conv2d'), + **kwargs): + super(NonLocal2d, self).__init__( + in_channels, conv_cfg=conv_cfg, **kwargs) + + self.sub_sample = sub_sample + + if sub_sample: + max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) + self.g = nn.Sequential(self.g, max_pool_layer) + if self.mode != 'gaussian': + self.phi = nn.Sequential(self.phi, max_pool_layer) + else: + self.phi = max_pool_layer + + +class NonLocal3d(_NonLocalNd): + """3D Non-local module. + + Args: + in_channels (int): Same as `NonLocalND`. + sub_sample (bool): Whether to apply max pooling after pairwise + function (Note that the `sub_sample` is applied on spatial only). + Default: False. + conv_cfg (None | dict): Same as `NonLocalND`. + Default: dict(type='Conv3d'). + """ + + def __init__(self, + in_channels, + sub_sample=False, + conv_cfg=dict(type='Conv3d'), + **kwargs): + super(NonLocal3d, self).__init__( + in_channels, conv_cfg=conv_cfg, **kwargs) + self.sub_sample = sub_sample + + if sub_sample: + max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) + self.g = nn.Sequential(self.g, max_pool_layer) + if self.mode != 'gaussian': + self.phi = nn.Sequential(self.phi, max_pool_layer) + else: + self.phi = max_pool_layer diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/norm.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/norm.py new file mode 100644 index 0000000000000000000000000000000000000000..31f4e49b24080485fc1d85b3e8ff810dc1383c95 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/norm.py @@ -0,0 +1,144 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import inspect + +import torch.nn as nn + +from annotator.mmpkg.mmcv.utils import is_tuple_of +from annotator.mmpkg.mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm +from .registry import NORM_LAYERS + +NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) +NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) +NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) +NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) +NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) +NORM_LAYERS.register_module('GN', module=nn.GroupNorm) +NORM_LAYERS.register_module('LN', module=nn.LayerNorm) +NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) +NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) +NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) +NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) + + +def infer_abbr(class_type): + """Infer abbreviation from the class name. + + When we build a norm layer with `build_norm_layer()`, we want to preserve + the norm type in variable names, e.g, self.bn1, self.gn. This method will + infer the abbreviation to map class types to abbreviations. + + Rule 1: If the class has the property "_abbr_", return the property. + Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or + InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and + "in" respectively. + Rule 3: If the class name contains "batch", "group", "layer" or "instance", + the abbreviation of this layer will be "bn", "gn", "ln" and "in" + respectively. + Rule 4: Otherwise, the abbreviation falls back to "norm". + + Args: + class_type (type): The norm layer type. + + Returns: + str: The inferred abbreviation. + """ + if not inspect.isclass(class_type): + raise TypeError( + f'class_type must be a type, but got {type(class_type)}') + if hasattr(class_type, '_abbr_'): + return class_type._abbr_ + if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN + return 'in' + elif issubclass(class_type, _BatchNorm): + return 'bn' + elif issubclass(class_type, nn.GroupNorm): + return 'gn' + elif issubclass(class_type, nn.LayerNorm): + return 'ln' + else: + class_name = class_type.__name__.lower() + if 'batch' in class_name: + return 'bn' + elif 'group' in class_name: + return 'gn' + elif 'layer' in class_name: + return 'ln' + elif 'instance' in class_name: + return 'in' + else: + return 'norm_layer' + + +def build_norm_layer(cfg, num_features, postfix=''): + """Build normalization layer. + + Args: + cfg (dict): The norm layer config, which should contain: + + - type (str): Layer type. + - layer args: Args needed to instantiate a norm layer. + - requires_grad (bool, optional): Whether stop gradient updates. + num_features (int): Number of input channels. + postfix (int | str): The postfix to be appended into norm abbreviation + to create named layer. + + Returns: + (str, nn.Module): The first element is the layer name consisting of + abbreviation and postfix, e.g., bn1, gn. The second element is the + created norm layer. + """ + if not isinstance(cfg, dict): + raise TypeError('cfg must be a dict') + if 'type' not in cfg: + raise KeyError('the cfg dict must contain the key "type"') + cfg_ = cfg.copy() + + layer_type = cfg_.pop('type') + if layer_type not in NORM_LAYERS: + raise KeyError(f'Unrecognized norm type {layer_type}') + + norm_layer = NORM_LAYERS.get(layer_type) + abbr = infer_abbr(norm_layer) + + assert isinstance(postfix, (int, str)) + name = abbr + str(postfix) + + requires_grad = cfg_.pop('requires_grad', True) + cfg_.setdefault('eps', 1e-5) + if layer_type != 'GN': + layer = norm_layer(num_features, **cfg_) + if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): + layer._specify_ddp_gpu_num(1) + else: + assert 'num_groups' in cfg_ + layer = norm_layer(num_channels=num_features, **cfg_) + + for param in layer.parameters(): + param.requires_grad = requires_grad + + return name, layer + + +def is_norm(layer, exclude=None): + """Check if a layer is a normalization layer. + + Args: + layer (nn.Module): The layer to be checked. + exclude (type | tuple[type]): Types to be excluded. + + Returns: + bool: Whether the layer is a norm layer. + """ + if exclude is not None: + if not isinstance(exclude, tuple): + exclude = (exclude, ) + if not is_tuple_of(exclude, type): + raise TypeError( + f'"exclude" must be either None or type or a tuple of types, ' + f'but got {type(exclude)}: {exclude}') + + if exclude and isinstance(layer, exclude): + return False + + all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) + return isinstance(layer, all_norm_bases) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/padding.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/padding.py new file mode 100644 index 0000000000000000000000000000000000000000..e4ac6b28a1789bd551c613a7d3e7b622433ac7ec --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/padding.py @@ -0,0 +1,36 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch.nn as nn + +from .registry import PADDING_LAYERS + +PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) +PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) +PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) + + +def build_padding_layer(cfg, *args, **kwargs): + """Build padding layer. + + Args: + cfg (None or dict): The padding layer config, which should contain: + - type (str): Layer type. + - layer args: Args needed to instantiate a padding layer. + + Returns: + nn.Module: Created padding layer. + """ + if not isinstance(cfg, dict): + raise TypeError('cfg must be a dict') + if 'type' not in cfg: + raise KeyError('the cfg dict must contain the key "type"') + + cfg_ = cfg.copy() + padding_type = cfg_.pop('type') + if padding_type not in PADDING_LAYERS: + raise KeyError(f'Unrecognized padding type {padding_type}.') + else: + padding_layer = PADDING_LAYERS.get(padding_type) + + layer = padding_layer(*args, **kwargs, **cfg_) + + return layer diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/plugin.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..07c010d4053174dd41107aa654ea67e82b46a25c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/plugin.py @@ -0,0 +1,88 @@ +import inspect +import platform + +from .registry import PLUGIN_LAYERS + +if platform.system() == 'Windows': + import regex as re +else: + import re + + +def infer_abbr(class_type): + """Infer abbreviation from the class name. + + This method will infer the abbreviation to map class types to + abbreviations. + + Rule 1: If the class has the property "abbr", return the property. + Rule 2: Otherwise, the abbreviation falls back to snake case of class + name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. + + Args: + class_type (type): The norm layer type. + + Returns: + str: The inferred abbreviation. + """ + + def camel2snack(word): + """Convert camel case word into snack case. + + Modified from `inflection lib + `_. + + Example:: + + >>> camel2snack("FancyBlock") + 'fancy_block' + """ + + word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) + word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) + word = word.replace('-', '_') + return word.lower() + + if not inspect.isclass(class_type): + raise TypeError( + f'class_type must be a type, but got {type(class_type)}') + if hasattr(class_type, '_abbr_'): + return class_type._abbr_ + else: + return camel2snack(class_type.__name__) + + +def build_plugin_layer(cfg, postfix='', **kwargs): + """Build plugin layer. + + Args: + cfg (None or dict): cfg should contain: + type (str): identify plugin layer type. + layer args: args needed to instantiate a plugin layer. + postfix (int, str): appended into norm abbreviation to + create named layer. Default: ''. + + Returns: + tuple[str, nn.Module]: + name (str): abbreviation + postfix + layer (nn.Module): created plugin layer + """ + if not isinstance(cfg, dict): + raise TypeError('cfg must be a dict') + if 'type' not in cfg: + raise KeyError('the cfg dict must contain the key "type"') + cfg_ = cfg.copy() + + layer_type = cfg_.pop('type') + if layer_type not in PLUGIN_LAYERS: + raise KeyError(f'Unrecognized plugin type {layer_type}') + + plugin_layer = PLUGIN_LAYERS.get(layer_type) + abbr = infer_abbr(plugin_layer) + + assert isinstance(postfix, (int, str)) + name = abbr + str(postfix) + + layer = plugin_layer(**kwargs, **cfg_) + + return name, layer diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/registry.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/registry.py new file mode 100644 index 0000000000000000000000000000000000000000..4f374cca4961c06babf328bb7407723a14026c47 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/registry.py @@ -0,0 +1,16 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from annotator.mmpkg.mmcv.utils import Registry + +CONV_LAYERS = Registry('conv layer') +NORM_LAYERS = Registry('norm layer') +ACTIVATION_LAYERS = Registry('activation layer') +PADDING_LAYERS = Registry('padding layer') +UPSAMPLE_LAYERS = Registry('upsample layer') +PLUGIN_LAYERS = Registry('plugin layer') + +DROPOUT_LAYERS = Registry('drop out layers') +POSITIONAL_ENCODING = Registry('position encoding') +ATTENTION = Registry('attention') +FEEDFORWARD_NETWORK = Registry('feed-forward Network') +TRANSFORMER_LAYER = Registry('transformerLayer') +TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/scale.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/scale.py new file mode 100644 index 0000000000000000000000000000000000000000..c905fffcc8bf998d18d94f927591963c428025e2 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/scale.py @@ -0,0 +1,21 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.nn as nn + + +class Scale(nn.Module): + """A learnable scale parameter. + + This layer scales the input by a learnable factor. It multiplies a + learnable scale parameter of shape (1,) with input of any shape. + + Args: + scale (float): Initial value of scale factor. Default: 1.0 + """ + + def __init__(self, scale=1.0): + super(Scale, self).__init__() + self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) + + def forward(self, x): + return x * self.scale diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/swish.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/swish.py new file mode 100644 index 0000000000000000000000000000000000000000..e2ca8ed7b749413f011ae54aac0cab27e6f0b51f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/swish.py @@ -0,0 +1,25 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.nn as nn + +from .registry import ACTIVATION_LAYERS + + +@ACTIVATION_LAYERS.register_module() +class Swish(nn.Module): + """Swish Module. + + This module applies the swish function: + + .. math:: + Swish(x) = x * Sigmoid(x) + + Returns: + Tensor: The output tensor. + """ + + def __init__(self): + super(Swish, self).__init__() + + def forward(self, x): + return x * torch.sigmoid(x) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/transformer.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/transformer.py new file mode 100644 index 0000000000000000000000000000000000000000..e16707142b645144b676059ffa992fc4306ef778 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/transformer.py @@ -0,0 +1,595 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import copy +import warnings + +import torch +import torch.nn as nn + +from annotator.mmpkg.mmcv import ConfigDict, deprecated_api_warning +from annotator.mmpkg.mmcv.cnn import Linear, build_activation_layer, build_norm_layer +from annotator.mmpkg.mmcv.runner.base_module import BaseModule, ModuleList, Sequential +from annotator.mmpkg.mmcv.utils import build_from_cfg +from .drop import build_dropout +from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, + TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) + +# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file +try: + from annotator.mmpkg.mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention # noqa F401 + warnings.warn( + ImportWarning( + '``MultiScaleDeformableAttention`` has been moved to ' + '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 + '``from annotator.mmpkg.mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 + 'to ``from annotator.mmpkg.mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 + )) + +except ImportError: + warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' + '``mmcv.ops.multi_scale_deform_attn``, ' + 'You should install ``mmcv-full`` if you need this module. ') + + +def build_positional_encoding(cfg, default_args=None): + """Builder for Position Encoding.""" + return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) + + +def build_attention(cfg, default_args=None): + """Builder for attention.""" + return build_from_cfg(cfg, ATTENTION, default_args) + + +def build_feedforward_network(cfg, default_args=None): + """Builder for feed-forward network (FFN).""" + return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) + + +def build_transformer_layer(cfg, default_args=None): + """Builder for transformer layer.""" + return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) + + +def build_transformer_layer_sequence(cfg, default_args=None): + """Builder for transformer encoder and transformer decoder.""" + return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) + + +@ATTENTION.register_module() +class MultiheadAttention(BaseModule): + """A wrapper for ``torch.nn.MultiheadAttention``. + + This module implements MultiheadAttention with identity connection, + and positional encoding is also passed as input. + + Args: + embed_dims (int): The embedding dimension. + num_heads (int): Parallel attention heads. + attn_drop (float): A Dropout layer on attn_output_weights. + Default: 0.0. + proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. + Default: 0.0. + dropout_layer (obj:`ConfigDict`): The dropout_layer used + when adding the shortcut. + init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. + Default: None. + batch_first (bool): When it is True, Key, Query and Value are shape of + (batch, n, embed_dim), otherwise (n, batch, embed_dim). + Default to False. + """ + + def __init__(self, + embed_dims, + num_heads, + attn_drop=0., + proj_drop=0., + dropout_layer=dict(type='Dropout', drop_prob=0.), + init_cfg=None, + batch_first=False, + **kwargs): + super(MultiheadAttention, self).__init__(init_cfg) + if 'dropout' in kwargs: + warnings.warn('The arguments `dropout` in MultiheadAttention ' + 'has been deprecated, now you can separately ' + 'set `attn_drop`(float), proj_drop(float), ' + 'and `dropout_layer`(dict) ') + attn_drop = kwargs['dropout'] + dropout_layer['drop_prob'] = kwargs.pop('dropout') + + self.embed_dims = embed_dims + self.num_heads = num_heads + self.batch_first = batch_first + + self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, + **kwargs) + + self.proj_drop = nn.Dropout(proj_drop) + self.dropout_layer = build_dropout( + dropout_layer) if dropout_layer else nn.Identity() + + @deprecated_api_warning({'residual': 'identity'}, + cls_name='MultiheadAttention') + def forward(self, + query, + key=None, + value=None, + identity=None, + query_pos=None, + key_pos=None, + attn_mask=None, + key_padding_mask=None, + **kwargs): + """Forward function for `MultiheadAttention`. + + **kwargs allow passing a more general data flow when combining + with other operations in `transformerlayer`. + + Args: + query (Tensor): The input query with shape [num_queries, bs, + embed_dims] if self.batch_first is False, else + [bs, num_queries embed_dims]. + key (Tensor): The key tensor with shape [num_keys, bs, + embed_dims] if self.batch_first is False, else + [bs, num_keys, embed_dims] . + If None, the ``query`` will be used. Defaults to None. + value (Tensor): The value tensor with same shape as `key`. + Same in `nn.MultiheadAttention.forward`. Defaults to None. + If None, the `key` will be used. + identity (Tensor): This tensor, with the same shape as x, + will be used for the identity link. + If None, `x` will be used. Defaults to None. + query_pos (Tensor): The positional encoding for query, with + the same shape as `x`. If not None, it will + be added to `x` before forward function. Defaults to None. + key_pos (Tensor): The positional encoding for `key`, with the + same shape as `key`. Defaults to None. If not None, it will + be added to `key` before forward function. If None, and + `query_pos` has the same shape as `key`, then `query_pos` + will be used for `key_pos`. Defaults to None. + attn_mask (Tensor): ByteTensor mask with shape [num_queries, + num_keys]. Same in `nn.MultiheadAttention.forward`. + Defaults to None. + key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. + Defaults to None. + + Returns: + Tensor: forwarded results with shape + [num_queries, bs, embed_dims] + if self.batch_first is False, else + [bs, num_queries embed_dims]. + """ + + if key is None: + key = query + if value is None: + value = key + if identity is None: + identity = query + if key_pos is None: + if query_pos is not None: + # use query_pos if key_pos is not available + if query_pos.shape == key.shape: + key_pos = query_pos + else: + warnings.warn(f'position encoding of key is' + f'missing in {self.__class__.__name__}.') + if query_pos is not None: + query = query + query_pos + if key_pos is not None: + key = key + key_pos + + # Because the dataflow('key', 'query', 'value') of + # ``torch.nn.MultiheadAttention`` is (num_query, batch, + # embed_dims), We should adjust the shape of dataflow from + # batch_first (batch, num_query, embed_dims) to num_query_first + # (num_query ,batch, embed_dims), and recover ``attn_output`` + # from num_query_first to batch_first. + if self.batch_first: + query = query.transpose(0, 1) + key = key.transpose(0, 1) + value = value.transpose(0, 1) + + out = self.attn( + query=query, + key=key, + value=value, + attn_mask=attn_mask, + key_padding_mask=key_padding_mask)[0] + + if self.batch_first: + out = out.transpose(0, 1) + + return identity + self.dropout_layer(self.proj_drop(out)) + + +@FEEDFORWARD_NETWORK.register_module() +class FFN(BaseModule): + """Implements feed-forward networks (FFNs) with identity connection. + + Args: + embed_dims (int): The feature dimension. Same as + `MultiheadAttention`. Defaults: 256. + feedforward_channels (int): The hidden dimension of FFNs. + Defaults: 1024. + num_fcs (int, optional): The number of fully-connected layers in + FFNs. Default: 2. + act_cfg (dict, optional): The activation config for FFNs. + Default: dict(type='ReLU') + ffn_drop (float, optional): Probability of an element to be + zeroed in FFN. Default 0.0. + add_identity (bool, optional): Whether to add the + identity connection. Default: `True`. + dropout_layer (obj:`ConfigDict`): The dropout_layer used + when adding the shortcut. + init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. + Default: None. + """ + + @deprecated_api_warning( + { + 'dropout': 'ffn_drop', + 'add_residual': 'add_identity' + }, + cls_name='FFN') + def __init__(self, + embed_dims=256, + feedforward_channels=1024, + num_fcs=2, + act_cfg=dict(type='ReLU', inplace=True), + ffn_drop=0., + dropout_layer=None, + add_identity=True, + init_cfg=None, + **kwargs): + super(FFN, self).__init__(init_cfg) + assert num_fcs >= 2, 'num_fcs should be no less ' \ + f'than 2. got {num_fcs}.' + self.embed_dims = embed_dims + self.feedforward_channels = feedforward_channels + self.num_fcs = num_fcs + self.act_cfg = act_cfg + self.activate = build_activation_layer(act_cfg) + + layers = [] + in_channels = embed_dims + for _ in range(num_fcs - 1): + layers.append( + Sequential( + Linear(in_channels, feedforward_channels), self.activate, + nn.Dropout(ffn_drop))) + in_channels = feedforward_channels + layers.append(Linear(feedforward_channels, embed_dims)) + layers.append(nn.Dropout(ffn_drop)) + self.layers = Sequential(*layers) + self.dropout_layer = build_dropout( + dropout_layer) if dropout_layer else torch.nn.Identity() + self.add_identity = add_identity + + @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') + def forward(self, x, identity=None): + """Forward function for `FFN`. + + The function would add x to the output tensor if residue is None. + """ + out = self.layers(x) + if not self.add_identity: + return self.dropout_layer(out) + if identity is None: + identity = x + return identity + self.dropout_layer(out) + + +@TRANSFORMER_LAYER.register_module() +class BaseTransformerLayer(BaseModule): + """Base `TransformerLayer` for vision transformer. + + It can be built from `mmcv.ConfigDict` and support more flexible + customization, for example, using any number of `FFN or LN ` and + use different kinds of `attention` by specifying a list of `ConfigDict` + named `attn_cfgs`. It is worth mentioning that it supports `prenorm` + when you specifying `norm` as the first element of `operation_order`. + More details about the `prenorm`: `On Layer Normalization in the + Transformer Architecture `_ . + + Args: + attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): + Configs for `self_attention` or `cross_attention` modules, + The order of the configs in the list should be consistent with + corresponding attentions in operation_order. + If it is a dict, all of the attention modules in operation_order + will be built with this config. Default: None. + ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): + Configs for FFN, The order of the configs in the list should be + consistent with corresponding ffn in operation_order. + If it is a dict, all of the attention modules in operation_order + will be built with this config. + operation_order (tuple[str]): The execution order of operation + in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). + Support `prenorm` when you specifying first element as `norm`. + Default:None. + norm_cfg (dict): Config dict for normalization layer. + Default: dict(type='LN'). + init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. + Default: None. + batch_first (bool): Key, Query and Value are shape + of (batch, n, embed_dim) + or (n, batch, embed_dim). Default to False. + """ + + def __init__(self, + attn_cfgs=None, + ffn_cfgs=dict( + type='FFN', + embed_dims=256, + feedforward_channels=1024, + num_fcs=2, + ffn_drop=0., + act_cfg=dict(type='ReLU', inplace=True), + ), + operation_order=None, + norm_cfg=dict(type='LN'), + init_cfg=None, + batch_first=False, + **kwargs): + + deprecated_args = dict( + feedforward_channels='feedforward_channels', + ffn_dropout='ffn_drop', + ffn_num_fcs='num_fcs') + for ori_name, new_name in deprecated_args.items(): + if ori_name in kwargs: + warnings.warn( + f'The arguments `{ori_name}` in BaseTransformerLayer ' + f'has been deprecated, now you should set `{new_name}` ' + f'and other FFN related arguments ' + f'to a dict named `ffn_cfgs`. ') + ffn_cfgs[new_name] = kwargs[ori_name] + + super(BaseTransformerLayer, self).__init__(init_cfg) + + self.batch_first = batch_first + + assert set(operation_order) & set( + ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ + set(operation_order), f'The operation_order of' \ + f' {self.__class__.__name__} should ' \ + f'contains all four operation type ' \ + f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" + + num_attn = operation_order.count('self_attn') + operation_order.count( + 'cross_attn') + if isinstance(attn_cfgs, dict): + attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] + else: + assert num_attn == len(attn_cfgs), f'The length ' \ + f'of attn_cfg {num_attn} is ' \ + f'not consistent with the number of attention' \ + f'in operation_order {operation_order}.' + + self.num_attn = num_attn + self.operation_order = operation_order + self.norm_cfg = norm_cfg + self.pre_norm = operation_order[0] == 'norm' + self.attentions = ModuleList() + + index = 0 + for operation_name in operation_order: + if operation_name in ['self_attn', 'cross_attn']: + if 'batch_first' in attn_cfgs[index]: + assert self.batch_first == attn_cfgs[index]['batch_first'] + else: + attn_cfgs[index]['batch_first'] = self.batch_first + attention = build_attention(attn_cfgs[index]) + # Some custom attentions used as `self_attn` + # or `cross_attn` can have different behavior. + attention.operation_name = operation_name + self.attentions.append(attention) + index += 1 + + self.embed_dims = self.attentions[0].embed_dims + + self.ffns = ModuleList() + num_ffns = operation_order.count('ffn') + if isinstance(ffn_cfgs, dict): + ffn_cfgs = ConfigDict(ffn_cfgs) + if isinstance(ffn_cfgs, dict): + ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] + assert len(ffn_cfgs) == num_ffns + for ffn_index in range(num_ffns): + if 'embed_dims' not in ffn_cfgs[ffn_index]: + ffn_cfgs['embed_dims'] = self.embed_dims + else: + assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims + self.ffns.append( + build_feedforward_network(ffn_cfgs[ffn_index], + dict(type='FFN'))) + + self.norms = ModuleList() + num_norms = operation_order.count('norm') + for _ in range(num_norms): + self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) + + def forward(self, + query, + key=None, + value=None, + query_pos=None, + key_pos=None, + attn_masks=None, + query_key_padding_mask=None, + key_padding_mask=None, + **kwargs): + """Forward function for `TransformerDecoderLayer`. + + **kwargs contains some specific arguments of attentions. + + Args: + query (Tensor): The input query with shape + [num_queries, bs, embed_dims] if + self.batch_first is False, else + [bs, num_queries embed_dims]. + key (Tensor): The key tensor with shape [num_keys, bs, + embed_dims] if self.batch_first is False, else + [bs, num_keys, embed_dims] . + value (Tensor): The value tensor with same shape as `key`. + query_pos (Tensor): The positional encoding for `query`. + Default: None. + key_pos (Tensor): The positional encoding for `key`. + Default: None. + attn_masks (List[Tensor] | None): 2D Tensor used in + calculation of corresponding attention. The length of + it should equal to the number of `attention` in + `operation_order`. Default: None. + query_key_padding_mask (Tensor): ByteTensor for `query`, with + shape [bs, num_queries]. Only used in `self_attn` layer. + Defaults to None. + key_padding_mask (Tensor): ByteTensor for `query`, with + shape [bs, num_keys]. Default: None. + + Returns: + Tensor: forwarded results with shape [num_queries, bs, embed_dims]. + """ + + norm_index = 0 + attn_index = 0 + ffn_index = 0 + identity = query + if attn_masks is None: + attn_masks = [None for _ in range(self.num_attn)] + elif isinstance(attn_masks, torch.Tensor): + attn_masks = [ + copy.deepcopy(attn_masks) for _ in range(self.num_attn) + ] + warnings.warn(f'Use same attn_mask in all attentions in ' + f'{self.__class__.__name__} ') + else: + assert len(attn_masks) == self.num_attn, f'The length of ' \ + f'attn_masks {len(attn_masks)} must be equal ' \ + f'to the number of attention in ' \ + f'operation_order {self.num_attn}' + + for layer in self.operation_order: + if layer == 'self_attn': + temp_key = temp_value = query + query = self.attentions[attn_index]( + query, + temp_key, + temp_value, + identity if self.pre_norm else None, + query_pos=query_pos, + key_pos=query_pos, + attn_mask=attn_masks[attn_index], + key_padding_mask=query_key_padding_mask, + **kwargs) + attn_index += 1 + identity = query + + elif layer == 'norm': + query = self.norms[norm_index](query) + norm_index += 1 + + elif layer == 'cross_attn': + query = self.attentions[attn_index]( + query, + key, + value, + identity if self.pre_norm else None, + query_pos=query_pos, + key_pos=key_pos, + attn_mask=attn_masks[attn_index], + key_padding_mask=key_padding_mask, + **kwargs) + attn_index += 1 + identity = query + + elif layer == 'ffn': + query = self.ffns[ffn_index]( + query, identity if self.pre_norm else None) + ffn_index += 1 + + return query + + +@TRANSFORMER_LAYER_SEQUENCE.register_module() +class TransformerLayerSequence(BaseModule): + """Base class for TransformerEncoder and TransformerDecoder in vision + transformer. + + As base-class of Encoder and Decoder in vision transformer. + Support customization such as specifying different kind + of `transformer_layer` in `transformer_coder`. + + Args: + transformerlayer (list[obj:`mmcv.ConfigDict`] | + obj:`mmcv.ConfigDict`): Config of transformerlayer + in TransformerCoder. If it is obj:`mmcv.ConfigDict`, + it would be repeated `num_layer` times to a + list[`mmcv.ConfigDict`]. Default: None. + num_layers (int): The number of `TransformerLayer`. Default: None. + init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. + Default: None. + """ + + def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): + super(TransformerLayerSequence, self).__init__(init_cfg) + if isinstance(transformerlayers, dict): + transformerlayers = [ + copy.deepcopy(transformerlayers) for _ in range(num_layers) + ] + else: + assert isinstance(transformerlayers, list) and \ + len(transformerlayers) == num_layers + self.num_layers = num_layers + self.layers = ModuleList() + for i in range(num_layers): + self.layers.append(build_transformer_layer(transformerlayers[i])) + self.embed_dims = self.layers[0].embed_dims + self.pre_norm = self.layers[0].pre_norm + + def forward(self, + query, + key, + value, + query_pos=None, + key_pos=None, + attn_masks=None, + query_key_padding_mask=None, + key_padding_mask=None, + **kwargs): + """Forward function for `TransformerCoder`. + + Args: + query (Tensor): Input query with shape + `(num_queries, bs, embed_dims)`. + key (Tensor): The key tensor with shape + `(num_keys, bs, embed_dims)`. + value (Tensor): The value tensor with shape + `(num_keys, bs, embed_dims)`. + query_pos (Tensor): The positional encoding for `query`. + Default: None. + key_pos (Tensor): The positional encoding for `key`. + Default: None. + attn_masks (List[Tensor], optional): Each element is 2D Tensor + which is used in calculation of corresponding attention in + operation_order. Default: None. + query_key_padding_mask (Tensor): ByteTensor for `query`, with + shape [bs, num_queries]. Only used in self-attention + Default: None. + key_padding_mask (Tensor): ByteTensor for `query`, with + shape [bs, num_keys]. Default: None. + + Returns: + Tensor: results with shape [num_queries, bs, embed_dims]. + """ + for layer in self.layers: + query = layer( + query, + key, + value, + query_pos=query_pos, + key_pos=key_pos, + attn_masks=attn_masks, + query_key_padding_mask=query_key_padding_mask, + key_padding_mask=key_padding_mask, + **kwargs) + return query diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/upsample.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/upsample.py new file mode 100644 index 0000000000000000000000000000000000000000..a1a353767d0ce8518f0d7289bed10dba0178ed12 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/upsample.py @@ -0,0 +1,84 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch.nn as nn +import torch.nn.functional as F + +from ..utils import xavier_init +from .registry import UPSAMPLE_LAYERS + +UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) +UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) + + +@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') +class PixelShufflePack(nn.Module): + """Pixel Shuffle upsample layer. + + This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to + achieve a simple upsampling with pixel shuffle. + + Args: + in_channels (int): Number of input channels. + out_channels (int): Number of output channels. + scale_factor (int): Upsample ratio. + upsample_kernel (int): Kernel size of the conv layer to expand the + channels. + """ + + def __init__(self, in_channels, out_channels, scale_factor, + upsample_kernel): + super(PixelShufflePack, self).__init__() + self.in_channels = in_channels + self.out_channels = out_channels + self.scale_factor = scale_factor + self.upsample_kernel = upsample_kernel + self.upsample_conv = nn.Conv2d( + self.in_channels, + self.out_channels * scale_factor * scale_factor, + self.upsample_kernel, + padding=(self.upsample_kernel - 1) // 2) + self.init_weights() + + def init_weights(self): + xavier_init(self.upsample_conv, distribution='uniform') + + def forward(self, x): + x = self.upsample_conv(x) + x = F.pixel_shuffle(x, self.scale_factor) + return x + + +def build_upsample_layer(cfg, *args, **kwargs): + """Build upsample layer. + + Args: + cfg (dict): The upsample layer config, which should contain: + + - type (str): Layer type. + - scale_factor (int): Upsample ratio, which is not applicable to + deconv. + - layer args: Args needed to instantiate a upsample layer. + args (argument list): Arguments passed to the ``__init__`` + method of the corresponding conv layer. + kwargs (keyword arguments): Keyword arguments passed to the + ``__init__`` method of the corresponding conv layer. + + Returns: + nn.Module: Created upsample layer. + """ + if not isinstance(cfg, dict): + raise TypeError(f'cfg must be a dict, but got {type(cfg)}') + if 'type' not in cfg: + raise KeyError( + f'the cfg dict must contain the key "type", but got {cfg}') + cfg_ = cfg.copy() + + layer_type = cfg_.pop('type') + if layer_type not in UPSAMPLE_LAYERS: + raise KeyError(f'Unrecognized upsample type {layer_type}') + else: + upsample = UPSAMPLE_LAYERS.get(layer_type) + + if upsample is nn.Upsample: + cfg_['mode'] = layer_type + layer = upsample(*args, **kwargs, **cfg_) + return layer diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/wrappers.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/wrappers.py new file mode 100644 index 0000000000000000000000000000000000000000..8aebf67bf52355a513f21756ee74fe510902d075 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/bricks/wrappers.py @@ -0,0 +1,180 @@ +# Copyright (c) OpenMMLab. All rights reserved. +r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 + +Wrap some nn modules to support empty tensor input. Currently, these wrappers +are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask +heads are trained on only positive RoIs. +""" +import math + +import torch +import torch.nn as nn +from torch.nn.modules.utils import _pair, _triple + +from .registry import CONV_LAYERS, UPSAMPLE_LAYERS + +if torch.__version__ == 'parrots': + TORCH_VERSION = torch.__version__ +else: + # torch.__version__ could be 1.3.1+cu92, we only need the first two + # for comparison + TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) + + +def obsolete_torch_version(torch_version, version_threshold): + return torch_version == 'parrots' or torch_version <= version_threshold + + +class NewEmptyTensorOp(torch.autograd.Function): + + @staticmethod + def forward(ctx, x, new_shape): + ctx.shape = x.shape + return x.new_empty(new_shape) + + @staticmethod + def backward(ctx, grad): + shape = ctx.shape + return NewEmptyTensorOp.apply(grad, shape), None + + +@CONV_LAYERS.register_module('Conv', force=True) +class Conv2d(nn.Conv2d): + + def forward(self, x): + if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): + out_shape = [x.shape[0], self.out_channels] + for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, + self.padding, self.stride, self.dilation): + o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 + out_shape.append(o) + empty = NewEmptyTensorOp.apply(x, out_shape) + if self.training: + # produce dummy gradient to avoid DDP warning. + dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 + return empty + dummy + else: + return empty + + return super().forward(x) + + +@CONV_LAYERS.register_module('Conv3d', force=True) +class Conv3d(nn.Conv3d): + + def forward(self, x): + if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): + out_shape = [x.shape[0], self.out_channels] + for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, + self.padding, self.stride, self.dilation): + o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 + out_shape.append(o) + empty = NewEmptyTensorOp.apply(x, out_shape) + if self.training: + # produce dummy gradient to avoid DDP warning. + dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 + return empty + dummy + else: + return empty + + return super().forward(x) + + +@CONV_LAYERS.register_module() +@CONV_LAYERS.register_module('deconv') +@UPSAMPLE_LAYERS.register_module('deconv', force=True) +class ConvTranspose2d(nn.ConvTranspose2d): + + def forward(self, x): + if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): + out_shape = [x.shape[0], self.out_channels] + for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, + self.padding, self.stride, + self.dilation, self.output_padding): + out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) + empty = NewEmptyTensorOp.apply(x, out_shape) + if self.training: + # produce dummy gradient to avoid DDP warning. + dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 + return empty + dummy + else: + return empty + + return super().forward(x) + + +@CONV_LAYERS.register_module() +@CONV_LAYERS.register_module('deconv3d') +@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) +class ConvTranspose3d(nn.ConvTranspose3d): + + def forward(self, x): + if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): + out_shape = [x.shape[0], self.out_channels] + for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, + self.padding, self.stride, + self.dilation, self.output_padding): + out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) + empty = NewEmptyTensorOp.apply(x, out_shape) + if self.training: + # produce dummy gradient to avoid DDP warning. + dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 + return empty + dummy + else: + return empty + + return super().forward(x) + + +class MaxPool2d(nn.MaxPool2d): + + def forward(self, x): + # PyTorch 1.9 does not support empty tensor inference yet + if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): + out_shape = list(x.shape[:2]) + for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), + _pair(self.padding), _pair(self.stride), + _pair(self.dilation)): + o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 + o = math.ceil(o) if self.ceil_mode else math.floor(o) + out_shape.append(o) + empty = NewEmptyTensorOp.apply(x, out_shape) + return empty + + return super().forward(x) + + +class MaxPool3d(nn.MaxPool3d): + + def forward(self, x): + # PyTorch 1.9 does not support empty tensor inference yet + if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): + out_shape = list(x.shape[:2]) + for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), + _triple(self.padding), + _triple(self.stride), + _triple(self.dilation)): + o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 + o = math.ceil(o) if self.ceil_mode else math.floor(o) + out_shape.append(o) + empty = NewEmptyTensorOp.apply(x, out_shape) + return empty + + return super().forward(x) + + +class Linear(torch.nn.Linear): + + def forward(self, x): + # empty tensor forward of Linear layer is supported in Pytorch 1.6 + if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): + out_shape = [x.shape[0], self.out_features] + empty = NewEmptyTensorOp.apply(x, out_shape) + if self.training: + # produce dummy gradient to avoid DDP warning. + dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 + return empty + dummy + else: + return empty + + return super().forward(x) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/builder.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/builder.py new file mode 100644 index 0000000000000000000000000000000000000000..7567316c566bd3aca6d8f65a84b00e9e890948a7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/builder.py @@ -0,0 +1,30 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from ..runner import Sequential +from ..utils import Registry, build_from_cfg + + +def build_model_from_cfg(cfg, registry, default_args=None): + """Build a PyTorch model from config dict(s). Different from + ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. + + Args: + cfg (dict, list[dict]): The config of modules, is is either a config + dict or a list of config dicts. If cfg is a list, a + the built modules will be wrapped with ``nn.Sequential``. + registry (:obj:`Registry`): A registry the module belongs to. + default_args (dict, optional): Default arguments to build the module. + Defaults to None. + + Returns: + nn.Module: A built nn module. + """ + if isinstance(cfg, list): + modules = [ + build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg + ] + return Sequential(*modules) + else: + return build_from_cfg(cfg, registry, default_args) + + +MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/resnet.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/resnet.py new file mode 100644 index 0000000000000000000000000000000000000000..1cb3ac057ee2d52c46fc94685b5d4e698aad8d5f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/resnet.py @@ -0,0 +1,316 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import logging + +import torch.nn as nn +import torch.utils.checkpoint as cp + +from .utils import constant_init, kaiming_init + + +def conv3x3(in_planes, out_planes, stride=1, dilation=1): + """3x3 convolution with padding.""" + return nn.Conv2d( + in_planes, + out_planes, + kernel_size=3, + stride=stride, + padding=dilation, + dilation=dilation, + bias=False) + + +class BasicBlock(nn.Module): + expansion = 1 + + def __init__(self, + inplanes, + planes, + stride=1, + dilation=1, + downsample=None, + style='pytorch', + with_cp=False): + super(BasicBlock, self).__init__() + assert style in ['pytorch', 'caffe'] + self.conv1 = conv3x3(inplanes, planes, stride, dilation) + self.bn1 = nn.BatchNorm2d(planes) + self.relu = nn.ReLU(inplace=True) + self.conv2 = conv3x3(planes, planes) + self.bn2 = nn.BatchNorm2d(planes) + self.downsample = downsample + self.stride = stride + self.dilation = dilation + assert not with_cp + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class Bottleneck(nn.Module): + expansion = 4 + + def __init__(self, + inplanes, + planes, + stride=1, + dilation=1, + downsample=None, + style='pytorch', + with_cp=False): + """Bottleneck block. + + If style is "pytorch", the stride-two layer is the 3x3 conv layer, if + it is "caffe", the stride-two layer is the first 1x1 conv layer. + """ + super(Bottleneck, self).__init__() + assert style in ['pytorch', 'caffe'] + if style == 'pytorch': + conv1_stride = 1 + conv2_stride = stride + else: + conv1_stride = stride + conv2_stride = 1 + self.conv1 = nn.Conv2d( + inplanes, planes, kernel_size=1, stride=conv1_stride, bias=False) + self.conv2 = nn.Conv2d( + planes, + planes, + kernel_size=3, + stride=conv2_stride, + padding=dilation, + dilation=dilation, + bias=False) + + self.bn1 = nn.BatchNorm2d(planes) + self.bn2 = nn.BatchNorm2d(planes) + self.conv3 = nn.Conv2d( + planes, planes * self.expansion, kernel_size=1, bias=False) + self.bn3 = nn.BatchNorm2d(planes * self.expansion) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + self.dilation = dilation + self.with_cp = with_cp + + def forward(self, x): + + def _inner_forward(x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + + return out + + if self.with_cp and x.requires_grad: + out = cp.checkpoint(_inner_forward, x) + else: + out = _inner_forward(x) + + out = self.relu(out) + + return out + + +def make_res_layer(block, + inplanes, + planes, + blocks, + stride=1, + dilation=1, + style='pytorch', + with_cp=False): + downsample = None + if stride != 1 or inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2d( + inplanes, + planes * block.expansion, + kernel_size=1, + stride=stride, + bias=False), + nn.BatchNorm2d(planes * block.expansion), + ) + + layers = [] + layers.append( + block( + inplanes, + planes, + stride, + dilation, + downsample, + style=style, + with_cp=with_cp)) + inplanes = planes * block.expansion + for _ in range(1, blocks): + layers.append( + block(inplanes, planes, 1, dilation, style=style, with_cp=with_cp)) + + return nn.Sequential(*layers) + + +class ResNet(nn.Module): + """ResNet backbone. + + Args: + depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. + num_stages (int): Resnet stages, normally 4. + strides (Sequence[int]): Strides of the first block of each stage. + dilations (Sequence[int]): Dilation of each stage. + out_indices (Sequence[int]): Output from which stages. + style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two + layer is the 3x3 conv layer, otherwise the stride-two layer is + the first 1x1 conv layer. + frozen_stages (int): Stages to be frozen (all param fixed). -1 means + not freezing any parameters. + bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze + running stats (mean and var). + bn_frozen (bool): Whether to freeze weight and bias of BN layers. + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. + """ + + arch_settings = { + 18: (BasicBlock, (2, 2, 2, 2)), + 34: (BasicBlock, (3, 4, 6, 3)), + 50: (Bottleneck, (3, 4, 6, 3)), + 101: (Bottleneck, (3, 4, 23, 3)), + 152: (Bottleneck, (3, 8, 36, 3)) + } + + def __init__(self, + depth, + num_stages=4, + strides=(1, 2, 2, 2), + dilations=(1, 1, 1, 1), + out_indices=(0, 1, 2, 3), + style='pytorch', + frozen_stages=-1, + bn_eval=True, + bn_frozen=False, + with_cp=False): + super(ResNet, self).__init__() + if depth not in self.arch_settings: + raise KeyError(f'invalid depth {depth} for resnet') + assert num_stages >= 1 and num_stages <= 4 + block, stage_blocks = self.arch_settings[depth] + stage_blocks = stage_blocks[:num_stages] + assert len(strides) == len(dilations) == num_stages + assert max(out_indices) < num_stages + + self.out_indices = out_indices + self.style = style + self.frozen_stages = frozen_stages + self.bn_eval = bn_eval + self.bn_frozen = bn_frozen + self.with_cp = with_cp + + self.inplanes = 64 + self.conv1 = nn.Conv2d( + 3, 64, kernel_size=7, stride=2, padding=3, bias=False) + self.bn1 = nn.BatchNorm2d(64) + self.relu = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + + self.res_layers = [] + for i, num_blocks in enumerate(stage_blocks): + stride = strides[i] + dilation = dilations[i] + planes = 64 * 2**i + res_layer = make_res_layer( + block, + self.inplanes, + planes, + num_blocks, + stride=stride, + dilation=dilation, + style=self.style, + with_cp=with_cp) + self.inplanes = planes * block.expansion + layer_name = f'layer{i + 1}' + self.add_module(layer_name, res_layer) + self.res_layers.append(layer_name) + + self.feat_dim = block.expansion * 64 * 2**(len(stage_blocks) - 1) + + def init_weights(self, pretrained=None): + if isinstance(pretrained, str): + logger = logging.getLogger() + from ..runner import load_checkpoint + load_checkpoint(self, pretrained, strict=False, logger=logger) + elif pretrained is None: + for m in self.modules(): + if isinstance(m, nn.Conv2d): + kaiming_init(m) + elif isinstance(m, nn.BatchNorm2d): + constant_init(m, 1) + else: + raise TypeError('pretrained must be a str or None') + + def forward(self, x): + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + x = self.maxpool(x) + outs = [] + for i, layer_name in enumerate(self.res_layers): + res_layer = getattr(self, layer_name) + x = res_layer(x) + if i in self.out_indices: + outs.append(x) + if len(outs) == 1: + return outs[0] + else: + return tuple(outs) + + def train(self, mode=True): + super(ResNet, self).train(mode) + if self.bn_eval: + for m in self.modules(): + if isinstance(m, nn.BatchNorm2d): + m.eval() + if self.bn_frozen: + for params in m.parameters(): + params.requires_grad = False + if mode and self.frozen_stages >= 0: + for param in self.conv1.parameters(): + param.requires_grad = False + for param in self.bn1.parameters(): + param.requires_grad = False + self.bn1.eval() + self.bn1.weight.requires_grad = False + self.bn1.bias.requires_grad = False + for i in range(1, self.frozen_stages + 1): + mod = getattr(self, f'layer{i}') + mod.eval() + for param in mod.parameters(): + param.requires_grad = False diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a263e31c1e3977712827ca229bbc04910b4e928e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/__init__.py @@ -0,0 +1,19 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .flops_counter import get_model_complexity_info +from .fuse_conv_bn import fuse_conv_bn +from .sync_bn import revert_sync_batchnorm +from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, + KaimingInit, NormalInit, PretrainedInit, + TruncNormalInit, UniformInit, XavierInit, + bias_init_with_prob, caffe2_xavier_init, + constant_init, initialize, kaiming_init, normal_init, + trunc_normal_init, uniform_init, xavier_init) + +__all__ = [ + 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', + 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', + 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', + 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', + 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', + 'Caffe2XavierInit', 'revert_sync_batchnorm' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/flops_counter.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/flops_counter.py new file mode 100644 index 0000000000000000000000000000000000000000..104240bfa524af727782ceb781147c5815529ee6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/flops_counter.py @@ -0,0 +1,599 @@ +# Modified from flops-counter.pytorch by Vladislav Sovrasov +# original repo: https://github.com/sovrasov/flops-counter.pytorch + +# MIT License + +# Copyright (c) 2018 Vladislav Sovrasov + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import sys +from functools import partial + +import numpy as np +import torch +import torch.nn as nn + +import annotator.mmpkg.mmcv as mmcv + + +def get_model_complexity_info(model, + input_shape, + print_per_layer_stat=True, + as_strings=True, + input_constructor=None, + flush=False, + ost=sys.stdout): + """Get complexity information of a model. + + This method can calculate FLOPs and parameter counts of a model with + corresponding input shape. It can also print complexity information for + each layer in a model. + + Supported layers are listed as below: + - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. + - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, ``nn.LeakyReLU``, + ``nn.ReLU6``. + - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, + ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, + ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, + ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, + ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. + - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, + ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, + ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. + - Linear: ``nn.Linear``. + - Deconvolution: ``nn.ConvTranspose2d``. + - Upsample: ``nn.Upsample``. + + Args: + model (nn.Module): The model for complexity calculation. + input_shape (tuple): Input shape used for calculation. + print_per_layer_stat (bool): Whether to print complexity information + for each layer in a model. Default: True. + as_strings (bool): Output FLOPs and params counts in a string form. + Default: True. + input_constructor (None | callable): If specified, it takes a callable + method that generates input. otherwise, it will generate a random + tensor with input shape to calculate FLOPs. Default: None. + flush (bool): same as that in :func:`print`. Default: False. + ost (stream): same as ``file`` param in :func:`print`. + Default: sys.stdout. + + Returns: + tuple[float | str]: If ``as_strings`` is set to True, it will return + FLOPs and parameter counts in a string format. otherwise, it will + return those in a float number format. + """ + assert type(input_shape) is tuple + assert len(input_shape) >= 1 + assert isinstance(model, nn.Module) + flops_model = add_flops_counting_methods(model) + flops_model.eval() + flops_model.start_flops_count() + if input_constructor: + input = input_constructor(input_shape) + _ = flops_model(**input) + else: + try: + batch = torch.ones(()).new_empty( + (1, *input_shape), + dtype=next(flops_model.parameters()).dtype, + device=next(flops_model.parameters()).device) + except StopIteration: + # Avoid StopIteration for models which have no parameters, + # like `nn.Relu()`, `nn.AvgPool2d`, etc. + batch = torch.ones(()).new_empty((1, *input_shape)) + + _ = flops_model(batch) + + flops_count, params_count = flops_model.compute_average_flops_cost() + if print_per_layer_stat: + print_model_with_flops( + flops_model, flops_count, params_count, ost=ost, flush=flush) + flops_model.stop_flops_count() + + if as_strings: + return flops_to_string(flops_count), params_to_string(params_count) + + return flops_count, params_count + + +def flops_to_string(flops, units='GFLOPs', precision=2): + """Convert FLOPs number into a string. + + Note that Here we take a multiply-add counts as one FLOP. + + Args: + flops (float): FLOPs number to be converted. + units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', + 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically + choose the most suitable unit for FLOPs. Default: 'GFLOPs'. + precision (int): Digit number after the decimal point. Default: 2. + + Returns: + str: The converted FLOPs number with units. + + Examples: + >>> flops_to_string(1e9) + '1.0 GFLOPs' + >>> flops_to_string(2e5, 'MFLOPs') + '0.2 MFLOPs' + >>> flops_to_string(3e-9, None) + '3e-09 FLOPs' + """ + if units is None: + if flops // 10**9 > 0: + return str(round(flops / 10.**9, precision)) + ' GFLOPs' + elif flops // 10**6 > 0: + return str(round(flops / 10.**6, precision)) + ' MFLOPs' + elif flops // 10**3 > 0: + return str(round(flops / 10.**3, precision)) + ' KFLOPs' + else: + return str(flops) + ' FLOPs' + else: + if units == 'GFLOPs': + return str(round(flops / 10.**9, precision)) + ' ' + units + elif units == 'MFLOPs': + return str(round(flops / 10.**6, precision)) + ' ' + units + elif units == 'KFLOPs': + return str(round(flops / 10.**3, precision)) + ' ' + units + else: + return str(flops) + ' FLOPs' + + +def params_to_string(num_params, units=None, precision=2): + """Convert parameter number into a string. + + Args: + num_params (float): Parameter number to be converted. + units (str | None): Converted FLOPs units. Options are None, 'M', + 'K' and ''. If set to None, it will automatically choose the most + suitable unit for Parameter number. Default: None. + precision (int): Digit number after the decimal point. Default: 2. + + Returns: + str: The converted parameter number with units. + + Examples: + >>> params_to_string(1e9) + '1000.0 M' + >>> params_to_string(2e5) + '200.0 k' + >>> params_to_string(3e-9) + '3e-09' + """ + if units is None: + if num_params // 10**6 > 0: + return str(round(num_params / 10**6, precision)) + ' M' + elif num_params // 10**3: + return str(round(num_params / 10**3, precision)) + ' k' + else: + return str(num_params) + else: + if units == 'M': + return str(round(num_params / 10.**6, precision)) + ' ' + units + elif units == 'K': + return str(round(num_params / 10.**3, precision)) + ' ' + units + else: + return str(num_params) + + +def print_model_with_flops(model, + total_flops, + total_params, + units='GFLOPs', + precision=3, + ost=sys.stdout, + flush=False): + """Print a model with FLOPs for each layer. + + Args: + model (nn.Module): The model to be printed. + total_flops (float): Total FLOPs of the model. + total_params (float): Total parameter counts of the model. + units (str | None): Converted FLOPs units. Default: 'GFLOPs'. + precision (int): Digit number after the decimal point. Default: 3. + ost (stream): same as `file` param in :func:`print`. + Default: sys.stdout. + flush (bool): same as that in :func:`print`. Default: False. + + Example: + >>> class ExampleModel(nn.Module): + + >>> def __init__(self): + >>> super().__init__() + >>> self.conv1 = nn.Conv2d(3, 8, 3) + >>> self.conv2 = nn.Conv2d(8, 256, 3) + >>> self.conv3 = nn.Conv2d(256, 8, 3) + >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) + >>> self.flatten = nn.Flatten() + >>> self.fc = nn.Linear(8, 1) + + >>> def forward(self, x): + >>> x = self.conv1(x) + >>> x = self.conv2(x) + >>> x = self.conv3(x) + >>> x = self.avg_pool(x) + >>> x = self.flatten(x) + >>> x = self.fc(x) + >>> return x + + >>> model = ExampleModel() + >>> x = (3, 16, 16) + to print the complexity information state for each layer, you can use + >>> get_model_complexity_info(model, x) + or directly use + >>> print_model_with_flops(model, 4579784.0, 37361) + ExampleModel( + 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, + (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 + (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) + (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) + (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) + (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) + (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) + ) + """ + + def accumulate_params(self): + if is_supported_instance(self): + return self.__params__ + else: + sum = 0 + for m in self.children(): + sum += m.accumulate_params() + return sum + + def accumulate_flops(self): + if is_supported_instance(self): + return self.__flops__ / model.__batch_counter__ + else: + sum = 0 + for m in self.children(): + sum += m.accumulate_flops() + return sum + + def flops_repr(self): + accumulated_num_params = self.accumulate_params() + accumulated_flops_cost = self.accumulate_flops() + return ', '.join([ + params_to_string( + accumulated_num_params, units='M', precision=precision), + '{:.3%} Params'.format(accumulated_num_params / total_params), + flops_to_string( + accumulated_flops_cost, units=units, precision=precision), + '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), + self.original_extra_repr() + ]) + + def add_extra_repr(m): + m.accumulate_flops = accumulate_flops.__get__(m) + m.accumulate_params = accumulate_params.__get__(m) + flops_extra_repr = flops_repr.__get__(m) + if m.extra_repr != flops_extra_repr: + m.original_extra_repr = m.extra_repr + m.extra_repr = flops_extra_repr + assert m.extra_repr != m.original_extra_repr + + def del_extra_repr(m): + if hasattr(m, 'original_extra_repr'): + m.extra_repr = m.original_extra_repr + del m.original_extra_repr + if hasattr(m, 'accumulate_flops'): + del m.accumulate_flops + + model.apply(add_extra_repr) + print(model, file=ost, flush=flush) + model.apply(del_extra_repr) + + +def get_model_parameters_number(model): + """Calculate parameter number of a model. + + Args: + model (nn.module): The model for parameter number calculation. + + Returns: + float: Parameter number of the model. + """ + num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) + return num_params + + +def add_flops_counting_methods(net_main_module): + # adding additional methods to the existing module object, + # this is done this way so that each function has access to self object + net_main_module.start_flops_count = start_flops_count.__get__( + net_main_module) + net_main_module.stop_flops_count = stop_flops_count.__get__( + net_main_module) + net_main_module.reset_flops_count = reset_flops_count.__get__( + net_main_module) + net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 + net_main_module) + + net_main_module.reset_flops_count() + + return net_main_module + + +def compute_average_flops_cost(self): + """Compute average FLOPs cost. + + A method to compute average FLOPs cost, which will be available after + `add_flops_counting_methods()` is called on a desired net object. + + Returns: + float: Current mean flops consumption per image. + """ + batches_count = self.__batch_counter__ + flops_sum = 0 + for module in self.modules(): + if is_supported_instance(module): + flops_sum += module.__flops__ + params_sum = get_model_parameters_number(self) + return flops_sum / batches_count, params_sum + + +def start_flops_count(self): + """Activate the computation of mean flops consumption per image. + + A method to activate the computation of mean flops consumption per image. + which will be available after ``add_flops_counting_methods()`` is called on + a desired net object. It should be called before running the network. + """ + add_batch_counter_hook_function(self) + + def add_flops_counter_hook_function(module): + if is_supported_instance(module): + if hasattr(module, '__flops_handle__'): + return + + else: + handle = module.register_forward_hook( + get_modules_mapping()[type(module)]) + + module.__flops_handle__ = handle + + self.apply(partial(add_flops_counter_hook_function)) + + +def stop_flops_count(self): + """Stop computing the mean flops consumption per image. + + A method to stop computing the mean flops consumption per image, which will + be available after ``add_flops_counting_methods()`` is called on a desired + net object. It can be called to pause the computation whenever. + """ + remove_batch_counter_hook_function(self) + self.apply(remove_flops_counter_hook_function) + + +def reset_flops_count(self): + """Reset statistics computed so far. + + A method to Reset computed statistics, which will be available after + `add_flops_counting_methods()` is called on a desired net object. + """ + add_batch_counter_variables_or_reset(self) + self.apply(add_flops_counter_variable_or_reset) + + +# ---- Internal functions +def empty_flops_counter_hook(module, input, output): + module.__flops__ += 0 + + +def upsample_flops_counter_hook(module, input, output): + output_size = output[0] + batch_size = output_size.shape[0] + output_elements_count = batch_size + for val in output_size.shape[1:]: + output_elements_count *= val + module.__flops__ += int(output_elements_count) + + +def relu_flops_counter_hook(module, input, output): + active_elements_count = output.numel() + module.__flops__ += int(active_elements_count) + + +def linear_flops_counter_hook(module, input, output): + input = input[0] + output_last_dim = output.shape[ + -1] # pytorch checks dimensions, so here we don't care much + module.__flops__ += int(np.prod(input.shape) * output_last_dim) + + +def pool_flops_counter_hook(module, input, output): + input = input[0] + module.__flops__ += int(np.prod(input.shape)) + + +def norm_flops_counter_hook(module, input, output): + input = input[0] + + batch_flops = np.prod(input.shape) + if (getattr(module, 'affine', False) + or getattr(module, 'elementwise_affine', False)): + batch_flops *= 2 + module.__flops__ += int(batch_flops) + + +def deconv_flops_counter_hook(conv_module, input, output): + # Can have multiple inputs, getting the first one + input = input[0] + + batch_size = input.shape[0] + input_height, input_width = input.shape[2:] + + kernel_height, kernel_width = conv_module.kernel_size + in_channels = conv_module.in_channels + out_channels = conv_module.out_channels + groups = conv_module.groups + + filters_per_channel = out_channels // groups + conv_per_position_flops = ( + kernel_height * kernel_width * in_channels * filters_per_channel) + + active_elements_count = batch_size * input_height * input_width + overall_conv_flops = conv_per_position_flops * active_elements_count + bias_flops = 0 + if conv_module.bias is not None: + output_height, output_width = output.shape[2:] + bias_flops = out_channels * batch_size * output_height * output_height + overall_flops = overall_conv_flops + bias_flops + + conv_module.__flops__ += int(overall_flops) + + +def conv_flops_counter_hook(conv_module, input, output): + # Can have multiple inputs, getting the first one + input = input[0] + + batch_size = input.shape[0] + output_dims = list(output.shape[2:]) + + kernel_dims = list(conv_module.kernel_size) + in_channels = conv_module.in_channels + out_channels = conv_module.out_channels + groups = conv_module.groups + + filters_per_channel = out_channels // groups + conv_per_position_flops = int( + np.prod(kernel_dims)) * in_channels * filters_per_channel + + active_elements_count = batch_size * int(np.prod(output_dims)) + + overall_conv_flops = conv_per_position_flops * active_elements_count + + bias_flops = 0 + + if conv_module.bias is not None: + + bias_flops = out_channels * active_elements_count + + overall_flops = overall_conv_flops + bias_flops + + conv_module.__flops__ += int(overall_flops) + + +def batch_counter_hook(module, input, output): + batch_size = 1 + if len(input) > 0: + # Can have multiple inputs, getting the first one + input = input[0] + batch_size = len(input) + else: + pass + print('Warning! No positional inputs found for a module, ' + 'assuming batch size is 1.') + module.__batch_counter__ += batch_size + + +def add_batch_counter_variables_or_reset(module): + + module.__batch_counter__ = 0 + + +def add_batch_counter_hook_function(module): + if hasattr(module, '__batch_counter_handle__'): + return + + handle = module.register_forward_hook(batch_counter_hook) + module.__batch_counter_handle__ = handle + + +def remove_batch_counter_hook_function(module): + if hasattr(module, '__batch_counter_handle__'): + module.__batch_counter_handle__.remove() + del module.__batch_counter_handle__ + + +def add_flops_counter_variable_or_reset(module): + if is_supported_instance(module): + if hasattr(module, '__flops__') or hasattr(module, '__params__'): + print('Warning: variables __flops__ or __params__ are already ' + 'defined for the module' + type(module).__name__ + + ' ptflops can affect your code!') + module.__flops__ = 0 + module.__params__ = get_model_parameters_number(module) + + +def is_supported_instance(module): + if type(module) in get_modules_mapping(): + return True + return False + + +def remove_flops_counter_hook_function(module): + if is_supported_instance(module): + if hasattr(module, '__flops_handle__'): + module.__flops_handle__.remove() + del module.__flops_handle__ + + +def get_modules_mapping(): + return { + # convolutions + nn.Conv1d: conv_flops_counter_hook, + nn.Conv2d: conv_flops_counter_hook, + mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, + nn.Conv3d: conv_flops_counter_hook, + mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, + # activations + nn.ReLU: relu_flops_counter_hook, + nn.PReLU: relu_flops_counter_hook, + nn.ELU: relu_flops_counter_hook, + nn.LeakyReLU: relu_flops_counter_hook, + nn.ReLU6: relu_flops_counter_hook, + # poolings + nn.MaxPool1d: pool_flops_counter_hook, + nn.AvgPool1d: pool_flops_counter_hook, + nn.AvgPool2d: pool_flops_counter_hook, + nn.MaxPool2d: pool_flops_counter_hook, + mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, + nn.MaxPool3d: pool_flops_counter_hook, + mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, + nn.AvgPool3d: pool_flops_counter_hook, + nn.AdaptiveMaxPool1d: pool_flops_counter_hook, + nn.AdaptiveAvgPool1d: pool_flops_counter_hook, + nn.AdaptiveMaxPool2d: pool_flops_counter_hook, + nn.AdaptiveAvgPool2d: pool_flops_counter_hook, + nn.AdaptiveMaxPool3d: pool_flops_counter_hook, + nn.AdaptiveAvgPool3d: pool_flops_counter_hook, + # normalizations + nn.BatchNorm1d: norm_flops_counter_hook, + nn.BatchNorm2d: norm_flops_counter_hook, + nn.BatchNorm3d: norm_flops_counter_hook, + nn.GroupNorm: norm_flops_counter_hook, + nn.InstanceNorm1d: norm_flops_counter_hook, + nn.InstanceNorm2d: norm_flops_counter_hook, + nn.InstanceNorm3d: norm_flops_counter_hook, + nn.LayerNorm: norm_flops_counter_hook, + # FC + nn.Linear: linear_flops_counter_hook, + mmcv.cnn.bricks.Linear: linear_flops_counter_hook, + # Upscale + nn.Upsample: upsample_flops_counter_hook, + # Deconvolution + nn.ConvTranspose2d: deconv_flops_counter_hook, + mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, + } diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/fuse_conv_bn.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/fuse_conv_bn.py new file mode 100644 index 0000000000000000000000000000000000000000..cb7076f80bf37f7931185bf0293ffcc1ce19c8ef --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/fuse_conv_bn.py @@ -0,0 +1,59 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.nn as nn + + +def _fuse_conv_bn(conv, bn): + """Fuse conv and bn into one module. + + Args: + conv (nn.Module): Conv to be fused. + bn (nn.Module): BN to be fused. + + Returns: + nn.Module: Fused module. + """ + conv_w = conv.weight + conv_b = conv.bias if conv.bias is not None else torch.zeros_like( + bn.running_mean) + + factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) + conv.weight = nn.Parameter(conv_w * + factor.reshape([conv.out_channels, 1, 1, 1])) + conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) + return conv + + +def fuse_conv_bn(module): + """Recursively fuse conv and bn in a module. + + During inference, the functionary of batch norm layers is turned off + but only the mean and var alone channels are used, which exposes the + chance to fuse it with the preceding conv layers to save computations and + simplify network structures. + + Args: + module (nn.Module): Module to be fused. + + Returns: + nn.Module: Fused module. + """ + last_conv = None + last_conv_name = None + + for name, child in module.named_children(): + if isinstance(child, + (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): + if last_conv is None: # only fuse BN that is after Conv + continue + fused_conv = _fuse_conv_bn(last_conv, child) + module._modules[last_conv_name] = fused_conv + # To reduce changes, set BN as Identity instead of deleting it. + module._modules[name] = nn.Identity() + last_conv = None + elif isinstance(child, nn.Conv2d): + last_conv = child + last_conv_name = name + else: + fuse_conv_bn(child) + return module diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/sync_bn.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/sync_bn.py new file mode 100644 index 0000000000000000000000000000000000000000..c0dbcb1b167ea0df690c0f47fe0217a3454b5d59 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/sync_bn.py @@ -0,0 +1,59 @@ +import torch + +import annotator.mmpkg.mmcv as mmcv + + +class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): + """A general BatchNorm layer without input dimension check. + + Reproduced from @kapily's work: + (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) + The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc + is `_check_input_dim` that is designed for tensor sanity checks. + The check has been bypassed in this class for the convenience of converting + SyncBatchNorm. + """ + + def _check_input_dim(self, input): + return + + +def revert_sync_batchnorm(module): + """Helper function to convert all `SyncBatchNorm` (SyncBN) and + `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to + `BatchNormXd` layers. + + Adapted from @kapily's work: + (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) + + Args: + module (nn.Module): The module containing `SyncBatchNorm` layers. + + Returns: + module_output: The converted module with `BatchNormXd` layers. + """ + module_output = module + module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] + if hasattr(mmcv, 'ops'): + module_checklist.append(mmcv.ops.SyncBatchNorm) + if isinstance(module, tuple(module_checklist)): + module_output = _BatchNormXd(module.num_features, module.eps, + module.momentum, module.affine, + module.track_running_stats) + if module.affine: + # no_grad() may not be needed here but + # just to be consistent with `convert_sync_batchnorm()` + with torch.no_grad(): + module_output.weight = module.weight + module_output.bias = module.bias + module_output.running_mean = module.running_mean + module_output.running_var = module.running_var + module_output.num_batches_tracked = module.num_batches_tracked + module_output.training = module.training + # qconfig exists in quantized models + if hasattr(module, 'qconfig'): + module_output.qconfig = module.qconfig + for name, child in module.named_children(): + module_output.add_module(name, revert_sync_batchnorm(child)) + del module + return module_output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/weight_init.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/weight_init.py new file mode 100644 index 0000000000000000000000000000000000000000..096d0ddcccbec84675f0771cb546d0fa003417e7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/utils/weight_init.py @@ -0,0 +1,684 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import copy +import math +import warnings + +import numpy as np +import torch +import torch.nn as nn +from torch import Tensor + +from annotator.mmpkg.mmcv.utils import Registry, build_from_cfg, get_logger, print_log + +INITIALIZERS = Registry('initializer') + + +def update_init_info(module, init_info): + """Update the `_params_init_info` in the module if the value of parameters + are changed. + + Args: + module (obj:`nn.Module`): The module of PyTorch with a user-defined + attribute `_params_init_info` which records the initialization + information. + init_info (str): The string that describes the initialization. + """ + assert hasattr( + module, + '_params_init_info'), f'Can not find `_params_init_info` in {module}' + for name, param in module.named_parameters(): + + assert param in module._params_init_info, ( + f'Find a new :obj:`Parameter` ' + f'named `{name}` during executing the ' + f'`init_weights` of ' + f'`{module.__class__.__name__}`. ' + f'Please do not add or ' + f'replace parameters during executing ' + f'the `init_weights`. ') + + # The parameter has been changed during executing the + # `init_weights` of module + mean_value = param.data.mean() + if module._params_init_info[param]['tmp_mean_value'] != mean_value: + module._params_init_info[param]['init_info'] = init_info + module._params_init_info[param]['tmp_mean_value'] = mean_value + + +def constant_init(module, val, bias=0): + if hasattr(module, 'weight') and module.weight is not None: + nn.init.constant_(module.weight, val) + if hasattr(module, 'bias') and module.bias is not None: + nn.init.constant_(module.bias, bias) + + +def xavier_init(module, gain=1, bias=0, distribution='normal'): + assert distribution in ['uniform', 'normal'] + if hasattr(module, 'weight') and module.weight is not None: + if distribution == 'uniform': + nn.init.xavier_uniform_(module.weight, gain=gain) + else: + nn.init.xavier_normal_(module.weight, gain=gain) + if hasattr(module, 'bias') and module.bias is not None: + nn.init.constant_(module.bias, bias) + + +def normal_init(module, mean=0, std=1, bias=0): + if hasattr(module, 'weight') and module.weight is not None: + nn.init.normal_(module.weight, mean, std) + if hasattr(module, 'bias') and module.bias is not None: + nn.init.constant_(module.bias, bias) + + +def trunc_normal_init(module: nn.Module, + mean: float = 0, + std: float = 1, + a: float = -2, + b: float = 2, + bias: float = 0) -> None: + if hasattr(module, 'weight') and module.weight is not None: + trunc_normal_(module.weight, mean, std, a, b) # type: ignore + if hasattr(module, 'bias') and module.bias is not None: + nn.init.constant_(module.bias, bias) # type: ignore + + +def uniform_init(module, a=0, b=1, bias=0): + if hasattr(module, 'weight') and module.weight is not None: + nn.init.uniform_(module.weight, a, b) + if hasattr(module, 'bias') and module.bias is not None: + nn.init.constant_(module.bias, bias) + + +def kaiming_init(module, + a=0, + mode='fan_out', + nonlinearity='relu', + bias=0, + distribution='normal'): + assert distribution in ['uniform', 'normal'] + if hasattr(module, 'weight') and module.weight is not None: + if distribution == 'uniform': + nn.init.kaiming_uniform_( + module.weight, a=a, mode=mode, nonlinearity=nonlinearity) + else: + nn.init.kaiming_normal_( + module.weight, a=a, mode=mode, nonlinearity=nonlinearity) + if hasattr(module, 'bias') and module.bias is not None: + nn.init.constant_(module.bias, bias) + + +def caffe2_xavier_init(module, bias=0): + # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch + # Acknowledgment to FAIR's internal code + kaiming_init( + module, + a=1, + mode='fan_in', + nonlinearity='leaky_relu', + bias=bias, + distribution='uniform') + + +def bias_init_with_prob(prior_prob): + """initialize conv/fc bias value according to a given probability value.""" + bias_init = float(-np.log((1 - prior_prob) / prior_prob)) + return bias_init + + +def _get_bases_name(m): + return [b.__name__ for b in m.__class__.__bases__] + + +class BaseInit(object): + + def __init__(self, *, bias=0, bias_prob=None, layer=None): + self.wholemodule = False + if not isinstance(bias, (int, float)): + raise TypeError(f'bias must be a number, but got a {type(bias)}') + + if bias_prob is not None: + if not isinstance(bias_prob, float): + raise TypeError(f'bias_prob type must be float, \ + but got {type(bias_prob)}') + + if layer is not None: + if not isinstance(layer, (str, list)): + raise TypeError(f'layer must be a str or a list of str, \ + but got a {type(layer)}') + else: + layer = [] + + if bias_prob is not None: + self.bias = bias_init_with_prob(bias_prob) + else: + self.bias = bias + self.layer = [layer] if isinstance(layer, str) else layer + + def _get_init_info(self): + info = f'{self.__class__.__name__}, bias={self.bias}' + return info + + +@INITIALIZERS.register_module(name='Constant') +class ConstantInit(BaseInit): + """Initialize module parameters with constant values. + + Args: + val (int | float): the value to fill the weights in the module with + bias (int | float): the value to fill the bias. Defaults to 0. + bias_prob (float, optional): the probability for bias initialization. + Defaults to None. + layer (str | list[str], optional): the layer will be initialized. + Defaults to None. + """ + + def __init__(self, val, **kwargs): + super().__init__(**kwargs) + self.val = val + + def __call__(self, module): + + def init(m): + if self.wholemodule: + constant_init(m, self.val, self.bias) + else: + layername = m.__class__.__name__ + basesname = _get_bases_name(m) + if len(set(self.layer) & set([layername] + basesname)): + constant_init(m, self.val, self.bias) + + module.apply(init) + if hasattr(module, '_params_init_info'): + update_init_info(module, init_info=self._get_init_info()) + + def _get_init_info(self): + info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' + return info + + +@INITIALIZERS.register_module(name='Xavier') +class XavierInit(BaseInit): + r"""Initialize module parameters with values according to the method + described in `Understanding the difficulty of training deep feedforward + neural networks - Glorot, X. & Bengio, Y. (2010). + `_ + + Args: + gain (int | float): an optional scaling factor. Defaults to 1. + bias (int | float): the value to fill the bias. Defaults to 0. + bias_prob (float, optional): the probability for bias initialization. + Defaults to None. + distribution (str): distribution either be ``'normal'`` + or ``'uniform'``. Defaults to ``'normal'``. + layer (str | list[str], optional): the layer will be initialized. + Defaults to None. + """ + + def __init__(self, gain=1, distribution='normal', **kwargs): + super().__init__(**kwargs) + self.gain = gain + self.distribution = distribution + + def __call__(self, module): + + def init(m): + if self.wholemodule: + xavier_init(m, self.gain, self.bias, self.distribution) + else: + layername = m.__class__.__name__ + basesname = _get_bases_name(m) + if len(set(self.layer) & set([layername] + basesname)): + xavier_init(m, self.gain, self.bias, self.distribution) + + module.apply(init) + if hasattr(module, '_params_init_info'): + update_init_info(module, init_info=self._get_init_info()) + + def _get_init_info(self): + info = f'{self.__class__.__name__}: gain={self.gain}, ' \ + f'distribution={self.distribution}, bias={self.bias}' + return info + + +@INITIALIZERS.register_module(name='Normal') +class NormalInit(BaseInit): + r"""Initialize module parameters with the values drawn from the normal + distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. + + Args: + mean (int | float):the mean of the normal distribution. Defaults to 0. + std (int | float): the standard deviation of the normal distribution. + Defaults to 1. + bias (int | float): the value to fill the bias. Defaults to 0. + bias_prob (float, optional): the probability for bias initialization. + Defaults to None. + layer (str | list[str], optional): the layer will be initialized. + Defaults to None. + + """ + + def __init__(self, mean=0, std=1, **kwargs): + super().__init__(**kwargs) + self.mean = mean + self.std = std + + def __call__(self, module): + + def init(m): + if self.wholemodule: + normal_init(m, self.mean, self.std, self.bias) + else: + layername = m.__class__.__name__ + basesname = _get_bases_name(m) + if len(set(self.layer) & set([layername] + basesname)): + normal_init(m, self.mean, self.std, self.bias) + + module.apply(init) + if hasattr(module, '_params_init_info'): + update_init_info(module, init_info=self._get_init_info()) + + def _get_init_info(self): + info = f'{self.__class__.__name__}: mean={self.mean},' \ + f' std={self.std}, bias={self.bias}' + return info + + +@INITIALIZERS.register_module(name='TruncNormal') +class TruncNormalInit(BaseInit): + r"""Initialize module parameters with the values drawn from the normal + distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values + outside :math:`[a, b]`. + + Args: + mean (float): the mean of the normal distribution. Defaults to 0. + std (float): the standard deviation of the normal distribution. + Defaults to 1. + a (float): The minimum cutoff value. + b ( float): The maximum cutoff value. + bias (float): the value to fill the bias. Defaults to 0. + bias_prob (float, optional): the probability for bias initialization. + Defaults to None. + layer (str | list[str], optional): the layer will be initialized. + Defaults to None. + + """ + + def __init__(self, + mean: float = 0, + std: float = 1, + a: float = -2, + b: float = 2, + **kwargs) -> None: + super().__init__(**kwargs) + self.mean = mean + self.std = std + self.a = a + self.b = b + + def __call__(self, module: nn.Module) -> None: + + def init(m): + if self.wholemodule: + trunc_normal_init(m, self.mean, self.std, self.a, self.b, + self.bias) + else: + layername = m.__class__.__name__ + basesname = _get_bases_name(m) + if len(set(self.layer) & set([layername] + basesname)): + trunc_normal_init(m, self.mean, self.std, self.a, self.b, + self.bias) + + module.apply(init) + if hasattr(module, '_params_init_info'): + update_init_info(module, init_info=self._get_init_info()) + + def _get_init_info(self): + info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ + f' mean={self.mean}, std={self.std}, bias={self.bias}' + return info + + +@INITIALIZERS.register_module(name='Uniform') +class UniformInit(BaseInit): + r"""Initialize module parameters with values drawn from the uniform + distribution :math:`\mathcal{U}(a, b)`. + + Args: + a (int | float): the lower bound of the uniform distribution. + Defaults to 0. + b (int | float): the upper bound of the uniform distribution. + Defaults to 1. + bias (int | float): the value to fill the bias. Defaults to 0. + bias_prob (float, optional): the probability for bias initialization. + Defaults to None. + layer (str | list[str], optional): the layer will be initialized. + Defaults to None. + """ + + def __init__(self, a=0, b=1, **kwargs): + super().__init__(**kwargs) + self.a = a + self.b = b + + def __call__(self, module): + + def init(m): + if self.wholemodule: + uniform_init(m, self.a, self.b, self.bias) + else: + layername = m.__class__.__name__ + basesname = _get_bases_name(m) + if len(set(self.layer) & set([layername] + basesname)): + uniform_init(m, self.a, self.b, self.bias) + + module.apply(init) + if hasattr(module, '_params_init_info'): + update_init_info(module, init_info=self._get_init_info()) + + def _get_init_info(self): + info = f'{self.__class__.__name__}: a={self.a},' \ + f' b={self.b}, bias={self.bias}' + return info + + +@INITIALIZERS.register_module(name='Kaiming') +class KaimingInit(BaseInit): + r"""Initialize module parameters with the values according to the method + described in `Delving deep into rectifiers: Surpassing human-level + performance on ImageNet classification - He, K. et al. (2015). + `_ + + Args: + a (int | float): the negative slope of the rectifier used after this + layer (only used with ``'leaky_relu'``). Defaults to 0. + mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing + ``'fan_in'`` preserves the magnitude of the variance of the weights + in the forward pass. Choosing ``'fan_out'`` preserves the + magnitudes in the backwards pass. Defaults to ``'fan_out'``. + nonlinearity (str): the non-linear function (`nn.functional` name), + recommended to use only with ``'relu'`` or ``'leaky_relu'`` . + Defaults to 'relu'. + bias (int | float): the value to fill the bias. Defaults to 0. + bias_prob (float, optional): the probability for bias initialization. + Defaults to None. + distribution (str): distribution either be ``'normal'`` or + ``'uniform'``. Defaults to ``'normal'``. + layer (str | list[str], optional): the layer will be initialized. + Defaults to None. + """ + + def __init__(self, + a=0, + mode='fan_out', + nonlinearity='relu', + distribution='normal', + **kwargs): + super().__init__(**kwargs) + self.a = a + self.mode = mode + self.nonlinearity = nonlinearity + self.distribution = distribution + + def __call__(self, module): + + def init(m): + if self.wholemodule: + kaiming_init(m, self.a, self.mode, self.nonlinearity, + self.bias, self.distribution) + else: + layername = m.__class__.__name__ + basesname = _get_bases_name(m) + if len(set(self.layer) & set([layername] + basesname)): + kaiming_init(m, self.a, self.mode, self.nonlinearity, + self.bias, self.distribution) + + module.apply(init) + if hasattr(module, '_params_init_info'): + update_init_info(module, init_info=self._get_init_info()) + + def _get_init_info(self): + info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ + f'nonlinearity={self.nonlinearity}, ' \ + f'distribution ={self.distribution}, bias={self.bias}' + return info + + +@INITIALIZERS.register_module(name='Caffe2Xavier') +class Caffe2XavierInit(KaimingInit): + # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch + # Acknowledgment to FAIR's internal code + def __init__(self, **kwargs): + super().__init__( + a=1, + mode='fan_in', + nonlinearity='leaky_relu', + distribution='uniform', + **kwargs) + + def __call__(self, module): + super().__call__(module) + + +@INITIALIZERS.register_module(name='Pretrained') +class PretrainedInit(object): + """Initialize module by loading a pretrained model. + + Args: + checkpoint (str): the checkpoint file of the pretrained model should + be load. + prefix (str, optional): the prefix of a sub-module in the pretrained + model. it is for loading a part of the pretrained model to + initialize. For example, if we would like to only load the + backbone of a detector model, we can set ``prefix='backbone.'``. + Defaults to None. + map_location (str): map tensors into proper locations. + """ + + def __init__(self, checkpoint, prefix=None, map_location=None): + self.checkpoint = checkpoint + self.prefix = prefix + self.map_location = map_location + + def __call__(self, module): + from annotator.mmpkg.mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, + load_state_dict) + logger = get_logger('mmcv') + if self.prefix is None: + print_log(f'load model from: {self.checkpoint}', logger=logger) + load_checkpoint( + module, + self.checkpoint, + map_location=self.map_location, + strict=False, + logger=logger) + else: + print_log( + f'load {self.prefix} in model from: {self.checkpoint}', + logger=logger) + state_dict = _load_checkpoint_with_prefix( + self.prefix, self.checkpoint, map_location=self.map_location) + load_state_dict(module, state_dict, strict=False, logger=logger) + + if hasattr(module, '_params_init_info'): + update_init_info(module, init_info=self._get_init_info()) + + def _get_init_info(self): + info = f'{self.__class__.__name__}: load from {self.checkpoint}' + return info + + +def _initialize(module, cfg, wholemodule=False): + func = build_from_cfg(cfg, INITIALIZERS) + # wholemodule flag is for override mode, there is no layer key in override + # and initializer will give init values for the whole module with the name + # in override. + func.wholemodule = wholemodule + func(module) + + +def _initialize_override(module, override, cfg): + if not isinstance(override, (dict, list)): + raise TypeError(f'override must be a dict or a list of dict, \ + but got {type(override)}') + + override = [override] if isinstance(override, dict) else override + + for override_ in override: + + cp_override = copy.deepcopy(override_) + name = cp_override.pop('name', None) + if name is None: + raise ValueError('`override` must contain the key "name",' + f'but got {cp_override}') + # if override only has name key, it means use args in init_cfg + if not cp_override: + cp_override.update(cfg) + # if override has name key and other args except type key, it will + # raise error + elif 'type' not in cp_override.keys(): + raise ValueError( + f'`override` need "type" key, but got {cp_override}') + + if hasattr(module, name): + _initialize(getattr(module, name), cp_override, wholemodule=True) + else: + raise RuntimeError(f'module did not have attribute {name}, ' + f'but init_cfg is {cp_override}.') + + +def initialize(module, init_cfg): + """Initialize a module. + + Args: + module (``torch.nn.Module``): the module will be initialized. + init_cfg (dict | list[dict]): initialization configuration dict to + define initializer. OpenMMLab has implemented 6 initializers + including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, + ``Kaiming``, and ``Pretrained``. + Example: + >>> module = nn.Linear(2, 3, bias=True) + >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) + >>> initialize(module, init_cfg) + + >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) + >>> # define key ``'layer'`` for initializing layer with different + >>> # configuration + >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), + dict(type='Constant', layer='Linear', val=2)] + >>> initialize(module, init_cfg) + + >>> # define key``'override'`` to initialize some specific part in + >>> # module + >>> class FooNet(nn.Module): + >>> def __init__(self): + >>> super().__init__() + >>> self.feat = nn.Conv2d(3, 16, 3) + >>> self.reg = nn.Conv2d(16, 10, 3) + >>> self.cls = nn.Conv2d(16, 5, 3) + >>> model = FooNet() + >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', + >>> override=dict(type='Constant', name='reg', val=3, bias=4)) + >>> initialize(model, init_cfg) + + >>> model = ResNet(depth=50) + >>> # Initialize weights with the pretrained model. + >>> init_cfg = dict(type='Pretrained', + checkpoint='torchvision://resnet50') + >>> initialize(model, init_cfg) + + >>> # Initialize weights of a sub-module with the specific part of + >>> # a pretrained model by using "prefix". + >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ + >>> 'retinanet_r50_fpn_1x_coco/'\ + >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' + >>> init_cfg = dict(type='Pretrained', + checkpoint=url, prefix='backbone.') + """ + if not isinstance(init_cfg, (dict, list)): + raise TypeError(f'init_cfg must be a dict or a list of dict, \ + but got {type(init_cfg)}') + + if isinstance(init_cfg, dict): + init_cfg = [init_cfg] + + for cfg in init_cfg: + # should deeply copy the original config because cfg may be used by + # other modules, e.g., one init_cfg shared by multiple bottleneck + # blocks, the expected cfg will be changed after pop and will change + # the initialization behavior of other modules + cp_cfg = copy.deepcopy(cfg) + override = cp_cfg.pop('override', None) + _initialize(module, cp_cfg) + + if override is not None: + cp_cfg.pop('layer', None) + _initialize_override(module, override, cp_cfg) + else: + # All attributes in module have same initialization. + pass + + +def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, + b: float) -> Tensor: + # Method based on + # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf + # Modified from + # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py + def norm_cdf(x): + # Computes standard normal cumulative distribution function + return (1. + math.erf(x / math.sqrt(2.))) / 2. + + if (mean < a - 2 * std) or (mean > b + 2 * std): + warnings.warn( + 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' + 'The distribution of values may be incorrect.', + stacklevel=2) + + with torch.no_grad(): + # Values are generated by using a truncated uniform distribution and + # then using the inverse CDF for the normal distribution. + # Get upper and lower cdf values + lower = norm_cdf((a - mean) / std) + upper = norm_cdf((b - mean) / std) + + # Uniformly fill tensor with values from [lower, upper], then translate + # to [2lower-1, 2upper-1]. + tensor.uniform_(2 * lower - 1, 2 * upper - 1) + + # Use inverse cdf transform for normal distribution to get truncated + # standard normal + tensor.erfinv_() + + # Transform to proper mean, std + tensor.mul_(std * math.sqrt(2.)) + tensor.add_(mean) + + # Clamp to ensure it's in the proper range + tensor.clamp_(min=a, max=b) + return tensor + + +def trunc_normal_(tensor: Tensor, + mean: float = 0., + std: float = 1., + a: float = -2., + b: float = 2.) -> Tensor: + r"""Fills the input Tensor with values drawn from a truncated + normal distribution. The values are effectively drawn from the + normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` + with values outside :math:`[a, b]` redrawn until they are within + the bounds. The method used for generating the random values works + best when :math:`a \leq \text{mean} \leq b`. + + Modified from + https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py + + Args: + tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. + mean (float): the mean of the normal distribution. + std (float): the standard deviation of the normal distribution. + a (float): the minimum cutoff value. + b (float): the maximum cutoff value. + """ + return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/vgg.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/vgg.py new file mode 100644 index 0000000000000000000000000000000000000000..8778b649561a45a9652b1a15a26c2d171e58f3e1 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/cnn/vgg.py @@ -0,0 +1,175 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import logging + +import torch.nn as nn + +from .utils import constant_init, kaiming_init, normal_init + + +def conv3x3(in_planes, out_planes, dilation=1): + """3x3 convolution with padding.""" + return nn.Conv2d( + in_planes, + out_planes, + kernel_size=3, + padding=dilation, + dilation=dilation) + + +def make_vgg_layer(inplanes, + planes, + num_blocks, + dilation=1, + with_bn=False, + ceil_mode=False): + layers = [] + for _ in range(num_blocks): + layers.append(conv3x3(inplanes, planes, dilation)) + if with_bn: + layers.append(nn.BatchNorm2d(planes)) + layers.append(nn.ReLU(inplace=True)) + inplanes = planes + layers.append(nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=ceil_mode)) + + return layers + + +class VGG(nn.Module): + """VGG backbone. + + Args: + depth (int): Depth of vgg, from {11, 13, 16, 19}. + with_bn (bool): Use BatchNorm or not. + num_classes (int): number of classes for classification. + num_stages (int): VGG stages, normally 5. + dilations (Sequence[int]): Dilation of each stage. + out_indices (Sequence[int]): Output from which stages. + frozen_stages (int): Stages to be frozen (all param fixed). -1 means + not freezing any parameters. + bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze + running stats (mean and var). + bn_frozen (bool): Whether to freeze weight and bias of BN layers. + """ + + arch_settings = { + 11: (1, 1, 2, 2, 2), + 13: (2, 2, 2, 2, 2), + 16: (2, 2, 3, 3, 3), + 19: (2, 2, 4, 4, 4) + } + + def __init__(self, + depth, + with_bn=False, + num_classes=-1, + num_stages=5, + dilations=(1, 1, 1, 1, 1), + out_indices=(0, 1, 2, 3, 4), + frozen_stages=-1, + bn_eval=True, + bn_frozen=False, + ceil_mode=False, + with_last_pool=True): + super(VGG, self).__init__() + if depth not in self.arch_settings: + raise KeyError(f'invalid depth {depth} for vgg') + assert num_stages >= 1 and num_stages <= 5 + stage_blocks = self.arch_settings[depth] + self.stage_blocks = stage_blocks[:num_stages] + assert len(dilations) == num_stages + assert max(out_indices) <= num_stages + + self.num_classes = num_classes + self.out_indices = out_indices + self.frozen_stages = frozen_stages + self.bn_eval = bn_eval + self.bn_frozen = bn_frozen + + self.inplanes = 3 + start_idx = 0 + vgg_layers = [] + self.range_sub_modules = [] + for i, num_blocks in enumerate(self.stage_blocks): + num_modules = num_blocks * (2 + with_bn) + 1 + end_idx = start_idx + num_modules + dilation = dilations[i] + planes = 64 * 2**i if i < 4 else 512 + vgg_layer = make_vgg_layer( + self.inplanes, + planes, + num_blocks, + dilation=dilation, + with_bn=with_bn, + ceil_mode=ceil_mode) + vgg_layers.extend(vgg_layer) + self.inplanes = planes + self.range_sub_modules.append([start_idx, end_idx]) + start_idx = end_idx + if not with_last_pool: + vgg_layers.pop(-1) + self.range_sub_modules[-1][1] -= 1 + self.module_name = 'features' + self.add_module(self.module_name, nn.Sequential(*vgg_layers)) + + if self.num_classes > 0: + self.classifier = nn.Sequential( + nn.Linear(512 * 7 * 7, 4096), + nn.ReLU(True), + nn.Dropout(), + nn.Linear(4096, 4096), + nn.ReLU(True), + nn.Dropout(), + nn.Linear(4096, num_classes), + ) + + def init_weights(self, pretrained=None): + if isinstance(pretrained, str): + logger = logging.getLogger() + from ..runner import load_checkpoint + load_checkpoint(self, pretrained, strict=False, logger=logger) + elif pretrained is None: + for m in self.modules(): + if isinstance(m, nn.Conv2d): + kaiming_init(m) + elif isinstance(m, nn.BatchNorm2d): + constant_init(m, 1) + elif isinstance(m, nn.Linear): + normal_init(m, std=0.01) + else: + raise TypeError('pretrained must be a str or None') + + def forward(self, x): + outs = [] + vgg_layers = getattr(self, self.module_name) + for i in range(len(self.stage_blocks)): + for j in range(*self.range_sub_modules[i]): + vgg_layer = vgg_layers[j] + x = vgg_layer(x) + if i in self.out_indices: + outs.append(x) + if self.num_classes > 0: + x = x.view(x.size(0), -1) + x = self.classifier(x) + outs.append(x) + if len(outs) == 1: + return outs[0] + else: + return tuple(outs) + + def train(self, mode=True): + super(VGG, self).train(mode) + if self.bn_eval: + for m in self.modules(): + if isinstance(m, nn.BatchNorm2d): + m.eval() + if self.bn_frozen: + for params in m.parameters(): + params.requires_grad = False + vgg_layers = getattr(self, self.module_name) + if mode and self.frozen_stages >= 0: + for i in range(self.frozen_stages): + for j in range(*self.range_sub_modules[i]): + mod = vgg_layers[j] + mod.eval() + for param in mod.parameters(): + param.requires_grad = False diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/engine/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/engine/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3193b7f664e19ce2458d81c836597fa22e4bb082 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/engine/__init__.py @@ -0,0 +1,8 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, + single_gpu_test) + +__all__ = [ + 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', + 'single_gpu_test' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/engine/test.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/engine/test.py new file mode 100644 index 0000000000000000000000000000000000000000..ad5f55c4b181f7ad7bf17ed9003496f7377bbd3e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/engine/test.py @@ -0,0 +1,202 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os.path as osp +import pickle +import shutil +import tempfile +import time + +import torch +import torch.distributed as dist + +import annotator.mmpkg.mmcv as mmcv +from annotator.mmpkg.mmcv.runner import get_dist_info + + +def single_gpu_test(model, data_loader): + """Test model with a single gpu. + + This method tests model with a single gpu and displays test progress bar. + + Args: + model (nn.Module): Model to be tested. + data_loader (nn.Dataloader): Pytorch data loader. + + Returns: + list: The prediction results. + """ + model.eval() + results = [] + dataset = data_loader.dataset + prog_bar = mmcv.ProgressBar(len(dataset)) + for data in data_loader: + with torch.no_grad(): + result = model(return_loss=False, **data) + results.extend(result) + + # Assume result has the same length of batch_size + # refer to https://github.com/open-mmlab/mmcv/issues/985 + batch_size = len(result) + for _ in range(batch_size): + prog_bar.update() + return results + + +def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): + """Test model with multiple gpus. + + This method tests model with multiple gpus and collects the results + under two different modes: gpu and cpu modes. By setting + ``gpu_collect=True``, it encodes results to gpu tensors and use gpu + communication for results collection. On cpu mode it saves the results on + different gpus to ``tmpdir`` and collects them by the rank 0 worker. + + Args: + model (nn.Module): Model to be tested. + data_loader (nn.Dataloader): Pytorch data loader. + tmpdir (str): Path of directory to save the temporary results from + different gpus under cpu mode. + gpu_collect (bool): Option to use either gpu or cpu to collect results. + + Returns: + list: The prediction results. + """ + model.eval() + results = [] + dataset = data_loader.dataset + rank, world_size = get_dist_info() + if rank == 0: + prog_bar = mmcv.ProgressBar(len(dataset)) + time.sleep(2) # This line can prevent deadlock problem in some cases. + for i, data in enumerate(data_loader): + with torch.no_grad(): + result = model(return_loss=False, **data) + results.extend(result) + + if rank == 0: + batch_size = len(result) + batch_size_all = batch_size * world_size + if batch_size_all + prog_bar.completed > len(dataset): + batch_size_all = len(dataset) - prog_bar.completed + for _ in range(batch_size_all): + prog_bar.update() + + # collect results from all ranks + if gpu_collect: + results = collect_results_gpu(results, len(dataset)) + else: + results = collect_results_cpu(results, len(dataset), tmpdir) + return results + + +def collect_results_cpu(result_part, size, tmpdir=None): + """Collect results under cpu mode. + + On cpu mode, this function will save the results on different gpus to + ``tmpdir`` and collect them by the rank 0 worker. + + Args: + result_part (list): Result list containing result parts + to be collected. + size (int): Size of the results, commonly equal to length of + the results. + tmpdir (str | None): temporal directory for collected results to + store. If set to None, it will create a random temporal directory + for it. + + Returns: + list: The collected results. + """ + rank, world_size = get_dist_info() + # create a tmp dir if it is not specified + if tmpdir is None: + MAX_LEN = 512 + # 32 is whitespace + dir_tensor = torch.full((MAX_LEN, ), + 32, + dtype=torch.uint8, + device='cuda') + if rank == 0: + mmcv.mkdir_or_exist('.dist_test') + tmpdir = tempfile.mkdtemp(dir='.dist_test') + tmpdir = torch.tensor( + bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') + dir_tensor[:len(tmpdir)] = tmpdir + dist.broadcast(dir_tensor, 0) + tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() + else: + mmcv.mkdir_or_exist(tmpdir) + # dump the part result to the dir + mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) + dist.barrier() + # collect all parts + if rank != 0: + return None + else: + # load results of all parts from tmp dir + part_list = [] + for i in range(world_size): + part_file = osp.join(tmpdir, f'part_{i}.pkl') + part_result = mmcv.load(part_file) + # When data is severely insufficient, an empty part_result + # on a certain gpu could makes the overall outputs empty. + if part_result: + part_list.append(part_result) + # sort the results + ordered_results = [] + for res in zip(*part_list): + ordered_results.extend(list(res)) + # the dataloader may pad some samples + ordered_results = ordered_results[:size] + # remove tmp dir + shutil.rmtree(tmpdir) + return ordered_results + + +def collect_results_gpu(result_part, size): + """Collect results under gpu mode. + + On gpu mode, this function will encode results to gpu tensors and use gpu + communication for results collection. + + Args: + result_part (list): Result list containing result parts + to be collected. + size (int): Size of the results, commonly equal to length of + the results. + + Returns: + list: The collected results. + """ + rank, world_size = get_dist_info() + # dump result part to tensor with pickle + part_tensor = torch.tensor( + bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') + # gather all result part tensor shape + shape_tensor = torch.tensor(part_tensor.shape, device='cuda') + shape_list = [shape_tensor.clone() for _ in range(world_size)] + dist.all_gather(shape_list, shape_tensor) + # padding result part tensor to max length + shape_max = torch.tensor(shape_list).max() + part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') + part_send[:shape_tensor[0]] = part_tensor + part_recv_list = [ + part_tensor.new_zeros(shape_max) for _ in range(world_size) + ] + # gather all result part + dist.all_gather(part_recv_list, part_send) + + if rank == 0: + part_list = [] + for recv, shape in zip(part_recv_list, shape_list): + part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) + # When data is severely insufficient, an empty part_result + # on a certain gpu could makes the overall outputs empty. + if part_result: + part_list.append(part_result) + # sort the results + ordered_results = [] + for res in zip(*part_list): + ordered_results.extend(list(res)) + # the dataloader may pad some samples + ordered_results = ordered_results[:size] + return ordered_results diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2051b85f7e59bff7bdbaa131849ce8cd31f059a4 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/__init__.py @@ -0,0 +1,11 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .file_client import BaseStorageBackend, FileClient +from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler +from .io import dump, load, register_handler +from .parse import dict_from_file, list_from_file + +__all__ = [ + 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', + 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', + 'list_from_file', 'dict_from_file' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/file_client.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/file_client.py new file mode 100644 index 0000000000000000000000000000000000000000..1ed2bf5f41a29000f9a080066497d8f3674fae15 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/file_client.py @@ -0,0 +1,1148 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import inspect +import os +import os.path as osp +import re +import tempfile +import warnings +from abc import ABCMeta, abstractmethod +from contextlib import contextmanager +from pathlib import Path +from typing import Iterable, Iterator, Optional, Tuple, Union +from urllib.request import urlopen + +import annotator.mmpkg.mmcv as mmcv +from annotator.mmpkg.mmcv.utils.misc import has_method +from annotator.mmpkg.mmcv.utils.path import is_filepath + + +class BaseStorageBackend(metaclass=ABCMeta): + """Abstract class of storage backends. + + All backends need to implement two apis: ``get()`` and ``get_text()``. + ``get()`` reads the file as a byte stream and ``get_text()`` reads the file + as texts. + """ + + # a flag to indicate whether the backend can create a symlink for a file + _allow_symlink = False + + @property + def name(self): + return self.__class__.__name__ + + @property + def allow_symlink(self): + return self._allow_symlink + + @abstractmethod + def get(self, filepath): + pass + + @abstractmethod + def get_text(self, filepath): + pass + + +class CephBackend(BaseStorageBackend): + """Ceph storage backend (for internal use). + + Args: + path_mapping (dict|None): path mapping dict from local path to Petrel + path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` + will be replaced by ``dst``. Default: None. + + .. warning:: + :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, + please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. + """ + + def __init__(self, path_mapping=None): + try: + import ceph + except ImportError: + raise ImportError('Please install ceph to enable CephBackend.') + + warnings.warn( + 'CephBackend will be deprecated, please use PetrelBackend instead') + self._client = ceph.S3Client() + assert isinstance(path_mapping, dict) or path_mapping is None + self.path_mapping = path_mapping + + def get(self, filepath): + filepath = str(filepath) + if self.path_mapping is not None: + for k, v in self.path_mapping.items(): + filepath = filepath.replace(k, v) + value = self._client.Get(filepath) + value_buf = memoryview(value) + return value_buf + + def get_text(self, filepath, encoding=None): + raise NotImplementedError + + +class PetrelBackend(BaseStorageBackend): + """Petrel storage backend (for internal use). + + PetrelBackend supports reading and writing data to multiple clusters. + If the file path contains the cluster name, PetrelBackend will read data + from specified cluster or write data to it. Otherwise, PetrelBackend will + access the default cluster. + + Args: + path_mapping (dict, optional): Path mapping dict from local path to + Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in + ``filepath`` will be replaced by ``dst``. Default: None. + enable_mc (bool, optional): Whether to enable memcached support. + Default: True. + + Examples: + >>> filepath1 = 's3://path/of/file' + >>> filepath2 = 'cluster-name:s3://path/of/file' + >>> client = PetrelBackend() + >>> client.get(filepath1) # get data from default cluster + >>> client.get(filepath2) # get data from 'cluster-name' cluster + """ + + def __init__(self, + path_mapping: Optional[dict] = None, + enable_mc: bool = True): + try: + from petrel_client import client + except ImportError: + raise ImportError('Please install petrel_client to enable ' + 'PetrelBackend.') + + self._client = client.Client(enable_mc=enable_mc) + assert isinstance(path_mapping, dict) or path_mapping is None + self.path_mapping = path_mapping + + def _map_path(self, filepath: Union[str, Path]) -> str: + """Map ``filepath`` to a string path whose prefix will be replaced by + :attr:`self.path_mapping`. + + Args: + filepath (str): Path to be mapped. + """ + filepath = str(filepath) + if self.path_mapping is not None: + for k, v in self.path_mapping.items(): + filepath = filepath.replace(k, v) + return filepath + + def _format_path(self, filepath: str) -> str: + """Convert a ``filepath`` to standard format of petrel oss. + + If the ``filepath`` is concatenated by ``os.path.join``, in a Windows + environment, the ``filepath`` will be the format of + 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the + above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. + + Args: + filepath (str): Path to be formatted. + """ + return re.sub(r'\\+', '/', filepath) + + def get(self, filepath: Union[str, Path]) -> memoryview: + """Read data from a given ``filepath`` with 'rb' mode. + + Args: + filepath (str or Path): Path to read data. + + Returns: + memoryview: A memory view of expected bytes object to avoid + copying. The memoryview object can be converted to bytes by + ``value_buf.tobytes()``. + """ + filepath = self._map_path(filepath) + filepath = self._format_path(filepath) + value = self._client.Get(filepath) + value_buf = memoryview(value) + return value_buf + + def get_text(self, + filepath: Union[str, Path], + encoding: str = 'utf-8') -> str: + """Read data from a given ``filepath`` with 'r' mode. + + Args: + filepath (str or Path): Path to read data. + encoding (str): The encoding format used to open the ``filepath``. + Default: 'utf-8'. + + Returns: + str: Expected text reading from ``filepath``. + """ + return str(self.get(filepath), encoding=encoding) + + def put(self, obj: bytes, filepath: Union[str, Path]) -> None: + """Save data to a given ``filepath``. + + Args: + obj (bytes): Data to be saved. + filepath (str or Path): Path to write data. + """ + filepath = self._map_path(filepath) + filepath = self._format_path(filepath) + self._client.put(filepath, obj) + + def put_text(self, + obj: str, + filepath: Union[str, Path], + encoding: str = 'utf-8') -> None: + """Save data to a given ``filepath``. + + Args: + obj (str): Data to be written. + filepath (str or Path): Path to write data. + encoding (str): The encoding format used to encode the ``obj``. + Default: 'utf-8'. + """ + self.put(bytes(obj, encoding=encoding), filepath) + + def remove(self, filepath: Union[str, Path]) -> None: + """Remove a file. + + Args: + filepath (str or Path): Path to be removed. + """ + if not has_method(self._client, 'delete'): + raise NotImplementedError( + ('Current version of Petrel Python SDK has not supported ' + 'the `delete` method, please use a higher version or dev' + ' branch instead.')) + + filepath = self._map_path(filepath) + filepath = self._format_path(filepath) + self._client.delete(filepath) + + def exists(self, filepath: Union[str, Path]) -> bool: + """Check whether a file path exists. + + Args: + filepath (str or Path): Path to be checked whether exists. + + Returns: + bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. + """ + if not (has_method(self._client, 'contains') + and has_method(self._client, 'isdir')): + raise NotImplementedError( + ('Current version of Petrel Python SDK has not supported ' + 'the `contains` and `isdir` methods, please use a higher' + 'version or dev branch instead.')) + + filepath = self._map_path(filepath) + filepath = self._format_path(filepath) + return self._client.contains(filepath) or self._client.isdir(filepath) + + def isdir(self, filepath: Union[str, Path]) -> bool: + """Check whether a file path is a directory. + + Args: + filepath (str or Path): Path to be checked whether it is a + directory. + + Returns: + bool: Return ``True`` if ``filepath`` points to a directory, + ``False`` otherwise. + """ + if not has_method(self._client, 'isdir'): + raise NotImplementedError( + ('Current version of Petrel Python SDK has not supported ' + 'the `isdir` method, please use a higher version or dev' + ' branch instead.')) + + filepath = self._map_path(filepath) + filepath = self._format_path(filepath) + return self._client.isdir(filepath) + + def isfile(self, filepath: Union[str, Path]) -> bool: + """Check whether a file path is a file. + + Args: + filepath (str or Path): Path to be checked whether it is a file. + + Returns: + bool: Return ``True`` if ``filepath`` points to a file, ``False`` + otherwise. + """ + if not has_method(self._client, 'contains'): + raise NotImplementedError( + ('Current version of Petrel Python SDK has not supported ' + 'the `contains` method, please use a higher version or ' + 'dev branch instead.')) + + filepath = self._map_path(filepath) + filepath = self._format_path(filepath) + return self._client.contains(filepath) + + def join_path(self, filepath: Union[str, Path], + *filepaths: Union[str, Path]) -> str: + """Concatenate all file paths. + + Args: + filepath (str or Path): Path to be concatenated. + + Returns: + str: The result after concatenation. + """ + filepath = self._format_path(self._map_path(filepath)) + if filepath.endswith('/'): + filepath = filepath[:-1] + formatted_paths = [filepath] + for path in filepaths: + formatted_paths.append(self._format_path(self._map_path(path))) + return '/'.join(formatted_paths) + + @contextmanager + def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: + """Download a file from ``filepath`` and return a temporary path. + + ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It + can be called with ``with`` statement, and when exists from the + ``with`` statement, the temporary path will be released. + + Args: + filepath (str | Path): Download a file from ``filepath``. + + Examples: + >>> client = PetrelBackend() + >>> # After existing from the ``with`` clause, + >>> # the path will be removed + >>> with client.get_local_path('s3://path/of/your/file') as path: + ... # do something here + + Yields: + Iterable[str]: Only yield one temporary path. + """ + filepath = self._map_path(filepath) + filepath = self._format_path(filepath) + assert self.isfile(filepath) + try: + f = tempfile.NamedTemporaryFile(delete=False) + f.write(self.get(filepath)) + f.close() + yield f.name + finally: + os.remove(f.name) + + def list_dir_or_file(self, + dir_path: Union[str, Path], + list_dir: bool = True, + list_file: bool = True, + suffix: Optional[Union[str, Tuple[str]]] = None, + recursive: bool = False) -> Iterator[str]: + """Scan a directory to find the interested directories or files in + arbitrary order. + + Note: + Petrel has no concept of directories but it simulates the directory + hierarchy in the filesystem through public prefixes. In addition, + if the returned path ends with '/', it means the path is a public + prefix which is a logical directory. + + Note: + :meth:`list_dir_or_file` returns the path relative to ``dir_path``. + In addition, the returned path of directory will not contains the + suffix '/' which is consistent with other backends. + + Args: + dir_path (str | Path): Path of the directory. + list_dir (bool): List the directories. Default: True. + list_file (bool): List the path of files. Default: True. + suffix (str or tuple[str], optional): File suffix + that we are interested in. Default: None. + recursive (bool): If set to True, recursively scan the + directory. Default: False. + + Yields: + Iterable[str]: A relative path to ``dir_path``. + """ + if not has_method(self._client, 'list'): + raise NotImplementedError( + ('Current version of Petrel Python SDK has not supported ' + 'the `list` method, please use a higher version or dev' + ' branch instead.')) + + dir_path = self._map_path(dir_path) + dir_path = self._format_path(dir_path) + if list_dir and suffix is not None: + raise TypeError( + '`list_dir` should be False when `suffix` is not None') + + if (suffix is not None) and not isinstance(suffix, (str, tuple)): + raise TypeError('`suffix` must be a string or tuple of strings') + + # Petrel's simulated directory hierarchy assumes that directory paths + # should end with `/` + if not dir_path.endswith('/'): + dir_path += '/' + + root = dir_path + + def _list_dir_or_file(dir_path, list_dir, list_file, suffix, + recursive): + for path in self._client.list(dir_path): + # the `self.isdir` is not used here to determine whether path + # is a directory, because `self.isdir` relies on + # `self._client.list` + if path.endswith('/'): # a directory path + next_dir_path = self.join_path(dir_path, path) + if list_dir: + # get the relative path and exclude the last + # character '/' + rel_dir = next_dir_path[len(root):-1] + yield rel_dir + if recursive: + yield from _list_dir_or_file(next_dir_path, list_dir, + list_file, suffix, + recursive) + else: # a file path + absolute_path = self.join_path(dir_path, path) + rel_path = absolute_path[len(root):] + if (suffix is None + or rel_path.endswith(suffix)) and list_file: + yield rel_path + + return _list_dir_or_file(dir_path, list_dir, list_file, suffix, + recursive) + + +class MemcachedBackend(BaseStorageBackend): + """Memcached storage backend. + + Attributes: + server_list_cfg (str): Config file for memcached server list. + client_cfg (str): Config file for memcached client. + sys_path (str | None): Additional path to be appended to `sys.path`. + Default: None. + """ + + def __init__(self, server_list_cfg, client_cfg, sys_path=None): + if sys_path is not None: + import sys + sys.path.append(sys_path) + try: + import mc + except ImportError: + raise ImportError( + 'Please install memcached to enable MemcachedBackend.') + + self.server_list_cfg = server_list_cfg + self.client_cfg = client_cfg + self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, + self.client_cfg) + # mc.pyvector servers as a point which points to a memory cache + self._mc_buffer = mc.pyvector() + + def get(self, filepath): + filepath = str(filepath) + import mc + self._client.Get(filepath, self._mc_buffer) + value_buf = mc.ConvertBuffer(self._mc_buffer) + return value_buf + + def get_text(self, filepath, encoding=None): + raise NotImplementedError + + +class LmdbBackend(BaseStorageBackend): + """Lmdb storage backend. + + Args: + db_path (str): Lmdb database path. + readonly (bool, optional): Lmdb environment parameter. If True, + disallow any write operations. Default: True. + lock (bool, optional): Lmdb environment parameter. If False, when + concurrent access occurs, do not lock the database. Default: False. + readahead (bool, optional): Lmdb environment parameter. If False, + disable the OS filesystem readahead mechanism, which may improve + random read performance when a database is larger than RAM. + Default: False. + + Attributes: + db_path (str): Lmdb database path. + """ + + def __init__(self, + db_path, + readonly=True, + lock=False, + readahead=False, + **kwargs): + try: + import lmdb + except ImportError: + raise ImportError('Please install lmdb to enable LmdbBackend.') + + self.db_path = str(db_path) + self._client = lmdb.open( + self.db_path, + readonly=readonly, + lock=lock, + readahead=readahead, + **kwargs) + + def get(self, filepath): + """Get values according to the filepath. + + Args: + filepath (str | obj:`Path`): Here, filepath is the lmdb key. + """ + filepath = str(filepath) + with self._client.begin(write=False) as txn: + value_buf = txn.get(filepath.encode('ascii')) + return value_buf + + def get_text(self, filepath, encoding=None): + raise NotImplementedError + + +class HardDiskBackend(BaseStorageBackend): + """Raw hard disks storage backend.""" + + _allow_symlink = True + + def get(self, filepath: Union[str, Path]) -> bytes: + """Read data from a given ``filepath`` with 'rb' mode. + + Args: + filepath (str or Path): Path to read data. + + Returns: + bytes: Expected bytes object. + """ + with open(filepath, 'rb') as f: + value_buf = f.read() + return value_buf + + def get_text(self, + filepath: Union[str, Path], + encoding: str = 'utf-8') -> str: + """Read data from a given ``filepath`` with 'r' mode. + + Args: + filepath (str or Path): Path to read data. + encoding (str): The encoding format used to open the ``filepath``. + Default: 'utf-8'. + + Returns: + str: Expected text reading from ``filepath``. + """ + with open(filepath, 'r', encoding=encoding) as f: + value_buf = f.read() + return value_buf + + def put(self, obj: bytes, filepath: Union[str, Path]) -> None: + """Write data to a given ``filepath`` with 'wb' mode. + + Note: + ``put`` will create a directory if the directory of ``filepath`` + does not exist. + + Args: + obj (bytes): Data to be written. + filepath (str or Path): Path to write data. + """ + mmcv.mkdir_or_exist(osp.dirname(filepath)) + with open(filepath, 'wb') as f: + f.write(obj) + + def put_text(self, + obj: str, + filepath: Union[str, Path], + encoding: str = 'utf-8') -> None: + """Write data to a given ``filepath`` with 'w' mode. + + Note: + ``put_text`` will create a directory if the directory of + ``filepath`` does not exist. + + Args: + obj (str): Data to be written. + filepath (str or Path): Path to write data. + encoding (str): The encoding format used to open the ``filepath``. + Default: 'utf-8'. + """ + mmcv.mkdir_or_exist(osp.dirname(filepath)) + with open(filepath, 'w', encoding=encoding) as f: + f.write(obj) + + def remove(self, filepath: Union[str, Path]) -> None: + """Remove a file. + + Args: + filepath (str or Path): Path to be removed. + """ + os.remove(filepath) + + def exists(self, filepath: Union[str, Path]) -> bool: + """Check whether a file path exists. + + Args: + filepath (str or Path): Path to be checked whether exists. + + Returns: + bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. + """ + return osp.exists(filepath) + + def isdir(self, filepath: Union[str, Path]) -> bool: + """Check whether a file path is a directory. + + Args: + filepath (str or Path): Path to be checked whether it is a + directory. + + Returns: + bool: Return ``True`` if ``filepath`` points to a directory, + ``False`` otherwise. + """ + return osp.isdir(filepath) + + def isfile(self, filepath: Union[str, Path]) -> bool: + """Check whether a file path is a file. + + Args: + filepath (str or Path): Path to be checked whether it is a file. + + Returns: + bool: Return ``True`` if ``filepath`` points to a file, ``False`` + otherwise. + """ + return osp.isfile(filepath) + + def join_path(self, filepath: Union[str, Path], + *filepaths: Union[str, Path]) -> str: + """Concatenate all file paths. + + Join one or more filepath components intelligently. The return value + is the concatenation of filepath and any members of *filepaths. + + Args: + filepath (str or Path): Path to be concatenated. + + Returns: + str: The result of concatenation. + """ + return osp.join(filepath, *filepaths) + + @contextmanager + def get_local_path( + self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: + """Only for unified API and do nothing.""" + yield filepath + + def list_dir_or_file(self, + dir_path: Union[str, Path], + list_dir: bool = True, + list_file: bool = True, + suffix: Optional[Union[str, Tuple[str]]] = None, + recursive: bool = False) -> Iterator[str]: + """Scan a directory to find the interested directories or files in + arbitrary order. + + Note: + :meth:`list_dir_or_file` returns the path relative to ``dir_path``. + + Args: + dir_path (str | Path): Path of the directory. + list_dir (bool): List the directories. Default: True. + list_file (bool): List the path of files. Default: True. + suffix (str or tuple[str], optional): File suffix + that we are interested in. Default: None. + recursive (bool): If set to True, recursively scan the + directory. Default: False. + + Yields: + Iterable[str]: A relative path to ``dir_path``. + """ + if list_dir and suffix is not None: + raise TypeError('`suffix` should be None when `list_dir` is True') + + if (suffix is not None) and not isinstance(suffix, (str, tuple)): + raise TypeError('`suffix` must be a string or tuple of strings') + + root = dir_path + + def _list_dir_or_file(dir_path, list_dir, list_file, suffix, + recursive): + for entry in os.scandir(dir_path): + if not entry.name.startswith('.') and entry.is_file(): + rel_path = osp.relpath(entry.path, root) + if (suffix is None + or rel_path.endswith(suffix)) and list_file: + yield rel_path + elif osp.isdir(entry.path): + if list_dir: + rel_dir = osp.relpath(entry.path, root) + yield rel_dir + if recursive: + yield from _list_dir_or_file(entry.path, list_dir, + list_file, suffix, + recursive) + + return _list_dir_or_file(dir_path, list_dir, list_file, suffix, + recursive) + + +class HTTPBackend(BaseStorageBackend): + """HTTP and HTTPS storage bachend.""" + + def get(self, filepath): + value_buf = urlopen(filepath).read() + return value_buf + + def get_text(self, filepath, encoding='utf-8'): + value_buf = urlopen(filepath).read() + return value_buf.decode(encoding) + + @contextmanager + def get_local_path(self, filepath: str) -> Iterable[str]: + """Download a file from ``filepath``. + + ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It + can be called with ``with`` statement, and when exists from the + ``with`` statement, the temporary path will be released. + + Args: + filepath (str): Download a file from ``filepath``. + + Examples: + >>> client = HTTPBackend() + >>> # After existing from the ``with`` clause, + >>> # the path will be removed + >>> with client.get_local_path('http://path/of/your/file') as path: + ... # do something here + """ + try: + f = tempfile.NamedTemporaryFile(delete=False) + f.write(self.get(filepath)) + f.close() + yield f.name + finally: + os.remove(f.name) + + +class FileClient: + """A general file client to access files in different backends. + + The client loads a file or text in a specified backend from its path + and returns it as a binary or text file. There are two ways to choose a + backend, the name of backend and the prefix of path. Although both of them + can be used to choose a storage backend, ``backend`` has a higher priority + that is if they are all set, the storage backend will be chosen by the + backend argument. If they are all `None`, the disk backend will be chosen. + Note that It can also register other backend accessor with a given name, + prefixes, and backend class. In addition, We use the singleton pattern to + avoid repeated object creation. If the arguments are the same, the same + object will be returned. + + Args: + backend (str, optional): The storage backend type. Options are "disk", + "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. + prefix (str, optional): The prefix of the registered storage backend. + Options are "s3", "http", "https". Default: None. + + Examples: + >>> # only set backend + >>> file_client = FileClient(backend='petrel') + >>> # only set prefix + >>> file_client = FileClient(prefix='s3') + >>> # set both backend and prefix but use backend to choose client + >>> file_client = FileClient(backend='petrel', prefix='s3') + >>> # if the arguments are the same, the same object is returned + >>> file_client1 = FileClient(backend='petrel') + >>> file_client1 is file_client + True + + Attributes: + client (:obj:`BaseStorageBackend`): The backend object. + """ + + _backends = { + 'disk': HardDiskBackend, + 'ceph': CephBackend, + 'memcached': MemcachedBackend, + 'lmdb': LmdbBackend, + 'petrel': PetrelBackend, + 'http': HTTPBackend, + } + # This collection is used to record the overridden backends, and when a + # backend appears in the collection, the singleton pattern is disabled for + # that backend, because if the singleton pattern is used, then the object + # returned will be the backend before overwriting + _overridden_backends = set() + _prefix_to_backends = { + 's3': PetrelBackend, + 'http': HTTPBackend, + 'https': HTTPBackend, + } + _overridden_prefixes = set() + + _instances = {} + + def __new__(cls, backend=None, prefix=None, **kwargs): + if backend is None and prefix is None: + backend = 'disk' + if backend is not None and backend not in cls._backends: + raise ValueError( + f'Backend {backend} is not supported. Currently supported ones' + f' are {list(cls._backends.keys())}') + if prefix is not None and prefix not in cls._prefix_to_backends: + raise ValueError( + f'prefix {prefix} is not supported. Currently supported ones ' + f'are {list(cls._prefix_to_backends.keys())}') + + # concatenate the arguments to a unique key for determining whether + # objects with the same arguments were created + arg_key = f'{backend}:{prefix}' + for key, value in kwargs.items(): + arg_key += f':{key}:{value}' + + # if a backend was overridden, it will create a new object + if (arg_key in cls._instances + and backend not in cls._overridden_backends + and prefix not in cls._overridden_prefixes): + _instance = cls._instances[arg_key] + else: + # create a new object and put it to _instance + _instance = super().__new__(cls) + if backend is not None: + _instance.client = cls._backends[backend](**kwargs) + else: + _instance.client = cls._prefix_to_backends[prefix](**kwargs) + + cls._instances[arg_key] = _instance + + return _instance + + @property + def name(self): + return self.client.name + + @property + def allow_symlink(self): + return self.client.allow_symlink + + @staticmethod + def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: + """Parse the prefix of a uri. + + Args: + uri (str | Path): Uri to be parsed that contains the file prefix. + + Examples: + >>> FileClient.parse_uri_prefix('s3://path/of/your/file') + 's3' + + Returns: + str | None: Return the prefix of uri if the uri contains '://' + else ``None``. + """ + assert is_filepath(uri) + uri = str(uri) + if '://' not in uri: + return None + else: + prefix, _ = uri.split('://') + # In the case of PetrelBackend, the prefix may contains the cluster + # name like clusterName:s3 + if ':' in prefix: + _, prefix = prefix.split(':') + return prefix + + @classmethod + def infer_client(cls, + file_client_args: Optional[dict] = None, + uri: Optional[Union[str, Path]] = None) -> 'FileClient': + """Infer a suitable file client based on the URI and arguments. + + Args: + file_client_args (dict, optional): Arguments to instantiate a + FileClient. Default: None. + uri (str | Path, optional): Uri to be parsed that contains the file + prefix. Default: None. + + Examples: + >>> uri = 's3://path/of/your/file' + >>> file_client = FileClient.infer_client(uri=uri) + >>> file_client_args = {'backend': 'petrel'} + >>> file_client = FileClient.infer_client(file_client_args) + + Returns: + FileClient: Instantiated FileClient object. + """ + assert file_client_args is not None or uri is not None + if file_client_args is None: + file_prefix = cls.parse_uri_prefix(uri) # type: ignore + return cls(prefix=file_prefix) + else: + return cls(**file_client_args) + + @classmethod + def _register_backend(cls, name, backend, force=False, prefixes=None): + if not isinstance(name, str): + raise TypeError('the backend name should be a string, ' + f'but got {type(name)}') + if not inspect.isclass(backend): + raise TypeError( + f'backend should be a class but got {type(backend)}') + if not issubclass(backend, BaseStorageBackend): + raise TypeError( + f'backend {backend} is not a subclass of BaseStorageBackend') + if not force and name in cls._backends: + raise KeyError( + f'{name} is already registered as a storage backend, ' + 'add "force=True" if you want to override it') + + if name in cls._backends and force: + cls._overridden_backends.add(name) + cls._backends[name] = backend + + if prefixes is not None: + if isinstance(prefixes, str): + prefixes = [prefixes] + else: + assert isinstance(prefixes, (list, tuple)) + for prefix in prefixes: + if prefix not in cls._prefix_to_backends: + cls._prefix_to_backends[prefix] = backend + elif (prefix in cls._prefix_to_backends) and force: + cls._overridden_prefixes.add(prefix) + cls._prefix_to_backends[prefix] = backend + else: + raise KeyError( + f'{prefix} is already registered as a storage backend,' + ' add "force=True" if you want to override it') + + @classmethod + def register_backend(cls, name, backend=None, force=False, prefixes=None): + """Register a backend to FileClient. + + This method can be used as a normal class method or a decorator. + + .. code-block:: python + + class NewBackend(BaseStorageBackend): + + def get(self, filepath): + return filepath + + def get_text(self, filepath): + return filepath + + FileClient.register_backend('new', NewBackend) + + or + + .. code-block:: python + + @FileClient.register_backend('new') + class NewBackend(BaseStorageBackend): + + def get(self, filepath): + return filepath + + def get_text(self, filepath): + return filepath + + Args: + name (str): The name of the registered backend. + backend (class, optional): The backend class to be registered, + which must be a subclass of :class:`BaseStorageBackend`. + When this method is used as a decorator, backend is None. + Defaults to None. + force (bool, optional): Whether to override the backend if the name + has already been registered. Defaults to False. + prefixes (str or list[str] or tuple[str], optional): The prefixes + of the registered storage backend. Default: None. + `New in version 1.3.15.` + """ + if backend is not None: + cls._register_backend( + name, backend, force=force, prefixes=prefixes) + return + + def _register(backend_cls): + cls._register_backend( + name, backend_cls, force=force, prefixes=prefixes) + return backend_cls + + return _register + + def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: + """Read data from a given ``filepath`` with 'rb' mode. + + Note: + There are two types of return values for ``get``, one is ``bytes`` + and the other is ``memoryview``. The advantage of using memoryview + is that you can avoid copying, and if you want to convert it to + ``bytes``, you can use ``.tobytes()``. + + Args: + filepath (str or Path): Path to read data. + + Returns: + bytes | memoryview: Expected bytes object or a memory view of the + bytes object. + """ + return self.client.get(filepath) + + def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: + """Read data from a given ``filepath`` with 'r' mode. + + Args: + filepath (str or Path): Path to read data. + encoding (str): The encoding format used to open the ``filepath``. + Default: 'utf-8'. + + Returns: + str: Expected text reading from ``filepath``. + """ + return self.client.get_text(filepath, encoding) + + def put(self, obj: bytes, filepath: Union[str, Path]) -> None: + """Write data to a given ``filepath`` with 'wb' mode. + + Note: + ``put`` should create a directory if the directory of ``filepath`` + does not exist. + + Args: + obj (bytes): Data to be written. + filepath (str or Path): Path to write data. + """ + self.client.put(obj, filepath) + + def put_text(self, obj: str, filepath: Union[str, Path]) -> None: + """Write data to a given ``filepath`` with 'w' mode. + + Note: + ``put_text`` should create a directory if the directory of + ``filepath`` does not exist. + + Args: + obj (str): Data to be written. + filepath (str or Path): Path to write data. + encoding (str, optional): The encoding format used to open the + `filepath`. Default: 'utf-8'. + """ + self.client.put_text(obj, filepath) + + def remove(self, filepath: Union[str, Path]) -> None: + """Remove a file. + + Args: + filepath (str, Path): Path to be removed. + """ + self.client.remove(filepath) + + def exists(self, filepath: Union[str, Path]) -> bool: + """Check whether a file path exists. + + Args: + filepath (str or Path): Path to be checked whether exists. + + Returns: + bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. + """ + return self.client.exists(filepath) + + def isdir(self, filepath: Union[str, Path]) -> bool: + """Check whether a file path is a directory. + + Args: + filepath (str or Path): Path to be checked whether it is a + directory. + + Returns: + bool: Return ``True`` if ``filepath`` points to a directory, + ``False`` otherwise. + """ + return self.client.isdir(filepath) + + def isfile(self, filepath: Union[str, Path]) -> bool: + """Check whether a file path is a file. + + Args: + filepath (str or Path): Path to be checked whether it is a file. + + Returns: + bool: Return ``True`` if ``filepath`` points to a file, ``False`` + otherwise. + """ + return self.client.isfile(filepath) + + def join_path(self, filepath: Union[str, Path], + *filepaths: Union[str, Path]) -> str: + """Concatenate all file paths. + + Join one or more filepath components intelligently. The return value + is the concatenation of filepath and any members of *filepaths. + + Args: + filepath (str or Path): Path to be concatenated. + + Returns: + str: The result of concatenation. + """ + return self.client.join_path(filepath, *filepaths) + + @contextmanager + def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: + """Download data from ``filepath`` and write the data to local path. + + ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It + can be called with ``with`` statement, and when exists from the + ``with`` statement, the temporary path will be released. + + Note: + If the ``filepath`` is a local path, just return itself. + + .. warning:: + ``get_local_path`` is an experimental interface that may change in + the future. + + Args: + filepath (str or Path): Path to be read data. + + Examples: + >>> file_client = FileClient(prefix='s3') + >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: + ... # do something here + + Yields: + Iterable[str]: Only yield one path. + """ + with self.client.get_local_path(str(filepath)) as local_path: + yield local_path + + def list_dir_or_file(self, + dir_path: Union[str, Path], + list_dir: bool = True, + list_file: bool = True, + suffix: Optional[Union[str, Tuple[str]]] = None, + recursive: bool = False) -> Iterator[str]: + """Scan a directory to find the interested directories or files in + arbitrary order. + + Note: + :meth:`list_dir_or_file` returns the path relative to ``dir_path``. + + Args: + dir_path (str | Path): Path of the directory. + list_dir (bool): List the directories. Default: True. + list_file (bool): List the path of files. Default: True. + suffix (str or tuple[str], optional): File suffix + that we are interested in. Default: None. + recursive (bool): If set to True, recursively scan the + directory. Default: False. + + Yields: + Iterable[str]: A relative path to ``dir_path``. + """ + yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, + suffix, recursive) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..aa24d91972837b8756b225f4879bac20436eb72a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/__init__.py @@ -0,0 +1,7 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .base import BaseFileHandler +from .json_handler import JsonHandler +from .pickle_handler import PickleHandler +from .yaml_handler import YamlHandler + +__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/base.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/base.py new file mode 100644 index 0000000000000000000000000000000000000000..288878bc57282fbb2f12b32290152ca8e9d3cab0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/base.py @@ -0,0 +1,30 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from abc import ABCMeta, abstractmethod + + +class BaseFileHandler(metaclass=ABCMeta): + # `str_like` is a flag to indicate whether the type of file object is + # str-like object or bytes-like object. Pickle only processes bytes-like + # objects but json only processes str-like object. If it is str-like + # object, `StringIO` will be used to process the buffer. + str_like = True + + @abstractmethod + def load_from_fileobj(self, file, **kwargs): + pass + + @abstractmethod + def dump_to_fileobj(self, obj, file, **kwargs): + pass + + @abstractmethod + def dump_to_str(self, obj, **kwargs): + pass + + def load_from_path(self, filepath, mode='r', **kwargs): + with open(filepath, mode) as f: + return self.load_from_fileobj(f, **kwargs) + + def dump_to_path(self, obj, filepath, mode='w', **kwargs): + with open(filepath, mode) as f: + self.dump_to_fileobj(obj, f, **kwargs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/json_handler.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/json_handler.py new file mode 100644 index 0000000000000000000000000000000000000000..18d4f15f74139d20adff18b20be5529c592a66b6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/json_handler.py @@ -0,0 +1,36 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import json + +import numpy as np + +from .base import BaseFileHandler + + +def set_default(obj): + """Set default json values for non-serializable values. + + It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. + It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, + etc.) into plain numbers of plain python built-in types. + """ + if isinstance(obj, (set, range)): + return list(obj) + elif isinstance(obj, np.ndarray): + return obj.tolist() + elif isinstance(obj, np.generic): + return obj.item() + raise TypeError(f'{type(obj)} is unsupported for json dump') + + +class JsonHandler(BaseFileHandler): + + def load_from_fileobj(self, file): + return json.load(file) + + def dump_to_fileobj(self, obj, file, **kwargs): + kwargs.setdefault('default', set_default) + json.dump(obj, file, **kwargs) + + def dump_to_str(self, obj, **kwargs): + kwargs.setdefault('default', set_default) + return json.dumps(obj, **kwargs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/pickle_handler.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/pickle_handler.py new file mode 100644 index 0000000000000000000000000000000000000000..b37c79bed4ef9fd8913715e62dbe3fc5cafdc3aa --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/pickle_handler.py @@ -0,0 +1,28 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import pickle + +from .base import BaseFileHandler + + +class PickleHandler(BaseFileHandler): + + str_like = False + + def load_from_fileobj(self, file, **kwargs): + return pickle.load(file, **kwargs) + + def load_from_path(self, filepath, **kwargs): + return super(PickleHandler, self).load_from_path( + filepath, mode='rb', **kwargs) + + def dump_to_str(self, obj, **kwargs): + kwargs.setdefault('protocol', 2) + return pickle.dumps(obj, **kwargs) + + def dump_to_fileobj(self, obj, file, **kwargs): + kwargs.setdefault('protocol', 2) + pickle.dump(obj, file, **kwargs) + + def dump_to_path(self, obj, filepath, **kwargs): + super(PickleHandler, self).dump_to_path( + obj, filepath, mode='wb', **kwargs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/yaml_handler.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/yaml_handler.py new file mode 100644 index 0000000000000000000000000000000000000000..c5aa2eea1e8c76f8baf753d1c8c959dee665e543 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/handlers/yaml_handler.py @@ -0,0 +1,24 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import yaml + +try: + from yaml import CLoader as Loader, CDumper as Dumper +except ImportError: + from yaml import Loader, Dumper + +from .base import BaseFileHandler # isort:skip + + +class YamlHandler(BaseFileHandler): + + def load_from_fileobj(self, file, **kwargs): + kwargs.setdefault('Loader', Loader) + return yaml.load(file, **kwargs) + + def dump_to_fileobj(self, obj, file, **kwargs): + kwargs.setdefault('Dumper', Dumper) + yaml.dump(obj, file, **kwargs) + + def dump_to_str(self, obj, **kwargs): + kwargs.setdefault('Dumper', Dumper) + return yaml.dump(obj, **kwargs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/io.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/io.py new file mode 100644 index 0000000000000000000000000000000000000000..aaefde58aa3ea5b58f86249ce7e1c40c186eb8dd --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/io.py @@ -0,0 +1,151 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from io import BytesIO, StringIO +from pathlib import Path + +from ..utils import is_list_of, is_str +from .file_client import FileClient +from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler + +file_handlers = { + 'json': JsonHandler(), + 'yaml': YamlHandler(), + 'yml': YamlHandler(), + 'pickle': PickleHandler(), + 'pkl': PickleHandler() +} + + +def load(file, file_format=None, file_client_args=None, **kwargs): + """Load data from json/yaml/pickle files. + + This method provides a unified api for loading data from serialized files. + + Note: + In v1.3.16 and later, ``load`` supports loading data from serialized + files those can be storaged in different backends. + + Args: + file (str or :obj:`Path` or file-like object): Filename or a file-like + object. + file_format (str, optional): If not specified, the file format will be + inferred from the file extension, otherwise use the specified one. + Currently supported formats include "json", "yaml/yml" and + "pickle/pkl". + file_client_args (dict, optional): Arguments to instantiate a + FileClient. See :class:`mmcv.fileio.FileClient` for details. + Default: None. + + Examples: + >>> load('/path/of/your/file') # file is storaged in disk + >>> load('https://path/of/your/file') # file is storaged in Internet + >>> load('s3://path/of/your/file') # file is storaged in petrel + + Returns: + The content from the file. + """ + if isinstance(file, Path): + file = str(file) + if file_format is None and is_str(file): + file_format = file.split('.')[-1] + if file_format not in file_handlers: + raise TypeError(f'Unsupported format: {file_format}') + + handler = file_handlers[file_format] + if is_str(file): + file_client = FileClient.infer_client(file_client_args, file) + if handler.str_like: + with StringIO(file_client.get_text(file)) as f: + obj = handler.load_from_fileobj(f, **kwargs) + else: + with BytesIO(file_client.get(file)) as f: + obj = handler.load_from_fileobj(f, **kwargs) + elif hasattr(file, 'read'): + obj = handler.load_from_fileobj(file, **kwargs) + else: + raise TypeError('"file" must be a filepath str or a file-object') + return obj + + +def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): + """Dump data to json/yaml/pickle strings or files. + + This method provides a unified api for dumping data as strings or to files, + and also supports custom arguments for each file format. + + Note: + In v1.3.16 and later, ``dump`` supports dumping data as strings or to + files which is saved to different backends. + + Args: + obj (any): The python object to be dumped. + file (str or :obj:`Path` or file-like object, optional): If not + specified, then the object is dumped to a str, otherwise to a file + specified by the filename or file-like object. + file_format (str, optional): Same as :func:`load`. + file_client_args (dict, optional): Arguments to instantiate a + FileClient. See :class:`mmcv.fileio.FileClient` for details. + Default: None. + + Examples: + >>> dump('hello world', '/path/of/your/file') # disk + >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel + + Returns: + bool: True for success, False otherwise. + """ + if isinstance(file, Path): + file = str(file) + if file_format is None: + if is_str(file): + file_format = file.split('.')[-1] + elif file is None: + raise ValueError( + 'file_format must be specified since file is None') + if file_format not in file_handlers: + raise TypeError(f'Unsupported format: {file_format}') + + handler = file_handlers[file_format] + if file is None: + return handler.dump_to_str(obj, **kwargs) + elif is_str(file): + file_client = FileClient.infer_client(file_client_args, file) + if handler.str_like: + with StringIO() as f: + handler.dump_to_fileobj(obj, f, **kwargs) + file_client.put_text(f.getvalue(), file) + else: + with BytesIO() as f: + handler.dump_to_fileobj(obj, f, **kwargs) + file_client.put(f.getvalue(), file) + elif hasattr(file, 'write'): + handler.dump_to_fileobj(obj, file, **kwargs) + else: + raise TypeError('"file" must be a filename str or a file-object') + + +def _register_handler(handler, file_formats): + """Register a handler for some file extensions. + + Args: + handler (:obj:`BaseFileHandler`): Handler to be registered. + file_formats (str or list[str]): File formats to be handled by this + handler. + """ + if not isinstance(handler, BaseFileHandler): + raise TypeError( + f'handler must be a child of BaseFileHandler, not {type(handler)}') + if isinstance(file_formats, str): + file_formats = [file_formats] + if not is_list_of(file_formats, str): + raise TypeError('file_formats must be a str or a list of str') + for ext in file_formats: + file_handlers[ext] = handler + + +def register_handler(file_formats, **kwargs): + + def wrap(cls): + _register_handler(cls(**kwargs), file_formats) + return cls + + return wrap diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/parse.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/parse.py new file mode 100644 index 0000000000000000000000000000000000000000..f60f0d611b8d75692221d0edd7dc993b0a6445c9 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/fileio/parse.py @@ -0,0 +1,97 @@ +# Copyright (c) OpenMMLab. All rights reserved. + +from io import StringIO + +from .file_client import FileClient + + +def list_from_file(filename, + prefix='', + offset=0, + max_num=0, + encoding='utf-8', + file_client_args=None): + """Load a text file and parse the content as a list of strings. + + Note: + In v1.3.16 and later, ``list_from_file`` supports loading a text file + which can be storaged in different backends and parsing the content as + a list for strings. + + Args: + filename (str): Filename. + prefix (str): The prefix to be inserted to the beginning of each item. + offset (int): The offset of lines. + max_num (int): The maximum number of lines to be read, + zeros and negatives mean no limitation. + encoding (str): Encoding used to open the file. Default utf-8. + file_client_args (dict, optional): Arguments to instantiate a + FileClient. See :class:`mmcv.fileio.FileClient` for details. + Default: None. + + Examples: + >>> list_from_file('/path/of/your/file') # disk + ['hello', 'world'] + >>> list_from_file('s3://path/of/your/file') # ceph or petrel + ['hello', 'world'] + + Returns: + list[str]: A list of strings. + """ + cnt = 0 + item_list = [] + file_client = FileClient.infer_client(file_client_args, filename) + with StringIO(file_client.get_text(filename, encoding)) as f: + for _ in range(offset): + f.readline() + for line in f: + if 0 < max_num <= cnt: + break + item_list.append(prefix + line.rstrip('\n\r')) + cnt += 1 + return item_list + + +def dict_from_file(filename, + key_type=str, + encoding='utf-8', + file_client_args=None): + """Load a text file and parse the content as a dict. + + Each line of the text file will be two or more columns split by + whitespaces or tabs. The first column will be parsed as dict keys, and + the following columns will be parsed as dict values. + + Note: + In v1.3.16 and later, ``dict_from_file`` supports loading a text file + which can be storaged in different backends and parsing the content as + a dict. + + Args: + filename(str): Filename. + key_type(type): Type of the dict keys. str is user by default and + type conversion will be performed if specified. + encoding (str): Encoding used to open the file. Default utf-8. + file_client_args (dict, optional): Arguments to instantiate a + FileClient. See :class:`mmcv.fileio.FileClient` for details. + Default: None. + + Examples: + >>> dict_from_file('/path/of/your/file') # disk + {'key1': 'value1', 'key2': 'value2'} + >>> dict_from_file('s3://path/of/your/file') # ceph or petrel + {'key1': 'value1', 'key2': 'value2'} + + Returns: + dict: The parsed contents. + """ + mapping = {} + file_client = FileClient.infer_client(file_client_args, filename) + with StringIO(file_client.get_text(filename, encoding)) as f: + for line in f: + items = line.rstrip('\n').split() + assert len(items) >= 2 + key = key_type(items[0]) + val = items[1:] if len(items) > 2 else items[1] + mapping[key] = val + return mapping diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d0051d609d3de4e7562e3fe638335c66617c4d91 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/__init__.py @@ -0,0 +1,28 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, + gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, + rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) +from .geometric import (cutout, imcrop, imflip, imflip_, impad, + impad_to_multiple, imrescale, imresize, imresize_like, + imresize_to_multiple, imrotate, imshear, imtranslate, + rescale_size) +from .io import imfrombytes, imread, imwrite, supported_backends, use_backend +from .misc import tensor2imgs +from .photometric import (adjust_brightness, adjust_color, adjust_contrast, + adjust_lighting, adjust_sharpness, auto_contrast, + clahe, imdenormalize, imequalize, iminvert, + imnormalize, imnormalize_, lut_transform, posterize, + solarize) + +__all__ = [ + 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', + 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', + 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', + 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', + 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', + 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', + 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', + 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', + 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', + 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/colorspace.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/colorspace.py new file mode 100644 index 0000000000000000000000000000000000000000..814533952fdfda23d67cb6a3073692d8c1156add --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/colorspace.py @@ -0,0 +1,306 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import cv2 +import numpy as np + + +def imconvert(img, src, dst): + """Convert an image from the src colorspace to dst colorspace. + + Args: + img (ndarray): The input image. + src (str): The source colorspace, e.g., 'rgb', 'hsv'. + dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. + + Returns: + ndarray: The converted image. + """ + code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') + out_img = cv2.cvtColor(img, code) + return out_img + + +def bgr2gray(img, keepdim=False): + """Convert a BGR image to grayscale image. + + Args: + img (ndarray): The input image. + keepdim (bool): If False (by default), then return the grayscale image + with 2 dims, otherwise 3 dims. + + Returns: + ndarray: The converted grayscale image. + """ + out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + if keepdim: + out_img = out_img[..., None] + return out_img + + +def rgb2gray(img, keepdim=False): + """Convert a RGB image to grayscale image. + + Args: + img (ndarray): The input image. + keepdim (bool): If False (by default), then return the grayscale image + with 2 dims, otherwise 3 dims. + + Returns: + ndarray: The converted grayscale image. + """ + out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) + if keepdim: + out_img = out_img[..., None] + return out_img + + +def gray2bgr(img): + """Convert a grayscale image to BGR image. + + Args: + img (ndarray): The input image. + + Returns: + ndarray: The converted BGR image. + """ + img = img[..., None] if img.ndim == 2 else img + out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) + return out_img + + +def gray2rgb(img): + """Convert a grayscale image to RGB image. + + Args: + img (ndarray): The input image. + + Returns: + ndarray: The converted RGB image. + """ + img = img[..., None] if img.ndim == 2 else img + out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) + return out_img + + +def _convert_input_type_range(img): + """Convert the type and range of the input image. + + It converts the input image to np.float32 type and range of [0, 1]. + It is mainly used for pre-processing the input image in colorspace + conversion functions such as rgb2ycbcr and ycbcr2rgb. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + + Returns: + (ndarray): The converted image with type of np.float32 and range of + [0, 1]. + """ + img_type = img.dtype + img = img.astype(np.float32) + if img_type == np.float32: + pass + elif img_type == np.uint8: + img /= 255. + else: + raise TypeError('The img type should be np.float32 or np.uint8, ' + f'but got {img_type}') + return img + + +def _convert_output_type_range(img, dst_type): + """Convert the type and range of the image according to dst_type. + + It converts the image to desired type and range. If `dst_type` is np.uint8, + images will be converted to np.uint8 type with range [0, 255]. If + `dst_type` is np.float32, it converts the image to np.float32 type with + range [0, 1]. + It is mainly used for post-processing images in colorspace conversion + functions such as rgb2ycbcr and ycbcr2rgb. + + Args: + img (ndarray): The image to be converted with np.float32 type and + range [0, 255]. + dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it + converts the image to np.uint8 type with range [0, 255]. If + dst_type is np.float32, it converts the image to np.float32 type + with range [0, 1]. + + Returns: + (ndarray): The converted image with desired type and range. + """ + if dst_type not in (np.uint8, np.float32): + raise TypeError('The dst_type should be np.float32 or np.uint8, ' + f'but got {dst_type}') + if dst_type == np.uint8: + img = img.round() + else: + img /= 255. + return img.astype(dst_type) + + +def rgb2ycbcr(img, y_only=False): + """Convert a RGB image to YCbCr image. + + This function produces the same results as Matlab's `rgb2ycbcr` function. + It implements the ITU-R BT.601 conversion for standard-definition + television. See more details in + https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. + + It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. + In OpenCV, it implements a JPEG conversion. See more details in + https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + y_only (bool): Whether to only return Y channel. Default: False. + + Returns: + ndarray: The converted YCbCr image. The output image has the same type + and range as input image. + """ + img_type = img.dtype + img = _convert_input_type_range(img) + if y_only: + out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 + else: + out_img = np.matmul( + img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], + [24.966, 112.0, -18.214]]) + [16, 128, 128] + out_img = _convert_output_type_range(out_img, img_type) + return out_img + + +def bgr2ycbcr(img, y_only=False): + """Convert a BGR image to YCbCr image. + + The bgr version of rgb2ycbcr. + It implements the ITU-R BT.601 conversion for standard-definition + television. See more details in + https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. + + It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. + In OpenCV, it implements a JPEG conversion. See more details in + https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + y_only (bool): Whether to only return Y channel. Default: False. + + Returns: + ndarray: The converted YCbCr image. The output image has the same type + and range as input image. + """ + img_type = img.dtype + img = _convert_input_type_range(img) + if y_only: + out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 + else: + out_img = np.matmul( + img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], + [65.481, -37.797, 112.0]]) + [16, 128, 128] + out_img = _convert_output_type_range(out_img, img_type) + return out_img + + +def ycbcr2rgb(img): + """Convert a YCbCr image to RGB image. + + This function produces the same results as Matlab's ycbcr2rgb function. + It implements the ITU-R BT.601 conversion for standard-definition + television. See more details in + https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. + + It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. + In OpenCV, it implements a JPEG conversion. See more details in + https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + + Returns: + ndarray: The converted RGB image. The output image has the same type + and range as input image. + """ + img_type = img.dtype + img = _convert_input_type_range(img) * 255 + out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], + [0, -0.00153632, 0.00791071], + [0.00625893, -0.00318811, 0]]) * 255.0 + [ + -222.921, 135.576, -276.836 + ] + out_img = _convert_output_type_range(out_img, img_type) + return out_img + + +def ycbcr2bgr(img): + """Convert a YCbCr image to BGR image. + + The bgr version of ycbcr2rgb. + It implements the ITU-R BT.601 conversion for standard-definition + television. See more details in + https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. + + It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. + In OpenCV, it implements a JPEG conversion. See more details in + https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + + Returns: + ndarray: The converted BGR image. The output image has the same type + and range as input image. + """ + img_type = img.dtype + img = _convert_input_type_range(img) * 255 + out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], + [0.00791071, -0.00153632, 0], + [0, -0.00318811, 0.00625893]]) * 255.0 + [ + -276.836, 135.576, -222.921 + ] + out_img = _convert_output_type_range(out_img, img_type) + return out_img + + +def convert_color_factory(src, dst): + + code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') + + def convert_color(img): + out_img = cv2.cvtColor(img, code) + return out_img + + convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} + image. + + Args: + img (ndarray or str): The input image. + + Returns: + ndarray: The converted {dst.upper()} image. + """ + + return convert_color + + +bgr2rgb = convert_color_factory('bgr', 'rgb') + +rgb2bgr = convert_color_factory('rgb', 'bgr') + +bgr2hsv = convert_color_factory('bgr', 'hsv') + +hsv2bgr = convert_color_factory('hsv', 'bgr') + +bgr2hls = convert_color_factory('bgr', 'hls') + +hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/geometric.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/geometric.py new file mode 100644 index 0000000000000000000000000000000000000000..cf97c201cb4e43796c911919d03fb26a07ed817d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/geometric.py @@ -0,0 +1,728 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import numbers + +import cv2 +import numpy as np + +from ..utils import to_2tuple +from .io import imread_backend + +try: + from PIL import Image +except ImportError: + Image = None + + +def _scale_size(size, scale): + """Rescale a size by a ratio. + + Args: + size (tuple[int]): (w, h). + scale (float | tuple(float)): Scaling factor. + + Returns: + tuple[int]: scaled size. + """ + if isinstance(scale, (float, int)): + scale = (scale, scale) + w, h = size + return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) + + +cv2_interp_codes = { + 'nearest': cv2.INTER_NEAREST, + 'bilinear': cv2.INTER_LINEAR, + 'bicubic': cv2.INTER_CUBIC, + 'area': cv2.INTER_AREA, + 'lanczos': cv2.INTER_LANCZOS4 +} + +if Image is not None: + pillow_interp_codes = { + 'nearest': Image.NEAREST, + 'bilinear': Image.BILINEAR, + 'bicubic': Image.BICUBIC, + 'box': Image.BOX, + 'lanczos': Image.LANCZOS, + 'hamming': Image.HAMMING + } + + +def imresize(img, + size, + return_scale=False, + interpolation='bilinear', + out=None, + backend=None): + """Resize image to a given size. + + Args: + img (ndarray): The input image. + size (tuple[int]): Target size (w, h). + return_scale (bool): Whether to return `w_scale` and `h_scale`. + interpolation (str): Interpolation method, accepted values are + "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' + backend, "nearest", "bilinear" for 'pillow' backend. + out (ndarray): The output destination. + backend (str | None): The image resize backend type. Options are `cv2`, + `pillow`, `None`. If backend is None, the global imread_backend + specified by ``mmcv.use_backend()`` will be used. Default: None. + + Returns: + tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or + `resized_img`. + """ + h, w = img.shape[:2] + if backend is None: + backend = imread_backend + if backend not in ['cv2', 'pillow']: + raise ValueError(f'backend: {backend} is not supported for resize.' + f"Supported backends are 'cv2', 'pillow'") + + if backend == 'pillow': + assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' + pil_image = Image.fromarray(img) + pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) + resized_img = np.array(pil_image) + else: + resized_img = cv2.resize( + img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) + if not return_scale: + return resized_img + else: + w_scale = size[0] / w + h_scale = size[1] / h + return resized_img, w_scale, h_scale + + +def imresize_to_multiple(img, + divisor, + size=None, + scale_factor=None, + keep_ratio=False, + return_scale=False, + interpolation='bilinear', + out=None, + backend=None): + """Resize image according to a given size or scale factor and then rounds + up the the resized or rescaled image size to the nearest value that can be + divided by the divisor. + + Args: + img (ndarray): The input image. + divisor (int | tuple): Resized image size will be a multiple of + divisor. If divisor is a tuple, divisor should be + (w_divisor, h_divisor). + size (None | int | tuple[int]): Target size (w, h). Default: None. + scale_factor (None | float | tuple[float]): Multiplier for spatial + size. Should match input size if it is a tuple and the 2D style is + (w_scale_factor, h_scale_factor). Default: None. + keep_ratio (bool): Whether to keep the aspect ratio when resizing the + image. Default: False. + return_scale (bool): Whether to return `w_scale` and `h_scale`. + interpolation (str): Interpolation method, accepted values are + "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' + backend, "nearest", "bilinear" for 'pillow' backend. + out (ndarray): The output destination. + backend (str | None): The image resize backend type. Options are `cv2`, + `pillow`, `None`. If backend is None, the global imread_backend + specified by ``mmcv.use_backend()`` will be used. Default: None. + + Returns: + tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or + `resized_img`. + """ + h, w = img.shape[:2] + if size is not None and scale_factor is not None: + raise ValueError('only one of size or scale_factor should be defined') + elif size is None and scale_factor is None: + raise ValueError('one of size or scale_factor should be defined') + elif size is not None: + size = to_2tuple(size) + if keep_ratio: + size = rescale_size((w, h), size, return_scale=False) + else: + size = _scale_size((w, h), scale_factor) + + divisor = to_2tuple(divisor) + size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) + resized_img, w_scale, h_scale = imresize( + img, + size, + return_scale=True, + interpolation=interpolation, + out=out, + backend=backend) + if return_scale: + return resized_img, w_scale, h_scale + else: + return resized_img + + +def imresize_like(img, + dst_img, + return_scale=False, + interpolation='bilinear', + backend=None): + """Resize image to the same size of a given image. + + Args: + img (ndarray): The input image. + dst_img (ndarray): The target image. + return_scale (bool): Whether to return `w_scale` and `h_scale`. + interpolation (str): Same as :func:`resize`. + backend (str | None): Same as :func:`resize`. + + Returns: + tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or + `resized_img`. + """ + h, w = dst_img.shape[:2] + return imresize(img, (w, h), return_scale, interpolation, backend=backend) + + +def rescale_size(old_size, scale, return_scale=False): + """Calculate the new size to be rescaled to. + + Args: + old_size (tuple[int]): The old size (w, h) of image. + scale (float | tuple[int]): The scaling factor or maximum size. + If it is a float number, then the image will be rescaled by this + factor, else if it is a tuple of 2 integers, then the image will + be rescaled as large as possible within the scale. + return_scale (bool): Whether to return the scaling factor besides the + rescaled image size. + + Returns: + tuple[int]: The new rescaled image size. + """ + w, h = old_size + if isinstance(scale, (float, int)): + if scale <= 0: + raise ValueError(f'Invalid scale {scale}, must be positive.') + scale_factor = scale + elif isinstance(scale, tuple): + max_long_edge = max(scale) + max_short_edge = min(scale) + scale_factor = min(max_long_edge / max(h, w), + max_short_edge / min(h, w)) + else: + raise TypeError( + f'Scale must be a number or tuple of int, but got {type(scale)}') + + new_size = _scale_size((w, h), scale_factor) + + if return_scale: + return new_size, scale_factor + else: + return new_size + + +def imrescale(img, + scale, + return_scale=False, + interpolation='bilinear', + backend=None): + """Resize image while keeping the aspect ratio. + + Args: + img (ndarray): The input image. + scale (float | tuple[int]): The scaling factor or maximum size. + If it is a float number, then the image will be rescaled by this + factor, else if it is a tuple of 2 integers, then the image will + be rescaled as large as possible within the scale. + return_scale (bool): Whether to return the scaling factor besides the + rescaled image. + interpolation (str): Same as :func:`resize`. + backend (str | None): Same as :func:`resize`. + + Returns: + ndarray: The rescaled image. + """ + h, w = img.shape[:2] + new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) + rescaled_img = imresize( + img, new_size, interpolation=interpolation, backend=backend) + if return_scale: + return rescaled_img, scale_factor + else: + return rescaled_img + + +def imflip(img, direction='horizontal'): + """Flip an image horizontally or vertically. + + Args: + img (ndarray): Image to be flipped. + direction (str): The flip direction, either "horizontal" or + "vertical" or "diagonal". + + Returns: + ndarray: The flipped image. + """ + assert direction in ['horizontal', 'vertical', 'diagonal'] + if direction == 'horizontal': + return np.flip(img, axis=1) + elif direction == 'vertical': + return np.flip(img, axis=0) + else: + return np.flip(img, axis=(0, 1)) + + +def imflip_(img, direction='horizontal'): + """Inplace flip an image horizontally or vertically. + + Args: + img (ndarray): Image to be flipped. + direction (str): The flip direction, either "horizontal" or + "vertical" or "diagonal". + + Returns: + ndarray: The flipped image (inplace). + """ + assert direction in ['horizontal', 'vertical', 'diagonal'] + if direction == 'horizontal': + return cv2.flip(img, 1, img) + elif direction == 'vertical': + return cv2.flip(img, 0, img) + else: + return cv2.flip(img, -1, img) + + +def imrotate(img, + angle, + center=None, + scale=1.0, + border_value=0, + interpolation='bilinear', + auto_bound=False): + """Rotate an image. + + Args: + img (ndarray): Image to be rotated. + angle (float): Rotation angle in degrees, positive values mean + clockwise rotation. + center (tuple[float], optional): Center point (w, h) of the rotation in + the source image. If not specified, the center of the image will be + used. + scale (float): Isotropic scale factor. + border_value (int): Border value. + interpolation (str): Same as :func:`resize`. + auto_bound (bool): Whether to adjust the image size to cover the whole + rotated image. + + Returns: + ndarray: The rotated image. + """ + if center is not None and auto_bound: + raise ValueError('`auto_bound` conflicts with `center`') + h, w = img.shape[:2] + if center is None: + center = ((w - 1) * 0.5, (h - 1) * 0.5) + assert isinstance(center, tuple) + + matrix = cv2.getRotationMatrix2D(center, -angle, scale) + if auto_bound: + cos = np.abs(matrix[0, 0]) + sin = np.abs(matrix[0, 1]) + new_w = h * sin + w * cos + new_h = h * cos + w * sin + matrix[0, 2] += (new_w - w) * 0.5 + matrix[1, 2] += (new_h - h) * 0.5 + w = int(np.round(new_w)) + h = int(np.round(new_h)) + rotated = cv2.warpAffine( + img, + matrix, (w, h), + flags=cv2_interp_codes[interpolation], + borderValue=border_value) + return rotated + + +def bbox_clip(bboxes, img_shape): + """Clip bboxes to fit the image shape. + + Args: + bboxes (ndarray): Shape (..., 4*k) + img_shape (tuple[int]): (height, width) of the image. + + Returns: + ndarray: Clipped bboxes. + """ + assert bboxes.shape[-1] % 4 == 0 + cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) + cmin[0::2] = img_shape[1] - 1 + cmin[1::2] = img_shape[0] - 1 + clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) + return clipped_bboxes + + +def bbox_scaling(bboxes, scale, clip_shape=None): + """Scaling bboxes w.r.t the box center. + + Args: + bboxes (ndarray): Shape(..., 4). + scale (float): Scaling factor. + clip_shape (tuple[int], optional): If specified, bboxes that exceed the + boundary will be clipped according to the given shape (h, w). + + Returns: + ndarray: Scaled bboxes. + """ + if float(scale) == 1.0: + scaled_bboxes = bboxes.copy() + else: + w = bboxes[..., 2] - bboxes[..., 0] + 1 + h = bboxes[..., 3] - bboxes[..., 1] + 1 + dw = (w * (scale - 1)) * 0.5 + dh = (h * (scale - 1)) * 0.5 + scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) + if clip_shape is not None: + return bbox_clip(scaled_bboxes, clip_shape) + else: + return scaled_bboxes + + +def imcrop(img, bboxes, scale=1.0, pad_fill=None): + """Crop image patches. + + 3 steps: scale the bboxes -> clip bboxes -> crop and pad. + + Args: + img (ndarray): Image to be cropped. + bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. + scale (float, optional): Scale ratio of bboxes, the default value + 1.0 means no padding. + pad_fill (Number | list[Number]): Value to be filled for padding. + Default: None, which means no padding. + + Returns: + list[ndarray] | ndarray: The cropped image patches. + """ + chn = 1 if img.ndim == 2 else img.shape[2] + if pad_fill is not None: + if isinstance(pad_fill, (int, float)): + pad_fill = [pad_fill for _ in range(chn)] + assert len(pad_fill) == chn + + _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes + scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) + clipped_bbox = bbox_clip(scaled_bboxes, img.shape) + + patches = [] + for i in range(clipped_bbox.shape[0]): + x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) + if pad_fill is None: + patch = img[y1:y2 + 1, x1:x2 + 1, ...] + else: + _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) + if chn == 1: + patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) + else: + patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) + patch = np.array( + pad_fill, dtype=img.dtype) * np.ones( + patch_shape, dtype=img.dtype) + x_start = 0 if _x1 >= 0 else -_x1 + y_start = 0 if _y1 >= 0 else -_y1 + w = x2 - x1 + 1 + h = y2 - y1 + 1 + patch[y_start:y_start + h, x_start:x_start + w, + ...] = img[y1:y1 + h, x1:x1 + w, ...] + patches.append(patch) + + if bboxes.ndim == 1: + return patches[0] + else: + return patches + + +def impad(img, + *, + shape=None, + padding=None, + pad_val=0, + padding_mode='constant'): + """Pad the given image to a certain shape or pad on all sides with + specified padding mode and padding value. + + Args: + img (ndarray): Image to be padded. + shape (tuple[int]): Expected padding shape (h, w). Default: None. + padding (int or tuple[int]): Padding on each border. If a single int is + provided this is used to pad all borders. If tuple of length 2 is + provided this is the padding on left/right and top/bottom + respectively. If a tuple of length 4 is provided this is the + padding for the left, top, right and bottom borders respectively. + Default: None. Note that `shape` and `padding` can not be both + set. + pad_val (Number | Sequence[Number]): Values to be filled in padding + areas when padding_mode is 'constant'. Default: 0. + padding_mode (str): Type of padding. Should be: constant, edge, + reflect or symmetric. Default: constant. + + - constant: pads with a constant value, this value is specified + with pad_val. + - edge: pads with the last value at the edge of the image. + - reflect: pads with reflection of image without repeating the + last value on the edge. For example, padding [1, 2, 3, 4] + with 2 elements on both sides in reflect mode will result + in [3, 2, 1, 2, 3, 4, 3, 2]. + - symmetric: pads with reflection of image repeating the last + value on the edge. For example, padding [1, 2, 3, 4] with + 2 elements on both sides in symmetric mode will result in + [2, 1, 1, 2, 3, 4, 4, 3] + + Returns: + ndarray: The padded image. + """ + + assert (shape is not None) ^ (padding is not None) + if shape is not None: + padding = (0, 0, shape[1] - img.shape[1], shape[0] - img.shape[0]) + + # check pad_val + if isinstance(pad_val, tuple): + assert len(pad_val) == img.shape[-1] + elif not isinstance(pad_val, numbers.Number): + raise TypeError('pad_val must be a int or a tuple. ' + f'But received {type(pad_val)}') + + # check padding + if isinstance(padding, tuple) and len(padding) in [2, 4]: + if len(padding) == 2: + padding = (padding[0], padding[1], padding[0], padding[1]) + elif isinstance(padding, numbers.Number): + padding = (padding, padding, padding, padding) + else: + raise ValueError('Padding must be a int or a 2, or 4 element tuple.' + f'But received {padding}') + + # check padding mode + assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] + + border_type = { + 'constant': cv2.BORDER_CONSTANT, + 'edge': cv2.BORDER_REPLICATE, + 'reflect': cv2.BORDER_REFLECT_101, + 'symmetric': cv2.BORDER_REFLECT + } + img = cv2.copyMakeBorder( + img, + padding[1], + padding[3], + padding[0], + padding[2], + border_type[padding_mode], + value=pad_val) + + return img + + +def impad_to_multiple(img, divisor, pad_val=0): + """Pad an image to ensure each edge to be multiple to some number. + + Args: + img (ndarray): Image to be padded. + divisor (int): Padded image edges will be multiple to divisor. + pad_val (Number | Sequence[Number]): Same as :func:`impad`. + + Returns: + ndarray: The padded image. + """ + pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor + pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor + return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) + + +def cutout(img, shape, pad_val=0): + """Randomly cut out a rectangle from the original img. + + Args: + img (ndarray): Image to be cutout. + shape (int | tuple[int]): Expected cutout shape (h, w). If given as a + int, the value will be used for both h and w. + pad_val (int | float | tuple[int | float]): Values to be filled in the + cut area. Defaults to 0. + + Returns: + ndarray: The cutout image. + """ + + channels = 1 if img.ndim == 2 else img.shape[2] + if isinstance(shape, int): + cut_h, cut_w = shape, shape + else: + assert isinstance(shape, tuple) and len(shape) == 2, \ + f'shape must be a int or a tuple with length 2, but got type ' \ + f'{type(shape)} instead.' + cut_h, cut_w = shape + if isinstance(pad_val, (int, float)): + pad_val = tuple([pad_val] * channels) + elif isinstance(pad_val, tuple): + assert len(pad_val) == channels, \ + 'Expected the num of elements in tuple equals the channels' \ + 'of input image. Found {} vs {}'.format( + len(pad_val), channels) + else: + raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') + + img_h, img_w = img.shape[:2] + y0 = np.random.uniform(img_h) + x0 = np.random.uniform(img_w) + + y1 = int(max(0, y0 - cut_h / 2.)) + x1 = int(max(0, x0 - cut_w / 2.)) + y2 = min(img_h, y1 + cut_h) + x2 = min(img_w, x1 + cut_w) + + if img.ndim == 2: + patch_shape = (y2 - y1, x2 - x1) + else: + patch_shape = (y2 - y1, x2 - x1, channels) + + img_cutout = img.copy() + patch = np.array( + pad_val, dtype=img.dtype) * np.ones( + patch_shape, dtype=img.dtype) + img_cutout[y1:y2, x1:x2, ...] = patch + + return img_cutout + + +def _get_shear_matrix(magnitude, direction='horizontal'): + """Generate the shear matrix for transformation. + + Args: + magnitude (int | float): The magnitude used for shear. + direction (str): The flip direction, either "horizontal" + or "vertical". + + Returns: + ndarray: The shear matrix with dtype float32. + """ + if direction == 'horizontal': + shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) + elif direction == 'vertical': + shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) + return shear_matrix + + +def imshear(img, + magnitude, + direction='horizontal', + border_value=0, + interpolation='bilinear'): + """Shear an image. + + Args: + img (ndarray): Image to be sheared with format (h, w) + or (h, w, c). + magnitude (int | float): The magnitude used for shear. + direction (str): The flip direction, either "horizontal" + or "vertical". + border_value (int | tuple[int]): Value used in case of a + constant border. + interpolation (str): Same as :func:`resize`. + + Returns: + ndarray: The sheared image. + """ + assert direction in ['horizontal', + 'vertical'], f'Invalid direction: {direction}' + height, width = img.shape[:2] + if img.ndim == 2: + channels = 1 + elif img.ndim == 3: + channels = img.shape[-1] + if isinstance(border_value, int): + border_value = tuple([border_value] * channels) + elif isinstance(border_value, tuple): + assert len(border_value) == channels, \ + 'Expected the num of elements in tuple equals the channels' \ + 'of input image. Found {} vs {}'.format( + len(border_value), channels) + else: + raise ValueError( + f'Invalid type {type(border_value)} for `border_value`') + shear_matrix = _get_shear_matrix(magnitude, direction) + sheared = cv2.warpAffine( + img, + shear_matrix, + (width, height), + # Note case when the number elements in `border_value` + # greater than 3 (e.g. shearing masks whose channels large + # than 3) will raise TypeError in `cv2.warpAffine`. + # Here simply slice the first 3 values in `border_value`. + borderValue=border_value[:3], + flags=cv2_interp_codes[interpolation]) + return sheared + + +def _get_translate_matrix(offset, direction='horizontal'): + """Generate the translate matrix. + + Args: + offset (int | float): The offset used for translate. + direction (str): The translate direction, either + "horizontal" or "vertical". + + Returns: + ndarray: The translate matrix with dtype float32. + """ + if direction == 'horizontal': + translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) + elif direction == 'vertical': + translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) + return translate_matrix + + +def imtranslate(img, + offset, + direction='horizontal', + border_value=0, + interpolation='bilinear'): + """Translate an image. + + Args: + img (ndarray): Image to be translated with format + (h, w) or (h, w, c). + offset (int | float): The offset used for translate. + direction (str): The translate direction, either "horizontal" + or "vertical". + border_value (int | tuple[int]): Value used in case of a + constant border. + interpolation (str): Same as :func:`resize`. + + Returns: + ndarray: The translated image. + """ + assert direction in ['horizontal', + 'vertical'], f'Invalid direction: {direction}' + height, width = img.shape[:2] + if img.ndim == 2: + channels = 1 + elif img.ndim == 3: + channels = img.shape[-1] + if isinstance(border_value, int): + border_value = tuple([border_value] * channels) + elif isinstance(border_value, tuple): + assert len(border_value) == channels, \ + 'Expected the num of elements in tuple equals the channels' \ + 'of input image. Found {} vs {}'.format( + len(border_value), channels) + else: + raise ValueError( + f'Invalid type {type(border_value)} for `border_value`.') + translate_matrix = _get_translate_matrix(offset, direction) + translated = cv2.warpAffine( + img, + translate_matrix, + (width, height), + # Note case when the number elements in `border_value` + # greater than 3 (e.g. translating masks whose channels + # large than 3) will raise TypeError in `cv2.warpAffine`. + # Here simply slice the first 3 values in `border_value`. + borderValue=border_value[:3], + flags=cv2_interp_codes[interpolation]) + return translated diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/io.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/io.py new file mode 100644 index 0000000000000000000000000000000000000000..4e8f1877978840aede93774d86643b129751db13 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/io.py @@ -0,0 +1,258 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import io +import os.path as osp +from pathlib import Path + +import cv2 +import numpy as np +from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, + IMREAD_UNCHANGED) + +from annotator.mmpkg.mmcv.utils import check_file_exist, is_str, mkdir_or_exist + +try: + from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG +except ImportError: + TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None + +try: + from PIL import Image, ImageOps +except ImportError: + Image = None + +try: + import tifffile +except ImportError: + tifffile = None + +jpeg = None +supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] + +imread_flags = { + 'color': IMREAD_COLOR, + 'grayscale': IMREAD_GRAYSCALE, + 'unchanged': IMREAD_UNCHANGED, + 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, + 'grayscale_ignore_orientation': + IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE +} + +imread_backend = 'cv2' + + +def use_backend(backend): + """Select a backend for image decoding. + + Args: + backend (str): The image decoding backend type. Options are `cv2`, + `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) + and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` + file format. + """ + assert backend in supported_backends + global imread_backend + imread_backend = backend + if imread_backend == 'turbojpeg': + if TurboJPEG is None: + raise ImportError('`PyTurboJPEG` is not installed') + global jpeg + if jpeg is None: + jpeg = TurboJPEG() + elif imread_backend == 'pillow': + if Image is None: + raise ImportError('`Pillow` is not installed') + elif imread_backend == 'tifffile': + if tifffile is None: + raise ImportError('`tifffile` is not installed') + + +def _jpegflag(flag='color', channel_order='bgr'): + channel_order = channel_order.lower() + if channel_order not in ['rgb', 'bgr']: + raise ValueError('channel order must be either "rgb" or "bgr"') + + if flag == 'color': + if channel_order == 'bgr': + return TJPF_BGR + elif channel_order == 'rgb': + return TJCS_RGB + elif flag == 'grayscale': + return TJPF_GRAY + else: + raise ValueError('flag must be "color" or "grayscale"') + + +def _pillow2array(img, flag='color', channel_order='bgr'): + """Convert a pillow image to numpy array. + + Args: + img (:obj:`PIL.Image.Image`): The image loaded using PIL + flag (str): Flags specifying the color type of a loaded image, + candidates are 'color', 'grayscale' and 'unchanged'. + Default to 'color'. + channel_order (str): The channel order of the output image array, + candidates are 'bgr' and 'rgb'. Default to 'bgr'. + + Returns: + np.ndarray: The converted numpy array + """ + channel_order = channel_order.lower() + if channel_order not in ['rgb', 'bgr']: + raise ValueError('channel order must be either "rgb" or "bgr"') + + if flag == 'unchanged': + array = np.array(img) + if array.ndim >= 3 and array.shape[2] >= 3: # color image + array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR + else: + # Handle exif orientation tag + if flag in ['color', 'grayscale']: + img = ImageOps.exif_transpose(img) + # If the image mode is not 'RGB', convert it to 'RGB' first. + if img.mode != 'RGB': + if img.mode != 'LA': + # Most formats except 'LA' can be directly converted to RGB + img = img.convert('RGB') + else: + # When the mode is 'LA', the default conversion will fill in + # the canvas with black, which sometimes shadows black objects + # in the foreground. + # + # Therefore, a random color (124, 117, 104) is used for canvas + img_rgba = img.convert('RGBA') + img = Image.new('RGB', img_rgba.size, (124, 117, 104)) + img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha + if flag in ['color', 'color_ignore_orientation']: + array = np.array(img) + if channel_order != 'rgb': + array = array[:, :, ::-1] # RGB to BGR + elif flag in ['grayscale', 'grayscale_ignore_orientation']: + img = img.convert('L') + array = np.array(img) + else: + raise ValueError( + 'flag must be "color", "grayscale", "unchanged", ' + f'"color_ignore_orientation" or "grayscale_ignore_orientation"' + f' but got {flag}') + return array + + +def imread(img_or_path, flag='color', channel_order='bgr', backend=None): + """Read an image. + + Args: + img_or_path (ndarray or str or Path): Either a numpy array or str or + pathlib.Path. If it is a numpy array (loaded image), then + it will be returned as is. + flag (str): Flags specifying the color type of a loaded image, + candidates are `color`, `grayscale`, `unchanged`, + `color_ignore_orientation` and `grayscale_ignore_orientation`. + By default, `cv2` and `pillow` backend would rotate the image + according to its EXIF info unless called with `unchanged` or + `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend + always ignore image's EXIF info regardless of the flag. + The `turbojpeg` backend only supports `color` and `grayscale`. + channel_order (str): Order of channel, candidates are `bgr` and `rgb`. + backend (str | None): The image decoding backend type. Options are + `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. + If backend is None, the global imread_backend specified by + ``mmcv.use_backend()`` will be used. Default: None. + + Returns: + ndarray: Loaded image array. + """ + + if backend is None: + backend = imread_backend + if backend not in supported_backends: + raise ValueError(f'backend: {backend} is not supported. Supported ' + "backends are 'cv2', 'turbojpeg', 'pillow'") + if isinstance(img_or_path, Path): + img_or_path = str(img_or_path) + + if isinstance(img_or_path, np.ndarray): + return img_or_path + elif is_str(img_or_path): + check_file_exist(img_or_path, + f'img file does not exist: {img_or_path}') + if backend == 'turbojpeg': + with open(img_or_path, 'rb') as in_file: + img = jpeg.decode(in_file.read(), + _jpegflag(flag, channel_order)) + if img.shape[-1] == 1: + img = img[:, :, 0] + return img + elif backend == 'pillow': + img = Image.open(img_or_path) + img = _pillow2array(img, flag, channel_order) + return img + elif backend == 'tifffile': + img = tifffile.imread(img_or_path) + return img + else: + flag = imread_flags[flag] if is_str(flag) else flag + img = cv2.imread(img_or_path, flag) + if flag == IMREAD_COLOR and channel_order == 'rgb': + cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) + return img + else: + raise TypeError('"img" must be a numpy array or a str or ' + 'a pathlib.Path object') + + +def imfrombytes(content, flag='color', channel_order='bgr', backend=None): + """Read an image from bytes. + + Args: + content (bytes): Image bytes got from files or other streams. + flag (str): Same as :func:`imread`. + backend (str | None): The image decoding backend type. Options are + `cv2`, `pillow`, `turbojpeg`, `None`. If backend is None, the + global imread_backend specified by ``mmcv.use_backend()`` will be + used. Default: None. + + Returns: + ndarray: Loaded image array. + """ + + if backend is None: + backend = imread_backend + if backend not in supported_backends: + raise ValueError(f'backend: {backend} is not supported. Supported ' + "backends are 'cv2', 'turbojpeg', 'pillow'") + if backend == 'turbojpeg': + img = jpeg.decode(content, _jpegflag(flag, channel_order)) + if img.shape[-1] == 1: + img = img[:, :, 0] + return img + elif backend == 'pillow': + buff = io.BytesIO(content) + img = Image.open(buff) + img = _pillow2array(img, flag, channel_order) + return img + else: + img_np = np.frombuffer(content, np.uint8) + flag = imread_flags[flag] if is_str(flag) else flag + img = cv2.imdecode(img_np, flag) + if flag == IMREAD_COLOR and channel_order == 'rgb': + cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) + return img + + +def imwrite(img, file_path, params=None, auto_mkdir=True): + """Write image to file. + + Args: + img (ndarray): Image array to be written. + file_path (str): Image file path. + params (None or list): Same as opencv :func:`imwrite` interface. + auto_mkdir (bool): If the parent folder of `file_path` does not exist, + whether to create it automatically. + + Returns: + bool: Successful or not. + """ + if auto_mkdir: + dir_name = osp.abspath(osp.dirname(file_path)) + mkdir_or_exist(dir_name) + return cv2.imwrite(file_path, img, params) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/misc.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/misc.py new file mode 100644 index 0000000000000000000000000000000000000000..cd60e66131719ca0627569598809366b9c1ac64d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/misc.py @@ -0,0 +1,44 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import numpy as np + +import annotator.mmpkg.mmcv as mmcv + +try: + import torch +except ImportError: + torch = None + + +def tensor2imgs(tensor, mean=(0, 0, 0), std=(1, 1, 1), to_rgb=True): + """Convert tensor to 3-channel images. + + Args: + tensor (torch.Tensor): Tensor that contains multiple images, shape ( + N, C, H, W). + mean (tuple[float], optional): Mean of images. Defaults to (0, 0, 0). + std (tuple[float], optional): Standard deviation of images. + Defaults to (1, 1, 1). + to_rgb (bool, optional): Whether the tensor was converted to RGB + format in the first place. If so, convert it back to BGR. + Defaults to True. + + Returns: + list[np.ndarray]: A list that contains multiple images. + """ + + if torch is None: + raise RuntimeError('pytorch is not installed') + assert torch.is_tensor(tensor) and tensor.ndim == 4 + assert len(mean) == 3 + assert len(std) == 3 + + num_imgs = tensor.size(0) + mean = np.array(mean, dtype=np.float32) + std = np.array(std, dtype=np.float32) + imgs = [] + for img_id in range(num_imgs): + img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) + img = mmcv.imdenormalize( + img, mean, std, to_bgr=to_rgb).astype(np.uint8) + imgs.append(np.ascontiguousarray(img)) + return imgs diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/photometric.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/photometric.py new file mode 100644 index 0000000000000000000000000000000000000000..5085d012019c0cbf56f66f421a378278c1a058ae --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/image/photometric.py @@ -0,0 +1,428 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import cv2 +import numpy as np + +from ..utils import is_tuple_of +from .colorspace import bgr2gray, gray2bgr + + +def imnormalize(img, mean, std, to_rgb=True): + """Normalize an image with mean and std. + + Args: + img (ndarray): Image to be normalized. + mean (ndarray): The mean to be used for normalize. + std (ndarray): The std to be used for normalize. + to_rgb (bool): Whether to convert to rgb. + + Returns: + ndarray: The normalized image. + """ + img = img.copy().astype(np.float32) + return imnormalize_(img, mean, std, to_rgb) + + +def imnormalize_(img, mean, std, to_rgb=True): + """Inplace normalize an image with mean and std. + + Args: + img (ndarray): Image to be normalized. + mean (ndarray): The mean to be used for normalize. + std (ndarray): The std to be used for normalize. + to_rgb (bool): Whether to convert to rgb. + + Returns: + ndarray: The normalized image. + """ + # cv2 inplace normalization does not accept uint8 + assert img.dtype != np.uint8 + mean = np.float64(mean.reshape(1, -1)) + stdinv = 1 / np.float64(std.reshape(1, -1)) + if to_rgb: + cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace + cv2.subtract(img, mean, img) # inplace + cv2.multiply(img, stdinv, img) # inplace + return img + + +def imdenormalize(img, mean, std, to_bgr=True): + assert img.dtype != np.uint8 + mean = mean.reshape(1, -1).astype(np.float64) + std = std.reshape(1, -1).astype(np.float64) + img = cv2.multiply(img, std) # make a copy + cv2.add(img, mean, img) # inplace + if to_bgr: + cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace + return img + + +def iminvert(img): + """Invert (negate) an image. + + Args: + img (ndarray): Image to be inverted. + + Returns: + ndarray: The inverted image. + """ + return np.full_like(img, 255) - img + + +def solarize(img, thr=128): + """Solarize an image (invert all pixel values above a threshold) + + Args: + img (ndarray): Image to be solarized. + thr (int): Threshold for solarizing (0 - 255). + + Returns: + ndarray: The solarized image. + """ + img = np.where(img < thr, img, 255 - img) + return img + + +def posterize(img, bits): + """Posterize an image (reduce the number of bits for each color channel) + + Args: + img (ndarray): Image to be posterized. + bits (int): Number of bits (1 to 8) to use for posterizing. + + Returns: + ndarray: The posterized image. + """ + shift = 8 - bits + img = np.left_shift(np.right_shift(img, shift), shift) + return img + + +def adjust_color(img, alpha=1, beta=None, gamma=0): + r"""It blends the source image and its gray image: + + .. math:: + output = img * alpha + gray\_img * beta + gamma + + Args: + img (ndarray): The input source image. + alpha (int | float): Weight for the source image. Default 1. + beta (int | float): Weight for the converted gray image. + If None, it's assigned the value (1 - `alpha`). + gamma (int | float): Scalar added to each sum. + Same as :func:`cv2.addWeighted`. Default 0. + + Returns: + ndarray: Colored image which has the same size and dtype as input. + """ + gray_img = bgr2gray(img) + gray_img = np.tile(gray_img[..., None], [1, 1, 3]) + if beta is None: + beta = 1 - alpha + colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) + if not colored_img.dtype == np.uint8: + # Note when the dtype of `img` is not the default `np.uint8` + # (e.g. np.float32), the value in `colored_img` got from cv2 + # is not guaranteed to be in range [0, 255], so here clip + # is needed. + colored_img = np.clip(colored_img, 0, 255) + return colored_img + + +def imequalize(img): + """Equalize the image histogram. + + This function applies a non-linear mapping to the input image, + in order to create a uniform distribution of grayscale values + in the output image. + + Args: + img (ndarray): Image to be equalized. + + Returns: + ndarray: The equalized image. + """ + + def _scale_channel(im, c): + """Scale the data in the corresponding channel.""" + im = im[:, :, c] + # Compute the histogram of the image channel. + histo = np.histogram(im, 256, (0, 255))[0] + # For computing the step, filter out the nonzeros. + nonzero_histo = histo[histo > 0] + step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 + if not step: + lut = np.array(range(256)) + else: + # Compute the cumulative sum, shifted by step // 2 + # and then normalized by step. + lut = (np.cumsum(histo) + (step // 2)) // step + # Shift lut, prepending with 0. + lut = np.concatenate([[0], lut[:-1]], 0) + # handle potential integer overflow + lut[lut > 255] = 255 + # If step is zero, return the original image. + # Otherwise, index from lut. + return np.where(np.equal(step, 0), im, lut[im]) + + # Scales each channel independently and then stacks + # the result. + s1 = _scale_channel(img, 0) + s2 = _scale_channel(img, 1) + s3 = _scale_channel(img, 2) + equalized_img = np.stack([s1, s2, s3], axis=-1) + return equalized_img.astype(img.dtype) + + +def adjust_brightness(img, factor=1.): + """Adjust image brightness. + + This function controls the brightness of an image. An + enhancement factor of 0.0 gives a black image. + A factor of 1.0 gives the original image. This function + blends the source image and the degenerated black image: + + .. math:: + output = img * factor + degenerated * (1 - factor) + + Args: + img (ndarray): Image to be brightened. + factor (float): A value controls the enhancement. + Factor 1.0 returns the original image, lower + factors mean less color (brightness, contrast, + etc), and higher values more. Default 1. + + Returns: + ndarray: The brightened image. + """ + degenerated = np.zeros_like(img) + # Note manually convert the dtype to np.float32, to + # achieve as close results as PIL.ImageEnhance.Brightness. + # Set beta=1-factor, and gamma=0 + brightened_img = cv2.addWeighted( + img.astype(np.float32), factor, degenerated.astype(np.float32), + 1 - factor, 0) + brightened_img = np.clip(brightened_img, 0, 255) + return brightened_img.astype(img.dtype) + + +def adjust_contrast(img, factor=1.): + """Adjust image contrast. + + This function controls the contrast of an image. An + enhancement factor of 0.0 gives a solid grey + image. A factor of 1.0 gives the original image. It + blends the source image and the degenerated mean image: + + .. math:: + output = img * factor + degenerated * (1 - factor) + + Args: + img (ndarray): Image to be contrasted. BGR order. + factor (float): Same as :func:`mmcv.adjust_brightness`. + + Returns: + ndarray: The contrasted image. + """ + gray_img = bgr2gray(img) + hist = np.histogram(gray_img, 256, (0, 255))[0] + mean = round(np.sum(gray_img) / np.sum(hist)) + degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) + degenerated = gray2bgr(degenerated) + contrasted_img = cv2.addWeighted( + img.astype(np.float32), factor, degenerated.astype(np.float32), + 1 - factor, 0) + contrasted_img = np.clip(contrasted_img, 0, 255) + return contrasted_img.astype(img.dtype) + + +def auto_contrast(img, cutoff=0): + """Auto adjust image contrast. + + This function maximize (normalize) image contrast by first removing cutoff + percent of the lightest and darkest pixels from the histogram and remapping + the image so that the darkest pixel becomes black (0), and the lightest + becomes white (255). + + Args: + img (ndarray): Image to be contrasted. BGR order. + cutoff (int | float | tuple): The cutoff percent of the lightest and + darkest pixels to be removed. If given as tuple, it shall be + (low, high). Otherwise, the single value will be used for both. + Defaults to 0. + + Returns: + ndarray: The contrasted image. + """ + + def _auto_contrast_channel(im, c, cutoff): + im = im[:, :, c] + # Compute the histogram of the image channel. + histo = np.histogram(im, 256, (0, 255))[0] + # Remove cut-off percent pixels from histo + histo_sum = np.cumsum(histo) + cut_low = histo_sum[-1] * cutoff[0] // 100 + cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 + histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low + histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) + + # Compute mapping + low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] + # If all the values have been cut off, return the origin img + if low >= high: + return im + scale = 255.0 / (high - low) + offset = -low * scale + lut = np.array(range(256)) + lut = lut * scale + offset + lut = np.clip(lut, 0, 255) + return lut[im] + + if isinstance(cutoff, (int, float)): + cutoff = (cutoff, cutoff) + else: + assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ + f'float or tuple, but got {type(cutoff)} instead.' + # Auto adjusts contrast for each channel independently and then stacks + # the result. + s1 = _auto_contrast_channel(img, 0, cutoff) + s2 = _auto_contrast_channel(img, 1, cutoff) + s3 = _auto_contrast_channel(img, 2, cutoff) + contrasted_img = np.stack([s1, s2, s3], axis=-1) + return contrasted_img.astype(img.dtype) + + +def adjust_sharpness(img, factor=1., kernel=None): + """Adjust image sharpness. + + This function controls the sharpness of an image. An + enhancement factor of 0.0 gives a blurred image. A + factor of 1.0 gives the original image. And a factor + of 2.0 gives a sharpened image. It blends the source + image and the degenerated mean image: + + .. math:: + output = img * factor + degenerated * (1 - factor) + + Args: + img (ndarray): Image to be sharpened. BGR order. + factor (float): Same as :func:`mmcv.adjust_brightness`. + kernel (np.ndarray, optional): Filter kernel to be applied on the img + to obtain the degenerated img. Defaults to None. + + Note: + No value sanity check is enforced on the kernel set by users. So with + an inappropriate kernel, the ``adjust_sharpness`` may fail to perform + the function its name indicates but end up performing whatever + transform determined by the kernel. + + Returns: + ndarray: The sharpened image. + """ + + if kernel is None: + # adopted from PIL.ImageFilter.SMOOTH + kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 + assert isinstance(kernel, np.ndarray), \ + f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' + assert kernel.ndim == 2, \ + f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' + + degenerated = cv2.filter2D(img, -1, kernel) + sharpened_img = cv2.addWeighted( + img.astype(np.float32), factor, degenerated.astype(np.float32), + 1 - factor, 0) + sharpened_img = np.clip(sharpened_img, 0, 255) + return sharpened_img.astype(img.dtype) + + +def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): + """AlexNet-style PCA jitter. + + This data augmentation is proposed in `ImageNet Classification with Deep + Convolutional Neural Networks + `_. + + Args: + img (ndarray): Image to be adjusted lighting. BGR order. + eigval (ndarray): the eigenvalue of the convariance matrix of pixel + values, respectively. + eigvec (ndarray): the eigenvector of the convariance matrix of pixel + values, respectively. + alphastd (float): The standard deviation for distribution of alpha. + Defaults to 0.1 + to_rgb (bool): Whether to convert img to rgb. + + Returns: + ndarray: The adjusted image. + """ + assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ + f'eigval and eigvec should both be of type np.ndarray, got ' \ + f'{type(eigval)} and {type(eigvec)} instead.' + + assert eigval.ndim == 1 and eigvec.ndim == 2 + assert eigvec.shape == (3, eigval.shape[0]) + n_eigval = eigval.shape[0] + assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ + f'got {type(alphastd)} instead.' + + img = img.copy().astype(np.float32) + if to_rgb: + cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace + + alpha = np.random.normal(0, alphastd, n_eigval) + alter = eigvec \ + * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ + * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) + alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) + img_adjusted = img + alter + return img_adjusted + + +def lut_transform(img, lut_table): + """Transform array by look-up table. + + The function lut_transform fills the output array with values from the + look-up table. Indices of the entries are taken from the input array. + + Args: + img (ndarray): Image to be transformed. + lut_table (ndarray): look-up table of 256 elements; in case of + multi-channel input array, the table should either have a single + channel (in this case the same table is used for all channels) or + the same number of channels as in the input array. + + Returns: + ndarray: The transformed image. + """ + assert isinstance(img, np.ndarray) + assert 0 <= np.min(img) and np.max(img) <= 255 + assert isinstance(lut_table, np.ndarray) + assert lut_table.shape == (256, ) + + return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) + + +def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): + """Use CLAHE method to process the image. + + See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. + Graphics Gems, 1994:474-485.` for more information. + + Args: + img (ndarray): Image to be processed. + clip_limit (float): Threshold for contrast limiting. Default: 40.0. + tile_grid_size (tuple[int]): Size of grid for histogram equalization. + Input image will be divided into equally sized rectangular tiles. + It defines the number of tiles in row and column. Default: (8, 8). + + Returns: + ndarray: The processed image. + """ + assert isinstance(img, np.ndarray) + assert img.ndim == 2 + assert isinstance(clip_limit, (float, int)) + assert is_tuple_of(tile_grid_size, int) + assert len(tile_grid_size) == 2 + + clahe = cv2.createCLAHE(clip_limit, tile_grid_size) + return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/model_zoo/deprecated.json b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/model_zoo/deprecated.json new file mode 100644 index 0000000000000000000000000000000000000000..25cf6f28caecc22a77e3136fefa6b8dfc0e6cb5b --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/model_zoo/deprecated.json @@ -0,0 +1,6 @@ +{ + "resnet50_caffe": "detectron/resnet50_caffe", + "resnet50_caffe_bgr": "detectron2/resnet50_caffe_bgr", + "resnet101_caffe": "detectron/resnet101_caffe", + "resnet101_caffe_bgr": "detectron2/resnet101_caffe_bgr" +} diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/model_zoo/mmcls.json b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/model_zoo/mmcls.json new file mode 100644 index 0000000000000000000000000000000000000000..bdb311d9fe6d9f317290feedc9e37236c6cf6e8f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/model_zoo/mmcls.json @@ -0,0 +1,31 @@ +{ + "vgg11": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_batch256_imagenet_20210208-4271cd6c.pth", + "vgg13": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_batch256_imagenet_20210208-4d1d6080.pth", + "vgg16": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_batch256_imagenet_20210208-db26f1a5.pth", + "vgg19": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_batch256_imagenet_20210208-e6920e4a.pth", + "vgg11_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_bn_batch256_imagenet_20210207-f244902c.pth", + "vgg13_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_bn_batch256_imagenet_20210207-1a8b7864.pth", + "vgg16_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_bn_batch256_imagenet_20210208-7e55cd29.pth", + "vgg19_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_bn_batch256_imagenet_20210208-da620c4f.pth", + "resnet18": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_batch256_imagenet_20200708-34ab8f90.pth", + "resnet34": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet34_batch256_imagenet_20200708-32ffb4f7.pth", + "resnet50": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_batch256_imagenet_20200708-cfb998bf.pth", + "resnet101": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet101_batch256_imagenet_20200708-753f3608.pth", + "resnet152": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet152_batch256_imagenet_20200708-ec25b1f9.pth", + "resnet50_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d50_batch256_imagenet_20200708-1ad0ce94.pth", + "resnet101_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d101_batch256_imagenet_20200708-9cb302ef.pth", + "resnet152_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d152_batch256_imagenet_20200708-e79cb6a2.pth", + "resnext50_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext50_32x4d_b32x8_imagenet_20210429-56066e27.pth", + "resnext101_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x4d_b32x8_imagenet_20210506-e0fa3dd5.pth", + "resnext101_32x8d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x8d_b32x8_imagenet_20210506-23a247d5.pth", + "resnext152_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext152_32x4d_b32x8_imagenet_20210524-927787be.pth", + "se-resnet50": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet50_batch256_imagenet_20200804-ae206104.pth", + "se-resnet101": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet101_batch256_imagenet_20200804-ba5b51d4.pth", + "resnest50": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest50_imagenet_converted-1ebf0afe.pth", + "resnest101": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest101_imagenet_converted-032caa52.pth", + "resnest200": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest200_imagenet_converted-581a60f2.pth", + "resnest269": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest269_imagenet_converted-59930960.pth", + "shufflenet_v1": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v1/shufflenet_v1_batch1024_imagenet_20200804-5d6cec73.pth", + "shufflenet_v2": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v2/shufflenet_v2_batch1024_imagenet_20200812-5bf4721e.pth", + "mobilenet_v2": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v2/mobilenet_v2_batch256_imagenet_20200708-3b2dc3af.pth" +} diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/model_zoo/open_mmlab.json b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/model_zoo/open_mmlab.json new file mode 100644 index 0000000000000000000000000000000000000000..8311db4feef92faa0841c697d75efbee8430c3a0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/model_zoo/open_mmlab.json @@ -0,0 +1,50 @@ +{ + "vgg16_caffe": "https://download.openmmlab.com/pretrain/third_party/vgg16_caffe-292e1171.pth", + "detectron/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_caffe-788b5fa3.pth", + "detectron2/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_msra-5891d200.pth", + "detectron/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_caffe-3ad79236.pth", + "detectron2/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_msra-6cc46731.pth", + "detectron2/resnext101_32x8d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x8d-1516f1aa.pth", + "resnext50_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext50-32x4d-0ab1a123.pth", + "resnext101_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d-a5af3160.pth", + "resnext101_64x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_64x4d-ee2c6f71.pth", + "contrib/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_thangvubk-ad1730dd.pth", + "detectron/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn-9186a21c.pth", + "detectron/resnet101_gn": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn-cac0ab98.pth", + "jhu/resnet50_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_ws-15beedd8.pth", + "jhu/resnet101_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn_ws-3e3c308c.pth", + "jhu/resnext50_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn_ws-0d87ac85.pth", + "jhu/resnext101_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn_ws-34ac1a9e.pth", + "jhu/resnext50_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn-c7e8b754.pth", + "jhu/resnext101_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn-ac3bb84e.pth", + "msra/hrnetv2_w18_small": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18_small-b5a04e21.pth", + "msra/hrnetv2_w18": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18-00eb2006.pth", + "msra/hrnetv2_w32": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w32-dc9eeb4f.pth", + "msra/hrnetv2_w40": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w40-ed0b031c.pth", + "msra/hrnetv2_w48": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w48-d2186c55.pth", + "bninception_caffe": "https://download.openmmlab.com/pretrain/third_party/bn_inception_caffe-ed2e8665.pth", + "kin400/i3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/i3d_r50_f32s2_k400-2c57e077.pth", + "kin400/nl3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/nl3d_r50_f32s2_k400-fa7e7caa.pth", + "res2net101_v1d_26w_4s": "https://download.openmmlab.com/pretrain/third_party/res2net101_v1d_26w_4s_mmdetv2-f0a600f9.pth", + "regnetx_400mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_400mf-a5b10d96.pth", + "regnetx_800mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_800mf-1f4be4c7.pth", + "regnetx_1.6gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_1.6gf-5791c176.pth", + "regnetx_3.2gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_3.2gf-c2599b0f.pth", + "regnetx_4.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_4.0gf-a88f671e.pth", + "regnetx_6.4gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_6.4gf-006af45d.pth", + "regnetx_8.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_8.0gf-3c68abe7.pth", + "regnetx_12gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_12gf-4c2a3350.pth", + "resnet18_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet18_v1c-b5776b93.pth", + "resnet50_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet50_v1c-2cccc1ad.pth", + "resnet101_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet101_v1c-e67eebb6.pth", + "mmedit/vgg16": "https://download.openmmlab.com/mmediting/third_party/vgg_state_dict.pth", + "mmedit/res34_en_nomixup": "https://download.openmmlab.com/mmediting/third_party/model_best_resnet34_En_nomixup.pth", + "mmedit/mobilenet_v2": "https://download.openmmlab.com/mmediting/third_party/mobilenet_v2.pth", + "contrib/mobilenet_v3_large": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_large-bc2c3fd3.pth", + "contrib/mobilenet_v3_small": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_small-47085aa1.pth", + "resnest50": "https://download.openmmlab.com/pretrain/third_party/resnest50_d2-7497a55b.pth", + "resnest101": "https://download.openmmlab.com/pretrain/third_party/resnest101_d2-f3b931b2.pth", + "resnest200": "https://download.openmmlab.com/pretrain/third_party/resnest200_d2-ca88e41f.pth", + "darknet53": "https://download.openmmlab.com/pretrain/third_party/darknet53-a628ea1b.pth", + "mmdet/mobilenet_v2": "https://download.openmmlab.com/mmdetection/v2.0/third_party/mobilenet_v2_batch256_imagenet-ff34753d.pth" +} diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..999e090a458ee148ceca0649f1e3806a40e909bd --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/__init__.py @@ -0,0 +1,81 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .assign_score_withk import assign_score_withk +from .ball_query import ball_query +from .bbox import bbox_overlaps +from .border_align import BorderAlign, border_align +from .box_iou_rotated import box_iou_rotated +from .carafe import CARAFE, CARAFENaive, CARAFEPack, carafe, carafe_naive +from .cc_attention import CrissCrossAttention +from .contour_expand import contour_expand +from .corner_pool import CornerPool +from .correlation import Correlation +from .deform_conv import DeformConv2d, DeformConv2dPack, deform_conv2d +from .deform_roi_pool import (DeformRoIPool, DeformRoIPoolPack, + ModulatedDeformRoIPoolPack, deform_roi_pool) +from .deprecated_wrappers import Conv2d_deprecated as Conv2d +from .deprecated_wrappers import ConvTranspose2d_deprecated as ConvTranspose2d +from .deprecated_wrappers import Linear_deprecated as Linear +from .deprecated_wrappers import MaxPool2d_deprecated as MaxPool2d +from .focal_loss import (SigmoidFocalLoss, SoftmaxFocalLoss, + sigmoid_focal_loss, softmax_focal_loss) +from .furthest_point_sample import (furthest_point_sample, + furthest_point_sample_with_dist) +from .fused_bias_leakyrelu import FusedBiasLeakyReLU, fused_bias_leakyrelu +from .gather_points import gather_points +from .group_points import GroupAll, QueryAndGroup, grouping_operation +from .info import (get_compiler_version, get_compiling_cuda_version, + get_onnxruntime_op_path) +from .iou3d import boxes_iou_bev, nms_bev, nms_normal_bev +from .knn import knn +from .masked_conv import MaskedConv2d, masked_conv2d +from .modulated_deform_conv import (ModulatedDeformConv2d, + ModulatedDeformConv2dPack, + modulated_deform_conv2d) +from .multi_scale_deform_attn import MultiScaleDeformableAttention +from .nms import batched_nms, nms, nms_match, nms_rotated, soft_nms +from .pixel_group import pixel_group +from .point_sample import (SimpleRoIAlign, point_sample, + rel_roi_point_to_rel_img_point) +from .points_in_boxes import (points_in_boxes_all, points_in_boxes_cpu, + points_in_boxes_part) +from .points_sampler import PointsSampler +from .psa_mask import PSAMask +from .roi_align import RoIAlign, roi_align +from .roi_align_rotated import RoIAlignRotated, roi_align_rotated +from .roi_pool import RoIPool, roi_pool +from .roiaware_pool3d import RoIAwarePool3d +from .roipoint_pool3d import RoIPointPool3d +from .saconv import SAConv2d +from .scatter_points import DynamicScatter, dynamic_scatter +from .sync_bn import SyncBatchNorm +from .three_interpolate import three_interpolate +from .three_nn import three_nn +from .tin_shift import TINShift, tin_shift +from .upfirdn2d import upfirdn2d +from .voxelize import Voxelization, voxelization + +__all__ = [ + 'bbox_overlaps', 'CARAFE', 'CARAFENaive', 'CARAFEPack', 'carafe', + 'carafe_naive', 'CornerPool', 'DeformConv2d', 'DeformConv2dPack', + 'deform_conv2d', 'DeformRoIPool', 'DeformRoIPoolPack', + 'ModulatedDeformRoIPoolPack', 'deform_roi_pool', 'SigmoidFocalLoss', + 'SoftmaxFocalLoss', 'sigmoid_focal_loss', 'softmax_focal_loss', + 'get_compiler_version', 'get_compiling_cuda_version', + 'get_onnxruntime_op_path', 'MaskedConv2d', 'masked_conv2d', + 'ModulatedDeformConv2d', 'ModulatedDeformConv2dPack', + 'modulated_deform_conv2d', 'batched_nms', 'nms', 'soft_nms', 'nms_match', + 'RoIAlign', 'roi_align', 'RoIPool', 'roi_pool', 'SyncBatchNorm', 'Conv2d', + 'ConvTranspose2d', 'Linear', 'MaxPool2d', 'CrissCrossAttention', 'PSAMask', + 'point_sample', 'rel_roi_point_to_rel_img_point', 'SimpleRoIAlign', + 'SAConv2d', 'TINShift', 'tin_shift', 'assign_score_withk', + 'box_iou_rotated', 'RoIPointPool3d', 'nms_rotated', 'knn', 'ball_query', + 'upfirdn2d', 'FusedBiasLeakyReLU', 'fused_bias_leakyrelu', + 'RoIAlignRotated', 'roi_align_rotated', 'pixel_group', 'QueryAndGroup', + 'GroupAll', 'grouping_operation', 'contour_expand', 'three_nn', + 'three_interpolate', 'MultiScaleDeformableAttention', 'BorderAlign', + 'border_align', 'gather_points', 'furthest_point_sample', + 'furthest_point_sample_with_dist', 'PointsSampler', 'Correlation', + 'boxes_iou_bev', 'nms_bev', 'nms_normal_bev', 'Voxelization', + 'voxelization', 'dynamic_scatter', 'DynamicScatter', 'RoIAwarePool3d', + 'points_in_boxes_part', 'points_in_boxes_cpu', 'points_in_boxes_all' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/assign_score_withk.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/assign_score_withk.py new file mode 100644 index 0000000000000000000000000000000000000000..4906adaa2cffd1b46912fbe7d4f87ef2f9fa0012 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/assign_score_withk.py @@ -0,0 +1,123 @@ +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', ['assign_score_withk_forward', 'assign_score_withk_backward']) + + +class AssignScoreWithK(Function): + r"""Perform weighted sum to generate output features according to scores. + Modified from `PAConv `_. + + This is a memory-efficient CUDA implementation of assign_scores operation, + which first transform all point features with weight bank, then assemble + neighbor features with ``knn_idx`` and perform weighted sum of ``scores``. + + See the `paper `_ appendix Sec. D for + more detailed descriptions. + + Note: + This implementation assumes using ``neighbor`` kernel input, which is + (point_features - center_features, point_features). + See https://github.com/CVMI-Lab/PAConv/blob/main/scene_seg/model/ + pointnet2/paconv.py#L128 for more details. + """ + + @staticmethod + def forward(ctx, + scores, + point_features, + center_features, + knn_idx, + aggregate='sum'): + """ + Args: + scores (torch.Tensor): (B, npoint, K, M), predicted scores to + aggregate weight matrices in the weight bank. + ``npoint`` is the number of sampled centers. + ``K`` is the number of queried neighbors. + ``M`` is the number of weight matrices in the weight bank. + point_features (torch.Tensor): (B, N, M, out_dim) + Pre-computed point features to be aggregated. + center_features (torch.Tensor): (B, N, M, out_dim) + Pre-computed center features to be aggregated. + knn_idx (torch.Tensor): (B, npoint, K), index of sampled kNN. + We assume the first idx in each row is the idx of the center. + aggregate (str, optional): Aggregation method. + Can be 'sum', 'avg' or 'max'. Defaults: 'sum'. + + Returns: + torch.Tensor: (B, out_dim, npoint, K), the aggregated features. + """ + agg = {'sum': 0, 'avg': 1, 'max': 2} + + B, N, M, out_dim = point_features.size() + _, npoint, K, _ = scores.size() + + output = point_features.new_zeros((B, out_dim, npoint, K)) + ext_module.assign_score_withk_forward( + point_features.contiguous(), + center_features.contiguous(), + scores.contiguous(), + knn_idx.contiguous(), + output, + B=B, + N0=N, + N1=npoint, + M=M, + K=K, + O=out_dim, + aggregate=agg[aggregate]) + + ctx.save_for_backward(output, point_features, center_features, scores, + knn_idx) + ctx.agg = agg[aggregate] + + return output + + @staticmethod + def backward(ctx, grad_out): + """ + Args: + grad_out (torch.Tensor): (B, out_dim, npoint, K) + + Returns: + grad_scores (torch.Tensor): (B, npoint, K, M) + grad_point_features (torch.Tensor): (B, N, M, out_dim) + grad_center_features (torch.Tensor): (B, N, M, out_dim) + """ + _, point_features, center_features, scores, knn_idx = ctx.saved_tensors + + agg = ctx.agg + + B, N, M, out_dim = point_features.size() + _, npoint, K, _ = scores.size() + + grad_point_features = point_features.new_zeros(point_features.shape) + grad_center_features = center_features.new_zeros(center_features.shape) + grad_scores = scores.new_zeros(scores.shape) + + ext_module.assign_score_withk_backward( + grad_out.contiguous(), + point_features.contiguous(), + center_features.contiguous(), + scores.contiguous(), + knn_idx.contiguous(), + grad_point_features, + grad_center_features, + grad_scores, + B=B, + N0=N, + N1=npoint, + M=M, + K=K, + O=out_dim, + aggregate=agg) + + return grad_scores, grad_point_features, \ + grad_center_features, None, None + + +assign_score_withk = AssignScoreWithK.apply diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/ball_query.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/ball_query.py new file mode 100644 index 0000000000000000000000000000000000000000..d0466847c6e5c1239e359a0397568413ebc1504a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/ball_query.py @@ -0,0 +1,55 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', ['ball_query_forward']) + + +class BallQuery(Function): + """Find nearby points in spherical space.""" + + @staticmethod + def forward(ctx, min_radius: float, max_radius: float, sample_num: int, + xyz: torch.Tensor, center_xyz: torch.Tensor) -> torch.Tensor: + """ + Args: + min_radius (float): minimum radius of the balls. + max_radius (float): maximum radius of the balls. + sample_num (int): maximum number of features in the balls. + xyz (Tensor): (B, N, 3) xyz coordinates of the features. + center_xyz (Tensor): (B, npoint, 3) centers of the ball query. + + Returns: + Tensor: (B, npoint, nsample) tensor with the indices of + the features that form the query balls. + """ + assert center_xyz.is_contiguous() + assert xyz.is_contiguous() + assert min_radius < max_radius + + B, N, _ = xyz.size() + npoint = center_xyz.size(1) + idx = xyz.new_zeros(B, npoint, sample_num, dtype=torch.int) + + ext_module.ball_query_forward( + center_xyz, + xyz, + idx, + b=B, + n=N, + m=npoint, + min_radius=min_radius, + max_radius=max_radius, + nsample=sample_num) + if torch.__version__ != 'parrots': + ctx.mark_non_differentiable(idx) + return idx + + @staticmethod + def backward(ctx, a=None): + return None, None, None, None + + +ball_query = BallQuery.apply diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/bbox.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/bbox.py new file mode 100644 index 0000000000000000000000000000000000000000..0c4d58b6c91f652933974f519acd3403a833e906 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/bbox.py @@ -0,0 +1,72 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', ['bbox_overlaps']) + + +def bbox_overlaps(bboxes1, bboxes2, mode='iou', aligned=False, offset=0): + """Calculate overlap between two set of bboxes. + + If ``aligned`` is ``False``, then calculate the ious between each bbox + of bboxes1 and bboxes2, otherwise the ious between each aligned pair of + bboxes1 and bboxes2. + + Args: + bboxes1 (Tensor): shape (m, 4) in format or empty. + bboxes2 (Tensor): shape (n, 4) in format or empty. + If aligned is ``True``, then m and n must be equal. + mode (str): "iou" (intersection over union) or iof (intersection over + foreground). + + Returns: + ious(Tensor): shape (m, n) if aligned == False else shape (m, 1) + + Example: + >>> bboxes1 = torch.FloatTensor([ + >>> [0, 0, 10, 10], + >>> [10, 10, 20, 20], + >>> [32, 32, 38, 42], + >>> ]) + >>> bboxes2 = torch.FloatTensor([ + >>> [0, 0, 10, 20], + >>> [0, 10, 10, 19], + >>> [10, 10, 20, 20], + >>> ]) + >>> bbox_overlaps(bboxes1, bboxes2) + tensor([[0.5000, 0.0000, 0.0000], + [0.0000, 0.0000, 1.0000], + [0.0000, 0.0000, 0.0000]]) + + Example: + >>> empty = torch.FloatTensor([]) + >>> nonempty = torch.FloatTensor([ + >>> [0, 0, 10, 9], + >>> ]) + >>> assert tuple(bbox_overlaps(empty, nonempty).shape) == (0, 1) + >>> assert tuple(bbox_overlaps(nonempty, empty).shape) == (1, 0) + >>> assert tuple(bbox_overlaps(empty, empty).shape) == (0, 0) + """ + + mode_dict = {'iou': 0, 'iof': 1} + assert mode in mode_dict.keys() + mode_flag = mode_dict[mode] + # Either the boxes are empty or the length of boxes' last dimension is 4 + assert (bboxes1.size(-1) == 4 or bboxes1.size(0) == 0) + assert (bboxes2.size(-1) == 4 or bboxes2.size(0) == 0) + assert offset == 1 or offset == 0 + + rows = bboxes1.size(0) + cols = bboxes2.size(0) + if aligned: + assert rows == cols + + if rows * cols == 0: + return bboxes1.new(rows, 1) if aligned else bboxes1.new(rows, cols) + + if aligned: + ious = bboxes1.new_zeros(rows) + else: + ious = bboxes1.new_zeros((rows, cols)) + ext_module.bbox_overlaps( + bboxes1, bboxes2, ious, mode=mode_flag, aligned=aligned, offset=offset) + return ious diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/border_align.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/border_align.py new file mode 100644 index 0000000000000000000000000000000000000000..ff305be328e9b0a15e1bbb5e6b41beb940f55c81 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/border_align.py @@ -0,0 +1,109 @@ +# Copyright (c) OpenMMLab. All rights reserved. +# modified from +# https://github.com/Megvii-BaseDetection/cvpods/blob/master/cvpods/layers/border_align.py + +import torch +import torch.nn as nn +from torch.autograd import Function +from torch.autograd.function import once_differentiable + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', ['border_align_forward', 'border_align_backward']) + + +class BorderAlignFunction(Function): + + @staticmethod + def symbolic(g, input, boxes, pool_size): + return g.op( + 'mmcv::MMCVBorderAlign', input, boxes, pool_size_i=pool_size) + + @staticmethod + def forward(ctx, input, boxes, pool_size): + ctx.pool_size = pool_size + ctx.input_shape = input.size() + + assert boxes.ndim == 3, 'boxes must be with shape [B, H*W, 4]' + assert boxes.size(2) == 4, \ + 'the last dimension of boxes must be (x1, y1, x2, y2)' + assert input.size(1) % 4 == 0, \ + 'the channel for input feature must be divisible by factor 4' + + # [B, C//4, H*W, 4] + output_shape = (input.size(0), input.size(1) // 4, boxes.size(1), 4) + output = input.new_zeros(output_shape) + # `argmax_idx` only used for backward + argmax_idx = input.new_zeros(output_shape).to(torch.int) + + ext_module.border_align_forward( + input, boxes, output, argmax_idx, pool_size=ctx.pool_size) + + ctx.save_for_backward(boxes, argmax_idx) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + boxes, argmax_idx = ctx.saved_tensors + grad_input = grad_output.new_zeros(ctx.input_shape) + # complex head architecture may cause grad_output uncontiguous + grad_output = grad_output.contiguous() + ext_module.border_align_backward( + grad_output, + boxes, + argmax_idx, + grad_input, + pool_size=ctx.pool_size) + return grad_input, None, None + + +border_align = BorderAlignFunction.apply + + +class BorderAlign(nn.Module): + r"""Border align pooling layer. + + Applies border_align over the input feature based on predicted bboxes. + The details were described in the paper + `BorderDet: Border Feature for Dense Object Detection + `_. + + For each border line (e.g. top, left, bottom or right) of each box, + border_align does the following: + 1. uniformly samples `pool_size`+1 positions on this line, involving \ + the start and end points. + 2. the corresponding features on these points are computed by \ + bilinear interpolation. + 3. max pooling over all the `pool_size`+1 positions are used for \ + computing pooled feature. + + Args: + pool_size (int): number of positions sampled over the boxes' borders + (e.g. top, bottom, left, right). + + """ + + def __init__(self, pool_size): + super(BorderAlign, self).__init__() + self.pool_size = pool_size + + def forward(self, input, boxes): + """ + Args: + input: Features with shape [N,4C,H,W]. Channels ranged in [0,C), + [C,2C), [2C,3C), [3C,4C) represent the top, left, bottom, + right features respectively. + boxes: Boxes with shape [N,H*W,4]. Coordinate format (x1,y1,x2,y2). + + Returns: + Tensor: Pooled features with shape [N,C,H*W,4]. The order is + (top,left,bottom,right) for the last dimension. + """ + return border_align(input, boxes, self.pool_size) + + def __repr__(self): + s = self.__class__.__name__ + s += f'(pool_size={self.pool_size})' + return s diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/box_iou_rotated.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/box_iou_rotated.py new file mode 100644 index 0000000000000000000000000000000000000000..2d78015e9c2a9e7a52859b4e18f84a9aa63481a0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/box_iou_rotated.py @@ -0,0 +1,45 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', ['box_iou_rotated']) + + +def box_iou_rotated(bboxes1, bboxes2, mode='iou', aligned=False): + """Return intersection-over-union (Jaccard index) of boxes. + + Both sets of boxes are expected to be in + (x_center, y_center, width, height, angle) format. + + If ``aligned`` is ``False``, then calculate the ious between each bbox + of bboxes1 and bboxes2, otherwise the ious between each aligned pair of + bboxes1 and bboxes2. + + Arguments: + boxes1 (Tensor): rotated bboxes 1. \ + It has shape (N, 5), indicating (x, y, w, h, theta) for each row. + Note that theta is in radian. + boxes2 (Tensor): rotated bboxes 2. \ + It has shape (M, 5), indicating (x, y, w, h, theta) for each row. + Note that theta is in radian. + mode (str): "iou" (intersection over union) or iof (intersection over + foreground). + + Returns: + ious(Tensor): shape (N, M) if aligned == False else shape (N,) + """ + assert mode in ['iou', 'iof'] + mode_dict = {'iou': 0, 'iof': 1} + mode_flag = mode_dict[mode] + rows = bboxes1.size(0) + cols = bboxes2.size(0) + if aligned: + ious = bboxes1.new_zeros(rows) + else: + ious = bboxes1.new_zeros((rows * cols)) + bboxes1 = bboxes1.contiguous() + bboxes2 = bboxes2.contiguous() + ext_module.box_iou_rotated( + bboxes1, bboxes2, ious, mode_flag=mode_flag, aligned=aligned) + if not aligned: + ious = ious.view(rows, cols) + return ious diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/carafe.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/carafe.py new file mode 100644 index 0000000000000000000000000000000000000000..5154cb3abfccfbbe0a1b2daa67018dbf80aaf6d2 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/carafe.py @@ -0,0 +1,287 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.autograd import Function +from torch.nn.modules.module import Module + +from ..cnn import UPSAMPLE_LAYERS, normal_init, xavier_init +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', [ + 'carafe_naive_forward', 'carafe_naive_backward', 'carafe_forward', + 'carafe_backward' +]) + + +class CARAFENaiveFunction(Function): + + @staticmethod + def symbolic(g, features, masks, kernel_size, group_size, scale_factor): + return g.op( + 'mmcv::MMCVCARAFENaive', + features, + masks, + kernel_size_i=kernel_size, + group_size_i=group_size, + scale_factor_f=scale_factor) + + @staticmethod + def forward(ctx, features, masks, kernel_size, group_size, scale_factor): + assert scale_factor >= 1 + assert masks.size(1) == kernel_size * kernel_size * group_size + assert masks.size(-1) == features.size(-1) * scale_factor + assert masks.size(-2) == features.size(-2) * scale_factor + assert features.size(1) % group_size == 0 + assert (kernel_size - 1) % 2 == 0 and kernel_size >= 1 + ctx.kernel_size = kernel_size + ctx.group_size = group_size + ctx.scale_factor = scale_factor + ctx.feature_size = features.size() + ctx.mask_size = masks.size() + + n, c, h, w = features.size() + output = features.new_zeros((n, c, h * scale_factor, w * scale_factor)) + ext_module.carafe_naive_forward( + features, + masks, + output, + kernel_size=kernel_size, + group_size=group_size, + scale_factor=scale_factor) + + if features.requires_grad or masks.requires_grad: + ctx.save_for_backward(features, masks) + return output + + @staticmethod + def backward(ctx, grad_output): + assert grad_output.is_cuda + + features, masks = ctx.saved_tensors + kernel_size = ctx.kernel_size + group_size = ctx.group_size + scale_factor = ctx.scale_factor + + grad_input = torch.zeros_like(features) + grad_masks = torch.zeros_like(masks) + ext_module.carafe_naive_backward( + grad_output.contiguous(), + features, + masks, + grad_input, + grad_masks, + kernel_size=kernel_size, + group_size=group_size, + scale_factor=scale_factor) + + return grad_input, grad_masks, None, None, None + + +carafe_naive = CARAFENaiveFunction.apply + + +class CARAFENaive(Module): + + def __init__(self, kernel_size, group_size, scale_factor): + super(CARAFENaive, self).__init__() + + assert isinstance(kernel_size, int) and isinstance( + group_size, int) and isinstance(scale_factor, int) + self.kernel_size = kernel_size + self.group_size = group_size + self.scale_factor = scale_factor + + def forward(self, features, masks): + return carafe_naive(features, masks, self.kernel_size, self.group_size, + self.scale_factor) + + +class CARAFEFunction(Function): + + @staticmethod + def symbolic(g, features, masks, kernel_size, group_size, scale_factor): + return g.op( + 'mmcv::MMCVCARAFE', + features, + masks, + kernel_size_i=kernel_size, + group_size_i=group_size, + scale_factor_f=scale_factor) + + @staticmethod + def forward(ctx, features, masks, kernel_size, group_size, scale_factor): + assert scale_factor >= 1 + assert masks.size(1) == kernel_size * kernel_size * group_size + assert masks.size(-1) == features.size(-1) * scale_factor + assert masks.size(-2) == features.size(-2) * scale_factor + assert features.size(1) % group_size == 0 + assert (kernel_size - 1) % 2 == 0 and kernel_size >= 1 + ctx.kernel_size = kernel_size + ctx.group_size = group_size + ctx.scale_factor = scale_factor + ctx.feature_size = features.size() + ctx.mask_size = masks.size() + + n, c, h, w = features.size() + output = features.new_zeros((n, c, h * scale_factor, w * scale_factor)) + routput = features.new_zeros(output.size(), requires_grad=False) + rfeatures = features.new_zeros(features.size(), requires_grad=False) + rmasks = masks.new_zeros(masks.size(), requires_grad=False) + ext_module.carafe_forward( + features, + masks, + rfeatures, + routput, + rmasks, + output, + kernel_size=kernel_size, + group_size=group_size, + scale_factor=scale_factor) + + if features.requires_grad or masks.requires_grad: + ctx.save_for_backward(features, masks, rfeatures) + return output + + @staticmethod + def backward(ctx, grad_output): + assert grad_output.is_cuda + + features, masks, rfeatures = ctx.saved_tensors + kernel_size = ctx.kernel_size + group_size = ctx.group_size + scale_factor = ctx.scale_factor + + rgrad_output = torch.zeros_like(grad_output, requires_grad=False) + rgrad_input_hs = torch.zeros_like(grad_output, requires_grad=False) + rgrad_input = torch.zeros_like(features, requires_grad=False) + rgrad_masks = torch.zeros_like(masks, requires_grad=False) + grad_input = torch.zeros_like(features, requires_grad=False) + grad_masks = torch.zeros_like(masks, requires_grad=False) + ext_module.carafe_backward( + grad_output.contiguous(), + rfeatures, + masks, + rgrad_output, + rgrad_input_hs, + rgrad_input, + rgrad_masks, + grad_input, + grad_masks, + kernel_size=kernel_size, + group_size=group_size, + scale_factor=scale_factor) + return grad_input, grad_masks, None, None, None + + +carafe = CARAFEFunction.apply + + +class CARAFE(Module): + """ CARAFE: Content-Aware ReAssembly of FEatures + + Please refer to https://arxiv.org/abs/1905.02188 for more details. + + Args: + kernel_size (int): reassemble kernel size + group_size (int): reassemble group size + scale_factor (int): upsample ratio + + Returns: + upsampled feature map + """ + + def __init__(self, kernel_size, group_size, scale_factor): + super(CARAFE, self).__init__() + + assert isinstance(kernel_size, int) and isinstance( + group_size, int) and isinstance(scale_factor, int) + self.kernel_size = kernel_size + self.group_size = group_size + self.scale_factor = scale_factor + + def forward(self, features, masks): + return carafe(features, masks, self.kernel_size, self.group_size, + self.scale_factor) + + +@UPSAMPLE_LAYERS.register_module(name='carafe') +class CARAFEPack(nn.Module): + """A unified package of CARAFE upsampler that contains: 1) channel + compressor 2) content encoder 3) CARAFE op. + + Official implementation of ICCV 2019 paper + CARAFE: Content-Aware ReAssembly of FEatures + Please refer to https://arxiv.org/abs/1905.02188 for more details. + + Args: + channels (int): input feature channels + scale_factor (int): upsample ratio + up_kernel (int): kernel size of CARAFE op + up_group (int): group size of CARAFE op + encoder_kernel (int): kernel size of content encoder + encoder_dilation (int): dilation of content encoder + compressed_channels (int): output channels of channels compressor + + Returns: + upsampled feature map + """ + + def __init__(self, + channels, + scale_factor, + up_kernel=5, + up_group=1, + encoder_kernel=3, + encoder_dilation=1, + compressed_channels=64): + super(CARAFEPack, self).__init__() + self.channels = channels + self.scale_factor = scale_factor + self.up_kernel = up_kernel + self.up_group = up_group + self.encoder_kernel = encoder_kernel + self.encoder_dilation = encoder_dilation + self.compressed_channels = compressed_channels + self.channel_compressor = nn.Conv2d(channels, self.compressed_channels, + 1) + self.content_encoder = nn.Conv2d( + self.compressed_channels, + self.up_kernel * self.up_kernel * self.up_group * + self.scale_factor * self.scale_factor, + self.encoder_kernel, + padding=int((self.encoder_kernel - 1) * self.encoder_dilation / 2), + dilation=self.encoder_dilation, + groups=1) + self.init_weights() + + def init_weights(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + xavier_init(m, distribution='uniform') + normal_init(self.content_encoder, std=0.001) + + def kernel_normalizer(self, mask): + mask = F.pixel_shuffle(mask, self.scale_factor) + n, mask_c, h, w = mask.size() + # use float division explicitly, + # to void inconsistency while exporting to onnx + mask_channel = int(mask_c / float(self.up_kernel**2)) + mask = mask.view(n, mask_channel, -1, h, w) + + mask = F.softmax(mask, dim=2, dtype=mask.dtype) + mask = mask.view(n, mask_c, h, w).contiguous() + + return mask + + def feature_reassemble(self, x, mask): + x = carafe(x, mask, self.up_kernel, self.up_group, self.scale_factor) + return x + + def forward(self, x): + compressed_x = self.channel_compressor(x) + mask = self.content_encoder(compressed_x) + mask = self.kernel_normalizer(mask) + + x = self.feature_reassemble(x, mask) + return x diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/cc_attention.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/cc_attention.py new file mode 100644 index 0000000000000000000000000000000000000000..8982f467185b5d839832baa2e51722613a8b87a2 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/cc_attention.py @@ -0,0 +1,83 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.nn as nn +import torch.nn.functional as F + +from annotator.mmpkg.mmcv.cnn import PLUGIN_LAYERS, Scale + + +def NEG_INF_DIAG(n, device): + """Returns a diagonal matrix of size [n, n]. + + The diagonal are all "-inf". This is for avoiding calculating the + overlapped element in the Criss-Cross twice. + """ + return torch.diag(torch.tensor(float('-inf')).to(device).repeat(n), 0) + + +@PLUGIN_LAYERS.register_module() +class CrissCrossAttention(nn.Module): + """Criss-Cross Attention Module. + + .. note:: + Before v1.3.13, we use a CUDA op. Since v1.3.13, we switch + to a pure PyTorch and equivalent implementation. For more + details, please refer to https://github.com/open-mmlab/mmcv/pull/1201. + + Speed comparison for one forward pass + + - Input size: [2,512,97,97] + - Device: 1 NVIDIA GeForce RTX 2080 Ti + + +-----------------------+---------------+------------+---------------+ + | |PyTorch version|CUDA version|Relative speed | + +=======================+===============+============+===============+ + |with torch.no_grad() |0.00554402 s |0.0299619 s |5.4x | + +-----------------------+---------------+------------+---------------+ + |no with torch.no_grad()|0.00562803 s |0.0301349 s |5.4x | + +-----------------------+---------------+------------+---------------+ + + Args: + in_channels (int): Channels of the input feature map. + """ + + def __init__(self, in_channels): + super().__init__() + self.query_conv = nn.Conv2d(in_channels, in_channels // 8, 1) + self.key_conv = nn.Conv2d(in_channels, in_channels // 8, 1) + self.value_conv = nn.Conv2d(in_channels, in_channels, 1) + self.gamma = Scale(0.) + self.in_channels = in_channels + + def forward(self, x): + """forward function of Criss-Cross Attention. + + Args: + x (Tensor): Input feature. \ + shape (batch_size, in_channels, height, width) + Returns: + Tensor: Output of the layer, with shape of \ + (batch_size, in_channels, height, width) + """ + B, C, H, W = x.size() + query = self.query_conv(x) + key = self.key_conv(x) + value = self.value_conv(x) + energy_H = torch.einsum('bchw,bciw->bwhi', query, key) + NEG_INF_DIAG( + H, query.device) + energy_H = energy_H.transpose(1, 2) + energy_W = torch.einsum('bchw,bchj->bhwj', query, key) + attn = F.softmax( + torch.cat([energy_H, energy_W], dim=-1), dim=-1) # [B,H,W,(H+W)] + out = torch.einsum('bciw,bhwi->bchw', value, attn[..., :H]) + out += torch.einsum('bchj,bhwj->bchw', value, attn[..., H:]) + + out = self.gamma(out) + x + out = out.contiguous() + + return out + + def __repr__(self): + s = self.__class__.__name__ + s += f'(in_channels={self.in_channels})' + return s diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/contour_expand.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/contour_expand.py new file mode 100644 index 0000000000000000000000000000000000000000..ea1111e1768b5f27e118bf7dbc0d9c70a7afd6d7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/contour_expand.py @@ -0,0 +1,49 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import numpy as np +import torch + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', ['contour_expand']) + + +def contour_expand(kernel_mask, internal_kernel_label, min_kernel_area, + kernel_num): + """Expand kernel contours so that foreground pixels are assigned into + instances. + + Arguments: + kernel_mask (np.array or Tensor): The instance kernel mask with + size hxw. + internal_kernel_label (np.array or Tensor): The instance internal + kernel label with size hxw. + min_kernel_area (int): The minimum kernel area. + kernel_num (int): The instance kernel number. + + Returns: + label (list): The instance index map with size hxw. + """ + assert isinstance(kernel_mask, (torch.Tensor, np.ndarray)) + assert isinstance(internal_kernel_label, (torch.Tensor, np.ndarray)) + assert isinstance(min_kernel_area, int) + assert isinstance(kernel_num, int) + + if isinstance(kernel_mask, np.ndarray): + kernel_mask = torch.from_numpy(kernel_mask) + if isinstance(internal_kernel_label, np.ndarray): + internal_kernel_label = torch.from_numpy(internal_kernel_label) + + if torch.__version__ == 'parrots': + if kernel_mask.shape[0] == 0 or internal_kernel_label.shape[0] == 0: + label = [] + else: + label = ext_module.contour_expand( + kernel_mask, + internal_kernel_label, + min_kernel_area=min_kernel_area, + kernel_num=kernel_num) + label = label.tolist() + else: + label = ext_module.contour_expand(kernel_mask, internal_kernel_label, + min_kernel_area, kernel_num) + return label diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/corner_pool.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/corner_pool.py new file mode 100644 index 0000000000000000000000000000000000000000..a33d798b43d405e4c86bee4cd6389be21ca9c637 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/corner_pool.py @@ -0,0 +1,161 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +from torch import nn +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', [ + 'top_pool_forward', 'top_pool_backward', 'bottom_pool_forward', + 'bottom_pool_backward', 'left_pool_forward', 'left_pool_backward', + 'right_pool_forward', 'right_pool_backward' +]) + +_mode_dict = {'top': 0, 'bottom': 1, 'left': 2, 'right': 3} + + +class TopPoolFunction(Function): + + @staticmethod + def symbolic(g, input): + output = g.op( + 'mmcv::MMCVCornerPool', input, mode_i=int(_mode_dict['top'])) + return output + + @staticmethod + def forward(ctx, input): + output = ext_module.top_pool_forward(input) + ctx.save_for_backward(input) + return output + + @staticmethod + def backward(ctx, grad_output): + input, = ctx.saved_tensors + output = ext_module.top_pool_backward(input, grad_output) + return output + + +class BottomPoolFunction(Function): + + @staticmethod + def symbolic(g, input): + output = g.op( + 'mmcv::MMCVCornerPool', input, mode_i=int(_mode_dict['bottom'])) + return output + + @staticmethod + def forward(ctx, input): + output = ext_module.bottom_pool_forward(input) + ctx.save_for_backward(input) + return output + + @staticmethod + def backward(ctx, grad_output): + input, = ctx.saved_tensors + output = ext_module.bottom_pool_backward(input, grad_output) + return output + + +class LeftPoolFunction(Function): + + @staticmethod + def symbolic(g, input): + output = g.op( + 'mmcv::MMCVCornerPool', input, mode_i=int(_mode_dict['left'])) + return output + + @staticmethod + def forward(ctx, input): + output = ext_module.left_pool_forward(input) + ctx.save_for_backward(input) + return output + + @staticmethod + def backward(ctx, grad_output): + input, = ctx.saved_tensors + output = ext_module.left_pool_backward(input, grad_output) + return output + + +class RightPoolFunction(Function): + + @staticmethod + def symbolic(g, input): + output = g.op( + 'mmcv::MMCVCornerPool', input, mode_i=int(_mode_dict['right'])) + return output + + @staticmethod + def forward(ctx, input): + output = ext_module.right_pool_forward(input) + ctx.save_for_backward(input) + return output + + @staticmethod + def backward(ctx, grad_output): + input, = ctx.saved_tensors + output = ext_module.right_pool_backward(input, grad_output) + return output + + +class CornerPool(nn.Module): + """Corner Pooling. + + Corner Pooling is a new type of pooling layer that helps a + convolutional network better localize corners of bounding boxes. + + Please refer to https://arxiv.org/abs/1808.01244 for more details. + Code is modified from https://github.com/princeton-vl/CornerNet-Lite. + + Args: + mode(str): Pooling orientation for the pooling layer + + - 'bottom': Bottom Pooling + - 'left': Left Pooling + - 'right': Right Pooling + - 'top': Top Pooling + + Returns: + Feature map after pooling. + """ + + pool_functions = { + 'bottom': BottomPoolFunction, + 'left': LeftPoolFunction, + 'right': RightPoolFunction, + 'top': TopPoolFunction, + } + + cummax_dim_flip = { + 'bottom': (2, False), + 'left': (3, True), + 'right': (3, False), + 'top': (2, True), + } + + def __init__(self, mode): + super(CornerPool, self).__init__() + assert mode in self.pool_functions + self.mode = mode + self.corner_pool = self.pool_functions[mode] + + def forward(self, x): + if torch.__version__ != 'parrots' and torch.__version__ >= '1.5.0': + if torch.onnx.is_in_onnx_export(): + assert torch.__version__ >= '1.7.0', \ + 'When `cummax` serves as an intermediate component whose '\ + 'outputs is used as inputs for another modules, it\'s '\ + 'expected that pytorch version must be >= 1.7.0, '\ + 'otherwise Error appears like: `RuntimeError: tuple '\ + 'appears in op that does not forward tuples, unsupported '\ + 'kind: prim::PythonOp`.' + + dim, flip = self.cummax_dim_flip[self.mode] + if flip: + x = x.flip(dim) + pool_tensor, _ = torch.cummax(x, dim=dim) + if flip: + pool_tensor = pool_tensor.flip(dim) + return pool_tensor + else: + return self.corner_pool.apply(x) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/correlation.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/correlation.py new file mode 100644 index 0000000000000000000000000000000000000000..3d0b79c301b29915dfaf4d2b1846c59be73127d3 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/correlation.py @@ -0,0 +1,196 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +from torch import Tensor, nn +from torch.autograd import Function +from torch.autograd.function import once_differentiable +from torch.nn.modules.utils import _pair + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', ['correlation_forward', 'correlation_backward']) + + +class CorrelationFunction(Function): + + @staticmethod + def forward(ctx, + input1, + input2, + kernel_size=1, + max_displacement=1, + stride=1, + padding=1, + dilation=1, + dilation_patch=1): + + ctx.save_for_backward(input1, input2) + + kH, kW = ctx.kernel_size = _pair(kernel_size) + patch_size = max_displacement * 2 + 1 + ctx.patch_size = patch_size + dH, dW = ctx.stride = _pair(stride) + padH, padW = ctx.padding = _pair(padding) + dilationH, dilationW = ctx.dilation = _pair(dilation) + dilation_patchH, dilation_patchW = ctx.dilation_patch = _pair( + dilation_patch) + + output_size = CorrelationFunction._output_size(ctx, input1) + + output = input1.new_zeros(output_size) + + ext_module.correlation_forward( + input1, + input2, + output, + kH=kH, + kW=kW, + patchH=patch_size, + patchW=patch_size, + padH=padH, + padW=padW, + dilationH=dilationH, + dilationW=dilationW, + dilation_patchH=dilation_patchH, + dilation_patchW=dilation_patchW, + dH=dH, + dW=dW) + + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + input1, input2 = ctx.saved_tensors + + kH, kW = ctx.kernel_size + patch_size = ctx.patch_size + padH, padW = ctx.padding + dilationH, dilationW = ctx.dilation + dilation_patchH, dilation_patchW = ctx.dilation_patch + dH, dW = ctx.stride + grad_input1 = torch.zeros_like(input1) + grad_input2 = torch.zeros_like(input2) + + ext_module.correlation_backward( + grad_output, + input1, + input2, + grad_input1, + grad_input2, + kH=kH, + kW=kW, + patchH=patch_size, + patchW=patch_size, + padH=padH, + padW=padW, + dilationH=dilationH, + dilationW=dilationW, + dilation_patchH=dilation_patchH, + dilation_patchW=dilation_patchW, + dH=dH, + dW=dW) + return grad_input1, grad_input2, None, None, None, None, None, None + + @staticmethod + def _output_size(ctx, input1): + iH, iW = input1.size(2), input1.size(3) + batch_size = input1.size(0) + kH, kW = ctx.kernel_size + patch_size = ctx.patch_size + dH, dW = ctx.stride + padH, padW = ctx.padding + dilationH, dilationW = ctx.dilation + dilatedKH = (kH - 1) * dilationH + 1 + dilatedKW = (kW - 1) * dilationW + 1 + + oH = int((iH + 2 * padH - dilatedKH) / dH + 1) + oW = int((iW + 2 * padW - dilatedKW) / dW + 1) + + output_size = (batch_size, patch_size, patch_size, oH, oW) + return output_size + + +class Correlation(nn.Module): + r"""Correlation operator + + This correlation operator works for optical flow correlation computation. + + There are two batched tensors with shape :math:`(N, C, H, W)`, + and the correlation output's shape is :math:`(N, max\_displacement \times + 2 + 1, max\_displacement * 2 + 1, H_{out}, W_{out})` + + where + + .. math:: + H_{out} = \left\lfloor\frac{H_{in} + 2 \times padding - + dilation \times (kernel\_size - 1) - 1} + {stride} + 1\right\rfloor + + .. math:: + W_{out} = \left\lfloor\frac{W_{in} + 2 \times padding - dilation + \times (kernel\_size - 1) - 1} + {stride} + 1\right\rfloor + + the correlation item :math:`(N_i, dy, dx)` is formed by taking the sliding + window convolution between input1 and shifted input2, + + .. math:: + Corr(N_i, dx, dy) = + \sum_{c=0}^{C-1} + input1(N_i, c) \star + \mathcal{S}(input2(N_i, c), dy, dx) + + where :math:`\star` is the valid 2d sliding window convolution operator, + and :math:`\mathcal{S}` means shifting the input features (auto-complete + zero marginal), and :math:`dx, dy` are shifting distance, :math:`dx, dy \in + [-max\_displacement \times dilation\_patch, max\_displacement \times + dilation\_patch]`. + + Args: + kernel_size (int): The size of sliding window i.e. local neighborhood + representing the center points and involved in correlation + computation. Defaults to 1. + max_displacement (int): The radius for computing correlation volume, + but the actual working space can be dilated by dilation_patch. + Defaults to 1. + stride (int): The stride of the sliding blocks in the input spatial + dimensions. Defaults to 1. + padding (int): Zero padding added to all four sides of the input1. + Defaults to 0. + dilation (int): The spacing of local neighborhood that will involved + in correlation. Defaults to 1. + dilation_patch (int): The spacing between position need to compute + correlation. Defaults to 1. + """ + + def __init__(self, + kernel_size: int = 1, + max_displacement: int = 1, + stride: int = 1, + padding: int = 0, + dilation: int = 1, + dilation_patch: int = 1) -> None: + super().__init__() + self.kernel_size = kernel_size + self.max_displacement = max_displacement + self.stride = stride + self.padding = padding + self.dilation = dilation + self.dilation_patch = dilation_patch + + def forward(self, input1: Tensor, input2: Tensor) -> Tensor: + return CorrelationFunction.apply(input1, input2, self.kernel_size, + self.max_displacement, self.stride, + self.padding, self.dilation, + self.dilation_patch) + + def __repr__(self) -> str: + s = self.__class__.__name__ + s += f'(kernel_size={self.kernel_size}, ' + s += f'max_displacement={self.max_displacement}, ' + s += f'stride={self.stride}, ' + s += f'padding={self.padding}, ' + s += f'dilation={self.dilation}, ' + s += f'dilation_patch={self.dilation_patch})' + return s diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/deform_conv.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/deform_conv.py new file mode 100644 index 0000000000000000000000000000000000000000..3de3aae1e7b2258360aef3ad9eb3a351f080f10f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/deform_conv.py @@ -0,0 +1,405 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from typing import Tuple, Union + +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch import Tensor +from torch.autograd import Function +from torch.autograd.function import once_differentiable +from torch.nn.modules.utils import _pair, _single + +from annotator.mmpkg.mmcv.utils import deprecated_api_warning +from ..cnn import CONV_LAYERS +from ..utils import ext_loader, print_log + +ext_module = ext_loader.load_ext('_ext', [ + 'deform_conv_forward', 'deform_conv_backward_input', + 'deform_conv_backward_parameters' +]) + + +class DeformConv2dFunction(Function): + + @staticmethod + def symbolic(g, + input, + offset, + weight, + stride, + padding, + dilation, + groups, + deform_groups, + bias=False, + im2col_step=32): + return g.op( + 'mmcv::MMCVDeformConv2d', + input, + offset, + weight, + stride_i=stride, + padding_i=padding, + dilation_i=dilation, + groups_i=groups, + deform_groups_i=deform_groups, + bias_i=bias, + im2col_step_i=im2col_step) + + @staticmethod + def forward(ctx, + input, + offset, + weight, + stride=1, + padding=0, + dilation=1, + groups=1, + deform_groups=1, + bias=False, + im2col_step=32): + if input is not None and input.dim() != 4: + raise ValueError( + f'Expected 4D tensor as input, got {input.dim()}D tensor \ + instead.') + assert bias is False, 'Only support bias is False.' + ctx.stride = _pair(stride) + ctx.padding = _pair(padding) + ctx.dilation = _pair(dilation) + ctx.groups = groups + ctx.deform_groups = deform_groups + ctx.im2col_step = im2col_step + + # When pytorch version >= 1.6.0, amp is adopted for fp16 mode; + # amp won't cast the type of model (float32), but "offset" is cast + # to float16 by nn.Conv2d automatically, leading to the type + # mismatch with input (when it is float32) or weight. + # The flag for whether to use fp16 or amp is the type of "offset", + # we cast weight and input to temporarily support fp16 and amp + # whatever the pytorch version is. + input = input.type_as(offset) + weight = weight.type_as(input) + ctx.save_for_backward(input, offset, weight) + + output = input.new_empty( + DeformConv2dFunction._output_size(ctx, input, weight)) + + ctx.bufs_ = [input.new_empty(0), input.new_empty(0)] # columns, ones + + cur_im2col_step = min(ctx.im2col_step, input.size(0)) + assert (input.size(0) % + cur_im2col_step) == 0, 'im2col step must divide batchsize' + ext_module.deform_conv_forward( + input, + weight, + offset, + output, + ctx.bufs_[0], + ctx.bufs_[1], + kW=weight.size(3), + kH=weight.size(2), + dW=ctx.stride[1], + dH=ctx.stride[0], + padW=ctx.padding[1], + padH=ctx.padding[0], + dilationW=ctx.dilation[1], + dilationH=ctx.dilation[0], + group=ctx.groups, + deformable_group=ctx.deform_groups, + im2col_step=cur_im2col_step) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + input, offset, weight = ctx.saved_tensors + + grad_input = grad_offset = grad_weight = None + + cur_im2col_step = min(ctx.im2col_step, input.size(0)) + assert (input.size(0) % cur_im2col_step + ) == 0, 'batch size must be divisible by im2col_step' + + grad_output = grad_output.contiguous() + if ctx.needs_input_grad[0] or ctx.needs_input_grad[1]: + grad_input = torch.zeros_like(input) + grad_offset = torch.zeros_like(offset) + ext_module.deform_conv_backward_input( + input, + offset, + grad_output, + grad_input, + grad_offset, + weight, + ctx.bufs_[0], + kW=weight.size(3), + kH=weight.size(2), + dW=ctx.stride[1], + dH=ctx.stride[0], + padW=ctx.padding[1], + padH=ctx.padding[0], + dilationW=ctx.dilation[1], + dilationH=ctx.dilation[0], + group=ctx.groups, + deformable_group=ctx.deform_groups, + im2col_step=cur_im2col_step) + + if ctx.needs_input_grad[2]: + grad_weight = torch.zeros_like(weight) + ext_module.deform_conv_backward_parameters( + input, + offset, + grad_output, + grad_weight, + ctx.bufs_[0], + ctx.bufs_[1], + kW=weight.size(3), + kH=weight.size(2), + dW=ctx.stride[1], + dH=ctx.stride[0], + padW=ctx.padding[1], + padH=ctx.padding[0], + dilationW=ctx.dilation[1], + dilationH=ctx.dilation[0], + group=ctx.groups, + deformable_group=ctx.deform_groups, + scale=1, + im2col_step=cur_im2col_step) + + return grad_input, grad_offset, grad_weight, \ + None, None, None, None, None, None, None + + @staticmethod + def _output_size(ctx, input, weight): + channels = weight.size(0) + output_size = (input.size(0), channels) + for d in range(input.dim() - 2): + in_size = input.size(d + 2) + pad = ctx.padding[d] + kernel = ctx.dilation[d] * (weight.size(d + 2) - 1) + 1 + stride_ = ctx.stride[d] + output_size += ((in_size + (2 * pad) - kernel) // stride_ + 1, ) + if not all(map(lambda s: s > 0, output_size)): + raise ValueError( + 'convolution input is too small (output would be ' + + 'x'.join(map(str, output_size)) + ')') + return output_size + + +deform_conv2d = DeformConv2dFunction.apply + + +class DeformConv2d(nn.Module): + r"""Deformable 2D convolution. + + Applies a deformable 2D convolution over an input signal composed of + several input planes. DeformConv2d was described in the paper + `Deformable Convolutional Networks + `_ + + Note: + The argument ``im2col_step`` was added in version 1.3.17, which means + number of samples processed by the ``im2col_cuda_kernel`` per call. + It enables users to define ``batch_size`` and ``im2col_step`` more + flexibly and solved `issue mmcv#1440 + `_. + + Args: + in_channels (int): Number of channels in the input image. + out_channels (int): Number of channels produced by the convolution. + kernel_size(int, tuple): Size of the convolving kernel. + stride(int, tuple): Stride of the convolution. Default: 1. + padding (int or tuple): Zero-padding added to both sides of the input. + Default: 0. + dilation (int or tuple): Spacing between kernel elements. Default: 1. + groups (int): Number of blocked connections from input. + channels to output channels. Default: 1. + deform_groups (int): Number of deformable group partitions. + bias (bool): If True, adds a learnable bias to the output. + Default: False. + im2col_step (int): Number of samples processed by im2col_cuda_kernel + per call. It will work when ``batch_size`` > ``im2col_step``, but + ``batch_size`` must be divisible by ``im2col_step``. Default: 32. + `New in version 1.3.17.` + """ + + @deprecated_api_warning({'deformable_groups': 'deform_groups'}, + cls_name='DeformConv2d') + def __init__(self, + in_channels: int, + out_channels: int, + kernel_size: Union[int, Tuple[int, ...]], + stride: Union[int, Tuple[int, ...]] = 1, + padding: Union[int, Tuple[int, ...]] = 0, + dilation: Union[int, Tuple[int, ...]] = 1, + groups: int = 1, + deform_groups: int = 1, + bias: bool = False, + im2col_step: int = 32) -> None: + super(DeformConv2d, self).__init__() + + assert not bias, \ + f'bias={bias} is not supported in DeformConv2d.' + assert in_channels % groups == 0, \ + f'in_channels {in_channels} cannot be divisible by groups {groups}' + assert out_channels % groups == 0, \ + f'out_channels {out_channels} cannot be divisible by groups \ + {groups}' + + self.in_channels = in_channels + self.out_channels = out_channels + self.kernel_size = _pair(kernel_size) + self.stride = _pair(stride) + self.padding = _pair(padding) + self.dilation = _pair(dilation) + self.groups = groups + self.deform_groups = deform_groups + self.im2col_step = im2col_step + # enable compatibility with nn.Conv2d + self.transposed = False + self.output_padding = _single(0) + + # only weight, no bias + self.weight = nn.Parameter( + torch.Tensor(out_channels, in_channels // self.groups, + *self.kernel_size)) + + self.reset_parameters() + + def reset_parameters(self): + # switch the initialization of `self.weight` to the standard kaiming + # method described in `Delving deep into rectifiers: Surpassing + # human-level performance on ImageNet classification` - He, K. et al. + # (2015), using a uniform distribution + nn.init.kaiming_uniform_(self.weight, nonlinearity='relu') + + def forward(self, x: Tensor, offset: Tensor) -> Tensor: + """Deformable Convolutional forward function. + + Args: + x (Tensor): Input feature, shape (B, C_in, H_in, W_in) + offset (Tensor): Offset for deformable convolution, shape + (B, deform_groups*kernel_size[0]*kernel_size[1]*2, + H_out, W_out), H_out, W_out are equal to the output's. + + An offset is like `[y0, x0, y1, x1, y2, x2, ..., y8, x8]`. + The spatial arrangement is like: + + .. code:: text + + (x0, y0) (x1, y1) (x2, y2) + (x3, y3) (x4, y4) (x5, y5) + (x6, y6) (x7, y7) (x8, y8) + + Returns: + Tensor: Output of the layer. + """ + # To fix an assert error in deform_conv_cuda.cpp:128 + # input image is smaller than kernel + input_pad = (x.size(2) < self.kernel_size[0]) or (x.size(3) < + self.kernel_size[1]) + if input_pad: + pad_h = max(self.kernel_size[0] - x.size(2), 0) + pad_w = max(self.kernel_size[1] - x.size(3), 0) + x = F.pad(x, (0, pad_w, 0, pad_h), 'constant', 0).contiguous() + offset = F.pad(offset, (0, pad_w, 0, pad_h), 'constant', 0) + offset = offset.contiguous() + out = deform_conv2d(x, offset, self.weight, self.stride, self.padding, + self.dilation, self.groups, self.deform_groups, + False, self.im2col_step) + if input_pad: + out = out[:, :, :out.size(2) - pad_h, :out.size(3) - + pad_w].contiguous() + return out + + def __repr__(self): + s = self.__class__.__name__ + s += f'(in_channels={self.in_channels},\n' + s += f'out_channels={self.out_channels},\n' + s += f'kernel_size={self.kernel_size},\n' + s += f'stride={self.stride},\n' + s += f'padding={self.padding},\n' + s += f'dilation={self.dilation},\n' + s += f'groups={self.groups},\n' + s += f'deform_groups={self.deform_groups},\n' + # bias is not supported in DeformConv2d. + s += 'bias=False)' + return s + + +@CONV_LAYERS.register_module('DCN') +class DeformConv2dPack(DeformConv2d): + """A Deformable Conv Encapsulation that acts as normal Conv layers. + + The offset tensor is like `[y0, x0, y1, x1, y2, x2, ..., y8, x8]`. + The spatial arrangement is like: + + .. code:: text + + (x0, y0) (x1, y1) (x2, y2) + (x3, y3) (x4, y4) (x5, y5) + (x6, y6) (x7, y7) (x8, y8) + + Args: + in_channels (int): Same as nn.Conv2d. + out_channels (int): Same as nn.Conv2d. + kernel_size (int or tuple[int]): Same as nn.Conv2d. + stride (int or tuple[int]): Same as nn.Conv2d. + padding (int or tuple[int]): Same as nn.Conv2d. + dilation (int or tuple[int]): Same as nn.Conv2d. + groups (int): Same as nn.Conv2d. + bias (bool or str): If specified as `auto`, it will be decided by the + norm_cfg. Bias will be set as True if norm_cfg is None, otherwise + False. + """ + + _version = 2 + + def __init__(self, *args, **kwargs): + super(DeformConv2dPack, self).__init__(*args, **kwargs) + self.conv_offset = nn.Conv2d( + self.in_channels, + self.deform_groups * 2 * self.kernel_size[0] * self.kernel_size[1], + kernel_size=self.kernel_size, + stride=_pair(self.stride), + padding=_pair(self.padding), + dilation=_pair(self.dilation), + bias=True) + self.init_offset() + + def init_offset(self): + self.conv_offset.weight.data.zero_() + self.conv_offset.bias.data.zero_() + + def forward(self, x): + offset = self.conv_offset(x) + return deform_conv2d(x, offset, self.weight, self.stride, self.padding, + self.dilation, self.groups, self.deform_groups, + False, self.im2col_step) + + def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, + missing_keys, unexpected_keys, error_msgs): + version = local_metadata.get('version', None) + + if version is None or version < 2: + # the key is different in early versions + # In version < 2, DeformConvPack loads previous benchmark models. + if (prefix + 'conv_offset.weight' not in state_dict + and prefix[:-1] + '_offset.weight' in state_dict): + state_dict[prefix + 'conv_offset.weight'] = state_dict.pop( + prefix[:-1] + '_offset.weight') + if (prefix + 'conv_offset.bias' not in state_dict + and prefix[:-1] + '_offset.bias' in state_dict): + state_dict[prefix + + 'conv_offset.bias'] = state_dict.pop(prefix[:-1] + + '_offset.bias') + + if version is not None and version > 1: + print_log( + f'DeformConv2dPack {prefix.rstrip(".")} is upgraded to ' + 'version 2.', + logger='root') + + super()._load_from_state_dict(state_dict, prefix, local_metadata, + strict, missing_keys, unexpected_keys, + error_msgs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/deform_roi_pool.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/deform_roi_pool.py new file mode 100644 index 0000000000000000000000000000000000000000..cc245ba91fee252226ba22e76bb94a35db9a629b --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/deform_roi_pool.py @@ -0,0 +1,204 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from torch import nn +from torch.autograd import Function +from torch.autograd.function import once_differentiable +from torch.nn.modules.utils import _pair + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', ['deform_roi_pool_forward', 'deform_roi_pool_backward']) + + +class DeformRoIPoolFunction(Function): + + @staticmethod + def symbolic(g, input, rois, offset, output_size, spatial_scale, + sampling_ratio, gamma): + return g.op( + 'mmcv::MMCVDeformRoIPool', + input, + rois, + offset, + pooled_height_i=output_size[0], + pooled_width_i=output_size[1], + spatial_scale_f=spatial_scale, + sampling_ratio_f=sampling_ratio, + gamma_f=gamma) + + @staticmethod + def forward(ctx, + input, + rois, + offset, + output_size, + spatial_scale=1.0, + sampling_ratio=0, + gamma=0.1): + if offset is None: + offset = input.new_zeros(0) + ctx.output_size = _pair(output_size) + ctx.spatial_scale = float(spatial_scale) + ctx.sampling_ratio = int(sampling_ratio) + ctx.gamma = float(gamma) + + assert rois.size(1) == 5, 'RoI must be (idx, x1, y1, x2, y2)!' + + output_shape = (rois.size(0), input.size(1), ctx.output_size[0], + ctx.output_size[1]) + output = input.new_zeros(output_shape) + + ext_module.deform_roi_pool_forward( + input, + rois, + offset, + output, + pooled_height=ctx.output_size[0], + pooled_width=ctx.output_size[1], + spatial_scale=ctx.spatial_scale, + sampling_ratio=ctx.sampling_ratio, + gamma=ctx.gamma) + + ctx.save_for_backward(input, rois, offset) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + input, rois, offset = ctx.saved_tensors + grad_input = grad_output.new_zeros(input.shape) + grad_offset = grad_output.new_zeros(offset.shape) + + ext_module.deform_roi_pool_backward( + grad_output, + input, + rois, + offset, + grad_input, + grad_offset, + pooled_height=ctx.output_size[0], + pooled_width=ctx.output_size[1], + spatial_scale=ctx.spatial_scale, + sampling_ratio=ctx.sampling_ratio, + gamma=ctx.gamma) + if grad_offset.numel() == 0: + grad_offset = None + return grad_input, None, grad_offset, None, None, None, None + + +deform_roi_pool = DeformRoIPoolFunction.apply + + +class DeformRoIPool(nn.Module): + + def __init__(self, + output_size, + spatial_scale=1.0, + sampling_ratio=0, + gamma=0.1): + super(DeformRoIPool, self).__init__() + self.output_size = _pair(output_size) + self.spatial_scale = float(spatial_scale) + self.sampling_ratio = int(sampling_ratio) + self.gamma = float(gamma) + + def forward(self, input, rois, offset=None): + return deform_roi_pool(input, rois, offset, self.output_size, + self.spatial_scale, self.sampling_ratio, + self.gamma) + + +class DeformRoIPoolPack(DeformRoIPool): + + def __init__(self, + output_size, + output_channels, + deform_fc_channels=1024, + spatial_scale=1.0, + sampling_ratio=0, + gamma=0.1): + super(DeformRoIPoolPack, self).__init__(output_size, spatial_scale, + sampling_ratio, gamma) + + self.output_channels = output_channels + self.deform_fc_channels = deform_fc_channels + + self.offset_fc = nn.Sequential( + nn.Linear( + self.output_size[0] * self.output_size[1] * + self.output_channels, self.deform_fc_channels), + nn.ReLU(inplace=True), + nn.Linear(self.deform_fc_channels, self.deform_fc_channels), + nn.ReLU(inplace=True), + nn.Linear(self.deform_fc_channels, + self.output_size[0] * self.output_size[1] * 2)) + self.offset_fc[-1].weight.data.zero_() + self.offset_fc[-1].bias.data.zero_() + + def forward(self, input, rois): + assert input.size(1) == self.output_channels + x = deform_roi_pool(input, rois, None, self.output_size, + self.spatial_scale, self.sampling_ratio, + self.gamma) + rois_num = rois.size(0) + offset = self.offset_fc(x.view(rois_num, -1)) + offset = offset.view(rois_num, 2, self.output_size[0], + self.output_size[1]) + return deform_roi_pool(input, rois, offset, self.output_size, + self.spatial_scale, self.sampling_ratio, + self.gamma) + + +class ModulatedDeformRoIPoolPack(DeformRoIPool): + + def __init__(self, + output_size, + output_channels, + deform_fc_channels=1024, + spatial_scale=1.0, + sampling_ratio=0, + gamma=0.1): + super(ModulatedDeformRoIPoolPack, + self).__init__(output_size, spatial_scale, sampling_ratio, gamma) + + self.output_channels = output_channels + self.deform_fc_channels = deform_fc_channels + + self.offset_fc = nn.Sequential( + nn.Linear( + self.output_size[0] * self.output_size[1] * + self.output_channels, self.deform_fc_channels), + nn.ReLU(inplace=True), + nn.Linear(self.deform_fc_channels, self.deform_fc_channels), + nn.ReLU(inplace=True), + nn.Linear(self.deform_fc_channels, + self.output_size[0] * self.output_size[1] * 2)) + self.offset_fc[-1].weight.data.zero_() + self.offset_fc[-1].bias.data.zero_() + + self.mask_fc = nn.Sequential( + nn.Linear( + self.output_size[0] * self.output_size[1] * + self.output_channels, self.deform_fc_channels), + nn.ReLU(inplace=True), + nn.Linear(self.deform_fc_channels, + self.output_size[0] * self.output_size[1] * 1), + nn.Sigmoid()) + self.mask_fc[2].weight.data.zero_() + self.mask_fc[2].bias.data.zero_() + + def forward(self, input, rois): + assert input.size(1) == self.output_channels + x = deform_roi_pool(input, rois, None, self.output_size, + self.spatial_scale, self.sampling_ratio, + self.gamma) + rois_num = rois.size(0) + offset = self.offset_fc(x.view(rois_num, -1)) + offset = offset.view(rois_num, 2, self.output_size[0], + self.output_size[1]) + mask = self.mask_fc(x.view(rois_num, -1)) + mask = mask.view(rois_num, 1, self.output_size[0], self.output_size[1]) + d = deform_roi_pool(input, rois, offset, self.output_size, + self.spatial_scale, self.sampling_ratio, + self.gamma) + return d * mask diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/deprecated_wrappers.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/deprecated_wrappers.py new file mode 100644 index 0000000000000000000000000000000000000000..a2e593df9ee57637038683d7a1efaa347b2b69e7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/deprecated_wrappers.py @@ -0,0 +1,43 @@ +# Copyright (c) OpenMMLab. All rights reserved. +# This file is for backward compatibility. +# Module wrappers for empty tensor have been moved to mmcv.cnn.bricks. +import warnings + +from ..cnn.bricks.wrappers import Conv2d, ConvTranspose2d, Linear, MaxPool2d + + +class Conv2d_deprecated(Conv2d): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + warnings.warn( + 'Importing Conv2d wrapper from "mmcv.ops" will be deprecated in' + ' the future. Please import them from "mmcv.cnn" instead') + + +class ConvTranspose2d_deprecated(ConvTranspose2d): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + warnings.warn( + 'Importing ConvTranspose2d wrapper from "mmcv.ops" will be ' + 'deprecated in the future. Please import them from "mmcv.cnn" ' + 'instead') + + +class MaxPool2d_deprecated(MaxPool2d): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + warnings.warn( + 'Importing MaxPool2d wrapper from "mmcv.ops" will be deprecated in' + ' the future. Please import them from "mmcv.cnn" instead') + + +class Linear_deprecated(Linear): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + warnings.warn( + 'Importing Linear wrapper from "mmcv.ops" will be deprecated in' + ' the future. Please import them from "mmcv.cnn" instead') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/focal_loss.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/focal_loss.py new file mode 100644 index 0000000000000000000000000000000000000000..763bc93bd2575c49ca8ccf20996bbd92d1e0d1a4 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/focal_loss.py @@ -0,0 +1,212 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.nn as nn +from torch.autograd import Function +from torch.autograd.function import once_differentiable + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', [ + 'sigmoid_focal_loss_forward', 'sigmoid_focal_loss_backward', + 'softmax_focal_loss_forward', 'softmax_focal_loss_backward' +]) + + +class SigmoidFocalLossFunction(Function): + + @staticmethod + def symbolic(g, input, target, gamma, alpha, weight, reduction): + return g.op( + 'mmcv::MMCVSigmoidFocalLoss', + input, + target, + gamma_f=gamma, + alpha_f=alpha, + weight_f=weight, + reduction_s=reduction) + + @staticmethod + def forward(ctx, + input, + target, + gamma=2.0, + alpha=0.25, + weight=None, + reduction='mean'): + + assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) + assert input.dim() == 2 + assert target.dim() == 1 + assert input.size(0) == target.size(0) + if weight is None: + weight = input.new_empty(0) + else: + assert weight.dim() == 1 + assert input.size(1) == weight.size(0) + ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} + assert reduction in ctx.reduction_dict.keys() + + ctx.gamma = float(gamma) + ctx.alpha = float(alpha) + ctx.reduction = ctx.reduction_dict[reduction] + + output = input.new_zeros(input.size()) + + ext_module.sigmoid_focal_loss_forward( + input, target, weight, output, gamma=ctx.gamma, alpha=ctx.alpha) + if ctx.reduction == ctx.reduction_dict['mean']: + output = output.sum() / input.size(0) + elif ctx.reduction == ctx.reduction_dict['sum']: + output = output.sum() + ctx.save_for_backward(input, target, weight) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + input, target, weight = ctx.saved_tensors + + grad_input = input.new_zeros(input.size()) + + ext_module.sigmoid_focal_loss_backward( + input, + target, + weight, + grad_input, + gamma=ctx.gamma, + alpha=ctx.alpha) + + grad_input *= grad_output + if ctx.reduction == ctx.reduction_dict['mean']: + grad_input /= input.size(0) + return grad_input, None, None, None, None, None + + +sigmoid_focal_loss = SigmoidFocalLossFunction.apply + + +class SigmoidFocalLoss(nn.Module): + + def __init__(self, gamma, alpha, weight=None, reduction='mean'): + super(SigmoidFocalLoss, self).__init__() + self.gamma = gamma + self.alpha = alpha + self.register_buffer('weight', weight) + self.reduction = reduction + + def forward(self, input, target): + return sigmoid_focal_loss(input, target, self.gamma, self.alpha, + self.weight, self.reduction) + + def __repr__(self): + s = self.__class__.__name__ + s += f'(gamma={self.gamma}, ' + s += f'alpha={self.alpha}, ' + s += f'reduction={self.reduction})' + return s + + +class SoftmaxFocalLossFunction(Function): + + @staticmethod + def symbolic(g, input, target, gamma, alpha, weight, reduction): + return g.op( + 'mmcv::MMCVSoftmaxFocalLoss', + input, + target, + gamma_f=gamma, + alpha_f=alpha, + weight_f=weight, + reduction_s=reduction) + + @staticmethod + def forward(ctx, + input, + target, + gamma=2.0, + alpha=0.25, + weight=None, + reduction='mean'): + + assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) + assert input.dim() == 2 + assert target.dim() == 1 + assert input.size(0) == target.size(0) + if weight is None: + weight = input.new_empty(0) + else: + assert weight.dim() == 1 + assert input.size(1) == weight.size(0) + ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} + assert reduction in ctx.reduction_dict.keys() + + ctx.gamma = float(gamma) + ctx.alpha = float(alpha) + ctx.reduction = ctx.reduction_dict[reduction] + + channel_stats, _ = torch.max(input, dim=1) + input_softmax = input - channel_stats.unsqueeze(1).expand_as(input) + input_softmax.exp_() + + channel_stats = input_softmax.sum(dim=1) + input_softmax /= channel_stats.unsqueeze(1).expand_as(input) + + output = input.new_zeros(input.size(0)) + ext_module.softmax_focal_loss_forward( + input_softmax, + target, + weight, + output, + gamma=ctx.gamma, + alpha=ctx.alpha) + + if ctx.reduction == ctx.reduction_dict['mean']: + output = output.sum() / input.size(0) + elif ctx.reduction == ctx.reduction_dict['sum']: + output = output.sum() + ctx.save_for_backward(input_softmax, target, weight) + return output + + @staticmethod + def backward(ctx, grad_output): + input_softmax, target, weight = ctx.saved_tensors + buff = input_softmax.new_zeros(input_softmax.size(0)) + grad_input = input_softmax.new_zeros(input_softmax.size()) + + ext_module.softmax_focal_loss_backward( + input_softmax, + target, + weight, + buff, + grad_input, + gamma=ctx.gamma, + alpha=ctx.alpha) + + grad_input *= grad_output + if ctx.reduction == ctx.reduction_dict['mean']: + grad_input /= input_softmax.size(0) + return grad_input, None, None, None, None, None + + +softmax_focal_loss = SoftmaxFocalLossFunction.apply + + +class SoftmaxFocalLoss(nn.Module): + + def __init__(self, gamma, alpha, weight=None, reduction='mean'): + super(SoftmaxFocalLoss, self).__init__() + self.gamma = gamma + self.alpha = alpha + self.register_buffer('weight', weight) + self.reduction = reduction + + def forward(self, input, target): + return softmax_focal_loss(input, target, self.gamma, self.alpha, + self.weight, self.reduction) + + def __repr__(self): + s = self.__class__.__name__ + s += f'(gamma={self.gamma}, ' + s += f'alpha={self.alpha}, ' + s += f'reduction={self.reduction})' + return s diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/furthest_point_sample.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/furthest_point_sample.py new file mode 100644 index 0000000000000000000000000000000000000000..374b7a878f1972c183941af28ba1df216ac1a60f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/furthest_point_sample.py @@ -0,0 +1,83 @@ +import torch +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', [ + 'furthest_point_sampling_forward', + 'furthest_point_sampling_with_dist_forward' +]) + + +class FurthestPointSampling(Function): + """Uses iterative furthest point sampling to select a set of features whose + corresponding points have the furthest distance.""" + + @staticmethod + def forward(ctx, points_xyz: torch.Tensor, + num_points: int) -> torch.Tensor: + """ + Args: + points_xyz (Tensor): (B, N, 3) where N > num_points. + num_points (int): Number of points in the sampled set. + + Returns: + Tensor: (B, num_points) indices of the sampled points. + """ + assert points_xyz.is_contiguous() + + B, N = points_xyz.size()[:2] + output = torch.cuda.IntTensor(B, num_points) + temp = torch.cuda.FloatTensor(B, N).fill_(1e10) + + ext_module.furthest_point_sampling_forward( + points_xyz, + temp, + output, + b=B, + n=N, + m=num_points, + ) + if torch.__version__ != 'parrots': + ctx.mark_non_differentiable(output) + return output + + @staticmethod + def backward(xyz, a=None): + return None, None + + +class FurthestPointSamplingWithDist(Function): + """Uses iterative furthest point sampling to select a set of features whose + corresponding points have the furthest distance.""" + + @staticmethod + def forward(ctx, points_dist: torch.Tensor, + num_points: int) -> torch.Tensor: + """ + Args: + points_dist (Tensor): (B, N, N) Distance between each point pair. + num_points (int): Number of points in the sampled set. + + Returns: + Tensor: (B, num_points) indices of the sampled points. + """ + assert points_dist.is_contiguous() + + B, N, _ = points_dist.size() + output = points_dist.new_zeros([B, num_points], dtype=torch.int32) + temp = points_dist.new_zeros([B, N]).fill_(1e10) + + ext_module.furthest_point_sampling_with_dist_forward( + points_dist, temp, output, b=B, n=N, m=num_points) + if torch.__version__ != 'parrots': + ctx.mark_non_differentiable(output) + return output + + @staticmethod + def backward(xyz, a=None): + return None, None + + +furthest_point_sample = FurthestPointSampling.apply +furthest_point_sample_with_dist = FurthestPointSamplingWithDist.apply diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/fused_bias_leakyrelu.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/fused_bias_leakyrelu.py new file mode 100644 index 0000000000000000000000000000000000000000..6d12508469c6c8fa1884debece44c58d158cb6fa --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/fused_bias_leakyrelu.py @@ -0,0 +1,268 @@ +# modified from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/fused_act.py # noqa:E501 + +# Copyright (c) 2021, NVIDIA Corporation. All rights reserved. +# NVIDIA Source Code License for StyleGAN2 with Adaptive Discriminator +# Augmentation (ADA) +# ======================================================================= + +# 1. Definitions + +# "Licensor" means any person or entity that distributes its Work. + +# "Software" means the original work of authorship made available under +# this License. + +# "Work" means the Software and any additions to or derivative works of +# the Software that are made available under this License. + +# The terms "reproduce," "reproduction," "derivative works," and +# "distribution" have the meaning as provided under U.S. copyright law; +# provided, however, that for the purposes of this License, derivative +# works shall not include works that remain separable from, or merely +# link (or bind by name) to the interfaces of, the Work. + +# Works, including the Software, are "made available" under this License +# by including in or with the Work either (a) a copyright notice +# referencing the applicability of this License to the Work, or (b) a +# copy of this License. + +# 2. License Grants + +# 2.1 Copyright Grant. Subject to the terms and conditions of this +# License, each Licensor grants to you a perpetual, worldwide, +# non-exclusive, royalty-free, copyright license to reproduce, +# prepare derivative works of, publicly display, publicly perform, +# sublicense and distribute its Work and any resulting derivative +# works in any form. + +# 3. Limitations + +# 3.1 Redistribution. You may reproduce or distribute the Work only +# if (a) you do so under this License, (b) you include a complete +# copy of this License with your distribution, and (c) you retain +# without modification any copyright, patent, trademark, or +# attribution notices that are present in the Work. + +# 3.2 Derivative Works. You may specify that additional or different +# terms apply to the use, reproduction, and distribution of your +# derivative works of the Work ("Your Terms") only if (a) Your Terms +# provide that the use limitation in Section 3.3 applies to your +# derivative works, and (b) you identify the specific derivative +# works that are subject to Your Terms. Notwithstanding Your Terms, +# this License (including the redistribution requirements in Section +# 3.1) will continue to apply to the Work itself. + +# 3.3 Use Limitation. The Work and any derivative works thereof only +# may be used or intended for use non-commercially. Notwithstanding +# the foregoing, NVIDIA and its affiliates may use the Work and any +# derivative works commercially. As used herein, "non-commercially" +# means for research or evaluation purposes only. + +# 3.4 Patent Claims. If you bring or threaten to bring a patent claim +# against any Licensor (including any claim, cross-claim or +# counterclaim in a lawsuit) to enforce any patents that you allege +# are infringed by any Work, then your rights under this License from +# such Licensor (including the grant in Section 2.1) will terminate +# immediately. + +# 3.5 Trademarks. This License does not grant any rights to use any +# Licensor’s or its affiliates’ names, logos, or trademarks, except +# as necessary to reproduce the notices described in this License. + +# 3.6 Termination. If you violate any term of this License, then your +# rights under this License (including the grant in Section 2.1) will +# terminate immediately. + +# 4. Disclaimer of Warranty. + +# THE WORK IS PROVIDED "AS IS" WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WARRANTIES OR CONDITIONS OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR +# NON-INFRINGEMENT. YOU BEAR THE RISK OF UNDERTAKING ANY ACTIVITIES UNDER +# THIS LICENSE. + +# 5. Limitation of Liability. + +# EXCEPT AS PROHIBITED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL +# THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE +# SHALL ANY LICENSOR BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, +# INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF +# OR RELATED TO THIS LICENSE, THE USE OR INABILITY TO USE THE WORK +# (INCLUDING BUT NOT LIMITED TO LOSS OF GOODWILL, BUSINESS INTERRUPTION, +# LOST PROFITS OR DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY OTHER +# COMMERCIAL DAMAGES OR LOSSES), EVEN IF THE LICENSOR HAS BEEN ADVISED OF +# THE POSSIBILITY OF SUCH DAMAGES. + +# ======================================================================= + +import torch +import torch.nn.functional as F +from torch import nn +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', ['fused_bias_leakyrelu']) + + +class FusedBiasLeakyReLUFunctionBackward(Function): + """Calculate second order deviation. + + This function is to compute the second order deviation for the fused leaky + relu operation. + """ + + @staticmethod + def forward(ctx, grad_output, out, negative_slope, scale): + ctx.save_for_backward(out) + ctx.negative_slope = negative_slope + ctx.scale = scale + + empty = grad_output.new_empty(0) + + grad_input = ext_module.fused_bias_leakyrelu( + grad_output, + empty, + out, + act=3, + grad=1, + alpha=negative_slope, + scale=scale) + + dim = [0] + + if grad_input.ndim > 2: + dim += list(range(2, grad_input.ndim)) + + grad_bias = grad_input.sum(dim).detach() + + return grad_input, grad_bias + + @staticmethod + def backward(ctx, gradgrad_input, gradgrad_bias): + out, = ctx.saved_tensors + + # The second order deviation, in fact, contains two parts, while the + # the first part is zero. Thus, we direct consider the second part + # which is similar with the first order deviation in implementation. + gradgrad_out = ext_module.fused_bias_leakyrelu( + gradgrad_input, + gradgrad_bias.to(out.dtype), + out, + act=3, + grad=1, + alpha=ctx.negative_slope, + scale=ctx.scale) + + return gradgrad_out, None, None, None + + +class FusedBiasLeakyReLUFunction(Function): + + @staticmethod + def forward(ctx, input, bias, negative_slope, scale): + empty = input.new_empty(0) + + out = ext_module.fused_bias_leakyrelu( + input, + bias, + empty, + act=3, + grad=0, + alpha=negative_slope, + scale=scale) + ctx.save_for_backward(out) + ctx.negative_slope = negative_slope + ctx.scale = scale + + return out + + @staticmethod + def backward(ctx, grad_output): + out, = ctx.saved_tensors + + grad_input, grad_bias = FusedBiasLeakyReLUFunctionBackward.apply( + grad_output, out, ctx.negative_slope, ctx.scale) + + return grad_input, grad_bias, None, None + + +class FusedBiasLeakyReLU(nn.Module): + """Fused bias leaky ReLU. + + This function is introduced in the StyleGAN2: + http://arxiv.org/abs/1912.04958 + + The bias term comes from the convolution operation. In addition, to keep + the variance of the feature map or gradients unchanged, they also adopt a + scale similarly with Kaiming initialization. However, since the + :math:`1+{alpha}^2` : is too small, we can just ignore it. Therefore, the + final scale is just :math:`\sqrt{2}`:. Of course, you may change it with # noqa: W605, E501 + your own scale. + + TODO: Implement the CPU version. + + Args: + channel (int): The channel number of the feature map. + negative_slope (float, optional): Same as nn.LeakyRelu. + Defaults to 0.2. + scale (float, optional): A scalar to adjust the variance of the feature + map. Defaults to 2**0.5. + """ + + def __init__(self, num_channels, negative_slope=0.2, scale=2**0.5): + super(FusedBiasLeakyReLU, self).__init__() + + self.bias = nn.Parameter(torch.zeros(num_channels)) + self.negative_slope = negative_slope + self.scale = scale + + def forward(self, input): + return fused_bias_leakyrelu(input, self.bias, self.negative_slope, + self.scale) + + +def fused_bias_leakyrelu(input, bias, negative_slope=0.2, scale=2**0.5): + """Fused bias leaky ReLU function. + + This function is introduced in the StyleGAN2: + http://arxiv.org/abs/1912.04958 + + The bias term comes from the convolution operation. In addition, to keep + the variance of the feature map or gradients unchanged, they also adopt a + scale similarly with Kaiming initialization. However, since the + :math:`1+{alpha}^2` : is too small, we can just ignore it. Therefore, the + final scale is just :math:`\sqrt{2}`:. Of course, you may change it with # noqa: W605, E501 + your own scale. + + Args: + input (torch.Tensor): Input feature map. + bias (nn.Parameter): The bias from convolution operation. + negative_slope (float, optional): Same as nn.LeakyRelu. + Defaults to 0.2. + scale (float, optional): A scalar to adjust the variance of the feature + map. Defaults to 2**0.5. + + Returns: + torch.Tensor: Feature map after non-linear activation. + """ + + if not input.is_cuda: + return bias_leakyrelu_ref(input, bias, negative_slope, scale) + + return FusedBiasLeakyReLUFunction.apply(input, bias.to(input.dtype), + negative_slope, scale) + + +def bias_leakyrelu_ref(x, bias, negative_slope=0.2, scale=2**0.5): + + if bias is not None: + assert bias.ndim == 1 + assert bias.shape[0] == x.shape[1] + x = x + bias.reshape([-1 if i == 1 else 1 for i in range(x.ndim)]) + + x = F.leaky_relu(x, negative_slope) + if scale != 1: + x = x * scale + + return x diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/gather_points.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/gather_points.py new file mode 100644 index 0000000000000000000000000000000000000000..f52f1677d8ea0facafc56a3672d37adb44677ff3 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/gather_points.py @@ -0,0 +1,57 @@ +import torch +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', ['gather_points_forward', 'gather_points_backward']) + + +class GatherPoints(Function): + """Gather points with given index.""" + + @staticmethod + def forward(ctx, features: torch.Tensor, + indices: torch.Tensor) -> torch.Tensor: + """ + Args: + features (Tensor): (B, C, N) features to gather. + indices (Tensor): (B, M) where M is the number of points. + + Returns: + Tensor: (B, C, M) where M is the number of points. + """ + assert features.is_contiguous() + assert indices.is_contiguous() + + B, npoint = indices.size() + _, C, N = features.size() + output = torch.cuda.FloatTensor(B, C, npoint) + + ext_module.gather_points_forward( + features, indices, output, b=B, c=C, n=N, npoints=npoint) + + ctx.for_backwards = (indices, C, N) + if torch.__version__ != 'parrots': + ctx.mark_non_differentiable(indices) + return output + + @staticmethod + def backward(ctx, grad_out): + idx, C, N = ctx.for_backwards + B, npoint = idx.size() + + grad_features = torch.cuda.FloatTensor(B, C, N).zero_() + grad_out_data = grad_out.data.contiguous() + ext_module.gather_points_backward( + grad_out_data, + idx, + grad_features.data, + b=B, + c=C, + n=N, + npoints=npoint) + return grad_features, None + + +gather_points = GatherPoints.apply diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/group_points.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/group_points.py new file mode 100644 index 0000000000000000000000000000000000000000..6c3ec9d758ebe4e1c2205882af4be154008253a5 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/group_points.py @@ -0,0 +1,224 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from typing import Tuple + +import torch +from torch import nn as nn +from torch.autograd import Function + +from ..utils import ext_loader +from .ball_query import ball_query +from .knn import knn + +ext_module = ext_loader.load_ext( + '_ext', ['group_points_forward', 'group_points_backward']) + + +class QueryAndGroup(nn.Module): + """Groups points with a ball query of radius. + + Args: + max_radius (float): The maximum radius of the balls. + If None is given, we will use kNN sampling instead of ball query. + sample_num (int): Maximum number of features to gather in the ball. + min_radius (float, optional): The minimum radius of the balls. + Default: 0. + use_xyz (bool, optional): Whether to use xyz. + Default: True. + return_grouped_xyz (bool, optional): Whether to return grouped xyz. + Default: False. + normalize_xyz (bool, optional): Whether to normalize xyz. + Default: False. + uniform_sample (bool, optional): Whether to sample uniformly. + Default: False + return_unique_cnt (bool, optional): Whether to return the count of + unique samples. Default: False. + return_grouped_idx (bool, optional): Whether to return grouped idx. + Default: False. + """ + + def __init__(self, + max_radius, + sample_num, + min_radius=0, + use_xyz=True, + return_grouped_xyz=False, + normalize_xyz=False, + uniform_sample=False, + return_unique_cnt=False, + return_grouped_idx=False): + super().__init__() + self.max_radius = max_radius + self.min_radius = min_radius + self.sample_num = sample_num + self.use_xyz = use_xyz + self.return_grouped_xyz = return_grouped_xyz + self.normalize_xyz = normalize_xyz + self.uniform_sample = uniform_sample + self.return_unique_cnt = return_unique_cnt + self.return_grouped_idx = return_grouped_idx + if self.return_unique_cnt: + assert self.uniform_sample, \ + 'uniform_sample should be True when ' \ + 'returning the count of unique samples' + if self.max_radius is None: + assert not self.normalize_xyz, \ + 'can not normalize grouped xyz when max_radius is None' + + def forward(self, points_xyz, center_xyz, features=None): + """ + Args: + points_xyz (Tensor): (B, N, 3) xyz coordinates of the features. + center_xyz (Tensor): (B, npoint, 3) coordinates of the centriods. + features (Tensor): (B, C, N) Descriptors of the features. + + Returns: + Tensor: (B, 3 + C, npoint, sample_num) Grouped feature. + """ + # if self.max_radius is None, we will perform kNN instead of ball query + # idx is of shape [B, npoint, sample_num] + if self.max_radius is None: + idx = knn(self.sample_num, points_xyz, center_xyz, False) + idx = idx.transpose(1, 2).contiguous() + else: + idx = ball_query(self.min_radius, self.max_radius, self.sample_num, + points_xyz, center_xyz) + + if self.uniform_sample: + unique_cnt = torch.zeros((idx.shape[0], idx.shape[1])) + for i_batch in range(idx.shape[0]): + for i_region in range(idx.shape[1]): + unique_ind = torch.unique(idx[i_batch, i_region, :]) + num_unique = unique_ind.shape[0] + unique_cnt[i_batch, i_region] = num_unique + sample_ind = torch.randint( + 0, + num_unique, (self.sample_num - num_unique, ), + dtype=torch.long) + all_ind = torch.cat((unique_ind, unique_ind[sample_ind])) + idx[i_batch, i_region, :] = all_ind + + xyz_trans = points_xyz.transpose(1, 2).contiguous() + # (B, 3, npoint, sample_num) + grouped_xyz = grouping_operation(xyz_trans, idx) + grouped_xyz_diff = grouped_xyz - \ + center_xyz.transpose(1, 2).unsqueeze(-1) # relative offsets + if self.normalize_xyz: + grouped_xyz_diff /= self.max_radius + + if features is not None: + grouped_features = grouping_operation(features, idx) + if self.use_xyz: + # (B, C + 3, npoint, sample_num) + new_features = torch.cat([grouped_xyz_diff, grouped_features], + dim=1) + else: + new_features = grouped_features + else: + assert (self.use_xyz + ), 'Cannot have not features and not use xyz as a feature!' + new_features = grouped_xyz_diff + + ret = [new_features] + if self.return_grouped_xyz: + ret.append(grouped_xyz) + if self.return_unique_cnt: + ret.append(unique_cnt) + if self.return_grouped_idx: + ret.append(idx) + if len(ret) == 1: + return ret[0] + else: + return tuple(ret) + + +class GroupAll(nn.Module): + """Group xyz with feature. + + Args: + use_xyz (bool): Whether to use xyz. + """ + + def __init__(self, use_xyz: bool = True): + super().__init__() + self.use_xyz = use_xyz + + def forward(self, + xyz: torch.Tensor, + new_xyz: torch.Tensor, + features: torch.Tensor = None): + """ + Args: + xyz (Tensor): (B, N, 3) xyz coordinates of the features. + new_xyz (Tensor): new xyz coordinates of the features. + features (Tensor): (B, C, N) features to group. + + Returns: + Tensor: (B, C + 3, 1, N) Grouped feature. + """ + grouped_xyz = xyz.transpose(1, 2).unsqueeze(2) + if features is not None: + grouped_features = features.unsqueeze(2) + if self.use_xyz: + # (B, 3 + C, 1, N) + new_features = torch.cat([grouped_xyz, grouped_features], + dim=1) + else: + new_features = grouped_features + else: + new_features = grouped_xyz + + return new_features + + +class GroupingOperation(Function): + """Group feature with given index.""" + + @staticmethod + def forward(ctx, features: torch.Tensor, + indices: torch.Tensor) -> torch.Tensor: + """ + Args: + features (Tensor): (B, C, N) tensor of features to group. + indices (Tensor): (B, npoint, nsample) the indices of + features to group with. + + Returns: + Tensor: (B, C, npoint, nsample) Grouped features. + """ + features = features.contiguous() + indices = indices.contiguous() + + B, nfeatures, nsample = indices.size() + _, C, N = features.size() + output = torch.cuda.FloatTensor(B, C, nfeatures, nsample) + + ext_module.group_points_forward(B, C, N, nfeatures, nsample, features, + indices, output) + + ctx.for_backwards = (indices, N) + return output + + @staticmethod + def backward(ctx, + grad_out: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Args: + grad_out (Tensor): (B, C, npoint, nsample) tensor of the gradients + of the output from forward. + + Returns: + Tensor: (B, C, N) gradient of the features. + """ + idx, N = ctx.for_backwards + + B, C, npoint, nsample = grad_out.size() + grad_features = torch.cuda.FloatTensor(B, C, N).zero_() + + grad_out_data = grad_out.data.contiguous() + ext_module.group_points_backward(B, C, N, npoint, nsample, + grad_out_data, idx, + grad_features.data) + return grad_features, None + + +grouping_operation = GroupingOperation.apply diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/info.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/info.py new file mode 100644 index 0000000000000000000000000000000000000000..29f2e5598ae2bb5866ccd15a7d3b4de33c0cd14d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/info.py @@ -0,0 +1,36 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import glob +import os + +import torch + +if torch.__version__ == 'parrots': + import parrots + + def get_compiler_version(): + return 'GCC ' + parrots.version.compiler + + def get_compiling_cuda_version(): + return parrots.version.cuda +else: + from ..utils import ext_loader + ext_module = ext_loader.load_ext( + '_ext', ['get_compiler_version', 'get_compiling_cuda_version']) + + def get_compiler_version(): + return ext_module.get_compiler_version() + + def get_compiling_cuda_version(): + return ext_module.get_compiling_cuda_version() + + +def get_onnxruntime_op_path(): + wildcard = os.path.join( + os.path.abspath(os.path.dirname(os.path.dirname(__file__))), + '_ext_ort.*.so') + + paths = glob.glob(wildcard) + if len(paths) > 0: + return paths[0] + else: + return '' diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/iou3d.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/iou3d.py new file mode 100644 index 0000000000000000000000000000000000000000..6fc71979190323f44c09f8b7e1761cf49cd2d76b --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/iou3d.py @@ -0,0 +1,85 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', [ + 'iou3d_boxes_iou_bev_forward', 'iou3d_nms_forward', + 'iou3d_nms_normal_forward' +]) + + +def boxes_iou_bev(boxes_a, boxes_b): + """Calculate boxes IoU in the Bird's Eye View. + + Args: + boxes_a (torch.Tensor): Input boxes a with shape (M, 5). + boxes_b (torch.Tensor): Input boxes b with shape (N, 5). + + Returns: + ans_iou (torch.Tensor): IoU result with shape (M, N). + """ + ans_iou = boxes_a.new_zeros( + torch.Size((boxes_a.shape[0], boxes_b.shape[0]))) + + ext_module.iou3d_boxes_iou_bev_forward(boxes_a.contiguous(), + boxes_b.contiguous(), ans_iou) + + return ans_iou + + +def nms_bev(boxes, scores, thresh, pre_max_size=None, post_max_size=None): + """NMS function GPU implementation (for BEV boxes). The overlap of two + boxes for IoU calculation is defined as the exact overlapping area of the + two boxes. In this function, one can also set ``pre_max_size`` and + ``post_max_size``. + + Args: + boxes (torch.Tensor): Input boxes with the shape of [N, 5] + ([x1, y1, x2, y2, ry]). + scores (torch.Tensor): Scores of boxes with the shape of [N]. + thresh (float): Overlap threshold of NMS. + pre_max_size (int, optional): Max size of boxes before NMS. + Default: None. + post_max_size (int, optional): Max size of boxes after NMS. + Default: None. + + Returns: + torch.Tensor: Indexes after NMS. + """ + assert boxes.size(1) == 5, 'Input boxes shape should be [N, 5]' + order = scores.sort(0, descending=True)[1] + + if pre_max_size is not None: + order = order[:pre_max_size] + boxes = boxes[order].contiguous() + + keep = torch.zeros(boxes.size(0), dtype=torch.long) + num_out = ext_module.iou3d_nms_forward(boxes, keep, thresh) + keep = order[keep[:num_out].cuda(boxes.device)].contiguous() + if post_max_size is not None: + keep = keep[:post_max_size] + return keep + + +def nms_normal_bev(boxes, scores, thresh): + """Normal NMS function GPU implementation (for BEV boxes). The overlap of + two boxes for IoU calculation is defined as the exact overlapping area of + the two boxes WITH their yaw angle set to 0. + + Args: + boxes (torch.Tensor): Input boxes with shape (N, 5). + scores (torch.Tensor): Scores of predicted boxes with shape (N). + thresh (float): Overlap threshold of NMS. + + Returns: + torch.Tensor: Remaining indices with scores in descending order. + """ + assert boxes.shape[1] == 5, 'Input boxes shape should be [N, 5]' + order = scores.sort(0, descending=True)[1] + + boxes = boxes[order].contiguous() + + keep = torch.zeros(boxes.size(0), dtype=torch.long) + num_out = ext_module.iou3d_nms_normal_forward(boxes, keep, thresh) + return order[keep[:num_out].cuda(boxes.device)].contiguous() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/knn.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/knn.py new file mode 100644 index 0000000000000000000000000000000000000000..f335785036669fc19239825b0aae6dde3f73bf92 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/knn.py @@ -0,0 +1,77 @@ +import torch +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', ['knn_forward']) + + +class KNN(Function): + r"""KNN (CUDA) based on heap data structure. + Modified from `PAConv `_. + + Find k-nearest points. + """ + + @staticmethod + def forward(ctx, + k: int, + xyz: torch.Tensor, + center_xyz: torch.Tensor = None, + transposed: bool = False) -> torch.Tensor: + """ + Args: + k (int): number of nearest neighbors. + xyz (Tensor): (B, N, 3) if transposed == False, else (B, 3, N). + xyz coordinates of the features. + center_xyz (Tensor, optional): (B, npoint, 3) if transposed == + False, else (B, 3, npoint). centers of the knn query. + Default: None. + transposed (bool, optional): whether the input tensors are + transposed. Should not explicitly use this keyword when + calling knn (=KNN.apply), just add the fourth param. + Default: False. + + Returns: + Tensor: (B, k, npoint) tensor with the indices of + the features that form k-nearest neighbours. + """ + assert (k > 0) & (k < 100), 'k should be in range(0, 100)' + + if center_xyz is None: + center_xyz = xyz + + if transposed: + xyz = xyz.transpose(2, 1).contiguous() + center_xyz = center_xyz.transpose(2, 1).contiguous() + + assert xyz.is_contiguous() # [B, N, 3] + assert center_xyz.is_contiguous() # [B, npoint, 3] + + center_xyz_device = center_xyz.get_device() + assert center_xyz_device == xyz.get_device(), \ + 'center_xyz and xyz should be put on the same device' + if torch.cuda.current_device() != center_xyz_device: + torch.cuda.set_device(center_xyz_device) + + B, npoint, _ = center_xyz.shape + N = xyz.shape[1] + + idx = center_xyz.new_zeros((B, npoint, k)).int() + dist2 = center_xyz.new_zeros((B, npoint, k)).float() + + ext_module.knn_forward( + xyz, center_xyz, idx, dist2, b=B, n=N, m=npoint, nsample=k) + # idx shape to [B, k, npoint] + idx = idx.transpose(2, 1).contiguous() + if torch.__version__ != 'parrots': + ctx.mark_non_differentiable(idx) + return idx + + @staticmethod + def backward(ctx, a=None): + return None, None, None + + +knn = KNN.apply diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/masked_conv.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/masked_conv.py new file mode 100644 index 0000000000000000000000000000000000000000..cd514cc204c1d571ea5dc7e74b038c0f477a008b --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/masked_conv.py @@ -0,0 +1,111 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import math + +import torch +import torch.nn as nn +from torch.autograd import Function +from torch.autograd.function import once_differentiable +from torch.nn.modules.utils import _pair + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', ['masked_im2col_forward', 'masked_col2im_forward']) + + +class MaskedConv2dFunction(Function): + + @staticmethod + def symbolic(g, features, mask, weight, bias, padding, stride): + return g.op( + 'mmcv::MMCVMaskedConv2d', + features, + mask, + weight, + bias, + padding_i=padding, + stride_i=stride) + + @staticmethod + def forward(ctx, features, mask, weight, bias, padding=0, stride=1): + assert mask.dim() == 3 and mask.size(0) == 1 + assert features.dim() == 4 and features.size(0) == 1 + assert features.size()[2:] == mask.size()[1:] + pad_h, pad_w = _pair(padding) + stride_h, stride_w = _pair(stride) + if stride_h != 1 or stride_w != 1: + raise ValueError( + 'Stride could not only be 1 in masked_conv2d currently.') + out_channel, in_channel, kernel_h, kernel_w = weight.size() + + batch_size = features.size(0) + out_h = int( + math.floor((features.size(2) + 2 * pad_h - + (kernel_h - 1) - 1) / stride_h + 1)) + out_w = int( + math.floor((features.size(3) + 2 * pad_w - + (kernel_h - 1) - 1) / stride_w + 1)) + mask_inds = torch.nonzero(mask[0] > 0, as_tuple=False) + output = features.new_zeros(batch_size, out_channel, out_h, out_w) + if mask_inds.numel() > 0: + mask_h_idx = mask_inds[:, 0].contiguous() + mask_w_idx = mask_inds[:, 1].contiguous() + data_col = features.new_zeros(in_channel * kernel_h * kernel_w, + mask_inds.size(0)) + ext_module.masked_im2col_forward( + features, + mask_h_idx, + mask_w_idx, + data_col, + kernel_h=kernel_h, + kernel_w=kernel_w, + pad_h=pad_h, + pad_w=pad_w) + + masked_output = torch.addmm(1, bias[:, None], 1, + weight.view(out_channel, -1), data_col) + ext_module.masked_col2im_forward( + masked_output, + mask_h_idx, + mask_w_idx, + output, + height=out_h, + width=out_w, + channels=out_channel) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + return (None, ) * 5 + + +masked_conv2d = MaskedConv2dFunction.apply + + +class MaskedConv2d(nn.Conv2d): + """A MaskedConv2d which inherits the official Conv2d. + + The masked forward doesn't implement the backward function and only + supports the stride parameter to be 1 currently. + """ + + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True): + super(MaskedConv2d, + self).__init__(in_channels, out_channels, kernel_size, stride, + padding, dilation, groups, bias) + + def forward(self, input, mask=None): + if mask is None: # fallback to the normal Conv2d + return super(MaskedConv2d, self).forward(input) + else: + return masked_conv2d(input, mask, self.weight, self.bias, + self.padding) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/merge_cells.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/merge_cells.py new file mode 100644 index 0000000000000000000000000000000000000000..48ca8cc0a8aca8432835bd760c0403a3c35b34cf --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/merge_cells.py @@ -0,0 +1,149 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from abc import abstractmethod + +import torch +import torch.nn as nn +import torch.nn.functional as F + +from ..cnn import ConvModule + + +class BaseMergeCell(nn.Module): + """The basic class for cells used in NAS-FPN and NAS-FCOS. + + BaseMergeCell takes 2 inputs. After applying convolution + on them, they are resized to the target size. Then, + they go through binary_op, which depends on the type of cell. + If with_out_conv is True, the result of output will go through + another convolution layer. + + Args: + in_channels (int): number of input channels in out_conv layer. + out_channels (int): number of output channels in out_conv layer. + with_out_conv (bool): Whether to use out_conv layer + out_conv_cfg (dict): Config dict for convolution layer, which should + contain "groups", "kernel_size", "padding", "bias" to build + out_conv layer. + out_norm_cfg (dict): Config dict for normalization layer in out_conv. + out_conv_order (tuple): The order of conv/norm/activation layers in + out_conv. + with_input1_conv (bool): Whether to use convolution on input1. + with_input2_conv (bool): Whether to use convolution on input2. + input_conv_cfg (dict): Config dict for building input1_conv layer and + input2_conv layer, which is expected to contain the type of + convolution. + Default: None, which means using conv2d. + input_norm_cfg (dict): Config dict for normalization layer in + input1_conv and input2_conv layer. Default: None. + upsample_mode (str): Interpolation method used to resize the output + of input1_conv and input2_conv to target size. Currently, we + support ['nearest', 'bilinear']. Default: 'nearest'. + """ + + def __init__(self, + fused_channels=256, + out_channels=256, + with_out_conv=True, + out_conv_cfg=dict( + groups=1, kernel_size=3, padding=1, bias=True), + out_norm_cfg=None, + out_conv_order=('act', 'conv', 'norm'), + with_input1_conv=False, + with_input2_conv=False, + input_conv_cfg=None, + input_norm_cfg=None, + upsample_mode='nearest'): + super(BaseMergeCell, self).__init__() + assert upsample_mode in ['nearest', 'bilinear'] + self.with_out_conv = with_out_conv + self.with_input1_conv = with_input1_conv + self.with_input2_conv = with_input2_conv + self.upsample_mode = upsample_mode + + if self.with_out_conv: + self.out_conv = ConvModule( + fused_channels, + out_channels, + **out_conv_cfg, + norm_cfg=out_norm_cfg, + order=out_conv_order) + + self.input1_conv = self._build_input_conv( + out_channels, input_conv_cfg, + input_norm_cfg) if with_input1_conv else nn.Sequential() + self.input2_conv = self._build_input_conv( + out_channels, input_conv_cfg, + input_norm_cfg) if with_input2_conv else nn.Sequential() + + def _build_input_conv(self, channel, conv_cfg, norm_cfg): + return ConvModule( + channel, + channel, + 3, + padding=1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + bias=True) + + @abstractmethod + def _binary_op(self, x1, x2): + pass + + def _resize(self, x, size): + if x.shape[-2:] == size: + return x + elif x.shape[-2:] < size: + return F.interpolate(x, size=size, mode=self.upsample_mode) + else: + assert x.shape[-2] % size[-2] == 0 and x.shape[-1] % size[-1] == 0 + kernel_size = x.shape[-1] // size[-1] + x = F.max_pool2d(x, kernel_size=kernel_size, stride=kernel_size) + return x + + def forward(self, x1, x2, out_size=None): + assert x1.shape[:2] == x2.shape[:2] + assert out_size is None or len(out_size) == 2 + if out_size is None: # resize to larger one + out_size = max(x1.size()[2:], x2.size()[2:]) + + x1 = self.input1_conv(x1) + x2 = self.input2_conv(x2) + + x1 = self._resize(x1, out_size) + x2 = self._resize(x2, out_size) + + x = self._binary_op(x1, x2) + if self.with_out_conv: + x = self.out_conv(x) + return x + + +class SumCell(BaseMergeCell): + + def __init__(self, in_channels, out_channels, **kwargs): + super(SumCell, self).__init__(in_channels, out_channels, **kwargs) + + def _binary_op(self, x1, x2): + return x1 + x2 + + +class ConcatCell(BaseMergeCell): + + def __init__(self, in_channels, out_channels, **kwargs): + super(ConcatCell, self).__init__(in_channels * 2, out_channels, + **kwargs) + + def _binary_op(self, x1, x2): + ret = torch.cat([x1, x2], dim=1) + return ret + + +class GlobalPoolingCell(BaseMergeCell): + + def __init__(self, in_channels=None, out_channels=None, **kwargs): + super().__init__(in_channels, out_channels, **kwargs) + self.global_pool = nn.AdaptiveAvgPool2d((1, 1)) + + def _binary_op(self, x1, x2): + x2_att = self.global_pool(x2).sigmoid() + return x2 + x2_att * x1 diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/modulated_deform_conv.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/modulated_deform_conv.py new file mode 100644 index 0000000000000000000000000000000000000000..f97278361d5262b1a87432dc5e3eb842b39ceb10 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/modulated_deform_conv.py @@ -0,0 +1,282 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import math + +import torch +import torch.nn as nn +from torch.autograd import Function +from torch.autograd.function import once_differentiable +from torch.nn.modules.utils import _pair, _single + +from annotator.mmpkg.mmcv.utils import deprecated_api_warning +from ..cnn import CONV_LAYERS +from ..utils import ext_loader, print_log + +ext_module = ext_loader.load_ext( + '_ext', + ['modulated_deform_conv_forward', 'modulated_deform_conv_backward']) + + +class ModulatedDeformConv2dFunction(Function): + + @staticmethod + def symbolic(g, input, offset, mask, weight, bias, stride, padding, + dilation, groups, deform_groups): + input_tensors = [input, offset, mask, weight] + if bias is not None: + input_tensors.append(bias) + return g.op( + 'mmcv::MMCVModulatedDeformConv2d', + *input_tensors, + stride_i=stride, + padding_i=padding, + dilation_i=dilation, + groups_i=groups, + deform_groups_i=deform_groups) + + @staticmethod + def forward(ctx, + input, + offset, + mask, + weight, + bias=None, + stride=1, + padding=0, + dilation=1, + groups=1, + deform_groups=1): + if input is not None and input.dim() != 4: + raise ValueError( + f'Expected 4D tensor as input, got {input.dim()}D tensor \ + instead.') + ctx.stride = _pair(stride) + ctx.padding = _pair(padding) + ctx.dilation = _pair(dilation) + ctx.groups = groups + ctx.deform_groups = deform_groups + ctx.with_bias = bias is not None + if not ctx.with_bias: + bias = input.new_empty(0) # fake tensor + # When pytorch version >= 1.6.0, amp is adopted for fp16 mode; + # amp won't cast the type of model (float32), but "offset" is cast + # to float16 by nn.Conv2d automatically, leading to the type + # mismatch with input (when it is float32) or weight. + # The flag for whether to use fp16 or amp is the type of "offset", + # we cast weight and input to temporarily support fp16 and amp + # whatever the pytorch version is. + input = input.type_as(offset) + weight = weight.type_as(input) + ctx.save_for_backward(input, offset, mask, weight, bias) + output = input.new_empty( + ModulatedDeformConv2dFunction._output_size(ctx, input, weight)) + ctx._bufs = [input.new_empty(0), input.new_empty(0)] + ext_module.modulated_deform_conv_forward( + input, + weight, + bias, + ctx._bufs[0], + offset, + mask, + output, + ctx._bufs[1], + kernel_h=weight.size(2), + kernel_w=weight.size(3), + stride_h=ctx.stride[0], + stride_w=ctx.stride[1], + pad_h=ctx.padding[0], + pad_w=ctx.padding[1], + dilation_h=ctx.dilation[0], + dilation_w=ctx.dilation[1], + group=ctx.groups, + deformable_group=ctx.deform_groups, + with_bias=ctx.with_bias) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + input, offset, mask, weight, bias = ctx.saved_tensors + grad_input = torch.zeros_like(input) + grad_offset = torch.zeros_like(offset) + grad_mask = torch.zeros_like(mask) + grad_weight = torch.zeros_like(weight) + grad_bias = torch.zeros_like(bias) + grad_output = grad_output.contiguous() + ext_module.modulated_deform_conv_backward( + input, + weight, + bias, + ctx._bufs[0], + offset, + mask, + ctx._bufs[1], + grad_input, + grad_weight, + grad_bias, + grad_offset, + grad_mask, + grad_output, + kernel_h=weight.size(2), + kernel_w=weight.size(3), + stride_h=ctx.stride[0], + stride_w=ctx.stride[1], + pad_h=ctx.padding[0], + pad_w=ctx.padding[1], + dilation_h=ctx.dilation[0], + dilation_w=ctx.dilation[1], + group=ctx.groups, + deformable_group=ctx.deform_groups, + with_bias=ctx.with_bias) + if not ctx.with_bias: + grad_bias = None + + return (grad_input, grad_offset, grad_mask, grad_weight, grad_bias, + None, None, None, None, None) + + @staticmethod + def _output_size(ctx, input, weight): + channels = weight.size(0) + output_size = (input.size(0), channels) + for d in range(input.dim() - 2): + in_size = input.size(d + 2) + pad = ctx.padding[d] + kernel = ctx.dilation[d] * (weight.size(d + 2) - 1) + 1 + stride_ = ctx.stride[d] + output_size += ((in_size + (2 * pad) - kernel) // stride_ + 1, ) + if not all(map(lambda s: s > 0, output_size)): + raise ValueError( + 'convolution input is too small (output would be ' + + 'x'.join(map(str, output_size)) + ')') + return output_size + + +modulated_deform_conv2d = ModulatedDeformConv2dFunction.apply + + +class ModulatedDeformConv2d(nn.Module): + + @deprecated_api_warning({'deformable_groups': 'deform_groups'}, + cls_name='ModulatedDeformConv2d') + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + deform_groups=1, + bias=True): + super(ModulatedDeformConv2d, self).__init__() + self.in_channels = in_channels + self.out_channels = out_channels + self.kernel_size = _pair(kernel_size) + self.stride = _pair(stride) + self.padding = _pair(padding) + self.dilation = _pair(dilation) + self.groups = groups + self.deform_groups = deform_groups + # enable compatibility with nn.Conv2d + self.transposed = False + self.output_padding = _single(0) + + self.weight = nn.Parameter( + torch.Tensor(out_channels, in_channels // groups, + *self.kernel_size)) + if bias: + self.bias = nn.Parameter(torch.Tensor(out_channels)) + else: + self.register_parameter('bias', None) + self.init_weights() + + def init_weights(self): + n = self.in_channels + for k in self.kernel_size: + n *= k + stdv = 1. / math.sqrt(n) + self.weight.data.uniform_(-stdv, stdv) + if self.bias is not None: + self.bias.data.zero_() + + def forward(self, x, offset, mask): + return modulated_deform_conv2d(x, offset, mask, self.weight, self.bias, + self.stride, self.padding, + self.dilation, self.groups, + self.deform_groups) + + +@CONV_LAYERS.register_module('DCNv2') +class ModulatedDeformConv2dPack(ModulatedDeformConv2d): + """A ModulatedDeformable Conv Encapsulation that acts as normal Conv + layers. + + Args: + in_channels (int): Same as nn.Conv2d. + out_channels (int): Same as nn.Conv2d. + kernel_size (int or tuple[int]): Same as nn.Conv2d. + stride (int): Same as nn.Conv2d, while tuple is not supported. + padding (int): Same as nn.Conv2d, while tuple is not supported. + dilation (int): Same as nn.Conv2d, while tuple is not supported. + groups (int): Same as nn.Conv2d. + bias (bool or str): If specified as `auto`, it will be decided by the + norm_cfg. Bias will be set as True if norm_cfg is None, otherwise + False. + """ + + _version = 2 + + def __init__(self, *args, **kwargs): + super(ModulatedDeformConv2dPack, self).__init__(*args, **kwargs) + self.conv_offset = nn.Conv2d( + self.in_channels, + self.deform_groups * 3 * self.kernel_size[0] * self.kernel_size[1], + kernel_size=self.kernel_size, + stride=self.stride, + padding=self.padding, + dilation=self.dilation, + bias=True) + self.init_weights() + + def init_weights(self): + super(ModulatedDeformConv2dPack, self).init_weights() + if hasattr(self, 'conv_offset'): + self.conv_offset.weight.data.zero_() + self.conv_offset.bias.data.zero_() + + def forward(self, x): + out = self.conv_offset(x) + o1, o2, mask = torch.chunk(out, 3, dim=1) + offset = torch.cat((o1, o2), dim=1) + mask = torch.sigmoid(mask) + return modulated_deform_conv2d(x, offset, mask, self.weight, self.bias, + self.stride, self.padding, + self.dilation, self.groups, + self.deform_groups) + + def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, + missing_keys, unexpected_keys, error_msgs): + version = local_metadata.get('version', None) + + if version is None or version < 2: + # the key is different in early versions + # In version < 2, ModulatedDeformConvPack + # loads previous benchmark models. + if (prefix + 'conv_offset.weight' not in state_dict + and prefix[:-1] + '_offset.weight' in state_dict): + state_dict[prefix + 'conv_offset.weight'] = state_dict.pop( + prefix[:-1] + '_offset.weight') + if (prefix + 'conv_offset.bias' not in state_dict + and prefix[:-1] + '_offset.bias' in state_dict): + state_dict[prefix + + 'conv_offset.bias'] = state_dict.pop(prefix[:-1] + + '_offset.bias') + + if version is not None and version > 1: + print_log( + f'ModulatedDeformConvPack {prefix.rstrip(".")} is upgraded to ' + 'version 2.', + logger='root') + + super()._load_from_state_dict(state_dict, prefix, local_metadata, + strict, missing_keys, unexpected_keys, + error_msgs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/multi_scale_deform_attn.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/multi_scale_deform_attn.py new file mode 100644 index 0000000000000000000000000000000000000000..fe755eaa931565aab77ecc387990328c01447343 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/multi_scale_deform_attn.py @@ -0,0 +1,358 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import math +import warnings + +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.autograd.function import Function, once_differentiable + +from annotator.mmpkg.mmcv import deprecated_api_warning +from annotator.mmpkg.mmcv.cnn import constant_init, xavier_init +from annotator.mmpkg.mmcv.cnn.bricks.registry import ATTENTION +from annotator.mmpkg.mmcv.runner import BaseModule +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', ['ms_deform_attn_backward', 'ms_deform_attn_forward']) + + +class MultiScaleDeformableAttnFunction(Function): + + @staticmethod + def forward(ctx, value, value_spatial_shapes, value_level_start_index, + sampling_locations, attention_weights, im2col_step): + """GPU version of multi-scale deformable attention. + + Args: + value (Tensor): The value has shape + (bs, num_keys, mum_heads, embed_dims//num_heads) + value_spatial_shapes (Tensor): Spatial shape of + each feature map, has shape (num_levels, 2), + last dimension 2 represent (h, w) + sampling_locations (Tensor): The location of sampling points, + has shape + (bs ,num_queries, num_heads, num_levels, num_points, 2), + the last dimension 2 represent (x, y). + attention_weights (Tensor): The weight of sampling points used + when calculate the attention, has shape + (bs ,num_queries, num_heads, num_levels, num_points), + im2col_step (Tensor): The step used in image to column. + + Returns: + Tensor: has shape (bs, num_queries, embed_dims) + """ + + ctx.im2col_step = im2col_step + output = ext_module.ms_deform_attn_forward( + value, + value_spatial_shapes, + value_level_start_index, + sampling_locations, + attention_weights, + im2col_step=ctx.im2col_step) + ctx.save_for_backward(value, value_spatial_shapes, + value_level_start_index, sampling_locations, + attention_weights) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + """GPU version of backward function. + + Args: + grad_output (Tensor): Gradient + of output tensor of forward. + + Returns: + Tuple[Tensor]: Gradient + of input tensors in forward. + """ + value, value_spatial_shapes, value_level_start_index,\ + sampling_locations, attention_weights = ctx.saved_tensors + grad_value = torch.zeros_like(value) + grad_sampling_loc = torch.zeros_like(sampling_locations) + grad_attn_weight = torch.zeros_like(attention_weights) + + ext_module.ms_deform_attn_backward( + value, + value_spatial_shapes, + value_level_start_index, + sampling_locations, + attention_weights, + grad_output.contiguous(), + grad_value, + grad_sampling_loc, + grad_attn_weight, + im2col_step=ctx.im2col_step) + + return grad_value, None, None, \ + grad_sampling_loc, grad_attn_weight, None + + +def multi_scale_deformable_attn_pytorch(value, value_spatial_shapes, + sampling_locations, attention_weights): + """CPU version of multi-scale deformable attention. + + Args: + value (Tensor): The value has shape + (bs, num_keys, mum_heads, embed_dims//num_heads) + value_spatial_shapes (Tensor): Spatial shape of + each feature map, has shape (num_levels, 2), + last dimension 2 represent (h, w) + sampling_locations (Tensor): The location of sampling points, + has shape + (bs ,num_queries, num_heads, num_levels, num_points, 2), + the last dimension 2 represent (x, y). + attention_weights (Tensor): The weight of sampling points used + when calculate the attention, has shape + (bs ,num_queries, num_heads, num_levels, num_points), + + Returns: + Tensor: has shape (bs, num_queries, embed_dims) + """ + + bs, _, num_heads, embed_dims = value.shape + _, num_queries, num_heads, num_levels, num_points, _ =\ + sampling_locations.shape + value_list = value.split([H_ * W_ for H_, W_ in value_spatial_shapes], + dim=1) + sampling_grids = 2 * sampling_locations - 1 + sampling_value_list = [] + for level, (H_, W_) in enumerate(value_spatial_shapes): + # bs, H_*W_, num_heads, embed_dims -> + # bs, H_*W_, num_heads*embed_dims -> + # bs, num_heads*embed_dims, H_*W_ -> + # bs*num_heads, embed_dims, H_, W_ + value_l_ = value_list[level].flatten(2).transpose(1, 2).reshape( + bs * num_heads, embed_dims, H_, W_) + # bs, num_queries, num_heads, num_points, 2 -> + # bs, num_heads, num_queries, num_points, 2 -> + # bs*num_heads, num_queries, num_points, 2 + sampling_grid_l_ = sampling_grids[:, :, :, + level].transpose(1, 2).flatten(0, 1) + # bs*num_heads, embed_dims, num_queries, num_points + sampling_value_l_ = F.grid_sample( + value_l_, + sampling_grid_l_, + mode='bilinear', + padding_mode='zeros', + align_corners=False) + sampling_value_list.append(sampling_value_l_) + # (bs, num_queries, num_heads, num_levels, num_points) -> + # (bs, num_heads, num_queries, num_levels, num_points) -> + # (bs, num_heads, 1, num_queries, num_levels*num_points) + attention_weights = attention_weights.transpose(1, 2).reshape( + bs * num_heads, 1, num_queries, num_levels * num_points) + output = (torch.stack(sampling_value_list, dim=-2).flatten(-2) * + attention_weights).sum(-1).view(bs, num_heads * embed_dims, + num_queries) + return output.transpose(1, 2).contiguous() + + +@ATTENTION.register_module() +class MultiScaleDeformableAttention(BaseModule): + """An attention module used in Deformable-Detr. + + `Deformable DETR: Deformable Transformers for End-to-End Object Detection. + `_. + + Args: + embed_dims (int): The embedding dimension of Attention. + Default: 256. + num_heads (int): Parallel attention heads. Default: 64. + num_levels (int): The number of feature map used in + Attention. Default: 4. + num_points (int): The number of sampling points for + each query in each head. Default: 4. + im2col_step (int): The step used in image_to_column. + Default: 64. + dropout (float): A Dropout layer on `inp_identity`. + Default: 0.1. + batch_first (bool): Key, Query and Value are shape of + (batch, n, embed_dim) + or (n, batch, embed_dim). Default to False. + norm_cfg (dict): Config dict for normalization layer. + Default: None. + init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. + Default: None. + """ + + def __init__(self, + embed_dims=256, + num_heads=8, + num_levels=4, + num_points=4, + im2col_step=64, + dropout=0.1, + batch_first=False, + norm_cfg=None, + init_cfg=None): + super().__init__(init_cfg) + if embed_dims % num_heads != 0: + raise ValueError(f'embed_dims must be divisible by num_heads, ' + f'but got {embed_dims} and {num_heads}') + dim_per_head = embed_dims // num_heads + self.norm_cfg = norm_cfg + self.dropout = nn.Dropout(dropout) + self.batch_first = batch_first + + # you'd better set dim_per_head to a power of 2 + # which is more efficient in the CUDA implementation + def _is_power_of_2(n): + if (not isinstance(n, int)) or (n < 0): + raise ValueError( + 'invalid input for _is_power_of_2: {} (type: {})'.format( + n, type(n))) + return (n & (n - 1) == 0) and n != 0 + + if not _is_power_of_2(dim_per_head): + warnings.warn( + "You'd better set embed_dims in " + 'MultiScaleDeformAttention to make ' + 'the dimension of each attention head a power of 2 ' + 'which is more efficient in our CUDA implementation.') + + self.im2col_step = im2col_step + self.embed_dims = embed_dims + self.num_levels = num_levels + self.num_heads = num_heads + self.num_points = num_points + self.sampling_offsets = nn.Linear( + embed_dims, num_heads * num_levels * num_points * 2) + self.attention_weights = nn.Linear(embed_dims, + num_heads * num_levels * num_points) + self.value_proj = nn.Linear(embed_dims, embed_dims) + self.output_proj = nn.Linear(embed_dims, embed_dims) + self.init_weights() + + def init_weights(self): + """Default initialization for Parameters of Module.""" + constant_init(self.sampling_offsets, 0.) + thetas = torch.arange( + self.num_heads, + dtype=torch.float32) * (2.0 * math.pi / self.num_heads) + grid_init = torch.stack([thetas.cos(), thetas.sin()], -1) + grid_init = (grid_init / + grid_init.abs().max(-1, keepdim=True)[0]).view( + self.num_heads, 1, 1, + 2).repeat(1, self.num_levels, self.num_points, 1) + for i in range(self.num_points): + grid_init[:, :, i, :] *= i + 1 + + self.sampling_offsets.bias.data = grid_init.view(-1) + constant_init(self.attention_weights, val=0., bias=0.) + xavier_init(self.value_proj, distribution='uniform', bias=0.) + xavier_init(self.output_proj, distribution='uniform', bias=0.) + self._is_init = True + + @deprecated_api_warning({'residual': 'identity'}, + cls_name='MultiScaleDeformableAttention') + def forward(self, + query, + key=None, + value=None, + identity=None, + query_pos=None, + key_padding_mask=None, + reference_points=None, + spatial_shapes=None, + level_start_index=None, + **kwargs): + """Forward Function of MultiScaleDeformAttention. + + Args: + query (Tensor): Query of Transformer with shape + (num_query, bs, embed_dims). + key (Tensor): The key tensor with shape + `(num_key, bs, embed_dims)`. + value (Tensor): The value tensor with shape + `(num_key, bs, embed_dims)`. + identity (Tensor): The tensor used for addition, with the + same shape as `query`. Default None. If None, + `query` will be used. + query_pos (Tensor): The positional encoding for `query`. + Default: None. + key_pos (Tensor): The positional encoding for `key`. Default + None. + reference_points (Tensor): The normalized reference + points with shape (bs, num_query, num_levels, 2), + all elements is range in [0, 1], top-left (0,0), + bottom-right (1, 1), including padding area. + or (N, Length_{query}, num_levels, 4), add + additional two dimensions is (w, h) to + form reference boxes. + key_padding_mask (Tensor): ByteTensor for `query`, with + shape [bs, num_key]. + spatial_shapes (Tensor): Spatial shape of features in + different levels. With shape (num_levels, 2), + last dimension represents (h, w). + level_start_index (Tensor): The start index of each level. + A tensor has shape ``(num_levels, )`` and can be represented + as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...]. + + Returns: + Tensor: forwarded results with shape [num_query, bs, embed_dims]. + """ + + if value is None: + value = query + + if identity is None: + identity = query + if query_pos is not None: + query = query + query_pos + if not self.batch_first: + # change to (bs, num_query ,embed_dims) + query = query.permute(1, 0, 2) + value = value.permute(1, 0, 2) + + bs, num_query, _ = query.shape + bs, num_value, _ = value.shape + assert (spatial_shapes[:, 0] * spatial_shapes[:, 1]).sum() == num_value + + value = self.value_proj(value) + if key_padding_mask is not None: + value = value.masked_fill(key_padding_mask[..., None], 0.0) + value = value.view(bs, num_value, self.num_heads, -1) + sampling_offsets = self.sampling_offsets(query).view( + bs, num_query, self.num_heads, self.num_levels, self.num_points, 2) + attention_weights = self.attention_weights(query).view( + bs, num_query, self.num_heads, self.num_levels * self.num_points) + attention_weights = attention_weights.softmax(-1) + + attention_weights = attention_weights.view(bs, num_query, + self.num_heads, + self.num_levels, + self.num_points) + if reference_points.shape[-1] == 2: + offset_normalizer = torch.stack( + [spatial_shapes[..., 1], spatial_shapes[..., 0]], -1) + sampling_locations = reference_points[:, :, None, :, None, :] \ + + sampling_offsets \ + / offset_normalizer[None, None, None, :, None, :] + elif reference_points.shape[-1] == 4: + sampling_locations = reference_points[:, :, None, :, None, :2] \ + + sampling_offsets / self.num_points \ + * reference_points[:, :, None, :, None, 2:] \ + * 0.5 + else: + raise ValueError( + f'Last dim of reference_points must be' + f' 2 or 4, but get {reference_points.shape[-1]} instead.') + if torch.cuda.is_available() and value.is_cuda: + output = MultiScaleDeformableAttnFunction.apply( + value, spatial_shapes, level_start_index, sampling_locations, + attention_weights, self.im2col_step) + else: + output = multi_scale_deformable_attn_pytorch( + value, spatial_shapes, sampling_locations, attention_weights) + + output = self.output_proj(output) + + if not self.batch_first: + # (num_query, bs ,embed_dims) + output = output.permute(1, 0, 2) + + return self.dropout(output) + identity diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/nms.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/nms.py new file mode 100644 index 0000000000000000000000000000000000000000..908ac66645eef29fb55fce82497eb9f6af1a2667 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/nms.py @@ -0,0 +1,417 @@ +import os + +import numpy as np +import torch + +from annotator.mmpkg.mmcv.utils import deprecated_api_warning +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', ['nms', 'softnms', 'nms_match', 'nms_rotated']) + + +# This function is modified from: https://github.com/pytorch/vision/ +class NMSop(torch.autograd.Function): + + @staticmethod + def forward(ctx, bboxes, scores, iou_threshold, offset, score_threshold, + max_num): + is_filtering_by_score = score_threshold > 0 + if is_filtering_by_score: + valid_mask = scores > score_threshold + bboxes, scores = bboxes[valid_mask], scores[valid_mask] + valid_inds = torch.nonzero( + valid_mask, as_tuple=False).squeeze(dim=1) + + inds = ext_module.nms( + bboxes, scores, iou_threshold=float(iou_threshold), offset=offset) + + if max_num > 0: + inds = inds[:max_num] + if is_filtering_by_score: + inds = valid_inds[inds] + return inds + + @staticmethod + def symbolic(g, bboxes, scores, iou_threshold, offset, score_threshold, + max_num): + from ..onnx import is_custom_op_loaded + has_custom_op = is_custom_op_loaded() + # TensorRT nms plugin is aligned with original nms in ONNXRuntime + is_trt_backend = os.environ.get('ONNX_BACKEND') == 'MMCVTensorRT' + if has_custom_op and (not is_trt_backend): + return g.op( + 'mmcv::NonMaxSuppression', + bboxes, + scores, + iou_threshold_f=float(iou_threshold), + offset_i=int(offset)) + else: + from torch.onnx.symbolic_opset9 import select, squeeze, unsqueeze + from ..onnx.onnx_utils.symbolic_helper import _size_helper + + boxes = unsqueeze(g, bboxes, 0) + scores = unsqueeze(g, unsqueeze(g, scores, 0), 0) + + if max_num > 0: + max_num = g.op( + 'Constant', + value_t=torch.tensor(max_num, dtype=torch.long)) + else: + dim = g.op('Constant', value_t=torch.tensor(0)) + max_num = _size_helper(g, bboxes, dim) + max_output_per_class = max_num + iou_threshold = g.op( + 'Constant', + value_t=torch.tensor([iou_threshold], dtype=torch.float)) + score_threshold = g.op( + 'Constant', + value_t=torch.tensor([score_threshold], dtype=torch.float)) + nms_out = g.op('NonMaxSuppression', boxes, scores, + max_output_per_class, iou_threshold, + score_threshold) + return squeeze( + g, + select( + g, nms_out, 1, + g.op( + 'Constant', + value_t=torch.tensor([2], dtype=torch.long))), 1) + + +class SoftNMSop(torch.autograd.Function): + + @staticmethod + def forward(ctx, boxes, scores, iou_threshold, sigma, min_score, method, + offset): + dets = boxes.new_empty((boxes.size(0), 5), device='cpu') + inds = ext_module.softnms( + boxes.cpu(), + scores.cpu(), + dets.cpu(), + iou_threshold=float(iou_threshold), + sigma=float(sigma), + min_score=float(min_score), + method=int(method), + offset=int(offset)) + return dets, inds + + @staticmethod + def symbolic(g, boxes, scores, iou_threshold, sigma, min_score, method, + offset): + from packaging import version + assert version.parse(torch.__version__) >= version.parse('1.7.0') + nms_out = g.op( + 'mmcv::SoftNonMaxSuppression', + boxes, + scores, + iou_threshold_f=float(iou_threshold), + sigma_f=float(sigma), + min_score_f=float(min_score), + method_i=int(method), + offset_i=int(offset), + outputs=2) + return nms_out + + +@deprecated_api_warning({'iou_thr': 'iou_threshold'}) +def nms(boxes, scores, iou_threshold, offset=0, score_threshold=0, max_num=-1): + """Dispatch to either CPU or GPU NMS implementations. + + The input can be either torch tensor or numpy array. GPU NMS will be used + if the input is gpu tensor, otherwise CPU NMS + will be used. The returned type will always be the same as inputs. + + Arguments: + boxes (torch.Tensor or np.ndarray): boxes in shape (N, 4). + scores (torch.Tensor or np.ndarray): scores in shape (N, ). + iou_threshold (float): IoU threshold for NMS. + offset (int, 0 or 1): boxes' width or height is (x2 - x1 + offset). + score_threshold (float): score threshold for NMS. + max_num (int): maximum number of boxes after NMS. + + Returns: + tuple: kept dets(boxes and scores) and indice, which is always the \ + same data type as the input. + + Example: + >>> boxes = np.array([[49.1, 32.4, 51.0, 35.9], + >>> [49.3, 32.9, 51.0, 35.3], + >>> [49.2, 31.8, 51.0, 35.4], + >>> [35.1, 11.5, 39.1, 15.7], + >>> [35.6, 11.8, 39.3, 14.2], + >>> [35.3, 11.5, 39.9, 14.5], + >>> [35.2, 11.7, 39.7, 15.7]], dtype=np.float32) + >>> scores = np.array([0.9, 0.9, 0.5, 0.5, 0.5, 0.4, 0.3],\ + dtype=np.float32) + >>> iou_threshold = 0.6 + >>> dets, inds = nms(boxes, scores, iou_threshold) + >>> assert len(inds) == len(dets) == 3 + """ + assert isinstance(boxes, (torch.Tensor, np.ndarray)) + assert isinstance(scores, (torch.Tensor, np.ndarray)) + is_numpy = False + if isinstance(boxes, np.ndarray): + is_numpy = True + boxes = torch.from_numpy(boxes) + if isinstance(scores, np.ndarray): + scores = torch.from_numpy(scores) + assert boxes.size(1) == 4 + assert boxes.size(0) == scores.size(0) + assert offset in (0, 1) + + if torch.__version__ == 'parrots': + indata_list = [boxes, scores] + indata_dict = { + 'iou_threshold': float(iou_threshold), + 'offset': int(offset) + } + inds = ext_module.nms(*indata_list, **indata_dict) + else: + inds = NMSop.apply(boxes, scores, iou_threshold, offset, + score_threshold, max_num) + dets = torch.cat((boxes[inds], scores[inds].reshape(-1, 1)), dim=1) + if is_numpy: + dets = dets.cpu().numpy() + inds = inds.cpu().numpy() + return dets, inds + + +@deprecated_api_warning({'iou_thr': 'iou_threshold'}) +def soft_nms(boxes, + scores, + iou_threshold=0.3, + sigma=0.5, + min_score=1e-3, + method='linear', + offset=0): + """Dispatch to only CPU Soft NMS implementations. + + The input can be either a torch tensor or numpy array. + The returned type will always be the same as inputs. + + Arguments: + boxes (torch.Tensor or np.ndarray): boxes in shape (N, 4). + scores (torch.Tensor or np.ndarray): scores in shape (N, ). + iou_threshold (float): IoU threshold for NMS. + sigma (float): hyperparameter for gaussian method + min_score (float): score filter threshold + method (str): either 'linear' or 'gaussian' + offset (int, 0 or 1): boxes' width or height is (x2 - x1 + offset). + + Returns: + tuple: kept dets(boxes and scores) and indice, which is always the \ + same data type as the input. + + Example: + >>> boxes = np.array([[4., 3., 5., 3.], + >>> [4., 3., 5., 4.], + >>> [3., 1., 3., 1.], + >>> [3., 1., 3., 1.], + >>> [3., 1., 3., 1.], + >>> [3., 1., 3., 1.]], dtype=np.float32) + >>> scores = np.array([0.9, 0.9, 0.5, 0.5, 0.4, 0.0], dtype=np.float32) + >>> iou_threshold = 0.6 + >>> dets, inds = soft_nms(boxes, scores, iou_threshold, sigma=0.5) + >>> assert len(inds) == len(dets) == 5 + """ + + assert isinstance(boxes, (torch.Tensor, np.ndarray)) + assert isinstance(scores, (torch.Tensor, np.ndarray)) + is_numpy = False + if isinstance(boxes, np.ndarray): + is_numpy = True + boxes = torch.from_numpy(boxes) + if isinstance(scores, np.ndarray): + scores = torch.from_numpy(scores) + assert boxes.size(1) == 4 + assert boxes.size(0) == scores.size(0) + assert offset in (0, 1) + method_dict = {'naive': 0, 'linear': 1, 'gaussian': 2} + assert method in method_dict.keys() + + if torch.__version__ == 'parrots': + dets = boxes.new_empty((boxes.size(0), 5), device='cpu') + indata_list = [boxes.cpu(), scores.cpu(), dets.cpu()] + indata_dict = { + 'iou_threshold': float(iou_threshold), + 'sigma': float(sigma), + 'min_score': min_score, + 'method': method_dict[method], + 'offset': int(offset) + } + inds = ext_module.softnms(*indata_list, **indata_dict) + else: + dets, inds = SoftNMSop.apply(boxes.cpu(), scores.cpu(), + float(iou_threshold), float(sigma), + float(min_score), method_dict[method], + int(offset)) + + dets = dets[:inds.size(0)] + + if is_numpy: + dets = dets.cpu().numpy() + inds = inds.cpu().numpy() + return dets, inds + else: + return dets.to(device=boxes.device), inds.to(device=boxes.device) + + +def batched_nms(boxes, scores, idxs, nms_cfg, class_agnostic=False): + """Performs non-maximum suppression in a batched fashion. + + Modified from https://github.com/pytorch/vision/blob + /505cd6957711af790211896d32b40291bea1bc21/torchvision/ops/boxes.py#L39. + In order to perform NMS independently per class, we add an offset to all + the boxes. The offset is dependent only on the class idx, and is large + enough so that boxes from different classes do not overlap. + + Arguments: + boxes (torch.Tensor): boxes in shape (N, 4). + scores (torch.Tensor): scores in shape (N, ). + idxs (torch.Tensor): each index value correspond to a bbox cluster, + and NMS will not be applied between elements of different idxs, + shape (N, ). + nms_cfg (dict): specify nms type and other parameters like iou_thr. + Possible keys includes the following. + + - iou_thr (float): IoU threshold used for NMS. + - split_thr (float): threshold number of boxes. In some cases the + number of boxes is large (e.g., 200k). To avoid OOM during + training, the users could set `split_thr` to a small value. + If the number of boxes is greater than the threshold, it will + perform NMS on each group of boxes separately and sequentially. + Defaults to 10000. + class_agnostic (bool): if true, nms is class agnostic, + i.e. IoU thresholding happens over all boxes, + regardless of the predicted class. + + Returns: + tuple: kept dets and indice. + """ + nms_cfg_ = nms_cfg.copy() + class_agnostic = nms_cfg_.pop('class_agnostic', class_agnostic) + if class_agnostic: + boxes_for_nms = boxes + else: + max_coordinate = boxes.max() + offsets = idxs.to(boxes) * (max_coordinate + torch.tensor(1).to(boxes)) + boxes_for_nms = boxes + offsets[:, None] + + nms_type = nms_cfg_.pop('type', 'nms') + nms_op = eval(nms_type) + + split_thr = nms_cfg_.pop('split_thr', 10000) + # Won't split to multiple nms nodes when exporting to onnx + if boxes_for_nms.shape[0] < split_thr or torch.onnx.is_in_onnx_export(): + dets, keep = nms_op(boxes_for_nms, scores, **nms_cfg_) + boxes = boxes[keep] + # -1 indexing works abnormal in TensorRT + # This assumes `dets` has 5 dimensions where + # the last dimension is score. + # TODO: more elegant way to handle the dimension issue. + # Some type of nms would reweight the score, such as SoftNMS + scores = dets[:, 4] + else: + max_num = nms_cfg_.pop('max_num', -1) + total_mask = scores.new_zeros(scores.size(), dtype=torch.bool) + # Some type of nms would reweight the score, such as SoftNMS + scores_after_nms = scores.new_zeros(scores.size()) + for id in torch.unique(idxs): + mask = (idxs == id).nonzero(as_tuple=False).view(-1) + dets, keep = nms_op(boxes_for_nms[mask], scores[mask], **nms_cfg_) + total_mask[mask[keep]] = True + scores_after_nms[mask[keep]] = dets[:, -1] + keep = total_mask.nonzero(as_tuple=False).view(-1) + + scores, inds = scores_after_nms[keep].sort(descending=True) + keep = keep[inds] + boxes = boxes[keep] + + if max_num > 0: + keep = keep[:max_num] + boxes = boxes[:max_num] + scores = scores[:max_num] + + return torch.cat([boxes, scores[:, None]], -1), keep + + +def nms_match(dets, iou_threshold): + """Matched dets into different groups by NMS. + + NMS match is Similar to NMS but when a bbox is suppressed, nms match will + record the indice of suppressed bbox and form a group with the indice of + kept bbox. In each group, indice is sorted as score order. + + Arguments: + dets (torch.Tensor | np.ndarray): Det boxes with scores, shape (N, 5). + iou_thr (float): IoU thresh for NMS. + + Returns: + List[torch.Tensor | np.ndarray]: The outer list corresponds different + matched group, the inner Tensor corresponds the indices for a group + in score order. + """ + if dets.shape[0] == 0: + matched = [] + else: + assert dets.shape[-1] == 5, 'inputs dets.shape should be (N, 5), ' \ + f'but get {dets.shape}' + if isinstance(dets, torch.Tensor): + dets_t = dets.detach().cpu() + else: + dets_t = torch.from_numpy(dets) + indata_list = [dets_t] + indata_dict = {'iou_threshold': float(iou_threshold)} + matched = ext_module.nms_match(*indata_list, **indata_dict) + if torch.__version__ == 'parrots': + matched = matched.tolist() + + if isinstance(dets, torch.Tensor): + return [dets.new_tensor(m, dtype=torch.long) for m in matched] + else: + return [np.array(m, dtype=np.int) for m in matched] + + +def nms_rotated(dets, scores, iou_threshold, labels=None): + """Performs non-maximum suppression (NMS) on the rotated boxes according to + their intersection-over-union (IoU). + + Rotated NMS iteratively removes lower scoring rotated boxes which have an + IoU greater than iou_threshold with another (higher scoring) rotated box. + + Args: + boxes (Tensor): Rotated boxes in shape (N, 5). They are expected to \ + be in (x_ctr, y_ctr, width, height, angle_radian) format. + scores (Tensor): scores in shape (N, ). + iou_threshold (float): IoU thresh for NMS. + labels (Tensor): boxes' label in shape (N,). + + Returns: + tuple: kept dets(boxes and scores) and indice, which is always the \ + same data type as the input. + """ + if dets.shape[0] == 0: + return dets, None + multi_label = labels is not None + if multi_label: + dets_wl = torch.cat((dets, labels.unsqueeze(1)), 1) + else: + dets_wl = dets + _, order = scores.sort(0, descending=True) + dets_sorted = dets_wl.index_select(0, order) + + if torch.__version__ == 'parrots': + keep_inds = ext_module.nms_rotated( + dets_wl, + scores, + order, + dets_sorted, + iou_threshold=iou_threshold, + multi_label=multi_label) + else: + keep_inds = ext_module.nms_rotated(dets_wl, scores, order, dets_sorted, + iou_threshold, multi_label) + dets = torch.cat((dets[keep_inds], scores[keep_inds].reshape(-1, 1)), + dim=1) + return dets, keep_inds diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/pixel_group.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/pixel_group.py new file mode 100644 index 0000000000000000000000000000000000000000..2143c75f835a467c802fc3c37ecd3ac0f85bcda4 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/pixel_group.py @@ -0,0 +1,75 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import numpy as np +import torch + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', ['pixel_group']) + + +def pixel_group(score, mask, embedding, kernel_label, kernel_contour, + kernel_region_num, distance_threshold): + """Group pixels into text instances, which is widely used text detection + methods. + + Arguments: + score (np.array or Tensor): The foreground score with size hxw. + mask (np.array or Tensor): The foreground mask with size hxw. + embedding (np.array or Tensor): The embedding with size hxwxc to + distinguish instances. + kernel_label (np.array or Tensor): The instance kernel index with + size hxw. + kernel_contour (np.array or Tensor): The kernel contour with size hxw. + kernel_region_num (int): The instance kernel region number. + distance_threshold (float): The embedding distance threshold between + kernel and pixel in one instance. + + Returns: + pixel_assignment (List[List[float]]): The instance coordinate list. + Each element consists of averaged confidence, pixel number, and + coordinates (x_i, y_i for all pixels) in order. + """ + assert isinstance(score, (torch.Tensor, np.ndarray)) + assert isinstance(mask, (torch.Tensor, np.ndarray)) + assert isinstance(embedding, (torch.Tensor, np.ndarray)) + assert isinstance(kernel_label, (torch.Tensor, np.ndarray)) + assert isinstance(kernel_contour, (torch.Tensor, np.ndarray)) + assert isinstance(kernel_region_num, int) + assert isinstance(distance_threshold, float) + + if isinstance(score, np.ndarray): + score = torch.from_numpy(score) + if isinstance(mask, np.ndarray): + mask = torch.from_numpy(mask) + if isinstance(embedding, np.ndarray): + embedding = torch.from_numpy(embedding) + if isinstance(kernel_label, np.ndarray): + kernel_label = torch.from_numpy(kernel_label) + if isinstance(kernel_contour, np.ndarray): + kernel_contour = torch.from_numpy(kernel_contour) + + if torch.__version__ == 'parrots': + label = ext_module.pixel_group( + score, + mask, + embedding, + kernel_label, + kernel_contour, + kernel_region_num=kernel_region_num, + distance_threshold=distance_threshold) + label = label.tolist() + label = label[0] + list_index = kernel_region_num + pixel_assignment = [] + for x in range(kernel_region_num): + pixel_assignment.append( + np.array( + label[list_index:list_index + int(label[x])], + dtype=np.float)) + list_index = list_index + int(label[x]) + else: + pixel_assignment = ext_module.pixel_group(score, mask, embedding, + kernel_label, kernel_contour, + kernel_region_num, + distance_threshold) + return pixel_assignment diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/point_sample.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/point_sample.py new file mode 100644 index 0000000000000000000000000000000000000000..08b1617805fa84e1c8afc61f3263b4b86bd2a136 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/point_sample.py @@ -0,0 +1,336 @@ +# Modified from https://github.com/facebookresearch/detectron2/tree/master/projects/PointRend # noqa + +from os import path as osp + +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.nn.modules.utils import _pair +from torch.onnx.operators import shape_as_tensor + + +def bilinear_grid_sample(im, grid, align_corners=False): + """Given an input and a flow-field grid, computes the output using input + values and pixel locations from grid. Supported only bilinear interpolation + method to sample the input pixels. + + Args: + im (torch.Tensor): Input feature map, shape (N, C, H, W) + grid (torch.Tensor): Point coordinates, shape (N, Hg, Wg, 2) + align_corners {bool}: If set to True, the extrema (-1 and 1) are + considered as referring to the center points of the input’s + corner pixels. If set to False, they are instead considered as + referring to the corner points of the input’s corner pixels, + making the sampling more resolution agnostic. + Returns: + torch.Tensor: A tensor with sampled points, shape (N, C, Hg, Wg) + """ + n, c, h, w = im.shape + gn, gh, gw, _ = grid.shape + assert n == gn + + x = grid[:, :, :, 0] + y = grid[:, :, :, 1] + + if align_corners: + x = ((x + 1) / 2) * (w - 1) + y = ((y + 1) / 2) * (h - 1) + else: + x = ((x + 1) * w - 1) / 2 + y = ((y + 1) * h - 1) / 2 + + x = x.view(n, -1) + y = y.view(n, -1) + + x0 = torch.floor(x).long() + y0 = torch.floor(y).long() + x1 = x0 + 1 + y1 = y0 + 1 + + wa = ((x1 - x) * (y1 - y)).unsqueeze(1) + wb = ((x1 - x) * (y - y0)).unsqueeze(1) + wc = ((x - x0) * (y1 - y)).unsqueeze(1) + wd = ((x - x0) * (y - y0)).unsqueeze(1) + + # Apply default for grid_sample function zero padding + im_padded = F.pad(im, pad=[1, 1, 1, 1], mode='constant', value=0) + padded_h = h + 2 + padded_w = w + 2 + # save points positions after padding + x0, x1, y0, y1 = x0 + 1, x1 + 1, y0 + 1, y1 + 1 + + # Clip coordinates to padded image size + x0 = torch.where(x0 < 0, torch.tensor(0), x0) + x0 = torch.where(x0 > padded_w - 1, torch.tensor(padded_w - 1), x0) + x1 = torch.where(x1 < 0, torch.tensor(0), x1) + x1 = torch.where(x1 > padded_w - 1, torch.tensor(padded_w - 1), x1) + y0 = torch.where(y0 < 0, torch.tensor(0), y0) + y0 = torch.where(y0 > padded_h - 1, torch.tensor(padded_h - 1), y0) + y1 = torch.where(y1 < 0, torch.tensor(0), y1) + y1 = torch.where(y1 > padded_h - 1, torch.tensor(padded_h - 1), y1) + + im_padded = im_padded.view(n, c, -1) + + x0_y0 = (x0 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) + x0_y1 = (x0 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) + x1_y0 = (x1 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) + x1_y1 = (x1 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) + + Ia = torch.gather(im_padded, 2, x0_y0) + Ib = torch.gather(im_padded, 2, x0_y1) + Ic = torch.gather(im_padded, 2, x1_y0) + Id = torch.gather(im_padded, 2, x1_y1) + + return (Ia * wa + Ib * wb + Ic * wc + Id * wd).reshape(n, c, gh, gw) + + +def is_in_onnx_export_without_custom_ops(): + from annotator.mmpkg.mmcv.ops import get_onnxruntime_op_path + ort_custom_op_path = get_onnxruntime_op_path() + return torch.onnx.is_in_onnx_export( + ) and not osp.exists(ort_custom_op_path) + + +def normalize(grid): + """Normalize input grid from [-1, 1] to [0, 1] + Args: + grid (Tensor): The grid to be normalize, range [-1, 1]. + Returns: + Tensor: Normalized grid, range [0, 1]. + """ + + return (grid + 1.0) / 2.0 + + +def denormalize(grid): + """Denormalize input grid from range [0, 1] to [-1, 1] + Args: + grid (Tensor): The grid to be denormalize, range [0, 1]. + Returns: + Tensor: Denormalized grid, range [-1, 1]. + """ + + return grid * 2.0 - 1.0 + + +def generate_grid(num_grid, size, device): + """Generate regular square grid of points in [0, 1] x [0, 1] coordinate + space. + + Args: + num_grid (int): The number of grids to sample, one for each region. + size (tuple(int, int)): The side size of the regular grid. + device (torch.device): Desired device of returned tensor. + + Returns: + (torch.Tensor): A tensor of shape (num_grid, size[0]*size[1], 2) that + contains coordinates for the regular grids. + """ + + affine_trans = torch.tensor([[[1., 0., 0.], [0., 1., 0.]]], device=device) + grid = F.affine_grid( + affine_trans, torch.Size((1, 1, *size)), align_corners=False) + grid = normalize(grid) + return grid.view(1, -1, 2).expand(num_grid, -1, -1) + + +def rel_roi_point_to_abs_img_point(rois, rel_roi_points): + """Convert roi based relative point coordinates to image based absolute + point coordinates. + + Args: + rois (Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) + rel_roi_points (Tensor): Point coordinates inside RoI, relative to + RoI, location, range (0, 1), shape (N, P, 2) + Returns: + Tensor: Image based absolute point coordinates, shape (N, P, 2) + """ + + with torch.no_grad(): + assert rel_roi_points.size(0) == rois.size(0) + assert rois.dim() == 2 + assert rel_roi_points.dim() == 3 + assert rel_roi_points.size(2) == 2 + # remove batch idx + if rois.size(1) == 5: + rois = rois[:, 1:] + abs_img_points = rel_roi_points.clone() + # To avoid an error during exporting to onnx use independent + # variables instead inplace computation + xs = abs_img_points[:, :, 0] * (rois[:, None, 2] - rois[:, None, 0]) + ys = abs_img_points[:, :, 1] * (rois[:, None, 3] - rois[:, None, 1]) + xs += rois[:, None, 0] + ys += rois[:, None, 1] + abs_img_points = torch.stack([xs, ys], dim=2) + return abs_img_points + + +def get_shape_from_feature_map(x): + """Get spatial resolution of input feature map considering exporting to + onnx mode. + + Args: + x (torch.Tensor): Input tensor, shape (N, C, H, W) + Returns: + torch.Tensor: Spatial resolution (width, height), shape (1, 1, 2) + """ + if torch.onnx.is_in_onnx_export(): + img_shape = shape_as_tensor(x)[2:].flip(0).view(1, 1, 2).to( + x.device).float() + else: + img_shape = torch.tensor(x.shape[2:]).flip(0).view(1, 1, 2).to( + x.device).float() + return img_shape + + +def abs_img_point_to_rel_img_point(abs_img_points, img, spatial_scale=1.): + """Convert image based absolute point coordinates to image based relative + coordinates for sampling. + + Args: + abs_img_points (Tensor): Image based absolute point coordinates, + shape (N, P, 2) + img (tuple/Tensor): (height, width) of image or feature map. + spatial_scale (float): Scale points by this factor. Default: 1. + + Returns: + Tensor: Image based relative point coordinates for sampling, + shape (N, P, 2) + """ + + assert (isinstance(img, tuple) and len(img) == 2) or \ + (isinstance(img, torch.Tensor) and len(img.shape) == 4) + + if isinstance(img, tuple): + h, w = img + scale = torch.tensor([w, h], + dtype=torch.float, + device=abs_img_points.device) + scale = scale.view(1, 1, 2) + else: + scale = get_shape_from_feature_map(img) + + return abs_img_points / scale * spatial_scale + + +def rel_roi_point_to_rel_img_point(rois, + rel_roi_points, + img, + spatial_scale=1.): + """Convert roi based relative point coordinates to image based absolute + point coordinates. + + Args: + rois (Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) + rel_roi_points (Tensor): Point coordinates inside RoI, relative to + RoI, location, range (0, 1), shape (N, P, 2) + img (tuple/Tensor): (height, width) of image or feature map. + spatial_scale (float): Scale points by this factor. Default: 1. + + Returns: + Tensor: Image based relative point coordinates for sampling, + shape (N, P, 2) + """ + + abs_img_point = rel_roi_point_to_abs_img_point(rois, rel_roi_points) + rel_img_point = abs_img_point_to_rel_img_point(abs_img_point, img, + spatial_scale) + + return rel_img_point + + +def point_sample(input, points, align_corners=False, **kwargs): + """A wrapper around :func:`grid_sample` to support 3D point_coords tensors + Unlike :func:`torch.nn.functional.grid_sample` it assumes point_coords to + lie inside ``[0, 1] x [0, 1]`` square. + + Args: + input (Tensor): Feature map, shape (N, C, H, W). + points (Tensor): Image based absolute point coordinates (normalized), + range [0, 1] x [0, 1], shape (N, P, 2) or (N, Hgrid, Wgrid, 2). + align_corners (bool): Whether align_corners. Default: False + + Returns: + Tensor: Features of `point` on `input`, shape (N, C, P) or + (N, C, Hgrid, Wgrid). + """ + + add_dim = False + if points.dim() == 3: + add_dim = True + points = points.unsqueeze(2) + if is_in_onnx_export_without_custom_ops(): + # If custom ops for onnx runtime not compiled use python + # implementation of grid_sample function to make onnx graph + # with supported nodes + output = bilinear_grid_sample( + input, denormalize(points), align_corners=align_corners) + else: + output = F.grid_sample( + input, denormalize(points), align_corners=align_corners, **kwargs) + if add_dim: + output = output.squeeze(3) + return output + + +class SimpleRoIAlign(nn.Module): + + def __init__(self, output_size, spatial_scale, aligned=True): + """Simple RoI align in PointRend, faster than standard RoIAlign. + + Args: + output_size (tuple[int]): h, w + spatial_scale (float): scale the input boxes by this number + aligned (bool): if False, use the legacy implementation in + MMDetection, align_corners=True will be used in F.grid_sample. + If True, align the results more perfectly. + """ + + super(SimpleRoIAlign, self).__init__() + self.output_size = _pair(output_size) + self.spatial_scale = float(spatial_scale) + # to be consistent with other RoI ops + self.use_torchvision = False + self.aligned = aligned + + def forward(self, features, rois): + num_imgs = features.size(0) + num_rois = rois.size(0) + rel_roi_points = generate_grid( + num_rois, self.output_size, device=rois.device) + + if torch.onnx.is_in_onnx_export(): + rel_img_points = rel_roi_point_to_rel_img_point( + rois, rel_roi_points, features, self.spatial_scale) + rel_img_points = rel_img_points.reshape(num_imgs, -1, + *rel_img_points.shape[1:]) + point_feats = point_sample( + features, rel_img_points, align_corners=not self.aligned) + point_feats = point_feats.transpose(1, 2) + else: + point_feats = [] + for batch_ind in range(num_imgs): + # unravel batch dim + feat = features[batch_ind].unsqueeze(0) + inds = (rois[:, 0].long() == batch_ind) + if inds.any(): + rel_img_points = rel_roi_point_to_rel_img_point( + rois[inds], rel_roi_points[inds], feat, + self.spatial_scale).unsqueeze(0) + point_feat = point_sample( + feat, rel_img_points, align_corners=not self.aligned) + point_feat = point_feat.squeeze(0).transpose(0, 1) + point_feats.append(point_feat) + + point_feats = torch.cat(point_feats, dim=0) + + channels = features.size(1) + roi_feats = point_feats.reshape(num_rois, channels, *self.output_size) + + return roi_feats + + def __repr__(self): + format_str = self.__class__.__name__ + format_str += '(output_size={}, spatial_scale={}'.format( + self.output_size, self.spatial_scale) + return format_str diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/points_in_boxes.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/points_in_boxes.py new file mode 100644 index 0000000000000000000000000000000000000000..4003173a53052161dbcd687a2fa1d755642fdab8 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/points_in_boxes.py @@ -0,0 +1,133 @@ +import torch + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', [ + 'points_in_boxes_part_forward', 'points_in_boxes_cpu_forward', + 'points_in_boxes_all_forward' +]) + + +def points_in_boxes_part(points, boxes): + """Find the box in which each point is (CUDA). + + Args: + points (torch.Tensor): [B, M, 3], [x, y, z] in LiDAR/DEPTH coordinate + boxes (torch.Tensor): [B, T, 7], + num_valid_boxes <= T, [x, y, z, x_size, y_size, z_size, rz] in + LiDAR/DEPTH coordinate, (x, y, z) is the bottom center + + Returns: + box_idxs_of_pts (torch.Tensor): (B, M), default background = -1 + """ + assert points.shape[0] == boxes.shape[0], \ + 'Points and boxes should have the same batch size, ' \ + f'but got {points.shape[0]} and {boxes.shape[0]}' + assert boxes.shape[2] == 7, \ + 'boxes dimension should be 7, ' \ + f'but got unexpected shape {boxes.shape[2]}' + assert points.shape[2] == 3, \ + 'points dimension should be 3, ' \ + f'but got unexpected shape {points.shape[2]}' + batch_size, num_points, _ = points.shape + + box_idxs_of_pts = points.new_zeros((batch_size, num_points), + dtype=torch.int).fill_(-1) + + # If manually put the tensor 'points' or 'boxes' on a device + # which is not the current device, some temporary variables + # will be created on the current device in the cuda op, + # and the output will be incorrect. + # Therefore, we force the current device to be the same + # as the device of the tensors if it was not. + # Please refer to https://github.com/open-mmlab/mmdetection3d/issues/305 + # for the incorrect output before the fix. + points_device = points.get_device() + assert points_device == boxes.get_device(), \ + 'Points and boxes should be put on the same device' + if torch.cuda.current_device() != points_device: + torch.cuda.set_device(points_device) + + ext_module.points_in_boxes_part_forward(boxes.contiguous(), + points.contiguous(), + box_idxs_of_pts) + + return box_idxs_of_pts + + +def points_in_boxes_cpu(points, boxes): + """Find all boxes in which each point is (CPU). The CPU version of + :meth:`points_in_boxes_all`. + + Args: + points (torch.Tensor): [B, M, 3], [x, y, z] in + LiDAR/DEPTH coordinate + boxes (torch.Tensor): [B, T, 7], + num_valid_boxes <= T, [x, y, z, x_size, y_size, z_size, rz], + (x, y, z) is the bottom center. + + Returns: + box_idxs_of_pts (torch.Tensor): (B, M, T), default background = 0. + """ + assert points.shape[0] == boxes.shape[0], \ + 'Points and boxes should have the same batch size, ' \ + f'but got {points.shape[0]} and {boxes.shape[0]}' + assert boxes.shape[2] == 7, \ + 'boxes dimension should be 7, ' \ + f'but got unexpected shape {boxes.shape[2]}' + assert points.shape[2] == 3, \ + 'points dimension should be 3, ' \ + f'but got unexpected shape {points.shape[2]}' + batch_size, num_points, _ = points.shape + num_boxes = boxes.shape[1] + + point_indices = points.new_zeros((batch_size, num_boxes, num_points), + dtype=torch.int) + for b in range(batch_size): + ext_module.points_in_boxes_cpu_forward(boxes[b].float().contiguous(), + points[b].float().contiguous(), + point_indices[b]) + point_indices = point_indices.transpose(1, 2) + + return point_indices + + +def points_in_boxes_all(points, boxes): + """Find all boxes in which each point is (CUDA). + + Args: + points (torch.Tensor): [B, M, 3], [x, y, z] in LiDAR/DEPTH coordinate + boxes (torch.Tensor): [B, T, 7], + num_valid_boxes <= T, [x, y, z, x_size, y_size, z_size, rz], + (x, y, z) is the bottom center. + + Returns: + box_idxs_of_pts (torch.Tensor): (B, M, T), default background = 0. + """ + assert boxes.shape[0] == points.shape[0], \ + 'Points and boxes should have the same batch size, ' \ + f'but got {boxes.shape[0]} and {boxes.shape[0]}' + assert boxes.shape[2] == 7, \ + 'boxes dimension should be 7, ' \ + f'but got unexpected shape {boxes.shape[2]}' + assert points.shape[2] == 3, \ + 'points dimension should be 3, ' \ + f'but got unexpected shape {points.shape[2]}' + batch_size, num_points, _ = points.shape + num_boxes = boxes.shape[1] + + box_idxs_of_pts = points.new_zeros((batch_size, num_points, num_boxes), + dtype=torch.int).fill_(0) + + # Same reason as line 25-32 + points_device = points.get_device() + assert points_device == boxes.get_device(), \ + 'Points and boxes should be put on the same device' + if torch.cuda.current_device() != points_device: + torch.cuda.set_device(points_device) + + ext_module.points_in_boxes_all_forward(boxes.contiguous(), + points.contiguous(), + box_idxs_of_pts) + + return box_idxs_of_pts diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/points_sampler.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/points_sampler.py new file mode 100644 index 0000000000000000000000000000000000000000..ae1a24f939dd0e2934765326363ea51c2f2b4cca --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/points_sampler.py @@ -0,0 +1,177 @@ +from typing import List + +import torch +from torch import nn as nn + +from annotator.mmpkg.mmcv.runner import force_fp32 +from .furthest_point_sample import (furthest_point_sample, + furthest_point_sample_with_dist) + + +def calc_square_dist(point_feat_a, point_feat_b, norm=True): + """Calculating square distance between a and b. + + Args: + point_feat_a (Tensor): (B, N, C) Feature vector of each point. + point_feat_b (Tensor): (B, M, C) Feature vector of each point. + norm (Bool, optional): Whether to normalize the distance. + Default: True. + + Returns: + Tensor: (B, N, M) Distance between each pair points. + """ + num_channel = point_feat_a.shape[-1] + # [bs, n, 1] + a_square = torch.sum(point_feat_a.unsqueeze(dim=2).pow(2), dim=-1) + # [bs, 1, m] + b_square = torch.sum(point_feat_b.unsqueeze(dim=1).pow(2), dim=-1) + + corr_matrix = torch.matmul(point_feat_a, point_feat_b.transpose(1, 2)) + + dist = a_square + b_square - 2 * corr_matrix + if norm: + dist = torch.sqrt(dist) / num_channel + return dist + + +def get_sampler_cls(sampler_type): + """Get the type and mode of points sampler. + + Args: + sampler_type (str): The type of points sampler. + The valid value are "D-FPS", "F-FPS", or "FS". + + Returns: + class: Points sampler type. + """ + sampler_mappings = { + 'D-FPS': DFPSSampler, + 'F-FPS': FFPSSampler, + 'FS': FSSampler, + } + try: + return sampler_mappings[sampler_type] + except KeyError: + raise KeyError( + f'Supported `sampler_type` are {sampler_mappings.keys()}, but got \ + {sampler_type}') + + +class PointsSampler(nn.Module): + """Points sampling. + + Args: + num_point (list[int]): Number of sample points. + fps_mod_list (list[str], optional): Type of FPS method, valid mod + ['F-FPS', 'D-FPS', 'FS'], Default: ['D-FPS']. + F-FPS: using feature distances for FPS. + D-FPS: using Euclidean distances of points for FPS. + FS: using F-FPS and D-FPS simultaneously. + fps_sample_range_list (list[int], optional): + Range of points to apply FPS. Default: [-1]. + """ + + def __init__(self, + num_point: List[int], + fps_mod_list: List[str] = ['D-FPS'], + fps_sample_range_list: List[int] = [-1]): + super().__init__() + # FPS would be applied to different fps_mod in the list, + # so the length of the num_point should be equal to + # fps_mod_list and fps_sample_range_list. + assert len(num_point) == len(fps_mod_list) == len( + fps_sample_range_list) + self.num_point = num_point + self.fps_sample_range_list = fps_sample_range_list + self.samplers = nn.ModuleList() + for fps_mod in fps_mod_list: + self.samplers.append(get_sampler_cls(fps_mod)()) + self.fp16_enabled = False + + @force_fp32() + def forward(self, points_xyz, features): + """ + Args: + points_xyz (Tensor): (B, N, 3) xyz coordinates of the features. + features (Tensor): (B, C, N) Descriptors of the features. + + Returns: + Tensor: (B, npoint, sample_num) Indices of sampled points. + """ + indices = [] + last_fps_end_index = 0 + + for fps_sample_range, sampler, npoint in zip( + self.fps_sample_range_list, self.samplers, self.num_point): + assert fps_sample_range < points_xyz.shape[1] + + if fps_sample_range == -1: + sample_points_xyz = points_xyz[:, last_fps_end_index:] + if features is not None: + sample_features = features[:, :, last_fps_end_index:] + else: + sample_features = None + else: + sample_points_xyz = \ + points_xyz[:, last_fps_end_index:fps_sample_range] + if features is not None: + sample_features = features[:, :, last_fps_end_index: + fps_sample_range] + else: + sample_features = None + + fps_idx = sampler(sample_points_xyz.contiguous(), sample_features, + npoint) + + indices.append(fps_idx + last_fps_end_index) + last_fps_end_index += fps_sample_range + indices = torch.cat(indices, dim=1) + + return indices + + +class DFPSSampler(nn.Module): + """Using Euclidean distances of points for FPS.""" + + def __init__(self): + super().__init__() + + def forward(self, points, features, npoint): + """Sampling points with D-FPS.""" + fps_idx = furthest_point_sample(points.contiguous(), npoint) + return fps_idx + + +class FFPSSampler(nn.Module): + """Using feature distances for FPS.""" + + def __init__(self): + super().__init__() + + def forward(self, points, features, npoint): + """Sampling points with F-FPS.""" + assert features is not None, \ + 'feature input to FFPS_Sampler should not be None' + features_for_fps = torch.cat([points, features.transpose(1, 2)], dim=2) + features_dist = calc_square_dist( + features_for_fps, features_for_fps, norm=False) + fps_idx = furthest_point_sample_with_dist(features_dist, npoint) + return fps_idx + + +class FSSampler(nn.Module): + """Using F-FPS and D-FPS simultaneously.""" + + def __init__(self): + super().__init__() + + def forward(self, points, features, npoint): + """Sampling points with FS_Sampling.""" + assert features is not None, \ + 'feature input to FS_Sampler should not be None' + ffps_sampler = FFPSSampler() + dfps_sampler = DFPSSampler() + fps_idx_ffps = ffps_sampler(points, features, npoint) + fps_idx_dfps = dfps_sampler(points, features, npoint) + fps_idx = torch.cat([fps_idx_ffps, fps_idx_dfps], dim=1) + return fps_idx diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/psa_mask.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/psa_mask.py new file mode 100644 index 0000000000000000000000000000000000000000..cdf14e62b50e8d4dd6856c94333c703bcc4c9ab6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/psa_mask.py @@ -0,0 +1,92 @@ +# Modified from https://github.com/hszhao/semseg/blob/master/lib/psa +from torch import nn +from torch.autograd import Function +from torch.nn.modules.utils import _pair + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', + ['psamask_forward', 'psamask_backward']) + + +class PSAMaskFunction(Function): + + @staticmethod + def symbolic(g, input, psa_type, mask_size): + return g.op( + 'mmcv::MMCVPSAMask', + input, + psa_type_i=psa_type, + mask_size_i=mask_size) + + @staticmethod + def forward(ctx, input, psa_type, mask_size): + ctx.psa_type = psa_type + ctx.mask_size = _pair(mask_size) + ctx.save_for_backward(input) + + h_mask, w_mask = ctx.mask_size + batch_size, channels, h_feature, w_feature = input.size() + assert channels == h_mask * w_mask + output = input.new_zeros( + (batch_size, h_feature * w_feature, h_feature, w_feature)) + + ext_module.psamask_forward( + input, + output, + psa_type=psa_type, + num_=batch_size, + h_feature=h_feature, + w_feature=w_feature, + h_mask=h_mask, + w_mask=w_mask, + half_h_mask=(h_mask - 1) // 2, + half_w_mask=(w_mask - 1) // 2) + return output + + @staticmethod + def backward(ctx, grad_output): + input = ctx.saved_tensors[0] + psa_type = ctx.psa_type + h_mask, w_mask = ctx.mask_size + batch_size, channels, h_feature, w_feature = input.size() + grad_input = grad_output.new_zeros( + (batch_size, channels, h_feature, w_feature)) + ext_module.psamask_backward( + grad_output, + grad_input, + psa_type=psa_type, + num_=batch_size, + h_feature=h_feature, + w_feature=w_feature, + h_mask=h_mask, + w_mask=w_mask, + half_h_mask=(h_mask - 1) // 2, + half_w_mask=(w_mask - 1) // 2) + return grad_input, None, None, None + + +psa_mask = PSAMaskFunction.apply + + +class PSAMask(nn.Module): + + def __init__(self, psa_type, mask_size=None): + super(PSAMask, self).__init__() + assert psa_type in ['collect', 'distribute'] + if psa_type == 'collect': + psa_type_enum = 0 + else: + psa_type_enum = 1 + self.psa_type_enum = psa_type_enum + self.mask_size = mask_size + self.psa_type = psa_type + + def forward(self, input): + return psa_mask(input, self.psa_type_enum, self.mask_size) + + def __repr__(self): + s = self.__class__.__name__ + s += f'(psa_type={self.psa_type}, ' + s += f'mask_size={self.mask_size})' + return s diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roi_align.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roi_align.py new file mode 100644 index 0000000000000000000000000000000000000000..0755aefc66e67233ceae0f4b77948301c443e9fb --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roi_align.py @@ -0,0 +1,223 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.nn as nn +from torch.autograd import Function +from torch.autograd.function import once_differentiable +from torch.nn.modules.utils import _pair + +from ..utils import deprecated_api_warning, ext_loader + +ext_module = ext_loader.load_ext('_ext', + ['roi_align_forward', 'roi_align_backward']) + + +class RoIAlignFunction(Function): + + @staticmethod + def symbolic(g, input, rois, output_size, spatial_scale, sampling_ratio, + pool_mode, aligned): + from ..onnx import is_custom_op_loaded + has_custom_op = is_custom_op_loaded() + if has_custom_op: + return g.op( + 'mmcv::MMCVRoiAlign', + input, + rois, + output_height_i=output_size[0], + output_width_i=output_size[1], + spatial_scale_f=spatial_scale, + sampling_ratio_i=sampling_ratio, + mode_s=pool_mode, + aligned_i=aligned) + else: + from torch.onnx.symbolic_opset9 import sub, squeeze + from torch.onnx.symbolic_helper import _slice_helper + from torch.onnx import TensorProtoDataType + # batch_indices = rois[:, 0].long() + batch_indices = _slice_helper( + g, rois, axes=[1], starts=[0], ends=[1]) + batch_indices = squeeze(g, batch_indices, 1) + batch_indices = g.op( + 'Cast', batch_indices, to_i=TensorProtoDataType.INT64) + # rois = rois[:, 1:] + rois = _slice_helper(g, rois, axes=[1], starts=[1], ends=[5]) + if aligned: + # rois -= 0.5/spatial_scale + aligned_offset = g.op( + 'Constant', + value_t=torch.tensor([0.5 / spatial_scale], + dtype=torch.float32)) + rois = sub(g, rois, aligned_offset) + # roi align + return g.op( + 'RoiAlign', + input, + rois, + batch_indices, + output_height_i=output_size[0], + output_width_i=output_size[1], + spatial_scale_f=spatial_scale, + sampling_ratio_i=max(0, sampling_ratio), + mode_s=pool_mode) + + @staticmethod + def forward(ctx, + input, + rois, + output_size, + spatial_scale=1.0, + sampling_ratio=0, + pool_mode='avg', + aligned=True): + ctx.output_size = _pair(output_size) + ctx.spatial_scale = spatial_scale + ctx.sampling_ratio = sampling_ratio + assert pool_mode in ('max', 'avg') + ctx.pool_mode = 0 if pool_mode == 'max' else 1 + ctx.aligned = aligned + ctx.input_shape = input.size() + + assert rois.size(1) == 5, 'RoI must be (idx, x1, y1, x2, y2)!' + + output_shape = (rois.size(0), input.size(1), ctx.output_size[0], + ctx.output_size[1]) + output = input.new_zeros(output_shape) + if ctx.pool_mode == 0: + argmax_y = input.new_zeros(output_shape) + argmax_x = input.new_zeros(output_shape) + else: + argmax_y = input.new_zeros(0) + argmax_x = input.new_zeros(0) + + ext_module.roi_align_forward( + input, + rois, + output, + argmax_y, + argmax_x, + aligned_height=ctx.output_size[0], + aligned_width=ctx.output_size[1], + spatial_scale=ctx.spatial_scale, + sampling_ratio=ctx.sampling_ratio, + pool_mode=ctx.pool_mode, + aligned=ctx.aligned) + + ctx.save_for_backward(rois, argmax_y, argmax_x) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + rois, argmax_y, argmax_x = ctx.saved_tensors + grad_input = grad_output.new_zeros(ctx.input_shape) + # complex head architecture may cause grad_output uncontiguous. + grad_output = grad_output.contiguous() + ext_module.roi_align_backward( + grad_output, + rois, + argmax_y, + argmax_x, + grad_input, + aligned_height=ctx.output_size[0], + aligned_width=ctx.output_size[1], + spatial_scale=ctx.spatial_scale, + sampling_ratio=ctx.sampling_ratio, + pool_mode=ctx.pool_mode, + aligned=ctx.aligned) + return grad_input, None, None, None, None, None, None + + +roi_align = RoIAlignFunction.apply + + +class RoIAlign(nn.Module): + """RoI align pooling layer. + + Args: + output_size (tuple): h, w + spatial_scale (float): scale the input boxes by this number + sampling_ratio (int): number of inputs samples to take for each + output sample. 0 to take samples densely for current models. + pool_mode (str, 'avg' or 'max'): pooling mode in each bin. + aligned (bool): if False, use the legacy implementation in + MMDetection. If True, align the results more perfectly. + use_torchvision (bool): whether to use roi_align from torchvision. + + Note: + The implementation of RoIAlign when aligned=True is modified from + https://github.com/facebookresearch/detectron2/ + + The meaning of aligned=True: + + Given a continuous coordinate c, its two neighboring pixel + indices (in our pixel model) are computed by floor(c - 0.5) and + ceil(c - 0.5). For example, c=1.3 has pixel neighbors with discrete + indices [0] and [1] (which are sampled from the underlying signal + at continuous coordinates 0.5 and 1.5). But the original roi_align + (aligned=False) does not subtract the 0.5 when computing + neighboring pixel indices and therefore it uses pixels with a + slightly incorrect alignment (relative to our pixel model) when + performing bilinear interpolation. + + With `aligned=True`, + we first appropriately scale the ROI and then shift it by -0.5 + prior to calling roi_align. This produces the correct neighbors; + + The difference does not make a difference to the model's + performance if ROIAlign is used together with conv layers. + """ + + @deprecated_api_warning( + { + 'out_size': 'output_size', + 'sample_num': 'sampling_ratio' + }, + cls_name='RoIAlign') + def __init__(self, + output_size, + spatial_scale=1.0, + sampling_ratio=0, + pool_mode='avg', + aligned=True, + use_torchvision=False): + super(RoIAlign, self).__init__() + + self.output_size = _pair(output_size) + self.spatial_scale = float(spatial_scale) + self.sampling_ratio = int(sampling_ratio) + self.pool_mode = pool_mode + self.aligned = aligned + self.use_torchvision = use_torchvision + + def forward(self, input, rois): + """ + Args: + input: NCHW images + rois: Bx5 boxes. First column is the index into N.\ + The other 4 columns are xyxy. + """ + if self.use_torchvision: + from torchvision.ops import roi_align as tv_roi_align + if 'aligned' in tv_roi_align.__code__.co_varnames: + return tv_roi_align(input, rois, self.output_size, + self.spatial_scale, self.sampling_ratio, + self.aligned) + else: + if self.aligned: + rois -= rois.new_tensor([0.] + + [0.5 / self.spatial_scale] * 4) + return tv_roi_align(input, rois, self.output_size, + self.spatial_scale, self.sampling_ratio) + else: + return roi_align(input, rois, self.output_size, self.spatial_scale, + self.sampling_ratio, self.pool_mode, self.aligned) + + def __repr__(self): + s = self.__class__.__name__ + s += f'(output_size={self.output_size}, ' + s += f'spatial_scale={self.spatial_scale}, ' + s += f'sampling_ratio={self.sampling_ratio}, ' + s += f'pool_mode={self.pool_mode}, ' + s += f'aligned={self.aligned}, ' + s += f'use_torchvision={self.use_torchvision})' + return s diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roi_align_rotated.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roi_align_rotated.py new file mode 100644 index 0000000000000000000000000000000000000000..0ce4961a3555d4da8bc3e32f1f7d5ad50036587d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roi_align_rotated.py @@ -0,0 +1,177 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch.nn as nn +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', ['roi_align_rotated_forward', 'roi_align_rotated_backward']) + + +class RoIAlignRotatedFunction(Function): + + @staticmethod + def symbolic(g, features, rois, out_size, spatial_scale, sample_num, + aligned, clockwise): + if isinstance(out_size, int): + out_h = out_size + out_w = out_size + elif isinstance(out_size, tuple): + assert len(out_size) == 2 + assert isinstance(out_size[0], int) + assert isinstance(out_size[1], int) + out_h, out_w = out_size + else: + raise TypeError( + '"out_size" must be an integer or tuple of integers') + return g.op( + 'mmcv::MMCVRoIAlignRotated', + features, + rois, + output_height_i=out_h, + output_width_i=out_h, + spatial_scale_f=spatial_scale, + sampling_ratio_i=sample_num, + aligned_i=aligned, + clockwise_i=clockwise) + + @staticmethod + def forward(ctx, + features, + rois, + out_size, + spatial_scale, + sample_num=0, + aligned=True, + clockwise=False): + if isinstance(out_size, int): + out_h = out_size + out_w = out_size + elif isinstance(out_size, tuple): + assert len(out_size) == 2 + assert isinstance(out_size[0], int) + assert isinstance(out_size[1], int) + out_h, out_w = out_size + else: + raise TypeError( + '"out_size" must be an integer or tuple of integers') + ctx.spatial_scale = spatial_scale + ctx.sample_num = sample_num + ctx.aligned = aligned + ctx.clockwise = clockwise + ctx.save_for_backward(rois) + ctx.feature_size = features.size() + + batch_size, num_channels, data_height, data_width = features.size() + num_rois = rois.size(0) + + output = features.new_zeros(num_rois, num_channels, out_h, out_w) + ext_module.roi_align_rotated_forward( + features, + rois, + output, + pooled_height=out_h, + pooled_width=out_w, + spatial_scale=spatial_scale, + sample_num=sample_num, + aligned=aligned, + clockwise=clockwise) + return output + + @staticmethod + def backward(ctx, grad_output): + feature_size = ctx.feature_size + spatial_scale = ctx.spatial_scale + aligned = ctx.aligned + clockwise = ctx.clockwise + sample_num = ctx.sample_num + rois = ctx.saved_tensors[0] + assert feature_size is not None + batch_size, num_channels, data_height, data_width = feature_size + + out_w = grad_output.size(3) + out_h = grad_output.size(2) + + grad_input = grad_rois = None + + if ctx.needs_input_grad[0]: + grad_input = rois.new_zeros(batch_size, num_channels, data_height, + data_width) + ext_module.roi_align_rotated_backward( + grad_output.contiguous(), + rois, + grad_input, + pooled_height=out_h, + pooled_width=out_w, + spatial_scale=spatial_scale, + sample_num=sample_num, + aligned=aligned, + clockwise=clockwise) + return grad_input, grad_rois, None, None, None, None, None + + +roi_align_rotated = RoIAlignRotatedFunction.apply + + +class RoIAlignRotated(nn.Module): + """RoI align pooling layer for rotated proposals. + + It accepts a feature map of shape (N, C, H, W) and rois with shape + (n, 6) with each roi decoded as (batch_index, center_x, center_y, + w, h, angle). The angle is in radian. + + Args: + out_size (tuple): h, w + spatial_scale (float): scale the input boxes by this number + sample_num (int): number of inputs samples to take for each + output sample. 0 to take samples densely for current models. + aligned (bool): if False, use the legacy implementation in + MMDetection. If True, align the results more perfectly. + Default: True. + clockwise (bool): If True, the angle in each proposal follows a + clockwise fashion in image space, otherwise, the angle is + counterclockwise. Default: False. + + Note: + The implementation of RoIAlign when aligned=True is modified from + https://github.com/facebookresearch/detectron2/ + + The meaning of aligned=True: + + Given a continuous coordinate c, its two neighboring pixel + indices (in our pixel model) are computed by floor(c - 0.5) and + ceil(c - 0.5). For example, c=1.3 has pixel neighbors with discrete + indices [0] and [1] (which are sampled from the underlying signal + at continuous coordinates 0.5 and 1.5). But the original roi_align + (aligned=False) does not subtract the 0.5 when computing + neighboring pixel indices and therefore it uses pixels with a + slightly incorrect alignment (relative to our pixel model) when + performing bilinear interpolation. + + With `aligned=True`, + we first appropriately scale the ROI and then shift it by -0.5 + prior to calling roi_align. This produces the correct neighbors; + + The difference does not make a difference to the model's + performance if ROIAlign is used together with conv layers. + """ + + def __init__(self, + out_size, + spatial_scale, + sample_num=0, + aligned=True, + clockwise=False): + super(RoIAlignRotated, self).__init__() + + self.out_size = out_size + self.spatial_scale = float(spatial_scale) + self.sample_num = int(sample_num) + self.aligned = aligned + self.clockwise = clockwise + + def forward(self, features, rois): + return RoIAlignRotatedFunction.apply(features, rois, self.out_size, + self.spatial_scale, + self.sample_num, self.aligned, + self.clockwise) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roi_pool.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roi_pool.py new file mode 100644 index 0000000000000000000000000000000000000000..d339d8f2941eabc1cbe181a9c6c5ab5ff4ff4e5f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roi_pool.py @@ -0,0 +1,86 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.nn as nn +from torch.autograd import Function +from torch.autograd.function import once_differentiable +from torch.nn.modules.utils import _pair + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', + ['roi_pool_forward', 'roi_pool_backward']) + + +class RoIPoolFunction(Function): + + @staticmethod + def symbolic(g, input, rois, output_size, spatial_scale): + return g.op( + 'MaxRoiPool', + input, + rois, + pooled_shape_i=output_size, + spatial_scale_f=spatial_scale) + + @staticmethod + def forward(ctx, input, rois, output_size, spatial_scale=1.0): + ctx.output_size = _pair(output_size) + ctx.spatial_scale = spatial_scale + ctx.input_shape = input.size() + + assert rois.size(1) == 5, 'RoI must be (idx, x1, y1, x2, y2)!' + + output_shape = (rois.size(0), input.size(1), ctx.output_size[0], + ctx.output_size[1]) + output = input.new_zeros(output_shape) + argmax = input.new_zeros(output_shape, dtype=torch.int) + + ext_module.roi_pool_forward( + input, + rois, + output, + argmax, + pooled_height=ctx.output_size[0], + pooled_width=ctx.output_size[1], + spatial_scale=ctx.spatial_scale) + + ctx.save_for_backward(rois, argmax) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + rois, argmax = ctx.saved_tensors + grad_input = grad_output.new_zeros(ctx.input_shape) + + ext_module.roi_pool_backward( + grad_output, + rois, + argmax, + grad_input, + pooled_height=ctx.output_size[0], + pooled_width=ctx.output_size[1], + spatial_scale=ctx.spatial_scale) + + return grad_input, None, None, None + + +roi_pool = RoIPoolFunction.apply + + +class RoIPool(nn.Module): + + def __init__(self, output_size, spatial_scale=1.0): + super(RoIPool, self).__init__() + + self.output_size = _pair(output_size) + self.spatial_scale = float(spatial_scale) + + def forward(self, input, rois): + return roi_pool(input, rois, self.output_size, self.spatial_scale) + + def __repr__(self): + s = self.__class__.__name__ + s += f'(output_size={self.output_size}, ' + s += f'spatial_scale={self.spatial_scale})' + return s diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roiaware_pool3d.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roiaware_pool3d.py new file mode 100644 index 0000000000000000000000000000000000000000..8191920ca50b388ef58f577dc986da101662ac53 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roiaware_pool3d.py @@ -0,0 +1,114 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +from torch import nn as nn +from torch.autograd import Function + +import annotator.mmpkg.mmcv as mmcv +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', ['roiaware_pool3d_forward', 'roiaware_pool3d_backward']) + + +class RoIAwarePool3d(nn.Module): + """Encode the geometry-specific features of each 3D proposal. + + Please refer to `PartA2 `_ for more + details. + + Args: + out_size (int or tuple): The size of output features. n or + [n1, n2, n3]. + max_pts_per_voxel (int, optional): The maximum number of points per + voxel. Default: 128. + mode (str, optional): Pooling method of RoIAware, 'max' or 'avg'. + Default: 'max'. + """ + + def __init__(self, out_size, max_pts_per_voxel=128, mode='max'): + super().__init__() + + self.out_size = out_size + self.max_pts_per_voxel = max_pts_per_voxel + assert mode in ['max', 'avg'] + pool_mapping = {'max': 0, 'avg': 1} + self.mode = pool_mapping[mode] + + def forward(self, rois, pts, pts_feature): + """ + Args: + rois (torch.Tensor): [N, 7], in LiDAR coordinate, + (x, y, z) is the bottom center of rois. + pts (torch.Tensor): [npoints, 3], coordinates of input points. + pts_feature (torch.Tensor): [npoints, C], features of input points. + + Returns: + pooled_features (torch.Tensor): [N, out_x, out_y, out_z, C] + """ + + return RoIAwarePool3dFunction.apply(rois, pts, pts_feature, + self.out_size, + self.max_pts_per_voxel, self.mode) + + +class RoIAwarePool3dFunction(Function): + + @staticmethod + def forward(ctx, rois, pts, pts_feature, out_size, max_pts_per_voxel, + mode): + """ + Args: + rois (torch.Tensor): [N, 7], in LiDAR coordinate, + (x, y, z) is the bottom center of rois. + pts (torch.Tensor): [npoints, 3], coordinates of input points. + pts_feature (torch.Tensor): [npoints, C], features of input points. + out_size (int or tuple): The size of output features. n or + [n1, n2, n3]. + max_pts_per_voxel (int): The maximum number of points per voxel. + Default: 128. + mode (int): Pooling method of RoIAware, 0 (max pool) or 1 (average + pool). + + Returns: + pooled_features (torch.Tensor): [N, out_x, out_y, out_z, C], output + pooled features. + """ + + if isinstance(out_size, int): + out_x = out_y = out_z = out_size + else: + assert len(out_size) == 3 + assert mmcv.is_tuple_of(out_size, int) + out_x, out_y, out_z = out_size + + num_rois = rois.shape[0] + num_channels = pts_feature.shape[-1] + num_pts = pts.shape[0] + + pooled_features = pts_feature.new_zeros( + (num_rois, out_x, out_y, out_z, num_channels)) + argmax = pts_feature.new_zeros( + (num_rois, out_x, out_y, out_z, num_channels), dtype=torch.int) + pts_idx_of_voxels = pts_feature.new_zeros( + (num_rois, out_x, out_y, out_z, max_pts_per_voxel), + dtype=torch.int) + + ext_module.roiaware_pool3d_forward(rois, pts, pts_feature, argmax, + pts_idx_of_voxels, pooled_features, + mode) + + ctx.roiaware_pool3d_for_backward = (pts_idx_of_voxels, argmax, mode, + num_pts, num_channels) + return pooled_features + + @staticmethod + def backward(ctx, grad_out): + ret = ctx.roiaware_pool3d_for_backward + pts_idx_of_voxels, argmax, mode, num_pts, num_channels = ret + + grad_in = grad_out.new_zeros((num_pts, num_channels)) + ext_module.roiaware_pool3d_backward(pts_idx_of_voxels, argmax, + grad_out.contiguous(), grad_in, + mode) + + return None, None, grad_in, None, None, None diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roipoint_pool3d.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roipoint_pool3d.py new file mode 100644 index 0000000000000000000000000000000000000000..0a21412c0728431c04b84245bc2e3109eea9aefc --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/roipoint_pool3d.py @@ -0,0 +1,77 @@ +from torch import nn as nn +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', ['roipoint_pool3d_forward']) + + +class RoIPointPool3d(nn.Module): + """Encode the geometry-specific features of each 3D proposal. + + Please refer to `Paper of PartA2 `_ + for more details. + + Args: + num_sampled_points (int, optional): Number of samples in each roi. + Default: 512. + """ + + def __init__(self, num_sampled_points=512): + super().__init__() + self.num_sampled_points = num_sampled_points + + def forward(self, points, point_features, boxes3d): + """ + Args: + points (torch.Tensor): Input points whose shape is (B, N, C). + point_features (torch.Tensor): Features of input points whose shape + is (B, N, C). + boxes3d (B, M, 7), Input bounding boxes whose shape is (B, M, 7). + + Returns: + pooled_features (torch.Tensor): The output pooled features whose + shape is (B, M, 512, 3 + C). + pooled_empty_flag (torch.Tensor): Empty flag whose shape is (B, M). + """ + return RoIPointPool3dFunction.apply(points, point_features, boxes3d, + self.num_sampled_points) + + +class RoIPointPool3dFunction(Function): + + @staticmethod + def forward(ctx, points, point_features, boxes3d, num_sampled_points=512): + """ + Args: + points (torch.Tensor): Input points whose shape is (B, N, C). + point_features (torch.Tensor): Features of input points whose shape + is (B, N, C). + boxes3d (B, M, 7), Input bounding boxes whose shape is (B, M, 7). + num_sampled_points (int, optional): The num of sampled points. + Default: 512. + + Returns: + pooled_features (torch.Tensor): The output pooled features whose + shape is (B, M, 512, 3 + C). + pooled_empty_flag (torch.Tensor): Empty flag whose shape is (B, M). + """ + assert len(points.shape) == 3 and points.shape[2] == 3 + batch_size, boxes_num, feature_len = points.shape[0], boxes3d.shape[ + 1], point_features.shape[2] + pooled_boxes3d = boxes3d.view(batch_size, -1, 7) + pooled_features = point_features.new_zeros( + (batch_size, boxes_num, num_sampled_points, 3 + feature_len)) + pooled_empty_flag = point_features.new_zeros( + (batch_size, boxes_num)).int() + + ext_module.roipoint_pool3d_forward(points.contiguous(), + pooled_boxes3d.contiguous(), + point_features.contiguous(), + pooled_features, pooled_empty_flag) + + return pooled_features, pooled_empty_flag + + @staticmethod + def backward(ctx, grad_out): + raise NotImplementedError diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/saconv.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/saconv.py new file mode 100644 index 0000000000000000000000000000000000000000..9d7be88c428ea2b9af2c32c60a86dddd13988ce8 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/saconv.py @@ -0,0 +1,145 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.nn as nn +import torch.nn.functional as F + +from annotator.mmpkg.mmcv.cnn import CONV_LAYERS, ConvAWS2d, constant_init +from annotator.mmpkg.mmcv.ops.deform_conv import deform_conv2d +from annotator.mmpkg.mmcv.utils import TORCH_VERSION, digit_version + + +@CONV_LAYERS.register_module(name='SAC') +class SAConv2d(ConvAWS2d): + """SAC (Switchable Atrous Convolution) + + This is an implementation of SAC in DetectoRS + (https://arxiv.org/pdf/2006.02334.pdf). + + Args: + in_channels (int): Number of channels in the input image + out_channels (int): Number of channels produced by the convolution + kernel_size (int or tuple): Size of the convolving kernel + stride (int or tuple, optional): Stride of the convolution. Default: 1 + padding (int or tuple, optional): Zero-padding added to both sides of + the input. Default: 0 + padding_mode (string, optional): ``'zeros'``, ``'reflect'``, + ``'replicate'`` or ``'circular'``. Default: ``'zeros'`` + dilation (int or tuple, optional): Spacing between kernel elements. + Default: 1 + groups (int, optional): Number of blocked connections from input + channels to output channels. Default: 1 + bias (bool, optional): If ``True``, adds a learnable bias to the + output. Default: ``True`` + use_deform: If ``True``, replace convolution with deformable + convolution. Default: ``False``. + """ + + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True, + use_deform=False): + super().__init__( + in_channels, + out_channels, + kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias=bias) + self.use_deform = use_deform + self.switch = nn.Conv2d( + self.in_channels, 1, kernel_size=1, stride=stride, bias=True) + self.weight_diff = nn.Parameter(torch.Tensor(self.weight.size())) + self.pre_context = nn.Conv2d( + self.in_channels, self.in_channels, kernel_size=1, bias=True) + self.post_context = nn.Conv2d( + self.out_channels, self.out_channels, kernel_size=1, bias=True) + if self.use_deform: + self.offset_s = nn.Conv2d( + self.in_channels, + 18, + kernel_size=3, + padding=1, + stride=stride, + bias=True) + self.offset_l = nn.Conv2d( + self.in_channels, + 18, + kernel_size=3, + padding=1, + stride=stride, + bias=True) + self.init_weights() + + def init_weights(self): + constant_init(self.switch, 0, bias=1) + self.weight_diff.data.zero_() + constant_init(self.pre_context, 0) + constant_init(self.post_context, 0) + if self.use_deform: + constant_init(self.offset_s, 0) + constant_init(self.offset_l, 0) + + def forward(self, x): + # pre-context + avg_x = F.adaptive_avg_pool2d(x, output_size=1) + avg_x = self.pre_context(avg_x) + avg_x = avg_x.expand_as(x) + x = x + avg_x + # switch + avg_x = F.pad(x, pad=(2, 2, 2, 2), mode='reflect') + avg_x = F.avg_pool2d(avg_x, kernel_size=5, stride=1, padding=0) + switch = self.switch(avg_x) + # sac + weight = self._get_weight(self.weight) + zero_bias = torch.zeros( + self.out_channels, device=weight.device, dtype=weight.dtype) + + if self.use_deform: + offset = self.offset_s(avg_x) + out_s = deform_conv2d(x, offset, weight, self.stride, self.padding, + self.dilation, self.groups, 1) + else: + if (TORCH_VERSION == 'parrots' + or digit_version(TORCH_VERSION) < digit_version('1.5.0')): + out_s = super().conv2d_forward(x, weight) + elif digit_version(TORCH_VERSION) >= digit_version('1.8.0'): + # bias is a required argument of _conv_forward in torch 1.8.0 + out_s = super()._conv_forward(x, weight, zero_bias) + else: + out_s = super()._conv_forward(x, weight) + ori_p = self.padding + ori_d = self.dilation + self.padding = tuple(3 * p for p in self.padding) + self.dilation = tuple(3 * d for d in self.dilation) + weight = weight + self.weight_diff + if self.use_deform: + offset = self.offset_l(avg_x) + out_l = deform_conv2d(x, offset, weight, self.stride, self.padding, + self.dilation, self.groups, 1) + else: + if (TORCH_VERSION == 'parrots' + or digit_version(TORCH_VERSION) < digit_version('1.5.0')): + out_l = super().conv2d_forward(x, weight) + elif digit_version(TORCH_VERSION) >= digit_version('1.8.0'): + # bias is a required argument of _conv_forward in torch 1.8.0 + out_l = super()._conv_forward(x, weight, zero_bias) + else: + out_l = super()._conv_forward(x, weight) + + out = switch * out_s + (1 - switch) * out_l + self.padding = ori_p + self.dilation = ori_d + # post-context + avg_x = F.adaptive_avg_pool2d(out, output_size=1) + avg_x = self.post_context(avg_x) + avg_x = avg_x.expand_as(out) + out = out + avg_x + return out diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/scatter_points.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/scatter_points.py new file mode 100644 index 0000000000000000000000000000000000000000..2b8aa4169e9f6ca4a6f845ce17d6d1e4db416bb8 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/scatter_points.py @@ -0,0 +1,135 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +from torch import nn +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', + ['dynamic_point_to_voxel_forward', 'dynamic_point_to_voxel_backward']) + + +class _DynamicScatter(Function): + + @staticmethod + def forward(ctx, feats, coors, reduce_type='max'): + """convert kitti points(N, >=3) to voxels. + + Args: + feats (torch.Tensor): [N, C]. Points features to be reduced + into voxels. + coors (torch.Tensor): [N, ndim]. Corresponding voxel coordinates + (specifically multi-dim voxel index) of each points. + reduce_type (str, optional): Reduce op. support 'max', 'sum' and + 'mean'. Default: 'max'. + + Returns: + voxel_feats (torch.Tensor): [M, C]. Reduced features, input + features that shares the same voxel coordinates are reduced to + one row. + voxel_coors (torch.Tensor): [M, ndim]. Voxel coordinates. + """ + results = ext_module.dynamic_point_to_voxel_forward( + feats, coors, reduce_type) + (voxel_feats, voxel_coors, point2voxel_map, + voxel_points_count) = results + ctx.reduce_type = reduce_type + ctx.save_for_backward(feats, voxel_feats, point2voxel_map, + voxel_points_count) + ctx.mark_non_differentiable(voxel_coors) + return voxel_feats, voxel_coors + + @staticmethod + def backward(ctx, grad_voxel_feats, grad_voxel_coors=None): + (feats, voxel_feats, point2voxel_map, + voxel_points_count) = ctx.saved_tensors + grad_feats = torch.zeros_like(feats) + # TODO: whether to use index put or use cuda_backward + # To use index put, need point to voxel index + ext_module.dynamic_point_to_voxel_backward( + grad_feats, grad_voxel_feats.contiguous(), feats, voxel_feats, + point2voxel_map, voxel_points_count, ctx.reduce_type) + return grad_feats, None, None + + +dynamic_scatter = _DynamicScatter.apply + + +class DynamicScatter(nn.Module): + """Scatters points into voxels, used in the voxel encoder with dynamic + voxelization. + + Note: + The CPU and GPU implementation get the same output, but have numerical + difference after summation and division (e.g., 5e-7). + + Args: + voxel_size (list): list [x, y, z] size of three dimension. + point_cloud_range (list): The coordinate range of points, [x_min, + y_min, z_min, x_max, y_max, z_max]. + average_points (bool): whether to use avg pooling to scatter points + into voxel. + """ + + def __init__(self, voxel_size, point_cloud_range, average_points: bool): + super().__init__() + + self.voxel_size = voxel_size + self.point_cloud_range = point_cloud_range + self.average_points = average_points + + def forward_single(self, points, coors): + """Scatters points into voxels. + + Args: + points (torch.Tensor): Points to be reduced into voxels. + coors (torch.Tensor): Corresponding voxel coordinates (specifically + multi-dim voxel index) of each points. + + Returns: + voxel_feats (torch.Tensor): Reduced features, input features that + shares the same voxel coordinates are reduced to one row. + voxel_coors (torch.Tensor): Voxel coordinates. + """ + reduce = 'mean' if self.average_points else 'max' + return dynamic_scatter(points.contiguous(), coors.contiguous(), reduce) + + def forward(self, points, coors): + """Scatters points/features into voxels. + + Args: + points (torch.Tensor): Points to be reduced into voxels. + coors (torch.Tensor): Corresponding voxel coordinates (specifically + multi-dim voxel index) of each points. + + Returns: + voxel_feats (torch.Tensor): Reduced features, input features that + shares the same voxel coordinates are reduced to one row. + voxel_coors (torch.Tensor): Voxel coordinates. + """ + if coors.size(-1) == 3: + return self.forward_single(points, coors) + else: + batch_size = coors[-1, 0] + 1 + voxels, voxel_coors = [], [] + for i in range(batch_size): + inds = torch.where(coors[:, 0] == i) + voxel, voxel_coor = self.forward_single( + points[inds], coors[inds][:, 1:]) + coor_pad = nn.functional.pad( + voxel_coor, (1, 0), mode='constant', value=i) + voxel_coors.append(coor_pad) + voxels.append(voxel) + features = torch.cat(voxels, dim=0) + feature_coors = torch.cat(voxel_coors, dim=0) + + return features, feature_coors + + def __repr__(self): + s = self.__class__.__name__ + '(' + s += 'voxel_size=' + str(self.voxel_size) + s += ', point_cloud_range=' + str(self.point_cloud_range) + s += ', average_points=' + str(self.average_points) + s += ')' + return s diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/sync_bn.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/sync_bn.py new file mode 100644 index 0000000000000000000000000000000000000000..46db9200f9eafbad662a04e71f60a099a3178346 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/sync_bn.py @@ -0,0 +1,279 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.distributed as dist +import torch.nn.functional as F +from torch.autograd import Function +from torch.autograd.function import once_differentiable +from torch.nn.modules.module import Module +from torch.nn.parameter import Parameter + +from annotator.mmpkg.mmcv.cnn import NORM_LAYERS +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', [ + 'sync_bn_forward_mean', 'sync_bn_forward_var', 'sync_bn_forward_output', + 'sync_bn_backward_param', 'sync_bn_backward_data' +]) + + +class SyncBatchNormFunction(Function): + + @staticmethod + def symbolic(g, input, running_mean, running_var, weight, bias, momentum, + eps, group, group_size, stats_mode): + return g.op( + 'mmcv::MMCVSyncBatchNorm', + input, + running_mean, + running_var, + weight, + bias, + momentum_f=momentum, + eps_f=eps, + group_i=group, + group_size_i=group_size, + stats_mode=stats_mode) + + @staticmethod + def forward(self, input, running_mean, running_var, weight, bias, momentum, + eps, group, group_size, stats_mode): + self.momentum = momentum + self.eps = eps + self.group = group + self.group_size = group_size + self.stats_mode = stats_mode + + assert isinstance( + input, (torch.HalfTensor, torch.FloatTensor, + torch.cuda.HalfTensor, torch.cuda.FloatTensor)), \ + f'only support Half or Float Tensor, but {input.type()}' + output = torch.zeros_like(input) + input3d = input.flatten(start_dim=2) + output3d = output.view_as(input3d) + num_channels = input3d.size(1) + + # ensure mean/var/norm/std are initialized as zeros + # ``torch.empty()`` does not guarantee that + mean = torch.zeros( + num_channels, dtype=torch.float, device=input3d.device) + var = torch.zeros( + num_channels, dtype=torch.float, device=input3d.device) + norm = torch.zeros_like( + input3d, dtype=torch.float, device=input3d.device) + std = torch.zeros( + num_channels, dtype=torch.float, device=input3d.device) + + batch_size = input3d.size(0) + if batch_size > 0: + ext_module.sync_bn_forward_mean(input3d, mean) + batch_flag = torch.ones([1], device=mean.device, dtype=mean.dtype) + else: + # skip updating mean and leave it as zeros when the input is empty + batch_flag = torch.zeros([1], device=mean.device, dtype=mean.dtype) + + # synchronize mean and the batch flag + vec = torch.cat([mean, batch_flag]) + if self.stats_mode == 'N': + vec *= batch_size + if self.group_size > 1: + dist.all_reduce(vec, group=self.group) + total_batch = vec[-1].detach() + mean = vec[:num_channels] + + if self.stats_mode == 'default': + mean = mean / self.group_size + elif self.stats_mode == 'N': + mean = mean / total_batch.clamp(min=1) + else: + raise NotImplementedError + + # leave var as zeros when the input is empty + if batch_size > 0: + ext_module.sync_bn_forward_var(input3d, mean, var) + + if self.stats_mode == 'N': + var *= batch_size + if self.group_size > 1: + dist.all_reduce(var, group=self.group) + + if self.stats_mode == 'default': + var /= self.group_size + elif self.stats_mode == 'N': + var /= total_batch.clamp(min=1) + else: + raise NotImplementedError + + # if the total batch size over all the ranks is zero, + # we should not update the statistics in the current batch + update_flag = total_batch.clamp(max=1) + momentum = update_flag * self.momentum + ext_module.sync_bn_forward_output( + input3d, + mean, + var, + weight, + bias, + running_mean, + running_var, + norm, + std, + output3d, + eps=self.eps, + momentum=momentum, + group_size=self.group_size) + self.save_for_backward(norm, std, weight) + return output + + @staticmethod + @once_differentiable + def backward(self, grad_output): + norm, std, weight = self.saved_tensors + grad_weight = torch.zeros_like(weight) + grad_bias = torch.zeros_like(weight) + grad_input = torch.zeros_like(grad_output) + grad_output3d = grad_output.flatten(start_dim=2) + grad_input3d = grad_input.view_as(grad_output3d) + + batch_size = grad_input3d.size(0) + if batch_size > 0: + ext_module.sync_bn_backward_param(grad_output3d, norm, grad_weight, + grad_bias) + + # all reduce + if self.group_size > 1: + dist.all_reduce(grad_weight, group=self.group) + dist.all_reduce(grad_bias, group=self.group) + grad_weight /= self.group_size + grad_bias /= self.group_size + + if batch_size > 0: + ext_module.sync_bn_backward_data(grad_output3d, weight, + grad_weight, grad_bias, norm, std, + grad_input3d) + + return grad_input, None, None, grad_weight, grad_bias, \ + None, None, None, None, None + + +@NORM_LAYERS.register_module(name='MMSyncBN') +class SyncBatchNorm(Module): + """Synchronized Batch Normalization. + + Args: + num_features (int): number of features/chennels in input tensor + eps (float, optional): a value added to the denominator for numerical + stability. Defaults to 1e-5. + momentum (float, optional): the value used for the running_mean and + running_var computation. Defaults to 0.1. + affine (bool, optional): whether to use learnable affine parameters. + Defaults to True. + track_running_stats (bool, optional): whether to track the running + mean and variance during training. When set to False, this + module does not track such statistics, and initializes statistics + buffers ``running_mean`` and ``running_var`` as ``None``. When + these buffers are ``None``, this module always uses batch + statistics in both training and eval modes. Defaults to True. + group (int, optional): synchronization of stats happen within + each process group individually. By default it is synchronization + across the whole world. Defaults to None. + stats_mode (str, optional): The statistical mode. Available options + includes ``'default'`` and ``'N'``. Defaults to 'default'. + When ``stats_mode=='default'``, it computes the overall statistics + using those from each worker with equal weight, i.e., the + statistics are synchronized and simply divied by ``group``. This + mode will produce inaccurate statistics when empty tensors occur. + When ``stats_mode=='N'``, it compute the overall statistics using + the total number of batches in each worker ignoring the number of + group, i.e., the statistics are synchronized and then divied by + the total batch ``N``. This mode is beneficial when empty tensors + occur during training, as it average the total mean by the real + number of batch. + """ + + def __init__(self, + num_features, + eps=1e-5, + momentum=0.1, + affine=True, + track_running_stats=True, + group=None, + stats_mode='default'): + super(SyncBatchNorm, self).__init__() + self.num_features = num_features + self.eps = eps + self.momentum = momentum + self.affine = affine + self.track_running_stats = track_running_stats + group = dist.group.WORLD if group is None else group + self.group = group + self.group_size = dist.get_world_size(group) + assert stats_mode in ['default', 'N'], \ + f'"stats_mode" only accepts "default" and "N", got "{stats_mode}"' + self.stats_mode = stats_mode + if self.affine: + self.weight = Parameter(torch.Tensor(num_features)) + self.bias = Parameter(torch.Tensor(num_features)) + else: + self.register_parameter('weight', None) + self.register_parameter('bias', None) + if self.track_running_stats: + self.register_buffer('running_mean', torch.zeros(num_features)) + self.register_buffer('running_var', torch.ones(num_features)) + self.register_buffer('num_batches_tracked', + torch.tensor(0, dtype=torch.long)) + else: + self.register_buffer('running_mean', None) + self.register_buffer('running_var', None) + self.register_buffer('num_batches_tracked', None) + self.reset_parameters() + + def reset_running_stats(self): + if self.track_running_stats: + self.running_mean.zero_() + self.running_var.fill_(1) + self.num_batches_tracked.zero_() + + def reset_parameters(self): + self.reset_running_stats() + if self.affine: + self.weight.data.uniform_() # pytorch use ones_() + self.bias.data.zero_() + + def forward(self, input): + if input.dim() < 2: + raise ValueError( + f'expected at least 2D input, got {input.dim()}D input') + if self.momentum is None: + exponential_average_factor = 0.0 + else: + exponential_average_factor = self.momentum + + if self.training and self.track_running_stats: + if self.num_batches_tracked is not None: + self.num_batches_tracked += 1 + if self.momentum is None: # use cumulative moving average + exponential_average_factor = 1.0 / float( + self.num_batches_tracked) + else: # use exponential moving average + exponential_average_factor = self.momentum + + if self.training or not self.track_running_stats: + return SyncBatchNormFunction.apply( + input, self.running_mean, self.running_var, self.weight, + self.bias, exponential_average_factor, self.eps, self.group, + self.group_size, self.stats_mode) + else: + return F.batch_norm(input, self.running_mean, self.running_var, + self.weight, self.bias, False, + exponential_average_factor, self.eps) + + def __repr__(self): + s = self.__class__.__name__ + s += f'({self.num_features}, ' + s += f'eps={self.eps}, ' + s += f'momentum={self.momentum}, ' + s += f'affine={self.affine}, ' + s += f'track_running_stats={self.track_running_stats}, ' + s += f'group_size={self.group_size},' + s += f'stats_mode={self.stats_mode})' + return s diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/three_interpolate.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/three_interpolate.py new file mode 100644 index 0000000000000000000000000000000000000000..203f47f05d58087e034fb3cd8cd6a09233947b4a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/three_interpolate.py @@ -0,0 +1,68 @@ +from typing import Tuple + +import torch +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', ['three_interpolate_forward', 'three_interpolate_backward']) + + +class ThreeInterpolate(Function): + """Performs weighted linear interpolation on 3 features. + + Please refer to `Paper of PointNet++ `_ + for more details. + """ + + @staticmethod + def forward(ctx, features: torch.Tensor, indices: torch.Tensor, + weight: torch.Tensor) -> torch.Tensor: + """ + Args: + features (Tensor): (B, C, M) Features descriptors to be + interpolated + indices (Tensor): (B, n, 3) index three nearest neighbors + of the target features in features + weight (Tensor): (B, n, 3) weights of interpolation + + Returns: + Tensor: (B, C, N) tensor of the interpolated features + """ + assert features.is_contiguous() + assert indices.is_contiguous() + assert weight.is_contiguous() + + B, c, m = features.size() + n = indices.size(1) + ctx.three_interpolate_for_backward = (indices, weight, m) + output = torch.cuda.FloatTensor(B, c, n) + + ext_module.three_interpolate_forward( + features, indices, weight, output, b=B, c=c, m=m, n=n) + return output + + @staticmethod + def backward( + ctx, grad_out: torch.Tensor + ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + """ + Args: + grad_out (Tensor): (B, C, N) tensor with gradients of outputs + + Returns: + Tensor: (B, C, M) tensor with gradients of features + """ + idx, weight, m = ctx.three_interpolate_for_backward + B, c, n = grad_out.size() + + grad_features = torch.cuda.FloatTensor(B, c, m).zero_() + grad_out_data = grad_out.data.contiguous() + + ext_module.three_interpolate_backward( + grad_out_data, idx, weight, grad_features.data, b=B, c=c, n=n, m=m) + return grad_features, None, None + + +three_interpolate = ThreeInterpolate.apply diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/three_nn.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/three_nn.py new file mode 100644 index 0000000000000000000000000000000000000000..2b01047a129989cd5545a0a86f23a487f4a13ce1 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/three_nn.py @@ -0,0 +1,51 @@ +from typing import Tuple + +import torch +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', ['three_nn_forward']) + + +class ThreeNN(Function): + """Find the top-3 nearest neighbors of the target set from the source set. + + Please refer to `Paper of PointNet++ `_ + for more details. + """ + + @staticmethod + def forward(ctx, target: torch.Tensor, + source: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Args: + target (Tensor): shape (B, N, 3), points set that needs to + find the nearest neighbors. + source (Tensor): shape (B, M, 3), points set that is used + to find the nearest neighbors of points in target set. + + Returns: + Tensor: shape (B, N, 3), L2 distance of each point in target + set to their corresponding nearest neighbors. + """ + target = target.contiguous() + source = source.contiguous() + + B, N, _ = target.size() + m = source.size(1) + dist2 = torch.cuda.FloatTensor(B, N, 3) + idx = torch.cuda.IntTensor(B, N, 3) + + ext_module.three_nn_forward(target, source, dist2, idx, b=B, n=N, m=m) + if torch.__version__ != 'parrots': + ctx.mark_non_differentiable(idx) + + return torch.sqrt(dist2), idx + + @staticmethod + def backward(ctx, a=None, b=None): + return None, None + + +three_nn = ThreeNN.apply diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/tin_shift.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/tin_shift.py new file mode 100644 index 0000000000000000000000000000000000000000..472c9fcfe45a124e819b7ed5653e585f94a8811e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/tin_shift.py @@ -0,0 +1,68 @@ +# Copyright (c) OpenMMLab. All rights reserved. +# Code reference from "Temporal Interlacing Network" +# https://github.com/deepcs233/TIN/blob/master/cuda_shift/rtc_wrap.py +# Hao Shao, Shengju Qian, Yu Liu +# shaoh19@mails.tsinghua.edu.cn, sjqian@cse.cuhk.edu.hk, yuliu@ee.cuhk.edu.hk + +import torch +import torch.nn as nn +from torch.autograd import Function + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext('_ext', + ['tin_shift_forward', 'tin_shift_backward']) + + +class TINShiftFunction(Function): + + @staticmethod + def forward(ctx, input, shift): + C = input.size(2) + num_segments = shift.size(1) + if C // num_segments <= 0 or C % num_segments != 0: + raise ValueError('C should be a multiple of num_segments, ' + f'but got C={C} and num_segments={num_segments}.') + + ctx.save_for_backward(shift) + + out = torch.zeros_like(input) + ext_module.tin_shift_forward(input, shift, out) + + return out + + @staticmethod + def backward(ctx, grad_output): + + shift = ctx.saved_tensors[0] + data_grad_input = grad_output.new(*grad_output.size()).zero_() + shift_grad_input = shift.new(*shift.size()).zero_() + ext_module.tin_shift_backward(grad_output, shift, data_grad_input) + + return data_grad_input, shift_grad_input + + +tin_shift = TINShiftFunction.apply + + +class TINShift(nn.Module): + """Temporal Interlace Shift. + + Temporal Interlace shift is a differentiable temporal-wise frame shifting + which is proposed in "Temporal Interlacing Network" + + Please refer to https://arxiv.org/abs/2001.06499 for more details. + Code is modified from https://github.com/mit-han-lab/temporal-shift-module + """ + + def forward(self, input, shift): + """Perform temporal interlace shift. + + Args: + input (Tensor): Feature map with shape [N, num_segments, C, H * W]. + shift (Tensor): Shift tensor with shape [N, num_segments]. + + Returns: + Feature map after temporal interlace shift. + """ + return tin_shift(input, shift) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/upfirdn2d.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/upfirdn2d.py new file mode 100644 index 0000000000000000000000000000000000000000..751db20a344e1164748d8d4d8b2f775247925eab --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/upfirdn2d.py @@ -0,0 +1,330 @@ +# modified from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/upfirdn2d.py # noqa:E501 + +# Copyright (c) 2021, NVIDIA Corporation. All rights reserved. +# NVIDIA Source Code License for StyleGAN2 with Adaptive Discriminator +# Augmentation (ADA) +# ======================================================================= + +# 1. Definitions + +# "Licensor" means any person or entity that distributes its Work. + +# "Software" means the original work of authorship made available under +# this License. + +# "Work" means the Software and any additions to or derivative works of +# the Software that are made available under this License. + +# The terms "reproduce," "reproduction," "derivative works," and +# "distribution" have the meaning as provided under U.S. copyright law; +# provided, however, that for the purposes of this License, derivative +# works shall not include works that remain separable from, or merely +# link (or bind by name) to the interfaces of, the Work. + +# Works, including the Software, are "made available" under this License +# by including in or with the Work either (a) a copyright notice +# referencing the applicability of this License to the Work, or (b) a +# copy of this License. + +# 2. License Grants + +# 2.1 Copyright Grant. Subject to the terms and conditions of this +# License, each Licensor grants to you a perpetual, worldwide, +# non-exclusive, royalty-free, copyright license to reproduce, +# prepare derivative works of, publicly display, publicly perform, +# sublicense and distribute its Work and any resulting derivative +# works in any form. + +# 3. Limitations + +# 3.1 Redistribution. You may reproduce or distribute the Work only +# if (a) you do so under this License, (b) you include a complete +# copy of this License with your distribution, and (c) you retain +# without modification any copyright, patent, trademark, or +# attribution notices that are present in the Work. + +# 3.2 Derivative Works. You may specify that additional or different +# terms apply to the use, reproduction, and distribution of your +# derivative works of the Work ("Your Terms") only if (a) Your Terms +# provide that the use limitation in Section 3.3 applies to your +# derivative works, and (b) you identify the specific derivative +# works that are subject to Your Terms. Notwithstanding Your Terms, +# this License (including the redistribution requirements in Section +# 3.1) will continue to apply to the Work itself. + +# 3.3 Use Limitation. The Work and any derivative works thereof only +# may be used or intended for use non-commercially. Notwithstanding +# the foregoing, NVIDIA and its affiliates may use the Work and any +# derivative works commercially. As used herein, "non-commercially" +# means for research or evaluation purposes only. + +# 3.4 Patent Claims. If you bring or threaten to bring a patent claim +# against any Licensor (including any claim, cross-claim or +# counterclaim in a lawsuit) to enforce any patents that you allege +# are infringed by any Work, then your rights under this License from +# such Licensor (including the grant in Section 2.1) will terminate +# immediately. + +# 3.5 Trademarks. This License does not grant any rights to use any +# Licensor’s or its affiliates’ names, logos, or trademarks, except +# as necessary to reproduce the notices described in this License. + +# 3.6 Termination. If you violate any term of this License, then your +# rights under this License (including the grant in Section 2.1) will +# terminate immediately. + +# 4. Disclaimer of Warranty. + +# THE WORK IS PROVIDED "AS IS" WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WARRANTIES OR CONDITIONS OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR +# NON-INFRINGEMENT. YOU BEAR THE RISK OF UNDERTAKING ANY ACTIVITIES UNDER +# THIS LICENSE. + +# 5. Limitation of Liability. + +# EXCEPT AS PROHIBITED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL +# THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE +# SHALL ANY LICENSOR BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, +# INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF +# OR RELATED TO THIS LICENSE, THE USE OR INABILITY TO USE THE WORK +# (INCLUDING BUT NOT LIMITED TO LOSS OF GOODWILL, BUSINESS INTERRUPTION, +# LOST PROFITS OR DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY OTHER +# COMMERCIAL DAMAGES OR LOSSES), EVEN IF THE LICENSOR HAS BEEN ADVISED OF +# THE POSSIBILITY OF SUCH DAMAGES. + +# ======================================================================= + +import torch +from torch.autograd import Function +from torch.nn import functional as F + +from annotator.mmpkg.mmcv.utils import to_2tuple +from ..utils import ext_loader + +upfirdn2d_ext = ext_loader.load_ext('_ext', ['upfirdn2d']) + + +class UpFirDn2dBackward(Function): + + @staticmethod + def forward(ctx, grad_output, kernel, grad_kernel, up, down, pad, g_pad, + in_size, out_size): + + up_x, up_y = up + down_x, down_y = down + g_pad_x0, g_pad_x1, g_pad_y0, g_pad_y1 = g_pad + + grad_output = grad_output.reshape(-1, out_size[0], out_size[1], 1) + + grad_input = upfirdn2d_ext.upfirdn2d( + grad_output, + grad_kernel, + up_x=down_x, + up_y=down_y, + down_x=up_x, + down_y=up_y, + pad_x0=g_pad_x0, + pad_x1=g_pad_x1, + pad_y0=g_pad_y0, + pad_y1=g_pad_y1) + grad_input = grad_input.view(in_size[0], in_size[1], in_size[2], + in_size[3]) + + ctx.save_for_backward(kernel) + + pad_x0, pad_x1, pad_y0, pad_y1 = pad + + ctx.up_x = up_x + ctx.up_y = up_y + ctx.down_x = down_x + ctx.down_y = down_y + ctx.pad_x0 = pad_x0 + ctx.pad_x1 = pad_x1 + ctx.pad_y0 = pad_y0 + ctx.pad_y1 = pad_y1 + ctx.in_size = in_size + ctx.out_size = out_size + + return grad_input + + @staticmethod + def backward(ctx, gradgrad_input): + kernel, = ctx.saved_tensors + + gradgrad_input = gradgrad_input.reshape(-1, ctx.in_size[2], + ctx.in_size[3], 1) + + gradgrad_out = upfirdn2d_ext.upfirdn2d( + gradgrad_input, + kernel, + up_x=ctx.up_x, + up_y=ctx.up_y, + down_x=ctx.down_x, + down_y=ctx.down_y, + pad_x0=ctx.pad_x0, + pad_x1=ctx.pad_x1, + pad_y0=ctx.pad_y0, + pad_y1=ctx.pad_y1) + # gradgrad_out = gradgrad_out.view(ctx.in_size[0], ctx.out_size[0], + # ctx.out_size[1], ctx.in_size[3]) + gradgrad_out = gradgrad_out.view(ctx.in_size[0], ctx.in_size[1], + ctx.out_size[0], ctx.out_size[1]) + + return gradgrad_out, None, None, None, None, None, None, None, None + + +class UpFirDn2d(Function): + + @staticmethod + def forward(ctx, input, kernel, up, down, pad): + up_x, up_y = up + down_x, down_y = down + pad_x0, pad_x1, pad_y0, pad_y1 = pad + + kernel_h, kernel_w = kernel.shape + batch, channel, in_h, in_w = input.shape + ctx.in_size = input.shape + + input = input.reshape(-1, in_h, in_w, 1) + + ctx.save_for_backward(kernel, torch.flip(kernel, [0, 1])) + + out_h = (in_h * up_y + pad_y0 + pad_y1 - kernel_h) // down_y + 1 + out_w = (in_w * up_x + pad_x0 + pad_x1 - kernel_w) // down_x + 1 + ctx.out_size = (out_h, out_w) + + ctx.up = (up_x, up_y) + ctx.down = (down_x, down_y) + ctx.pad = (pad_x0, pad_x1, pad_y0, pad_y1) + + g_pad_x0 = kernel_w - pad_x0 - 1 + g_pad_y0 = kernel_h - pad_y0 - 1 + g_pad_x1 = in_w * up_x - out_w * down_x + pad_x0 - up_x + 1 + g_pad_y1 = in_h * up_y - out_h * down_y + pad_y0 - up_y + 1 + + ctx.g_pad = (g_pad_x0, g_pad_x1, g_pad_y0, g_pad_y1) + + out = upfirdn2d_ext.upfirdn2d( + input, + kernel, + up_x=up_x, + up_y=up_y, + down_x=down_x, + down_y=down_y, + pad_x0=pad_x0, + pad_x1=pad_x1, + pad_y0=pad_y0, + pad_y1=pad_y1) + # out = out.view(major, out_h, out_w, minor) + out = out.view(-1, channel, out_h, out_w) + + return out + + @staticmethod + def backward(ctx, grad_output): + kernel, grad_kernel = ctx.saved_tensors + + grad_input = UpFirDn2dBackward.apply( + grad_output, + kernel, + grad_kernel, + ctx.up, + ctx.down, + ctx.pad, + ctx.g_pad, + ctx.in_size, + ctx.out_size, + ) + + return grad_input, None, None, None, None + + +def upfirdn2d(input, kernel, up=1, down=1, pad=(0, 0)): + """UpFRIDn for 2d features. + + UpFIRDn is short for upsample, apply FIR filter and downsample. More + details can be found in: + https://www.mathworks.com/help/signal/ref/upfirdn.html + + Args: + input (Tensor): Tensor with shape of (n, c, h, w). + kernel (Tensor): Filter kernel. + up (int | tuple[int], optional): Upsampling factor. If given a number, + we will use this factor for the both height and width side. + Defaults to 1. + down (int | tuple[int], optional): Downsampling factor. If given a + number, we will use this factor for the both height and width side. + Defaults to 1. + pad (tuple[int], optional): Padding for tensors, (x_pad, y_pad) or + (x_pad_0, x_pad_1, y_pad_0, y_pad_1). Defaults to (0, 0). + + Returns: + Tensor: Tensor after UpFIRDn. + """ + if input.device.type == 'cpu': + if len(pad) == 2: + pad = (pad[0], pad[1], pad[0], pad[1]) + + up = to_2tuple(up) + + down = to_2tuple(down) + + out = upfirdn2d_native(input, kernel, up[0], up[1], down[0], down[1], + pad[0], pad[1], pad[2], pad[3]) + else: + _up = to_2tuple(up) + + _down = to_2tuple(down) + + if len(pad) == 4: + _pad = pad + elif len(pad) == 2: + _pad = (pad[0], pad[1], pad[0], pad[1]) + + out = UpFirDn2d.apply(input, kernel, _up, _down, _pad) + + return out + + +def upfirdn2d_native(input, kernel, up_x, up_y, down_x, down_y, pad_x0, pad_x1, + pad_y0, pad_y1): + _, channel, in_h, in_w = input.shape + input = input.reshape(-1, in_h, in_w, 1) + + _, in_h, in_w, minor = input.shape + kernel_h, kernel_w = kernel.shape + + out = input.view(-1, in_h, 1, in_w, 1, minor) + out = F.pad(out, [0, 0, 0, up_x - 1, 0, 0, 0, up_y - 1]) + out = out.view(-1, in_h * up_y, in_w * up_x, minor) + + out = F.pad( + out, + [0, 0, + max(pad_x0, 0), + max(pad_x1, 0), + max(pad_y0, 0), + max(pad_y1, 0)]) + out = out[:, + max(-pad_y0, 0):out.shape[1] - max(-pad_y1, 0), + max(-pad_x0, 0):out.shape[2] - max(-pad_x1, 0), :, ] + + out = out.permute(0, 3, 1, 2) + out = out.reshape( + [-1, 1, in_h * up_y + pad_y0 + pad_y1, in_w * up_x + pad_x0 + pad_x1]) + w = torch.flip(kernel, [0, 1]).view(1, 1, kernel_h, kernel_w) + out = F.conv2d(out, w) + out = out.reshape( + -1, + minor, + in_h * up_y + pad_y0 + pad_y1 - kernel_h + 1, + in_w * up_x + pad_x0 + pad_x1 - kernel_w + 1, + ) + out = out.permute(0, 2, 3, 1) + out = out[:, ::down_y, ::down_x, :] + + out_h = (in_h * up_y + pad_y0 + pad_y1 - kernel_h) // down_y + 1 + out_w = (in_w * up_x + pad_x0 + pad_x1 - kernel_w) // down_x + 1 + + return out.view(-1, channel, out_h, out_w) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/voxelize.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/voxelize.py new file mode 100644 index 0000000000000000000000000000000000000000..ca3226a4fbcbfe58490fa2ea8e1c16b531214121 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/ops/voxelize.py @@ -0,0 +1,132 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +from torch import nn +from torch.autograd import Function +from torch.nn.modules.utils import _pair + +from ..utils import ext_loader + +ext_module = ext_loader.load_ext( + '_ext', ['dynamic_voxelize_forward', 'hard_voxelize_forward']) + + +class _Voxelization(Function): + + @staticmethod + def forward(ctx, + points, + voxel_size, + coors_range, + max_points=35, + max_voxels=20000): + """Convert kitti points(N, >=3) to voxels. + + Args: + points (torch.Tensor): [N, ndim]. Points[:, :3] contain xyz points + and points[:, 3:] contain other information like reflectivity. + voxel_size (tuple or float): The size of voxel with the shape of + [3]. + coors_range (tuple or float): The coordinate range of voxel with + the shape of [6]. + max_points (int, optional): maximum points contained in a voxel. if + max_points=-1, it means using dynamic_voxelize. Default: 35. + max_voxels (int, optional): maximum voxels this function create. + for second, 20000 is a good choice. Users should shuffle points + before call this function because max_voxels may drop points. + Default: 20000. + + Returns: + voxels_out (torch.Tensor): Output voxels with the shape of [M, + max_points, ndim]. Only contain points and returned when + max_points != -1. + coors_out (torch.Tensor): Output coordinates with the shape of + [M, 3]. + num_points_per_voxel_out (torch.Tensor): Num points per voxel with + the shape of [M]. Only returned when max_points != -1. + """ + if max_points == -1 or max_voxels == -1: + coors = points.new_zeros(size=(points.size(0), 3), dtype=torch.int) + ext_module.dynamic_voxelize_forward(points, coors, voxel_size, + coors_range, 3) + return coors + else: + voxels = points.new_zeros( + size=(max_voxels, max_points, points.size(1))) + coors = points.new_zeros(size=(max_voxels, 3), dtype=torch.int) + num_points_per_voxel = points.new_zeros( + size=(max_voxels, ), dtype=torch.int) + voxel_num = ext_module.hard_voxelize_forward( + points, voxels, coors, num_points_per_voxel, voxel_size, + coors_range, max_points, max_voxels, 3) + # select the valid voxels + voxels_out = voxels[:voxel_num] + coors_out = coors[:voxel_num] + num_points_per_voxel_out = num_points_per_voxel[:voxel_num] + return voxels_out, coors_out, num_points_per_voxel_out + + +voxelization = _Voxelization.apply + + +class Voxelization(nn.Module): + """Convert kitti points(N, >=3) to voxels. + + Please refer to `PVCNN `_ for more + details. + + Args: + voxel_size (tuple or float): The size of voxel with the shape of [3]. + point_cloud_range (tuple or float): The coordinate range of voxel with + the shape of [6]. + max_num_points (int): maximum points contained in a voxel. if + max_points=-1, it means using dynamic_voxelize. + max_voxels (int, optional): maximum voxels this function create. + for second, 20000 is a good choice. Users should shuffle points + before call this function because max_voxels may drop points. + Default: 20000. + """ + + def __init__(self, + voxel_size, + point_cloud_range, + max_num_points, + max_voxels=20000): + super().__init__() + + self.voxel_size = voxel_size + self.point_cloud_range = point_cloud_range + self.max_num_points = max_num_points + if isinstance(max_voxels, tuple): + self.max_voxels = max_voxels + else: + self.max_voxels = _pair(max_voxels) + + point_cloud_range = torch.tensor( + point_cloud_range, dtype=torch.float32) + voxel_size = torch.tensor(voxel_size, dtype=torch.float32) + grid_size = (point_cloud_range[3:] - + point_cloud_range[:3]) / voxel_size + grid_size = torch.round(grid_size).long() + input_feat_shape = grid_size[:2] + self.grid_size = grid_size + # the origin shape is as [x-len, y-len, z-len] + # [w, h, d] -> [d, h, w] + self.pcd_shape = [*input_feat_shape, 1][::-1] + + def forward(self, input): + if self.training: + max_voxels = self.max_voxels[0] + else: + max_voxels = self.max_voxels[1] + + return voxelization(input, self.voxel_size, self.point_cloud_range, + self.max_num_points, max_voxels) + + def __repr__(self): + s = self.__class__.__name__ + '(' + s += 'voxel_size=' + str(self.voxel_size) + s += ', point_cloud_range=' + str(self.point_cloud_range) + s += ', max_num_points=' + str(self.max_num_points) + s += ', max_voxels=' + str(self.max_voxels) + s += ')' + return s diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2ed2c17ad357742e423beeaf4d35db03fe9af469 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .collate import collate +from .data_container import DataContainer +from .data_parallel import MMDataParallel +from .distributed import MMDistributedDataParallel +from .registry import MODULE_WRAPPERS +from .scatter_gather import scatter, scatter_kwargs +from .utils import is_module_wrapper + +__all__ = [ + 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', + 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/_functions.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/_functions.py new file mode 100644 index 0000000000000000000000000000000000000000..9b5a8a44483ab991411d07122b22a1d027e4be8e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/_functions.py @@ -0,0 +1,79 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +from torch.nn.parallel._functions import _get_stream + + +def scatter(input, devices, streams=None): + """Scatters tensor across multiple GPUs.""" + if streams is None: + streams = [None] * len(devices) + + if isinstance(input, list): + chunk_size = (len(input) - 1) // len(devices) + 1 + outputs = [ + scatter(input[i], [devices[i // chunk_size]], + [streams[i // chunk_size]]) for i in range(len(input)) + ] + return outputs + elif isinstance(input, torch.Tensor): + output = input.contiguous() + # TODO: copy to a pinned buffer first (if copying from CPU) + stream = streams[0] if output.numel() > 0 else None + if devices != [-1]: + with torch.cuda.device(devices[0]), torch.cuda.stream(stream): + output = output.cuda(devices[0], non_blocking=True) + else: + # unsqueeze the first dimension thus the tensor's shape is the + # same as those scattered with GPU. + output = output.unsqueeze(0) + return output + else: + raise Exception(f'Unknown type {type(input)}.') + + +def synchronize_stream(output, devices, streams): + if isinstance(output, list): + chunk_size = len(output) // len(devices) + for i in range(len(devices)): + for j in range(chunk_size): + synchronize_stream(output[i * chunk_size + j], [devices[i]], + [streams[i]]) + elif isinstance(output, torch.Tensor): + if output.numel() != 0: + with torch.cuda.device(devices[0]): + main_stream = torch.cuda.current_stream() + main_stream.wait_stream(streams[0]) + output.record_stream(main_stream) + else: + raise Exception(f'Unknown type {type(output)}.') + + +def get_input_device(input): + if isinstance(input, list): + for item in input: + input_device = get_input_device(item) + if input_device != -1: + return input_device + return -1 + elif isinstance(input, torch.Tensor): + return input.get_device() if input.is_cuda else -1 + else: + raise Exception(f'Unknown type {type(input)}.') + + +class Scatter: + + @staticmethod + def forward(target_gpus, input): + input_device = get_input_device(input) + streams = None + if input_device == -1 and target_gpus != [-1]: + # Perform CPU to GPU copies in a background stream + streams = [_get_stream(device) for device in target_gpus] + + outputs = scatter(input, target_gpus, streams) + # Synchronize with the copy stream + if streams is not None: + synchronize_stream(outputs, target_gpus, streams) + + return tuple(outputs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/collate.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/collate.py new file mode 100644 index 0000000000000000000000000000000000000000..ad749197df21b0d74297548be5f66a696adebf7f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/collate.py @@ -0,0 +1,84 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from collections.abc import Mapping, Sequence + +import torch +import torch.nn.functional as F +from torch.utils.data.dataloader import default_collate + +from .data_container import DataContainer + + +def collate(batch, samples_per_gpu=1): + """Puts each data field into a tensor/DataContainer with outer dimension + batch size. + + Extend default_collate to add support for + :type:`~mmcv.parallel.DataContainer`. There are 3 cases. + + 1. cpu_only = True, e.g., meta data + 2. cpu_only = False, stack = True, e.g., images tensors + 3. cpu_only = False, stack = False, e.g., gt bboxes + """ + + if not isinstance(batch, Sequence): + raise TypeError(f'{batch.dtype} is not supported.') + + if isinstance(batch[0], DataContainer): + stacked = [] + if batch[0].cpu_only: + for i in range(0, len(batch), samples_per_gpu): + stacked.append( + [sample.data for sample in batch[i:i + samples_per_gpu]]) + return DataContainer( + stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) + elif batch[0].stack: + for i in range(0, len(batch), samples_per_gpu): + assert isinstance(batch[i].data, torch.Tensor) + + if batch[i].pad_dims is not None: + ndim = batch[i].dim() + assert ndim > batch[i].pad_dims + max_shape = [0 for _ in range(batch[i].pad_dims)] + for dim in range(1, batch[i].pad_dims + 1): + max_shape[dim - 1] = batch[i].size(-dim) + for sample in batch[i:i + samples_per_gpu]: + for dim in range(0, ndim - batch[i].pad_dims): + assert batch[i].size(dim) == sample.size(dim) + for dim in range(1, batch[i].pad_dims + 1): + max_shape[dim - 1] = max(max_shape[dim - 1], + sample.size(-dim)) + padded_samples = [] + for sample in batch[i:i + samples_per_gpu]: + pad = [0 for _ in range(batch[i].pad_dims * 2)] + for dim in range(1, batch[i].pad_dims + 1): + pad[2 * dim - + 1] = max_shape[dim - 1] - sample.size(-dim) + padded_samples.append( + F.pad( + sample.data, pad, value=sample.padding_value)) + stacked.append(default_collate(padded_samples)) + elif batch[i].pad_dims is None: + stacked.append( + default_collate([ + sample.data + for sample in batch[i:i + samples_per_gpu] + ])) + else: + raise ValueError( + 'pad_dims should be either None or integers (1-3)') + + else: + for i in range(0, len(batch), samples_per_gpu): + stacked.append( + [sample.data for sample in batch[i:i + samples_per_gpu]]) + return DataContainer(stacked, batch[0].stack, batch[0].padding_value) + elif isinstance(batch[0], Sequence): + transposed = zip(*batch) + return [collate(samples, samples_per_gpu) for samples in transposed] + elif isinstance(batch[0], Mapping): + return { + key: collate([d[key] for d in batch], samples_per_gpu) + for key in batch[0] + } + else: + return default_collate(batch) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/data_container.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/data_container.py new file mode 100644 index 0000000000000000000000000000000000000000..cedb0d32a51a1f575a622b38de2cee3ab4757821 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/data_container.py @@ -0,0 +1,89 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import functools + +import torch + + +def assert_tensor_type(func): + + @functools.wraps(func) + def wrapper(*args, **kwargs): + if not isinstance(args[0].data, torch.Tensor): + raise AttributeError( + f'{args[0].__class__.__name__} has no attribute ' + f'{func.__name__} for type {args[0].datatype}') + return func(*args, **kwargs) + + return wrapper + + +class DataContainer: + """A container for any type of objects. + + Typically tensors will be stacked in the collate function and sliced along + some dimension in the scatter function. This behavior has some limitations. + 1. All tensors have to be the same size. + 2. Types are limited (numpy array or Tensor). + + We design `DataContainer` and `MMDataParallel` to overcome these + limitations. The behavior can be either of the following. + + - copy to GPU, pad all tensors to the same size and stack them + - copy to GPU without stacking + - leave the objects as is and pass it to the model + - pad_dims specifies the number of last few dimensions to do padding + """ + + def __init__(self, + data, + stack=False, + padding_value=0, + cpu_only=False, + pad_dims=2): + self._data = data + self._cpu_only = cpu_only + self._stack = stack + self._padding_value = padding_value + assert pad_dims in [None, 1, 2, 3] + self._pad_dims = pad_dims + + def __repr__(self): + return f'{self.__class__.__name__}({repr(self.data)})' + + def __len__(self): + return len(self._data) + + @property + def data(self): + return self._data + + @property + def datatype(self): + if isinstance(self.data, torch.Tensor): + return self.data.type() + else: + return type(self.data) + + @property + def cpu_only(self): + return self._cpu_only + + @property + def stack(self): + return self._stack + + @property + def padding_value(self): + return self._padding_value + + @property + def pad_dims(self): + return self._pad_dims + + @assert_tensor_type + def size(self, *args, **kwargs): + return self.data.size(*args, **kwargs) + + @assert_tensor_type + def dim(self): + return self.data.dim() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/data_parallel.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/data_parallel.py new file mode 100644 index 0000000000000000000000000000000000000000..79b5f69b654cf647dc7ae9174223781ab5c607d2 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/data_parallel.py @@ -0,0 +1,89 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from itertools import chain + +from torch.nn.parallel import DataParallel + +from .scatter_gather import scatter_kwargs + + +class MMDataParallel(DataParallel): + """The DataParallel module that supports DataContainer. + + MMDataParallel has two main differences with PyTorch DataParallel: + + - It supports a custom type :class:`DataContainer` which allows more + flexible control of input data during both GPU and CPU inference. + - It implement two more APIs ``train_step()`` and ``val_step()``. + + Args: + module (:class:`nn.Module`): Module to be encapsulated. + device_ids (list[int]): Device IDS of modules to be scattered to. + Defaults to None when GPU is not available. + output_device (str | int): Device ID for output. Defaults to None. + dim (int): Dimension used to scatter the data. Defaults to 0. + """ + + def __init__(self, *args, dim=0, **kwargs): + super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) + self.dim = dim + + def forward(self, *inputs, **kwargs): + """Override the original forward function. + + The main difference lies in the CPU inference where the data in + :class:`DataContainers` will still be gathered. + """ + if not self.device_ids: + # We add the following line thus the module could gather and + # convert data containers as those in GPU inference + inputs, kwargs = self.scatter(inputs, kwargs, [-1]) + return self.module(*inputs[0], **kwargs[0]) + else: + return super().forward(*inputs, **kwargs) + + def scatter(self, inputs, kwargs, device_ids): + return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) + + def train_step(self, *inputs, **kwargs): + if not self.device_ids: + # We add the following line thus the module could gather and + # convert data containers as those in GPU inference + inputs, kwargs = self.scatter(inputs, kwargs, [-1]) + return self.module.train_step(*inputs[0], **kwargs[0]) + + assert len(self.device_ids) == 1, \ + ('MMDataParallel only supports single GPU training, if you need to' + ' train with multiple GPUs, please use MMDistributedDataParallel' + 'instead.') + + for t in chain(self.module.parameters(), self.module.buffers()): + if t.device != self.src_device_obj: + raise RuntimeError( + 'module must have its parameters and buffers ' + f'on device {self.src_device_obj} (device_ids[0]) but ' + f'found one of them on device: {t.device}') + + inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) + return self.module.train_step(*inputs[0], **kwargs[0]) + + def val_step(self, *inputs, **kwargs): + if not self.device_ids: + # We add the following line thus the module could gather and + # convert data containers as those in GPU inference + inputs, kwargs = self.scatter(inputs, kwargs, [-1]) + return self.module.val_step(*inputs[0], **kwargs[0]) + + assert len(self.device_ids) == 1, \ + ('MMDataParallel only supports single GPU training, if you need to' + ' train with multiple GPUs, please use MMDistributedDataParallel' + ' instead.') + + for t in chain(self.module.parameters(), self.module.buffers()): + if t.device != self.src_device_obj: + raise RuntimeError( + 'module must have its parameters and buffers ' + f'on device {self.src_device_obj} (device_ids[0]) but ' + f'found one of them on device: {t.device}') + + inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) + return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/distributed.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/distributed.py new file mode 100644 index 0000000000000000000000000000000000000000..929c7a451a7443d715ab0cceef530c53eff44cb9 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/distributed.py @@ -0,0 +1,112 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +from torch.nn.parallel.distributed import (DistributedDataParallel, + _find_tensors) + +from annotator.mmpkg.mmcv import print_log +from annotator.mmpkg.mmcv.utils import TORCH_VERSION, digit_version +from .scatter_gather import scatter_kwargs + + +class MMDistributedDataParallel(DistributedDataParallel): + """The DDP module that supports DataContainer. + + MMDDP has two main differences with PyTorch DDP: + + - It supports a custom type :class:`DataContainer` which allows more + flexible control of input data. + - It implement two APIs ``train_step()`` and ``val_step()``. + """ + + def to_kwargs(self, inputs, kwargs, device_id): + # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 + # to move all tensors to device_id + return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) + + def scatter(self, inputs, kwargs, device_ids): + return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) + + def train_step(self, *inputs, **kwargs): + """train_step() API for module wrapped by DistributedDataParallel. + + This method is basically the same as + ``DistributedDataParallel.forward()``, while replacing + ``self.module.forward()`` with ``self.module.train_step()``. + It is compatible with PyTorch 1.1 - 1.5. + """ + + # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the + # end of backward to the beginning of forward. + if ('parrots' not in TORCH_VERSION + and digit_version(TORCH_VERSION) >= digit_version('1.7') + and self.reducer._rebuild_buckets()): + print_log( + 'Reducer buckets have been rebuilt in this iteration.', + logger='mmcv') + + if getattr(self, 'require_forward_param_sync', True): + self._sync_params() + if self.device_ids: + inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) + if len(self.device_ids) == 1: + output = self.module.train_step(*inputs[0], **kwargs[0]) + else: + outputs = self.parallel_apply( + self._module_copies[:len(inputs)], inputs, kwargs) + output = self.gather(outputs, self.output_device) + else: + output = self.module.train_step(*inputs, **kwargs) + + if torch.is_grad_enabled() and getattr( + self, 'require_backward_grad_sync', True): + if self.find_unused_parameters: + self.reducer.prepare_for_backward(list(_find_tensors(output))) + else: + self.reducer.prepare_for_backward([]) + else: + if ('parrots' not in TORCH_VERSION + and digit_version(TORCH_VERSION) > digit_version('1.2')): + self.require_forward_param_sync = False + return output + + def val_step(self, *inputs, **kwargs): + """val_step() API for module wrapped by DistributedDataParallel. + + This method is basically the same as + ``DistributedDataParallel.forward()``, while replacing + ``self.module.forward()`` with ``self.module.val_step()``. + It is compatible with PyTorch 1.1 - 1.5. + """ + # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the + # end of backward to the beginning of forward. + if ('parrots' not in TORCH_VERSION + and digit_version(TORCH_VERSION) >= digit_version('1.7') + and self.reducer._rebuild_buckets()): + print_log( + 'Reducer buckets have been rebuilt in this iteration.', + logger='mmcv') + + if getattr(self, 'require_forward_param_sync', True): + self._sync_params() + if self.device_ids: + inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) + if len(self.device_ids) == 1: + output = self.module.val_step(*inputs[0], **kwargs[0]) + else: + outputs = self.parallel_apply( + self._module_copies[:len(inputs)], inputs, kwargs) + output = self.gather(outputs, self.output_device) + else: + output = self.module.val_step(*inputs, **kwargs) + + if torch.is_grad_enabled() and getattr( + self, 'require_backward_grad_sync', True): + if self.find_unused_parameters: + self.reducer.prepare_for_backward(list(_find_tensors(output))) + else: + self.reducer.prepare_for_backward([]) + else: + if ('parrots' not in TORCH_VERSION + and digit_version(TORCH_VERSION) > digit_version('1.2')): + self.require_forward_param_sync = False + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/distributed_deprecated.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/distributed_deprecated.py new file mode 100644 index 0000000000000000000000000000000000000000..be60a37041fc6a76deae1851dde30448eaff054f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/distributed_deprecated.py @@ -0,0 +1,70 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +import torch.distributed as dist +import torch.nn as nn +from torch._utils import (_flatten_dense_tensors, _take_tensors, + _unflatten_dense_tensors) + +from annotator.mmpkg.mmcv.utils import TORCH_VERSION, digit_version +from .registry import MODULE_WRAPPERS +from .scatter_gather import scatter_kwargs + + +@MODULE_WRAPPERS.register_module() +class MMDistributedDataParallel(nn.Module): + + def __init__(self, + module, + dim=0, + broadcast_buffers=True, + bucket_cap_mb=25): + super(MMDistributedDataParallel, self).__init__() + self.module = module + self.dim = dim + self.broadcast_buffers = broadcast_buffers + + self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 + self._sync_params() + + def _dist_broadcast_coalesced(self, tensors, buffer_size): + for tensors in _take_tensors(tensors, buffer_size): + flat_tensors = _flatten_dense_tensors(tensors) + dist.broadcast(flat_tensors, 0) + for tensor, synced in zip( + tensors, _unflatten_dense_tensors(flat_tensors, tensors)): + tensor.copy_(synced) + + def _sync_params(self): + module_states = list(self.module.state_dict().values()) + if len(module_states) > 0: + self._dist_broadcast_coalesced(module_states, + self.broadcast_bucket_size) + if self.broadcast_buffers: + if (TORCH_VERSION != 'parrots' + and digit_version(TORCH_VERSION) < digit_version('1.0')): + buffers = [b.data for b in self.module._all_buffers()] + else: + buffers = [b.data for b in self.module.buffers()] + if len(buffers) > 0: + self._dist_broadcast_coalesced(buffers, + self.broadcast_bucket_size) + + def scatter(self, inputs, kwargs, device_ids): + return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) + + def forward(self, *inputs, **kwargs): + inputs, kwargs = self.scatter(inputs, kwargs, + [torch.cuda.current_device()]) + return self.module(*inputs[0], **kwargs[0]) + + def train_step(self, *inputs, **kwargs): + inputs, kwargs = self.scatter(inputs, kwargs, + [torch.cuda.current_device()]) + output = self.module.train_step(*inputs[0], **kwargs[0]) + return output + + def val_step(self, *inputs, **kwargs): + inputs, kwargs = self.scatter(inputs, kwargs, + [torch.cuda.current_device()]) + output = self.module.val_step(*inputs[0], **kwargs[0]) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/registry.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/registry.py new file mode 100644 index 0000000000000000000000000000000000000000..6ce151e5f890691e8b583e5d50b492801bae82bd --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/registry.py @@ -0,0 +1,8 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from torch.nn.parallel import DataParallel, DistributedDataParallel + +from annotator.mmpkg.mmcv.utils import Registry + +MODULE_WRAPPERS = Registry('module wrapper') +MODULE_WRAPPERS.register_module(module=DataParallel) +MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/scatter_gather.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/scatter_gather.py new file mode 100644 index 0000000000000000000000000000000000000000..900ff88566f8f14830590459dc4fd16d4b382e47 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/scatter_gather.py @@ -0,0 +1,59 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch +from torch.nn.parallel._functions import Scatter as OrigScatter + +from ._functions import Scatter +from .data_container import DataContainer + + +def scatter(inputs, target_gpus, dim=0): + """Scatter inputs to target gpus. + + The only difference from original :func:`scatter` is to add support for + :type:`~mmcv.parallel.DataContainer`. + """ + + def scatter_map(obj): + if isinstance(obj, torch.Tensor): + if target_gpus != [-1]: + return OrigScatter.apply(target_gpus, None, dim, obj) + else: + # for CPU inference we use self-implemented scatter + return Scatter.forward(target_gpus, obj) + if isinstance(obj, DataContainer): + if obj.cpu_only: + return obj.data + else: + return Scatter.forward(target_gpus, obj.data) + if isinstance(obj, tuple) and len(obj) > 0: + return list(zip(*map(scatter_map, obj))) + if isinstance(obj, list) and len(obj) > 0: + out = list(map(list, zip(*map(scatter_map, obj)))) + return out + if isinstance(obj, dict) and len(obj) > 0: + out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) + return out + return [obj for targets in target_gpus] + + # After scatter_map is called, a scatter_map cell will exist. This cell + # has a reference to the actual function scatter_map, which has references + # to a closure that has a reference to the scatter_map cell (because the + # fn is recursive). To avoid this reference cycle, we set the function to + # None, clearing the cell + try: + return scatter_map(inputs) + finally: + scatter_map = None + + +def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): + """Scatter with support for kwargs dictionary.""" + inputs = scatter(inputs, target_gpus, dim) if inputs else [] + kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] + if len(inputs) < len(kwargs): + inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) + elif len(kwargs) < len(inputs): + kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) + inputs = tuple(inputs) + kwargs = tuple(kwargs) + return inputs, kwargs diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/utils.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..0f5712cb42c38a2e8563bf563efb6681383cab9b --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/parallel/utils.py @@ -0,0 +1,20 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .registry import MODULE_WRAPPERS + + +def is_module_wrapper(module): + """Check if a module is a module wrapper. + + The following 3 modules in MMCV (and their subclasses) are regarded as + module wrappers: DataParallel, DistributedDataParallel, + MMDistributedDataParallel (the deprecated version). You may add you own + module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. + + Args: + module (nn.Module): The module to be checked. + + Returns: + bool: True if the input module is a module wrapper. + """ + module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) + return isinstance(module, module_wrappers) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..52e4b48d383a84a055dcd7f6236f6e8e58eab924 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/__init__.py @@ -0,0 +1,47 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .base_module import BaseModule, ModuleList, Sequential +from .base_runner import BaseRunner +from .builder import RUNNERS, build_runner +from .checkpoint import (CheckpointLoader, _load_checkpoint, + _load_checkpoint_with_prefix, load_checkpoint, + load_state_dict, save_checkpoint, weights_to_cpu) +from .default_constructor import DefaultRunnerConstructor +from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, + init_dist, master_only) +from .epoch_based_runner import EpochBasedRunner, Runner +from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model +from .hooks import (HOOKS, CheckpointHook, ClosureHook, DistEvalHook, + DistSamplerSeedHook, DvcliveLoggerHook, EMAHook, EvalHook, + Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, + GradientCumulativeOptimizerHook, Hook, IterTimerHook, + LoggerHook, LrUpdaterHook, MlflowLoggerHook, + NeptuneLoggerHook, OptimizerHook, PaviLoggerHook, + SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, + WandbLoggerHook) +from .iter_based_runner import IterBasedRunner, IterLoader +from .log_buffer import LogBuffer +from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, + DefaultOptimizerConstructor, build_optimizer, + build_optimizer_constructor) +from .priority import Priority, get_priority +from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed + +__all__ = [ + 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', + 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', + 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', + 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', + 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', + 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', + 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', + 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', + 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', + 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', + 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', + 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', + 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', + 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', + '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', + 'ModuleList', 'GradientCumulativeOptimizerHook', + 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/base_module.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/base_module.py new file mode 100644 index 0000000000000000000000000000000000000000..72e1164dfc442056cdc386050177f011b4e9900f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/base_module.py @@ -0,0 +1,195 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import copy +import warnings +from abc import ABCMeta +from collections import defaultdict +from logging import FileHandler + +import torch.nn as nn + +from annotator.mmpkg.mmcv.runner.dist_utils import master_only +from annotator.mmpkg.mmcv.utils.logging import get_logger, logger_initialized, print_log + + +class BaseModule(nn.Module, metaclass=ABCMeta): + """Base module for all modules in openmmlab. + + ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional + functionality of parameter initialization. Compared with + ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. + + - ``init_cfg``: the config to control the initialization. + - ``init_weights``: The function of parameter + initialization and recording initialization + information. + - ``_params_init_info``: Used to track the parameter + initialization information. This attribute only + exists during executing the ``init_weights``. + + Args: + init_cfg (dict, optional): Initialization config dict. + """ + + def __init__(self, init_cfg=None): + """Initialize BaseModule, inherited from `torch.nn.Module`""" + + # NOTE init_cfg can be defined in different levels, but init_cfg + # in low levels has a higher priority. + + super(BaseModule, self).__init__() + # define default value of init_cfg instead of hard code + # in init_weights() function + self._is_init = False + + self.init_cfg = copy.deepcopy(init_cfg) + + # Backward compatibility in derived classes + # if pretrained is not None: + # warnings.warn('DeprecationWarning: pretrained is a deprecated \ + # key, please consider using init_cfg') + # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) + + @property + def is_init(self): + return self._is_init + + def init_weights(self): + """Initialize the weights.""" + + is_top_level_module = False + # check if it is top-level module + if not hasattr(self, '_params_init_info'): + # The `_params_init_info` is used to record the initialization + # information of the parameters + # the key should be the obj:`nn.Parameter` of model and the value + # should be a dict containing + # - init_info (str): The string that describes the initialization. + # - tmp_mean_value (FloatTensor): The mean of the parameter, + # which indicates whether the parameter has been modified. + # this attribute would be deleted after all parameters + # is initialized. + self._params_init_info = defaultdict(dict) + is_top_level_module = True + + # Initialize the `_params_init_info`, + # When detecting the `tmp_mean_value` of + # the corresponding parameter is changed, update related + # initialization information + for name, param in self.named_parameters(): + self._params_init_info[param][ + 'init_info'] = f'The value is the same before and ' \ + f'after calling `init_weights` ' \ + f'of {self.__class__.__name__} ' + self._params_init_info[param][ + 'tmp_mean_value'] = param.data.mean() + + # pass `params_init_info` to all submodules + # All submodules share the same `params_init_info`, + # so it will be updated when parameters are + # modified at any level of the model. + for sub_module in self.modules(): + sub_module._params_init_info = self._params_init_info + + # Get the initialized logger, if not exist, + # create a logger named `mmcv` + logger_names = list(logger_initialized.keys()) + logger_name = logger_names[0] if logger_names else 'mmcv' + + from ..cnn import initialize + from ..cnn.utils.weight_init import update_init_info + module_name = self.__class__.__name__ + if not self._is_init: + if self.init_cfg: + print_log( + f'initialize {module_name} with init_cfg {self.init_cfg}', + logger=logger_name) + initialize(self, self.init_cfg) + if isinstance(self.init_cfg, dict): + # prevent the parameters of + # the pre-trained model + # from being overwritten by + # the `init_weights` + if self.init_cfg['type'] == 'Pretrained': + return + + for m in self.children(): + if hasattr(m, 'init_weights'): + m.init_weights() + # users may overload the `init_weights` + update_init_info( + m, + init_info=f'Initialized by ' + f'user-defined `init_weights`' + f' in {m.__class__.__name__} ') + + self._is_init = True + else: + warnings.warn(f'init_weights of {self.__class__.__name__} has ' + f'been called more than once.') + + if is_top_level_module: + self._dump_init_info(logger_name) + + for sub_module in self.modules(): + del sub_module._params_init_info + + @master_only + def _dump_init_info(self, logger_name): + """Dump the initialization information to a file named + `initialization.log.json` in workdir. + + Args: + logger_name (str): The name of logger. + """ + + logger = get_logger(logger_name) + + with_file_handler = False + # dump the information to the logger file if there is a `FileHandler` + for handler in logger.handlers: + if isinstance(handler, FileHandler): + handler.stream.write( + 'Name of parameter - Initialization information\n') + for name, param in self.named_parameters(): + handler.stream.write( + f'\n{name} - {param.shape}: ' + f"\n{self._params_init_info[param]['init_info']} \n") + handler.stream.flush() + with_file_handler = True + if not with_file_handler: + for name, param in self.named_parameters(): + print_log( + f'\n{name} - {param.shape}: ' + f"\n{self._params_init_info[param]['init_info']} \n ", + logger=logger_name) + + def __repr__(self): + s = super().__repr__() + if self.init_cfg: + s += f'\ninit_cfg={self.init_cfg}' + return s + + +class Sequential(BaseModule, nn.Sequential): + """Sequential module in openmmlab. + + Args: + init_cfg (dict, optional): Initialization config dict. + """ + + def __init__(self, *args, init_cfg=None): + BaseModule.__init__(self, init_cfg) + nn.Sequential.__init__(self, *args) + + +class ModuleList(BaseModule, nn.ModuleList): + """ModuleList in openmmlab. + + Args: + modules (iterable, optional): an iterable of modules to add. + init_cfg (dict, optional): Initialization config dict. + """ + + def __init__(self, modules=None, init_cfg=None): + BaseModule.__init__(self, init_cfg) + nn.ModuleList.__init__(self, modules) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/base_runner.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/base_runner.py new file mode 100644 index 0000000000000000000000000000000000000000..a75a7d5db9f281fda10008636b24e2b98d9336a0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/base_runner.py @@ -0,0 +1,542 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import copy +import logging +import os.path as osp +import warnings +from abc import ABCMeta, abstractmethod + +import torch +from torch.optim import Optimizer + +import annotator.mmpkg.mmcv as mmcv +from ..parallel import is_module_wrapper +from .checkpoint import load_checkpoint +from .dist_utils import get_dist_info +from .hooks import HOOKS, Hook +from .log_buffer import LogBuffer +from .priority import Priority, get_priority +from .utils import get_time_str + + +class BaseRunner(metaclass=ABCMeta): + """The base class of Runner, a training helper for PyTorch. + + All subclasses should implement the following APIs: + + - ``run()`` + - ``train()`` + - ``val()`` + - ``save_checkpoint()`` + + Args: + model (:obj:`torch.nn.Module`): The model to be run. + batch_processor (callable): A callable method that process a data + batch. The interface of this method should be + `batch_processor(model, data, train_mode) -> dict` + optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an + optimizer (in most cases) or a dict of optimizers (in models that + requires more than one optimizer, e.g., GAN). + work_dir (str, optional): The working directory to save checkpoints + and logs. Defaults to None. + logger (:obj:`logging.Logger`): Logger used during training. + Defaults to None. (The default value is just for backward + compatibility) + meta (dict | None): A dict records some import information such as + environment info and seed, which will be logged in logger hook. + Defaults to None. + max_epochs (int, optional): Total training epochs. + max_iters (int, optional): Total training iterations. + """ + + def __init__(self, + model, + batch_processor=None, + optimizer=None, + work_dir=None, + logger=None, + meta=None, + max_iters=None, + max_epochs=None): + if batch_processor is not None: + if not callable(batch_processor): + raise TypeError('batch_processor must be callable, ' + f'but got {type(batch_processor)}') + warnings.warn('batch_processor is deprecated, please implement ' + 'train_step() and val_step() in the model instead.') + # raise an error is `batch_processor` is not None and + # `model.train_step()` exists. + if is_module_wrapper(model): + _model = model.module + else: + _model = model + if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): + raise RuntimeError( + 'batch_processor and model.train_step()/model.val_step() ' + 'cannot be both available.') + else: + assert hasattr(model, 'train_step') + + # check the type of `optimizer` + if isinstance(optimizer, dict): + for name, optim in optimizer.items(): + if not isinstance(optim, Optimizer): + raise TypeError( + f'optimizer must be a dict of torch.optim.Optimizers, ' + f'but optimizer["{name}"] is a {type(optim)}') + elif not isinstance(optimizer, Optimizer) and optimizer is not None: + raise TypeError( + f'optimizer must be a torch.optim.Optimizer object ' + f'or dict or None, but got {type(optimizer)}') + + # check the type of `logger` + if not isinstance(logger, logging.Logger): + raise TypeError(f'logger must be a logging.Logger object, ' + f'but got {type(logger)}') + + # check the type of `meta` + if meta is not None and not isinstance(meta, dict): + raise TypeError( + f'meta must be a dict or None, but got {type(meta)}') + + self.model = model + self.batch_processor = batch_processor + self.optimizer = optimizer + self.logger = logger + self.meta = meta + # create work_dir + if mmcv.is_str(work_dir): + self.work_dir = osp.abspath(work_dir) + mmcv.mkdir_or_exist(self.work_dir) + elif work_dir is None: + self.work_dir = None + else: + raise TypeError('"work_dir" must be a str or None') + + # get model name from the model class + if hasattr(self.model, 'module'): + self._model_name = self.model.module.__class__.__name__ + else: + self._model_name = self.model.__class__.__name__ + + self._rank, self._world_size = get_dist_info() + self.timestamp = get_time_str() + self.mode = None + self._hooks = [] + self._epoch = 0 + self._iter = 0 + self._inner_iter = 0 + + if max_epochs is not None and max_iters is not None: + raise ValueError( + 'Only one of `max_epochs` or `max_iters` can be set.') + + self._max_epochs = max_epochs + self._max_iters = max_iters + # TODO: Redesign LogBuffer, it is not flexible and elegant enough + self.log_buffer = LogBuffer() + + @property + def model_name(self): + """str: Name of the model, usually the module class name.""" + return self._model_name + + @property + def rank(self): + """int: Rank of current process. (distributed training)""" + return self._rank + + @property + def world_size(self): + """int: Number of processes participating in the job. + (distributed training)""" + return self._world_size + + @property + def hooks(self): + """list[:obj:`Hook`]: A list of registered hooks.""" + return self._hooks + + @property + def epoch(self): + """int: Current epoch.""" + return self._epoch + + @property + def iter(self): + """int: Current iteration.""" + return self._iter + + @property + def inner_iter(self): + """int: Iteration in an epoch.""" + return self._inner_iter + + @property + def max_epochs(self): + """int: Maximum training epochs.""" + return self._max_epochs + + @property + def max_iters(self): + """int: Maximum training iterations.""" + return self._max_iters + + @abstractmethod + def train(self): + pass + + @abstractmethod + def val(self): + pass + + @abstractmethod + def run(self, data_loaders, workflow, **kwargs): + pass + + @abstractmethod + def save_checkpoint(self, + out_dir, + filename_tmpl, + save_optimizer=True, + meta=None, + create_symlink=True): + pass + + def current_lr(self): + """Get current learning rates. + + Returns: + list[float] | dict[str, list[float]]: Current learning rates of all + param groups. If the runner has a dict of optimizers, this + method will return a dict. + """ + if isinstance(self.optimizer, torch.optim.Optimizer): + lr = [group['lr'] for group in self.optimizer.param_groups] + elif isinstance(self.optimizer, dict): + lr = dict() + for name, optim in self.optimizer.items(): + lr[name] = [group['lr'] for group in optim.param_groups] + else: + raise RuntimeError( + 'lr is not applicable because optimizer does not exist.') + return lr + + def current_momentum(self): + """Get current momentums. + + Returns: + list[float] | dict[str, list[float]]: Current momentums of all + param groups. If the runner has a dict of optimizers, this + method will return a dict. + """ + + def _get_momentum(optimizer): + momentums = [] + for group in optimizer.param_groups: + if 'momentum' in group.keys(): + momentums.append(group['momentum']) + elif 'betas' in group.keys(): + momentums.append(group['betas'][0]) + else: + momentums.append(0) + return momentums + + if self.optimizer is None: + raise RuntimeError( + 'momentum is not applicable because optimizer does not exist.') + elif isinstance(self.optimizer, torch.optim.Optimizer): + momentums = _get_momentum(self.optimizer) + elif isinstance(self.optimizer, dict): + momentums = dict() + for name, optim in self.optimizer.items(): + momentums[name] = _get_momentum(optim) + return momentums + + def register_hook(self, hook, priority='NORMAL'): + """Register a hook into the hook list. + + The hook will be inserted into a priority queue, with the specified + priority (See :class:`Priority` for details of priorities). + For hooks with the same priority, they will be triggered in the same + order as they are registered. + + Args: + hook (:obj:`Hook`): The hook to be registered. + priority (int or str or :obj:`Priority`): Hook priority. + Lower value means higher priority. + """ + assert isinstance(hook, Hook) + if hasattr(hook, 'priority'): + raise ValueError('"priority" is a reserved attribute for hooks') + priority = get_priority(priority) + hook.priority = priority + # insert the hook to a sorted list + inserted = False + for i in range(len(self._hooks) - 1, -1, -1): + if priority >= self._hooks[i].priority: + self._hooks.insert(i + 1, hook) + inserted = True + break + if not inserted: + self._hooks.insert(0, hook) + + def register_hook_from_cfg(self, hook_cfg): + """Register a hook from its cfg. + + Args: + hook_cfg (dict): Hook config. It should have at least keys 'type' + and 'priority' indicating its type and priority. + + Notes: + The specific hook class to register should not use 'type' and + 'priority' arguments during initialization. + """ + hook_cfg = hook_cfg.copy() + priority = hook_cfg.pop('priority', 'NORMAL') + hook = mmcv.build_from_cfg(hook_cfg, HOOKS) + self.register_hook(hook, priority=priority) + + def call_hook(self, fn_name): + """Call all hooks. + + Args: + fn_name (str): The function name in each hook to be called, such as + "before_train_epoch". + """ + for hook in self._hooks: + getattr(hook, fn_name)(self) + + def get_hook_info(self): + # Get hooks info in each stage + stage_hook_map = {stage: [] for stage in Hook.stages} + for hook in self.hooks: + try: + priority = Priority(hook.priority).name + except ValueError: + priority = hook.priority + classname = hook.__class__.__name__ + hook_info = f'({priority:<12}) {classname:<35}' + for trigger_stage in hook.get_triggered_stages(): + stage_hook_map[trigger_stage].append(hook_info) + + stage_hook_infos = [] + for stage in Hook.stages: + hook_infos = stage_hook_map[stage] + if len(hook_infos) > 0: + info = f'{stage}:\n' + info += '\n'.join(hook_infos) + info += '\n -------------------- ' + stage_hook_infos.append(info) + return '\n'.join(stage_hook_infos) + + def load_checkpoint(self, + filename, + map_location='cpu', + strict=False, + revise_keys=[(r'^module.', '')]): + return load_checkpoint( + self.model, + filename, + map_location, + strict, + self.logger, + revise_keys=revise_keys) + + def resume(self, + checkpoint, + resume_optimizer=True, + map_location='default'): + if map_location == 'default': + if torch.cuda.is_available(): + device_id = torch.cuda.current_device() + checkpoint = self.load_checkpoint( + checkpoint, + map_location=lambda storage, loc: storage.cuda(device_id)) + else: + checkpoint = self.load_checkpoint(checkpoint) + else: + checkpoint = self.load_checkpoint( + checkpoint, map_location=map_location) + + self._epoch = checkpoint['meta']['epoch'] + self._iter = checkpoint['meta']['iter'] + if self.meta is None: + self.meta = {} + self.meta.setdefault('hook_msgs', {}) + # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages + self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) + + # Re-calculate the number of iterations when resuming + # models with different number of GPUs + if 'config' in checkpoint['meta']: + config = mmcv.Config.fromstring( + checkpoint['meta']['config'], file_format='.py') + previous_gpu_ids = config.get('gpu_ids', None) + if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( + previous_gpu_ids) != self.world_size: + self._iter = int(self._iter * len(previous_gpu_ids) / + self.world_size) + self.logger.info('the iteration number is changed due to ' + 'change of GPU number') + + # resume meta information meta + self.meta = checkpoint['meta'] + + if 'optimizer' in checkpoint and resume_optimizer: + if isinstance(self.optimizer, Optimizer): + self.optimizer.load_state_dict(checkpoint['optimizer']) + elif isinstance(self.optimizer, dict): + for k in self.optimizer.keys(): + self.optimizer[k].load_state_dict( + checkpoint['optimizer'][k]) + else: + raise TypeError( + 'Optimizer should be dict or torch.optim.Optimizer ' + f'but got {type(self.optimizer)}') + + self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) + + def register_lr_hook(self, lr_config): + if lr_config is None: + return + elif isinstance(lr_config, dict): + assert 'policy' in lr_config + policy_type = lr_config.pop('policy') + # If the type of policy is all in lower case, e.g., 'cyclic', + # then its first letter will be capitalized, e.g., to be 'Cyclic'. + # This is for the convenient usage of Lr updater. + # Since this is not applicable for ` + # CosineAnnealingLrUpdater`, + # the string will not be changed if it contains capital letters. + if policy_type == policy_type.lower(): + policy_type = policy_type.title() + hook_type = policy_type + 'LrUpdaterHook' + lr_config['type'] = hook_type + hook = mmcv.build_from_cfg(lr_config, HOOKS) + else: + hook = lr_config + self.register_hook(hook, priority='VERY_HIGH') + + def register_momentum_hook(self, momentum_config): + if momentum_config is None: + return + if isinstance(momentum_config, dict): + assert 'policy' in momentum_config + policy_type = momentum_config.pop('policy') + # If the type of policy is all in lower case, e.g., 'cyclic', + # then its first letter will be capitalized, e.g., to be 'Cyclic'. + # This is for the convenient usage of momentum updater. + # Since this is not applicable for + # `CosineAnnealingMomentumUpdater`, + # the string will not be changed if it contains capital letters. + if policy_type == policy_type.lower(): + policy_type = policy_type.title() + hook_type = policy_type + 'MomentumUpdaterHook' + momentum_config['type'] = hook_type + hook = mmcv.build_from_cfg(momentum_config, HOOKS) + else: + hook = momentum_config + self.register_hook(hook, priority='HIGH') + + def register_optimizer_hook(self, optimizer_config): + if optimizer_config is None: + return + if isinstance(optimizer_config, dict): + optimizer_config.setdefault('type', 'OptimizerHook') + hook = mmcv.build_from_cfg(optimizer_config, HOOKS) + else: + hook = optimizer_config + self.register_hook(hook, priority='ABOVE_NORMAL') + + def register_checkpoint_hook(self, checkpoint_config): + if checkpoint_config is None: + return + if isinstance(checkpoint_config, dict): + checkpoint_config.setdefault('type', 'CheckpointHook') + hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) + else: + hook = checkpoint_config + self.register_hook(hook, priority='NORMAL') + + def register_logger_hooks(self, log_config): + if log_config is None: + return + log_interval = log_config['interval'] + for info in log_config['hooks']: + logger_hook = mmcv.build_from_cfg( + info, HOOKS, default_args=dict(interval=log_interval)) + self.register_hook(logger_hook, priority='VERY_LOW') + + def register_timer_hook(self, timer_config): + if timer_config is None: + return + if isinstance(timer_config, dict): + timer_config_ = copy.deepcopy(timer_config) + hook = mmcv.build_from_cfg(timer_config_, HOOKS) + else: + hook = timer_config + self.register_hook(hook, priority='LOW') + + def register_custom_hooks(self, custom_config): + if custom_config is None: + return + + if not isinstance(custom_config, list): + custom_config = [custom_config] + + for item in custom_config: + if isinstance(item, dict): + self.register_hook_from_cfg(item) + else: + self.register_hook(item, priority='NORMAL') + + def register_profiler_hook(self, profiler_config): + if profiler_config is None: + return + if isinstance(profiler_config, dict): + profiler_config.setdefault('type', 'ProfilerHook') + hook = mmcv.build_from_cfg(profiler_config, HOOKS) + else: + hook = profiler_config + self.register_hook(hook) + + def register_training_hooks(self, + lr_config, + optimizer_config=None, + checkpoint_config=None, + log_config=None, + momentum_config=None, + timer_config=dict(type='IterTimerHook'), + custom_hooks_config=None): + """Register default and custom hooks for training. + + Default and custom hooks include: + + +----------------------+-------------------------+ + | Hooks | Priority | + +======================+=========================+ + | LrUpdaterHook | VERY_HIGH (10) | + +----------------------+-------------------------+ + | MomentumUpdaterHook | HIGH (30) | + +----------------------+-------------------------+ + | OptimizerStepperHook | ABOVE_NORMAL (40) | + +----------------------+-------------------------+ + | CheckpointSaverHook | NORMAL (50) | + +----------------------+-------------------------+ + | IterTimerHook | LOW (70) | + +----------------------+-------------------------+ + | LoggerHook(s) | VERY_LOW (90) | + +----------------------+-------------------------+ + | CustomHook(s) | defaults to NORMAL (50) | + +----------------------+-------------------------+ + + If custom hooks have same priority with default hooks, custom hooks + will be triggered after default hooks. + """ + self.register_lr_hook(lr_config) + self.register_momentum_hook(momentum_config) + self.register_optimizer_hook(optimizer_config) + self.register_checkpoint_hook(checkpoint_config) + self.register_timer_hook(timer_config) + self.register_logger_hooks(log_config) + self.register_custom_hooks(custom_hooks_config) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/builder.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/builder.py new file mode 100644 index 0000000000000000000000000000000000000000..77c96ba0b2f30ead9da23f293c5dc84dd3e4a74f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/builder.py @@ -0,0 +1,24 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import copy + +from ..utils import Registry + +RUNNERS = Registry('runner') +RUNNER_BUILDERS = Registry('runner builder') + + +def build_runner_constructor(cfg): + return RUNNER_BUILDERS.build(cfg) + + +def build_runner(cfg, default_args=None): + runner_cfg = copy.deepcopy(cfg) + constructor_type = runner_cfg.pop('constructor', + 'DefaultRunnerConstructor') + runner_constructor = build_runner_constructor( + dict( + type=constructor_type, + runner_cfg=runner_cfg, + default_args=default_args)) + runner = runner_constructor() + return runner diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/checkpoint.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/checkpoint.py new file mode 100644 index 0000000000000000000000000000000000000000..d690be1dfe70b1b82eaac8fe4db7022b35d5426c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/checkpoint.py @@ -0,0 +1,707 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import io +import os +import os.path as osp +import pkgutil +import re +import time +import warnings +from collections import OrderedDict +from importlib import import_module +from tempfile import TemporaryDirectory + +import torch +import torchvision +from torch.optim import Optimizer +from torch.utils import model_zoo + +import annotator.mmpkg.mmcv as mmcv +from ..fileio import FileClient +from ..fileio import load as load_file +from ..parallel import is_module_wrapper +from ..utils import mkdir_or_exist +from .dist_utils import get_dist_info + +ENV_MMCV_HOME = 'MMCV_HOME' +ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' +DEFAULT_CACHE_DIR = '~/.cache' + + +def _get_mmcv_home(): + mmcv_home = os.path.expanduser( + os.getenv( + ENV_MMCV_HOME, + os.path.join( + os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) + + mkdir_or_exist(mmcv_home) + return mmcv_home + + +def load_state_dict(module, state_dict, strict=False, logger=None): + """Load state_dict to a module. + + This method is modified from :meth:`torch.nn.Module.load_state_dict`. + Default value for ``strict`` is set to ``False`` and the message for + param mismatch will be shown even if strict is False. + + Args: + module (Module): Module that receives the state_dict. + state_dict (OrderedDict): Weights. + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. + logger (:obj:`logging.Logger`, optional): Logger to log the error + message. If not specified, print function will be used. + """ + unexpected_keys = [] + all_missing_keys = [] + err_msg = [] + + metadata = getattr(state_dict, '_metadata', None) + state_dict = state_dict.copy() + if metadata is not None: + state_dict._metadata = metadata + + # use _load_from_state_dict to enable checkpoint version control + def load(module, prefix=''): + # recursively check parallel module in case that the model has a + # complicated structure, e.g., nn.Module(nn.Module(DDP)) + if is_module_wrapper(module): + module = module.module + local_metadata = {} if metadata is None else metadata.get( + prefix[:-1], {}) + module._load_from_state_dict(state_dict, prefix, local_metadata, True, + all_missing_keys, unexpected_keys, + err_msg) + for name, child in module._modules.items(): + if child is not None: + load(child, prefix + name + '.') + + load(module) + load = None # break load->load reference cycle + + # ignore "num_batches_tracked" of BN layers + missing_keys = [ + key for key in all_missing_keys if 'num_batches_tracked' not in key + ] + + if unexpected_keys: + err_msg.append('unexpected key in source ' + f'state_dict: {", ".join(unexpected_keys)}\n') + if missing_keys: + err_msg.append( + f'missing keys in source state_dict: {", ".join(missing_keys)}\n') + + rank, _ = get_dist_info() + if len(err_msg) > 0 and rank == 0: + err_msg.insert( + 0, 'The model and loaded state dict do not match exactly\n') + err_msg = '\n'.join(err_msg) + if strict: + raise RuntimeError(err_msg) + elif logger is not None: + logger.warning(err_msg) + else: + print(err_msg) + + +def get_torchvision_models(): + model_urls = dict() + for _, name, ispkg in pkgutil.walk_packages(torchvision.models.__path__): + if ispkg: + continue + _zoo = import_module(f'torchvision.models.{name}') + if hasattr(_zoo, 'model_urls'): + _urls = getattr(_zoo, 'model_urls') + model_urls.update(_urls) + return model_urls + + +def get_external_models(): + mmcv_home = _get_mmcv_home() + default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') + default_urls = load_file(default_json_path) + assert isinstance(default_urls, dict) + external_json_path = osp.join(mmcv_home, 'open_mmlab.json') + if osp.exists(external_json_path): + external_urls = load_file(external_json_path) + assert isinstance(external_urls, dict) + default_urls.update(external_urls) + + return default_urls + + +def get_mmcls_models(): + mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') + mmcls_urls = load_file(mmcls_json_path) + + return mmcls_urls + + +def get_deprecated_model_names(): + deprecate_json_path = osp.join(mmcv.__path__[0], + 'model_zoo/deprecated.json') + deprecate_urls = load_file(deprecate_json_path) + assert isinstance(deprecate_urls, dict) + + return deprecate_urls + + +def _process_mmcls_checkpoint(checkpoint): + state_dict = checkpoint['state_dict'] + new_state_dict = OrderedDict() + for k, v in state_dict.items(): + if k.startswith('backbone.'): + new_state_dict[k[9:]] = v + new_checkpoint = dict(state_dict=new_state_dict) + + return new_checkpoint + + +class CheckpointLoader: + """A general checkpoint loader to manage all schemes.""" + + _schemes = {} + + @classmethod + def _register_scheme(cls, prefixes, loader, force=False): + if isinstance(prefixes, str): + prefixes = [prefixes] + else: + assert isinstance(prefixes, (list, tuple)) + for prefix in prefixes: + if (prefix not in cls._schemes) or force: + cls._schemes[prefix] = loader + else: + raise KeyError( + f'{prefix} is already registered as a loader backend, ' + 'add "force=True" if you want to override it') + # sort, longer prefixes take priority + cls._schemes = OrderedDict( + sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) + + @classmethod + def register_scheme(cls, prefixes, loader=None, force=False): + """Register a loader to CheckpointLoader. + + This method can be used as a normal class method or a decorator. + + Args: + prefixes (str or list[str] or tuple[str]): + The prefix of the registered loader. + loader (function, optional): The loader function to be registered. + When this method is used as a decorator, loader is None. + Defaults to None. + force (bool, optional): Whether to override the loader + if the prefix has already been registered. Defaults to False. + """ + + if loader is not None: + cls._register_scheme(prefixes, loader, force=force) + return + + def _register(loader_cls): + cls._register_scheme(prefixes, loader_cls, force=force) + return loader_cls + + return _register + + @classmethod + def _get_checkpoint_loader(cls, path): + """Finds a loader that supports the given path. Falls back to the local + loader if no other loader is found. + + Args: + path (str): checkpoint path + + Returns: + loader (function): checkpoint loader + """ + + for p in cls._schemes: + if path.startswith(p): + return cls._schemes[p] + + @classmethod + def load_checkpoint(cls, filename, map_location=None, logger=None): + """load checkpoint through URL scheme path. + + Args: + filename (str): checkpoint file name with given prefix + map_location (str, optional): Same as :func:`torch.load`. + Default: None + logger (:mod:`logging.Logger`, optional): The logger for message. + Default: None + + Returns: + dict or OrderedDict: The loaded checkpoint. + """ + + checkpoint_loader = cls._get_checkpoint_loader(filename) + class_name = checkpoint_loader.__name__ + mmcv.print_log( + f'load checkpoint from {class_name[10:]} path: {filename}', logger) + return checkpoint_loader(filename, map_location) + + +@CheckpointLoader.register_scheme(prefixes='') +def load_from_local(filename, map_location): + """load checkpoint by local file path. + + Args: + filename (str): local checkpoint file path + map_location (str, optional): Same as :func:`torch.load`. + + Returns: + dict or OrderedDict: The loaded checkpoint. + """ + + if not osp.isfile(filename): + raise IOError(f'{filename} is not a checkpoint file') + checkpoint = torch.load(filename, map_location=map_location) + return checkpoint + + +@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) +def load_from_http(filename, map_location=None, model_dir=None): + """load checkpoint through HTTP or HTTPS scheme path. In distributed + setting, this function only download checkpoint at local rank 0. + + Args: + filename (str): checkpoint file path with modelzoo or + torchvision prefix + map_location (str, optional): Same as :func:`torch.load`. + model_dir (string, optional): directory in which to save the object, + Default: None + + Returns: + dict or OrderedDict: The loaded checkpoint. + """ + rank, world_size = get_dist_info() + rank = int(os.environ.get('LOCAL_RANK', rank)) + if rank == 0: + checkpoint = model_zoo.load_url( + filename, model_dir=model_dir, map_location=map_location) + if world_size > 1: + torch.distributed.barrier() + if rank > 0: + checkpoint = model_zoo.load_url( + filename, model_dir=model_dir, map_location=map_location) + return checkpoint + + +@CheckpointLoader.register_scheme(prefixes='pavi://') +def load_from_pavi(filename, map_location=None): + """load checkpoint through the file path prefixed with pavi. In distributed + setting, this function download ckpt at all ranks to different temporary + directories. + + Args: + filename (str): checkpoint file path with pavi prefix + map_location (str, optional): Same as :func:`torch.load`. + Default: None + + Returns: + dict or OrderedDict: The loaded checkpoint. + """ + assert filename.startswith('pavi://'), \ + f'Expected filename startswith `pavi://`, but get {filename}' + model_path = filename[7:] + + try: + from pavi import modelcloud + except ImportError: + raise ImportError( + 'Please install pavi to load checkpoint from modelcloud.') + + model = modelcloud.get(model_path) + with TemporaryDirectory() as tmp_dir: + downloaded_file = osp.join(tmp_dir, model.name) + model.download(downloaded_file) + checkpoint = torch.load(downloaded_file, map_location=map_location) + return checkpoint + + +@CheckpointLoader.register_scheme(prefixes='s3://') +def load_from_ceph(filename, map_location=None, backend='petrel'): + """load checkpoint through the file path prefixed with s3. In distributed + setting, this function download ckpt at all ranks to different temporary + directories. + + Args: + filename (str): checkpoint file path with s3 prefix + map_location (str, optional): Same as :func:`torch.load`. + backend (str, optional): The storage backend type. Options are 'ceph', + 'petrel'. Default: 'petrel'. + + .. warning:: + :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, + please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. + + Returns: + dict or OrderedDict: The loaded checkpoint. + """ + allowed_backends = ['ceph', 'petrel'] + if backend not in allowed_backends: + raise ValueError(f'Load from Backend {backend} is not supported.') + + if backend == 'ceph': + warnings.warn( + 'CephBackend will be deprecated, please use PetrelBackend instead') + + # CephClient and PetrelBackend have the same prefix 's3://' and the latter + # will be chosen as default. If PetrelBackend can not be instantiated + # successfully, the CephClient will be chosen. + try: + file_client = FileClient(backend=backend) + except ImportError: + allowed_backends.remove(backend) + file_client = FileClient(backend=allowed_backends[0]) + + with io.BytesIO(file_client.get(filename)) as buffer: + checkpoint = torch.load(buffer, map_location=map_location) + return checkpoint + + +@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) +def load_from_torchvision(filename, map_location=None): + """load checkpoint through the file path prefixed with modelzoo or + torchvision. + + Args: + filename (str): checkpoint file path with modelzoo or + torchvision prefix + map_location (str, optional): Same as :func:`torch.load`. + + Returns: + dict or OrderedDict: The loaded checkpoint. + """ + model_urls = get_torchvision_models() + if filename.startswith('modelzoo://'): + warnings.warn('The URL scheme of "modelzoo://" is deprecated, please ' + 'use "torchvision://" instead') + model_name = filename[11:] + else: + model_name = filename[14:] + return load_from_http(model_urls[model_name], map_location=map_location) + + +@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) +def load_from_openmmlab(filename, map_location=None): + """load checkpoint through the file path prefixed with open-mmlab or + openmmlab. + + Args: + filename (str): checkpoint file path with open-mmlab or + openmmlab prefix + map_location (str, optional): Same as :func:`torch.load`. + Default: None + + Returns: + dict or OrderedDict: The loaded checkpoint. + """ + + model_urls = get_external_models() + prefix_str = 'open-mmlab://' + if filename.startswith(prefix_str): + model_name = filename[13:] + else: + model_name = filename[12:] + prefix_str = 'openmmlab://' + + deprecated_urls = get_deprecated_model_names() + if model_name in deprecated_urls: + warnings.warn(f'{prefix_str}{model_name} is deprecated in favor ' + f'of {prefix_str}{deprecated_urls[model_name]}') + model_name = deprecated_urls[model_name] + model_url = model_urls[model_name] + # check if is url + if model_url.startswith(('http://', 'https://')): + checkpoint = load_from_http(model_url, map_location=map_location) + else: + filename = osp.join(_get_mmcv_home(), model_url) + if not osp.isfile(filename): + raise IOError(f'{filename} is not a checkpoint file') + checkpoint = torch.load(filename, map_location=map_location) + return checkpoint + + +@CheckpointLoader.register_scheme(prefixes='mmcls://') +def load_from_mmcls(filename, map_location=None): + """load checkpoint through the file path prefixed with mmcls. + + Args: + filename (str): checkpoint file path with mmcls prefix + map_location (str, optional): Same as :func:`torch.load`. + + Returns: + dict or OrderedDict: The loaded checkpoint. + """ + + model_urls = get_mmcls_models() + model_name = filename[8:] + checkpoint = load_from_http( + model_urls[model_name], map_location=map_location) + checkpoint = _process_mmcls_checkpoint(checkpoint) + return checkpoint + + +def _load_checkpoint(filename, map_location=None, logger=None): + """Load checkpoint from somewhere (modelzoo, file, url). + + Args: + filename (str): Accept local filepath, URL, ``torchvision://xxx``, + ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for + details. + map_location (str, optional): Same as :func:`torch.load`. + Default: None. + logger (:mod:`logging.Logger`, optional): The logger for error message. + Default: None + + Returns: + dict or OrderedDict: The loaded checkpoint. It can be either an + OrderedDict storing model weights or a dict containing other + information, which depends on the checkpoint. + """ + return CheckpointLoader.load_checkpoint(filename, map_location, logger) + + +def _load_checkpoint_with_prefix(prefix, filename, map_location=None): + """Load partial pretrained model with specific prefix. + + Args: + prefix (str): The prefix of sub-module. + filename (str): Accept local filepath, URL, ``torchvision://xxx``, + ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for + details. + map_location (str | None): Same as :func:`torch.load`. Default: None. + + Returns: + dict or OrderedDict: The loaded checkpoint. + """ + + checkpoint = _load_checkpoint(filename, map_location=map_location) + + if 'state_dict' in checkpoint: + state_dict = checkpoint['state_dict'] + else: + state_dict = checkpoint + if not prefix.endswith('.'): + prefix += '.' + prefix_len = len(prefix) + + state_dict = { + k[prefix_len:]: v + for k, v in state_dict.items() if k.startswith(prefix) + } + + assert state_dict, f'{prefix} is not in the pretrained model' + return state_dict + + +def load_checkpoint(model, + filename, + map_location=None, + strict=False, + logger=None, + revise_keys=[(r'^module\.', '')]): + """Load checkpoint from a file or URI. + + Args: + model (Module): Module to load checkpoint. + filename (str): Accept local filepath, URL, ``torchvision://xxx``, + ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for + details. + map_location (str): Same as :func:`torch.load`. + strict (bool): Whether to allow different params for the model and + checkpoint. + logger (:mod:`logging.Logger` or None): The logger for error message. + revise_keys (list): A list of customized keywords to modify the + state_dict in checkpoint. Each item is a (pattern, replacement) + pair of the regular expression operations. Default: strip + the prefix 'module.' by [(r'^module\\.', '')]. + + Returns: + dict or OrderedDict: The loaded checkpoint. + """ + checkpoint = _load_checkpoint(filename, map_location, logger) + # OrderedDict is a subclass of dict + if not isinstance(checkpoint, dict): + raise RuntimeError( + f'No state_dict found in checkpoint file {filename}') + # get state_dict from checkpoint + if 'state_dict' in checkpoint: + state_dict = checkpoint['state_dict'] + else: + state_dict = checkpoint + + # strip prefix of state_dict + metadata = getattr(state_dict, '_metadata', OrderedDict()) + for p, r in revise_keys: + state_dict = OrderedDict( + {re.sub(p, r, k): v + for k, v in state_dict.items()}) + # Keep metadata in state_dict + state_dict._metadata = metadata + + # load state_dict + load_state_dict(model, state_dict, strict, logger) + return checkpoint + + +def weights_to_cpu(state_dict): + """Copy a model state_dict to cpu. + + Args: + state_dict (OrderedDict): Model weights on GPU. + + Returns: + OrderedDict: Model weights on GPU. + """ + state_dict_cpu = OrderedDict() + for key, val in state_dict.items(): + state_dict_cpu[key] = val.cpu() + # Keep metadata in state_dict + state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) + return state_dict_cpu + + +def _save_to_state_dict(module, destination, prefix, keep_vars): + """Saves module state to `destination` dictionary. + + This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. + + Args: + module (nn.Module): The module to generate state_dict. + destination (dict): A dict where state will be stored. + prefix (str): The prefix for parameters and buffers used in this + module. + """ + for name, param in module._parameters.items(): + if param is not None: + destination[prefix + name] = param if keep_vars else param.detach() + for name, buf in module._buffers.items(): + # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d + if buf is not None: + destination[prefix + name] = buf if keep_vars else buf.detach() + + +def get_state_dict(module, destination=None, prefix='', keep_vars=False): + """Returns a dictionary containing a whole state of the module. + + Both parameters and persistent buffers (e.g. running averages) are + included. Keys are corresponding parameter and buffer names. + + This method is modified from :meth:`torch.nn.Module.state_dict` to + recursively check parallel module in case that the model has a complicated + structure, e.g., nn.Module(nn.Module(DDP)). + + Args: + module (nn.Module): The module to generate state_dict. + destination (OrderedDict): Returned dict for the state of the + module. + prefix (str): Prefix of the key. + keep_vars (bool): Whether to keep the variable property of the + parameters. Default: False. + + Returns: + dict: A dictionary containing a whole state of the module. + """ + # recursively check parallel module in case that the model has a + # complicated structure, e.g., nn.Module(nn.Module(DDP)) + if is_module_wrapper(module): + module = module.module + + # below is the same as torch.nn.Module.state_dict() + if destination is None: + destination = OrderedDict() + destination._metadata = OrderedDict() + destination._metadata[prefix[:-1]] = local_metadata = dict( + version=module._version) + _save_to_state_dict(module, destination, prefix, keep_vars) + for name, child in module._modules.items(): + if child is not None: + get_state_dict( + child, destination, prefix + name + '.', keep_vars=keep_vars) + for hook in module._state_dict_hooks.values(): + hook_result = hook(module, destination, prefix, local_metadata) + if hook_result is not None: + destination = hook_result + return destination + + +def save_checkpoint(model, + filename, + optimizer=None, + meta=None, + file_client_args=None): + """Save checkpoint to file. + + The checkpoint will have 3 fields: ``meta``, ``state_dict`` and + ``optimizer``. By default ``meta`` will contain version and time info. + + Args: + model (Module): Module whose params are to be saved. + filename (str): Checkpoint filename. + optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. + meta (dict, optional): Metadata to be saved in checkpoint. + file_client_args (dict, optional): Arguments to instantiate a + FileClient. See :class:`mmcv.fileio.FileClient` for details. + Default: None. + `New in version 1.3.16.` + """ + if meta is None: + meta = {} + elif not isinstance(meta, dict): + raise TypeError(f'meta must be a dict or None, but got {type(meta)}') + meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) + + if is_module_wrapper(model): + model = model.module + + if hasattr(model, 'CLASSES') and model.CLASSES is not None: + # save class name to the meta + meta.update(CLASSES=model.CLASSES) + + checkpoint = { + 'meta': meta, + 'state_dict': weights_to_cpu(get_state_dict(model)) + } + # save optimizer state dict in the checkpoint + if isinstance(optimizer, Optimizer): + checkpoint['optimizer'] = optimizer.state_dict() + elif isinstance(optimizer, dict): + checkpoint['optimizer'] = {} + for name, optim in optimizer.items(): + checkpoint['optimizer'][name] = optim.state_dict() + + if filename.startswith('pavi://'): + if file_client_args is not None: + raise ValueError( + 'file_client_args should be "None" if filename starts with' + f'"pavi://", but got {file_client_args}') + try: + from pavi import modelcloud + from pavi import exception + except ImportError: + raise ImportError( + 'Please install pavi to load checkpoint from modelcloud.') + model_path = filename[7:] + root = modelcloud.Folder() + model_dir, model_name = osp.split(model_path) + try: + model = modelcloud.get(model_dir) + except exception.NodeNotFoundError: + model = root.create_training_model(model_dir) + with TemporaryDirectory() as tmp_dir: + checkpoint_file = osp.join(tmp_dir, model_name) + with open(checkpoint_file, 'wb') as f: + torch.save(checkpoint, f) + f.flush() + model.create_file(checkpoint_file, name=model_name) + else: + file_client = FileClient.infer_client(file_client_args, filename) + with io.BytesIO() as f: + torch.save(checkpoint, f) + file_client.put(f.getvalue(), filename) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/default_constructor.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/default_constructor.py new file mode 100644 index 0000000000000000000000000000000000000000..bdd7803289d6d70240977fa243d7f4432ccde8f8 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/default_constructor.py @@ -0,0 +1,44 @@ +from .builder import RUNNER_BUILDERS, RUNNERS + + +@RUNNER_BUILDERS.register_module() +class DefaultRunnerConstructor: + """Default constructor for runners. + + Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. + For example, We can inject some new properties and functions for `Runner`. + + Example: + >>> from annotator.mmpkg.mmcv.runner import RUNNER_BUILDERS, build_runner + >>> # Define a new RunnerReconstructor + >>> @RUNNER_BUILDERS.register_module() + >>> class MyRunnerConstructor: + ... def __init__(self, runner_cfg, default_args=None): + ... if not isinstance(runner_cfg, dict): + ... raise TypeError('runner_cfg should be a dict', + ... f'but got {type(runner_cfg)}') + ... self.runner_cfg = runner_cfg + ... self.default_args = default_args + ... + ... def __call__(self): + ... runner = RUNNERS.build(self.runner_cfg, + ... default_args=self.default_args) + ... # Add new properties for existing runner + ... runner.my_name = 'my_runner' + ... runner.my_function = lambda self: print(self.my_name) + ... ... + >>> # build your runner + >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, + ... constructor='MyRunnerConstructor') + >>> runner = build_runner(runner_cfg) + """ + + def __init__(self, runner_cfg, default_args=None): + if not isinstance(runner_cfg, dict): + raise TypeError('runner_cfg should be a dict', + f'but got {type(runner_cfg)}') + self.runner_cfg = runner_cfg + self.default_args = default_args + + def __call__(self): + return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/dist_utils.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/dist_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..d3a1ef3fda5ceeb31bf15a73779da1b1903ab0fe --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/dist_utils.py @@ -0,0 +1,164 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import functools +import os +import subprocess +from collections import OrderedDict + +import torch +import torch.multiprocessing as mp +from torch import distributed as dist +from torch._utils import (_flatten_dense_tensors, _take_tensors, + _unflatten_dense_tensors) + + +def init_dist(launcher, backend='nccl', **kwargs): + if mp.get_start_method(allow_none=True) is None: + mp.set_start_method('spawn') + if launcher == 'pytorch': + _init_dist_pytorch(backend, **kwargs) + elif launcher == 'mpi': + _init_dist_mpi(backend, **kwargs) + elif launcher == 'slurm': + _init_dist_slurm(backend, **kwargs) + else: + raise ValueError(f'Invalid launcher type: {launcher}') + + +def _init_dist_pytorch(backend, **kwargs): + # TODO: use local_rank instead of rank % num_gpus + rank = int(os.environ['RANK']) + num_gpus = torch.cuda.device_count() + torch.cuda.set_device(rank % num_gpus) + dist.init_process_group(backend=backend, **kwargs) + + +def _init_dist_mpi(backend, **kwargs): + # TODO: use local_rank instead of rank % num_gpus + rank = int(os.environ['OMPI_COMM_WORLD_RANK']) + num_gpus = torch.cuda.device_count() + torch.cuda.set_device(rank % num_gpus) + dist.init_process_group(backend=backend, **kwargs) + + +def _init_dist_slurm(backend, port=None): + """Initialize slurm distributed training environment. + + If argument ``port`` is not specified, then the master port will be system + environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system + environment variable, then a default port ``29500`` will be used. + + Args: + backend (str): Backend of torch.distributed. + port (int, optional): Master port. Defaults to None. + """ + proc_id = int(os.environ['SLURM_PROCID']) + ntasks = int(os.environ['SLURM_NTASKS']) + node_list = os.environ['SLURM_NODELIST'] + num_gpus = torch.cuda.device_count() + torch.cuda.set_device(proc_id % num_gpus) + addr = subprocess.getoutput( + f'scontrol show hostname {node_list} | head -n1') + # specify master port + if port is not None: + os.environ['MASTER_PORT'] = str(port) + elif 'MASTER_PORT' in os.environ: + pass # use MASTER_PORT in the environment variable + else: + # 29500 is torch.distributed default port + os.environ['MASTER_PORT'] = '29500' + # use MASTER_ADDR in the environment variable if it already exists + if 'MASTER_ADDR' not in os.environ: + os.environ['MASTER_ADDR'] = addr + os.environ['WORLD_SIZE'] = str(ntasks) + os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) + os.environ['RANK'] = str(proc_id) + dist.init_process_group(backend=backend) + + +def get_dist_info(): + if dist.is_available() and dist.is_initialized(): + rank = dist.get_rank() + world_size = dist.get_world_size() + else: + rank = 0 + world_size = 1 + return rank, world_size + + +def master_only(func): + + @functools.wraps(func) + def wrapper(*args, **kwargs): + rank, _ = get_dist_info() + if rank == 0: + return func(*args, **kwargs) + + return wrapper + + +def allreduce_params(params, coalesce=True, bucket_size_mb=-1): + """Allreduce parameters. + + Args: + params (list[torch.Parameters]): List of parameters or buffers of a + model. + coalesce (bool, optional): Whether allreduce parameters as a whole. + Defaults to True. + bucket_size_mb (int, optional): Size of bucket, the unit is MB. + Defaults to -1. + """ + _, world_size = get_dist_info() + if world_size == 1: + return + params = [param.data for param in params] + if coalesce: + _allreduce_coalesced(params, world_size, bucket_size_mb) + else: + for tensor in params: + dist.all_reduce(tensor.div_(world_size)) + + +def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): + """Allreduce gradients. + + Args: + params (list[torch.Parameters]): List of parameters of a model + coalesce (bool, optional): Whether allreduce parameters as a whole. + Defaults to True. + bucket_size_mb (int, optional): Size of bucket, the unit is MB. + Defaults to -1. + """ + grads = [ + param.grad.data for param in params + if param.requires_grad and param.grad is not None + ] + _, world_size = get_dist_info() + if world_size == 1: + return + if coalesce: + _allreduce_coalesced(grads, world_size, bucket_size_mb) + else: + for tensor in grads: + dist.all_reduce(tensor.div_(world_size)) + + +def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): + if bucket_size_mb > 0: + bucket_size_bytes = bucket_size_mb * 1024 * 1024 + buckets = _take_tensors(tensors, bucket_size_bytes) + else: + buckets = OrderedDict() + for tensor in tensors: + tp = tensor.type() + if tp not in buckets: + buckets[tp] = [] + buckets[tp].append(tensor) + buckets = buckets.values() + + for bucket in buckets: + flat_tensors = _flatten_dense_tensors(bucket) + dist.all_reduce(flat_tensors) + flat_tensors.div_(world_size) + for tensor, synced in zip( + bucket, _unflatten_dense_tensors(flat_tensors, bucket)): + tensor.copy_(synced) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/epoch_based_runner.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/epoch_based_runner.py new file mode 100644 index 0000000000000000000000000000000000000000..d4df071e1740baa4aea2951590ac929b3715daa2 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/epoch_based_runner.py @@ -0,0 +1,187 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os.path as osp +import platform +import shutil +import time +import warnings + +import torch + +import annotator.mmpkg.mmcv as mmcv +from .base_runner import BaseRunner +from .builder import RUNNERS +from .checkpoint import save_checkpoint +from .utils import get_host_info + + +@RUNNERS.register_module() +class EpochBasedRunner(BaseRunner): + """Epoch-based Runner. + + This runner train models epoch by epoch. + """ + + def run_iter(self, data_batch, train_mode, **kwargs): + if self.batch_processor is not None: + outputs = self.batch_processor( + self.model, data_batch, train_mode=train_mode, **kwargs) + elif train_mode: + outputs = self.model.train_step(data_batch, self.optimizer, + **kwargs) + else: + outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) + if not isinstance(outputs, dict): + raise TypeError('"batch_processor()" or "model.train_step()"' + 'and "model.val_step()" must return a dict') + if 'log_vars' in outputs: + self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) + self.outputs = outputs + + def train(self, data_loader, **kwargs): + self.model.train() + self.mode = 'train' + self.data_loader = data_loader + self._max_iters = self._max_epochs * len(self.data_loader) + self.call_hook('before_train_epoch') + time.sleep(2) # Prevent possible deadlock during epoch transition + for i, data_batch in enumerate(self.data_loader): + self._inner_iter = i + self.call_hook('before_train_iter') + self.run_iter(data_batch, train_mode=True, **kwargs) + self.call_hook('after_train_iter') + self._iter += 1 + + self.call_hook('after_train_epoch') + self._epoch += 1 + + @torch.no_grad() + def val(self, data_loader, **kwargs): + self.model.eval() + self.mode = 'val' + self.data_loader = data_loader + self.call_hook('before_val_epoch') + time.sleep(2) # Prevent possible deadlock during epoch transition + for i, data_batch in enumerate(self.data_loader): + self._inner_iter = i + self.call_hook('before_val_iter') + self.run_iter(data_batch, train_mode=False) + self.call_hook('after_val_iter') + + self.call_hook('after_val_epoch') + + def run(self, data_loaders, workflow, max_epochs=None, **kwargs): + """Start running. + + Args: + data_loaders (list[:obj:`DataLoader`]): Dataloaders for training + and validation. + workflow (list[tuple]): A list of (phase, epochs) to specify the + running order and epochs. E.g, [('train', 2), ('val', 1)] means + running 2 epochs for training and 1 epoch for validation, + iteratively. + """ + assert isinstance(data_loaders, list) + assert mmcv.is_list_of(workflow, tuple) + assert len(data_loaders) == len(workflow) + if max_epochs is not None: + warnings.warn( + 'setting max_epochs in run is deprecated, ' + 'please set max_epochs in runner_config', DeprecationWarning) + self._max_epochs = max_epochs + + assert self._max_epochs is not None, ( + 'max_epochs must be specified during instantiation') + + for i, flow in enumerate(workflow): + mode, epochs = flow + if mode == 'train': + self._max_iters = self._max_epochs * len(data_loaders[i]) + break + + work_dir = self.work_dir if self.work_dir is not None else 'NONE' + self.logger.info('Start running, host: %s, work_dir: %s', + get_host_info(), work_dir) + self.logger.info('Hooks will be executed in the following order:\n%s', + self.get_hook_info()) + self.logger.info('workflow: %s, max: %d epochs', workflow, + self._max_epochs) + self.call_hook('before_run') + + while self.epoch < self._max_epochs: + for i, flow in enumerate(workflow): + mode, epochs = flow + if isinstance(mode, str): # self.train() + if not hasattr(self, mode): + raise ValueError( + f'runner has no method named "{mode}" to run an ' + 'epoch') + epoch_runner = getattr(self, mode) + else: + raise TypeError( + 'mode in workflow must be a str, but got {}'.format( + type(mode))) + + for _ in range(epochs): + if mode == 'train' and self.epoch >= self._max_epochs: + break + epoch_runner(data_loaders[i], **kwargs) + + time.sleep(1) # wait for some hooks like loggers to finish + self.call_hook('after_run') + + def save_checkpoint(self, + out_dir, + filename_tmpl='epoch_{}.pth', + save_optimizer=True, + meta=None, + create_symlink=True): + """Save the checkpoint. + + Args: + out_dir (str): The directory that checkpoints are saved. + filename_tmpl (str, optional): The checkpoint filename template, + which contains a placeholder for the epoch number. + Defaults to 'epoch_{}.pth'. + save_optimizer (bool, optional): Whether to save the optimizer to + the checkpoint. Defaults to True. + meta (dict, optional): The meta information to be saved in the + checkpoint. Defaults to None. + create_symlink (bool, optional): Whether to create a symlink + "latest.pth" to point to the latest checkpoint. + Defaults to True. + """ + if meta is None: + meta = {} + elif not isinstance(meta, dict): + raise TypeError( + f'meta should be a dict or None, but got {type(meta)}') + if self.meta is not None: + meta.update(self.meta) + # Note: meta.update(self.meta) should be done before + # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise + # there will be problems with resumed checkpoints. + # More details in https://github.com/open-mmlab/mmcv/pull/1108 + meta.update(epoch=self.epoch + 1, iter=self.iter) + + filename = filename_tmpl.format(self.epoch + 1) + filepath = osp.join(out_dir, filename) + optimizer = self.optimizer if save_optimizer else None + save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) + # in some environments, `os.symlink` is not supported, you may need to + # set `create_symlink` to False + if create_symlink: + dst_file = osp.join(out_dir, 'latest.pth') + if platform.system() != 'Windows': + mmcv.symlink(filename, dst_file) + else: + shutil.copy(filepath, dst_file) + + +@RUNNERS.register_module() +class Runner(EpochBasedRunner): + """Deprecated name of EpochBasedRunner.""" + + def __init__(self, *args, **kwargs): + warnings.warn( + 'Runner was deprecated, please use EpochBasedRunner instead') + super().__init__(*args, **kwargs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/fp16_utils.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/fp16_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..f6b54886519fd2808360b1632e5bebf6563eced2 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/fp16_utils.py @@ -0,0 +1,410 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import functools +import warnings +from collections import abc +from inspect import getfullargspec + +import numpy as np +import torch +import torch.nn as nn + +from annotator.mmpkg.mmcv.utils import TORCH_VERSION, digit_version +from .dist_utils import allreduce_grads as _allreduce_grads + +try: + # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported + # and used; otherwise, auto fp16 will adopt mmcv's implementation. + # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 + # manually, so the behavior may not be consistent with real amp. + from torch.cuda.amp import autocast +except ImportError: + pass + + +def cast_tensor_type(inputs, src_type, dst_type): + """Recursively convert Tensor in inputs from src_type to dst_type. + + Args: + inputs: Inputs that to be casted. + src_type (torch.dtype): Source type.. + dst_type (torch.dtype): Destination type. + + Returns: + The same type with inputs, but all contained Tensors have been cast. + """ + if isinstance(inputs, nn.Module): + return inputs + elif isinstance(inputs, torch.Tensor): + return inputs.to(dst_type) + elif isinstance(inputs, str): + return inputs + elif isinstance(inputs, np.ndarray): + return inputs + elif isinstance(inputs, abc.Mapping): + return type(inputs)({ + k: cast_tensor_type(v, src_type, dst_type) + for k, v in inputs.items() + }) + elif isinstance(inputs, abc.Iterable): + return type(inputs)( + cast_tensor_type(item, src_type, dst_type) for item in inputs) + else: + return inputs + + +def auto_fp16(apply_to=None, out_fp32=False): + """Decorator to enable fp16 training automatically. + + This decorator is useful when you write custom modules and want to support + mixed precision training. If inputs arguments are fp32 tensors, they will + be converted to fp16 automatically. Arguments other than fp32 tensors are + ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the + backend, otherwise, original mmcv implementation will be adopted. + + Args: + apply_to (Iterable, optional): The argument names to be converted. + `None` indicates all arguments. + out_fp32 (bool): Whether to convert the output back to fp32. + + Example: + + >>> import torch.nn as nn + >>> class MyModule1(nn.Module): + >>> + >>> # Convert x and y to fp16 + >>> @auto_fp16() + >>> def forward(self, x, y): + >>> pass + + >>> import torch.nn as nn + >>> class MyModule2(nn.Module): + >>> + >>> # convert pred to fp16 + >>> @auto_fp16(apply_to=('pred', )) + >>> def do_something(self, pred, others): + >>> pass + """ + + def auto_fp16_wrapper(old_func): + + @functools.wraps(old_func) + def new_func(*args, **kwargs): + # check if the module has set the attribute `fp16_enabled`, if not, + # just fallback to the original method. + if not isinstance(args[0], torch.nn.Module): + raise TypeError('@auto_fp16 can only be used to decorate the ' + 'method of nn.Module') + if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): + return old_func(*args, **kwargs) + + # get the arg spec of the decorated method + args_info = getfullargspec(old_func) + # get the argument names to be casted + args_to_cast = args_info.args if apply_to is None else apply_to + # convert the args that need to be processed + new_args = [] + # NOTE: default args are not taken into consideration + if args: + arg_names = args_info.args[:len(args)] + for i, arg_name in enumerate(arg_names): + if arg_name in args_to_cast: + new_args.append( + cast_tensor_type(args[i], torch.float, torch.half)) + else: + new_args.append(args[i]) + # convert the kwargs that need to be processed + new_kwargs = {} + if kwargs: + for arg_name, arg_value in kwargs.items(): + if arg_name in args_to_cast: + new_kwargs[arg_name] = cast_tensor_type( + arg_value, torch.float, torch.half) + else: + new_kwargs[arg_name] = arg_value + # apply converted arguments to the decorated method + if (TORCH_VERSION != 'parrots' and + digit_version(TORCH_VERSION) >= digit_version('1.6.0')): + with autocast(enabled=True): + output = old_func(*new_args, **new_kwargs) + else: + output = old_func(*new_args, **new_kwargs) + # cast the results back to fp32 if necessary + if out_fp32: + output = cast_tensor_type(output, torch.half, torch.float) + return output + + return new_func + + return auto_fp16_wrapper + + +def force_fp32(apply_to=None, out_fp16=False): + """Decorator to convert input arguments to fp32 in force. + + This decorator is useful when you write custom modules and want to support + mixed precision training. If there are some inputs that must be processed + in fp32 mode, then this decorator can handle it. If inputs arguments are + fp16 tensors, they will be converted to fp32 automatically. Arguments other + than fp16 tensors are ignored. If you are using PyTorch >= 1.6, + torch.cuda.amp is used as the backend, otherwise, original mmcv + implementation will be adopted. + + Args: + apply_to (Iterable, optional): The argument names to be converted. + `None` indicates all arguments. + out_fp16 (bool): Whether to convert the output back to fp16. + + Example: + + >>> import torch.nn as nn + >>> class MyModule1(nn.Module): + >>> + >>> # Convert x and y to fp32 + >>> @force_fp32() + >>> def loss(self, x, y): + >>> pass + + >>> import torch.nn as nn + >>> class MyModule2(nn.Module): + >>> + >>> # convert pred to fp32 + >>> @force_fp32(apply_to=('pred', )) + >>> def post_process(self, pred, others): + >>> pass + """ + + def force_fp32_wrapper(old_func): + + @functools.wraps(old_func) + def new_func(*args, **kwargs): + # check if the module has set the attribute `fp16_enabled`, if not, + # just fallback to the original method. + if not isinstance(args[0], torch.nn.Module): + raise TypeError('@force_fp32 can only be used to decorate the ' + 'method of nn.Module') + if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): + return old_func(*args, **kwargs) + # get the arg spec of the decorated method + args_info = getfullargspec(old_func) + # get the argument names to be casted + args_to_cast = args_info.args if apply_to is None else apply_to + # convert the args that need to be processed + new_args = [] + if args: + arg_names = args_info.args[:len(args)] + for i, arg_name in enumerate(arg_names): + if arg_name in args_to_cast: + new_args.append( + cast_tensor_type(args[i], torch.half, torch.float)) + else: + new_args.append(args[i]) + # convert the kwargs that need to be processed + new_kwargs = dict() + if kwargs: + for arg_name, arg_value in kwargs.items(): + if arg_name in args_to_cast: + new_kwargs[arg_name] = cast_tensor_type( + arg_value, torch.half, torch.float) + else: + new_kwargs[arg_name] = arg_value + # apply converted arguments to the decorated method + if (TORCH_VERSION != 'parrots' and + digit_version(TORCH_VERSION) >= digit_version('1.6.0')): + with autocast(enabled=False): + output = old_func(*new_args, **new_kwargs) + else: + output = old_func(*new_args, **new_kwargs) + # cast the results back to fp32 if necessary + if out_fp16: + output = cast_tensor_type(output, torch.float, torch.half) + return output + + return new_func + + return force_fp32_wrapper + + +def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): + warnings.warning( + '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' + 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads') + _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) + + +def wrap_fp16_model(model): + """Wrap the FP32 model to FP16. + + If you are using PyTorch >= 1.6, torch.cuda.amp is used as the + backend, otherwise, original mmcv implementation will be adopted. + + For PyTorch >= 1.6, this function will + 1. Set fp16 flag inside the model to True. + + Otherwise: + 1. Convert FP32 model to FP16. + 2. Remain some necessary layers to be FP32, e.g., normalization layers. + 3. Set `fp16_enabled` flag inside the model to True. + + Args: + model (nn.Module): Model in FP32. + """ + if (TORCH_VERSION == 'parrots' + or digit_version(TORCH_VERSION) < digit_version('1.6.0')): + # convert model to fp16 + model.half() + # patch the normalization layers to make it work in fp32 mode + patch_norm_fp32(model) + # set `fp16_enabled` flag + for m in model.modules(): + if hasattr(m, 'fp16_enabled'): + m.fp16_enabled = True + + +def patch_norm_fp32(module): + """Recursively convert normalization layers from FP16 to FP32. + + Args: + module (nn.Module): The modules to be converted in FP16. + + Returns: + nn.Module: The converted module, the normalization layers have been + converted to FP32. + """ + if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): + module.float() + if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': + module.forward = patch_forward_method(module.forward, torch.half, + torch.float) + for child in module.children(): + patch_norm_fp32(child) + return module + + +def patch_forward_method(func, src_type, dst_type, convert_output=True): + """Patch the forward method of a module. + + Args: + func (callable): The original forward method. + src_type (torch.dtype): Type of input arguments to be converted from. + dst_type (torch.dtype): Type of input arguments to be converted to. + convert_output (bool): Whether to convert the output back to src_type. + + Returns: + callable: The patched forward method. + """ + + def new_forward(*args, **kwargs): + output = func(*cast_tensor_type(args, src_type, dst_type), + **cast_tensor_type(kwargs, src_type, dst_type)) + if convert_output: + output = cast_tensor_type(output, dst_type, src_type) + return output + + return new_forward + + +class LossScaler: + """Class that manages loss scaling in mixed precision training which + supports both dynamic or static mode. + + The implementation refers to + https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. + Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. + It's important to understand how :class:`LossScaler` operates. + Loss scaling is designed to combat the problem of underflowing + gradients encountered at long times when training fp16 networks. + Dynamic loss scaling begins by attempting a very high loss + scale. Ironically, this may result in OVERflowing gradients. + If overflowing gradients are encountered, :class:`FP16_Optimizer` then + skips the update step for this particular iteration/minibatch, + and :class:`LossScaler` adjusts the loss scale to a lower value. + If a certain number of iterations occur without overflowing gradients + detected,:class:`LossScaler` increases the loss scale once more. + In this way :class:`LossScaler` attempts to "ride the edge" of always + using the highest loss scale possible without incurring overflow. + + Args: + init_scale (float): Initial loss scale value, default: 2**32. + scale_factor (float): Factor used when adjusting the loss scale. + Default: 2. + mode (str): Loss scaling mode. 'dynamic' or 'static' + scale_window (int): Number of consecutive iterations without an + overflow to wait before increasing the loss scale. Default: 1000. + """ + + def __init__(self, + init_scale=2**32, + mode='dynamic', + scale_factor=2., + scale_window=1000): + self.cur_scale = init_scale + self.cur_iter = 0 + assert mode in ('dynamic', + 'static'), 'mode can only be dynamic or static' + self.mode = mode + self.last_overflow_iter = -1 + self.scale_factor = scale_factor + self.scale_window = scale_window + + def has_overflow(self, params): + """Check if params contain overflow.""" + if self.mode != 'dynamic': + return False + for p in params: + if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): + return True + return False + + def _has_inf_or_nan(x): + """Check if params contain NaN.""" + try: + cpu_sum = float(x.float().sum()) + except RuntimeError as instance: + if 'value cannot be converted' not in instance.args[0]: + raise + return True + else: + if cpu_sum == float('inf') or cpu_sum == -float('inf') \ + or cpu_sum != cpu_sum: + return True + return False + + def update_scale(self, overflow): + """update the current loss scale value when overflow happens.""" + if self.mode != 'dynamic': + return + if overflow: + self.cur_scale = max(self.cur_scale / self.scale_factor, 1) + self.last_overflow_iter = self.cur_iter + else: + if (self.cur_iter - self.last_overflow_iter) % \ + self.scale_window == 0: + self.cur_scale *= self.scale_factor + self.cur_iter += 1 + + def state_dict(self): + """Returns the state of the scaler as a :class:`dict`.""" + return dict( + cur_scale=self.cur_scale, + cur_iter=self.cur_iter, + mode=self.mode, + last_overflow_iter=self.last_overflow_iter, + scale_factor=self.scale_factor, + scale_window=self.scale_window) + + def load_state_dict(self, state_dict): + """Loads the loss_scaler state dict. + + Args: + state_dict (dict): scaler state. + """ + self.cur_scale = state_dict['cur_scale'] + self.cur_iter = state_dict['cur_iter'] + self.mode = state_dict['mode'] + self.last_overflow_iter = state_dict['last_overflow_iter'] + self.scale_factor = state_dict['scale_factor'] + self.scale_window = state_dict['scale_window'] + + @property + def loss_scale(self): + return self.cur_scale diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..915af28cefab14a14c1188ed861161080fd138a3 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/__init__.py @@ -0,0 +1,29 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .checkpoint import CheckpointHook +from .closure import ClosureHook +from .ema import EMAHook +from .evaluation import DistEvalHook, EvalHook +from .hook import HOOKS, Hook +from .iter_timer import IterTimerHook +from .logger import (DvcliveLoggerHook, LoggerHook, MlflowLoggerHook, + NeptuneLoggerHook, PaviLoggerHook, TensorboardLoggerHook, + TextLoggerHook, WandbLoggerHook) +from .lr_updater import LrUpdaterHook +from .memory import EmptyCacheHook +from .momentum_updater import MomentumUpdaterHook +from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, + GradientCumulativeOptimizerHook, OptimizerHook) +from .profiler import ProfilerHook +from .sampler_seed import DistSamplerSeedHook +from .sync_buffer import SyncBuffersHook + +__all__ = [ + 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', + 'OptimizerHook', 'Fp16OptimizerHook', 'IterTimerHook', + 'DistSamplerSeedHook', 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', + 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', + 'NeptuneLoggerHook', 'WandbLoggerHook', 'DvcliveLoggerHook', + 'MomentumUpdaterHook', 'SyncBuffersHook', 'EMAHook', 'EvalHook', + 'DistEvalHook', 'ProfilerHook', 'GradientCumulativeOptimizerHook', + 'GradientCumulativeFp16OptimizerHook' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/checkpoint.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/checkpoint.py new file mode 100644 index 0000000000000000000000000000000000000000..877aa8b84ac48bea0a06f9d0733d74f88be2ecfc --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/checkpoint.py @@ -0,0 +1,167 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os.path as osp +import warnings + +from annotator.mmpkg.mmcv.fileio import FileClient +from ..dist_utils import allreduce_params, master_only +from .hook import HOOKS, Hook + + +@HOOKS.register_module() +class CheckpointHook(Hook): + """Save checkpoints periodically. + + Args: + interval (int): The saving period. If ``by_epoch=True``, interval + indicates epochs, otherwise it indicates iterations. + Default: -1, which means "never". + by_epoch (bool): Saving checkpoints by epoch or by iteration. + Default: True. + save_optimizer (bool): Whether to save optimizer state_dict in the + checkpoint. It is usually used for resuming experiments. + Default: True. + out_dir (str, optional): The root directory to save checkpoints. If not + specified, ``runner.work_dir`` will be used by default. If + specified, the ``out_dir`` will be the concatenation of ``out_dir`` + and the last level directory of ``runner.work_dir``. + `Changed in version 1.3.16.` + max_keep_ckpts (int, optional): The maximum checkpoints to keep. + In some cases we want only the latest few checkpoints and would + like to delete old ones to save the disk space. + Default: -1, which means unlimited. + save_last (bool, optional): Whether to force the last checkpoint to be + saved regardless of interval. Default: True. + sync_buffer (bool, optional): Whether to synchronize buffers in + different gpus. Default: False. + file_client_args (dict, optional): Arguments to instantiate a + FileClient. See :class:`mmcv.fileio.FileClient` for details. + Default: None. + `New in version 1.3.16.` + + .. warning:: + Before v1.3.16, the ``out_dir`` argument indicates the path where the + checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the + root directory and the final path to save checkpoint is the + concatenation of ``out_dir`` and the last level directory of + ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" + and the value of ``runner.work_dir`` is "/path/of/B", then the final + path will be "/path/of/A/B". + """ + + def __init__(self, + interval=-1, + by_epoch=True, + save_optimizer=True, + out_dir=None, + max_keep_ckpts=-1, + save_last=True, + sync_buffer=False, + file_client_args=None, + **kwargs): + self.interval = interval + self.by_epoch = by_epoch + self.save_optimizer = save_optimizer + self.out_dir = out_dir + self.max_keep_ckpts = max_keep_ckpts + self.save_last = save_last + self.args = kwargs + self.sync_buffer = sync_buffer + self.file_client_args = file_client_args + + def before_run(self, runner): + if not self.out_dir: + self.out_dir = runner.work_dir + + self.file_client = FileClient.infer_client(self.file_client_args, + self.out_dir) + + # if `self.out_dir` is not equal to `runner.work_dir`, it means that + # `self.out_dir` is set so the final `self.out_dir` is the + # concatenation of `self.out_dir` and the last level directory of + # `runner.work_dir` + if self.out_dir != runner.work_dir: + basename = osp.basename(runner.work_dir.rstrip(osp.sep)) + self.out_dir = self.file_client.join_path(self.out_dir, basename) + + runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' + f'{self.file_client.name}.')) + + # disable the create_symlink option because some file backends do not + # allow to create a symlink + if 'create_symlink' in self.args: + if self.args[ + 'create_symlink'] and not self.file_client.allow_symlink: + self.args['create_symlink'] = False + warnings.warn( + ('create_symlink is set as True by the user but is changed' + 'to be False because creating symbolic link is not ' + f'allowed in {self.file_client.name}')) + else: + self.args['create_symlink'] = self.file_client.allow_symlink + + def after_train_epoch(self, runner): + if not self.by_epoch: + return + + # save checkpoint for following cases: + # 1. every ``self.interval`` epochs + # 2. reach the last epoch of training + if self.every_n_epochs( + runner, self.interval) or (self.save_last + and self.is_last_epoch(runner)): + runner.logger.info( + f'Saving checkpoint at {runner.epoch + 1} epochs') + if self.sync_buffer: + allreduce_params(runner.model.buffers()) + self._save_checkpoint(runner) + + @master_only + def _save_checkpoint(self, runner): + """Save the current checkpoint and delete unwanted checkpoint.""" + runner.save_checkpoint( + self.out_dir, save_optimizer=self.save_optimizer, **self.args) + if runner.meta is not None: + if self.by_epoch: + cur_ckpt_filename = self.args.get( + 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) + else: + cur_ckpt_filename = self.args.get( + 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) + runner.meta.setdefault('hook_msgs', dict()) + runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( + self.out_dir, cur_ckpt_filename) + # remove other checkpoints + if self.max_keep_ckpts > 0: + if self.by_epoch: + name = 'epoch_{}.pth' + current_ckpt = runner.epoch + 1 + else: + name = 'iter_{}.pth' + current_ckpt = runner.iter + 1 + redundant_ckpts = range( + current_ckpt - self.max_keep_ckpts * self.interval, 0, + -self.interval) + filename_tmpl = self.args.get('filename_tmpl', name) + for _step in redundant_ckpts: + ckpt_path = self.file_client.join_path( + self.out_dir, filename_tmpl.format(_step)) + if self.file_client.isfile(ckpt_path): + self.file_client.remove(ckpt_path) + else: + break + + def after_train_iter(self, runner): + if self.by_epoch: + return + + # save checkpoint for following cases: + # 1. every ``self.interval`` iterations + # 2. reach the last iteration of training + if self.every_n_iters( + runner, self.interval) or (self.save_last + and self.is_last_iter(runner)): + runner.logger.info( + f'Saving checkpoint at {runner.iter + 1} iterations') + if self.sync_buffer: + allreduce_params(runner.model.buffers()) + self._save_checkpoint(runner) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/closure.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/closure.py new file mode 100644 index 0000000000000000000000000000000000000000..b955f81f425be4ac3e6bb3f4aac653887989e872 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/closure.py @@ -0,0 +1,11 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .hook import HOOKS, Hook + + +@HOOKS.register_module() +class ClosureHook(Hook): + + def __init__(self, fn_name, fn): + assert hasattr(self, fn_name) + assert callable(fn) + setattr(self, fn_name, fn) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/ema.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/ema.py new file mode 100644 index 0000000000000000000000000000000000000000..15c7e68088f019802a59e7ae41cc1fe0c7f28f96 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/ema.py @@ -0,0 +1,89 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from ...parallel import is_module_wrapper +from ..hooks.hook import HOOKS, Hook + + +@HOOKS.register_module() +class EMAHook(Hook): + r"""Exponential Moving Average Hook. + + Use Exponential Moving Average on all parameters of model in training + process. All parameters have a ema backup, which update by the formula + as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. + + .. math:: + + \text{Xema\_{t+1}} = (1 - \text{momentum}) \times + \text{Xema\_{t}} + \text{momentum} \times X_t + + Args: + momentum (float): The momentum used for updating ema parameter. + Defaults to 0.0002. + interval (int): Update ema parameter every interval iteration. + Defaults to 1. + warm_up (int): During first warm_up steps, we may use smaller momentum + to update ema parameters more slowly. Defaults to 100. + resume_from (str): The checkpoint path. Defaults to None. + """ + + def __init__(self, + momentum=0.0002, + interval=1, + warm_up=100, + resume_from=None): + assert isinstance(interval, int) and interval > 0 + self.warm_up = warm_up + self.interval = interval + assert momentum > 0 and momentum < 1 + self.momentum = momentum**interval + self.checkpoint = resume_from + + def before_run(self, runner): + """To resume model with it's ema parameters more friendly. + + Register ema parameter as ``named_buffer`` to model + """ + model = runner.model + if is_module_wrapper(model): + model = model.module + self.param_ema_buffer = {} + self.model_parameters = dict(model.named_parameters(recurse=True)) + for name, value in self.model_parameters.items(): + # "." is not allowed in module's buffer name + buffer_name = f"ema_{name.replace('.', '_')}" + self.param_ema_buffer[name] = buffer_name + model.register_buffer(buffer_name, value.data.clone()) + self.model_buffers = dict(model.named_buffers(recurse=True)) + if self.checkpoint is not None: + runner.resume(self.checkpoint) + + def after_train_iter(self, runner): + """Update ema parameter every self.interval iterations.""" + curr_step = runner.iter + # We warm up the momentum considering the instability at beginning + momentum = min(self.momentum, + (1 + curr_step) / (self.warm_up + curr_step)) + if curr_step % self.interval != 0: + return + for name, parameter in self.model_parameters.items(): + buffer_name = self.param_ema_buffer[name] + buffer_parameter = self.model_buffers[buffer_name] + buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) + + def after_train_epoch(self, runner): + """We load parameter values from ema backup to model before the + EvalHook.""" + self._swap_ema_parameters() + + def before_train_epoch(self, runner): + """We recover model's parameter from ema backup after last epoch's + EvalHook.""" + self._swap_ema_parameters() + + def _swap_ema_parameters(self): + """Swap the parameter of model with parameter in ema_buffer.""" + for name, value in self.model_parameters.items(): + temp = value.data.clone() + ema_buffer = self.model_buffers[self.param_ema_buffer[name]] + value.data.copy_(ema_buffer.data) + ema_buffer.data.copy_(temp) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/evaluation.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/evaluation.py new file mode 100644 index 0000000000000000000000000000000000000000..a1dbdfd593bae505a70534226b79791baec6453e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/evaluation.py @@ -0,0 +1,509 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os.path as osp +import warnings +from math import inf + +import torch.distributed as dist +from torch.nn.modules.batchnorm import _BatchNorm +from torch.utils.data import DataLoader + +from annotator.mmpkg.mmcv.fileio import FileClient +from annotator.mmpkg.mmcv.utils import is_seq_of +from .hook import Hook +from .logger import LoggerHook + + +class EvalHook(Hook): + """Non-Distributed evaluation hook. + + This hook will regularly perform evaluation in a given interval when + performing in non-distributed environment. + + Args: + dataloader (DataLoader): A PyTorch dataloader, whose dataset has + implemented ``evaluate`` function. + start (int | None, optional): Evaluation starting epoch. It enables + evaluation before the training starts if ``start`` <= the resuming + epoch. If None, whether to evaluate is merely decided by + ``interval``. Default: None. + interval (int): Evaluation interval. Default: 1. + by_epoch (bool): Determine perform evaluation by epoch or by iteration. + If set to True, it will perform by epoch. Otherwise, by iteration. + Default: True. + save_best (str, optional): If a metric is specified, it would measure + the best checkpoint during evaluation. The information about best + checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep + best score value and best checkpoint path, which will be also + loaded when resume checkpoint. Options are the evaluation metrics + on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox + detection and instance segmentation. ``AR@100`` for proposal + recall. If ``save_best`` is ``auto``, the first key of the returned + ``OrderedDict`` result will be used. Default: None. + rule (str | None, optional): Comparison rule for best score. If set to + None, it will infer a reasonable rule. Keys such as 'acc', 'top' + .etc will be inferred by 'greater' rule. Keys contain 'loss' will + be inferred by 'less' rule. Options are 'greater', 'less', None. + Default: None. + test_fn (callable, optional): test a model with samples from a + dataloader, and return the test results. If ``None``, the default + test function ``mmcv.engine.single_gpu_test`` will be used. + (default: ``None``) + greater_keys (List[str] | None, optional): Metric keys that will be + inferred by 'greater' comparison rule. If ``None``, + _default_greater_keys will be used. (default: ``None``) + less_keys (List[str] | None, optional): Metric keys that will be + inferred by 'less' comparison rule. If ``None``, _default_less_keys + will be used. (default: ``None``) + out_dir (str, optional): The root directory to save checkpoints. If not + specified, `runner.work_dir` will be used by default. If specified, + the `out_dir` will be the concatenation of `out_dir` and the last + level directory of `runner.work_dir`. + `New in version 1.3.16.` + file_client_args (dict): Arguments to instantiate a FileClient. + See :class:`mmcv.fileio.FileClient` for details. Default: None. + `New in version 1.3.16.` + **eval_kwargs: Evaluation arguments fed into the evaluate function of + the dataset. + + Notes: + If new arguments are added for EvalHook, tools/test.py, + tools/eval_metric.py may be affected. + """ + + # Since the key for determine greater or less is related to the downstream + # tasks, downstream repos may need to overwrite the following inner + # variable accordingly. + + rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} + init_value_map = {'greater': -inf, 'less': inf} + _default_greater_keys = [ + 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', + 'mAcc', 'aAcc' + ] + _default_less_keys = ['loss'] + + def __init__(self, + dataloader, + start=None, + interval=1, + by_epoch=True, + save_best=None, + rule=None, + test_fn=None, + greater_keys=None, + less_keys=None, + out_dir=None, + file_client_args=None, + **eval_kwargs): + if not isinstance(dataloader, DataLoader): + raise TypeError(f'dataloader must be a pytorch DataLoader, ' + f'but got {type(dataloader)}') + + if interval <= 0: + raise ValueError(f'interval must be a positive number, ' + f'but got {interval}') + + assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' + + if start is not None and start < 0: + raise ValueError(f'The evaluation start epoch {start} is smaller ' + f'than 0') + + self.dataloader = dataloader + self.interval = interval + self.start = start + self.by_epoch = by_epoch + + assert isinstance(save_best, str) or save_best is None, \ + '""save_best"" should be a str or None ' \ + f'rather than {type(save_best)}' + self.save_best = save_best + self.eval_kwargs = eval_kwargs + self.initial_flag = True + + if test_fn is None: + from annotator.mmpkg.mmcv.engine import single_gpu_test + self.test_fn = single_gpu_test + else: + self.test_fn = test_fn + + if greater_keys is None: + self.greater_keys = self._default_greater_keys + else: + if not isinstance(greater_keys, (list, tuple)): + greater_keys = (greater_keys, ) + assert is_seq_of(greater_keys, str) + self.greater_keys = greater_keys + + if less_keys is None: + self.less_keys = self._default_less_keys + else: + if not isinstance(less_keys, (list, tuple)): + less_keys = (less_keys, ) + assert is_seq_of(less_keys, str) + self.less_keys = less_keys + + if self.save_best is not None: + self.best_ckpt_path = None + self._init_rule(rule, self.save_best) + + self.out_dir = out_dir + self.file_client_args = file_client_args + + def _init_rule(self, rule, key_indicator): + """Initialize rule, key_indicator, comparison_func, and best score. + + Here is the rule to determine which rule is used for key indicator + when the rule is not specific (note that the key indicator matching + is case-insensitive): + 1. If the key indicator is in ``self.greater_keys``, the rule will be + specified as 'greater'. + 2. Or if the key indicator is in ``self.less_keys``, the rule will be + specified as 'less'. + 3. Or if the key indicator is equal to the substring in any one item + in ``self.greater_keys``, the rule will be specified as 'greater'. + 4. Or if the key indicator is equal to the substring in any one item + in ``self.less_keys``, the rule will be specified as 'less'. + + Args: + rule (str | None): Comparison rule for best score. + key_indicator (str | None): Key indicator to determine the + comparison rule. + """ + if rule not in self.rule_map and rule is not None: + raise KeyError(f'rule must be greater, less or None, ' + f'but got {rule}.') + + if rule is None: + if key_indicator != 'auto': + # `_lc` here means we use the lower case of keys for + # case-insensitive matching + key_indicator_lc = key_indicator.lower() + greater_keys = [key.lower() for key in self.greater_keys] + less_keys = [key.lower() for key in self.less_keys] + + if key_indicator_lc in greater_keys: + rule = 'greater' + elif key_indicator_lc in less_keys: + rule = 'less' + elif any(key in key_indicator_lc for key in greater_keys): + rule = 'greater' + elif any(key in key_indicator_lc for key in less_keys): + rule = 'less' + else: + raise ValueError(f'Cannot infer the rule for key ' + f'{key_indicator}, thus a specific rule ' + f'must be specified.') + self.rule = rule + self.key_indicator = key_indicator + if self.rule is not None: + self.compare_func = self.rule_map[self.rule] + + def before_run(self, runner): + if not self.out_dir: + self.out_dir = runner.work_dir + + self.file_client = FileClient.infer_client(self.file_client_args, + self.out_dir) + + # if `self.out_dir` is not equal to `runner.work_dir`, it means that + # `self.out_dir` is set so the final `self.out_dir` is the + # concatenation of `self.out_dir` and the last level directory of + # `runner.work_dir` + if self.out_dir != runner.work_dir: + basename = osp.basename(runner.work_dir.rstrip(osp.sep)) + self.out_dir = self.file_client.join_path(self.out_dir, basename) + runner.logger.info( + (f'The best checkpoint will be saved to {self.out_dir} by ' + f'{self.file_client.name}')) + + if self.save_best is not None: + if runner.meta is None: + warnings.warn('runner.meta is None. Creating an empty one.') + runner.meta = dict() + runner.meta.setdefault('hook_msgs', dict()) + self.best_ckpt_path = runner.meta['hook_msgs'].get( + 'best_ckpt', None) + + def before_train_iter(self, runner): + """Evaluate the model only at the start of training by iteration.""" + if self.by_epoch or not self.initial_flag: + return + if self.start is not None and runner.iter >= self.start: + self.after_train_iter(runner) + self.initial_flag = False + + def before_train_epoch(self, runner): + """Evaluate the model only at the start of training by epoch.""" + if not (self.by_epoch and self.initial_flag): + return + if self.start is not None and runner.epoch >= self.start: + self.after_train_epoch(runner) + self.initial_flag = False + + def after_train_iter(self, runner): + """Called after every training iter to evaluate the results.""" + if not self.by_epoch and self._should_evaluate(runner): + # Because the priority of EvalHook is higher than LoggerHook, the + # training log and the evaluating log are mixed. Therefore, + # we need to dump the training log and clear it before evaluating + # log is generated. In addition, this problem will only appear in + # `IterBasedRunner` whose `self.by_epoch` is False, because + # `EpochBasedRunner` whose `self.by_epoch` is True calls + # `_do_evaluate` in `after_train_epoch` stage, and at this stage + # the training log has been printed, so it will not cause any + # problem. more details at + # https://github.com/open-mmlab/mmsegmentation/issues/694 + for hook in runner._hooks: + if isinstance(hook, LoggerHook): + hook.after_train_iter(runner) + runner.log_buffer.clear() + + self._do_evaluate(runner) + + def after_train_epoch(self, runner): + """Called after every training epoch to evaluate the results.""" + if self.by_epoch and self._should_evaluate(runner): + self._do_evaluate(runner) + + def _do_evaluate(self, runner): + """perform evaluation and save ckpt.""" + results = self.test_fn(runner.model, self.dataloader) + runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) + key_score = self.evaluate(runner, results) + # the key_score may be `None` so it needs to skip the action to save + # the best checkpoint + if self.save_best and key_score: + self._save_ckpt(runner, key_score) + + def _should_evaluate(self, runner): + """Judge whether to perform evaluation. + + Here is the rule to judge whether to perform evaluation: + 1. It will not perform evaluation during the epoch/iteration interval, + which is determined by ``self.interval``. + 2. It will not perform evaluation if the start time is larger than + current time. + 3. It will not perform evaluation when current time is larger than + the start time but during epoch/iteration interval. + + Returns: + bool: The flag indicating whether to perform evaluation. + """ + if self.by_epoch: + current = runner.epoch + check_time = self.every_n_epochs + else: + current = runner.iter + check_time = self.every_n_iters + + if self.start is None: + if not check_time(runner, self.interval): + # No evaluation during the interval. + return False + elif (current + 1) < self.start: + # No evaluation if start is larger than the current time. + return False + else: + # Evaluation only at epochs/iters 3, 5, 7... + # if start==3 and interval==2 + if (current + 1 - self.start) % self.interval: + return False + return True + + def _save_ckpt(self, runner, key_score): + """Save the best checkpoint. + + It will compare the score according to the compare function, write + related information (best score, best checkpoint path) and save the + best checkpoint into ``work_dir``. + """ + if self.by_epoch: + current = f'epoch_{runner.epoch + 1}' + cur_type, cur_time = 'epoch', runner.epoch + 1 + else: + current = f'iter_{runner.iter + 1}' + cur_type, cur_time = 'iter', runner.iter + 1 + + best_score = runner.meta['hook_msgs'].get( + 'best_score', self.init_value_map[self.rule]) + if self.compare_func(key_score, best_score): + best_score = key_score + runner.meta['hook_msgs']['best_score'] = best_score + + if self.best_ckpt_path and self.file_client.isfile( + self.best_ckpt_path): + self.file_client.remove(self.best_ckpt_path) + runner.logger.info( + (f'The previous best checkpoint {self.best_ckpt_path} was ' + 'removed')) + + best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' + self.best_ckpt_path = self.file_client.join_path( + self.out_dir, best_ckpt_name) + runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path + + runner.save_checkpoint( + self.out_dir, best_ckpt_name, create_symlink=False) + runner.logger.info( + f'Now best checkpoint is saved as {best_ckpt_name}.') + runner.logger.info( + f'Best {self.key_indicator} is {best_score:0.4f} ' + f'at {cur_time} {cur_type}.') + + def evaluate(self, runner, results): + """Evaluate the results. + + Args: + runner (:obj:`mmcv.Runner`): The underlined training runner. + results (list): Output results. + """ + eval_res = self.dataloader.dataset.evaluate( + results, logger=runner.logger, **self.eval_kwargs) + + for name, val in eval_res.items(): + runner.log_buffer.output[name] = val + runner.log_buffer.ready = True + + if self.save_best is not None: + # If the performance of model is pool, the `eval_res` may be an + # empty dict and it will raise exception when `self.save_best` is + # not None. More details at + # https://github.com/open-mmlab/mmdetection/issues/6265. + if not eval_res: + warnings.warn( + 'Since `eval_res` is an empty dict, the behavior to save ' + 'the best checkpoint will be skipped in this evaluation.') + return None + + if self.key_indicator == 'auto': + # infer from eval_results + self._init_rule(self.rule, list(eval_res.keys())[0]) + return eval_res[self.key_indicator] + + return None + + +class DistEvalHook(EvalHook): + """Distributed evaluation hook. + + This hook will regularly perform evaluation in a given interval when + performing in distributed environment. + + Args: + dataloader (DataLoader): A PyTorch dataloader, whose dataset has + implemented ``evaluate`` function. + start (int | None, optional): Evaluation starting epoch. It enables + evaluation before the training starts if ``start`` <= the resuming + epoch. If None, whether to evaluate is merely decided by + ``interval``. Default: None. + interval (int): Evaluation interval. Default: 1. + by_epoch (bool): Determine perform evaluation by epoch or by iteration. + If set to True, it will perform by epoch. Otherwise, by iteration. + default: True. + save_best (str, optional): If a metric is specified, it would measure + the best checkpoint during evaluation. The information about best + checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep + best score value and best checkpoint path, which will be also + loaded when resume checkpoint. Options are the evaluation metrics + on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox + detection and instance segmentation. ``AR@100`` for proposal + recall. If ``save_best`` is ``auto``, the first key of the returned + ``OrderedDict`` result will be used. Default: None. + rule (str | None, optional): Comparison rule for best score. If set to + None, it will infer a reasonable rule. Keys such as 'acc', 'top' + .etc will be inferred by 'greater' rule. Keys contain 'loss' will + be inferred by 'less' rule. Options are 'greater', 'less', None. + Default: None. + test_fn (callable, optional): test a model with samples from a + dataloader in a multi-gpu manner, and return the test results. If + ``None``, the default test function ``mmcv.engine.multi_gpu_test`` + will be used. (default: ``None``) + tmpdir (str | None): Temporary directory to save the results of all + processes. Default: None. + gpu_collect (bool): Whether to use gpu or cpu to collect results. + Default: False. + broadcast_bn_buffer (bool): Whether to broadcast the + buffer(running_mean and running_var) of rank 0 to other rank + before evaluation. Default: True. + out_dir (str, optional): The root directory to save checkpoints. If not + specified, `runner.work_dir` will be used by default. If specified, + the `out_dir` will be the concatenation of `out_dir` and the last + level directory of `runner.work_dir`. + file_client_args (dict): Arguments to instantiate a FileClient. + See :class:`mmcv.fileio.FileClient` for details. Default: None. + **eval_kwargs: Evaluation arguments fed into the evaluate function of + the dataset. + """ + + def __init__(self, + dataloader, + start=None, + interval=1, + by_epoch=True, + save_best=None, + rule=None, + test_fn=None, + greater_keys=None, + less_keys=None, + broadcast_bn_buffer=True, + tmpdir=None, + gpu_collect=False, + out_dir=None, + file_client_args=None, + **eval_kwargs): + + if test_fn is None: + from annotator.mmpkg.mmcv.engine import multi_gpu_test + test_fn = multi_gpu_test + + super().__init__( + dataloader, + start=start, + interval=interval, + by_epoch=by_epoch, + save_best=save_best, + rule=rule, + test_fn=test_fn, + greater_keys=greater_keys, + less_keys=less_keys, + out_dir=out_dir, + file_client_args=file_client_args, + **eval_kwargs) + + self.broadcast_bn_buffer = broadcast_bn_buffer + self.tmpdir = tmpdir + self.gpu_collect = gpu_collect + + def _do_evaluate(self, runner): + """perform evaluation and save ckpt.""" + # Synchronization of BatchNorm's buffer (running_mean + # and running_var) is not supported in the DDP of pytorch, + # which may cause the inconsistent performance of models in + # different ranks, so we broadcast BatchNorm's buffers + # of rank 0 to other ranks to avoid this. + if self.broadcast_bn_buffer: + model = runner.model + for name, module in model.named_modules(): + if isinstance(module, + _BatchNorm) and module.track_running_stats: + dist.broadcast(module.running_var, 0) + dist.broadcast(module.running_mean, 0) + + tmpdir = self.tmpdir + if tmpdir is None: + tmpdir = osp.join(runner.work_dir, '.eval_hook') + + results = self.test_fn( + runner.model, + self.dataloader, + tmpdir=tmpdir, + gpu_collect=self.gpu_collect) + if runner.rank == 0: + print('\n') + runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) + key_score = self.evaluate(runner, results) + # the key_score may be `None` so it needs to skip the action to + # save the best checkpoint + if self.save_best and key_score: + self._save_ckpt(runner, key_score) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/hook.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/hook.py new file mode 100644 index 0000000000000000000000000000000000000000..bd31f985fee739ccb7ac62eefc6cef9f0c0d65d0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/hook.py @@ -0,0 +1,92 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from annotator.mmpkg.mmcv.utils import Registry, is_method_overridden + +HOOKS = Registry('hook') + + +class Hook: + stages = ('before_run', 'before_train_epoch', 'before_train_iter', + 'after_train_iter', 'after_train_epoch', 'before_val_epoch', + 'before_val_iter', 'after_val_iter', 'after_val_epoch', + 'after_run') + + def before_run(self, runner): + pass + + def after_run(self, runner): + pass + + def before_epoch(self, runner): + pass + + def after_epoch(self, runner): + pass + + def before_iter(self, runner): + pass + + def after_iter(self, runner): + pass + + def before_train_epoch(self, runner): + self.before_epoch(runner) + + def before_val_epoch(self, runner): + self.before_epoch(runner) + + def after_train_epoch(self, runner): + self.after_epoch(runner) + + def after_val_epoch(self, runner): + self.after_epoch(runner) + + def before_train_iter(self, runner): + self.before_iter(runner) + + def before_val_iter(self, runner): + self.before_iter(runner) + + def after_train_iter(self, runner): + self.after_iter(runner) + + def after_val_iter(self, runner): + self.after_iter(runner) + + def every_n_epochs(self, runner, n): + return (runner.epoch + 1) % n == 0 if n > 0 else False + + def every_n_inner_iters(self, runner, n): + return (runner.inner_iter + 1) % n == 0 if n > 0 else False + + def every_n_iters(self, runner, n): + return (runner.iter + 1) % n == 0 if n > 0 else False + + def end_of_epoch(self, runner): + return runner.inner_iter + 1 == len(runner.data_loader) + + def is_last_epoch(self, runner): + return runner.epoch + 1 == runner._max_epochs + + def is_last_iter(self, runner): + return runner.iter + 1 == runner._max_iters + + def get_triggered_stages(self): + trigger_stages = set() + for stage in Hook.stages: + if is_method_overridden(stage, Hook, self): + trigger_stages.add(stage) + + # some methods will be triggered in multi stages + # use this dict to map method to stages. + method_stages_map = { + 'before_epoch': ['before_train_epoch', 'before_val_epoch'], + 'after_epoch': ['after_train_epoch', 'after_val_epoch'], + 'before_iter': ['before_train_iter', 'before_val_iter'], + 'after_iter': ['after_train_iter', 'after_val_iter'], + } + + for method, map_stages in method_stages_map.items(): + if is_method_overridden(method, Hook, self): + trigger_stages.update(map_stages) + + return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/iter_timer.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/iter_timer.py new file mode 100644 index 0000000000000000000000000000000000000000..cfd5002fe85ffc6992155ac01003878064a1d9be --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/iter_timer.py @@ -0,0 +1,18 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import time + +from .hook import HOOKS, Hook + + +@HOOKS.register_module() +class IterTimerHook(Hook): + + def before_epoch(self, runner): + self.t = time.time() + + def before_iter(self, runner): + runner.log_buffer.update({'data_time': time.time() - self.t}) + + def after_iter(self, runner): + runner.log_buffer.update({'time': time.time() - self.t}) + self.t = time.time() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a0b6b345640a895368ac8a647afef6f24333d90e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .base import LoggerHook +from .dvclive import DvcliveLoggerHook +from .mlflow import MlflowLoggerHook +from .neptune import NeptuneLoggerHook +from .pavi import PaviLoggerHook +from .tensorboard import TensorboardLoggerHook +from .text import TextLoggerHook +from .wandb import WandbLoggerHook + +__all__ = [ + 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', + 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', + 'NeptuneLoggerHook', 'DvcliveLoggerHook' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/base.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/base.py new file mode 100644 index 0000000000000000000000000000000000000000..f845256729458ced821762a1b8ef881e17ff9955 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/base.py @@ -0,0 +1,166 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import numbers +from abc import ABCMeta, abstractmethod + +import numpy as np +import torch + +from ..hook import Hook + + +class LoggerHook(Hook): + """Base class for logger hooks. + + Args: + interval (int): Logging interval (every k iterations). + ignore_last (bool): Ignore the log of last iterations in each epoch + if less than `interval`. + reset_flag (bool): Whether to clear the output buffer after logging. + by_epoch (bool): Whether EpochBasedRunner is used. + """ + + __metaclass__ = ABCMeta + + def __init__(self, + interval=10, + ignore_last=True, + reset_flag=False, + by_epoch=True): + self.interval = interval + self.ignore_last = ignore_last + self.reset_flag = reset_flag + self.by_epoch = by_epoch + + @abstractmethod + def log(self, runner): + pass + + @staticmethod + def is_scalar(val, include_np=True, include_torch=True): + """Tell the input variable is a scalar or not. + + Args: + val: Input variable. + include_np (bool): Whether include 0-d np.ndarray as a scalar. + include_torch (bool): Whether include 0-d torch.Tensor as a scalar. + + Returns: + bool: True or False. + """ + if isinstance(val, numbers.Number): + return True + elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: + return True + elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: + return True + else: + return False + + def get_mode(self, runner): + if runner.mode == 'train': + if 'time' in runner.log_buffer.output: + mode = 'train' + else: + mode = 'val' + elif runner.mode == 'val': + mode = 'val' + else: + raise ValueError(f"runner mode should be 'train' or 'val', " + f'but got {runner.mode}') + return mode + + def get_epoch(self, runner): + if runner.mode == 'train': + epoch = runner.epoch + 1 + elif runner.mode == 'val': + # normal val mode + # runner.epoch += 1 has been done before val workflow + epoch = runner.epoch + else: + raise ValueError(f"runner mode should be 'train' or 'val', " + f'but got {runner.mode}') + return epoch + + def get_iter(self, runner, inner_iter=False): + """Get the current training iteration step.""" + if self.by_epoch and inner_iter: + current_iter = runner.inner_iter + 1 + else: + current_iter = runner.iter + 1 + return current_iter + + def get_lr_tags(self, runner): + tags = {} + lrs = runner.current_lr() + if isinstance(lrs, dict): + for name, value in lrs.items(): + tags[f'learning_rate/{name}'] = value[0] + else: + tags['learning_rate'] = lrs[0] + return tags + + def get_momentum_tags(self, runner): + tags = {} + momentums = runner.current_momentum() + if isinstance(momentums, dict): + for name, value in momentums.items(): + tags[f'momentum/{name}'] = value[0] + else: + tags['momentum'] = momentums[0] + return tags + + def get_loggable_tags(self, + runner, + allow_scalar=True, + allow_text=False, + add_mode=True, + tags_to_skip=('time', 'data_time')): + tags = {} + for var, val in runner.log_buffer.output.items(): + if var in tags_to_skip: + continue + if self.is_scalar(val) and not allow_scalar: + continue + if isinstance(val, str) and not allow_text: + continue + if add_mode: + var = f'{self.get_mode(runner)}/{var}' + tags[var] = val + tags.update(self.get_lr_tags(runner)) + tags.update(self.get_momentum_tags(runner)) + return tags + + def before_run(self, runner): + for hook in runner.hooks[::-1]: + if isinstance(hook, LoggerHook): + hook.reset_flag = True + break + + def before_epoch(self, runner): + runner.log_buffer.clear() # clear logs of last epoch + + def after_train_iter(self, runner): + if self.by_epoch and self.every_n_inner_iters(runner, self.interval): + runner.log_buffer.average(self.interval) + elif not self.by_epoch and self.every_n_iters(runner, self.interval): + runner.log_buffer.average(self.interval) + elif self.end_of_epoch(runner) and not self.ignore_last: + # not precise but more stable + runner.log_buffer.average(self.interval) + + if runner.log_buffer.ready: + self.log(runner) + if self.reset_flag: + runner.log_buffer.clear_output() + + def after_train_epoch(self, runner): + if runner.log_buffer.ready: + self.log(runner) + if self.reset_flag: + runner.log_buffer.clear_output() + + def after_val_epoch(self, runner): + runner.log_buffer.average() + self.log(runner) + if self.reset_flag: + runner.log_buffer.clear_output() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/dvclive.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/dvclive.py new file mode 100644 index 0000000000000000000000000000000000000000..687cdc58c0336c92b1e4f9a410ba67ebaab2bc7a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/dvclive.py @@ -0,0 +1,58 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from ...dist_utils import master_only +from ..hook import HOOKS +from .base import LoggerHook + + +@HOOKS.register_module() +class DvcliveLoggerHook(LoggerHook): + """Class to log metrics with dvclive. + + It requires `dvclive`_ to be installed. + + Args: + path (str): Directory where dvclive will write TSV log files. + interval (int): Logging interval (every k iterations). + Default 10. + ignore_last (bool): Ignore the log of last iterations in each epoch + if less than `interval`. + Default: True. + reset_flag (bool): Whether to clear the output buffer after logging. + Default: True. + by_epoch (bool): Whether EpochBasedRunner is used. + Default: True. + + .. _dvclive: + https://dvc.org/doc/dvclive + """ + + def __init__(self, + path, + interval=10, + ignore_last=True, + reset_flag=True, + by_epoch=True): + + super(DvcliveLoggerHook, self).__init__(interval, ignore_last, + reset_flag, by_epoch) + self.path = path + self.import_dvclive() + + def import_dvclive(self): + try: + import dvclive + except ImportError: + raise ImportError( + 'Please run "pip install dvclive" to install dvclive') + self.dvclive = dvclive + + @master_only + def before_run(self, runner): + self.dvclive.init(self.path) + + @master_only + def log(self, runner): + tags = self.get_loggable_tags(runner) + if tags: + for k, v in tags.items(): + self.dvclive.log(k, v, step=self.get_iter(runner)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/mlflow.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/mlflow.py new file mode 100644 index 0000000000000000000000000000000000000000..f9a72592be47b534ce22573775fd5a7e8e86d72d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/mlflow.py @@ -0,0 +1,78 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from ...dist_utils import master_only +from ..hook import HOOKS +from .base import LoggerHook + + +@HOOKS.register_module() +class MlflowLoggerHook(LoggerHook): + + def __init__(self, + exp_name=None, + tags=None, + log_model=True, + interval=10, + ignore_last=True, + reset_flag=False, + by_epoch=True): + """Class to log metrics and (optionally) a trained model to MLflow. + + It requires `MLflow`_ to be installed. + + Args: + exp_name (str, optional): Name of the experiment to be used. + Default None. + If not None, set the active experiment. + If experiment does not exist, an experiment with provided name + will be created. + tags (dict of str: str, optional): Tags for the current run. + Default None. + If not None, set tags for the current run. + log_model (bool, optional): Whether to log an MLflow artifact. + Default True. + If True, log runner.model as an MLflow artifact + for the current run. + interval (int): Logging interval (every k iterations). + ignore_last (bool): Ignore the log of last iterations in each epoch + if less than `interval`. + reset_flag (bool): Whether to clear the output buffer after logging + by_epoch (bool): Whether EpochBasedRunner is used. + + .. _MLflow: + https://www.mlflow.org/docs/latest/index.html + """ + super(MlflowLoggerHook, self).__init__(interval, ignore_last, + reset_flag, by_epoch) + self.import_mlflow() + self.exp_name = exp_name + self.tags = tags + self.log_model = log_model + + def import_mlflow(self): + try: + import mlflow + import mlflow.pytorch as mlflow_pytorch + except ImportError: + raise ImportError( + 'Please run "pip install mlflow" to install mlflow') + self.mlflow = mlflow + self.mlflow_pytorch = mlflow_pytorch + + @master_only + def before_run(self, runner): + super(MlflowLoggerHook, self).before_run(runner) + if self.exp_name is not None: + self.mlflow.set_experiment(self.exp_name) + if self.tags is not None: + self.mlflow.set_tags(self.tags) + + @master_only + def log(self, runner): + tags = self.get_loggable_tags(runner) + if tags: + self.mlflow.log_metrics(tags, step=self.get_iter(runner)) + + @master_only + def after_run(self, runner): + if self.log_model: + self.mlflow_pytorch.log_model(runner.model, 'models') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/neptune.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/neptune.py new file mode 100644 index 0000000000000000000000000000000000000000..7a38772b0c93a8608f32c6357b8616e77c139dc9 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/neptune.py @@ -0,0 +1,82 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from ...dist_utils import master_only +from ..hook import HOOKS +from .base import LoggerHook + + +@HOOKS.register_module() +class NeptuneLoggerHook(LoggerHook): + """Class to log metrics to NeptuneAI. + + It requires `neptune-client` to be installed. + + Args: + init_kwargs (dict): a dict contains the initialization keys as below: + - project (str): Name of a project in a form of + namespace/project_name. If None, the value of + NEPTUNE_PROJECT environment variable will be taken. + - api_token (str): User’s API token. + If None, the value of NEPTUNE_API_TOKEN environment + variable will be taken. Note: It is strongly recommended + to use NEPTUNE_API_TOKEN environment variable rather than + placing your API token in plain text in your source code. + - name (str, optional, default is 'Untitled'): Editable name of + the run. Name is displayed in the run's Details and in + Runs table as a column. + Check https://docs.neptune.ai/api-reference/neptune#init for + more init arguments. + interval (int): Logging interval (every k iterations). + ignore_last (bool): Ignore the log of last iterations in each epoch + if less than `interval`. + reset_flag (bool): Whether to clear the output buffer after logging + by_epoch (bool): Whether EpochBasedRunner is used. + + .. _NeptuneAI: + https://docs.neptune.ai/you-should-know/logging-metadata + """ + + def __init__(self, + init_kwargs=None, + interval=10, + ignore_last=True, + reset_flag=True, + with_step=True, + by_epoch=True): + + super(NeptuneLoggerHook, self).__init__(interval, ignore_last, + reset_flag, by_epoch) + self.import_neptune() + self.init_kwargs = init_kwargs + self.with_step = with_step + + def import_neptune(self): + try: + import neptune.new as neptune + except ImportError: + raise ImportError( + 'Please run "pip install neptune-client" to install neptune') + self.neptune = neptune + self.run = None + + @master_only + def before_run(self, runner): + if self.init_kwargs: + self.run = self.neptune.init(**self.init_kwargs) + else: + self.run = self.neptune.init() + + @master_only + def log(self, runner): + tags = self.get_loggable_tags(runner) + if tags: + for tag_name, tag_value in tags.items(): + if self.with_step: + self.run[tag_name].log( + tag_value, step=self.get_iter(runner)) + else: + tags['global_step'] = self.get_iter(runner) + self.run[tag_name].log(tags) + + @master_only + def after_run(self, runner): + self.run.stop() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/pavi.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/pavi.py new file mode 100644 index 0000000000000000000000000000000000000000..5d1c4286920361e6b80f135b8d60b250f98f507a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/pavi.py @@ -0,0 +1,117 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import json +import os +import os.path as osp + +import torch +import yaml + +import annotator.mmpkg.mmcv as mmcv +from ....parallel.utils import is_module_wrapper +from ...dist_utils import master_only +from ..hook import HOOKS +from .base import LoggerHook + + +@HOOKS.register_module() +class PaviLoggerHook(LoggerHook): + + def __init__(self, + init_kwargs=None, + add_graph=False, + add_last_ckpt=False, + interval=10, + ignore_last=True, + reset_flag=False, + by_epoch=True, + img_key='img_info'): + super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, + by_epoch) + self.init_kwargs = init_kwargs + self.add_graph = add_graph + self.add_last_ckpt = add_last_ckpt + self.img_key = img_key + + @master_only + def before_run(self, runner): + super(PaviLoggerHook, self).before_run(runner) + try: + from pavi import SummaryWriter + except ImportError: + raise ImportError('Please run "pip install pavi" to install pavi.') + + self.run_name = runner.work_dir.split('/')[-1] + + if not self.init_kwargs: + self.init_kwargs = dict() + self.init_kwargs['name'] = self.run_name + self.init_kwargs['model'] = runner._model_name + if runner.meta is not None: + if 'config_dict' in runner.meta: + config_dict = runner.meta['config_dict'] + assert isinstance( + config_dict, + dict), ('meta["config_dict"] has to be of a dict, ' + f'but got {type(config_dict)}') + elif 'config_file' in runner.meta: + config_file = runner.meta['config_file'] + config_dict = dict(mmcv.Config.fromfile(config_file)) + else: + config_dict = None + if config_dict is not None: + # 'max_.*iter' is parsed in pavi sdk as the maximum iterations + # to properly set up the progress bar. + config_dict = config_dict.copy() + config_dict.setdefault('max_iter', runner.max_iters) + # non-serializable values are first converted in + # mmcv.dump to json + config_dict = json.loads( + mmcv.dump(config_dict, file_format='json')) + session_text = yaml.dump(config_dict) + self.init_kwargs['session_text'] = session_text + self.writer = SummaryWriter(**self.init_kwargs) + + def get_step(self, runner): + """Get the total training step/epoch.""" + if self.get_mode(runner) == 'val' and self.by_epoch: + return self.get_epoch(runner) + else: + return self.get_iter(runner) + + @master_only + def log(self, runner): + tags = self.get_loggable_tags(runner, add_mode=False) + if tags: + self.writer.add_scalars( + self.get_mode(runner), tags, self.get_step(runner)) + + @master_only + def after_run(self, runner): + if self.add_last_ckpt: + ckpt_path = osp.join(runner.work_dir, 'latest.pth') + if osp.islink(ckpt_path): + ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) + + if osp.isfile(ckpt_path): + # runner.epoch += 1 has been done before `after_run`. + iteration = runner.epoch if self.by_epoch else runner.iter + return self.writer.add_snapshot_file( + tag=self.run_name, + snapshot_file_path=ckpt_path, + iteration=iteration) + + # flush the buffer and send a task ending signal to Pavi + self.writer.close() + + @master_only + def before_epoch(self, runner): + if runner.epoch == 0 and self.add_graph: + if is_module_wrapper(runner.model): + _model = runner.model.module + else: + _model = runner.model + device = next(_model.parameters()).device + data = next(iter(runner.data_loader)) + image = data[self.img_key][0:1].to(device) + with torch.no_grad(): + self.writer.add_graph(_model, image) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/tensorboard.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/tensorboard.py new file mode 100644 index 0000000000000000000000000000000000000000..7c480a560e90f5b06abb4afaf9597aaf7c1eaa82 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/tensorboard.py @@ -0,0 +1,57 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os.path as osp + +from annotator.mmpkg.mmcv.utils import TORCH_VERSION, digit_version +from ...dist_utils import master_only +from ..hook import HOOKS +from .base import LoggerHook + + +@HOOKS.register_module() +class TensorboardLoggerHook(LoggerHook): + + def __init__(self, + log_dir=None, + interval=10, + ignore_last=True, + reset_flag=False, + by_epoch=True): + super(TensorboardLoggerHook, self).__init__(interval, ignore_last, + reset_flag, by_epoch) + self.log_dir = log_dir + + @master_only + def before_run(self, runner): + super(TensorboardLoggerHook, self).before_run(runner) + if (TORCH_VERSION == 'parrots' + or digit_version(TORCH_VERSION) < digit_version('1.1')): + try: + from tensorboardX import SummaryWriter + except ImportError: + raise ImportError('Please install tensorboardX to use ' + 'TensorboardLoggerHook.') + else: + try: + from torch.utils.tensorboard import SummaryWriter + except ImportError: + raise ImportError( + 'Please run "pip install future tensorboard" to install ' + 'the dependencies to use torch.utils.tensorboard ' + '(applicable to PyTorch 1.1 or higher)') + + if self.log_dir is None: + self.log_dir = osp.join(runner.work_dir, 'tf_logs') + self.writer = SummaryWriter(self.log_dir) + + @master_only + def log(self, runner): + tags = self.get_loggable_tags(runner, allow_text=True) + for tag, val in tags.items(): + if isinstance(val, str): + self.writer.add_text(tag, val, self.get_iter(runner)) + else: + self.writer.add_scalar(tag, val, self.get_iter(runner)) + + @master_only + def after_run(self, runner): + self.writer.close() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/text.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/text.py new file mode 100644 index 0000000000000000000000000000000000000000..0b30577469d5f70e544e1ce73816326e38dadb20 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/text.py @@ -0,0 +1,256 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import datetime +import os +import os.path as osp +from collections import OrderedDict + +import torch +import torch.distributed as dist + +import annotator.mmpkg.mmcv as mmcv +from annotator.mmpkg.mmcv.fileio.file_client import FileClient +from annotator.mmpkg.mmcv.utils import is_tuple_of, scandir +from ..hook import HOOKS +from .base import LoggerHook + + +@HOOKS.register_module() +class TextLoggerHook(LoggerHook): + """Logger hook in text. + + In this logger hook, the information will be printed on terminal and + saved in json file. + + Args: + by_epoch (bool, optional): Whether EpochBasedRunner is used. + Default: True. + interval (int, optional): Logging interval (every k iterations). + Default: 10. + ignore_last (bool, optional): Ignore the log of last iterations in each + epoch if less than :attr:`interval`. Default: True. + reset_flag (bool, optional): Whether to clear the output buffer after + logging. Default: False. + interval_exp_name (int, optional): Logging interval for experiment + name. This feature is to help users conveniently get the experiment + information from screen or log file. Default: 1000. + out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. + If ``out_dir`` is specified, logs will be copied to a new directory + which is the concatenation of ``out_dir`` and the last level + directory of ``runner.work_dir``. Default: None. + `New in version 1.3.16.` + out_suffix (str or tuple[str], optional): Those filenames ending with + ``out_suffix`` will be copied to ``out_dir``. + Default: ('.log.json', '.log', '.py'). + `New in version 1.3.16.` + keep_local (bool, optional): Whether to keep local log when + :attr:`out_dir` is specified. If False, the local log will be + removed. Default: True. + `New in version 1.3.16.` + file_client_args (dict, optional): Arguments to instantiate a + FileClient. See :class:`mmcv.fileio.FileClient` for details. + Default: None. + `New in version 1.3.16.` + """ + + def __init__(self, + by_epoch=True, + interval=10, + ignore_last=True, + reset_flag=False, + interval_exp_name=1000, + out_dir=None, + out_suffix=('.log.json', '.log', '.py'), + keep_local=True, + file_client_args=None): + super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, + by_epoch) + self.by_epoch = by_epoch + self.time_sec_tot = 0 + self.interval_exp_name = interval_exp_name + + if out_dir is None and file_client_args is not None: + raise ValueError( + 'file_client_args should be "None" when `out_dir` is not' + 'specified.') + self.out_dir = out_dir + + if not (out_dir is None or isinstance(out_dir, str) + or is_tuple_of(out_dir, str)): + raise TypeError('out_dir should be "None" or string or tuple of ' + 'string, but got {out_dir}') + self.out_suffix = out_suffix + + self.keep_local = keep_local + self.file_client_args = file_client_args + if self.out_dir is not None: + self.file_client = FileClient.infer_client(file_client_args, + self.out_dir) + + def before_run(self, runner): + super(TextLoggerHook, self).before_run(runner) + + if self.out_dir is not None: + self.file_client = FileClient.infer_client(self.file_client_args, + self.out_dir) + # The final `self.out_dir` is the concatenation of `self.out_dir` + # and the last level directory of `runner.work_dir` + basename = osp.basename(runner.work_dir.rstrip(osp.sep)) + self.out_dir = self.file_client.join_path(self.out_dir, basename) + runner.logger.info( + (f'Text logs will be saved to {self.out_dir} by ' + f'{self.file_client.name} after the training process.')) + + self.start_iter = runner.iter + self.json_log_path = osp.join(runner.work_dir, + f'{runner.timestamp}.log.json') + if runner.meta is not None: + self._dump_log(runner.meta, runner) + + def _get_max_memory(self, runner): + device = getattr(runner.model, 'output_device', None) + mem = torch.cuda.max_memory_allocated(device=device) + mem_mb = torch.tensor([mem / (1024 * 1024)], + dtype=torch.int, + device=device) + if runner.world_size > 1: + dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) + return mem_mb.item() + + def _log_info(self, log_dict, runner): + # print exp name for users to distinguish experiments + # at every ``interval_exp_name`` iterations and the end of each epoch + if runner.meta is not None and 'exp_name' in runner.meta: + if (self.every_n_iters(runner, self.interval_exp_name)) or ( + self.by_epoch and self.end_of_epoch(runner)): + exp_info = f'Exp name: {runner.meta["exp_name"]}' + runner.logger.info(exp_info) + + if log_dict['mode'] == 'train': + if isinstance(log_dict['lr'], dict): + lr_str = [] + for k, val in log_dict['lr'].items(): + lr_str.append(f'lr_{k}: {val:.3e}') + lr_str = ' '.join(lr_str) + else: + lr_str = f'lr: {log_dict["lr"]:.3e}' + + # by epoch: Epoch [4][100/1000] + # by iter: Iter [100/100000] + if self.by_epoch: + log_str = f'Epoch [{log_dict["epoch"]}]' \ + f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' + else: + log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' + log_str += f'{lr_str}, ' + + if 'time' in log_dict.keys(): + self.time_sec_tot += (log_dict['time'] * self.interval) + time_sec_avg = self.time_sec_tot / ( + runner.iter - self.start_iter + 1) + eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) + eta_str = str(datetime.timedelta(seconds=int(eta_sec))) + log_str += f'eta: {eta_str}, ' + log_str += f'time: {log_dict["time"]:.3f}, ' \ + f'data_time: {log_dict["data_time"]:.3f}, ' + # statistic memory + if torch.cuda.is_available(): + log_str += f'memory: {log_dict["memory"]}, ' + else: + # val/test time + # here 1000 is the length of the val dataloader + # by epoch: Epoch[val] [4][1000] + # by iter: Iter[val] [1000] + if self.by_epoch: + log_str = f'Epoch({log_dict["mode"]}) ' \ + f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' + else: + log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' + + log_items = [] + for name, val in log_dict.items(): + # TODO: resolve this hack + # these items have been in log_str + if name in [ + 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', + 'memory', 'epoch' + ]: + continue + if isinstance(val, float): + val = f'{val:.4f}' + log_items.append(f'{name}: {val}') + log_str += ', '.join(log_items) + + runner.logger.info(log_str) + + def _dump_log(self, log_dict, runner): + # dump log in json format + json_log = OrderedDict() + for k, v in log_dict.items(): + json_log[k] = self._round_float(v) + # only append log at last line + if runner.rank == 0: + with open(self.json_log_path, 'a+') as f: + mmcv.dump(json_log, f, file_format='json') + f.write('\n') + + def _round_float(self, items): + if isinstance(items, list): + return [self._round_float(item) for item in items] + elif isinstance(items, float): + return round(items, 5) + else: + return items + + def log(self, runner): + if 'eval_iter_num' in runner.log_buffer.output: + # this doesn't modify runner.iter and is regardless of by_epoch + cur_iter = runner.log_buffer.output.pop('eval_iter_num') + else: + cur_iter = self.get_iter(runner, inner_iter=True) + + log_dict = OrderedDict( + mode=self.get_mode(runner), + epoch=self.get_epoch(runner), + iter=cur_iter) + + # only record lr of the first param group + cur_lr = runner.current_lr() + if isinstance(cur_lr, list): + log_dict['lr'] = cur_lr[0] + else: + assert isinstance(cur_lr, dict) + log_dict['lr'] = {} + for k, lr_ in cur_lr.items(): + assert isinstance(lr_, list) + log_dict['lr'].update({k: lr_[0]}) + + if 'time' in runner.log_buffer.output: + # statistic memory + if torch.cuda.is_available(): + log_dict['memory'] = self._get_max_memory(runner) + + log_dict = dict(log_dict, **runner.log_buffer.output) + + self._log_info(log_dict, runner) + self._dump_log(log_dict, runner) + return log_dict + + def after_run(self, runner): + # copy or upload logs to self.out_dir + if self.out_dir is not None: + for filename in scandir(runner.work_dir, self.out_suffix, True): + local_filepath = osp.join(runner.work_dir, filename) + out_filepath = self.file_client.join_path( + self.out_dir, filename) + with open(local_filepath, 'r') as f: + self.file_client.put_text(f.read(), out_filepath) + + runner.logger.info( + (f'The file {local_filepath} has been uploaded to ' + f'{out_filepath}.')) + + if not self.keep_local: + os.remove(local_filepath) + runner.logger.info( + (f'{local_filepath} was removed due to the ' + '`self.keep_local=False`')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/wandb.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/wandb.py new file mode 100644 index 0000000000000000000000000000000000000000..9f6808462eb79ab2b04806a5d9f0d3dd079b5ea9 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/logger/wandb.py @@ -0,0 +1,56 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from ...dist_utils import master_only +from ..hook import HOOKS +from .base import LoggerHook + + +@HOOKS.register_module() +class WandbLoggerHook(LoggerHook): + + def __init__(self, + init_kwargs=None, + interval=10, + ignore_last=True, + reset_flag=False, + commit=True, + by_epoch=True, + with_step=True): + super(WandbLoggerHook, self).__init__(interval, ignore_last, + reset_flag, by_epoch) + self.import_wandb() + self.init_kwargs = init_kwargs + self.commit = commit + self.with_step = with_step + + def import_wandb(self): + try: + import wandb + except ImportError: + raise ImportError( + 'Please run "pip install wandb" to install wandb') + self.wandb = wandb + + @master_only + def before_run(self, runner): + super(WandbLoggerHook, self).before_run(runner) + if self.wandb is None: + self.import_wandb() + if self.init_kwargs: + self.wandb.init(**self.init_kwargs) + else: + self.wandb.init() + + @master_only + def log(self, runner): + tags = self.get_loggable_tags(runner) + if tags: + if self.with_step: + self.wandb.log( + tags, step=self.get_iter(runner), commit=self.commit) + else: + tags['global_step'] = self.get_iter(runner) + self.wandb.log(tags, commit=self.commit) + + @master_only + def after_run(self, runner): + self.wandb.join() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/lr_updater.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/lr_updater.py new file mode 100644 index 0000000000000000000000000000000000000000..b9851d2ca3c4e60b95ad734c19a2484b9ca7c708 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/lr_updater.py @@ -0,0 +1,670 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import numbers +from math import cos, pi + +import annotator.mmpkg.mmcv as mmcv +from .hook import HOOKS, Hook + + +class LrUpdaterHook(Hook): + """LR Scheduler in MMCV. + + Args: + by_epoch (bool): LR changes epoch by epoch + warmup (string): Type of warmup used. It can be None(use no warmup), + 'constant', 'linear' or 'exp' + warmup_iters (int): The number of iterations or epochs that warmup + lasts + warmup_ratio (float): LR used at the beginning of warmup equals to + warmup_ratio * initial_lr + warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters + means the number of epochs that warmup lasts, otherwise means the + number of iteration that warmup lasts + """ + + def __init__(self, + by_epoch=True, + warmup=None, + warmup_iters=0, + warmup_ratio=0.1, + warmup_by_epoch=False): + # validate the "warmup" argument + if warmup is not None: + if warmup not in ['constant', 'linear', 'exp']: + raise ValueError( + f'"{warmup}" is not a supported type for warming up, valid' + ' types are "constant" and "linear"') + if warmup is not None: + assert warmup_iters > 0, \ + '"warmup_iters" must be a positive integer' + assert 0 < warmup_ratio <= 1.0, \ + '"warmup_ratio" must be in range (0,1]' + + self.by_epoch = by_epoch + self.warmup = warmup + self.warmup_iters = warmup_iters + self.warmup_ratio = warmup_ratio + self.warmup_by_epoch = warmup_by_epoch + + if self.warmup_by_epoch: + self.warmup_epochs = self.warmup_iters + self.warmup_iters = None + else: + self.warmup_epochs = None + + self.base_lr = [] # initial lr for all param groups + self.regular_lr = [] # expected lr if no warming up is performed + + def _set_lr(self, runner, lr_groups): + if isinstance(runner.optimizer, dict): + for k, optim in runner.optimizer.items(): + for param_group, lr in zip(optim.param_groups, lr_groups[k]): + param_group['lr'] = lr + else: + for param_group, lr in zip(runner.optimizer.param_groups, + lr_groups): + param_group['lr'] = lr + + def get_lr(self, runner, base_lr): + raise NotImplementedError + + def get_regular_lr(self, runner): + if isinstance(runner.optimizer, dict): + lr_groups = {} + for k in runner.optimizer.keys(): + _lr_group = [ + self.get_lr(runner, _base_lr) + for _base_lr in self.base_lr[k] + ] + lr_groups.update({k: _lr_group}) + + return lr_groups + else: + return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] + + def get_warmup_lr(self, cur_iters): + + def _get_warmup_lr(cur_iters, regular_lr): + if self.warmup == 'constant': + warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] + elif self.warmup == 'linear': + k = (1 - cur_iters / self.warmup_iters) * (1 - + self.warmup_ratio) + warmup_lr = [_lr * (1 - k) for _lr in regular_lr] + elif self.warmup == 'exp': + k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) + warmup_lr = [_lr * k for _lr in regular_lr] + return warmup_lr + + if isinstance(self.regular_lr, dict): + lr_groups = {} + for key, regular_lr in self.regular_lr.items(): + lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) + return lr_groups + else: + return _get_warmup_lr(cur_iters, self.regular_lr) + + def before_run(self, runner): + # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, + # it will be set according to the optimizer params + if isinstance(runner.optimizer, dict): + self.base_lr = {} + for k, optim in runner.optimizer.items(): + for group in optim.param_groups: + group.setdefault('initial_lr', group['lr']) + _base_lr = [ + group['initial_lr'] for group in optim.param_groups + ] + self.base_lr.update({k: _base_lr}) + else: + for group in runner.optimizer.param_groups: + group.setdefault('initial_lr', group['lr']) + self.base_lr = [ + group['initial_lr'] for group in runner.optimizer.param_groups + ] + + def before_train_epoch(self, runner): + if self.warmup_iters is None: + epoch_len = len(runner.data_loader) + self.warmup_iters = self.warmup_epochs * epoch_len + + if not self.by_epoch: + return + + self.regular_lr = self.get_regular_lr(runner) + self._set_lr(runner, self.regular_lr) + + def before_train_iter(self, runner): + cur_iter = runner.iter + if not self.by_epoch: + self.regular_lr = self.get_regular_lr(runner) + if self.warmup is None or cur_iter >= self.warmup_iters: + self._set_lr(runner, self.regular_lr) + else: + warmup_lr = self.get_warmup_lr(cur_iter) + self._set_lr(runner, warmup_lr) + elif self.by_epoch: + if self.warmup is None or cur_iter > self.warmup_iters: + return + elif cur_iter == self.warmup_iters: + self._set_lr(runner, self.regular_lr) + else: + warmup_lr = self.get_warmup_lr(cur_iter) + self._set_lr(runner, warmup_lr) + + +@HOOKS.register_module() +class FixedLrUpdaterHook(LrUpdaterHook): + + def __init__(self, **kwargs): + super(FixedLrUpdaterHook, self).__init__(**kwargs) + + def get_lr(self, runner, base_lr): + return base_lr + + +@HOOKS.register_module() +class StepLrUpdaterHook(LrUpdaterHook): + """Step LR scheduler with min_lr clipping. + + Args: + step (int | list[int]): Step to decay the LR. If an int value is given, + regard it as the decay interval. If a list is given, decay LR at + these steps. + gamma (float, optional): Decay LR ratio. Default: 0.1. + min_lr (float, optional): Minimum LR value to keep. If LR after decay + is lower than `min_lr`, it will be clipped to this value. If None + is given, we don't perform lr clipping. Default: None. + """ + + def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): + if isinstance(step, list): + assert mmcv.is_list_of(step, int) + assert all([s > 0 for s in step]) + elif isinstance(step, int): + assert step > 0 + else: + raise TypeError('"step" must be a list or integer') + self.step = step + self.gamma = gamma + self.min_lr = min_lr + super(StepLrUpdaterHook, self).__init__(**kwargs) + + def get_lr(self, runner, base_lr): + progress = runner.epoch if self.by_epoch else runner.iter + + # calculate exponential term + if isinstance(self.step, int): + exp = progress // self.step + else: + exp = len(self.step) + for i, s in enumerate(self.step): + if progress < s: + exp = i + break + + lr = base_lr * (self.gamma**exp) + if self.min_lr is not None: + # clip to a minimum value + lr = max(lr, self.min_lr) + return lr + + +@HOOKS.register_module() +class ExpLrUpdaterHook(LrUpdaterHook): + + def __init__(self, gamma, **kwargs): + self.gamma = gamma + super(ExpLrUpdaterHook, self).__init__(**kwargs) + + def get_lr(self, runner, base_lr): + progress = runner.epoch if self.by_epoch else runner.iter + return base_lr * self.gamma**progress + + +@HOOKS.register_module() +class PolyLrUpdaterHook(LrUpdaterHook): + + def __init__(self, power=1., min_lr=0., **kwargs): + self.power = power + self.min_lr = min_lr + super(PolyLrUpdaterHook, self).__init__(**kwargs) + + def get_lr(self, runner, base_lr): + if self.by_epoch: + progress = runner.epoch + max_progress = runner.max_epochs + else: + progress = runner.iter + max_progress = runner.max_iters + coeff = (1 - progress / max_progress)**self.power + return (base_lr - self.min_lr) * coeff + self.min_lr + + +@HOOKS.register_module() +class InvLrUpdaterHook(LrUpdaterHook): + + def __init__(self, gamma, power=1., **kwargs): + self.gamma = gamma + self.power = power + super(InvLrUpdaterHook, self).__init__(**kwargs) + + def get_lr(self, runner, base_lr): + progress = runner.epoch if self.by_epoch else runner.iter + return base_lr * (1 + self.gamma * progress)**(-self.power) + + +@HOOKS.register_module() +class CosineAnnealingLrUpdaterHook(LrUpdaterHook): + + def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): + assert (min_lr is None) ^ (min_lr_ratio is None) + self.min_lr = min_lr + self.min_lr_ratio = min_lr_ratio + super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) + + def get_lr(self, runner, base_lr): + if self.by_epoch: + progress = runner.epoch + max_progress = runner.max_epochs + else: + progress = runner.iter + max_progress = runner.max_iters + + if self.min_lr_ratio is not None: + target_lr = base_lr * self.min_lr_ratio + else: + target_lr = self.min_lr + return annealing_cos(base_lr, target_lr, progress / max_progress) + + +@HOOKS.register_module() +class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): + """Flat + Cosine lr schedule. + + Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 + + Args: + start_percent (float): When to start annealing the learning rate + after the percentage of the total training steps. + The value should be in range [0, 1). + Default: 0.75 + min_lr (float, optional): The minimum lr. Default: None. + min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. + Either `min_lr` or `min_lr_ratio` should be specified. + Default: None. + """ + + def __init__(self, + start_percent=0.75, + min_lr=None, + min_lr_ratio=None, + **kwargs): + assert (min_lr is None) ^ (min_lr_ratio is None) + if start_percent < 0 or start_percent > 1 or not isinstance( + start_percent, float): + raise ValueError( + 'expected float between 0 and 1 start_percent, but ' + f'got {start_percent}') + self.start_percent = start_percent + self.min_lr = min_lr + self.min_lr_ratio = min_lr_ratio + super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) + + def get_lr(self, runner, base_lr): + if self.by_epoch: + start = round(runner.max_epochs * self.start_percent) + progress = runner.epoch - start + max_progress = runner.max_epochs - start + else: + start = round(runner.max_iters * self.start_percent) + progress = runner.iter - start + max_progress = runner.max_iters - start + + if self.min_lr_ratio is not None: + target_lr = base_lr * self.min_lr_ratio + else: + target_lr = self.min_lr + + if progress < 0: + return base_lr + else: + return annealing_cos(base_lr, target_lr, progress / max_progress) + + +@HOOKS.register_module() +class CosineRestartLrUpdaterHook(LrUpdaterHook): + """Cosine annealing with restarts learning rate scheme. + + Args: + periods (list[int]): Periods for each cosine anneling cycle. + restart_weights (list[float], optional): Restart weights at each + restart iteration. Default: [1]. + min_lr (float, optional): The minimum lr. Default: None. + min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. + Either `min_lr` or `min_lr_ratio` should be specified. + Default: None. + """ + + def __init__(self, + periods, + restart_weights=[1], + min_lr=None, + min_lr_ratio=None, + **kwargs): + assert (min_lr is None) ^ (min_lr_ratio is None) + self.periods = periods + self.min_lr = min_lr + self.min_lr_ratio = min_lr_ratio + self.restart_weights = restart_weights + assert (len(self.periods) == len(self.restart_weights) + ), 'periods and restart_weights should have the same length.' + super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) + + self.cumulative_periods = [ + sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) + ] + + def get_lr(self, runner, base_lr): + if self.by_epoch: + progress = runner.epoch + else: + progress = runner.iter + + if self.min_lr_ratio is not None: + target_lr = base_lr * self.min_lr_ratio + else: + target_lr = self.min_lr + + idx = get_position_from_periods(progress, self.cumulative_periods) + current_weight = self.restart_weights[idx] + nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] + current_periods = self.periods[idx] + + alpha = min((progress - nearest_restart) / current_periods, 1) + return annealing_cos(base_lr, target_lr, alpha, current_weight) + + +def get_position_from_periods(iteration, cumulative_periods): + """Get the position from a period list. + + It will return the index of the right-closest number in the period list. + For example, the cumulative_periods = [100, 200, 300, 400], + if iteration == 50, return 0; + if iteration == 210, return 2; + if iteration == 300, return 3. + + Args: + iteration (int): Current iteration. + cumulative_periods (list[int]): Cumulative period list. + + Returns: + int: The position of the right-closest number in the period list. + """ + for i, period in enumerate(cumulative_periods): + if iteration < period: + return i + raise ValueError(f'Current iteration {iteration} exceeds ' + f'cumulative_periods {cumulative_periods}') + + +@HOOKS.register_module() +class CyclicLrUpdaterHook(LrUpdaterHook): + """Cyclic LR Scheduler. + + Implement the cyclical learning rate policy (CLR) described in + https://arxiv.org/pdf/1506.01186.pdf + + Different from the original paper, we use cosine annealing rather than + triangular policy inside a cycle. This improves the performance in the + 3D detection area. + + Args: + by_epoch (bool): Whether to update LR by epoch. + target_ratio (tuple[float]): Relative ratio of the highest LR and the + lowest LR to the initial LR. + cyclic_times (int): Number of cycles during training + step_ratio_up (float): The ratio of the increasing process of LR in + the total cycle. + anneal_strategy (str): {'cos', 'linear'} + Specifies the annealing strategy: 'cos' for cosine annealing, + 'linear' for linear annealing. Default: 'cos'. + """ + + def __init__(self, + by_epoch=False, + target_ratio=(10, 1e-4), + cyclic_times=1, + step_ratio_up=0.4, + anneal_strategy='cos', + **kwargs): + if isinstance(target_ratio, float): + target_ratio = (target_ratio, target_ratio / 1e5) + elif isinstance(target_ratio, tuple): + target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ + if len(target_ratio) == 1 else target_ratio + else: + raise ValueError('target_ratio should be either float ' + f'or tuple, got {type(target_ratio)}') + + assert len(target_ratio) == 2, \ + '"target_ratio" must be list or tuple of two floats' + assert 0 <= step_ratio_up < 1.0, \ + '"step_ratio_up" must be in range [0,1)' + + self.target_ratio = target_ratio + self.cyclic_times = cyclic_times + self.step_ratio_up = step_ratio_up + self.lr_phases = [] # init lr_phases + # validate anneal_strategy + if anneal_strategy not in ['cos', 'linear']: + raise ValueError('anneal_strategy must be one of "cos" or ' + f'"linear", instead got {anneal_strategy}') + elif anneal_strategy == 'cos': + self.anneal_func = annealing_cos + elif anneal_strategy == 'linear': + self.anneal_func = annealing_linear + + assert not by_epoch, \ + 'currently only support "by_epoch" = False' + super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) + + def before_run(self, runner): + super(CyclicLrUpdaterHook, self).before_run(runner) + # initiate lr_phases + # total lr_phases are separated as up and down + max_iter_per_phase = runner.max_iters // self.cyclic_times + iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) + self.lr_phases.append( + [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) + self.lr_phases.append([ + iter_up_phase, max_iter_per_phase, max_iter_per_phase, + self.target_ratio[0], self.target_ratio[1] + ]) + + def get_lr(self, runner, base_lr): + curr_iter = runner.iter + for (start_iter, end_iter, max_iter_per_phase, start_ratio, + end_ratio) in self.lr_phases: + curr_iter %= max_iter_per_phase + if start_iter <= curr_iter < end_iter: + progress = curr_iter - start_iter + return self.anneal_func(base_lr * start_ratio, + base_lr * end_ratio, + progress / (end_iter - start_iter)) + + +@HOOKS.register_module() +class OneCycleLrUpdaterHook(LrUpdaterHook): + """One Cycle LR Scheduler. + + The 1cycle learning rate policy changes the learning rate after every + batch. The one cycle learning rate policy is described in + https://arxiv.org/pdf/1708.07120.pdf + + Args: + max_lr (float or list): Upper learning rate boundaries in the cycle + for each parameter group. + total_steps (int, optional): The total number of steps in the cycle. + Note that if a value is not provided here, it will be the max_iter + of runner. Default: None. + pct_start (float): The percentage of the cycle (in number of steps) + spent increasing the learning rate. + Default: 0.3 + anneal_strategy (str): {'cos', 'linear'} + Specifies the annealing strategy: 'cos' for cosine annealing, + 'linear' for linear annealing. + Default: 'cos' + div_factor (float): Determines the initial learning rate via + initial_lr = max_lr/div_factor + Default: 25 + final_div_factor (float): Determines the minimum learning rate via + min_lr = initial_lr/final_div_factor + Default: 1e4 + three_phase (bool): If three_phase is True, use a third phase of the + schedule to annihilate the learning rate according to + final_div_factor instead of modifying the second phase (the first + two phases will be symmetrical about the step indicated by + pct_start). + Default: False + """ + + def __init__(self, + max_lr, + total_steps=None, + pct_start=0.3, + anneal_strategy='cos', + div_factor=25, + final_div_factor=1e4, + three_phase=False, + **kwargs): + # validate by_epoch, currently only support by_epoch = False + if 'by_epoch' not in kwargs: + kwargs['by_epoch'] = False + else: + assert not kwargs['by_epoch'], \ + 'currently only support "by_epoch" = False' + if not isinstance(max_lr, (numbers.Number, list, dict)): + raise ValueError('the type of max_lr must be the one of list or ' + f'dict, but got {type(max_lr)}') + self._max_lr = max_lr + if total_steps is not None: + if not isinstance(total_steps, int): + raise ValueError('the type of total_steps must be int, but' + f'got {type(total_steps)}') + self.total_steps = total_steps + # validate pct_start + if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): + raise ValueError('expected float between 0 and 1 pct_start, but ' + f'got {pct_start}') + self.pct_start = pct_start + # validate anneal_strategy + if anneal_strategy not in ['cos', 'linear']: + raise ValueError('anneal_strategy must be one of "cos" or ' + f'"linear", instead got {anneal_strategy}') + elif anneal_strategy == 'cos': + self.anneal_func = annealing_cos + elif anneal_strategy == 'linear': + self.anneal_func = annealing_linear + self.div_factor = div_factor + self.final_div_factor = final_div_factor + self.three_phase = three_phase + self.lr_phases = [] # init lr_phases + super(OneCycleLrUpdaterHook, self).__init__(**kwargs) + + def before_run(self, runner): + if hasattr(self, 'total_steps'): + total_steps = self.total_steps + else: + total_steps = runner.max_iters + if total_steps < runner.max_iters: + raise ValueError( + 'The total steps must be greater than or equal to max ' + f'iterations {runner.max_iters} of runner, but total steps ' + f'is {total_steps}.') + + if isinstance(runner.optimizer, dict): + self.base_lr = {} + for k, optim in runner.optimizer.items(): + _max_lr = format_param(k, optim, self._max_lr) + self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] + for group, lr in zip(optim.param_groups, self.base_lr[k]): + group.setdefault('initial_lr', lr) + else: + k = type(runner.optimizer).__name__ + _max_lr = format_param(k, runner.optimizer, self._max_lr) + self.base_lr = [lr / self.div_factor for lr in _max_lr] + for group, lr in zip(runner.optimizer.param_groups, self.base_lr): + group.setdefault('initial_lr', lr) + + if self.three_phase: + self.lr_phases.append( + [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) + self.lr_phases.append([ + float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 + ]) + self.lr_phases.append( + [total_steps - 1, 1, 1 / self.final_div_factor]) + else: + self.lr_phases.append( + [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) + self.lr_phases.append( + [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) + + def get_lr(self, runner, base_lr): + curr_iter = runner.iter + start_iter = 0 + for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): + if curr_iter <= end_iter: + pct = (curr_iter - start_iter) / (end_iter - start_iter) + lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, + pct) + break + start_iter = end_iter + return lr + + +def annealing_cos(start, end, factor, weight=1): + """Calculate annealing cos learning rate. + + Cosine anneal from `weight * start + (1 - weight) * end` to `end` as + percentage goes from 0.0 to 1.0. + + Args: + start (float): The starting learning rate of the cosine annealing. + end (float): The ending learing rate of the cosine annealing. + factor (float): The coefficient of `pi` when calculating the current + percentage. Range from 0.0 to 1.0. + weight (float, optional): The combination factor of `start` and `end` + when calculating the actual starting learning rate. Default to 1. + """ + cos_out = cos(pi * factor) + 1 + return end + 0.5 * weight * (start - end) * cos_out + + +def annealing_linear(start, end, factor): + """Calculate annealing linear learning rate. + + Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. + + Args: + start (float): The starting learning rate of the linear annealing. + end (float): The ending learing rate of the linear annealing. + factor (float): The coefficient of `pi` when calculating the current + percentage. Range from 0.0 to 1.0. + """ + return start + (end - start) * factor + + +def format_param(name, optim, param): + if isinstance(param, numbers.Number): + return [param] * len(optim.param_groups) + elif isinstance(param, (list, tuple)): # multi param groups + if len(param) != len(optim.param_groups): + raise ValueError(f'expected {len(optim.param_groups)} ' + f'values for {name}, got {len(param)}') + return param + else: # multi optimizers + if name not in param: + raise KeyError(f'{name} is not found in {param.keys()}') + return param[name] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/memory.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/memory.py new file mode 100644 index 0000000000000000000000000000000000000000..70cf9a838fb314e3bd3c07aadbc00921a81e83ed --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/memory.py @@ -0,0 +1,25 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch + +from .hook import HOOKS, Hook + + +@HOOKS.register_module() +class EmptyCacheHook(Hook): + + def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): + self._before_epoch = before_epoch + self._after_epoch = after_epoch + self._after_iter = after_iter + + def after_iter(self, runner): + if self._after_iter: + torch.cuda.empty_cache() + + def before_epoch(self, runner): + if self._before_epoch: + torch.cuda.empty_cache() + + def after_epoch(self, runner): + if self._after_epoch: + torch.cuda.empty_cache() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/momentum_updater.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/momentum_updater.py new file mode 100644 index 0000000000000000000000000000000000000000..cdc70246280c2318f51034bb6b66eade7b478b79 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/momentum_updater.py @@ -0,0 +1,493 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import annotator.mmpkg.mmcv as mmcv +from .hook import HOOKS, Hook +from .lr_updater import annealing_cos, annealing_linear, format_param + + +class MomentumUpdaterHook(Hook): + + def __init__(self, + by_epoch=True, + warmup=None, + warmup_iters=0, + warmup_ratio=0.9): + # validate the "warmup" argument + if warmup is not None: + if warmup not in ['constant', 'linear', 'exp']: + raise ValueError( + f'"{warmup}" is not a supported type for warming up, valid' + ' types are "constant" and "linear"') + if warmup is not None: + assert warmup_iters > 0, \ + '"warmup_iters" must be a positive integer' + assert 0 < warmup_ratio <= 1.0, \ + '"warmup_momentum" must be in range (0,1]' + + self.by_epoch = by_epoch + self.warmup = warmup + self.warmup_iters = warmup_iters + self.warmup_ratio = warmup_ratio + + self.base_momentum = [] # initial momentum for all param groups + self.regular_momentum = [ + ] # expected momentum if no warming up is performed + + def _set_momentum(self, runner, momentum_groups): + if isinstance(runner.optimizer, dict): + for k, optim in runner.optimizer.items(): + for param_group, mom in zip(optim.param_groups, + momentum_groups[k]): + if 'momentum' in param_group.keys(): + param_group['momentum'] = mom + elif 'betas' in param_group.keys(): + param_group['betas'] = (mom, param_group['betas'][1]) + else: + for param_group, mom in zip(runner.optimizer.param_groups, + momentum_groups): + if 'momentum' in param_group.keys(): + param_group['momentum'] = mom + elif 'betas' in param_group.keys(): + param_group['betas'] = (mom, param_group['betas'][1]) + + def get_momentum(self, runner, base_momentum): + raise NotImplementedError + + def get_regular_momentum(self, runner): + if isinstance(runner.optimizer, dict): + momentum_groups = {} + for k in runner.optimizer.keys(): + _momentum_group = [ + self.get_momentum(runner, _base_momentum) + for _base_momentum in self.base_momentum[k] + ] + momentum_groups.update({k: _momentum_group}) + return momentum_groups + else: + return [ + self.get_momentum(runner, _base_momentum) + for _base_momentum in self.base_momentum + ] + + def get_warmup_momentum(self, cur_iters): + + def _get_warmup_momentum(cur_iters, regular_momentum): + if self.warmup == 'constant': + warmup_momentum = [ + _momentum / self.warmup_ratio + for _momentum in self.regular_momentum + ] + elif self.warmup == 'linear': + k = (1 - cur_iters / self.warmup_iters) * (1 - + self.warmup_ratio) + warmup_momentum = [ + _momentum / (1 - k) for _momentum in self.regular_mom + ] + elif self.warmup == 'exp': + k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) + warmup_momentum = [ + _momentum / k for _momentum in self.regular_mom + ] + return warmup_momentum + + if isinstance(self.regular_momentum, dict): + momentum_groups = {} + for key, regular_momentum in self.regular_momentum.items(): + momentum_groups[key] = _get_warmup_momentum( + cur_iters, regular_momentum) + return momentum_groups + else: + return _get_warmup_momentum(cur_iters, self.regular_momentum) + + def before_run(self, runner): + # NOTE: when resuming from a checkpoint, + # if 'initial_momentum' is not saved, + # it will be set according to the optimizer params + if isinstance(runner.optimizer, dict): + self.base_momentum = {} + for k, optim in runner.optimizer.items(): + for group in optim.param_groups: + if 'momentum' in group.keys(): + group.setdefault('initial_momentum', group['momentum']) + else: + group.setdefault('initial_momentum', group['betas'][0]) + _base_momentum = [ + group['initial_momentum'] for group in optim.param_groups + ] + self.base_momentum.update({k: _base_momentum}) + else: + for group in runner.optimizer.param_groups: + if 'momentum' in group.keys(): + group.setdefault('initial_momentum', group['momentum']) + else: + group.setdefault('initial_momentum', group['betas'][0]) + self.base_momentum = [ + group['initial_momentum'] + for group in runner.optimizer.param_groups + ] + + def before_train_epoch(self, runner): + if not self.by_epoch: + return + self.regular_mom = self.get_regular_momentum(runner) + self._set_momentum(runner, self.regular_mom) + + def before_train_iter(self, runner): + cur_iter = runner.iter + if not self.by_epoch: + self.regular_mom = self.get_regular_momentum(runner) + if self.warmup is None or cur_iter >= self.warmup_iters: + self._set_momentum(runner, self.regular_mom) + else: + warmup_momentum = self.get_warmup_momentum(cur_iter) + self._set_momentum(runner, warmup_momentum) + elif self.by_epoch: + if self.warmup is None or cur_iter > self.warmup_iters: + return + elif cur_iter == self.warmup_iters: + self._set_momentum(runner, self.regular_mom) + else: + warmup_momentum = self.get_warmup_momentum(cur_iter) + self._set_momentum(runner, warmup_momentum) + + +@HOOKS.register_module() +class StepMomentumUpdaterHook(MomentumUpdaterHook): + """Step momentum scheduler with min value clipping. + + Args: + step (int | list[int]): Step to decay the momentum. If an int value is + given, regard it as the decay interval. If a list is given, decay + momentum at these steps. + gamma (float, optional): Decay momentum ratio. Default: 0.5. + min_momentum (float, optional): Minimum momentum value to keep. If + momentum after decay is lower than this value, it will be clipped + accordingly. If None is given, we don't perform lr clipping. + Default: None. + """ + + def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): + if isinstance(step, list): + assert mmcv.is_list_of(step, int) + assert all([s > 0 for s in step]) + elif isinstance(step, int): + assert step > 0 + else: + raise TypeError('"step" must be a list or integer') + self.step = step + self.gamma = gamma + self.min_momentum = min_momentum + super(StepMomentumUpdaterHook, self).__init__(**kwargs) + + def get_momentum(self, runner, base_momentum): + progress = runner.epoch if self.by_epoch else runner.iter + + # calculate exponential term + if isinstance(self.step, int): + exp = progress // self.step + else: + exp = len(self.step) + for i, s in enumerate(self.step): + if progress < s: + exp = i + break + + momentum = base_momentum * (self.gamma**exp) + if self.min_momentum is not None: + # clip to a minimum value + momentum = max(momentum, self.min_momentum) + return momentum + + +@HOOKS.register_module() +class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): + + def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): + assert (min_momentum is None) ^ (min_momentum_ratio is None) + self.min_momentum = min_momentum + self.min_momentum_ratio = min_momentum_ratio + super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) + + def get_momentum(self, runner, base_momentum): + if self.by_epoch: + progress = runner.epoch + max_progress = runner.max_epochs + else: + progress = runner.iter + max_progress = runner.max_iters + if self.min_momentum_ratio is not None: + target_momentum = base_momentum * self.min_momentum_ratio + else: + target_momentum = self.min_momentum + return annealing_cos(base_momentum, target_momentum, + progress / max_progress) + + +@HOOKS.register_module() +class CyclicMomentumUpdaterHook(MomentumUpdaterHook): + """Cyclic momentum Scheduler. + + Implement the cyclical momentum scheduler policy described in + https://arxiv.org/pdf/1708.07120.pdf + + This momentum scheduler usually used together with the CyclicLRUpdater + to improve the performance in the 3D detection area. + + Attributes: + target_ratio (tuple[float]): Relative ratio of the lowest momentum and + the highest momentum to the initial momentum. + cyclic_times (int): Number of cycles during training + step_ratio_up (float): The ratio of the increasing process of momentum + in the total cycle. + by_epoch (bool): Whether to update momentum by epoch. + """ + + def __init__(self, + by_epoch=False, + target_ratio=(0.85 / 0.95, 1), + cyclic_times=1, + step_ratio_up=0.4, + **kwargs): + if isinstance(target_ratio, float): + target_ratio = (target_ratio, target_ratio / 1e5) + elif isinstance(target_ratio, tuple): + target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ + if len(target_ratio) == 1 else target_ratio + else: + raise ValueError('target_ratio should be either float ' + f'or tuple, got {type(target_ratio)}') + + assert len(target_ratio) == 2, \ + '"target_ratio" must be list or tuple of two floats' + assert 0 <= step_ratio_up < 1.0, \ + '"step_ratio_up" must be in range [0,1)' + + self.target_ratio = target_ratio + self.cyclic_times = cyclic_times + self.step_ratio_up = step_ratio_up + self.momentum_phases = [] # init momentum_phases + # currently only support by_epoch=False + assert not by_epoch, \ + 'currently only support "by_epoch" = False' + super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) + + def before_run(self, runner): + super(CyclicMomentumUpdaterHook, self).before_run(runner) + # initiate momentum_phases + # total momentum_phases are separated as up and down + max_iter_per_phase = runner.max_iters // self.cyclic_times + iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) + self.momentum_phases.append( + [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) + self.momentum_phases.append([ + iter_up_phase, max_iter_per_phase, max_iter_per_phase, + self.target_ratio[0], self.target_ratio[1] + ]) + + def get_momentum(self, runner, base_momentum): + curr_iter = runner.iter + for (start_iter, end_iter, max_iter_per_phase, start_ratio, + end_ratio) in self.momentum_phases: + curr_iter %= max_iter_per_phase + if start_iter <= curr_iter < end_iter: + progress = curr_iter - start_iter + return annealing_cos(base_momentum * start_ratio, + base_momentum * end_ratio, + progress / (end_iter - start_iter)) + + +@HOOKS.register_module() +class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): + """OneCycle momentum Scheduler. + + This momentum scheduler usually used together with the OneCycleLrUpdater + to improve the performance. + + Args: + base_momentum (float or list): Lower momentum boundaries in the cycle + for each parameter group. Note that momentum is cycled inversely + to learning rate; at the peak of a cycle, momentum is + 'base_momentum' and learning rate is 'max_lr'. + Default: 0.85 + max_momentum (float or list): Upper momentum boundaries in the cycle + for each parameter group. Functionally, + it defines the cycle amplitude (max_momentum - base_momentum). + Note that momentum is cycled inversely + to learning rate; at the start of a cycle, momentum is + 'max_momentum' and learning rate is 'base_lr' + Default: 0.95 + pct_start (float): The percentage of the cycle (in number of steps) + spent increasing the learning rate. + Default: 0.3 + anneal_strategy (str): {'cos', 'linear'} + Specifies the annealing strategy: 'cos' for cosine annealing, + 'linear' for linear annealing. + Default: 'cos' + three_phase (bool): If three_phase is True, use a third phase of the + schedule to annihilate the learning rate according to + final_div_factor instead of modifying the second phase (the first + two phases will be symmetrical about the step indicated by + pct_start). + Default: False + """ + + def __init__(self, + base_momentum=0.85, + max_momentum=0.95, + pct_start=0.3, + anneal_strategy='cos', + three_phase=False, + **kwargs): + # validate by_epoch, currently only support by_epoch=False + if 'by_epoch' not in kwargs: + kwargs['by_epoch'] = False + else: + assert not kwargs['by_epoch'], \ + 'currently only support "by_epoch" = False' + if not isinstance(base_momentum, (float, list, dict)): + raise ValueError('base_momentum must be the type among of float,' + 'list or dict.') + self._base_momentum = base_momentum + if not isinstance(max_momentum, (float, list, dict)): + raise ValueError('max_momentum must be the type among of float,' + 'list or dict.') + self._max_momentum = max_momentum + # validate pct_start + if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): + raise ValueError('Expected float between 0 and 1 pct_start, but ' + f'got {pct_start}') + self.pct_start = pct_start + # validate anneal_strategy + if anneal_strategy not in ['cos', 'linear']: + raise ValueError('anneal_strategy must by one of "cos" or ' + f'"linear", instead got {anneal_strategy}') + elif anneal_strategy == 'cos': + self.anneal_func = annealing_cos + elif anneal_strategy == 'linear': + self.anneal_func = annealing_linear + self.three_phase = three_phase + self.momentum_phases = [] # init momentum_phases + super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) + + def before_run(self, runner): + if isinstance(runner.optimizer, dict): + for k, optim in runner.optimizer.items(): + if ('momentum' not in optim.defaults + and 'betas' not in optim.defaults): + raise ValueError('optimizer must support momentum with' + 'option enabled') + self.use_beta1 = 'betas' in optim.defaults + _base_momentum = format_param(k, optim, self._base_momentum) + _max_momentum = format_param(k, optim, self._max_momentum) + for group, b_momentum, m_momentum in zip( + optim.param_groups, _base_momentum, _max_momentum): + if self.use_beta1: + _, beta2 = group['betas'] + group['betas'] = (m_momentum, beta2) + else: + group['momentum'] = m_momentum + group['base_momentum'] = b_momentum + group['max_momentum'] = m_momentum + else: + optim = runner.optimizer + if ('momentum' not in optim.defaults + and 'betas' not in optim.defaults): + raise ValueError('optimizer must support momentum with' + 'option enabled') + self.use_beta1 = 'betas' in optim.defaults + k = type(optim).__name__ + _base_momentum = format_param(k, optim, self._base_momentum) + _max_momentum = format_param(k, optim, self._max_momentum) + for group, b_momentum, m_momentum in zip(optim.param_groups, + _base_momentum, + _max_momentum): + if self.use_beta1: + _, beta2 = group['betas'] + group['betas'] = (m_momentum, beta2) + else: + group['momentum'] = m_momentum + group['base_momentum'] = b_momentum + group['max_momentum'] = m_momentum + + if self.three_phase: + self.momentum_phases.append({ + 'end_iter': + float(self.pct_start * runner.max_iters) - 1, + 'start_momentum': + 'max_momentum', + 'end_momentum': + 'base_momentum' + }) + self.momentum_phases.append({ + 'end_iter': + float(2 * self.pct_start * runner.max_iters) - 2, + 'start_momentum': + 'base_momentum', + 'end_momentum': + 'max_momentum' + }) + self.momentum_phases.append({ + 'end_iter': runner.max_iters - 1, + 'start_momentum': 'max_momentum', + 'end_momentum': 'max_momentum' + }) + else: + self.momentum_phases.append({ + 'end_iter': + float(self.pct_start * runner.max_iters) - 1, + 'start_momentum': + 'max_momentum', + 'end_momentum': + 'base_momentum' + }) + self.momentum_phases.append({ + 'end_iter': runner.max_iters - 1, + 'start_momentum': 'base_momentum', + 'end_momentum': 'max_momentum' + }) + + def _set_momentum(self, runner, momentum_groups): + if isinstance(runner.optimizer, dict): + for k, optim in runner.optimizer.items(): + for param_group, mom in zip(optim.param_groups, + momentum_groups[k]): + if 'momentum' in param_group.keys(): + param_group['momentum'] = mom + elif 'betas' in param_group.keys(): + param_group['betas'] = (mom, param_group['betas'][1]) + else: + for param_group, mom in zip(runner.optimizer.param_groups, + momentum_groups): + if 'momentum' in param_group.keys(): + param_group['momentum'] = mom + elif 'betas' in param_group.keys(): + param_group['betas'] = (mom, param_group['betas'][1]) + + def get_momentum(self, runner, param_group): + curr_iter = runner.iter + start_iter = 0 + for i, phase in enumerate(self.momentum_phases): + end_iter = phase['end_iter'] + if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: + pct = (curr_iter - start_iter) / (end_iter - start_iter) + momentum = self.anneal_func( + param_group[phase['start_momentum']], + param_group[phase['end_momentum']], pct) + break + start_iter = end_iter + return momentum + + def get_regular_momentum(self, runner): + if isinstance(runner.optimizer, dict): + momentum_groups = {} + for k, optim in runner.optimizer.items(): + _momentum_group = [ + self.get_momentum(runner, param_group) + for param_group in optim.param_groups + ] + momentum_groups.update({k: _momentum_group}) + return momentum_groups + else: + momentum_groups = [] + for param_group in runner.optimizer.param_groups: + momentum_groups.append(self.get_momentum(runner, param_group)) + return momentum_groups diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/optimizer.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/optimizer.py new file mode 100644 index 0000000000000000000000000000000000000000..580a183639a5d95c04ecae9c619afb795a169e9e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/optimizer.py @@ -0,0 +1,508 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import copy +from collections import defaultdict +from itertools import chain + +from torch.nn.utils import clip_grad + +from annotator.mmpkg.mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version +from ..dist_utils import allreduce_grads +from ..fp16_utils import LossScaler, wrap_fp16_model +from .hook import HOOKS, Hook + +try: + # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported + # and used; otherwise, auto fp16 will adopt mmcv's implementation. + from torch.cuda.amp import GradScaler +except ImportError: + pass + + +@HOOKS.register_module() +class OptimizerHook(Hook): + + def __init__(self, grad_clip=None): + self.grad_clip = grad_clip + + def clip_grads(self, params): + params = list( + filter(lambda p: p.requires_grad and p.grad is not None, params)) + if len(params) > 0: + return clip_grad.clip_grad_norm_(params, **self.grad_clip) + + def after_train_iter(self, runner): + runner.optimizer.zero_grad() + runner.outputs['loss'].backward() + if self.grad_clip is not None: + grad_norm = self.clip_grads(runner.model.parameters()) + if grad_norm is not None: + # Add grad norm to the logger + runner.log_buffer.update({'grad_norm': float(grad_norm)}, + runner.outputs['num_samples']) + runner.optimizer.step() + + +@HOOKS.register_module() +class GradientCumulativeOptimizerHook(OptimizerHook): + """Optimizer Hook implements multi-iters gradient cumulating. + + Args: + cumulative_iters (int, optional): Num of gradient cumulative iters. + The optimizer will step every `cumulative_iters` iters. + Defaults to 1. + + Examples: + >>> # Use cumulative_iters to simulate a large batch size + >>> # It is helpful when the hardware cannot handle a large batch size. + >>> loader = DataLoader(data, batch_size=64) + >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) + >>> # almost equals to + >>> loader = DataLoader(data, batch_size=256) + >>> optim_hook = OptimizerHook() + """ + + def __init__(self, cumulative_iters=1, **kwargs): + super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) + + assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ + f'cumulative_iters only accepts positive int, but got ' \ + f'{type(cumulative_iters)} instead.' + + self.cumulative_iters = cumulative_iters + self.divisible_iters = 0 + self.remainder_iters = 0 + self.initialized = False + + def has_batch_norm(self, module): + if isinstance(module, _BatchNorm): + return True + for m in module.children(): + if self.has_batch_norm(m): + return True + return False + + def _init(self, runner): + if runner.iter % self.cumulative_iters != 0: + runner.logger.warning( + 'Resume iter number is not divisible by cumulative_iters in ' + 'GradientCumulativeOptimizerHook, which means the gradient of ' + 'some iters is lost and the result may be influenced slightly.' + ) + + if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: + runner.logger.warning( + 'GradientCumulativeOptimizerHook may slightly decrease ' + 'performance if the model has BatchNorm layers.') + + residual_iters = runner.max_iters - runner.iter + + self.divisible_iters = ( + residual_iters // self.cumulative_iters * self.cumulative_iters) + self.remainder_iters = residual_iters - self.divisible_iters + + self.initialized = True + + def after_train_iter(self, runner): + if not self.initialized: + self._init(runner) + + if runner.iter < self.divisible_iters: + loss_factor = self.cumulative_iters + else: + loss_factor = self.remainder_iters + loss = runner.outputs['loss'] + loss = loss / loss_factor + loss.backward() + + if (self.every_n_iters(runner, self.cumulative_iters) + or self.is_last_iter(runner)): + + if self.grad_clip is not None: + grad_norm = self.clip_grads(runner.model.parameters()) + if grad_norm is not None: + # Add grad norm to the logger + runner.log_buffer.update({'grad_norm': float(grad_norm)}, + runner.outputs['num_samples']) + runner.optimizer.step() + runner.optimizer.zero_grad() + + +if (TORCH_VERSION != 'parrots' + and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): + + @HOOKS.register_module() + class Fp16OptimizerHook(OptimizerHook): + """FP16 optimizer hook (using PyTorch's implementation). + + If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, + to take care of the optimization procedure. + + Args: + loss_scale (float | str | dict): Scale factor configuration. + If loss_scale is a float, static loss scaling will be used with + the specified scale. If loss_scale is a string, it must be + 'dynamic', then dynamic loss scaling will be used. + It can also be a dict containing arguments of GradScalar. + Defaults to 512. For Pytorch >= 1.6, mmcv uses official + implementation of GradScaler. If you use a dict version of + loss_scale to create GradScaler, please refer to: + https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler + for the parameters. + + Examples: + >>> loss_scale = dict( + ... init_scale=65536.0, + ... growth_factor=2.0, + ... backoff_factor=0.5, + ... growth_interval=2000 + ... ) + >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) + """ + + def __init__(self, + grad_clip=None, + coalesce=True, + bucket_size_mb=-1, + loss_scale=512., + distributed=True): + self.grad_clip = grad_clip + self.coalesce = coalesce + self.bucket_size_mb = bucket_size_mb + self.distributed = distributed + self._scale_update_param = None + if loss_scale == 'dynamic': + self.loss_scaler = GradScaler() + elif isinstance(loss_scale, float): + self._scale_update_param = loss_scale + self.loss_scaler = GradScaler(init_scale=loss_scale) + elif isinstance(loss_scale, dict): + self.loss_scaler = GradScaler(**loss_scale) + else: + raise ValueError('loss_scale must be of type float, dict, or ' + f'"dynamic", got {loss_scale}') + + def before_run(self, runner): + """Preparing steps before Mixed Precision Training.""" + # wrap model mode to fp16 + wrap_fp16_model(runner.model) + # resume from state dict + if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: + scaler_state_dict = runner.meta['fp16']['loss_scaler'] + self.loss_scaler.load_state_dict(scaler_state_dict) + + def copy_grads_to_fp32(self, fp16_net, fp32_weights): + """Copy gradients from fp16 model to fp32 weight copy.""" + for fp32_param, fp16_param in zip(fp32_weights, + fp16_net.parameters()): + if fp16_param.grad is not None: + if fp32_param.grad is None: + fp32_param.grad = fp32_param.data.new( + fp32_param.size()) + fp32_param.grad.copy_(fp16_param.grad) + + def copy_params_to_fp16(self, fp16_net, fp32_weights): + """Copy updated params from fp32 weight copy to fp16 model.""" + for fp16_param, fp32_param in zip(fp16_net.parameters(), + fp32_weights): + fp16_param.data.copy_(fp32_param.data) + + def after_train_iter(self, runner): + """Backward optimization steps for Mixed Precision Training. For + dynamic loss scaling, please refer to + https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. + + 1. Scale the loss by a scale factor. + 2. Backward the loss to obtain the gradients. + 3. Unscale the optimizer’s gradient tensors. + 4. Call optimizer.step() and update scale factor. + 5. Save loss_scaler state_dict for resume purpose. + """ + # clear grads of last iteration + runner.model.zero_grad() + runner.optimizer.zero_grad() + + self.loss_scaler.scale(runner.outputs['loss']).backward() + self.loss_scaler.unscale_(runner.optimizer) + # grad clip + if self.grad_clip is not None: + grad_norm = self.clip_grads(runner.model.parameters()) + if grad_norm is not None: + # Add grad norm to the logger + runner.log_buffer.update({'grad_norm': float(grad_norm)}, + runner.outputs['num_samples']) + # backward and update scaler + self.loss_scaler.step(runner.optimizer) + self.loss_scaler.update(self._scale_update_param) + + # save state_dict of loss_scaler + runner.meta.setdefault( + 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() + + @HOOKS.register_module() + class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, + Fp16OptimizerHook): + """Fp16 optimizer Hook (using PyTorch's implementation) implements + multi-iters gradient cumulating. + + If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, + to take care of the optimization procedure. + """ + + def __init__(self, *args, **kwargs): + super(GradientCumulativeFp16OptimizerHook, + self).__init__(*args, **kwargs) + + def after_train_iter(self, runner): + if not self.initialized: + self._init(runner) + + if runner.iter < self.divisible_iters: + loss_factor = self.cumulative_iters + else: + loss_factor = self.remainder_iters + loss = runner.outputs['loss'] + loss = loss / loss_factor + + self.loss_scaler.scale(loss).backward() + + if (self.every_n_iters(runner, self.cumulative_iters) + or self.is_last_iter(runner)): + + # copy fp16 grads in the model to fp32 params in the optimizer + self.loss_scaler.unscale_(runner.optimizer) + + if self.grad_clip is not None: + grad_norm = self.clip_grads(runner.model.parameters()) + if grad_norm is not None: + # Add grad norm to the logger + runner.log_buffer.update( + {'grad_norm': float(grad_norm)}, + runner.outputs['num_samples']) + + # backward and update scaler + self.loss_scaler.step(runner.optimizer) + self.loss_scaler.update(self._scale_update_param) + + # save state_dict of loss_scaler + runner.meta.setdefault( + 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() + + # clear grads + runner.model.zero_grad() + runner.optimizer.zero_grad() + +else: + + @HOOKS.register_module() + class Fp16OptimizerHook(OptimizerHook): + """FP16 optimizer hook (mmcv's implementation). + + The steps of fp16 optimizer is as follows. + 1. Scale the loss value. + 2. BP in the fp16 model. + 2. Copy gradients from fp16 model to fp32 weights. + 3. Update fp32 weights. + 4. Copy updated parameters from fp32 weights to fp16 model. + + Refer to https://arxiv.org/abs/1710.03740 for more details. + + Args: + loss_scale (float | str | dict): Scale factor configuration. + If loss_scale is a float, static loss scaling will be used with + the specified scale. If loss_scale is a string, it must be + 'dynamic', then dynamic loss scaling will be used. + It can also be a dict containing arguments of LossScaler. + Defaults to 512. + """ + + def __init__(self, + grad_clip=None, + coalesce=True, + bucket_size_mb=-1, + loss_scale=512., + distributed=True): + self.grad_clip = grad_clip + self.coalesce = coalesce + self.bucket_size_mb = bucket_size_mb + self.distributed = distributed + if loss_scale == 'dynamic': + self.loss_scaler = LossScaler(mode='dynamic') + elif isinstance(loss_scale, float): + self.loss_scaler = LossScaler( + init_scale=loss_scale, mode='static') + elif isinstance(loss_scale, dict): + self.loss_scaler = LossScaler(**loss_scale) + else: + raise ValueError('loss_scale must be of type float, dict, or ' + f'"dynamic", got {loss_scale}') + + def before_run(self, runner): + """Preparing steps before Mixed Precision Training. + + 1. Make a master copy of fp32 weights for optimization. + 2. Convert the main model from fp32 to fp16. + """ + # keep a copy of fp32 weights + old_groups = runner.optimizer.param_groups + runner.optimizer.param_groups = copy.deepcopy( + runner.optimizer.param_groups) + state = defaultdict(dict) + p_map = { + old_p: p + for old_p, p in zip( + chain(*(g['params'] for g in old_groups)), + chain(*(g['params'] + for g in runner.optimizer.param_groups))) + } + for k, v in runner.optimizer.state.items(): + state[p_map[k]] = v + runner.optimizer.state = state + # convert model to fp16 + wrap_fp16_model(runner.model) + # resume from state dict + if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: + scaler_state_dict = runner.meta['fp16']['loss_scaler'] + self.loss_scaler.load_state_dict(scaler_state_dict) + + def copy_grads_to_fp32(self, fp16_net, fp32_weights): + """Copy gradients from fp16 model to fp32 weight copy.""" + for fp32_param, fp16_param in zip(fp32_weights, + fp16_net.parameters()): + if fp16_param.grad is not None: + if fp32_param.grad is None: + fp32_param.grad = fp32_param.data.new( + fp32_param.size()) + fp32_param.grad.copy_(fp16_param.grad) + + def copy_params_to_fp16(self, fp16_net, fp32_weights): + """Copy updated params from fp32 weight copy to fp16 model.""" + for fp16_param, fp32_param in zip(fp16_net.parameters(), + fp32_weights): + fp16_param.data.copy_(fp32_param.data) + + def after_train_iter(self, runner): + """Backward optimization steps for Mixed Precision Training. For + dynamic loss scaling, please refer `loss_scalar.py` + + 1. Scale the loss by a scale factor. + 2. Backward the loss to obtain the gradients (fp16). + 3. Copy gradients from the model to the fp32 weight copy. + 4. Scale the gradients back and update the fp32 weight copy. + 5. Copy back the params from fp32 weight copy to the fp16 model. + 6. Save loss_scaler state_dict for resume purpose. + """ + # clear grads of last iteration + runner.model.zero_grad() + runner.optimizer.zero_grad() + # scale the loss value + scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale + scaled_loss.backward() + # copy fp16 grads in the model to fp32 params in the optimizer + + fp32_weights = [] + for param_group in runner.optimizer.param_groups: + fp32_weights += param_group['params'] + self.copy_grads_to_fp32(runner.model, fp32_weights) + # allreduce grads + if self.distributed: + allreduce_grads(fp32_weights, self.coalesce, + self.bucket_size_mb) + + has_overflow = self.loss_scaler.has_overflow(fp32_weights) + # if has overflow, skip this iteration + if not has_overflow: + # scale the gradients back + for param in fp32_weights: + if param.grad is not None: + param.grad.div_(self.loss_scaler.loss_scale) + if self.grad_clip is not None: + grad_norm = self.clip_grads(fp32_weights) + if grad_norm is not None: + # Add grad norm to the logger + runner.log_buffer.update( + {'grad_norm': float(grad_norm)}, + runner.outputs['num_samples']) + # update fp32 params + runner.optimizer.step() + # copy fp32 params to the fp16 model + self.copy_params_to_fp16(runner.model, fp32_weights) + self.loss_scaler.update_scale(has_overflow) + if has_overflow: + runner.logger.warning('Check overflow, downscale loss scale ' + f'to {self.loss_scaler.cur_scale}') + + # save state_dict of loss_scaler + runner.meta.setdefault( + 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() + + @HOOKS.register_module() + class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, + Fp16OptimizerHook): + """Fp16 optimizer Hook (using mmcv implementation) implements multi- + iters gradient cumulating.""" + + def __init__(self, *args, **kwargs): + super(GradientCumulativeFp16OptimizerHook, + self).__init__(*args, **kwargs) + + def after_train_iter(self, runner): + if not self.initialized: + self._init(runner) + + if runner.iter < self.divisible_iters: + loss_factor = self.cumulative_iters + else: + loss_factor = self.remainder_iters + + loss = runner.outputs['loss'] + loss = loss / loss_factor + + # scale the loss value + scaled_loss = loss * self.loss_scaler.loss_scale + scaled_loss.backward() + + if (self.every_n_iters(runner, self.cumulative_iters) + or self.is_last_iter(runner)): + + # copy fp16 grads in the model to fp32 params in the optimizer + fp32_weights = [] + for param_group in runner.optimizer.param_groups: + fp32_weights += param_group['params'] + self.copy_grads_to_fp32(runner.model, fp32_weights) + # allreduce grads + if self.distributed: + allreduce_grads(fp32_weights, self.coalesce, + self.bucket_size_mb) + + has_overflow = self.loss_scaler.has_overflow(fp32_weights) + # if has overflow, skip this iteration + if not has_overflow: + # scale the gradients back + for param in fp32_weights: + if param.grad is not None: + param.grad.div_(self.loss_scaler.loss_scale) + if self.grad_clip is not None: + grad_norm = self.clip_grads(fp32_weights) + if grad_norm is not None: + # Add grad norm to the logger + runner.log_buffer.update( + {'grad_norm': float(grad_norm)}, + runner.outputs['num_samples']) + # update fp32 params + runner.optimizer.step() + # copy fp32 params to the fp16 model + self.copy_params_to_fp16(runner.model, fp32_weights) + else: + runner.logger.warning( + 'Check overflow, downscale loss scale ' + f'to {self.loss_scaler.cur_scale}') + + self.loss_scaler.update_scale(has_overflow) + + # save state_dict of loss_scaler + runner.meta.setdefault( + 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() + + # clear grads + runner.model.zero_grad() + runner.optimizer.zero_grad() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/profiler.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/profiler.py new file mode 100644 index 0000000000000000000000000000000000000000..b70236997eec59c2209ef351ae38863b4112d0ec --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/profiler.py @@ -0,0 +1,180 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import warnings +from typing import Callable, List, Optional, Union + +import torch + +from ..dist_utils import master_only +from .hook import HOOKS, Hook + + +@HOOKS.register_module() +class ProfilerHook(Hook): + """Profiler to analyze performance during training. + + PyTorch Profiler is a tool that allows the collection of the performance + metrics during the training. More details on Profiler can be found at + https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile + + Args: + by_epoch (bool): Profile performance by epoch or by iteration. + Default: True. + profile_iters (int): Number of iterations for profiling. + If ``by_epoch=True``, profile_iters indicates that they are the + first profile_iters epochs at the beginning of the + training, otherwise it indicates the first profile_iters + iterations. Default: 1. + activities (list[str]): List of activity groups (CPU, CUDA) to use in + profiling. Default: ['cpu', 'cuda']. + schedule (dict, optional): Config of generating the callable schedule. + if schedule is None, profiler will not add step markers into the + trace and table view. Default: None. + on_trace_ready (callable, dict): Either a handler or a dict of generate + handler. Default: None. + record_shapes (bool): Save information about operator's input shapes. + Default: False. + profile_memory (bool): Track tensor memory allocation/deallocation. + Default: False. + with_stack (bool): Record source information (file and line number) + for the ops. Default: False. + with_flops (bool): Use formula to estimate the FLOPS of specific + operators (matrix multiplication and 2D convolution). + Default: False. + json_trace_path (str, optional): Exports the collected trace in Chrome + JSON format. Default: None. + + Example: + >>> runner = ... # instantiate a Runner + >>> # tensorboard trace + >>> trace_config = dict(type='tb_trace', dir_name='work_dir') + >>> profiler_config = dict(on_trace_ready=trace_config) + >>> runner.register_profiler_hook(profiler_config) + >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) + """ + + def __init__(self, + by_epoch: bool = True, + profile_iters: int = 1, + activities: List[str] = ['cpu', 'cuda'], + schedule: Optional[dict] = None, + on_trace_ready: Optional[Union[Callable, dict]] = None, + record_shapes: bool = False, + profile_memory: bool = False, + with_stack: bool = False, + with_flops: bool = False, + json_trace_path: Optional[str] = None) -> None: + try: + from torch import profiler # torch version >= 1.8.1 + except ImportError: + raise ImportError('profiler is the new feature of torch1.8.1, ' + f'but your version is {torch.__version__}') + + assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' + self.by_epoch = by_epoch + + if profile_iters < 1: + raise ValueError('profile_iters should be greater than 0, but got ' + f'{profile_iters}') + self.profile_iters = profile_iters + + if not isinstance(activities, list): + raise ValueError( + f'activities should be list, but got {type(activities)}') + self.activities = [] + for activity in activities: + activity = activity.lower() + if activity == 'cpu': + self.activities.append(profiler.ProfilerActivity.CPU) + elif activity == 'cuda': + self.activities.append(profiler.ProfilerActivity.CUDA) + else: + raise ValueError( + f'activity should be "cpu" or "cuda", but got {activity}') + + if schedule is not None: + self.schedule = profiler.schedule(**schedule) + else: + self.schedule = None + + self.on_trace_ready = on_trace_ready + self.record_shapes = record_shapes + self.profile_memory = profile_memory + self.with_stack = with_stack + self.with_flops = with_flops + self.json_trace_path = json_trace_path + + @master_only + def before_run(self, runner): + if self.by_epoch and runner.max_epochs < self.profile_iters: + raise ValueError('self.profile_iters should not be greater than ' + f'{runner.max_epochs}') + + if not self.by_epoch and runner.max_iters < self.profile_iters: + raise ValueError('self.profile_iters should not be greater than ' + f'{runner.max_iters}') + + if callable(self.on_trace_ready): # handler + _on_trace_ready = self.on_trace_ready + elif isinstance(self.on_trace_ready, dict): # config of handler + trace_cfg = self.on_trace_ready.copy() + trace_type = trace_cfg.pop('type') # log_trace handler + if trace_type == 'log_trace': + + def _log_handler(prof): + print(prof.key_averages().table(**trace_cfg)) + + _on_trace_ready = _log_handler + elif trace_type == 'tb_trace': # tensorboard_trace handler + try: + import torch_tb_profiler # noqa: F401 + except ImportError: + raise ImportError('please run "pip install ' + 'torch-tb-profiler" to install ' + 'torch_tb_profiler') + _on_trace_ready = torch.profiler.tensorboard_trace_handler( + **trace_cfg) + else: + raise ValueError('trace_type should be "log_trace" or ' + f'"tb_trace", but got {trace_type}') + elif self.on_trace_ready is None: + _on_trace_ready = None # type: ignore + else: + raise ValueError('on_trace_ready should be handler, dict or None, ' + f'but got {type(self.on_trace_ready)}') + + if runner.max_epochs > 1: + warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' + 'instead of 1 epoch. Since profiler will slow down ' + 'the training, it is recommended to train 1 epoch ' + 'with ProfilerHook and adjust your setting according' + ' to the profiler summary. During normal training ' + '(epoch > 1), you may disable the ProfilerHook.') + + self.profiler = torch.profiler.profile( + activities=self.activities, + schedule=self.schedule, + on_trace_ready=_on_trace_ready, + record_shapes=self.record_shapes, + profile_memory=self.profile_memory, + with_stack=self.with_stack, + with_flops=self.with_flops) + + self.profiler.__enter__() + runner.logger.info('profiler is profiling...') + + @master_only + def after_train_epoch(self, runner): + if self.by_epoch and runner.epoch == self.profile_iters - 1: + runner.logger.info('profiler may take a few minutes...') + self.profiler.__exit__(None, None, None) + if self.json_trace_path is not None: + self.profiler.export_chrome_trace(self.json_trace_path) + + @master_only + def after_train_iter(self, runner): + self.profiler.step() + if not self.by_epoch and runner.iter == self.profile_iters - 1: + runner.logger.info('profiler may take a few minutes...') + self.profiler.__exit__(None, None, None) + if self.json_trace_path is not None: + self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/sampler_seed.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/sampler_seed.py new file mode 100644 index 0000000000000000000000000000000000000000..ee0dc6bdd8df5775857028aaed5444c0f59caf80 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/sampler_seed.py @@ -0,0 +1,20 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .hook import HOOKS, Hook + + +@HOOKS.register_module() +class DistSamplerSeedHook(Hook): + """Data-loading sampler for distributed training. + + When distributed training, it is only useful in conjunction with + :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same + purpose with :obj:`IterLoader`. + """ + + def before_epoch(self, runner): + if hasattr(runner.data_loader.sampler, 'set_epoch'): + # in case the data loader uses `SequentialSampler` in Pytorch + runner.data_loader.sampler.set_epoch(runner.epoch) + elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): + # batch sampler in pytorch warps the sampler as its attributes. + runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/sync_buffer.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/sync_buffer.py new file mode 100644 index 0000000000000000000000000000000000000000..6376b7ff894280cb2782243b25e8973650591577 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/hooks/sync_buffer.py @@ -0,0 +1,22 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from ..dist_utils import allreduce_params +from .hook import HOOKS, Hook + + +@HOOKS.register_module() +class SyncBuffersHook(Hook): + """Synchronize model buffers such as running_mean and running_var in BN at + the end of each epoch. + + Args: + distributed (bool): Whether distributed training is used. It is + effective only for distributed training. Defaults to True. + """ + + def __init__(self, distributed=True): + self.distributed = distributed + + def after_epoch(self, runner): + """All-reduce model buffers at the end of each epoch.""" + if self.distributed: + allreduce_params(runner.model.buffers()) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/iter_based_runner.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/iter_based_runner.py new file mode 100644 index 0000000000000000000000000000000000000000..e93849ba8a0960d958c76151d5bdd406e4b795a4 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/iter_based_runner.py @@ -0,0 +1,273 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os.path as osp +import platform +import shutil +import time +import warnings + +import torch +from torch.optim import Optimizer + +import annotator.mmpkg.mmcv as mmcv +from .base_runner import BaseRunner +from .builder import RUNNERS +from .checkpoint import save_checkpoint +from .hooks import IterTimerHook +from .utils import get_host_info + + +class IterLoader: + + def __init__(self, dataloader): + self._dataloader = dataloader + self.iter_loader = iter(self._dataloader) + self._epoch = 0 + + @property + def epoch(self): + return self._epoch + + def __next__(self): + try: + data = next(self.iter_loader) + except StopIteration: + self._epoch += 1 + if hasattr(self._dataloader.sampler, 'set_epoch'): + self._dataloader.sampler.set_epoch(self._epoch) + time.sleep(2) # Prevent possible deadlock during epoch transition + self.iter_loader = iter(self._dataloader) + data = next(self.iter_loader) + + return data + + def __len__(self): + return len(self._dataloader) + + +@RUNNERS.register_module() +class IterBasedRunner(BaseRunner): + """Iteration-based Runner. + + This runner train models iteration by iteration. + """ + + def train(self, data_loader, **kwargs): + self.model.train() + self.mode = 'train' + self.data_loader = data_loader + self._epoch = data_loader.epoch + data_batch = next(data_loader) + self.call_hook('before_train_iter') + outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) + if not isinstance(outputs, dict): + raise TypeError('model.train_step() must return a dict') + if 'log_vars' in outputs: + self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) + self.outputs = outputs + self.call_hook('after_train_iter') + self._inner_iter += 1 + self._iter += 1 + + @torch.no_grad() + def val(self, data_loader, **kwargs): + self.model.eval() + self.mode = 'val' + self.data_loader = data_loader + data_batch = next(data_loader) + self.call_hook('before_val_iter') + outputs = self.model.val_step(data_batch, **kwargs) + if not isinstance(outputs, dict): + raise TypeError('model.val_step() must return a dict') + if 'log_vars' in outputs: + self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) + self.outputs = outputs + self.call_hook('after_val_iter') + self._inner_iter += 1 + + def run(self, data_loaders, workflow, max_iters=None, **kwargs): + """Start running. + + Args: + data_loaders (list[:obj:`DataLoader`]): Dataloaders for training + and validation. + workflow (list[tuple]): A list of (phase, iters) to specify the + running order and iterations. E.g, [('train', 10000), + ('val', 1000)] means running 10000 iterations for training and + 1000 iterations for validation, iteratively. + """ + assert isinstance(data_loaders, list) + assert mmcv.is_list_of(workflow, tuple) + assert len(data_loaders) == len(workflow) + if max_iters is not None: + warnings.warn( + 'setting max_iters in run is deprecated, ' + 'please set max_iters in runner_config', DeprecationWarning) + self._max_iters = max_iters + assert self._max_iters is not None, ( + 'max_iters must be specified during instantiation') + + work_dir = self.work_dir if self.work_dir is not None else 'NONE' + self.logger.info('Start running, host: %s, work_dir: %s', + get_host_info(), work_dir) + self.logger.info('Hooks will be executed in the following order:\n%s', + self.get_hook_info()) + self.logger.info('workflow: %s, max: %d iters', workflow, + self._max_iters) + self.call_hook('before_run') + + iter_loaders = [IterLoader(x) for x in data_loaders] + + self.call_hook('before_epoch') + + while self.iter < self._max_iters: + for i, flow in enumerate(workflow): + self._inner_iter = 0 + mode, iters = flow + if not isinstance(mode, str) or not hasattr(self, mode): + raise ValueError( + 'runner has no method named "{}" to run a workflow'. + format(mode)) + iter_runner = getattr(self, mode) + for _ in range(iters): + if mode == 'train' and self.iter >= self._max_iters: + break + iter_runner(iter_loaders[i], **kwargs) + + time.sleep(1) # wait for some hooks like loggers to finish + self.call_hook('after_epoch') + self.call_hook('after_run') + + def resume(self, + checkpoint, + resume_optimizer=True, + map_location='default'): + """Resume model from checkpoint. + + Args: + checkpoint (str): Checkpoint to resume from. + resume_optimizer (bool, optional): Whether resume the optimizer(s) + if the checkpoint file includes optimizer(s). Default to True. + map_location (str, optional): Same as :func:`torch.load`. + Default to 'default'. + """ + if map_location == 'default': + device_id = torch.cuda.current_device() + checkpoint = self.load_checkpoint( + checkpoint, + map_location=lambda storage, loc: storage.cuda(device_id)) + else: + checkpoint = self.load_checkpoint( + checkpoint, map_location=map_location) + + self._epoch = checkpoint['meta']['epoch'] + self._iter = checkpoint['meta']['iter'] + self._inner_iter = checkpoint['meta']['iter'] + if 'optimizer' in checkpoint and resume_optimizer: + if isinstance(self.optimizer, Optimizer): + self.optimizer.load_state_dict(checkpoint['optimizer']) + elif isinstance(self.optimizer, dict): + for k in self.optimizer.keys(): + self.optimizer[k].load_state_dict( + checkpoint['optimizer'][k]) + else: + raise TypeError( + 'Optimizer should be dict or torch.optim.Optimizer ' + f'but got {type(self.optimizer)}') + + self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') + + def save_checkpoint(self, + out_dir, + filename_tmpl='iter_{}.pth', + meta=None, + save_optimizer=True, + create_symlink=True): + """Save checkpoint to file. + + Args: + out_dir (str): Directory to save checkpoint files. + filename_tmpl (str, optional): Checkpoint file template. + Defaults to 'iter_{}.pth'. + meta (dict, optional): Metadata to be saved in checkpoint. + Defaults to None. + save_optimizer (bool, optional): Whether save optimizer. + Defaults to True. + create_symlink (bool, optional): Whether create symlink to the + latest checkpoint file. Defaults to True. + """ + if meta is None: + meta = {} + elif not isinstance(meta, dict): + raise TypeError( + f'meta should be a dict or None, but got {type(meta)}') + if self.meta is not None: + meta.update(self.meta) + # Note: meta.update(self.meta) should be done before + # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise + # there will be problems with resumed checkpoints. + # More details in https://github.com/open-mmlab/mmcv/pull/1108 + meta.update(epoch=self.epoch + 1, iter=self.iter) + + filename = filename_tmpl.format(self.iter + 1) + filepath = osp.join(out_dir, filename) + optimizer = self.optimizer if save_optimizer else None + save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) + # in some environments, `os.symlink` is not supported, you may need to + # set `create_symlink` to False + if create_symlink: + dst_file = osp.join(out_dir, 'latest.pth') + if platform.system() != 'Windows': + mmcv.symlink(filename, dst_file) + else: + shutil.copy(filepath, dst_file) + + def register_training_hooks(self, + lr_config, + optimizer_config=None, + checkpoint_config=None, + log_config=None, + momentum_config=None, + custom_hooks_config=None): + """Register default hooks for iter-based training. + + Checkpoint hook, optimizer stepper hook and logger hooks will be set to + `by_epoch=False` by default. + + Default hooks include: + + +----------------------+-------------------------+ + | Hooks | Priority | + +======================+=========================+ + | LrUpdaterHook | VERY_HIGH (10) | + +----------------------+-------------------------+ + | MomentumUpdaterHook | HIGH (30) | + +----------------------+-------------------------+ + | OptimizerStepperHook | ABOVE_NORMAL (40) | + +----------------------+-------------------------+ + | CheckpointSaverHook | NORMAL (50) | + +----------------------+-------------------------+ + | IterTimerHook | LOW (70) | + +----------------------+-------------------------+ + | LoggerHook(s) | VERY_LOW (90) | + +----------------------+-------------------------+ + | CustomHook(s) | defaults to NORMAL (50) | + +----------------------+-------------------------+ + + If custom hooks have same priority with default hooks, custom hooks + will be triggered after default hooks. + """ + if checkpoint_config is not None: + checkpoint_config.setdefault('by_epoch', False) + if lr_config is not None: + lr_config.setdefault('by_epoch', False) + if log_config is not None: + for info in log_config['hooks']: + info.setdefault('by_epoch', False) + super(IterBasedRunner, self).register_training_hooks( + lr_config=lr_config, + momentum_config=momentum_config, + optimizer_config=optimizer_config, + checkpoint_config=checkpoint_config, + log_config=log_config, + timer_config=IterTimerHook(), + custom_hooks_config=custom_hooks_config) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/log_buffer.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/log_buffer.py new file mode 100644 index 0000000000000000000000000000000000000000..d949e2941c5400088c7cd8a1dc893d8b233ae785 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/log_buffer.py @@ -0,0 +1,41 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from collections import OrderedDict + +import numpy as np + + +class LogBuffer: + + def __init__(self): + self.val_history = OrderedDict() + self.n_history = OrderedDict() + self.output = OrderedDict() + self.ready = False + + def clear(self): + self.val_history.clear() + self.n_history.clear() + self.clear_output() + + def clear_output(self): + self.output.clear() + self.ready = False + + def update(self, vars, count=1): + assert isinstance(vars, dict) + for key, var in vars.items(): + if key not in self.val_history: + self.val_history[key] = [] + self.n_history[key] = [] + self.val_history[key].append(var) + self.n_history[key].append(count) + + def average(self, n=0): + """Average latest n values or all values.""" + assert n >= 0 + for key in self.val_history: + values = np.array(self.val_history[key][-n:]) + nums = np.array(self.n_history[key][-n:]) + avg = np.sum(values * nums) / np.sum(nums) + self.output[key] = avg + self.ready = True diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/optimizer/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/optimizer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..53c34d0470992cbc374f29681fdd00dc0e57968d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/optimizer/__init__.py @@ -0,0 +1,9 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, + build_optimizer_constructor) +from .default_constructor import DefaultOptimizerConstructor + +__all__ = [ + 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', + 'build_optimizer', 'build_optimizer_constructor' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/optimizer/builder.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/optimizer/builder.py new file mode 100644 index 0000000000000000000000000000000000000000..f9234eed8f1f186d9d8dfda34562157ee39bdb3a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/optimizer/builder.py @@ -0,0 +1,44 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import copy +import inspect + +import torch + +from ...utils import Registry, build_from_cfg + +OPTIMIZERS = Registry('optimizer') +OPTIMIZER_BUILDERS = Registry('optimizer builder') + + +def register_torch_optimizers(): + torch_optimizers = [] + for module_name in dir(torch.optim): + if module_name.startswith('__'): + continue + _optim = getattr(torch.optim, module_name) + if inspect.isclass(_optim) and issubclass(_optim, + torch.optim.Optimizer): + OPTIMIZERS.register_module()(_optim) + torch_optimizers.append(module_name) + return torch_optimizers + + +TORCH_OPTIMIZERS = register_torch_optimizers() + + +def build_optimizer_constructor(cfg): + return build_from_cfg(cfg, OPTIMIZER_BUILDERS) + + +def build_optimizer(model, cfg): + optimizer_cfg = copy.deepcopy(cfg) + constructor_type = optimizer_cfg.pop('constructor', + 'DefaultOptimizerConstructor') + paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) + optim_constructor = build_optimizer_constructor( + dict( + type=constructor_type, + optimizer_cfg=optimizer_cfg, + paramwise_cfg=paramwise_cfg)) + optimizer = optim_constructor(model) + return optimizer diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/optimizer/default_constructor.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/optimizer/default_constructor.py new file mode 100644 index 0000000000000000000000000000000000000000..de2ae39cb6378cc17c098f5324f5d5c321879b91 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/optimizer/default_constructor.py @@ -0,0 +1,249 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import warnings + +import torch +from torch.nn import GroupNorm, LayerNorm + +from annotator.mmpkg.mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of +from annotator.mmpkg.mmcv.utils.ext_loader import check_ops_exist +from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS + + +@OPTIMIZER_BUILDERS.register_module() +class DefaultOptimizerConstructor: + """Default constructor for optimizers. + + By default each parameter share the same optimizer settings, and we + provide an argument ``paramwise_cfg`` to specify parameter-wise settings. + It is a dict and may contain the following fields: + + - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If + one of the keys in ``custom_keys`` is a substring of the name of one + parameter, then the setting of the parameter will be specified by + ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will + be ignored. It should be noted that the aforementioned ``key`` is the + longest key that is a substring of the name of the parameter. If there + are multiple matched keys with the same length, then the key with lower + alphabet order will be chosen. + ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` + and ``decay_mult``. See Example 2 below. + - ``bias_lr_mult`` (float): It will be multiplied to the learning + rate for all bias parameters (except for those in normalization + layers and offset layers of DCN). + - ``bias_decay_mult`` (float): It will be multiplied to the weight + decay for all bias parameters (except for those in + normalization layers, depthwise conv layers, offset layers of DCN). + - ``norm_decay_mult`` (float): It will be multiplied to the weight + decay for all weight and bias parameters of normalization + layers. + - ``dwconv_decay_mult`` (float): It will be multiplied to the weight + decay for all weight and bias parameters of depthwise conv + layers. + - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning + rate for parameters of offset layer in the deformable convs + of a model. + - ``bypass_duplicate`` (bool): If true, the duplicate parameters + would not be added into optimizer. Default: False. + + Note: + 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will + override the effect of ``bias_lr_mult`` in the bias of offset + layer. So be careful when using both ``bias_lr_mult`` and + ``dcn_offset_lr_mult``. If you wish to apply both of them to the + offset layer in deformable convs, set ``dcn_offset_lr_mult`` + to the original ``dcn_offset_lr_mult`` * ``bias_lr_mult``. + 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will + apply it to all the DCN layers in the model. So be careful when + the model contains multiple DCN layers in places other than + backbone. + + Args: + model (:obj:`nn.Module`): The model with parameters to be optimized. + optimizer_cfg (dict): The config dict of the optimizer. + Positional fields are + + - `type`: class name of the optimizer. + + Optional fields are + + - any arguments of the corresponding optimizer type, e.g., + lr, weight_decay, momentum, etc. + paramwise_cfg (dict, optional): Parameter-wise options. + + Example 1: + >>> model = torch.nn.modules.Conv1d(1, 1, 1) + >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, + >>> weight_decay=0.0001) + >>> paramwise_cfg = dict(norm_decay_mult=0.) + >>> optim_builder = DefaultOptimizerConstructor( + >>> optimizer_cfg, paramwise_cfg) + >>> optimizer = optim_builder(model) + + Example 2: + >>> # assume model have attribute model.backbone and model.cls_head + >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) + >>> paramwise_cfg = dict(custom_keys={ + '.backbone': dict(lr_mult=0.1, decay_mult=0.9)}) + >>> optim_builder = DefaultOptimizerConstructor( + >>> optimizer_cfg, paramwise_cfg) + >>> optimizer = optim_builder(model) + >>> # Then the `lr` and `weight_decay` for model.backbone is + >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for + >>> # model.cls_head is (0.01, 0.95). + """ + + def __init__(self, optimizer_cfg, paramwise_cfg=None): + if not isinstance(optimizer_cfg, dict): + raise TypeError('optimizer_cfg should be a dict', + f'but got {type(optimizer_cfg)}') + self.optimizer_cfg = optimizer_cfg + self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg + self.base_lr = optimizer_cfg.get('lr', None) + self.base_wd = optimizer_cfg.get('weight_decay', None) + self._validate_cfg() + + def _validate_cfg(self): + if not isinstance(self.paramwise_cfg, dict): + raise TypeError('paramwise_cfg should be None or a dict, ' + f'but got {type(self.paramwise_cfg)}') + + if 'custom_keys' in self.paramwise_cfg: + if not isinstance(self.paramwise_cfg['custom_keys'], dict): + raise TypeError( + 'If specified, custom_keys must be a dict, ' + f'but got {type(self.paramwise_cfg["custom_keys"])}') + if self.base_wd is None: + for key in self.paramwise_cfg['custom_keys']: + if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: + raise ValueError('base_wd should not be None') + + # get base lr and weight decay + # weight_decay must be explicitly specified if mult is specified + if ('bias_decay_mult' in self.paramwise_cfg + or 'norm_decay_mult' in self.paramwise_cfg + or 'dwconv_decay_mult' in self.paramwise_cfg): + if self.base_wd is None: + raise ValueError('base_wd should not be None') + + def _is_in(self, param_group, param_group_list): + assert is_list_of(param_group_list, dict) + param = set(param_group['params']) + param_set = set() + for group in param_group_list: + param_set.update(set(group['params'])) + + return not param.isdisjoint(param_set) + + def add_params(self, params, module, prefix='', is_dcn_module=None): + """Add all parameters of module to the params list. + + The parameters of the given module will be added to the list of param + groups, with specific rules defined by paramwise_cfg. + + Args: + params (list[dict]): A list of param groups, it will be modified + in place. + module (nn.Module): The module to be added. + prefix (str): The prefix of the module + is_dcn_module (int|float|None): If the current module is a + submodule of DCN, `is_dcn_module` will be passed to + control conv_offset layer's learning rate. Defaults to None. + """ + # get param-wise options + custom_keys = self.paramwise_cfg.get('custom_keys', {}) + # first sort with alphabet order and then sort with reversed len of str + sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) + + bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) + bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) + norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) + dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) + bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) + dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) + + # special rules for norm layers and depth-wise conv layers + is_norm = isinstance(module, + (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) + is_dwconv = ( + isinstance(module, torch.nn.Conv2d) + and module.in_channels == module.groups) + + for name, param in module.named_parameters(recurse=False): + param_group = {'params': [param]} + if not param.requires_grad: + params.append(param_group) + continue + if bypass_duplicate and self._is_in(param_group, params): + warnings.warn(f'{prefix} is duplicate. It is skipped since ' + f'bypass_duplicate={bypass_duplicate}') + continue + # if the parameter match one of the custom keys, ignore other rules + is_custom = False + for key in sorted_keys: + if key in f'{prefix}.{name}': + is_custom = True + lr_mult = custom_keys[key].get('lr_mult', 1.) + param_group['lr'] = self.base_lr * lr_mult + if self.base_wd is not None: + decay_mult = custom_keys[key].get('decay_mult', 1.) + param_group['weight_decay'] = self.base_wd * decay_mult + break + + if not is_custom: + # bias_lr_mult affects all bias parameters + # except for norm.bias dcn.conv_offset.bias + if name == 'bias' and not (is_norm or is_dcn_module): + param_group['lr'] = self.base_lr * bias_lr_mult + + if (prefix.find('conv_offset') != -1 and is_dcn_module + and isinstance(module, torch.nn.Conv2d)): + # deal with both dcn_offset's bias & weight + param_group['lr'] = self.base_lr * dcn_offset_lr_mult + + # apply weight decay policies + if self.base_wd is not None: + # norm decay + if is_norm: + param_group[ + 'weight_decay'] = self.base_wd * norm_decay_mult + # depth-wise conv + elif is_dwconv: + param_group[ + 'weight_decay'] = self.base_wd * dwconv_decay_mult + # bias lr and decay + elif name == 'bias' and not is_dcn_module: + # TODO: current bias_decay_mult will have affect on DCN + param_group[ + 'weight_decay'] = self.base_wd * bias_decay_mult + params.append(param_group) + + if check_ops_exist(): + from annotator.mmpkg.mmcv.ops import DeformConv2d, ModulatedDeformConv2d + is_dcn_module = isinstance(module, + (DeformConv2d, ModulatedDeformConv2d)) + else: + is_dcn_module = False + for child_name, child_mod in module.named_children(): + child_prefix = f'{prefix}.{child_name}' if prefix else child_name + self.add_params( + params, + child_mod, + prefix=child_prefix, + is_dcn_module=is_dcn_module) + + def __call__(self, model): + if hasattr(model, 'module'): + model = model.module + + optimizer_cfg = self.optimizer_cfg.copy() + # if no paramwise option is specified, just use the global setting + if not self.paramwise_cfg: + optimizer_cfg['params'] = model.parameters() + return build_from_cfg(optimizer_cfg, OPTIMIZERS) + + # set param-wise lr and weight decay recursively + params = [] + self.add_params(params, model) + optimizer_cfg['params'] = params + + return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/priority.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/priority.py new file mode 100644 index 0000000000000000000000000000000000000000..64cc4e3a05f8d5b89ab6eb32461e6e80f1d62e67 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/priority.py @@ -0,0 +1,60 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from enum import Enum + + +class Priority(Enum): + """Hook priority levels. + + +--------------+------------+ + | Level | Value | + +==============+============+ + | HIGHEST | 0 | + +--------------+------------+ + | VERY_HIGH | 10 | + +--------------+------------+ + | HIGH | 30 | + +--------------+------------+ + | ABOVE_NORMAL | 40 | + +--------------+------------+ + | NORMAL | 50 | + +--------------+------------+ + | BELOW_NORMAL | 60 | + +--------------+------------+ + | LOW | 70 | + +--------------+------------+ + | VERY_LOW | 90 | + +--------------+------------+ + | LOWEST | 100 | + +--------------+------------+ + """ + + HIGHEST = 0 + VERY_HIGH = 10 + HIGH = 30 + ABOVE_NORMAL = 40 + NORMAL = 50 + BELOW_NORMAL = 60 + LOW = 70 + VERY_LOW = 90 + LOWEST = 100 + + +def get_priority(priority): + """Get priority value. + + Args: + priority (int or str or :obj:`Priority`): Priority. + + Returns: + int: The priority value. + """ + if isinstance(priority, int): + if priority < 0 or priority > 100: + raise ValueError('priority must be between 0 and 100') + return priority + elif isinstance(priority, Priority): + return priority.value + elif isinstance(priority, str): + return Priority[priority.upper()].value + else: + raise TypeError('priority must be an integer or Priority enum value') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/utils.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..11bbc523e9a009119531c5eb903a93fe40cc5bca --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/runner/utils.py @@ -0,0 +1,93 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os +import random +import sys +import time +import warnings +from getpass import getuser +from socket import gethostname + +import numpy as np +import torch + +import annotator.mmpkg.mmcv as mmcv + + +def get_host_info(): + """Get hostname and username. + + Return empty string if exception raised, e.g. ``getpass.getuser()`` will + lead to error in docker container + """ + host = '' + try: + host = f'{getuser()}@{gethostname()}' + except Exception as e: + warnings.warn(f'Host or user not found: {str(e)}') + finally: + return host + + +def get_time_str(): + return time.strftime('%Y%m%d_%H%M%S', time.localtime()) + + +def obj_from_dict(info, parent=None, default_args=None): + """Initialize an object from dict. + + The dict must contain the key "type", which indicates the object type, it + can be either a string or type, such as "list" or ``list``. Remaining + fields are treated as the arguments for constructing the object. + + Args: + info (dict): Object types and arguments. + parent (:class:`module`): Module which may containing expected object + classes. + default_args (dict, optional): Default arguments for initializing the + object. + + Returns: + any type: Object built from the dict. + """ + assert isinstance(info, dict) and 'type' in info + assert isinstance(default_args, dict) or default_args is None + args = info.copy() + obj_type = args.pop('type') + if mmcv.is_str(obj_type): + if parent is not None: + obj_type = getattr(parent, obj_type) + else: + obj_type = sys.modules[obj_type] + elif not isinstance(obj_type, type): + raise TypeError('type must be a str or valid type, but ' + f'got {type(obj_type)}') + if default_args is not None: + for name, value in default_args.items(): + args.setdefault(name, value) + return obj_type(**args) + + +def set_random_seed(seed, deterministic=False, use_rank_shift=False): + """Set random seed. + + Args: + seed (int): Seed to be used. + deterministic (bool): Whether to set the deterministic option for + CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` + to True and `torch.backends.cudnn.benchmark` to False. + Default: False. + rank_shift (bool): Whether to add rank number to the random seed to + have different random seed in different threads. Default: False. + """ + if use_rank_shift: + rank, _ = mmcv.runner.get_dist_info() + seed += rank + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + os.environ['PYTHONHASHSEED'] = str(seed) + if deterministic: + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..378a0068432a371af364de9d73785901c0f83383 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/__init__.py @@ -0,0 +1,69 @@ +# flake8: noqa +# Copyright (c) OpenMMLab. All rights reserved. +from .config import Config, ConfigDict, DictAction +from .misc import (check_prerequisites, concat_list, deprecated_api_warning, + has_method, import_modules_from_strings, is_list_of, + is_method_overridden, is_seq_of, is_str, is_tuple_of, + iter_cast, list_cast, requires_executable, requires_package, + slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, + to_ntuple, tuple_cast) +from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, + scandir, symlink) +from .progressbar import (ProgressBar, track_iter_progress, + track_parallel_progress, track_progress) +from .testing import (assert_attrs_equal, assert_dict_contains_subset, + assert_dict_has_keys, assert_is_norm_layer, + assert_keys_equal, assert_params_all_zeros, + check_python_script) +from .timer import Timer, TimerError, check_time +from .version_utils import digit_version, get_git_hash + +try: + import torch +except ImportError: + __all__ = [ + 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', + 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', + 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', + 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', + 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', + 'track_progress', 'track_iter_progress', 'track_parallel_progress', + 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', + 'digit_version', 'get_git_hash', 'import_modules_from_strings', + 'assert_dict_contains_subset', 'assert_attrs_equal', + 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', + 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', + 'is_method_overridden', 'has_method' + ] +else: + from .env import collect_env + from .logging import get_logger, print_log + from .parrots_jit import jit, skip_no_elena + from .parrots_wrapper import ( + TORCH_VERSION, BuildExtension, CppExtension, CUDAExtension, DataLoader, + PoolDataLoader, SyncBatchNorm, _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, + _AvgPoolNd, _BatchNorm, _ConvNd, _ConvTransposeMixin, _InstanceNorm, + _MaxPoolNd, get_build_config, is_rocm_pytorch, _get_cuda_home) + from .registry import Registry, build_from_cfg + from .trace import is_jit_tracing + __all__ = [ + 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', + 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', + 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', + 'check_prerequisites', 'requires_package', 'requires_executable', + 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', + 'symlink', 'scandir', 'ProgressBar', 'track_progress', + 'track_iter_progress', 'track_parallel_progress', 'Registry', + 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', + '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', + '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', + 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', + 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', + 'deprecated_api_warning', 'digit_version', 'get_git_hash', + 'import_modules_from_strings', 'jit', 'skip_no_elena', + 'assert_dict_contains_subset', 'assert_attrs_equal', + 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', + 'assert_params_all_zeros', 'check_python_script', + 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', + '_get_cuda_home', 'has_method' + ] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/config.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/config.py new file mode 100644 index 0000000000000000000000000000000000000000..e2f7551f95cbf5d8ffa225bba7325632b5e7f01b --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/config.py @@ -0,0 +1,688 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import ast +import copy +import os +import os.path as osp +import platform +import shutil +import sys +import tempfile +import uuid +import warnings +from argparse import Action, ArgumentParser +from collections import abc +from importlib import import_module + +from addict import Dict +from yapf.yapflib.yapf_api import FormatCode + +from .misc import import_modules_from_strings +from .path import check_file_exist + +if platform.system() == 'Windows': + import regex as re +else: + import re + +BASE_KEY = '_base_' +DELETE_KEY = '_delete_' +DEPRECATION_KEY = '_deprecation_' +RESERVED_KEYS = ['filename', 'text', 'pretty_text'] + + +class ConfigDict(Dict): + + def __missing__(self, name): + raise KeyError(name) + + def __getattr__(self, name): + try: + value = super(ConfigDict, self).__getattr__(name) + except KeyError: + ex = AttributeError(f"'{self.__class__.__name__}' object has no " + f"attribute '{name}'") + except Exception as e: + ex = e + else: + return value + raise ex + + +def add_args(parser, cfg, prefix=''): + for k, v in cfg.items(): + if isinstance(v, str): + parser.add_argument('--' + prefix + k) + elif isinstance(v, int): + parser.add_argument('--' + prefix + k, type=int) + elif isinstance(v, float): + parser.add_argument('--' + prefix + k, type=float) + elif isinstance(v, bool): + parser.add_argument('--' + prefix + k, action='store_true') + elif isinstance(v, dict): + add_args(parser, v, prefix + k + '.') + elif isinstance(v, abc.Iterable): + parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') + else: + print(f'cannot parse key {prefix + k} of type {type(v)}') + return parser + + +class Config: + """A facility for config and config files. + + It supports common file formats as configs: python/json/yaml. The interface + is the same as a dict object and also allows access config values as + attributes. + + Example: + >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) + >>> cfg.a + 1 + >>> cfg.b + {'b1': [0, 1]} + >>> cfg.b.b1 + [0, 1] + >>> cfg = Config.fromfile('tests/data/config/a.py') + >>> cfg.filename + "/home/kchen/projects/mmcv/tests/data/config/a.py" + >>> cfg.item4 + 'test' + >>> cfg + "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " + "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" + """ + + @staticmethod + def _validate_py_syntax(filename): + with open(filename, 'r', encoding='utf-8') as f: + # Setting encoding explicitly to resolve coding issue on windows + content = f.read() + try: + ast.parse(content) + except SyntaxError as e: + raise SyntaxError('There are syntax errors in config ' + f'file {filename}: {e}') + + @staticmethod + def _substitute_predefined_vars(filename, temp_config_name): + file_dirname = osp.dirname(filename) + file_basename = osp.basename(filename) + file_basename_no_extension = osp.splitext(file_basename)[0] + file_extname = osp.splitext(filename)[1] + support_templates = dict( + fileDirname=file_dirname, + fileBasename=file_basename, + fileBasenameNoExtension=file_basename_no_extension, + fileExtname=file_extname) + with open(filename, 'r', encoding='utf-8') as f: + # Setting encoding explicitly to resolve coding issue on windows + config_file = f.read() + for key, value in support_templates.items(): + regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' + value = value.replace('\\', '/') + config_file = re.sub(regexp, value, config_file) + with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: + tmp_config_file.write(config_file) + + @staticmethod + def _pre_substitute_base_vars(filename, temp_config_name): + """Substitute base variable placehoders to string, so that parsing + would work.""" + with open(filename, 'r', encoding='utf-8') as f: + # Setting encoding explicitly to resolve coding issue on windows + config_file = f.read() + base_var_dict = {} + regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' + base_vars = set(re.findall(regexp, config_file)) + for base_var in base_vars: + randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' + base_var_dict[randstr] = base_var + regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' + config_file = re.sub(regexp, f'"{randstr}"', config_file) + with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: + tmp_config_file.write(config_file) + return base_var_dict + + @staticmethod + def _substitute_base_vars(cfg, base_var_dict, base_cfg): + """Substitute variable strings to their actual values.""" + cfg = copy.deepcopy(cfg) + + if isinstance(cfg, dict): + for k, v in cfg.items(): + if isinstance(v, str) and v in base_var_dict: + new_v = base_cfg + for new_k in base_var_dict[v].split('.'): + new_v = new_v[new_k] + cfg[k] = new_v + elif isinstance(v, (list, tuple, dict)): + cfg[k] = Config._substitute_base_vars( + v, base_var_dict, base_cfg) + elif isinstance(cfg, tuple): + cfg = tuple( + Config._substitute_base_vars(c, base_var_dict, base_cfg) + for c in cfg) + elif isinstance(cfg, list): + cfg = [ + Config._substitute_base_vars(c, base_var_dict, base_cfg) + for c in cfg + ] + elif isinstance(cfg, str) and cfg in base_var_dict: + new_v = base_cfg + for new_k in base_var_dict[cfg].split('.'): + new_v = new_v[new_k] + cfg = new_v + + return cfg + + @staticmethod + def _file2dict(filename, use_predefined_variables=True): + filename = osp.abspath(osp.expanduser(filename)) + check_file_exist(filename) + fileExtname = osp.splitext(filename)[1] + if fileExtname not in ['.py', '.json', '.yaml', '.yml']: + raise IOError('Only py/yml/yaml/json type are supported now!') + + with tempfile.TemporaryDirectory() as temp_config_dir: + temp_config_file = tempfile.NamedTemporaryFile( + dir=temp_config_dir, suffix=fileExtname) + if platform.system() == 'Windows': + temp_config_file.close() + temp_config_name = osp.basename(temp_config_file.name) + # Substitute predefined variables + if use_predefined_variables: + Config._substitute_predefined_vars(filename, + temp_config_file.name) + else: + shutil.copyfile(filename, temp_config_file.name) + # Substitute base variables from placeholders to strings + base_var_dict = Config._pre_substitute_base_vars( + temp_config_file.name, temp_config_file.name) + + if filename.endswith('.py'): + temp_module_name = osp.splitext(temp_config_name)[0] + sys.path.insert(0, temp_config_dir) + Config._validate_py_syntax(filename) + mod = import_module(temp_module_name) + sys.path.pop(0) + cfg_dict = { + name: value + for name, value in mod.__dict__.items() + if not name.startswith('__') + } + # delete imported module + del sys.modules[temp_module_name] + elif filename.endswith(('.yml', '.yaml', '.json')): + import annotator.mmpkg.mmcv as mmcv + cfg_dict = mmcv.load(temp_config_file.name) + # close temp file + temp_config_file.close() + + # check deprecation information + if DEPRECATION_KEY in cfg_dict: + deprecation_info = cfg_dict.pop(DEPRECATION_KEY) + warning_msg = f'The config file {filename} will be deprecated ' \ + 'in the future.' + if 'expected' in deprecation_info: + warning_msg += f' Please use {deprecation_info["expected"]} ' \ + 'instead.' + if 'reference' in deprecation_info: + warning_msg += ' More information can be found at ' \ + f'{deprecation_info["reference"]}' + warnings.warn(warning_msg) + + cfg_text = filename + '\n' + with open(filename, 'r', encoding='utf-8') as f: + # Setting encoding explicitly to resolve coding issue on windows + cfg_text += f.read() + + if BASE_KEY in cfg_dict: + cfg_dir = osp.dirname(filename) + base_filename = cfg_dict.pop(BASE_KEY) + base_filename = base_filename if isinstance( + base_filename, list) else [base_filename] + + cfg_dict_list = list() + cfg_text_list = list() + for f in base_filename: + _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) + cfg_dict_list.append(_cfg_dict) + cfg_text_list.append(_cfg_text) + + base_cfg_dict = dict() + for c in cfg_dict_list: + duplicate_keys = base_cfg_dict.keys() & c.keys() + if len(duplicate_keys) > 0: + raise KeyError('Duplicate key is not allowed among bases. ' + f'Duplicate keys: {duplicate_keys}') + base_cfg_dict.update(c) + + # Substitute base variables from strings to their actual values + cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, + base_cfg_dict) + + base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) + cfg_dict = base_cfg_dict + + # merge cfg_text + cfg_text_list.append(cfg_text) + cfg_text = '\n'.join(cfg_text_list) + + return cfg_dict, cfg_text + + @staticmethod + def _merge_a_into_b(a, b, allow_list_keys=False): + """merge dict ``a`` into dict ``b`` (non-inplace). + + Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid + in-place modifications. + + Args: + a (dict): The source dict to be merged into ``b``. + b (dict): The origin dict to be fetch keys from ``a``. + allow_list_keys (bool): If True, int string keys (e.g. '0', '1') + are allowed in source ``a`` and will replace the element of the + corresponding index in b if b is a list. Default: False. + + Returns: + dict: The modified dict of ``b`` using ``a``. + + Examples: + # Normally merge a into b. + >>> Config._merge_a_into_b( + ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) + {'obj': {'a': 2}} + + # Delete b first and merge a into b. + >>> Config._merge_a_into_b( + ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) + {'obj': {'a': 2}} + + # b is a list + >>> Config._merge_a_into_b( + ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) + [{'a': 2}, {'b': 2}] + """ + b = b.copy() + for k, v in a.items(): + if allow_list_keys and k.isdigit() and isinstance(b, list): + k = int(k) + if len(b) <= k: + raise KeyError(f'Index {k} exceeds the length of list {b}') + b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) + elif isinstance(v, + dict) and k in b and not v.pop(DELETE_KEY, False): + allowed_types = (dict, list) if allow_list_keys else dict + if not isinstance(b[k], allowed_types): + raise TypeError( + f'{k}={v} in child config cannot inherit from base ' + f'because {k} is a dict in the child config but is of ' + f'type {type(b[k])} in base config. You may set ' + f'`{DELETE_KEY}=True` to ignore the base config') + b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) + else: + b[k] = v + return b + + @staticmethod + def fromfile(filename, + use_predefined_variables=True, + import_custom_modules=True): + cfg_dict, cfg_text = Config._file2dict(filename, + use_predefined_variables) + if import_custom_modules and cfg_dict.get('custom_imports', None): + import_modules_from_strings(**cfg_dict['custom_imports']) + return Config(cfg_dict, cfg_text=cfg_text, filename=filename) + + @staticmethod + def fromstring(cfg_str, file_format): + """Generate config from config str. + + Args: + cfg_str (str): Config str. + file_format (str): Config file format corresponding to the + config str. Only py/yml/yaml/json type are supported now! + + Returns: + obj:`Config`: Config obj. + """ + if file_format not in ['.py', '.json', '.yaml', '.yml']: + raise IOError('Only py/yml/yaml/json type are supported now!') + if file_format != '.py' and 'dict(' in cfg_str: + # check if users specify a wrong suffix for python + warnings.warn( + 'Please check "file_format", the file format may be .py') + with tempfile.NamedTemporaryFile( + 'w', encoding='utf-8', suffix=file_format, + delete=False) as temp_file: + temp_file.write(cfg_str) + # on windows, previous implementation cause error + # see PR 1077 for details + cfg = Config.fromfile(temp_file.name) + os.remove(temp_file.name) + return cfg + + @staticmethod + def auto_argparser(description=None): + """Generate argparser from config file automatically (experimental)""" + partial_parser = ArgumentParser(description=description) + partial_parser.add_argument('config', help='config file path') + cfg_file = partial_parser.parse_known_args()[0].config + cfg = Config.fromfile(cfg_file) + parser = ArgumentParser(description=description) + parser.add_argument('config', help='config file path') + add_args(parser, cfg) + return parser, cfg + + def __init__(self, cfg_dict=None, cfg_text=None, filename=None): + if cfg_dict is None: + cfg_dict = dict() + elif not isinstance(cfg_dict, dict): + raise TypeError('cfg_dict must be a dict, but ' + f'got {type(cfg_dict)}') + for key in cfg_dict: + if key in RESERVED_KEYS: + raise KeyError(f'{key} is reserved for config file') + + super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) + super(Config, self).__setattr__('_filename', filename) + if cfg_text: + text = cfg_text + elif filename: + with open(filename, 'r') as f: + text = f.read() + else: + text = '' + super(Config, self).__setattr__('_text', text) + + @property + def filename(self): + return self._filename + + @property + def text(self): + return self._text + + @property + def pretty_text(self): + + indent = 4 + + def _indent(s_, num_spaces): + s = s_.split('\n') + if len(s) == 1: + return s_ + first = s.pop(0) + s = [(num_spaces * ' ') + line for line in s] + s = '\n'.join(s) + s = first + '\n' + s + return s + + def _format_basic_types(k, v, use_mapping=False): + if isinstance(v, str): + v_str = f"'{v}'" + else: + v_str = str(v) + + if use_mapping: + k_str = f"'{k}'" if isinstance(k, str) else str(k) + attr_str = f'{k_str}: {v_str}' + else: + attr_str = f'{str(k)}={v_str}' + attr_str = _indent(attr_str, indent) + + return attr_str + + def _format_list(k, v, use_mapping=False): + # check if all items in the list are dict + if all(isinstance(_, dict) for _ in v): + v_str = '[\n' + v_str += '\n'.join( + f'dict({_indent(_format_dict(v_), indent)}),' + for v_ in v).rstrip(',') + if use_mapping: + k_str = f"'{k}'" if isinstance(k, str) else str(k) + attr_str = f'{k_str}: {v_str}' + else: + attr_str = f'{str(k)}={v_str}' + attr_str = _indent(attr_str, indent) + ']' + else: + attr_str = _format_basic_types(k, v, use_mapping) + return attr_str + + def _contain_invalid_identifier(dict_str): + contain_invalid_identifier = False + for key_name in dict_str: + contain_invalid_identifier |= \ + (not str(key_name).isidentifier()) + return contain_invalid_identifier + + def _format_dict(input_dict, outest_level=False): + r = '' + s = [] + + use_mapping = _contain_invalid_identifier(input_dict) + if use_mapping: + r += '{' + for idx, (k, v) in enumerate(input_dict.items()): + is_last = idx >= len(input_dict) - 1 + end = '' if outest_level or is_last else ',' + if isinstance(v, dict): + v_str = '\n' + _format_dict(v) + if use_mapping: + k_str = f"'{k}'" if isinstance(k, str) else str(k) + attr_str = f'{k_str}: dict({v_str}' + else: + attr_str = f'{str(k)}=dict({v_str}' + attr_str = _indent(attr_str, indent) + ')' + end + elif isinstance(v, list): + attr_str = _format_list(k, v, use_mapping) + end + else: + attr_str = _format_basic_types(k, v, use_mapping) + end + + s.append(attr_str) + r += '\n'.join(s) + if use_mapping: + r += '}' + return r + + cfg_dict = self._cfg_dict.to_dict() + text = _format_dict(cfg_dict, outest_level=True) + # copied from setup.cfg + yapf_style = dict( + based_on_style='pep8', + blank_line_before_nested_class_or_def=True, + split_before_expression_after_opening_paren=True) + text, _ = FormatCode(text, style_config=yapf_style, verify=True) + + return text + + def __repr__(self): + return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' + + def __len__(self): + return len(self._cfg_dict) + + def __getattr__(self, name): + return getattr(self._cfg_dict, name) + + def __getitem__(self, name): + return self._cfg_dict.__getitem__(name) + + def __setattr__(self, name, value): + if isinstance(value, dict): + value = ConfigDict(value) + self._cfg_dict.__setattr__(name, value) + + def __setitem__(self, name, value): + if isinstance(value, dict): + value = ConfigDict(value) + self._cfg_dict.__setitem__(name, value) + + def __iter__(self): + return iter(self._cfg_dict) + + def __getstate__(self): + return (self._cfg_dict, self._filename, self._text) + + def __setstate__(self, state): + _cfg_dict, _filename, _text = state + super(Config, self).__setattr__('_cfg_dict', _cfg_dict) + super(Config, self).__setattr__('_filename', _filename) + super(Config, self).__setattr__('_text', _text) + + def dump(self, file=None): + cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() + if self.filename.endswith('.py'): + if file is None: + return self.pretty_text + else: + with open(file, 'w', encoding='utf-8') as f: + f.write(self.pretty_text) + else: + import annotator.mmpkg.mmcv as mmcv + if file is None: + file_format = self.filename.split('.')[-1] + return mmcv.dump(cfg_dict, file_format=file_format) + else: + mmcv.dump(cfg_dict, file) + + def merge_from_dict(self, options, allow_list_keys=True): + """Merge list into cfg_dict. + + Merge the dict parsed by MultipleKVAction into this cfg. + + Examples: + >>> options = {'model.backbone.depth': 50, + ... 'model.backbone.with_cp':True} + >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) + >>> cfg.merge_from_dict(options) + >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') + >>> assert cfg_dict == dict( + ... model=dict(backbone=dict(depth=50, with_cp=True))) + + # Merge list element + >>> cfg = Config(dict(pipeline=[ + ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) + >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) + >>> cfg.merge_from_dict(options, allow_list_keys=True) + >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') + >>> assert cfg_dict == dict(pipeline=[ + ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) + + Args: + options (dict): dict of configs to merge from. + allow_list_keys (bool): If True, int string keys (e.g. '0', '1') + are allowed in ``options`` and will replace the element of the + corresponding index in the config if the config is a list. + Default: True. + """ + option_cfg_dict = {} + for full_key, v in options.items(): + d = option_cfg_dict + key_list = full_key.split('.') + for subkey in key_list[:-1]: + d.setdefault(subkey, ConfigDict()) + d = d[subkey] + subkey = key_list[-1] + d[subkey] = v + + cfg_dict = super(Config, self).__getattribute__('_cfg_dict') + super(Config, self).__setattr__( + '_cfg_dict', + Config._merge_a_into_b( + option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) + + +class DictAction(Action): + """ + argparse action to split an argument into KEY=VALUE form + on the first = and append to a dictionary. List options can + be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit + brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build + list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' + """ + + @staticmethod + def _parse_int_float_bool(val): + try: + return int(val) + except ValueError: + pass + try: + return float(val) + except ValueError: + pass + if val.lower() in ['true', 'false']: + return True if val.lower() == 'true' else False + return val + + @staticmethod + def _parse_iterable(val): + """Parse iterable values in the string. + + All elements inside '()' or '[]' are treated as iterable values. + + Args: + val (str): Value string. + + Returns: + list | tuple: The expanded list or tuple from the string. + + Examples: + >>> DictAction._parse_iterable('1,2,3') + [1, 2, 3] + >>> DictAction._parse_iterable('[a, b, c]') + ['a', 'b', 'c'] + >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') + [(1, 2, 3), ['a', 'b'], 'c'] + """ + + def find_next_comma(string): + """Find the position of next comma in the string. + + If no ',' is found in the string, return the string length. All + chars inside '()' and '[]' are treated as one element and thus ',' + inside these brackets are ignored. + """ + assert (string.count('(') == string.count(')')) and ( + string.count('[') == string.count(']')), \ + f'Imbalanced brackets exist in {string}' + end = len(string) + for idx, char in enumerate(string): + pre = string[:idx] + # The string before this ',' is balanced + if ((char == ',') and (pre.count('(') == pre.count(')')) + and (pre.count('[') == pre.count(']'))): + end = idx + break + return end + + # Strip ' and " characters and replace whitespace. + val = val.strip('\'\"').replace(' ', '') + is_tuple = False + if val.startswith('(') and val.endswith(')'): + is_tuple = True + val = val[1:-1] + elif val.startswith('[') and val.endswith(']'): + val = val[1:-1] + elif ',' not in val: + # val is a single value + return DictAction._parse_int_float_bool(val) + + values = [] + while len(val) > 0: + comma_idx = find_next_comma(val) + element = DictAction._parse_iterable(val[:comma_idx]) + values.append(element) + val = val[comma_idx + 1:] + if is_tuple: + values = tuple(values) + return values + + def __call__(self, parser, namespace, values, option_string=None): + options = {} + for kv in values: + key, val = kv.split('=', maxsplit=1) + options[key] = self._parse_iterable(val) + setattr(namespace, self.dest, options) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/env.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/env.py new file mode 100644 index 0000000000000000000000000000000000000000..a0c6e64a63f8a3ed813b749c134823a0ef69964c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/env.py @@ -0,0 +1,95 @@ +# Copyright (c) OpenMMLab. All rights reserved. +"""This file holding some environment constant for sharing by other files.""" + +import os.path as osp +import subprocess +import sys +from collections import defaultdict + +import cv2 +import torch + +import annotator.mmpkg.mmcv as mmcv +from .parrots_wrapper import get_build_config + + +def collect_env(): + """Collect the information of the running environments. + + Returns: + dict: The environment information. The following fields are contained. + + - sys.platform: The variable of ``sys.platform``. + - Python: Python version. + - CUDA available: Bool, indicating if CUDA is available. + - GPU devices: Device type of each GPU. + - CUDA_HOME (optional): The env var ``CUDA_HOME``. + - NVCC (optional): NVCC version. + - GCC: GCC version, "n/a" if GCC is not installed. + - PyTorch: PyTorch version. + - PyTorch compiling details: The output of \ + ``torch.__config__.show()``. + - TorchVision (optional): TorchVision version. + - OpenCV: OpenCV version. + - MMCV: MMCV version. + - MMCV Compiler: The GCC version for compiling MMCV ops. + - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. + """ + env_info = {} + env_info['sys.platform'] = sys.platform + env_info['Python'] = sys.version.replace('\n', '') + + cuda_available = torch.cuda.is_available() + env_info['CUDA available'] = cuda_available + + if cuda_available: + devices = defaultdict(list) + for k in range(torch.cuda.device_count()): + devices[torch.cuda.get_device_name(k)].append(str(k)) + for name, device_ids in devices.items(): + env_info['GPU ' + ','.join(device_ids)] = name + + from annotator.mmpkg.mmcv.utils.parrots_wrapper import _get_cuda_home + CUDA_HOME = _get_cuda_home() + env_info['CUDA_HOME'] = CUDA_HOME + + if CUDA_HOME is not None and osp.isdir(CUDA_HOME): + try: + nvcc = osp.join(CUDA_HOME, 'bin/nvcc') + nvcc = subprocess.check_output( + f'"{nvcc}" -V | tail -n1', shell=True) + nvcc = nvcc.decode('utf-8').strip() + except subprocess.SubprocessError: + nvcc = 'Not Available' + env_info['NVCC'] = nvcc + + try: + gcc = subprocess.check_output('gcc --version | head -n1', shell=True) + gcc = gcc.decode('utf-8').strip() + env_info['GCC'] = gcc + except subprocess.CalledProcessError: # gcc is unavailable + env_info['GCC'] = 'n/a' + + env_info['PyTorch'] = torch.__version__ + env_info['PyTorch compiling details'] = get_build_config() + + try: + import torchvision + env_info['TorchVision'] = torchvision.__version__ + except ModuleNotFoundError: + pass + + env_info['OpenCV'] = cv2.__version__ + + env_info['MMCV'] = mmcv.__version__ + + try: + from annotator.mmpkg.mmcv.ops import get_compiler_version, get_compiling_cuda_version + except ModuleNotFoundError: + env_info['MMCV Compiler'] = 'n/a' + env_info['MMCV CUDA Compiler'] = 'n/a' + else: + env_info['MMCV Compiler'] = get_compiler_version() + env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() + + return env_info diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/ext_loader.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/ext_loader.py new file mode 100644 index 0000000000000000000000000000000000000000..08132d2c1b9a1c28880e4bab4d4fa1ba39d9d083 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/ext_loader.py @@ -0,0 +1,71 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import importlib +import os +import pkgutil +import warnings +from collections import namedtuple + +import torch + +if torch.__version__ != 'parrots': + + def load_ext(name, funcs): + ext = importlib.import_module('mmcv.' + name) + for fun in funcs: + assert hasattr(ext, fun), f'{fun} miss in module {name}' + return ext +else: + from parrots import extension + from parrots.base import ParrotsException + + has_return_value_ops = [ + 'nms', + 'softnms', + 'nms_match', + 'nms_rotated', + 'top_pool_forward', + 'top_pool_backward', + 'bottom_pool_forward', + 'bottom_pool_backward', + 'left_pool_forward', + 'left_pool_backward', + 'right_pool_forward', + 'right_pool_backward', + 'fused_bias_leakyrelu', + 'upfirdn2d', + 'ms_deform_attn_forward', + 'pixel_group', + 'contour_expand', + ] + + def get_fake_func(name, e): + + def fake_func(*args, **kwargs): + warnings.warn(f'{name} is not supported in parrots now') + raise e + + return fake_func + + def load_ext(name, funcs): + ExtModule = namedtuple('ExtModule', funcs) + ext_list = [] + lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + for fun in funcs: + try: + ext_fun = extension.load(fun, name, lib_dir=lib_root) + except ParrotsException as e: + if 'No element registered' not in e.message: + warnings.warn(e.message) + ext_fun = get_fake_func(fun, e) + ext_list.append(ext_fun) + else: + if fun in has_return_value_ops: + ext_list.append(ext_fun.op) + else: + ext_list.append(ext_fun.op_) + return ExtModule(*ext_list) + + +def check_ops_exist(): + ext_loader = pkgutil.find_loader('mmcv._ext') + return ext_loader is not None diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/logging.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/logging.py new file mode 100644 index 0000000000000000000000000000000000000000..4aa0e04bb9b3ab2a4bfbc4def50404ccbac2c6e6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/logging.py @@ -0,0 +1,110 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import logging + +import torch.distributed as dist + +logger_initialized = {} + + +def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): + """Initialize and get a logger by name. + + If the logger has not been initialized, this method will initialize the + logger by adding one or two handlers, otherwise the initialized logger will + be directly returned. During initialization, a StreamHandler will always be + added. If `log_file` is specified and the process rank is 0, a FileHandler + will also be added. + + Args: + name (str): Logger name. + log_file (str | None): The log filename. If specified, a FileHandler + will be added to the logger. + log_level (int): The logger level. Note that only the process of + rank 0 is affected, and other processes will set the level to + "Error" thus be silent most of the time. + file_mode (str): The file mode used in opening log file. + Defaults to 'w'. + + Returns: + logging.Logger: The expected logger. + """ + logger = logging.getLogger(name) + if name in logger_initialized: + return logger + # handle hierarchical names + # e.g., logger "a" is initialized, then logger "a.b" will skip the + # initialization since it is a child of "a". + for logger_name in logger_initialized: + if name.startswith(logger_name): + return logger + + # handle duplicate logs to the console + # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) + # to the root logger. As logger.propagate is True by default, this root + # level handler causes logging messages from rank>0 processes to + # unexpectedly show up on the console, creating much unwanted clutter. + # To fix this issue, we set the root logger's StreamHandler, if any, to log + # at the ERROR level. + for handler in logger.root.handlers: + if type(handler) is logging.StreamHandler: + handler.setLevel(logging.ERROR) + + stream_handler = logging.StreamHandler() + handlers = [stream_handler] + + if dist.is_available() and dist.is_initialized(): + rank = dist.get_rank() + else: + rank = 0 + + # only rank 0 will add a FileHandler + if rank == 0 and log_file is not None: + # Here, the default behaviour of the official logger is 'a'. Thus, we + # provide an interface to change the file mode to the default + # behaviour. + file_handler = logging.FileHandler(log_file, file_mode) + handlers.append(file_handler) + + formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s') + for handler in handlers: + handler.setFormatter(formatter) + handler.setLevel(log_level) + logger.addHandler(handler) + + if rank == 0: + logger.setLevel(log_level) + else: + logger.setLevel(logging.ERROR) + + logger_initialized[name] = True + + return logger + + +def print_log(msg, logger=None, level=logging.INFO): + """Print a log message. + + Args: + msg (str): The message to be logged. + logger (logging.Logger | str | None): The logger to be used. + Some special loggers are: + - "silent": no message will be printed. + - other str: the logger obtained with `get_root_logger(logger)`. + - None: The `print()` method will be used to print log messages. + level (int): Logging level. Only available when `logger` is a Logger + object or "root". + """ + if logger is None: + print(msg) + elif isinstance(logger, logging.Logger): + logger.log(level, msg) + elif logger == 'silent': + pass + elif isinstance(logger, str): + _logger = get_logger(logger) + _logger.log(level, msg) + else: + raise TypeError( + 'logger should be either a logging.Logger object, str, ' + f'"silent" or None, but got {type(logger)}') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/misc.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/misc.py new file mode 100644 index 0000000000000000000000000000000000000000..2c58d0d7fee9fe3d4519270ad8c1e998d0d8a18c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/misc.py @@ -0,0 +1,377 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import collections.abc +import functools +import itertools +import subprocess +import warnings +from collections import abc +from importlib import import_module +from inspect import getfullargspec +from itertools import repeat + + +# From PyTorch internals +def _ntuple(n): + + def parse(x): + if isinstance(x, collections.abc.Iterable): + return x + return tuple(repeat(x, n)) + + return parse + + +to_1tuple = _ntuple(1) +to_2tuple = _ntuple(2) +to_3tuple = _ntuple(3) +to_4tuple = _ntuple(4) +to_ntuple = _ntuple + + +def is_str(x): + """Whether the input is an string instance. + + Note: This method is deprecated since python 2 is no longer supported. + """ + return isinstance(x, str) + + +def import_modules_from_strings(imports, allow_failed_imports=False): + """Import modules from the given list of strings. + + Args: + imports (list | str | None): The given module names to be imported. + allow_failed_imports (bool): If True, the failed imports will return + None. Otherwise, an ImportError is raise. Default: False. + + Returns: + list[module] | module | None: The imported modules. + + Examples: + >>> osp, sys = import_modules_from_strings( + ... ['os.path', 'sys']) + >>> import os.path as osp_ + >>> import sys as sys_ + >>> assert osp == osp_ + >>> assert sys == sys_ + """ + if not imports: + return + single_import = False + if isinstance(imports, str): + single_import = True + imports = [imports] + if not isinstance(imports, list): + raise TypeError( + f'custom_imports must be a list but got type {type(imports)}') + imported = [] + for imp in imports: + if not isinstance(imp, str): + raise TypeError( + f'{imp} is of type {type(imp)} and cannot be imported.') + try: + imported_tmp = import_module(imp) + except ImportError: + if allow_failed_imports: + warnings.warn(f'{imp} failed to import and is ignored.', + UserWarning) + imported_tmp = None + else: + raise ImportError + imported.append(imported_tmp) + if single_import: + imported = imported[0] + return imported + + +def iter_cast(inputs, dst_type, return_type=None): + """Cast elements of an iterable object into some type. + + Args: + inputs (Iterable): The input object. + dst_type (type): Destination type. + return_type (type, optional): If specified, the output object will be + converted to this type, otherwise an iterator. + + Returns: + iterator or specified type: The converted object. + """ + if not isinstance(inputs, abc.Iterable): + raise TypeError('inputs must be an iterable object') + if not isinstance(dst_type, type): + raise TypeError('"dst_type" must be a valid type') + + out_iterable = map(dst_type, inputs) + + if return_type is None: + return out_iterable + else: + return return_type(out_iterable) + + +def list_cast(inputs, dst_type): + """Cast elements of an iterable object into a list of some type. + + A partial method of :func:`iter_cast`. + """ + return iter_cast(inputs, dst_type, return_type=list) + + +def tuple_cast(inputs, dst_type): + """Cast elements of an iterable object into a tuple of some type. + + A partial method of :func:`iter_cast`. + """ + return iter_cast(inputs, dst_type, return_type=tuple) + + +def is_seq_of(seq, expected_type, seq_type=None): + """Check whether it is a sequence of some type. + + Args: + seq (Sequence): The sequence to be checked. + expected_type (type): Expected type of sequence items. + seq_type (type, optional): Expected sequence type. + + Returns: + bool: Whether the sequence is valid. + """ + if seq_type is None: + exp_seq_type = abc.Sequence + else: + assert isinstance(seq_type, type) + exp_seq_type = seq_type + if not isinstance(seq, exp_seq_type): + return False + for item in seq: + if not isinstance(item, expected_type): + return False + return True + + +def is_list_of(seq, expected_type): + """Check whether it is a list of some type. + + A partial method of :func:`is_seq_of`. + """ + return is_seq_of(seq, expected_type, seq_type=list) + + +def is_tuple_of(seq, expected_type): + """Check whether it is a tuple of some type. + + A partial method of :func:`is_seq_of`. + """ + return is_seq_of(seq, expected_type, seq_type=tuple) + + +def slice_list(in_list, lens): + """Slice a list into several sub lists by a list of given length. + + Args: + in_list (list): The list to be sliced. + lens(int or list): The expected length of each out list. + + Returns: + list: A list of sliced list. + """ + if isinstance(lens, int): + assert len(in_list) % lens == 0 + lens = [lens] * int(len(in_list) / lens) + if not isinstance(lens, list): + raise TypeError('"indices" must be an integer or a list of integers') + elif sum(lens) != len(in_list): + raise ValueError('sum of lens and list length does not ' + f'match: {sum(lens)} != {len(in_list)}') + out_list = [] + idx = 0 + for i in range(len(lens)): + out_list.append(in_list[idx:idx + lens[i]]) + idx += lens[i] + return out_list + + +def concat_list(in_list): + """Concatenate a list of list into a single list. + + Args: + in_list (list): The list of list to be merged. + + Returns: + list: The concatenated flat list. + """ + return list(itertools.chain(*in_list)) + + +def check_prerequisites( + prerequisites, + checker, + msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' + 'found, please install them first.'): # yapf: disable + """A decorator factory to check if prerequisites are satisfied. + + Args: + prerequisites (str of list[str]): Prerequisites to be checked. + checker (callable): The checker method that returns True if a + prerequisite is meet, False otherwise. + msg_tmpl (str): The message template with two variables. + + Returns: + decorator: A specific decorator. + """ + + def wrap(func): + + @functools.wraps(func) + def wrapped_func(*args, **kwargs): + requirements = [prerequisites] if isinstance( + prerequisites, str) else prerequisites + missing = [] + for item in requirements: + if not checker(item): + missing.append(item) + if missing: + print(msg_tmpl.format(', '.join(missing), func.__name__)) + raise RuntimeError('Prerequisites not meet.') + else: + return func(*args, **kwargs) + + return wrapped_func + + return wrap + + +def _check_py_package(package): + try: + import_module(package) + except ImportError: + return False + else: + return True + + +def _check_executable(cmd): + if subprocess.call(f'which {cmd}', shell=True) != 0: + return False + else: + return True + + +def requires_package(prerequisites): + """A decorator to check if some python packages are installed. + + Example: + >>> @requires_package('numpy') + >>> func(arg1, args): + >>> return numpy.zeros(1) + array([0.]) + >>> @requires_package(['numpy', 'non_package']) + >>> func(arg1, args): + >>> return numpy.zeros(1) + ImportError + """ + return check_prerequisites(prerequisites, checker=_check_py_package) + + +def requires_executable(prerequisites): + """A decorator to check if some executable files are installed. + + Example: + >>> @requires_executable('ffmpeg') + >>> func(arg1, args): + >>> print(1) + 1 + """ + return check_prerequisites(prerequisites, checker=_check_executable) + + +def deprecated_api_warning(name_dict, cls_name=None): + """A decorator to check if some arguments are deprecate and try to replace + deprecate src_arg_name to dst_arg_name. + + Args: + name_dict(dict): + key (str): Deprecate argument names. + val (str): Expected argument names. + + Returns: + func: New function. + """ + + def api_warning_wrapper(old_func): + + @functools.wraps(old_func) + def new_func(*args, **kwargs): + # get the arg spec of the decorated method + args_info = getfullargspec(old_func) + # get name of the function + func_name = old_func.__name__ + if cls_name is not None: + func_name = f'{cls_name}.{func_name}' + if args: + arg_names = args_info.args[:len(args)] + for src_arg_name, dst_arg_name in name_dict.items(): + if src_arg_name in arg_names: + warnings.warn( + f'"{src_arg_name}" is deprecated in ' + f'`{func_name}`, please use "{dst_arg_name}" ' + 'instead') + arg_names[arg_names.index(src_arg_name)] = dst_arg_name + if kwargs: + for src_arg_name, dst_arg_name in name_dict.items(): + if src_arg_name in kwargs: + + assert dst_arg_name not in kwargs, ( + f'The expected behavior is to replace ' + f'the deprecated key `{src_arg_name}` to ' + f'new key `{dst_arg_name}`, but got them ' + f'in the arguments at the same time, which ' + f'is confusing. `{src_arg_name} will be ' + f'deprecated in the future, please ' + f'use `{dst_arg_name}` instead.') + + warnings.warn( + f'"{src_arg_name}" is deprecated in ' + f'`{func_name}`, please use "{dst_arg_name}" ' + 'instead') + kwargs[dst_arg_name] = kwargs.pop(src_arg_name) + + # apply converted arguments to the decorated method + output = old_func(*args, **kwargs) + return output + + return new_func + + return api_warning_wrapper + + +def is_method_overridden(method, base_class, derived_class): + """Check if a method of base class is overridden in derived class. + + Args: + method (str): the method name to check. + base_class (type): the class of the base class. + derived_class (type | Any): the class or instance of the derived class. + """ + assert isinstance(base_class, type), \ + "base_class doesn't accept instance, Please pass class instead." + + if not isinstance(derived_class, type): + derived_class = derived_class.__class__ + + base_method = getattr(base_class, method) + derived_method = getattr(derived_class, method) + return derived_method != base_method + + +def has_method(obj: object, method: str) -> bool: + """Check whether the object has a method. + + Args: + method (str): The method name to check. + obj (object): The object to check. + + Returns: + bool: True if the object has the method else False. + """ + return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/parrots_jit.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/parrots_jit.py new file mode 100644 index 0000000000000000000000000000000000000000..61873f6dbb9b10ed972c90aa8faa321e3cb3249e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/parrots_jit.py @@ -0,0 +1,41 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os + +from .parrots_wrapper import TORCH_VERSION + +parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') + +if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': + from parrots.jit import pat as jit +else: + + def jit(func=None, + check_input=None, + full_shape=True, + derivate=False, + coderize=False, + optimize=False): + + def wrapper(func): + + def wrapper_inner(*args, **kargs): + return func(*args, **kargs) + + return wrapper_inner + + if func is None: + return wrapper + else: + return func + + +if TORCH_VERSION == 'parrots': + from parrots.utils.tester import skip_no_elena +else: + + def skip_no_elena(func): + + def wrapper(*args, **kargs): + return func(*args, **kargs) + + return wrapper diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/parrots_wrapper.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/parrots_wrapper.py new file mode 100644 index 0000000000000000000000000000000000000000..93c97640d4b9ed088ca82cfe03e6efebfcfa9dbf --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/parrots_wrapper.py @@ -0,0 +1,107 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from functools import partial + +import torch + +TORCH_VERSION = torch.__version__ + + +def is_rocm_pytorch() -> bool: + is_rocm = False + if TORCH_VERSION != 'parrots': + try: + from torch.utils.cpp_extension import ROCM_HOME + is_rocm = True if ((torch.version.hip is not None) and + (ROCM_HOME is not None)) else False + except ImportError: + pass + return is_rocm + + +def _get_cuda_home(): + if TORCH_VERSION == 'parrots': + from parrots.utils.build_extension import CUDA_HOME + else: + if is_rocm_pytorch(): + from torch.utils.cpp_extension import ROCM_HOME + CUDA_HOME = ROCM_HOME + else: + from torch.utils.cpp_extension import CUDA_HOME + return CUDA_HOME + + +def get_build_config(): + if TORCH_VERSION == 'parrots': + from parrots.config import get_build_info + return get_build_info() + else: + return torch.__config__.show() + + +def _get_conv(): + if TORCH_VERSION == 'parrots': + from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin + else: + from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin + return _ConvNd, _ConvTransposeMixin + + +def _get_dataloader(): + if TORCH_VERSION == 'parrots': + from torch.utils.data import DataLoader, PoolDataLoader + else: + from torch.utils.data import DataLoader + PoolDataLoader = DataLoader + return DataLoader, PoolDataLoader + + +def _get_extension(): + if TORCH_VERSION == 'parrots': + from parrots.utils.build_extension import BuildExtension, Extension + CppExtension = partial(Extension, cuda=False) + CUDAExtension = partial(Extension, cuda=True) + else: + from torch.utils.cpp_extension import (BuildExtension, CppExtension, + CUDAExtension) + return BuildExtension, CppExtension, CUDAExtension + + +def _get_pool(): + if TORCH_VERSION == 'parrots': + from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, + _AdaptiveMaxPoolNd, _AvgPoolNd, + _MaxPoolNd) + else: + from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, + _AdaptiveMaxPoolNd, _AvgPoolNd, + _MaxPoolNd) + return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd + + +def _get_norm(): + if TORCH_VERSION == 'parrots': + from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm + SyncBatchNorm_ = torch.nn.SyncBatchNorm2d + else: + from torch.nn.modules.instancenorm import _InstanceNorm + from torch.nn.modules.batchnorm import _BatchNorm + SyncBatchNorm_ = torch.nn.SyncBatchNorm + return _BatchNorm, _InstanceNorm, SyncBatchNorm_ + + +_ConvNd, _ConvTransposeMixin = _get_conv() +DataLoader, PoolDataLoader = _get_dataloader() +BuildExtension, CppExtension, CUDAExtension = _get_extension() +_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() +_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() + + +class SyncBatchNorm(SyncBatchNorm_): + + def _check_input_dim(self, input): + if TORCH_VERSION == 'parrots': + if input.dim() < 2: + raise ValueError( + f'expected at least 2D input (got {input.dim()}D input)') + else: + super()._check_input_dim(input) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/path.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/path.py new file mode 100644 index 0000000000000000000000000000000000000000..7dab4b3041413b1432b0f434b8b14783097d33c6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/path.py @@ -0,0 +1,101 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os +import os.path as osp +from pathlib import Path + +from .misc import is_str + + +def is_filepath(x): + return is_str(x) or isinstance(x, Path) + + +def fopen(filepath, *args, **kwargs): + if is_str(filepath): + return open(filepath, *args, **kwargs) + elif isinstance(filepath, Path): + return filepath.open(*args, **kwargs) + raise ValueError('`filepath` should be a string or a Path') + + +def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): + if not osp.isfile(filename): + raise FileNotFoundError(msg_tmpl.format(filename)) + + +def mkdir_or_exist(dir_name, mode=0o777): + if dir_name == '': + return + dir_name = osp.expanduser(dir_name) + os.makedirs(dir_name, mode=mode, exist_ok=True) + + +def symlink(src, dst, overwrite=True, **kwargs): + if os.path.lexists(dst) and overwrite: + os.remove(dst) + os.symlink(src, dst, **kwargs) + + +def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): + """Scan a directory to find the interested files. + + Args: + dir_path (str | obj:`Path`): Path of the directory. + suffix (str | tuple(str), optional): File suffix that we are + interested in. Default: None. + recursive (bool, optional): If set to True, recursively scan the + directory. Default: False. + case_sensitive (bool, optional) : If set to False, ignore the case of + suffix. Default: True. + + Returns: + A generator for all the interested files with relative paths. + """ + if isinstance(dir_path, (str, Path)): + dir_path = str(dir_path) + else: + raise TypeError('"dir_path" must be a string or Path object') + + if (suffix is not None) and not isinstance(suffix, (str, tuple)): + raise TypeError('"suffix" must be a string or tuple of strings') + + if suffix is not None and not case_sensitive: + suffix = suffix.lower() if isinstance(suffix, str) else tuple( + item.lower() for item in suffix) + + root = dir_path + + def _scandir(dir_path, suffix, recursive, case_sensitive): + for entry in os.scandir(dir_path): + if not entry.name.startswith('.') and entry.is_file(): + rel_path = osp.relpath(entry.path, root) + _rel_path = rel_path if case_sensitive else rel_path.lower() + if suffix is None or _rel_path.endswith(suffix): + yield rel_path + elif recursive and os.path.isdir(entry.path): + # scan recursively if entry.path is a directory + yield from _scandir(entry.path, suffix, recursive, + case_sensitive) + + return _scandir(dir_path, suffix, recursive, case_sensitive) + + +def find_vcs_root(path, markers=('.git', )): + """Finds the root directory (including itself) of specified markers. + + Args: + path (str): Path of directory or file. + markers (list[str], optional): List of file or directory names. + + Returns: + The directory contained one of the markers or None if not found. + """ + if osp.isfile(path): + path = osp.dirname(path) + + prev, cur = None, osp.abspath(osp.expanduser(path)) + while cur != prev: + if any(osp.exists(osp.join(cur, marker)) for marker in markers): + return cur + prev, cur = cur, osp.split(cur)[0] + return None diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/progressbar.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/progressbar.py new file mode 100644 index 0000000000000000000000000000000000000000..0062f670dd94fa9da559ab26ef85517dcf5211c7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/progressbar.py @@ -0,0 +1,208 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import sys +from collections.abc import Iterable +from multiprocessing import Pool +from shutil import get_terminal_size + +from .timer import Timer + + +class ProgressBar: + """A progress bar which can print the progress.""" + + def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): + self.task_num = task_num + self.bar_width = bar_width + self.completed = 0 + self.file = file + if start: + self.start() + + @property + def terminal_width(self): + width, _ = get_terminal_size() + return width + + def start(self): + if self.task_num > 0: + self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' + 'elapsed: 0s, ETA:') + else: + self.file.write('completed: 0, elapsed: 0s') + self.file.flush() + self.timer = Timer() + + def update(self, num_tasks=1): + assert num_tasks > 0 + self.completed += num_tasks + elapsed = self.timer.since_start() + if elapsed > 0: + fps = self.completed / elapsed + else: + fps = float('inf') + if self.task_num > 0: + percentage = self.completed / float(self.task_num) + eta = int(elapsed * (1 - percentage) / percentage + 0.5) + msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ + f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ + f'ETA: {eta:5}s' + + bar_width = min(self.bar_width, + int(self.terminal_width - len(msg)) + 2, + int(self.terminal_width * 0.6)) + bar_width = max(2, bar_width) + mark_width = int(bar_width * percentage) + bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) + self.file.write(msg.format(bar_chars)) + else: + self.file.write( + f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' + f' {fps:.1f} tasks/s') + self.file.flush() + + +def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): + """Track the progress of tasks execution with a progress bar. + + Tasks are done with a simple for-loop. + + Args: + func (callable): The function to be applied to each task. + tasks (list or tuple[Iterable, int]): A list of tasks or + (tasks, total num). + bar_width (int): Width of progress bar. + + Returns: + list: The task results. + """ + if isinstance(tasks, tuple): + assert len(tasks) == 2 + assert isinstance(tasks[0], Iterable) + assert isinstance(tasks[1], int) + task_num = tasks[1] + tasks = tasks[0] + elif isinstance(tasks, Iterable): + task_num = len(tasks) + else: + raise TypeError( + '"tasks" must be an iterable object or a (iterator, int) tuple') + prog_bar = ProgressBar(task_num, bar_width, file=file) + results = [] + for task in tasks: + results.append(func(task, **kwargs)) + prog_bar.update() + prog_bar.file.write('\n') + return results + + +def init_pool(process_num, initializer=None, initargs=None): + if initializer is None: + return Pool(process_num) + elif initargs is None: + return Pool(process_num, initializer) + else: + if not isinstance(initargs, tuple): + raise TypeError('"initargs" must be a tuple') + return Pool(process_num, initializer, initargs) + + +def track_parallel_progress(func, + tasks, + nproc, + initializer=None, + initargs=None, + bar_width=50, + chunksize=1, + skip_first=False, + keep_order=True, + file=sys.stdout): + """Track the progress of parallel task execution with a progress bar. + + The built-in :mod:`multiprocessing` module is used for process pools and + tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. + + Args: + func (callable): The function to be applied to each task. + tasks (list or tuple[Iterable, int]): A list of tasks or + (tasks, total num). + nproc (int): Process (worker) number. + initializer (None or callable): Refer to :class:`multiprocessing.Pool` + for details. + initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for + details. + chunksize (int): Refer to :class:`multiprocessing.Pool` for details. + bar_width (int): Width of progress bar. + skip_first (bool): Whether to skip the first sample for each worker + when estimating fps, since the initialization step may takes + longer. + keep_order (bool): If True, :func:`Pool.imap` is used, otherwise + :func:`Pool.imap_unordered` is used. + + Returns: + list: The task results. + """ + if isinstance(tasks, tuple): + assert len(tasks) == 2 + assert isinstance(tasks[0], Iterable) + assert isinstance(tasks[1], int) + task_num = tasks[1] + tasks = tasks[0] + elif isinstance(tasks, Iterable): + task_num = len(tasks) + else: + raise TypeError( + '"tasks" must be an iterable object or a (iterator, int) tuple') + pool = init_pool(nproc, initializer, initargs) + start = not skip_first + task_num -= nproc * chunksize * int(skip_first) + prog_bar = ProgressBar(task_num, bar_width, start, file=file) + results = [] + if keep_order: + gen = pool.imap(func, tasks, chunksize) + else: + gen = pool.imap_unordered(func, tasks, chunksize) + for result in gen: + results.append(result) + if skip_first: + if len(results) < nproc * chunksize: + continue + elif len(results) == nproc * chunksize: + prog_bar.start() + continue + prog_bar.update() + prog_bar.file.write('\n') + pool.close() + pool.join() + return results + + +def track_iter_progress(tasks, bar_width=50, file=sys.stdout): + """Track the progress of tasks iteration or enumeration with a progress + bar. + + Tasks are yielded with a simple for-loop. + + Args: + tasks (list or tuple[Iterable, int]): A list of tasks or + (tasks, total num). + bar_width (int): Width of progress bar. + + Yields: + list: The task results. + """ + if isinstance(tasks, tuple): + assert len(tasks) == 2 + assert isinstance(tasks[0], Iterable) + assert isinstance(tasks[1], int) + task_num = tasks[1] + tasks = tasks[0] + elif isinstance(tasks, Iterable): + task_num = len(tasks) + else: + raise TypeError( + '"tasks" must be an iterable object or a (iterator, int) tuple') + prog_bar = ProgressBar(task_num, bar_width, file=file) + for task in tasks: + yield task + prog_bar.update() + prog_bar.file.write('\n') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/registry.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/registry.py new file mode 100644 index 0000000000000000000000000000000000000000..fa9df39bc9f3d8d568361e7250ab35468f2b74e0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/registry.py @@ -0,0 +1,315 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import inspect +import warnings +from functools import partial + +from .misc import is_seq_of + + +def build_from_cfg(cfg, registry, default_args=None): + """Build a module from config dict. + + Args: + cfg (dict): Config dict. It should at least contain the key "type". + registry (:obj:`Registry`): The registry to search the type from. + default_args (dict, optional): Default initialization arguments. + + Returns: + object: The constructed object. + """ + if not isinstance(cfg, dict): + raise TypeError(f'cfg must be a dict, but got {type(cfg)}') + if 'type' not in cfg: + if default_args is None or 'type' not in default_args: + raise KeyError( + '`cfg` or `default_args` must contain the key "type", ' + f'but got {cfg}\n{default_args}') + if not isinstance(registry, Registry): + raise TypeError('registry must be an mmcv.Registry object, ' + f'but got {type(registry)}') + if not (isinstance(default_args, dict) or default_args is None): + raise TypeError('default_args must be a dict or None, ' + f'but got {type(default_args)}') + + args = cfg.copy() + + if default_args is not None: + for name, value in default_args.items(): + args.setdefault(name, value) + + obj_type = args.pop('type') + if isinstance(obj_type, str): + obj_cls = registry.get(obj_type) + if obj_cls is None: + raise KeyError( + f'{obj_type} is not in the {registry.name} registry') + elif inspect.isclass(obj_type): + obj_cls = obj_type + else: + raise TypeError( + f'type must be a str or valid type, but got {type(obj_type)}') + try: + return obj_cls(**args) + except Exception as e: + # Normal TypeError does not print class name. + raise type(e)(f'{obj_cls.__name__}: {e}') + + +class Registry: + """A registry to map strings to classes. + + Registered object could be built from registry. + Example: + >>> MODELS = Registry('models') + >>> @MODELS.register_module() + >>> class ResNet: + >>> pass + >>> resnet = MODELS.build(dict(type='ResNet')) + + Please refer to + https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for + advanced usage. + + Args: + name (str): Registry name. + build_func(func, optional): Build function to construct instance from + Registry, func:`build_from_cfg` is used if neither ``parent`` or + ``build_func`` is specified. If ``parent`` is specified and + ``build_func`` is not given, ``build_func`` will be inherited + from ``parent``. Default: None. + parent (Registry, optional): Parent registry. The class registered in + children registry could be built from parent. Default: None. + scope (str, optional): The scope of registry. It is the key to search + for children registry. If not specified, scope will be the name of + the package where class is defined, e.g. mmdet, mmcls, mmseg. + Default: None. + """ + + def __init__(self, name, build_func=None, parent=None, scope=None): + self._name = name + self._module_dict = dict() + self._children = dict() + self._scope = self.infer_scope() if scope is None else scope + + # self.build_func will be set with the following priority: + # 1. build_func + # 2. parent.build_func + # 3. build_from_cfg + if build_func is None: + if parent is not None: + self.build_func = parent.build_func + else: + self.build_func = build_from_cfg + else: + self.build_func = build_func + if parent is not None: + assert isinstance(parent, Registry) + parent._add_children(self) + self.parent = parent + else: + self.parent = None + + def __len__(self): + return len(self._module_dict) + + def __contains__(self, key): + return self.get(key) is not None + + def __repr__(self): + format_str = self.__class__.__name__ + \ + f'(name={self._name}, ' \ + f'items={self._module_dict})' + return format_str + + @staticmethod + def infer_scope(): + """Infer the scope of registry. + + The name of the package where registry is defined will be returned. + + Example: + # in mmdet/models/backbone/resnet.py + >>> MODELS = Registry('models') + >>> @MODELS.register_module() + >>> class ResNet: + >>> pass + The scope of ``ResNet`` will be ``mmdet``. + + + Returns: + scope (str): The inferred scope name. + """ + # inspect.stack() trace where this function is called, the index-2 + # indicates the frame where `infer_scope()` is called + filename = inspect.getmodule(inspect.stack()[2][0]).__name__ + split_filename = filename.split('.') + return split_filename[0] + + @staticmethod + def split_scope_key(key): + """Split scope and key. + + The first scope will be split from key. + + Examples: + >>> Registry.split_scope_key('mmdet.ResNet') + 'mmdet', 'ResNet' + >>> Registry.split_scope_key('ResNet') + None, 'ResNet' + + Return: + scope (str, None): The first scope. + key (str): The remaining key. + """ + split_index = key.find('.') + if split_index != -1: + return key[:split_index], key[split_index + 1:] + else: + return None, key + + @property + def name(self): + return self._name + + @property + def scope(self): + return self._scope + + @property + def module_dict(self): + return self._module_dict + + @property + def children(self): + return self._children + + def get(self, key): + """Get the registry record. + + Args: + key (str): The class name in string format. + + Returns: + class: The corresponding class. + """ + scope, real_key = self.split_scope_key(key) + if scope is None or scope == self._scope: + # get from self + if real_key in self._module_dict: + return self._module_dict[real_key] + else: + # get from self._children + if scope in self._children: + return self._children[scope].get(real_key) + else: + # goto root + parent = self.parent + while parent.parent is not None: + parent = parent.parent + return parent.get(key) + + def build(self, *args, **kwargs): + return self.build_func(*args, **kwargs, registry=self) + + def _add_children(self, registry): + """Add children for a registry. + + The ``registry`` will be added as children based on its scope. + The parent registry could build objects from children registry. + + Example: + >>> models = Registry('models') + >>> mmdet_models = Registry('models', parent=models) + >>> @mmdet_models.register_module() + >>> class ResNet: + >>> pass + >>> resnet = models.build(dict(type='mmdet.ResNet')) + """ + + assert isinstance(registry, Registry) + assert registry.scope is not None + assert registry.scope not in self.children, \ + f'scope {registry.scope} exists in {self.name} registry' + self.children[registry.scope] = registry + + def _register_module(self, module_class, module_name=None, force=False): + if not inspect.isclass(module_class): + raise TypeError('module must be a class, ' + f'but got {type(module_class)}') + + if module_name is None: + module_name = module_class.__name__ + if isinstance(module_name, str): + module_name = [module_name] + for name in module_name: + if not force and name in self._module_dict: + raise KeyError(f'{name} is already registered ' + f'in {self.name}') + self._module_dict[name] = module_class + + def deprecated_register_module(self, cls=None, force=False): + warnings.warn( + 'The old API of register_module(module, force=False) ' + 'is deprecated and will be removed, please use the new API ' + 'register_module(name=None, force=False, module=None) instead.') + if cls is None: + return partial(self.deprecated_register_module, force=force) + self._register_module(cls, force=force) + return cls + + def register_module(self, name=None, force=False, module=None): + """Register a module. + + A record will be added to `self._module_dict`, whose key is the class + name or the specified name, and value is the class itself. + It can be used as a decorator or a normal function. + + Example: + >>> backbones = Registry('backbone') + >>> @backbones.register_module() + >>> class ResNet: + >>> pass + + >>> backbones = Registry('backbone') + >>> @backbones.register_module(name='mnet') + >>> class MobileNet: + >>> pass + + >>> backbones = Registry('backbone') + >>> class ResNet: + >>> pass + >>> backbones.register_module(ResNet) + + Args: + name (str | None): The module name to be registered. If not + specified, the class name will be used. + force (bool, optional): Whether to override an existing class with + the same name. Default: False. + module (type): Module class to be registered. + """ + if not isinstance(force, bool): + raise TypeError(f'force must be a boolean, but got {type(force)}') + # NOTE: This is a walkaround to be compatible with the old api, + # while it may introduce unexpected bugs. + if isinstance(name, type): + return self.deprecated_register_module(name, force=force) + + # raise the error ahead of time + if not (name is None or isinstance(name, str) or is_seq_of(name, str)): + raise TypeError( + 'name must be either of None, an instance of str or a sequence' + f' of str, but got {type(name)}') + + # use it as a normal method: x.register_module(module=SomeClass) + if module is not None: + self._register_module( + module_class=module, module_name=name, force=force) + return module + + # use it as a decorator: @x.register_module() + def _register(cls): + self._register_module( + module_class=cls, module_name=name, force=force) + return cls + + return _register diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/testing.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/testing.py new file mode 100644 index 0000000000000000000000000000000000000000..a27f936da8ec14bac18562ede0a79d476d82f797 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/testing.py @@ -0,0 +1,140 @@ +# Copyright (c) Open-MMLab. +import sys +from collections.abc import Iterable +from runpy import run_path +from shlex import split +from typing import Any, Dict, List +from unittest.mock import patch + + +def check_python_script(cmd): + """Run the python cmd script with `__main__`. The difference between + `os.system` is that, this function exectues code in the current process, so + that it can be tracked by coverage tools. Currently it supports two forms: + + - ./tests/data/scripts/hello.py zz + - python tests/data/scripts/hello.py zz + """ + args = split(cmd) + if args[0] == 'python': + args = args[1:] + with patch.object(sys, 'argv', args): + run_path(args[0], run_name='__main__') + + +def _any(judge_result): + """Since built-in ``any`` works only when the element of iterable is not + iterable, implement the function.""" + if not isinstance(judge_result, Iterable): + return judge_result + + try: + for element in judge_result: + if _any(element): + return True + except TypeError: + # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) + if judge_result: + return True + return False + + +def assert_dict_contains_subset(dict_obj: Dict[Any, Any], + expected_subset: Dict[Any, Any]) -> bool: + """Check if the dict_obj contains the expected_subset. + + Args: + dict_obj (Dict[Any, Any]): Dict object to be checked. + expected_subset (Dict[Any, Any]): Subset expected to be contained in + dict_obj. + + Returns: + bool: Whether the dict_obj contains the expected_subset. + """ + + for key, value in expected_subset.items(): + if key not in dict_obj.keys() or _any(dict_obj[key] != value): + return False + return True + + +def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: + """Check if attribute of class object is correct. + + Args: + obj (object): Class object to be checked. + expected_attrs (Dict[str, Any]): Dict of the expected attrs. + + Returns: + bool: Whether the attribute of class object is correct. + """ + for attr, value in expected_attrs.items(): + if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): + return False + return True + + +def assert_dict_has_keys(obj: Dict[str, Any], + expected_keys: List[str]) -> bool: + """Check if the obj has all the expected_keys. + + Args: + obj (Dict[str, Any]): Object to be checked. + expected_keys (List[str]): Keys expected to contained in the keys of + the obj. + + Returns: + bool: Whether the obj has the expected keys. + """ + return set(expected_keys).issubset(set(obj.keys())) + + +def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: + """Check if target_keys is equal to result_keys. + + Args: + result_keys (List[str]): Result keys to be checked. + target_keys (List[str]): Target keys to be checked. + + Returns: + bool: Whether target_keys is equal to result_keys. + """ + return set(result_keys) == set(target_keys) + + +def assert_is_norm_layer(module) -> bool: + """Check if the module is a norm layer. + + Args: + module (nn.Module): The module to be checked. + + Returns: + bool: Whether the module is a norm layer. + """ + from .parrots_wrapper import _BatchNorm, _InstanceNorm + from torch.nn import GroupNorm, LayerNorm + norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) + return isinstance(module, norm_layer_candidates) + + +def assert_params_all_zeros(module) -> bool: + """Check if the parameters of the module is all zeros. + + Args: + module (nn.Module): The module to be checked. + + Returns: + bool: Whether the parameters of the module is all zeros. + """ + weight_data = module.weight.data + is_weight_zero = weight_data.allclose( + weight_data.new_zeros(weight_data.size())) + + if hasattr(module, 'bias') and module.bias is not None: + bias_data = module.bias.data + is_bias_zero = bias_data.allclose( + bias_data.new_zeros(bias_data.size())) + else: + is_bias_zero = True + + return is_weight_zero and is_bias_zero diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/timer.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/timer.py new file mode 100644 index 0000000000000000000000000000000000000000..0435c1250ebb63e0d881d7022979a76b2dcc7298 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/timer.py @@ -0,0 +1,118 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from time import time + + +class TimerError(Exception): + + def __init__(self, message): + self.message = message + super(TimerError, self).__init__(message) + + +class Timer: + """A flexible Timer class. + + :Example: + + >>> import time + >>> import annotator.mmpkg.mmcv as mmcv + >>> with mmcv.Timer(): + >>> # simulate a code block that will run for 1s + >>> time.sleep(1) + 1.000 + >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): + >>> # simulate a code block that will run for 1s + >>> time.sleep(1) + it takes 1.0 seconds + >>> timer = mmcv.Timer() + >>> time.sleep(0.5) + >>> print(timer.since_start()) + 0.500 + >>> time.sleep(0.5) + >>> print(timer.since_last_check()) + 0.500 + >>> print(timer.since_start()) + 1.000 + """ + + def __init__(self, start=True, print_tmpl=None): + self._is_running = False + self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' + if start: + self.start() + + @property + def is_running(self): + """bool: indicate whether the timer is running""" + return self._is_running + + def __enter__(self): + self.start() + return self + + def __exit__(self, type, value, traceback): + print(self.print_tmpl.format(self.since_last_check())) + self._is_running = False + + def start(self): + """Start the timer.""" + if not self._is_running: + self._t_start = time() + self._is_running = True + self._t_last = time() + + def since_start(self): + """Total time since the timer is started. + + Returns (float): Time in seconds. + """ + if not self._is_running: + raise TimerError('timer is not running') + self._t_last = time() + return self._t_last - self._t_start + + def since_last_check(self): + """Time since the last checking. + + Either :func:`since_start` or :func:`since_last_check` is a checking + operation. + + Returns (float): Time in seconds. + """ + if not self._is_running: + raise TimerError('timer is not running') + dur = time() - self._t_last + self._t_last = time() + return dur + + +_g_timers = {} # global timers + + +def check_time(timer_id): + """Add check points in a single line. + + This method is suitable for running a task on a list of items. A timer will + be registered when the method is called for the first time. + + :Example: + + >>> import time + >>> import annotator.mmpkg.mmcv as mmcv + >>> for i in range(1, 6): + >>> # simulate a code block + >>> time.sleep(i) + >>> mmcv.check_time('task1') + 2.000 + 3.000 + 4.000 + 5.000 + + Args: + timer_id (str): Timer identifier. + """ + if timer_id not in _g_timers: + _g_timers[timer_id] = Timer() + return 0 + else: + return _g_timers[timer_id].since_last_check() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/trace.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/trace.py new file mode 100644 index 0000000000000000000000000000000000000000..51f6e3cab4ac7bbdf561583d7463a5f2897960e7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/trace.py @@ -0,0 +1,23 @@ +import warnings + +import torch + +from annotator.mmpkg.mmcv.utils import digit_version + + +def is_jit_tracing() -> bool: + if (torch.__version__ != 'parrots' + and digit_version(torch.__version__) >= digit_version('1.6.0')): + on_trace = torch.jit.is_tracing() + # In PyTorch 1.6, torch.jit.is_tracing has a bug. + # Refers to https://github.com/pytorch/pytorch/issues/42448 + if isinstance(on_trace, bool): + return on_trace + else: + return torch._C._is_tracing() + else: + warnings.warn( + 'torch.jit.is_tracing is only supported after v1.6.0. ' + 'Therefore is_tracing returns False automatically. Please ' + 'set on_trace manually if you are using trace.', UserWarning) + return False diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/version_utils.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/version_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..963c45a2e8a86a88413ab6c18c22481fb9831985 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/utils/version_utils.py @@ -0,0 +1,90 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os +import subprocess +import warnings + +from packaging.version import parse + + +def digit_version(version_str: str, length: int = 4): + """Convert a version string into a tuple of integers. + + This method is usually used for comparing two versions. For pre-release + versions: alpha < beta < rc. + + Args: + version_str (str): The version string. + length (int): The maximum number of version levels. Default: 4. + + Returns: + tuple[int]: The version info in digits (integers). + """ + assert 'parrots' not in version_str + version = parse(version_str) + assert version.release, f'failed to parse version {version_str}' + release = list(version.release) + release = release[:length] + if len(release) < length: + release = release + [0] * (length - len(release)) + if version.is_prerelease: + mapping = {'a': -3, 'b': -2, 'rc': -1} + val = -4 + # version.pre can be None + if version.pre: + if version.pre[0] not in mapping: + warnings.warn(f'unknown prerelease version {version.pre[0]}, ' + 'version checking may go wrong') + else: + val = mapping[version.pre[0]] + release.extend([val, version.pre[-1]]) + else: + release.extend([val, 0]) + + elif version.is_postrelease: + release.extend([1, version.post]) + else: + release.extend([0, 0]) + return tuple(release) + + +def _minimal_ext_cmd(cmd): + # construct minimal environment + env = {} + for k in ['SYSTEMROOT', 'PATH', 'HOME']: + v = os.environ.get(k) + if v is not None: + env[k] = v + # LANGUAGE is used on win32 + env['LANGUAGE'] = 'C' + env['LANG'] = 'C' + env['LC_ALL'] = 'C' + out = subprocess.Popen( + cmd, stdout=subprocess.PIPE, env=env).communicate()[0] + return out + + +def get_git_hash(fallback='unknown', digits=None): + """Get the git hash of the current repo. + + Args: + fallback (str, optional): The fallback string when git hash is + unavailable. Defaults to 'unknown'. + digits (int, optional): kept digits of the hash. Defaults to None, + meaning all digits are kept. + + Returns: + str: Git commit hash. + """ + + if digits is not None and not isinstance(digits, int): + raise TypeError('digits must be None or an integer') + + try: + out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) + sha = out.strip().decode('ascii') + if digits is not None: + sha = sha[:digits] + except OSError: + sha = fallback + + return sha diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/version.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/version.py new file mode 100644 index 0000000000000000000000000000000000000000..1cce4e50bd692d4002e3cac3c545a3fb2efe95d0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/version.py @@ -0,0 +1,35 @@ +# Copyright (c) OpenMMLab. All rights reserved. +__version__ = '1.3.17' + + +def parse_version_info(version_str: str, length: int = 4) -> tuple: + """Parse a version string into a tuple. + + Args: + version_str (str): The version string. + length (int): The maximum number of version levels. Default: 4. + + Returns: + tuple[int | str]: The version info, e.g., "1.3.0" is parsed into + (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into + (2, 0, 0, 0, 'rc', 1) (when length is set to 4). + """ + from packaging.version import parse + version = parse(version_str) + assert version.release, f'failed to parse version {version_str}' + release = list(version.release) + release = release[:length] + if len(release) < length: + release = release + [0] * (length - len(release)) + if version.is_prerelease: + release.extend(list(version.pre)) + elif version.is_postrelease: + release.extend(list(version.post)) + else: + release.extend([0, 0]) + return tuple(release) + + +version_info = tuple(int(x) for x in __version__.split('.')[:3]) + +__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/video/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/video/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..73199b01dec52820dc6ca0139903536344d5a1eb --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/video/__init__.py @@ -0,0 +1,11 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .io import Cache, VideoReader, frames2video +from .optflow import (dequantize_flow, flow_from_bytes, flow_warp, flowread, + flowwrite, quantize_flow, sparse_flow_from_bytes) +from .processing import concat_video, convert_video, cut_video, resize_video + +__all__ = [ + 'Cache', 'VideoReader', 'frames2video', 'convert_video', 'resize_video', + 'cut_video', 'concat_video', 'flowread', 'flowwrite', 'quantize_flow', + 'dequantize_flow', 'flow_warp', 'flow_from_bytes', 'sparse_flow_from_bytes' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/video/io.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/video/io.py new file mode 100644 index 0000000000000000000000000000000000000000..06ae9b8ae4404ec7822fd49c01c183a0be0cbf35 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/video/io.py @@ -0,0 +1,318 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os.path as osp +from collections import OrderedDict + +import cv2 +from cv2 import (CAP_PROP_FOURCC, CAP_PROP_FPS, CAP_PROP_FRAME_COUNT, + CAP_PROP_FRAME_HEIGHT, CAP_PROP_FRAME_WIDTH, + CAP_PROP_POS_FRAMES, VideoWriter_fourcc) + +from annotator.mmpkg.mmcv.utils import (check_file_exist, mkdir_or_exist, scandir, + track_progress) + + +class Cache: + + def __init__(self, capacity): + self._cache = OrderedDict() + self._capacity = int(capacity) + if capacity <= 0: + raise ValueError('capacity must be a positive integer') + + @property + def capacity(self): + return self._capacity + + @property + def size(self): + return len(self._cache) + + def put(self, key, val): + if key in self._cache: + return + if len(self._cache) >= self.capacity: + self._cache.popitem(last=False) + self._cache[key] = val + + def get(self, key, default=None): + val = self._cache[key] if key in self._cache else default + return val + + +class VideoReader: + """Video class with similar usage to a list object. + + This video warpper class provides convenient apis to access frames. + There exists an issue of OpenCV's VideoCapture class that jumping to a + certain frame may be inaccurate. It is fixed in this class by checking + the position after jumping each time. + Cache is used when decoding videos. So if the same frame is visited for + the second time, there is no need to decode again if it is stored in the + cache. + + :Example: + + >>> import annotator.mmpkg.mmcv as mmcv + >>> v = mmcv.VideoReader('sample.mp4') + >>> len(v) # get the total frame number with `len()` + 120 + >>> for img in v: # v is iterable + >>> mmcv.imshow(img) + >>> v[5] # get the 6th frame + """ + + def __init__(self, filename, cache_capacity=10): + # Check whether the video path is a url + if not filename.startswith(('https://', 'http://')): + check_file_exist(filename, 'Video file not found: ' + filename) + self._vcap = cv2.VideoCapture(filename) + assert cache_capacity > 0 + self._cache = Cache(cache_capacity) + self._position = 0 + # get basic info + self._width = int(self._vcap.get(CAP_PROP_FRAME_WIDTH)) + self._height = int(self._vcap.get(CAP_PROP_FRAME_HEIGHT)) + self._fps = self._vcap.get(CAP_PROP_FPS) + self._frame_cnt = int(self._vcap.get(CAP_PROP_FRAME_COUNT)) + self._fourcc = self._vcap.get(CAP_PROP_FOURCC) + + @property + def vcap(self): + """:obj:`cv2.VideoCapture`: The raw VideoCapture object.""" + return self._vcap + + @property + def opened(self): + """bool: Indicate whether the video is opened.""" + return self._vcap.isOpened() + + @property + def width(self): + """int: Width of video frames.""" + return self._width + + @property + def height(self): + """int: Height of video frames.""" + return self._height + + @property + def resolution(self): + """tuple: Video resolution (width, height).""" + return (self._width, self._height) + + @property + def fps(self): + """float: FPS of the video.""" + return self._fps + + @property + def frame_cnt(self): + """int: Total frames of the video.""" + return self._frame_cnt + + @property + def fourcc(self): + """str: "Four character code" of the video.""" + return self._fourcc + + @property + def position(self): + """int: Current cursor position, indicating frame decoded.""" + return self._position + + def _get_real_position(self): + return int(round(self._vcap.get(CAP_PROP_POS_FRAMES))) + + def _set_real_position(self, frame_id): + self._vcap.set(CAP_PROP_POS_FRAMES, frame_id) + pos = self._get_real_position() + for _ in range(frame_id - pos): + self._vcap.read() + self._position = frame_id + + def read(self): + """Read the next frame. + + If the next frame have been decoded before and in the cache, then + return it directly, otherwise decode, cache and return it. + + Returns: + ndarray or None: Return the frame if successful, otherwise None. + """ + # pos = self._position + if self._cache: + img = self._cache.get(self._position) + if img is not None: + ret = True + else: + if self._position != self._get_real_position(): + self._set_real_position(self._position) + ret, img = self._vcap.read() + if ret: + self._cache.put(self._position, img) + else: + ret, img = self._vcap.read() + if ret: + self._position += 1 + return img + + def get_frame(self, frame_id): + """Get frame by index. + + Args: + frame_id (int): Index of the expected frame, 0-based. + + Returns: + ndarray or None: Return the frame if successful, otherwise None. + """ + if frame_id < 0 or frame_id >= self._frame_cnt: + raise IndexError( + f'"frame_id" must be between 0 and {self._frame_cnt - 1}') + if frame_id == self._position: + return self.read() + if self._cache: + img = self._cache.get(frame_id) + if img is not None: + self._position = frame_id + 1 + return img + self._set_real_position(frame_id) + ret, img = self._vcap.read() + if ret: + if self._cache: + self._cache.put(self._position, img) + self._position += 1 + return img + + def current_frame(self): + """Get the current frame (frame that is just visited). + + Returns: + ndarray or None: If the video is fresh, return None, otherwise + return the frame. + """ + if self._position == 0: + return None + return self._cache.get(self._position - 1) + + def cvt2frames(self, + frame_dir, + file_start=0, + filename_tmpl='{:06d}.jpg', + start=0, + max_num=0, + show_progress=True): + """Convert a video to frame images. + + Args: + frame_dir (str): Output directory to store all the frame images. + file_start (int): Filenames will start from the specified number. + filename_tmpl (str): Filename template with the index as the + placeholder. + start (int): The starting frame index. + max_num (int): Maximum number of frames to be written. + show_progress (bool): Whether to show a progress bar. + """ + mkdir_or_exist(frame_dir) + if max_num == 0: + task_num = self.frame_cnt - start + else: + task_num = min(self.frame_cnt - start, max_num) + if task_num <= 0: + raise ValueError('start must be less than total frame number') + if start > 0: + self._set_real_position(start) + + def write_frame(file_idx): + img = self.read() + if img is None: + return + filename = osp.join(frame_dir, filename_tmpl.format(file_idx)) + cv2.imwrite(filename, img) + + if show_progress: + track_progress(write_frame, range(file_start, + file_start + task_num)) + else: + for i in range(task_num): + write_frame(file_start + i) + + def __len__(self): + return self.frame_cnt + + def __getitem__(self, index): + if isinstance(index, slice): + return [ + self.get_frame(i) + for i in range(*index.indices(self.frame_cnt)) + ] + # support negative indexing + if index < 0: + index += self.frame_cnt + if index < 0: + raise IndexError('index out of range') + return self.get_frame(index) + + def __iter__(self): + self._set_real_position(0) + return self + + def __next__(self): + img = self.read() + if img is not None: + return img + else: + raise StopIteration + + next = __next__ + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self._vcap.release() + + +def frames2video(frame_dir, + video_file, + fps=30, + fourcc='XVID', + filename_tmpl='{:06d}.jpg', + start=0, + end=0, + show_progress=True): + """Read the frame images from a directory and join them as a video. + + Args: + frame_dir (str): The directory containing video frames. + video_file (str): Output filename. + fps (float): FPS of the output video. + fourcc (str): Fourcc of the output video, this should be compatible + with the output file type. + filename_tmpl (str): Filename template with the index as the variable. + start (int): Starting frame index. + end (int): Ending frame index. + show_progress (bool): Whether to show a progress bar. + """ + if end == 0: + ext = filename_tmpl.split('.')[-1] + end = len([name for name in scandir(frame_dir, ext)]) + first_file = osp.join(frame_dir, filename_tmpl.format(start)) + check_file_exist(first_file, 'The start frame not found: ' + first_file) + img = cv2.imread(first_file) + height, width = img.shape[:2] + resolution = (width, height) + vwriter = cv2.VideoWriter(video_file, VideoWriter_fourcc(*fourcc), fps, + resolution) + + def write_frame(file_idx): + filename = osp.join(frame_dir, filename_tmpl.format(file_idx)) + img = cv2.imread(filename) + vwriter.write(img) + + if show_progress: + track_progress(write_frame, range(start, end)) + else: + for i in range(start, end): + write_frame(i) + vwriter.release() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/video/optflow.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/video/optflow.py new file mode 100644 index 0000000000000000000000000000000000000000..7bd78970dce8faf30bce0d5f2ec278b994fdd623 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/video/optflow.py @@ -0,0 +1,254 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import warnings + +import cv2 +import numpy as np + +from annotator.mmpkg.mmcv.arraymisc import dequantize, quantize +from annotator.mmpkg.mmcv.image import imread, imwrite +from annotator.mmpkg.mmcv.utils import is_str + + +def flowread(flow_or_path, quantize=False, concat_axis=0, *args, **kwargs): + """Read an optical flow map. + + Args: + flow_or_path (ndarray or str): A flow map or filepath. + quantize (bool): whether to read quantized pair, if set to True, + remaining args will be passed to :func:`dequantize_flow`. + concat_axis (int): The axis that dx and dy are concatenated, + can be either 0 or 1. Ignored if quantize is False. + + Returns: + ndarray: Optical flow represented as a (h, w, 2) numpy array + """ + if isinstance(flow_or_path, np.ndarray): + if (flow_or_path.ndim != 3) or (flow_or_path.shape[-1] != 2): + raise ValueError(f'Invalid flow with shape {flow_or_path.shape}') + return flow_or_path + elif not is_str(flow_or_path): + raise TypeError(f'"flow_or_path" must be a filename or numpy array, ' + f'not {type(flow_or_path)}') + + if not quantize: + with open(flow_or_path, 'rb') as f: + try: + header = f.read(4).decode('utf-8') + except Exception: + raise IOError(f'Invalid flow file: {flow_or_path}') + else: + if header != 'PIEH': + raise IOError(f'Invalid flow file: {flow_or_path}, ' + 'header does not contain PIEH') + + w = np.fromfile(f, np.int32, 1).squeeze() + h = np.fromfile(f, np.int32, 1).squeeze() + flow = np.fromfile(f, np.float32, w * h * 2).reshape((h, w, 2)) + else: + assert concat_axis in [0, 1] + cat_flow = imread(flow_or_path, flag='unchanged') + if cat_flow.ndim != 2: + raise IOError( + f'{flow_or_path} is not a valid quantized flow file, ' + f'its dimension is {cat_flow.ndim}.') + assert cat_flow.shape[concat_axis] % 2 == 0 + dx, dy = np.split(cat_flow, 2, axis=concat_axis) + flow = dequantize_flow(dx, dy, *args, **kwargs) + + return flow.astype(np.float32) + + +def flowwrite(flow, filename, quantize=False, concat_axis=0, *args, **kwargs): + """Write optical flow to file. + + If the flow is not quantized, it will be saved as a .flo file losslessly, + otherwise a jpeg image which is lossy but of much smaller size. (dx and dy + will be concatenated horizontally into a single image if quantize is True.) + + Args: + flow (ndarray): (h, w, 2) array of optical flow. + filename (str): Output filepath. + quantize (bool): Whether to quantize the flow and save it to 2 jpeg + images. If set to True, remaining args will be passed to + :func:`quantize_flow`. + concat_axis (int): The axis that dx and dy are concatenated, + can be either 0 or 1. Ignored if quantize is False. + """ + if not quantize: + with open(filename, 'wb') as f: + f.write('PIEH'.encode('utf-8')) + np.array([flow.shape[1], flow.shape[0]], dtype=np.int32).tofile(f) + flow = flow.astype(np.float32) + flow.tofile(f) + f.flush() + else: + assert concat_axis in [0, 1] + dx, dy = quantize_flow(flow, *args, **kwargs) + dxdy = np.concatenate((dx, dy), axis=concat_axis) + imwrite(dxdy, filename) + + +def quantize_flow(flow, max_val=0.02, norm=True): + """Quantize flow to [0, 255]. + + After this step, the size of flow will be much smaller, and can be + dumped as jpeg images. + + Args: + flow (ndarray): (h, w, 2) array of optical flow. + max_val (float): Maximum value of flow, values beyond + [-max_val, max_val] will be truncated. + norm (bool): Whether to divide flow values by image width/height. + + Returns: + tuple[ndarray]: Quantized dx and dy. + """ + h, w, _ = flow.shape + dx = flow[..., 0] + dy = flow[..., 1] + if norm: + dx = dx / w # avoid inplace operations + dy = dy / h + # use 255 levels instead of 256 to make sure 0 is 0 after dequantization. + flow_comps = [ + quantize(d, -max_val, max_val, 255, np.uint8) for d in [dx, dy] + ] + return tuple(flow_comps) + + +def dequantize_flow(dx, dy, max_val=0.02, denorm=True): + """Recover from quantized flow. + + Args: + dx (ndarray): Quantized dx. + dy (ndarray): Quantized dy. + max_val (float): Maximum value used when quantizing. + denorm (bool): Whether to multiply flow values with width/height. + + Returns: + ndarray: Dequantized flow. + """ + assert dx.shape == dy.shape + assert dx.ndim == 2 or (dx.ndim == 3 and dx.shape[-1] == 1) + + dx, dy = [dequantize(d, -max_val, max_val, 255) for d in [dx, dy]] + + if denorm: + dx *= dx.shape[1] + dy *= dx.shape[0] + flow = np.dstack((dx, dy)) + return flow + + +def flow_warp(img, flow, filling_value=0, interpolate_mode='nearest'): + """Use flow to warp img. + + Args: + img (ndarray, float or uint8): Image to be warped. + flow (ndarray, float): Optical Flow. + filling_value (int): The missing pixels will be set with filling_value. + interpolate_mode (str): bilinear -> Bilinear Interpolation; + nearest -> Nearest Neighbor. + + Returns: + ndarray: Warped image with the same shape of img + """ + warnings.warn('This function is just for prototyping and cannot ' + 'guarantee the computational efficiency.') + assert flow.ndim == 3, 'Flow must be in 3D arrays.' + height = flow.shape[0] + width = flow.shape[1] + channels = img.shape[2] + + output = np.ones( + (height, width, channels), dtype=img.dtype) * filling_value + + grid = np.indices((height, width)).swapaxes(0, 1).swapaxes(1, 2) + dx = grid[:, :, 0] + flow[:, :, 1] + dy = grid[:, :, 1] + flow[:, :, 0] + sx = np.floor(dx).astype(int) + sy = np.floor(dy).astype(int) + valid = (sx >= 0) & (sx < height - 1) & (sy >= 0) & (sy < width - 1) + + if interpolate_mode == 'nearest': + output[valid, :] = img[dx[valid].round().astype(int), + dy[valid].round().astype(int), :] + elif interpolate_mode == 'bilinear': + # dirty walkround for integer positions + eps_ = 1e-6 + dx, dy = dx + eps_, dy + eps_ + left_top_ = img[np.floor(dx[valid]).astype(int), + np.floor(dy[valid]).astype(int), :] * ( + np.ceil(dx[valid]) - dx[valid])[:, None] * ( + np.ceil(dy[valid]) - dy[valid])[:, None] + left_down_ = img[np.ceil(dx[valid]).astype(int), + np.floor(dy[valid]).astype(int), :] * ( + dx[valid] - np.floor(dx[valid]))[:, None] * ( + np.ceil(dy[valid]) - dy[valid])[:, None] + right_top_ = img[np.floor(dx[valid]).astype(int), + np.ceil(dy[valid]).astype(int), :] * ( + np.ceil(dx[valid]) - dx[valid])[:, None] * ( + dy[valid] - np.floor(dy[valid]))[:, None] + right_down_ = img[np.ceil(dx[valid]).astype(int), + np.ceil(dy[valid]).astype(int), :] * ( + dx[valid] - np.floor(dx[valid]))[:, None] * ( + dy[valid] - np.floor(dy[valid]))[:, None] + output[valid, :] = left_top_ + left_down_ + right_top_ + right_down_ + else: + raise NotImplementedError( + 'We only support interpolation modes of nearest and bilinear, ' + f'but got {interpolate_mode}.') + return output.astype(img.dtype) + + +def flow_from_bytes(content): + """Read dense optical flow from bytes. + + .. note:: + This load optical flow function works for FlyingChairs, FlyingThings3D, + Sintel, FlyingChairsOcc datasets, but cannot load the data from + ChairsSDHom. + + Args: + content (bytes): Optical flow bytes got from files or other streams. + + Returns: + ndarray: Loaded optical flow with the shape (H, W, 2). + """ + + # header in first 4 bytes + header = content[:4] + if header.decode('utf-8') != 'PIEH': + raise Exception('Flow file header does not contain PIEH') + # width in second 4 bytes + width = np.frombuffer(content[4:], np.int32, 1).squeeze() + # height in third 4 bytes + height = np.frombuffer(content[8:], np.int32, 1).squeeze() + # after first 12 bytes, all bytes are flow + flow = np.frombuffer(content[12:], np.float32, width * height * 2).reshape( + (height, width, 2)) + + return flow + + +def sparse_flow_from_bytes(content): + """Read the optical flow in KITTI datasets from bytes. + + This function is modified from RAFT load the `KITTI datasets + `_. + + Args: + content (bytes): Optical flow bytes got from files or other streams. + + Returns: + Tuple(ndarray, ndarray): Loaded optical flow with the shape (H, W, 2) + and flow valid mask with the shape (H, W). + """ # nopa + + content = np.frombuffer(content, np.uint8) + flow = cv2.imdecode(content, cv2.IMREAD_ANYDEPTH | cv2.IMREAD_COLOR) + flow = flow[:, :, ::-1].astype(np.float32) + # flow shape (H, W, 2) valid shape (H, W) + flow, valid = flow[:, :, :2], flow[:, :, 2] + flow = (flow - 2**15) / 64.0 + return flow, valid diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/video/processing.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/video/processing.py new file mode 100644 index 0000000000000000000000000000000000000000..2b93a59215d56b6e5ba05f48bca3527772f0c744 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/video/processing.py @@ -0,0 +1,160 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os +import os.path as osp +import subprocess +import tempfile + +from annotator.mmpkg.mmcv.utils import requires_executable + + +@requires_executable('ffmpeg') +def convert_video(in_file, + out_file, + print_cmd=False, + pre_options='', + **kwargs): + """Convert a video with ffmpeg. + + This provides a general api to ffmpeg, the executed command is:: + + `ffmpeg -y -i ` + + Options(kwargs) are mapped to ffmpeg commands with the following rules: + + - key=val: "-key val" + - key=True: "-key" + - key=False: "" + + Args: + in_file (str): Input video filename. + out_file (str): Output video filename. + pre_options (str): Options appears before "-i ". + print_cmd (bool): Whether to print the final ffmpeg command. + """ + options = [] + for k, v in kwargs.items(): + if isinstance(v, bool): + if v: + options.append(f'-{k}') + elif k == 'log_level': + assert v in [ + 'quiet', 'panic', 'fatal', 'error', 'warning', 'info', + 'verbose', 'debug', 'trace' + ] + options.append(f'-loglevel {v}') + else: + options.append(f'-{k} {v}') + cmd = f'ffmpeg -y {pre_options} -i {in_file} {" ".join(options)} ' \ + f'{out_file}' + if print_cmd: + print(cmd) + subprocess.call(cmd, shell=True) + + +@requires_executable('ffmpeg') +def resize_video(in_file, + out_file, + size=None, + ratio=None, + keep_ar=False, + log_level='info', + print_cmd=False): + """Resize a video. + + Args: + in_file (str): Input video filename. + out_file (str): Output video filename. + size (tuple): Expected size (w, h), eg, (320, 240) or (320, -1). + ratio (tuple or float): Expected resize ratio, (2, 0.5) means + (w*2, h*0.5). + keep_ar (bool): Whether to keep original aspect ratio. + log_level (str): Logging level of ffmpeg. + print_cmd (bool): Whether to print the final ffmpeg command. + """ + if size is None and ratio is None: + raise ValueError('expected size or ratio must be specified') + if size is not None and ratio is not None: + raise ValueError('size and ratio cannot be specified at the same time') + options = {'log_level': log_level} + if size: + if not keep_ar: + options['vf'] = f'scale={size[0]}:{size[1]}' + else: + options['vf'] = f'scale=w={size[0]}:h={size[1]}:' \ + 'force_original_aspect_ratio=decrease' + else: + if not isinstance(ratio, tuple): + ratio = (ratio, ratio) + options['vf'] = f'scale="trunc(iw*{ratio[0]}):trunc(ih*{ratio[1]})"' + convert_video(in_file, out_file, print_cmd, **options) + + +@requires_executable('ffmpeg') +def cut_video(in_file, + out_file, + start=None, + end=None, + vcodec=None, + acodec=None, + log_level='info', + print_cmd=False): + """Cut a clip from a video. + + Args: + in_file (str): Input video filename. + out_file (str): Output video filename. + start (None or float): Start time (in seconds). + end (None or float): End time (in seconds). + vcodec (None or str): Output video codec, None for unchanged. + acodec (None or str): Output audio codec, None for unchanged. + log_level (str): Logging level of ffmpeg. + print_cmd (bool): Whether to print the final ffmpeg command. + """ + options = {'log_level': log_level} + if vcodec is None: + options['vcodec'] = 'copy' + if acodec is None: + options['acodec'] = 'copy' + if start: + options['ss'] = start + else: + start = 0 + if end: + options['t'] = end - start + convert_video(in_file, out_file, print_cmd, **options) + + +@requires_executable('ffmpeg') +def concat_video(video_list, + out_file, + vcodec=None, + acodec=None, + log_level='info', + print_cmd=False): + """Concatenate multiple videos into a single one. + + Args: + video_list (list): A list of video filenames + out_file (str): Output video filename + vcodec (None or str): Output video codec, None for unchanged + acodec (None or str): Output audio codec, None for unchanged + log_level (str): Logging level of ffmpeg. + print_cmd (bool): Whether to print the final ffmpeg command. + """ + tmp_filehandler, tmp_filename = tempfile.mkstemp(suffix='.txt', text=True) + with open(tmp_filename, 'w') as f: + for filename in video_list: + f.write(f'file {osp.abspath(filename)}\n') + options = {'log_level': log_level} + if vcodec is None: + options['vcodec'] = 'copy' + if acodec is None: + options['acodec'] = 'copy' + convert_video( + tmp_filename, + out_file, + print_cmd, + pre_options='-f concat -safe 0', + **options) + os.close(tmp_filehandler) + os.remove(tmp_filename) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/visualization/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/visualization/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..835df136bdcf69348281d22914d41aa84cdf92b1 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/visualization/__init__.py @@ -0,0 +1,9 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .color import Color, color_val +from .image import imshow, imshow_bboxes, imshow_det_bboxes +from .optflow import flow2rgb, flowshow, make_color_wheel + +__all__ = [ + 'Color', 'color_val', 'imshow', 'imshow_bboxes', 'imshow_det_bboxes', + 'flowshow', 'flow2rgb', 'make_color_wheel' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/visualization/color.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/visualization/color.py new file mode 100644 index 0000000000000000000000000000000000000000..48379a283e48570f226426510270de8e15323c8d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/visualization/color.py @@ -0,0 +1,51 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from enum import Enum + +import numpy as np + +from annotator.mmpkg.mmcv.utils import is_str + + +class Color(Enum): + """An enum that defines common colors. + + Contains red, green, blue, cyan, yellow, magenta, white and black. + """ + red = (0, 0, 255) + green = (0, 255, 0) + blue = (255, 0, 0) + cyan = (255, 255, 0) + yellow = (0, 255, 255) + magenta = (255, 0, 255) + white = (255, 255, 255) + black = (0, 0, 0) + + +def color_val(color): + """Convert various input to color tuples. + + Args: + color (:obj:`Color`/str/tuple/int/ndarray): Color inputs + + Returns: + tuple[int]: A tuple of 3 integers indicating BGR channels. + """ + if is_str(color): + return Color[color].value + elif isinstance(color, Color): + return color.value + elif isinstance(color, tuple): + assert len(color) == 3 + for channel in color: + assert 0 <= channel <= 255 + return color + elif isinstance(color, int): + assert 0 <= color <= 255 + return color, color, color + elif isinstance(color, np.ndarray): + assert color.ndim == 1 and color.size == 3 + assert np.all((color >= 0) & (color <= 255)) + color = color.astype(np.uint8) + return tuple(color) + else: + raise TypeError(f'Invalid type for color: {type(color)}') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/visualization/image.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/visualization/image.py new file mode 100644 index 0000000000000000000000000000000000000000..378de2104f6554389fcb2e6a3904283345fd74b0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/visualization/image.py @@ -0,0 +1,152 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import cv2 +import numpy as np + +from annotator.mmpkg.mmcv.image import imread, imwrite +from .color import color_val + + +def imshow(img, win_name='', wait_time=0): + """Show an image. + + Args: + img (str or ndarray): The image to be displayed. + win_name (str): The window name. + wait_time (int): Value of waitKey param. + """ + cv2.imshow(win_name, imread(img)) + if wait_time == 0: # prevent from hanging if windows was closed + while True: + ret = cv2.waitKey(1) + + closed = cv2.getWindowProperty(win_name, cv2.WND_PROP_VISIBLE) < 1 + # if user closed window or if some key pressed + if closed or ret != -1: + break + else: + ret = cv2.waitKey(wait_time) + + +def imshow_bboxes(img, + bboxes, + colors='green', + top_k=-1, + thickness=1, + show=True, + win_name='', + wait_time=0, + out_file=None): + """Draw bboxes on an image. + + Args: + img (str or ndarray): The image to be displayed. + bboxes (list or ndarray): A list of ndarray of shape (k, 4). + colors (list[str or tuple or Color]): A list of colors. + top_k (int): Plot the first k bboxes only if set positive. + thickness (int): Thickness of lines. + show (bool): Whether to show the image. + win_name (str): The window name. + wait_time (int): Value of waitKey param. + out_file (str, optional): The filename to write the image. + + Returns: + ndarray: The image with bboxes drawn on it. + """ + img = imread(img) + img = np.ascontiguousarray(img) + + if isinstance(bboxes, np.ndarray): + bboxes = [bboxes] + if not isinstance(colors, list): + colors = [colors for _ in range(len(bboxes))] + colors = [color_val(c) for c in colors] + assert len(bboxes) == len(colors) + + for i, _bboxes in enumerate(bboxes): + _bboxes = _bboxes.astype(np.int32) + if top_k <= 0: + _top_k = _bboxes.shape[0] + else: + _top_k = min(top_k, _bboxes.shape[0]) + for j in range(_top_k): + left_top = (_bboxes[j, 0], _bboxes[j, 1]) + right_bottom = (_bboxes[j, 2], _bboxes[j, 3]) + cv2.rectangle( + img, left_top, right_bottom, colors[i], thickness=thickness) + + if show: + imshow(img, win_name, wait_time) + if out_file is not None: + imwrite(img, out_file) + return img + + +def imshow_det_bboxes(img, + bboxes, + labels, + class_names=None, + score_thr=0, + bbox_color='green', + text_color='green', + thickness=1, + font_scale=0.5, + show=True, + win_name='', + wait_time=0, + out_file=None): + """Draw bboxes and class labels (with scores) on an image. + + Args: + img (str or ndarray): The image to be displayed. + bboxes (ndarray): Bounding boxes (with scores), shaped (n, 4) or + (n, 5). + labels (ndarray): Labels of bboxes. + class_names (list[str]): Names of each classes. + score_thr (float): Minimum score of bboxes to be shown. + bbox_color (str or tuple or :obj:`Color`): Color of bbox lines. + text_color (str or tuple or :obj:`Color`): Color of texts. + thickness (int): Thickness of lines. + font_scale (float): Font scales of texts. + show (bool): Whether to show the image. + win_name (str): The window name. + wait_time (int): Value of waitKey param. + out_file (str or None): The filename to write the image. + + Returns: + ndarray: The image with bboxes drawn on it. + """ + assert bboxes.ndim == 2 + assert labels.ndim == 1 + assert bboxes.shape[0] == labels.shape[0] + assert bboxes.shape[1] == 4 or bboxes.shape[1] == 5 + img = imread(img) + img = np.ascontiguousarray(img) + + if score_thr > 0: + assert bboxes.shape[1] == 5 + scores = bboxes[:, -1] + inds = scores > score_thr + bboxes = bboxes[inds, :] + labels = labels[inds] + + bbox_color = color_val(bbox_color) + text_color = color_val(text_color) + + for bbox, label in zip(bboxes, labels): + bbox_int = bbox.astype(np.int32) + left_top = (bbox_int[0], bbox_int[1]) + right_bottom = (bbox_int[2], bbox_int[3]) + cv2.rectangle( + img, left_top, right_bottom, bbox_color, thickness=thickness) + label_text = class_names[ + label] if class_names is not None else f'cls {label}' + if len(bbox) > 4: + label_text += f'|{bbox[-1]:.02f}' + cv2.putText(img, label_text, (bbox_int[0], bbox_int[1] - 2), + cv2.FONT_HERSHEY_COMPLEX, font_scale, text_color) + + if show: + imshow(img, win_name, wait_time) + if out_file is not None: + imwrite(img, out_file) + return img diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/visualization/optflow.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/visualization/optflow.py new file mode 100644 index 0000000000000000000000000000000000000000..b4c3ce980f9f6c74c85fe714aca1623a08ae7a8d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmcv/visualization/optflow.py @@ -0,0 +1,112 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from __future__ import division + +import numpy as np + +from annotator.mmpkg.mmcv.image import rgb2bgr +from annotator.mmpkg.mmcv.video import flowread +from .image import imshow + + +def flowshow(flow, win_name='', wait_time=0): + """Show optical flow. + + Args: + flow (ndarray or str): The optical flow to be displayed. + win_name (str): The window name. + wait_time (int): Value of waitKey param. + """ + flow = flowread(flow) + flow_img = flow2rgb(flow) + imshow(rgb2bgr(flow_img), win_name, wait_time) + + +def flow2rgb(flow, color_wheel=None, unknown_thr=1e6): + """Convert flow map to RGB image. + + Args: + flow (ndarray): Array of optical flow. + color_wheel (ndarray or None): Color wheel used to map flow field to + RGB colorspace. Default color wheel will be used if not specified. + unknown_thr (str): Values above this threshold will be marked as + unknown and thus ignored. + + Returns: + ndarray: RGB image that can be visualized. + """ + assert flow.ndim == 3 and flow.shape[-1] == 2 + if color_wheel is None: + color_wheel = make_color_wheel() + assert color_wheel.ndim == 2 and color_wheel.shape[1] == 3 + num_bins = color_wheel.shape[0] + + dx = flow[:, :, 0].copy() + dy = flow[:, :, 1].copy() + + ignore_inds = ( + np.isnan(dx) | np.isnan(dy) | (np.abs(dx) > unknown_thr) | + (np.abs(dy) > unknown_thr)) + dx[ignore_inds] = 0 + dy[ignore_inds] = 0 + + rad = np.sqrt(dx**2 + dy**2) + if np.any(rad > np.finfo(float).eps): + max_rad = np.max(rad) + dx /= max_rad + dy /= max_rad + + rad = np.sqrt(dx**2 + dy**2) + angle = np.arctan2(-dy, -dx) / np.pi + + bin_real = (angle + 1) / 2 * (num_bins - 1) + bin_left = np.floor(bin_real).astype(int) + bin_right = (bin_left + 1) % num_bins + w = (bin_real - bin_left.astype(np.float32))[..., None] + flow_img = (1 - + w) * color_wheel[bin_left, :] + w * color_wheel[bin_right, :] + small_ind = rad <= 1 + flow_img[small_ind] = 1 - rad[small_ind, None] * (1 - flow_img[small_ind]) + flow_img[np.logical_not(small_ind)] *= 0.75 + + flow_img[ignore_inds, :] = 0 + + return flow_img + + +def make_color_wheel(bins=None): + """Build a color wheel. + + Args: + bins(list or tuple, optional): Specify the number of bins for each + color range, corresponding to six ranges: red -> yellow, + yellow -> green, green -> cyan, cyan -> blue, blue -> magenta, + magenta -> red. [15, 6, 4, 11, 13, 6] is used for default + (see Middlebury). + + Returns: + ndarray: Color wheel of shape (total_bins, 3). + """ + if bins is None: + bins = [15, 6, 4, 11, 13, 6] + assert len(bins) == 6 + + RY, YG, GC, CB, BM, MR = tuple(bins) + + ry = [1, np.arange(RY) / RY, 0] + yg = [1 - np.arange(YG) / YG, 1, 0] + gc = [0, 1, np.arange(GC) / GC] + cb = [0, 1 - np.arange(CB) / CB, 1] + bm = [np.arange(BM) / BM, 0, 1] + mr = [1, 0, 1 - np.arange(MR) / MR] + + num_bins = RY + YG + GC + CB + BM + MR + + color_wheel = np.zeros((3, num_bins), dtype=np.float32) + + col = 0 + for i, color in enumerate([ry, yg, gc, cb, bm, mr]): + for j in range(3): + color_wheel[j, col:col + bins[i]] = color[j] + col += bins[i] + + return color_wheel.T diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/apis/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/apis/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..170724be38de42daf2bc1a1910e181d68818f165 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/apis/__init__.py @@ -0,0 +1,9 @@ +from .inference import inference_segmentor, init_segmentor, show_result_pyplot +from .test import multi_gpu_test, single_gpu_test +from .train import get_root_logger, set_random_seed, train_segmentor + +__all__ = [ + 'get_root_logger', 'set_random_seed', 'train_segmentor', 'init_segmentor', + 'inference_segmentor', 'multi_gpu_test', 'single_gpu_test', + 'show_result_pyplot' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/apis/inference.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/apis/inference.py new file mode 100644 index 0000000000000000000000000000000000000000..515e459ff6e66e955624fedaf32d2076be750563 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/apis/inference.py @@ -0,0 +1,138 @@ +import matplotlib.pyplot as plt +import annotator.mmpkg.mmcv as mmcv +import torch +from annotator.mmpkg.mmcv.parallel import collate, scatter +from annotator.mmpkg.mmcv.runner import load_checkpoint + +from annotator.mmpkg.mmseg.datasets.pipelines import Compose +from annotator.mmpkg.mmseg.models import build_segmentor +from modules import devices + + +def init_segmentor(config, checkpoint=None, device=devices.get_device_for("controlnet")): + """Initialize a segmentor from config file. + + Args: + config (str or :obj:`mmcv.Config`): Config file path or the config + object. + checkpoint (str, optional): Checkpoint path. If left as None, the model + will not load any weights. + device (str, optional) CPU/CUDA device option. Default 'cuda:0'. + Use 'cpu' for loading model on CPU. + Returns: + nn.Module: The constructed segmentor. + """ + if isinstance(config, str): + config = mmcv.Config.fromfile(config) + elif not isinstance(config, mmcv.Config): + raise TypeError('config must be a filename or Config object, ' + 'but got {}'.format(type(config))) + config.model.pretrained = None + config.model.train_cfg = None + model = build_segmentor(config.model, test_cfg=config.get('test_cfg')) + if checkpoint is not None: + checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') + model.CLASSES = checkpoint['meta']['CLASSES'] + model.PALETTE = checkpoint['meta']['PALETTE'] + model.cfg = config # save the config in the model for convenience + model.to(device) + model.eval() + return model + + +class LoadImage: + """A simple pipeline to load image.""" + + def __call__(self, results): + """Call function to load images into results. + + Args: + results (dict): A result dict contains the file name + of the image to be read. + + Returns: + dict: ``results`` will be returned containing loaded image. + """ + + if isinstance(results['img'], str): + results['filename'] = results['img'] + results['ori_filename'] = results['img'] + else: + results['filename'] = None + results['ori_filename'] = None + img = mmcv.imread(results['img']) + results['img'] = img + results['img_shape'] = img.shape + results['ori_shape'] = img.shape + return results + + +def inference_segmentor(model, img): + """Inference image(s) with the segmentor. + + Args: + model (nn.Module): The loaded segmentor. + imgs (str/ndarray or list[str/ndarray]): Either image files or loaded + images. + + Returns: + (list[Tensor]): The segmentation result. + """ + cfg = model.cfg + device = next(model.parameters()).device # model device + # build the data pipeline + test_pipeline = [LoadImage()] + cfg.data.test.pipeline[1:] + test_pipeline = Compose(test_pipeline) + # prepare data + data = dict(img=img) + data = test_pipeline(data) + data = collate([data], samples_per_gpu=1) + if next(model.parameters()).is_cuda: + # scatter to specified GPU + data = scatter(data, [device])[0] + else: + data['img'][0] = data['img'][0].to(devices.get_device_for("controlnet")) + data['img_metas'] = [i.data[0] for i in data['img_metas']] + + # forward the model + with torch.no_grad(): + result = model(return_loss=False, rescale=True, **data) + return result + + +def show_result_pyplot(model, + img, + result, + palette=None, + fig_size=(15, 10), + opacity=0.5, + title='', + block=True): + """Visualize the segmentation results on the image. + + Args: + model (nn.Module): The loaded segmentor. + img (str or np.ndarray): Image filename or loaded image. + result (list): The segmentation result. + palette (list[list[int]]] | None): The palette of segmentation + map. If None is given, random palette will be generated. + Default: None + fig_size (tuple): Figure size of the pyplot figure. + opacity(float): Opacity of painted segmentation map. + Default 0.5. + Must be in (0, 1] range. + title (str): The title of pyplot figure. + Default is ''. + block (bool): Whether to block the pyplot figure. + Default is True. + """ + if hasattr(model, 'module'): + model = model.module + img = model.show_result( + img, result, palette=palette, show=False, opacity=opacity) + # plt.figure(figsize=fig_size) + # plt.imshow(mmcv.bgr2rgb(img)) + # plt.title(title) + # plt.tight_layout() + # plt.show(block=block) + return mmcv.bgr2rgb(img) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/apis/test.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/apis/test.py new file mode 100644 index 0000000000000000000000000000000000000000..f9954e6a3709afdbf6a2027b213afcad644c47d7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/apis/test.py @@ -0,0 +1,238 @@ +import os.path as osp +import pickle +import shutil +import tempfile + +import annotator.mmpkg.mmcv as mmcv +import numpy as np +import torch +import torch.distributed as dist +from annotator.mmpkg.mmcv.image import tensor2imgs +from annotator.mmpkg.mmcv.runner import get_dist_info + + +def np2tmp(array, temp_file_name=None): + """Save ndarray to local numpy file. + + Args: + array (ndarray): Ndarray to save. + temp_file_name (str): Numpy file name. If 'temp_file_name=None', this + function will generate a file name with tempfile.NamedTemporaryFile + to save ndarray. Default: None. + + Returns: + str: The numpy file name. + """ + + if temp_file_name is None: + temp_file_name = tempfile.NamedTemporaryFile( + suffix='.npy', delete=False).name + np.save(temp_file_name, array) + return temp_file_name + + +def single_gpu_test(model, + data_loader, + show=False, + out_dir=None, + efficient_test=False, + opacity=0.5): + """Test with single GPU. + + Args: + model (nn.Module): Model to be tested. + data_loader (utils.data.Dataloader): Pytorch data loader. + show (bool): Whether show results during inference. Default: False. + out_dir (str, optional): If specified, the results will be dumped into + the directory to save output results. + efficient_test (bool): Whether save the results as local numpy files to + save CPU memory during evaluation. Default: False. + opacity(float): Opacity of painted segmentation map. + Default 0.5. + Must be in (0, 1] range. + Returns: + list: The prediction results. + """ + + model.eval() + results = [] + dataset = data_loader.dataset + prog_bar = mmcv.ProgressBar(len(dataset)) + for i, data in enumerate(data_loader): + with torch.no_grad(): + result = model(return_loss=False, **data) + + if show or out_dir: + img_tensor = data['img'][0] + img_metas = data['img_metas'][0].data[0] + imgs = tensor2imgs(img_tensor, **img_metas[0]['img_norm_cfg']) + assert len(imgs) == len(img_metas) + + for img, img_meta in zip(imgs, img_metas): + h, w, _ = img_meta['img_shape'] + img_show = img[:h, :w, :] + + ori_h, ori_w = img_meta['ori_shape'][:-1] + img_show = mmcv.imresize(img_show, (ori_w, ori_h)) + + if out_dir: + out_file = osp.join(out_dir, img_meta['ori_filename']) + else: + out_file = None + + model.module.show_result( + img_show, + result, + palette=dataset.PALETTE, + show=show, + out_file=out_file, + opacity=opacity) + + if isinstance(result, list): + if efficient_test: + result = [np2tmp(_) for _ in result] + results.extend(result) + else: + if efficient_test: + result = np2tmp(result) + results.append(result) + + batch_size = len(result) + for _ in range(batch_size): + prog_bar.update() + return results + + +def multi_gpu_test(model, + data_loader, + tmpdir=None, + gpu_collect=False, + efficient_test=False): + """Test model with multiple gpus. + + This method tests model with multiple gpus and collects the results + under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' + it encodes results to gpu tensors and use gpu communication for results + collection. On cpu mode it saves the results on different gpus to 'tmpdir' + and collects them by the rank 0 worker. + + Args: + model (nn.Module): Model to be tested. + data_loader (utils.data.Dataloader): Pytorch data loader. + tmpdir (str): Path of directory to save the temporary results from + different gpus under cpu mode. + gpu_collect (bool): Option to use either gpu or cpu to collect results. + efficient_test (bool): Whether save the results as local numpy files to + save CPU memory during evaluation. Default: False. + + Returns: + list: The prediction results. + """ + + model.eval() + results = [] + dataset = data_loader.dataset + rank, world_size = get_dist_info() + if rank == 0: + prog_bar = mmcv.ProgressBar(len(dataset)) + for i, data in enumerate(data_loader): + with torch.no_grad(): + result = model(return_loss=False, rescale=True, **data) + + if isinstance(result, list): + if efficient_test: + result = [np2tmp(_) for _ in result] + results.extend(result) + else: + if efficient_test: + result = np2tmp(result) + results.append(result) + + if rank == 0: + batch_size = data['img'][0].size(0) + for _ in range(batch_size * world_size): + prog_bar.update() + + # collect results from all ranks + if gpu_collect: + results = collect_results_gpu(results, len(dataset)) + else: + results = collect_results_cpu(results, len(dataset), tmpdir) + return results + + +def collect_results_cpu(result_part, size, tmpdir=None): + """Collect results with CPU.""" + rank, world_size = get_dist_info() + # create a tmp dir if it is not specified + if tmpdir is None: + MAX_LEN = 512 + # 32 is whitespace + dir_tensor = torch.full((MAX_LEN, ), + 32, + dtype=torch.uint8, + device='cuda') + if rank == 0: + tmpdir = tempfile.mkdtemp() + tmpdir = torch.tensor( + bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') + dir_tensor[:len(tmpdir)] = tmpdir + dist.broadcast(dir_tensor, 0) + tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() + else: + mmcv.mkdir_or_exist(tmpdir) + # dump the part result to the dir + mmcv.dump(result_part, osp.join(tmpdir, 'part_{}.pkl'.format(rank))) + dist.barrier() + # collect all parts + if rank != 0: + return None + else: + # load results of all parts from tmp dir + part_list = [] + for i in range(world_size): + part_file = osp.join(tmpdir, 'part_{}.pkl'.format(i)) + part_list.append(mmcv.load(part_file)) + # sort the results + ordered_results = [] + for res in zip(*part_list): + ordered_results.extend(list(res)) + # the dataloader may pad some samples + ordered_results = ordered_results[:size] + # remove tmp dir + shutil.rmtree(tmpdir) + return ordered_results + + +def collect_results_gpu(result_part, size): + """Collect results with GPU.""" + rank, world_size = get_dist_info() + # dump result part to tensor with pickle + part_tensor = torch.tensor( + bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') + # gather all result part tensor shape + shape_tensor = torch.tensor(part_tensor.shape, device='cuda') + shape_list = [shape_tensor.clone() for _ in range(world_size)] + dist.all_gather(shape_list, shape_tensor) + # padding result part tensor to max length + shape_max = torch.tensor(shape_list).max() + part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') + part_send[:shape_tensor[0]] = part_tensor + part_recv_list = [ + part_tensor.new_zeros(shape_max) for _ in range(world_size) + ] + # gather all result part + dist.all_gather(part_recv_list, part_send) + + if rank == 0: + part_list = [] + for recv, shape in zip(part_recv_list, shape_list): + part_list.append( + pickle.loads(recv[:shape[0]].cpu().numpy().tobytes())) + # sort the results + ordered_results = [] + for res in zip(*part_list): + ordered_results.extend(list(res)) + # the dataloader may pad some samples + ordered_results = ordered_results[:size] + return ordered_results diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/apis/train.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/apis/train.py new file mode 100644 index 0000000000000000000000000000000000000000..f0a87d65c72e4581c96b41aebf879905510c9d22 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/apis/train.py @@ -0,0 +1,116 @@ +import random +import warnings + +import numpy as np +import torch +from annotator.mmpkg.mmcv.parallel import MMDataParallel, MMDistributedDataParallel +from annotator.mmpkg.mmcv.runner import build_optimizer, build_runner + +from annotator.mmpkg.mmseg.core import DistEvalHook, EvalHook +from annotator.mmpkg.mmseg.datasets import build_dataloader, build_dataset +from annotator.mmpkg.mmseg.utils import get_root_logger + + +def set_random_seed(seed, deterministic=False): + """Set random seed. + + Args: + seed (int): Seed to be used. + deterministic (bool): Whether to set the deterministic option for + CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` + to True and `torch.backends.cudnn.benchmark` to False. + Default: False. + """ + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + if deterministic: + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + + +def train_segmentor(model, + dataset, + cfg, + distributed=False, + validate=False, + timestamp=None, + meta=None): + """Launch segmentor training.""" + logger = get_root_logger(cfg.log_level) + + # prepare data loaders + dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] + data_loaders = [ + build_dataloader( + ds, + cfg.data.samples_per_gpu, + cfg.data.workers_per_gpu, + # cfg.gpus will be ignored if distributed + len(cfg.gpu_ids), + dist=distributed, + seed=cfg.seed, + drop_last=True) for ds in dataset + ] + + # put model on gpus + if distributed: + find_unused_parameters = cfg.get('find_unused_parameters', False) + # Sets the `find_unused_parameters` parameter in + # torch.nn.parallel.DistributedDataParallel + model = MMDistributedDataParallel( + model.cuda(), + device_ids=[torch.cuda.current_device()], + broadcast_buffers=False, + find_unused_parameters=find_unused_parameters) + else: + model = MMDataParallel( + model.cuda(cfg.gpu_ids[0]), device_ids=cfg.gpu_ids) + + # build runner + optimizer = build_optimizer(model, cfg.optimizer) + + if cfg.get('runner') is None: + cfg.runner = {'type': 'IterBasedRunner', 'max_iters': cfg.total_iters} + warnings.warn( + 'config is now expected to have a `runner` section, ' + 'please set `runner` in your config.', UserWarning) + + runner = build_runner( + cfg.runner, + default_args=dict( + model=model, + batch_processor=None, + optimizer=optimizer, + work_dir=cfg.work_dir, + logger=logger, + meta=meta)) + + # register hooks + runner.register_training_hooks(cfg.lr_config, cfg.optimizer_config, + cfg.checkpoint_config, cfg.log_config, + cfg.get('momentum_config', None)) + + # an ugly walkaround to make the .log and .log.json filenames the same + runner.timestamp = timestamp + + # register eval hooks + if validate: + val_dataset = build_dataset(cfg.data.val, dict(test_mode=True)) + val_dataloader = build_dataloader( + val_dataset, + samples_per_gpu=1, + workers_per_gpu=cfg.data.workers_per_gpu, + dist=distributed, + shuffle=False) + eval_cfg = cfg.get('evaluation', {}) + eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner' + eval_hook = DistEvalHook if distributed else EvalHook + runner.register_hook(eval_hook(val_dataloader, **eval_cfg), priority='LOW') + + if cfg.resume_from: + runner.resume(cfg.resume_from) + elif cfg.load_from: + runner.load_checkpoint(cfg.load_from) + runner.run(data_loaders, cfg.workflow) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..965605587211b7bf0bd6bc3acdbb33dd49cab023 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/__init__.py @@ -0,0 +1,3 @@ +from .evaluation import * # noqa: F401, F403 +from .seg import * # noqa: F401, F403 +from .utils import * # noqa: F401, F403 diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/evaluation/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/evaluation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f7cc4b23413a0639e9de00eeb0bf600632d2c6cd --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/evaluation/__init__.py @@ -0,0 +1,8 @@ +from .class_names import get_classes, get_palette +from .eval_hooks import DistEvalHook, EvalHook +from .metrics import eval_metrics, mean_dice, mean_fscore, mean_iou + +__all__ = [ + 'EvalHook', 'DistEvalHook', 'mean_dice', 'mean_iou', 'mean_fscore', + 'eval_metrics', 'get_classes', 'get_palette' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/evaluation/class_names.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/evaluation/class_names.py new file mode 100644 index 0000000000000000000000000000000000000000..532c5fd78946ede66d747ec8e7b72dbb66471aac --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/evaluation/class_names.py @@ -0,0 +1,152 @@ +import annotator.mmpkg.mmcv as mmcv + + +def cityscapes_classes(): + """Cityscapes class names for external use.""" + return [ + 'road', 'sidewalk', 'building', 'wall', 'fence', 'pole', + 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', + 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', + 'bicycle' + ] + + +def ade_classes(): + """ADE20K class names for external use.""" + return [ + 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', + 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', + 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', + 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', + 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', + 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', + 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', + 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', + 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', + 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', + 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', + 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', + 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', + 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', + 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', + 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', + 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', + 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', + 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', + 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', + 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', + 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', + 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', + 'clock', 'flag' + ] + + +def voc_classes(): + """Pascal VOC class names for external use.""" + return [ + 'background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', + 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', + 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', + 'tvmonitor' + ] + + +def cityscapes_palette(): + """Cityscapes palette for external use.""" + return [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], + [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], + [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], + [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], [0, 80, 100], + [0, 0, 230], [119, 11, 32]] + + +def ade_palette(): + """ADE20K palette for external use.""" + return [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], + [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], + [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], + [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], + [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], + [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], + [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], + [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], + [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], + [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], + [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], + [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], + [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], + [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], + [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], + [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], + [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], + [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], + [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], + [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], + [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], + [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], + [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], + [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], + [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], + [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], + [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], + [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], + [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], + [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], + [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], + [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], + [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], + [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], + [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], + [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], + [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], + [102, 255, 0], [92, 0, 255]] + + +def voc_palette(): + """Pascal VOC palette for external use.""" + return [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], + [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], + [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], + [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], + [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] + + +dataset_aliases = { + 'cityscapes': ['cityscapes'], + 'ade': ['ade', 'ade20k'], + 'voc': ['voc', 'pascal_voc', 'voc12', 'voc12aug'] +} + + +def get_classes(dataset): + """Get class names of a dataset.""" + alias2name = {} + for name, aliases in dataset_aliases.items(): + for alias in aliases: + alias2name[alias] = name + + if mmcv.is_str(dataset): + if dataset in alias2name: + labels = eval(alias2name[dataset] + '_classes()') + else: + raise ValueError(f'Unrecognized dataset: {dataset}') + else: + raise TypeError(f'dataset must a str, but got {type(dataset)}') + return labels + + +def get_palette(dataset): + """Get class palette (RGB) of a dataset.""" + alias2name = {} + for name, aliases in dataset_aliases.items(): + for alias in aliases: + alias2name[alias] = name + + if mmcv.is_str(dataset): + if dataset in alias2name: + labels = eval(alias2name[dataset] + '_palette()') + else: + raise ValueError(f'Unrecognized dataset: {dataset}') + else: + raise TypeError(f'dataset must a str, but got {type(dataset)}') + return labels diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/evaluation/eval_hooks.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/evaluation/eval_hooks.py new file mode 100644 index 0000000000000000000000000000000000000000..408e9670f61d1b118477562b341adc644c52799a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/evaluation/eval_hooks.py @@ -0,0 +1,109 @@ +import os.path as osp + +from annotator.mmpkg.mmcv.runner import DistEvalHook as _DistEvalHook +from annotator.mmpkg.mmcv.runner import EvalHook as _EvalHook + + +class EvalHook(_EvalHook): + """Single GPU EvalHook, with efficient test support. + + Args: + by_epoch (bool): Determine perform evaluation by epoch or by iteration. + If set to True, it will perform by epoch. Otherwise, by iteration. + Default: False. + efficient_test (bool): Whether save the results as local numpy files to + save CPU memory during evaluation. Default: False. + Returns: + list: The prediction results. + """ + + greater_keys = ['mIoU', 'mAcc', 'aAcc'] + + def __init__(self, *args, by_epoch=False, efficient_test=False, **kwargs): + super().__init__(*args, by_epoch=by_epoch, **kwargs) + self.efficient_test = efficient_test + + def after_train_iter(self, runner): + """After train epoch hook. + + Override default ``single_gpu_test``. + """ + if self.by_epoch or not self.every_n_iters(runner, self.interval): + return + from annotator.mmpkg.mmseg.apis import single_gpu_test + runner.log_buffer.clear() + results = single_gpu_test( + runner.model, + self.dataloader, + show=False, + efficient_test=self.efficient_test) + self.evaluate(runner, results) + + def after_train_epoch(self, runner): + """After train epoch hook. + + Override default ``single_gpu_test``. + """ + if not self.by_epoch or not self.every_n_epochs(runner, self.interval): + return + from annotator.mmpkg.mmseg.apis import single_gpu_test + runner.log_buffer.clear() + results = single_gpu_test(runner.model, self.dataloader, show=False) + self.evaluate(runner, results) + + +class DistEvalHook(_DistEvalHook): + """Distributed EvalHook, with efficient test support. + + Args: + by_epoch (bool): Determine perform evaluation by epoch or by iteration. + If set to True, it will perform by epoch. Otherwise, by iteration. + Default: False. + efficient_test (bool): Whether save the results as local numpy files to + save CPU memory during evaluation. Default: False. + Returns: + list: The prediction results. + """ + + greater_keys = ['mIoU', 'mAcc', 'aAcc'] + + def __init__(self, *args, by_epoch=False, efficient_test=False, **kwargs): + super().__init__(*args, by_epoch=by_epoch, **kwargs) + self.efficient_test = efficient_test + + def after_train_iter(self, runner): + """After train epoch hook. + + Override default ``multi_gpu_test``. + """ + if self.by_epoch or not self.every_n_iters(runner, self.interval): + return + from annotator.mmpkg.mmseg.apis import multi_gpu_test + runner.log_buffer.clear() + results = multi_gpu_test( + runner.model, + self.dataloader, + tmpdir=osp.join(runner.work_dir, '.eval_hook'), + gpu_collect=self.gpu_collect, + efficient_test=self.efficient_test) + if runner.rank == 0: + print('\n') + self.evaluate(runner, results) + + def after_train_epoch(self, runner): + """After train epoch hook. + + Override default ``multi_gpu_test``. + """ + if not self.by_epoch or not self.every_n_epochs(runner, self.interval): + return + from annotator.mmpkg.mmseg.apis import multi_gpu_test + runner.log_buffer.clear() + results = multi_gpu_test( + runner.model, + self.dataloader, + tmpdir=osp.join(runner.work_dir, '.eval_hook'), + gpu_collect=self.gpu_collect) + if runner.rank == 0: + print('\n') + self.evaluate(runner, results) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/evaluation/metrics.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/evaluation/metrics.py new file mode 100644 index 0000000000000000000000000000000000000000..8ede737624a0ba6e6365639f7019ac2527052cfd --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/evaluation/metrics.py @@ -0,0 +1,326 @@ +from collections import OrderedDict + +import annotator.mmpkg.mmcv as mmcv +import numpy as np +import torch + + +def f_score(precision, recall, beta=1): + """calcuate the f-score value. + + Args: + precision (float | torch.Tensor): The precision value. + recall (float | torch.Tensor): The recall value. + beta (int): Determines the weight of recall in the combined score. + Default: False. + + Returns: + [torch.tensor]: The f-score value. + """ + score = (1 + beta**2) * (precision * recall) / ( + (beta**2 * precision) + recall) + return score + + +def intersect_and_union(pred_label, + label, + num_classes, + ignore_index, + label_map=dict(), + reduce_zero_label=False): + """Calculate intersection and Union. + + Args: + pred_label (ndarray | str): Prediction segmentation map + or predict result filename. + label (ndarray | str): Ground truth segmentation map + or label filename. + num_classes (int): Number of categories. + ignore_index (int): Index that will be ignored in evaluation. + label_map (dict): Mapping old labels to new labels. The parameter will + work only when label is str. Default: dict(). + reduce_zero_label (bool): Whether ignore zero label. The parameter will + work only when label is str. Default: False. + + Returns: + torch.Tensor: The intersection of prediction and ground truth + histogram on all classes. + torch.Tensor: The union of prediction and ground truth histogram on + all classes. + torch.Tensor: The prediction histogram on all classes. + torch.Tensor: The ground truth histogram on all classes. + """ + + if isinstance(pred_label, str): + pred_label = torch.from_numpy(np.load(pred_label)) + else: + pred_label = torch.from_numpy((pred_label)) + + if isinstance(label, str): + label = torch.from_numpy( + mmcv.imread(label, flag='unchanged', backend='pillow')) + else: + label = torch.from_numpy(label) + + if label_map is not None: + for old_id, new_id in label_map.items(): + label[label == old_id] = new_id + if reduce_zero_label: + label[label == 0] = 255 + label = label - 1 + label[label == 254] = 255 + + mask = (label != ignore_index) + pred_label = pred_label[mask] + label = label[mask] + + intersect = pred_label[pred_label == label] + area_intersect = torch.histc( + intersect.float(), bins=(num_classes), min=0, max=num_classes - 1) + area_pred_label = torch.histc( + pred_label.float(), bins=(num_classes), min=0, max=num_classes - 1) + area_label = torch.histc( + label.float(), bins=(num_classes), min=0, max=num_classes - 1) + area_union = area_pred_label + area_label - area_intersect + return area_intersect, area_union, area_pred_label, area_label + + +def total_intersect_and_union(results, + gt_seg_maps, + num_classes, + ignore_index, + label_map=dict(), + reduce_zero_label=False): + """Calculate Total Intersection and Union. + + Args: + results (list[ndarray] | list[str]): List of prediction segmentation + maps or list of prediction result filenames. + gt_seg_maps (list[ndarray] | list[str]): list of ground truth + segmentation maps or list of label filenames. + num_classes (int): Number of categories. + ignore_index (int): Index that will be ignored in evaluation. + label_map (dict): Mapping old labels to new labels. Default: dict(). + reduce_zero_label (bool): Whether ignore zero label. Default: False. + + Returns: + ndarray: The intersection of prediction and ground truth histogram + on all classes. + ndarray: The union of prediction and ground truth histogram on all + classes. + ndarray: The prediction histogram on all classes. + ndarray: The ground truth histogram on all classes. + """ + num_imgs = len(results) + assert len(gt_seg_maps) == num_imgs + total_area_intersect = torch.zeros((num_classes, ), dtype=torch.float64) + total_area_union = torch.zeros((num_classes, ), dtype=torch.float64) + total_area_pred_label = torch.zeros((num_classes, ), dtype=torch.float64) + total_area_label = torch.zeros((num_classes, ), dtype=torch.float64) + for i in range(num_imgs): + area_intersect, area_union, area_pred_label, area_label = \ + intersect_and_union( + results[i], gt_seg_maps[i], num_classes, ignore_index, + label_map, reduce_zero_label) + total_area_intersect += area_intersect + total_area_union += area_union + total_area_pred_label += area_pred_label + total_area_label += area_label + return total_area_intersect, total_area_union, total_area_pred_label, \ + total_area_label + + +def mean_iou(results, + gt_seg_maps, + num_classes, + ignore_index, + nan_to_num=None, + label_map=dict(), + reduce_zero_label=False): + """Calculate Mean Intersection and Union (mIoU) + + Args: + results (list[ndarray] | list[str]): List of prediction segmentation + maps or list of prediction result filenames. + gt_seg_maps (list[ndarray] | list[str]): list of ground truth + segmentation maps or list of label filenames. + num_classes (int): Number of categories. + ignore_index (int): Index that will be ignored in evaluation. + nan_to_num (int, optional): If specified, NaN values will be replaced + by the numbers defined by the user. Default: None. + label_map (dict): Mapping old labels to new labels. Default: dict(). + reduce_zero_label (bool): Whether ignore zero label. Default: False. + + Returns: + dict[str, float | ndarray]: + float: Overall accuracy on all images. + ndarray: Per category accuracy, shape (num_classes, ). + ndarray: Per category IoU, shape (num_classes, ). + """ + iou_result = eval_metrics( + results=results, + gt_seg_maps=gt_seg_maps, + num_classes=num_classes, + ignore_index=ignore_index, + metrics=['mIoU'], + nan_to_num=nan_to_num, + label_map=label_map, + reduce_zero_label=reduce_zero_label) + return iou_result + + +def mean_dice(results, + gt_seg_maps, + num_classes, + ignore_index, + nan_to_num=None, + label_map=dict(), + reduce_zero_label=False): + """Calculate Mean Dice (mDice) + + Args: + results (list[ndarray] | list[str]): List of prediction segmentation + maps or list of prediction result filenames. + gt_seg_maps (list[ndarray] | list[str]): list of ground truth + segmentation maps or list of label filenames. + num_classes (int): Number of categories. + ignore_index (int): Index that will be ignored in evaluation. + nan_to_num (int, optional): If specified, NaN values will be replaced + by the numbers defined by the user. Default: None. + label_map (dict): Mapping old labels to new labels. Default: dict(). + reduce_zero_label (bool): Whether ignore zero label. Default: False. + + Returns: + dict[str, float | ndarray]: Default metrics. + float: Overall accuracy on all images. + ndarray: Per category accuracy, shape (num_classes, ). + ndarray: Per category dice, shape (num_classes, ). + """ + + dice_result = eval_metrics( + results=results, + gt_seg_maps=gt_seg_maps, + num_classes=num_classes, + ignore_index=ignore_index, + metrics=['mDice'], + nan_to_num=nan_to_num, + label_map=label_map, + reduce_zero_label=reduce_zero_label) + return dice_result + + +def mean_fscore(results, + gt_seg_maps, + num_classes, + ignore_index, + nan_to_num=None, + label_map=dict(), + reduce_zero_label=False, + beta=1): + """Calculate Mean Intersection and Union (mIoU) + + Args: + results (list[ndarray] | list[str]): List of prediction segmentation + maps or list of prediction result filenames. + gt_seg_maps (list[ndarray] | list[str]): list of ground truth + segmentation maps or list of label filenames. + num_classes (int): Number of categories. + ignore_index (int): Index that will be ignored in evaluation. + nan_to_num (int, optional): If specified, NaN values will be replaced + by the numbers defined by the user. Default: None. + label_map (dict): Mapping old labels to new labels. Default: dict(). + reduce_zero_label (bool): Whether ignore zero label. Default: False. + beta (int): Determines the weight of recall in the combined score. + Default: False. + + + Returns: + dict[str, float | ndarray]: Default metrics. + float: Overall accuracy on all images. + ndarray: Per category recall, shape (num_classes, ). + ndarray: Per category precision, shape (num_classes, ). + ndarray: Per category f-score, shape (num_classes, ). + """ + fscore_result = eval_metrics( + results=results, + gt_seg_maps=gt_seg_maps, + num_classes=num_classes, + ignore_index=ignore_index, + metrics=['mFscore'], + nan_to_num=nan_to_num, + label_map=label_map, + reduce_zero_label=reduce_zero_label, + beta=beta) + return fscore_result + + +def eval_metrics(results, + gt_seg_maps, + num_classes, + ignore_index, + metrics=['mIoU'], + nan_to_num=None, + label_map=dict(), + reduce_zero_label=False, + beta=1): + """Calculate evaluation metrics + Args: + results (list[ndarray] | list[str]): List of prediction segmentation + maps or list of prediction result filenames. + gt_seg_maps (list[ndarray] | list[str]): list of ground truth + segmentation maps or list of label filenames. + num_classes (int): Number of categories. + ignore_index (int): Index that will be ignored in evaluation. + metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. + nan_to_num (int, optional): If specified, NaN values will be replaced + by the numbers defined by the user. Default: None. + label_map (dict): Mapping old labels to new labels. Default: dict(). + reduce_zero_label (bool): Whether ignore zero label. Default: False. + Returns: + float: Overall accuracy on all images. + ndarray: Per category accuracy, shape (num_classes, ). + ndarray: Per category evaluation metrics, shape (num_classes, ). + """ + if isinstance(metrics, str): + metrics = [metrics] + allowed_metrics = ['mIoU', 'mDice', 'mFscore'] + if not set(metrics).issubset(set(allowed_metrics)): + raise KeyError('metrics {} is not supported'.format(metrics)) + + total_area_intersect, total_area_union, total_area_pred_label, \ + total_area_label = total_intersect_and_union( + results, gt_seg_maps, num_classes, ignore_index, label_map, + reduce_zero_label) + all_acc = total_area_intersect.sum() / total_area_label.sum() + ret_metrics = OrderedDict({'aAcc': all_acc}) + for metric in metrics: + if metric == 'mIoU': + iou = total_area_intersect / total_area_union + acc = total_area_intersect / total_area_label + ret_metrics['IoU'] = iou + ret_metrics['Acc'] = acc + elif metric == 'mDice': + dice = 2 * total_area_intersect / ( + total_area_pred_label + total_area_label) + acc = total_area_intersect / total_area_label + ret_metrics['Dice'] = dice + ret_metrics['Acc'] = acc + elif metric == 'mFscore': + precision = total_area_intersect / total_area_pred_label + recall = total_area_intersect / total_area_label + f_value = torch.tensor( + [f_score(x[0], x[1], beta) for x in zip(precision, recall)]) + ret_metrics['Fscore'] = f_value + ret_metrics['Precision'] = precision + ret_metrics['Recall'] = recall + + ret_metrics = { + metric: value.numpy() + for metric, value in ret_metrics.items() + } + if nan_to_num is not None: + ret_metrics = OrderedDict({ + metric: np.nan_to_num(metric_value, nan=nan_to_num) + for metric, metric_value in ret_metrics.items() + }) + return ret_metrics diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..93bc129b685e4a3efca2cc891729981b2865900d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/__init__.py @@ -0,0 +1,4 @@ +from .builder import build_pixel_sampler +from .sampler import BasePixelSampler, OHEMPixelSampler + +__all__ = ['build_pixel_sampler', 'BasePixelSampler', 'OHEMPixelSampler'] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/builder.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/builder.py new file mode 100644 index 0000000000000000000000000000000000000000..f8fff6375622282f85b3acf15af1a7d27fb9c426 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/builder.py @@ -0,0 +1,8 @@ +from annotator.mmpkg.mmcv.utils import Registry, build_from_cfg + +PIXEL_SAMPLERS = Registry('pixel sampler') + + +def build_pixel_sampler(cfg, **default_args): + """Build pixel sampler for segmentation map.""" + return build_from_cfg(cfg, PIXEL_SAMPLERS, default_args) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/sampler/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/sampler/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..332b242c03d1c5e80d4577df442a9a037b1816e1 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/sampler/__init__.py @@ -0,0 +1,4 @@ +from .base_pixel_sampler import BasePixelSampler +from .ohem_pixel_sampler import OHEMPixelSampler + +__all__ = ['BasePixelSampler', 'OHEMPixelSampler'] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/sampler/base_pixel_sampler.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/sampler/base_pixel_sampler.py new file mode 100644 index 0000000000000000000000000000000000000000..b75b1566c9f18169cee51d4b55d75e0357b69c57 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/sampler/base_pixel_sampler.py @@ -0,0 +1,12 @@ +from abc import ABCMeta, abstractmethod + + +class BasePixelSampler(metaclass=ABCMeta): + """Base class of pixel sampler.""" + + def __init__(self, **kwargs): + pass + + @abstractmethod + def sample(self, seg_logit, seg_label): + """Placeholder for sample function.""" diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/sampler/ohem_pixel_sampler.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/sampler/ohem_pixel_sampler.py new file mode 100644 index 0000000000000000000000000000000000000000..88bb10d44026ba9f21756eaea9e550841cd59b9f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/seg/sampler/ohem_pixel_sampler.py @@ -0,0 +1,76 @@ +import torch +import torch.nn.functional as F + +from ..builder import PIXEL_SAMPLERS +from .base_pixel_sampler import BasePixelSampler + + +@PIXEL_SAMPLERS.register_module() +class OHEMPixelSampler(BasePixelSampler): + """Online Hard Example Mining Sampler for segmentation. + + Args: + context (nn.Module): The context of sampler, subclass of + :obj:`BaseDecodeHead`. + thresh (float, optional): The threshold for hard example selection. + Below which, are prediction with low confidence. If not + specified, the hard examples will be pixels of top ``min_kept`` + loss. Default: None. + min_kept (int, optional): The minimum number of predictions to keep. + Default: 100000. + """ + + def __init__(self, context, thresh=None, min_kept=100000): + super(OHEMPixelSampler, self).__init__() + self.context = context + assert min_kept > 1 + self.thresh = thresh + self.min_kept = min_kept + + def sample(self, seg_logit, seg_label): + """Sample pixels that have high loss or with low prediction confidence. + + Args: + seg_logit (torch.Tensor): segmentation logits, shape (N, C, H, W) + seg_label (torch.Tensor): segmentation label, shape (N, 1, H, W) + + Returns: + torch.Tensor: segmentation weight, shape (N, H, W) + """ + with torch.no_grad(): + assert seg_logit.shape[2:] == seg_label.shape[2:] + assert seg_label.shape[1] == 1 + seg_label = seg_label.squeeze(1).long() + batch_kept = self.min_kept * seg_label.size(0) + valid_mask = seg_label != self.context.ignore_index + seg_weight = seg_logit.new_zeros(size=seg_label.size()) + valid_seg_weight = seg_weight[valid_mask] + if self.thresh is not None: + seg_prob = F.softmax(seg_logit, dim=1) + + tmp_seg_label = seg_label.clone().unsqueeze(1) + tmp_seg_label[tmp_seg_label == self.context.ignore_index] = 0 + seg_prob = seg_prob.gather(1, tmp_seg_label).squeeze(1) + sort_prob, sort_indices = seg_prob[valid_mask].sort() + + if sort_prob.numel() > 0: + min_threshold = sort_prob[min(batch_kept, + sort_prob.numel() - 1)] + else: + min_threshold = 0.0 + threshold = max(min_threshold, self.thresh) + valid_seg_weight[seg_prob[valid_mask] < threshold] = 1. + else: + losses = self.context.loss_decode( + seg_logit, + seg_label, + weight=None, + ignore_index=self.context.ignore_index, + reduction_override='none') + # faster than topk according to https://github.com/pytorch/pytorch/issues/22812 # noqa + _, sort_indices = losses[valid_mask].sort(descending=True) + valid_seg_weight[sort_indices[:batch_kept]] = 1. + + seg_weight[valid_mask] = valid_seg_weight + + return seg_weight diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/utils/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f2678b321c295bcceaef945111ac3524be19d6e4 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/utils/__init__.py @@ -0,0 +1,3 @@ +from .misc import add_prefix + +__all__ = ['add_prefix'] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/utils/misc.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/utils/misc.py new file mode 100644 index 0000000000000000000000000000000000000000..eb862a82bd47c8624db3dd5c6fb6ad8a03b62466 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/core/utils/misc.py @@ -0,0 +1,17 @@ +def add_prefix(inputs, prefix): + """Add prefix for dict. + + Args: + inputs (dict): The input dict with str keys. + prefix (str): The prefix to add. + + Returns: + + dict: The dict with keys updated with ``prefix``. + """ + + outputs = dict() + for name, value in inputs.items(): + outputs[f'{prefix}.{name}'] = value + + return outputs diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ebeaef4a28ef655e43578552a8aef6b77f13a636 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/__init__.py @@ -0,0 +1,19 @@ +from .ade import ADE20KDataset +from .builder import DATASETS, PIPELINES, build_dataloader, build_dataset +from .chase_db1 import ChaseDB1Dataset +from .cityscapes import CityscapesDataset +from .custom import CustomDataset +from .dataset_wrappers import ConcatDataset, RepeatDataset +from .drive import DRIVEDataset +from .hrf import HRFDataset +from .pascal_context import PascalContextDataset, PascalContextDataset59 +from .stare import STAREDataset +from .voc import PascalVOCDataset + +__all__ = [ + 'CustomDataset', 'build_dataloader', 'ConcatDataset', 'RepeatDataset', + 'DATASETS', 'build_dataset', 'PIPELINES', 'CityscapesDataset', + 'PascalVOCDataset', 'ADE20KDataset', 'PascalContextDataset', + 'PascalContextDataset59', 'ChaseDB1Dataset', 'DRIVEDataset', 'HRFDataset', + 'STAREDataset' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/ade.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/ade.py new file mode 100644 index 0000000000000000000000000000000000000000..5913e43775ed4920b6934c855eb5a37c54218ebf --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/ade.py @@ -0,0 +1,84 @@ +from .builder import DATASETS +from .custom import CustomDataset + + +@DATASETS.register_module() +class ADE20KDataset(CustomDataset): + """ADE20K dataset. + + In segmentation map annotation for ADE20K, 0 stands for background, which + is not included in 150 categories. ``reduce_zero_label`` is fixed to True. + The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is fixed to + '.png'. + """ + CLASSES = ( + 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', + 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', + 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', + 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', + 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', + 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', + 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', + 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', + 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', + 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', + 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', + 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', + 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', + 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', + 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', + 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', + 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', + 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', + 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', + 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', + 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', + 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', + 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', + 'clock', 'flag') + + PALETTE = [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], + [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], + [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], + [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], + [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], + [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], + [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], + [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], + [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], + [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], + [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], + [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], + [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], + [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], + [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], + [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], + [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], + [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], + [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], + [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], + [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], + [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], + [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], + [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], + [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], + [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], + [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], + [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], + [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], + [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], + [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], + [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], + [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], + [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], + [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], + [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], + [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], + [102, 255, 0], [92, 0, 255]] + + def __init__(self, **kwargs): + super(ADE20KDataset, self).__init__( + img_suffix='.jpg', + seg_map_suffix='.png', + reduce_zero_label=True, + **kwargs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/builder.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/builder.py new file mode 100644 index 0000000000000000000000000000000000000000..6cf8b4d9d32d4464905507cd54a84eb534f38bb6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/builder.py @@ -0,0 +1,169 @@ +import copy +import platform +import random +from functools import partial + +import numpy as np +from annotator.mmpkg.mmcv.parallel import collate +from annotator.mmpkg.mmcv.runner import get_dist_info +from annotator.mmpkg.mmcv.utils import Registry, build_from_cfg +from annotator.mmpkg.mmcv.utils.parrots_wrapper import DataLoader, PoolDataLoader +from torch.utils.data import DistributedSampler + +if platform.system() != 'Windows': + # https://github.com/pytorch/pytorch/issues/973 + import resource + rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) + hard_limit = rlimit[1] + soft_limit = min(4096, hard_limit) + resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) + +DATASETS = Registry('dataset') +PIPELINES = Registry('pipeline') + + +def _concat_dataset(cfg, default_args=None): + """Build :obj:`ConcatDataset by.""" + from .dataset_wrappers import ConcatDataset + img_dir = cfg['img_dir'] + ann_dir = cfg.get('ann_dir', None) + split = cfg.get('split', None) + num_img_dir = len(img_dir) if isinstance(img_dir, (list, tuple)) else 1 + if ann_dir is not None: + num_ann_dir = len(ann_dir) if isinstance(ann_dir, (list, tuple)) else 1 + else: + num_ann_dir = 0 + if split is not None: + num_split = len(split) if isinstance(split, (list, tuple)) else 1 + else: + num_split = 0 + if num_img_dir > 1: + assert num_img_dir == num_ann_dir or num_ann_dir == 0 + assert num_img_dir == num_split or num_split == 0 + else: + assert num_split == num_ann_dir or num_ann_dir <= 1 + num_dset = max(num_split, num_img_dir) + + datasets = [] + for i in range(num_dset): + data_cfg = copy.deepcopy(cfg) + if isinstance(img_dir, (list, tuple)): + data_cfg['img_dir'] = img_dir[i] + if isinstance(ann_dir, (list, tuple)): + data_cfg['ann_dir'] = ann_dir[i] + if isinstance(split, (list, tuple)): + data_cfg['split'] = split[i] + datasets.append(build_dataset(data_cfg, default_args)) + + return ConcatDataset(datasets) + + +def build_dataset(cfg, default_args=None): + """Build datasets.""" + from .dataset_wrappers import ConcatDataset, RepeatDataset + if isinstance(cfg, (list, tuple)): + dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) + elif cfg['type'] == 'RepeatDataset': + dataset = RepeatDataset( + build_dataset(cfg['dataset'], default_args), cfg['times']) + elif isinstance(cfg.get('img_dir'), (list, tuple)) or isinstance( + cfg.get('split', None), (list, tuple)): + dataset = _concat_dataset(cfg, default_args) + else: + dataset = build_from_cfg(cfg, DATASETS, default_args) + + return dataset + + +def build_dataloader(dataset, + samples_per_gpu, + workers_per_gpu, + num_gpus=1, + dist=True, + shuffle=True, + seed=None, + drop_last=False, + pin_memory=True, + dataloader_type='PoolDataLoader', + **kwargs): + """Build PyTorch DataLoader. + + In distributed training, each GPU/process has a dataloader. + In non-distributed training, there is only one dataloader for all GPUs. + + Args: + dataset (Dataset): A PyTorch dataset. + samples_per_gpu (int): Number of training samples on each GPU, i.e., + batch size of each GPU. + workers_per_gpu (int): How many subprocesses to use for data loading + for each GPU. + num_gpus (int): Number of GPUs. Only used in non-distributed training. + dist (bool): Distributed training/test or not. Default: True. + shuffle (bool): Whether to shuffle the data at every epoch. + Default: True. + seed (int | None): Seed to be used. Default: None. + drop_last (bool): Whether to drop the last incomplete batch in epoch. + Default: False + pin_memory (bool): Whether to use pin_memory in DataLoader. + Default: True + dataloader_type (str): Type of dataloader. Default: 'PoolDataLoader' + kwargs: any keyword argument to be used to initialize DataLoader + + Returns: + DataLoader: A PyTorch dataloader. + """ + rank, world_size = get_dist_info() + if dist: + sampler = DistributedSampler( + dataset, world_size, rank, shuffle=shuffle) + shuffle = False + batch_size = samples_per_gpu + num_workers = workers_per_gpu + else: + sampler = None + batch_size = num_gpus * samples_per_gpu + num_workers = num_gpus * workers_per_gpu + + init_fn = partial( + worker_init_fn, num_workers=num_workers, rank=rank, + seed=seed) if seed is not None else None + + assert dataloader_type in ( + 'DataLoader', + 'PoolDataLoader'), f'unsupported dataloader {dataloader_type}' + + if dataloader_type == 'PoolDataLoader': + dataloader = PoolDataLoader + elif dataloader_type == 'DataLoader': + dataloader = DataLoader + + data_loader = dataloader( + dataset, + batch_size=batch_size, + sampler=sampler, + num_workers=num_workers, + collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), + pin_memory=pin_memory, + shuffle=shuffle, + worker_init_fn=init_fn, + drop_last=drop_last, + **kwargs) + + return data_loader + + +def worker_init_fn(worker_id, num_workers, rank, seed): + """Worker init func for dataloader. + + The seed of each worker equals to num_worker * rank + worker_id + user_seed + + Args: + worker_id (int): Worker id. + num_workers (int): Number of workers. + rank (int): The rank of current process. + seed (int): The random seed to use. + """ + + worker_seed = num_workers * rank + worker_id + seed + np.random.seed(worker_seed) + random.seed(worker_seed) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/chase_db1.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/chase_db1.py new file mode 100644 index 0000000000000000000000000000000000000000..8bc29bea14704a4407f83474610cbc3bef32c708 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/chase_db1.py @@ -0,0 +1,27 @@ +import os.path as osp + +from .builder import DATASETS +from .custom import CustomDataset + + +@DATASETS.register_module() +class ChaseDB1Dataset(CustomDataset): + """Chase_db1 dataset. + + In segmentation map annotation for Chase_db1, 0 stands for background, + which is included in 2 categories. ``reduce_zero_label`` is fixed to False. + The ``img_suffix`` is fixed to '.png' and ``seg_map_suffix`` is fixed to + '_1stHO.png'. + """ + + CLASSES = ('background', 'vessel') + + PALETTE = [[120, 120, 120], [6, 230, 230]] + + def __init__(self, **kwargs): + super(ChaseDB1Dataset, self).__init__( + img_suffix='.png', + seg_map_suffix='_1stHO.png', + reduce_zero_label=False, + **kwargs) + assert osp.exists(self.img_dir) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/cityscapes.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/cityscapes.py new file mode 100644 index 0000000000000000000000000000000000000000..38f80e8043d25178cf5dac18911241c74be4e3ac --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/cityscapes.py @@ -0,0 +1,217 @@ +import os.path as osp +import tempfile + +import annotator.mmpkg.mmcv as mmcv +import numpy as np +from annotator.mmpkg.mmcv.utils import print_log +from PIL import Image + +from .builder import DATASETS +from .custom import CustomDataset + + +@DATASETS.register_module() +class CityscapesDataset(CustomDataset): + """Cityscapes dataset. + + The ``img_suffix`` is fixed to '_leftImg8bit.png' and ``seg_map_suffix`` is + fixed to '_gtFine_labelTrainIds.png' for Cityscapes dataset. + """ + + CLASSES = ('road', 'sidewalk', 'building', 'wall', 'fence', 'pole', + 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', + 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', + 'bicycle') + + PALETTE = [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], + [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], + [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], + [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], + [0, 80, 100], [0, 0, 230], [119, 11, 32]] + + def __init__(self, **kwargs): + super(CityscapesDataset, self).__init__( + img_suffix='_leftImg8bit.png', + seg_map_suffix='_gtFine_labelTrainIds.png', + **kwargs) + + @staticmethod + def _convert_to_label_id(result): + """Convert trainId to id for cityscapes.""" + if isinstance(result, str): + result = np.load(result) + import cityscapesscripts.helpers.labels as CSLabels + result_copy = result.copy() + for trainId, label in CSLabels.trainId2label.items(): + result_copy[result == trainId] = label.id + + return result_copy + + def results2img(self, results, imgfile_prefix, to_label_id): + """Write the segmentation results to images. + + Args: + results (list[list | tuple | ndarray]): Testing results of the + dataset. + imgfile_prefix (str): The filename prefix of the png files. + If the prefix is "somepath/xxx", + the png files will be named "somepath/xxx.png". + to_label_id (bool): whether convert output to label_id for + submission + + Returns: + list[str: str]: result txt files which contains corresponding + semantic segmentation images. + """ + mmcv.mkdir_or_exist(imgfile_prefix) + result_files = [] + prog_bar = mmcv.ProgressBar(len(self)) + for idx in range(len(self)): + result = results[idx] + if to_label_id: + result = self._convert_to_label_id(result) + filename = self.img_infos[idx]['filename'] + basename = osp.splitext(osp.basename(filename))[0] + + png_filename = osp.join(imgfile_prefix, f'{basename}.png') + + output = Image.fromarray(result.astype(np.uint8)).convert('P') + import cityscapesscripts.helpers.labels as CSLabels + palette = np.zeros((len(CSLabels.id2label), 3), dtype=np.uint8) + for label_id, label in CSLabels.id2label.items(): + palette[label_id] = label.color + + output.putpalette(palette) + output.save(png_filename) + result_files.append(png_filename) + prog_bar.update() + + return result_files + + def format_results(self, results, imgfile_prefix=None, to_label_id=True): + """Format the results into dir (standard format for Cityscapes + evaluation). + + Args: + results (list): Testing results of the dataset. + imgfile_prefix (str | None): The prefix of images files. It + includes the file path and the prefix of filename, e.g., + "a/b/prefix". If not specified, a temp file will be created. + Default: None. + to_label_id (bool): whether convert output to label_id for + submission. Default: False + + Returns: + tuple: (result_files, tmp_dir), result_files is a list containing + the image paths, tmp_dir is the temporal directory created + for saving json/png files when img_prefix is not specified. + """ + + assert isinstance(results, list), 'results must be a list' + assert len(results) == len(self), ( + 'The length of results is not equal to the dataset len: ' + f'{len(results)} != {len(self)}') + + if imgfile_prefix is None: + tmp_dir = tempfile.TemporaryDirectory() + imgfile_prefix = tmp_dir.name + else: + tmp_dir = None + result_files = self.results2img(results, imgfile_prefix, to_label_id) + + return result_files, tmp_dir + + def evaluate(self, + results, + metric='mIoU', + logger=None, + imgfile_prefix=None, + efficient_test=False): + """Evaluation in Cityscapes/default protocol. + + Args: + results (list): Testing results of the dataset. + metric (str | list[str]): Metrics to be evaluated. + logger (logging.Logger | None | str): Logger used for printing + related information during evaluation. Default: None. + imgfile_prefix (str | None): The prefix of output image file, + for cityscapes evaluation only. It includes the file path and + the prefix of filename, e.g., "a/b/prefix". + If results are evaluated with cityscapes protocol, it would be + the prefix of output png files. The output files would be + png images under folder "a/b/prefix/xxx.png", where "xxx" is + the image name of cityscapes. If not specified, a temp file + will be created for evaluation. + Default: None. + + Returns: + dict[str, float]: Cityscapes/default metrics. + """ + + eval_results = dict() + metrics = metric.copy() if isinstance(metric, list) else [metric] + if 'cityscapes' in metrics: + eval_results.update( + self._evaluate_cityscapes(results, logger, imgfile_prefix)) + metrics.remove('cityscapes') + if len(metrics) > 0: + eval_results.update( + super(CityscapesDataset, + self).evaluate(results, metrics, logger, efficient_test)) + + return eval_results + + def _evaluate_cityscapes(self, results, logger, imgfile_prefix): + """Evaluation in Cityscapes protocol. + + Args: + results (list): Testing results of the dataset. + logger (logging.Logger | str | None): Logger used for printing + related information during evaluation. Default: None. + imgfile_prefix (str | None): The prefix of output image file + + Returns: + dict[str: float]: Cityscapes evaluation results. + """ + try: + import cityscapesscripts.evaluation.evalPixelLevelSemanticLabeling as CSEval # noqa + except ImportError: + raise ImportError('Please run "pip install cityscapesscripts" to ' + 'install cityscapesscripts first.') + msg = 'Evaluating in Cityscapes style' + if logger is None: + msg = '\n' + msg + print_log(msg, logger=logger) + + result_files, tmp_dir = self.format_results(results, imgfile_prefix) + + if tmp_dir is None: + result_dir = imgfile_prefix + else: + result_dir = tmp_dir.name + + eval_results = dict() + print_log(f'Evaluating results under {result_dir} ...', logger=logger) + + CSEval.args.evalInstLevelScore = True + CSEval.args.predictionPath = osp.abspath(result_dir) + CSEval.args.evalPixelAccuracy = True + CSEval.args.JSONOutput = False + + seg_map_list = [] + pred_list = [] + + # when evaluating with official cityscapesscripts, + # **_gtFine_labelIds.png is used + for seg_map in mmcv.scandir( + self.ann_dir, 'gtFine_labelIds.png', recursive=True): + seg_map_list.append(osp.join(self.ann_dir, seg_map)) + pred_list.append(CSEval.getPrediction(CSEval.args, seg_map)) + + eval_results.update( + CSEval.evaluateImgLists(pred_list, seg_map_list, CSEval.args)) + + if tmp_dir is not None: + tmp_dir.cleanup() + + return eval_results diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/custom.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/custom.py new file mode 100644 index 0000000000000000000000000000000000000000..3a626976c7fa88c3d1c1e871ef621422acc1be83 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/custom.py @@ -0,0 +1,403 @@ +import os +import os.path as osp +from collections import OrderedDict +from functools import reduce + +import annotator.mmpkg.mmcv as mmcv +import numpy as np +from annotator.mmpkg.mmcv.utils import print_log +from torch.utils.data import Dataset + +from annotator.mmpkg.mmseg.core import eval_metrics +from annotator.mmpkg.mmseg.utils import get_root_logger +from .builder import DATASETS +from .pipelines import Compose + + +@DATASETS.register_module() +class CustomDataset(Dataset): + """Custom dataset for semantic segmentation. An example of file structure + is as followed. + + .. code-block:: none + + ├── data + │ ├── my_dataset + │ │ ├── img_dir + │ │ │ ├── train + │ │ │ │ ├── xxx{img_suffix} + │ │ │ │ ├── yyy{img_suffix} + │ │ │ │ ├── zzz{img_suffix} + │ │ │ ├── val + │ │ ├── ann_dir + │ │ │ ├── train + │ │ │ │ ├── xxx{seg_map_suffix} + │ │ │ │ ├── yyy{seg_map_suffix} + │ │ │ │ ├── zzz{seg_map_suffix} + │ │ │ ├── val + + The img/gt_semantic_seg pair of CustomDataset should be of the same + except suffix. A valid img/gt_semantic_seg filename pair should be like + ``xxx{img_suffix}`` and ``xxx{seg_map_suffix}`` (extension is also included + in the suffix). If split is given, then ``xxx`` is specified in txt file. + Otherwise, all files in ``img_dir/``and ``ann_dir`` will be loaded. + Please refer to ``docs/tutorials/new_dataset.md`` for more details. + + + Args: + pipeline (list[dict]): Processing pipeline + img_dir (str): Path to image directory + img_suffix (str): Suffix of images. Default: '.jpg' + ann_dir (str, optional): Path to annotation directory. Default: None + seg_map_suffix (str): Suffix of segmentation maps. Default: '.png' + split (str, optional): Split txt file. If split is specified, only + file with suffix in the splits will be loaded. Otherwise, all + images in img_dir/ann_dir will be loaded. Default: None + data_root (str, optional): Data root for img_dir/ann_dir. Default: + None. + test_mode (bool): If test_mode=True, gt wouldn't be loaded. + ignore_index (int): The label index to be ignored. Default: 255 + reduce_zero_label (bool): Whether to mark label zero as ignored. + Default: False + classes (str | Sequence[str], optional): Specify classes to load. + If is None, ``cls.CLASSES`` will be used. Default: None. + palette (Sequence[Sequence[int]]] | np.ndarray | None): + The palette of segmentation map. If None is given, and + self.PALETTE is None, random palette will be generated. + Default: None + """ + + CLASSES = None + + PALETTE = None + + def __init__(self, + pipeline, + img_dir, + img_suffix='.jpg', + ann_dir=None, + seg_map_suffix='.png', + split=None, + data_root=None, + test_mode=False, + ignore_index=255, + reduce_zero_label=False, + classes=None, + palette=None): + self.pipeline = Compose(pipeline) + self.img_dir = img_dir + self.img_suffix = img_suffix + self.ann_dir = ann_dir + self.seg_map_suffix = seg_map_suffix + self.split = split + self.data_root = data_root + self.test_mode = test_mode + self.ignore_index = ignore_index + self.reduce_zero_label = reduce_zero_label + self.label_map = None + self.CLASSES, self.PALETTE = self.get_classes_and_palette( + classes, palette) + + # join paths if data_root is specified + if self.data_root is not None: + if not osp.isabs(self.img_dir): + self.img_dir = osp.join(self.data_root, self.img_dir) + if not (self.ann_dir is None or osp.isabs(self.ann_dir)): + self.ann_dir = osp.join(self.data_root, self.ann_dir) + if not (self.split is None or osp.isabs(self.split)): + self.split = osp.join(self.data_root, self.split) + + # load annotations + self.img_infos = self.load_annotations(self.img_dir, self.img_suffix, + self.ann_dir, + self.seg_map_suffix, self.split) + + def __len__(self): + """Total number of samples of data.""" + return len(self.img_infos) + + def load_annotations(self, img_dir, img_suffix, ann_dir, seg_map_suffix, + split): + """Load annotation from directory. + + Args: + img_dir (str): Path to image directory + img_suffix (str): Suffix of images. + ann_dir (str|None): Path to annotation directory. + seg_map_suffix (str|None): Suffix of segmentation maps. + split (str|None): Split txt file. If split is specified, only file + with suffix in the splits will be loaded. Otherwise, all images + in img_dir/ann_dir will be loaded. Default: None + + Returns: + list[dict]: All image info of dataset. + """ + + img_infos = [] + if split is not None: + with open(split) as f: + for line in f: + img_name = line.strip() + img_info = dict(filename=img_name + img_suffix) + if ann_dir is not None: + seg_map = img_name + seg_map_suffix + img_info['ann'] = dict(seg_map=seg_map) + img_infos.append(img_info) + else: + for img in mmcv.scandir(img_dir, img_suffix, recursive=True): + img_info = dict(filename=img) + if ann_dir is not None: + seg_map = img.replace(img_suffix, seg_map_suffix) + img_info['ann'] = dict(seg_map=seg_map) + img_infos.append(img_info) + + print_log(f'Loaded {len(img_infos)} images', logger=get_root_logger()) + return img_infos + + def get_ann_info(self, idx): + """Get annotation by index. + + Args: + idx (int): Index of data. + + Returns: + dict: Annotation info of specified index. + """ + + return self.img_infos[idx]['ann'] + + def pre_pipeline(self, results): + """Prepare results dict for pipeline.""" + results['seg_fields'] = [] + results['img_prefix'] = self.img_dir + results['seg_prefix'] = self.ann_dir + if self.custom_classes: + results['label_map'] = self.label_map + + def __getitem__(self, idx): + """Get training/test data after pipeline. + + Args: + idx (int): Index of data. + + Returns: + dict: Training/test data (with annotation if `test_mode` is set + False). + """ + + if self.test_mode: + return self.prepare_test_img(idx) + else: + return self.prepare_train_img(idx) + + def prepare_train_img(self, idx): + """Get training data and annotations after pipeline. + + Args: + idx (int): Index of data. + + Returns: + dict: Training data and annotation after pipeline with new keys + introduced by pipeline. + """ + + img_info = self.img_infos[idx] + ann_info = self.get_ann_info(idx) + results = dict(img_info=img_info, ann_info=ann_info) + self.pre_pipeline(results) + return self.pipeline(results) + + def prepare_test_img(self, idx): + """Get testing data after pipeline. + + Args: + idx (int): Index of data. + + Returns: + dict: Testing data after pipeline with new keys introduced by + pipeline. + """ + + img_info = self.img_infos[idx] + results = dict(img_info=img_info) + self.pre_pipeline(results) + return self.pipeline(results) + + def format_results(self, results, **kwargs): + """Place holder to format result to dataset specific output.""" + + def get_gt_seg_maps(self, efficient_test=False): + """Get ground truth segmentation maps for evaluation.""" + gt_seg_maps = [] + for img_info in self.img_infos: + seg_map = osp.join(self.ann_dir, img_info['ann']['seg_map']) + if efficient_test: + gt_seg_map = seg_map + else: + gt_seg_map = mmcv.imread( + seg_map, flag='unchanged', backend='pillow') + gt_seg_maps.append(gt_seg_map) + return gt_seg_maps + + def get_classes_and_palette(self, classes=None, palette=None): + """Get class names of current dataset. + + Args: + classes (Sequence[str] | str | None): If classes is None, use + default CLASSES defined by builtin dataset. If classes is a + string, take it as a file name. The file contains the name of + classes where each line contains one class name. If classes is + a tuple or list, override the CLASSES defined by the dataset. + palette (Sequence[Sequence[int]]] | np.ndarray | None): + The palette of segmentation map. If None is given, random + palette will be generated. Default: None + """ + if classes is None: + self.custom_classes = False + return self.CLASSES, self.PALETTE + + self.custom_classes = True + if isinstance(classes, str): + # take it as a file path + class_names = mmcv.list_from_file(classes) + elif isinstance(classes, (tuple, list)): + class_names = classes + else: + raise ValueError(f'Unsupported type {type(classes)} of classes.') + + if self.CLASSES: + if not set(classes).issubset(self.CLASSES): + raise ValueError('classes is not a subset of CLASSES.') + + # dictionary, its keys are the old label ids and its values + # are the new label ids. + # used for changing pixel labels in load_annotations. + self.label_map = {} + for i, c in enumerate(self.CLASSES): + if c not in class_names: + self.label_map[i] = -1 + else: + self.label_map[i] = classes.index(c) + + palette = self.get_palette_for_custom_classes(class_names, palette) + + return class_names, palette + + def get_palette_for_custom_classes(self, class_names, palette=None): + + if self.label_map is not None: + # return subset of palette + palette = [] + for old_id, new_id in sorted( + self.label_map.items(), key=lambda x: x[1]): + if new_id != -1: + palette.append(self.PALETTE[old_id]) + palette = type(self.PALETTE)(palette) + + elif palette is None: + if self.PALETTE is None: + palette = np.random.randint(0, 255, size=(len(class_names), 3)) + else: + palette = self.PALETTE + + return palette + + def evaluate(self, + results, + metric='mIoU', + logger=None, + efficient_test=False, + **kwargs): + """Evaluate the dataset. + + Args: + results (list): Testing results of the dataset. + metric (str | list[str]): Metrics to be evaluated. 'mIoU', + 'mDice' and 'mFscore' are supported. + logger (logging.Logger | None | str): Logger used for printing + related information during evaluation. Default: None. + + Returns: + dict[str, float]: Default metrics. + """ + + if isinstance(metric, str): + metric = [metric] + allowed_metrics = ['mIoU', 'mDice', 'mFscore'] + if not set(metric).issubset(set(allowed_metrics)): + raise KeyError('metric {} is not supported'.format(metric)) + eval_results = {} + gt_seg_maps = self.get_gt_seg_maps(efficient_test) + if self.CLASSES is None: + num_classes = len( + reduce(np.union1d, [np.unique(_) for _ in gt_seg_maps])) + else: + num_classes = len(self.CLASSES) + ret_metrics = eval_metrics( + results, + gt_seg_maps, + num_classes, + self.ignore_index, + metric, + label_map=self.label_map, + reduce_zero_label=self.reduce_zero_label) + + if self.CLASSES is None: + class_names = tuple(range(num_classes)) + else: + class_names = self.CLASSES + + # summary table + ret_metrics_summary = OrderedDict({ + ret_metric: np.round(np.nanmean(ret_metric_value) * 100, 2) + for ret_metric, ret_metric_value in ret_metrics.items() + }) + + # each class table + ret_metrics.pop('aAcc', None) + ret_metrics_class = OrderedDict({ + ret_metric: np.round(ret_metric_value * 100, 2) + for ret_metric, ret_metric_value in ret_metrics.items() + }) + ret_metrics_class.update({'Class': class_names}) + ret_metrics_class.move_to_end('Class', last=False) + + try: + from prettytable import PrettyTable + # for logger + class_table_data = PrettyTable() + for key, val in ret_metrics_class.items(): + class_table_data.add_column(key, val) + + summary_table_data = PrettyTable() + for key, val in ret_metrics_summary.items(): + if key == 'aAcc': + summary_table_data.add_column(key, [val]) + else: + summary_table_data.add_column('m' + key, [val]) + + print_log('per class results:', logger) + print_log('\n' + class_table_data.get_string(), logger=logger) + print_log('Summary:', logger) + print_log('\n' + summary_table_data.get_string(), logger=logger) + except ImportError: # prettytable is not installed + pass + + # each metric dict + for key, value in ret_metrics_summary.items(): + if key == 'aAcc': + eval_results[key] = value / 100.0 + else: + eval_results['m' + key] = value / 100.0 + + ret_metrics_class.pop('Class', None) + for key, value in ret_metrics_class.items(): + eval_results.update({ + key + '.' + str(name): value[idx] / 100.0 + for idx, name in enumerate(class_names) + }) + + if mmcv.is_list_of(results, str): + for file_name in results: + os.remove(file_name) + return eval_results diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/dataset_wrappers.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/dataset_wrappers.py new file mode 100644 index 0000000000000000000000000000000000000000..d6a5e957ec3b44465432617cf6e8f0b86a8a5efa --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/dataset_wrappers.py @@ -0,0 +1,50 @@ +from torch.utils.data.dataset import ConcatDataset as _ConcatDataset + +from .builder import DATASETS + + +@DATASETS.register_module() +class ConcatDataset(_ConcatDataset): + """A wrapper of concatenated dataset. + + Same as :obj:`torch.utils.data.dataset.ConcatDataset`, but + concat the group flag for image aspect ratio. + + Args: + datasets (list[:obj:`Dataset`]): A list of datasets. + """ + + def __init__(self, datasets): + super(ConcatDataset, self).__init__(datasets) + self.CLASSES = datasets[0].CLASSES + self.PALETTE = datasets[0].PALETTE + + +@DATASETS.register_module() +class RepeatDataset(object): + """A wrapper of repeated dataset. + + The length of repeated dataset will be `times` larger than the original + dataset. This is useful when the data loading time is long but the dataset + is small. Using RepeatDataset can reduce the data loading time between + epochs. + + Args: + dataset (:obj:`Dataset`): The dataset to be repeated. + times (int): Repeat times. + """ + + def __init__(self, dataset, times): + self.dataset = dataset + self.times = times + self.CLASSES = dataset.CLASSES + self.PALETTE = dataset.PALETTE + self._ori_len = len(self.dataset) + + def __getitem__(self, idx): + """Get item from original dataset.""" + return self.dataset[idx % self._ori_len] + + def __len__(self): + """The length is multiplied by ``times``""" + return self.times * self._ori_len diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/drive.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/drive.py new file mode 100644 index 0000000000000000000000000000000000000000..3cbfda8ae74bdf26c5aef197ff2866a7c7ad0cfd --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/drive.py @@ -0,0 +1,27 @@ +import os.path as osp + +from .builder import DATASETS +from .custom import CustomDataset + + +@DATASETS.register_module() +class DRIVEDataset(CustomDataset): + """DRIVE dataset. + + In segmentation map annotation for DRIVE, 0 stands for background, which is + included in 2 categories. ``reduce_zero_label`` is fixed to False. The + ``img_suffix`` is fixed to '.png' and ``seg_map_suffix`` is fixed to + '_manual1.png'. + """ + + CLASSES = ('background', 'vessel') + + PALETTE = [[120, 120, 120], [6, 230, 230]] + + def __init__(self, **kwargs): + super(DRIVEDataset, self).__init__( + img_suffix='.png', + seg_map_suffix='_manual1.png', + reduce_zero_label=False, + **kwargs) + assert osp.exists(self.img_dir) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/hrf.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/hrf.py new file mode 100644 index 0000000000000000000000000000000000000000..923203b51377f9344277fc561803d7a78bd2c684 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/hrf.py @@ -0,0 +1,27 @@ +import os.path as osp + +from .builder import DATASETS +from .custom import CustomDataset + + +@DATASETS.register_module() +class HRFDataset(CustomDataset): + """HRF dataset. + + In segmentation map annotation for HRF, 0 stands for background, which is + included in 2 categories. ``reduce_zero_label`` is fixed to False. The + ``img_suffix`` is fixed to '.png' and ``seg_map_suffix`` is fixed to + '.png'. + """ + + CLASSES = ('background', 'vessel') + + PALETTE = [[120, 120, 120], [6, 230, 230]] + + def __init__(self, **kwargs): + super(HRFDataset, self).__init__( + img_suffix='.png', + seg_map_suffix='.png', + reduce_zero_label=False, + **kwargs) + assert osp.exists(self.img_dir) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pascal_context.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pascal_context.py new file mode 100644 index 0000000000000000000000000000000000000000..541a63c66a13fb16fd52921e755715ad8d078fdd --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pascal_context.py @@ -0,0 +1,103 @@ +import os.path as osp + +from .builder import DATASETS +from .custom import CustomDataset + + +@DATASETS.register_module() +class PascalContextDataset(CustomDataset): + """PascalContext dataset. + + In segmentation map annotation for PascalContext, 0 stands for background, + which is included in 60 categories. ``reduce_zero_label`` is fixed to + False. The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is + fixed to '.png'. + + Args: + split (str): Split txt file for PascalContext. + """ + + CLASSES = ('background', 'aeroplane', 'bag', 'bed', 'bedclothes', 'bench', + 'bicycle', 'bird', 'boat', 'book', 'bottle', 'building', 'bus', + 'cabinet', 'car', 'cat', 'ceiling', 'chair', 'cloth', + 'computer', 'cow', 'cup', 'curtain', 'dog', 'door', 'fence', + 'floor', 'flower', 'food', 'grass', 'ground', 'horse', + 'keyboard', 'light', 'motorbike', 'mountain', 'mouse', 'person', + 'plate', 'platform', 'pottedplant', 'road', 'rock', 'sheep', + 'shelves', 'sidewalk', 'sign', 'sky', 'snow', 'sofa', 'table', + 'track', 'train', 'tree', 'truck', 'tvmonitor', 'wall', 'water', + 'window', 'wood') + + PALETTE = [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], + [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], + [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], + [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], + [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], + [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], + [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], + [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], + [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], + [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], + [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], + [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], + [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], + [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], + [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255]] + + def __init__(self, split, **kwargs): + super(PascalContextDataset, self).__init__( + img_suffix='.jpg', + seg_map_suffix='.png', + split=split, + reduce_zero_label=False, + **kwargs) + assert osp.exists(self.img_dir) and self.split is not None + + +@DATASETS.register_module() +class PascalContextDataset59(CustomDataset): + """PascalContext dataset. + + In segmentation map annotation for PascalContext, 0 stands for background, + which is included in 60 categories. ``reduce_zero_label`` is fixed to + False. The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is + fixed to '.png'. + + Args: + split (str): Split txt file for PascalContext. + """ + + CLASSES = ('aeroplane', 'bag', 'bed', 'bedclothes', 'bench', 'bicycle', + 'bird', 'boat', 'book', 'bottle', 'building', 'bus', 'cabinet', + 'car', 'cat', 'ceiling', 'chair', 'cloth', 'computer', 'cow', + 'cup', 'curtain', 'dog', 'door', 'fence', 'floor', 'flower', + 'food', 'grass', 'ground', 'horse', 'keyboard', 'light', + 'motorbike', 'mountain', 'mouse', 'person', 'plate', 'platform', + 'pottedplant', 'road', 'rock', 'sheep', 'shelves', 'sidewalk', + 'sign', 'sky', 'snow', 'sofa', 'table', 'track', 'train', + 'tree', 'truck', 'tvmonitor', 'wall', 'water', 'window', 'wood') + + PALETTE = [[180, 120, 120], [6, 230, 230], [80, 50, 50], [4, 200, 3], + [120, 120, 80], [140, 140, 140], [204, 5, 255], [230, 230, 230], + [4, 250, 7], [224, 5, 255], [235, 255, 7], [150, 5, 61], + [120, 120, 70], [8, 255, 51], [255, 6, 82], [143, 255, 140], + [204, 255, 4], [255, 51, 7], [204, 70, 3], [0, 102, 200], + [61, 230, 250], [255, 6, 51], [11, 102, 255], [255, 7, 71], + [255, 9, 224], [9, 7, 230], [220, 220, 220], [255, 9, 92], + [112, 9, 255], [8, 255, 214], [7, 255, 224], [255, 184, 6], + [10, 255, 71], [255, 41, 10], [7, 255, 255], [224, 255, 8], + [102, 8, 255], [255, 61, 6], [255, 194, 7], [255, 122, 8], + [0, 255, 20], [255, 8, 41], [255, 5, 153], [6, 51, 255], + [235, 12, 255], [160, 150, 20], [0, 163, 255], [140, 140, 140], + [250, 10, 15], [20, 255, 0], [31, 255, 0], [255, 31, 0], + [255, 224, 0], [153, 255, 0], [0, 0, 255], [255, 71, 0], + [0, 235, 255], [0, 173, 255], [31, 0, 255]] + + def __init__(self, split, **kwargs): + super(PascalContextDataset59, self).__init__( + img_suffix='.jpg', + seg_map_suffix='.png', + split=split, + reduce_zero_label=True, + **kwargs) + assert osp.exists(self.img_dir) and self.split is not None diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8b9046b07bb4ddea7a707a392b42e72db7c9df67 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/__init__.py @@ -0,0 +1,16 @@ +from .compose import Compose +from .formating import (Collect, ImageToTensor, ToDataContainer, ToTensor, + Transpose, to_tensor) +from .loading import LoadAnnotations, LoadImageFromFile +from .test_time_aug import MultiScaleFlipAug +from .transforms import (CLAHE, AdjustGamma, Normalize, Pad, + PhotoMetricDistortion, RandomCrop, RandomFlip, + RandomRotate, Rerange, Resize, RGB2Gray, SegRescale) + +__all__ = [ + 'Compose', 'to_tensor', 'ToTensor', 'ImageToTensor', 'ToDataContainer', + 'Transpose', 'Collect', 'LoadAnnotations', 'LoadImageFromFile', + 'MultiScaleFlipAug', 'Resize', 'RandomFlip', 'Pad', 'RandomCrop', + 'Normalize', 'SegRescale', 'PhotoMetricDistortion', 'RandomRotate', + 'AdjustGamma', 'CLAHE', 'Rerange', 'RGB2Gray' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/compose.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/compose.py new file mode 100644 index 0000000000000000000000000000000000000000..1683e533237ce6420e4a53e477513853d6b33b3e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/compose.py @@ -0,0 +1,51 @@ +import collections + +from annotator.mmpkg.mmcv.utils import build_from_cfg + +from ..builder import PIPELINES + + +@PIPELINES.register_module() +class Compose(object): + """Compose multiple transforms sequentially. + + Args: + transforms (Sequence[dict | callable]): Sequence of transform object or + config dict to be composed. + """ + + def __init__(self, transforms): + assert isinstance(transforms, collections.abc.Sequence) + self.transforms = [] + for transform in transforms: + if isinstance(transform, dict): + transform = build_from_cfg(transform, PIPELINES) + self.transforms.append(transform) + elif callable(transform): + self.transforms.append(transform) + else: + raise TypeError('transform must be callable or a dict') + + def __call__(self, data): + """Call function to apply transforms sequentially. + + Args: + data (dict): A result dict contains the data to transform. + + Returns: + dict: Transformed data. + """ + + for t in self.transforms: + data = t(data) + if data is None: + return None + return data + + def __repr__(self): + format_string = self.__class__.__name__ + '(' + for t in self.transforms: + format_string += '\n' + format_string += f' {t}' + format_string += '\n)' + return format_string diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/formating.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/formating.py new file mode 100644 index 0000000000000000000000000000000000000000..82e2e08ff819506bb7a7693be189017d473e677f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/formating.py @@ -0,0 +1,288 @@ +from collections.abc import Sequence + +import annotator.mmpkg.mmcv as mmcv +import numpy as np +import torch +from annotator.mmpkg.mmcv.parallel import DataContainer as DC + +from ..builder import PIPELINES + + +def to_tensor(data): + """Convert objects of various python types to :obj:`torch.Tensor`. + + Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, + :class:`Sequence`, :class:`int` and :class:`float`. + + Args: + data (torch.Tensor | numpy.ndarray | Sequence | int | float): Data to + be converted. + """ + + if isinstance(data, torch.Tensor): + return data + elif isinstance(data, np.ndarray): + return torch.from_numpy(data) + elif isinstance(data, Sequence) and not mmcv.is_str(data): + return torch.tensor(data) + elif isinstance(data, int): + return torch.LongTensor([data]) + elif isinstance(data, float): + return torch.FloatTensor([data]) + else: + raise TypeError(f'type {type(data)} cannot be converted to tensor.') + + +@PIPELINES.register_module() +class ToTensor(object): + """Convert some results to :obj:`torch.Tensor` by given keys. + + Args: + keys (Sequence[str]): Keys that need to be converted to Tensor. + """ + + def __init__(self, keys): + self.keys = keys + + def __call__(self, results): + """Call function to convert data in results to :obj:`torch.Tensor`. + + Args: + results (dict): Result dict contains the data to convert. + + Returns: + dict: The result dict contains the data converted + to :obj:`torch.Tensor`. + """ + + for key in self.keys: + results[key] = to_tensor(results[key]) + return results + + def __repr__(self): + return self.__class__.__name__ + f'(keys={self.keys})' + + +@PIPELINES.register_module() +class ImageToTensor(object): + """Convert image to :obj:`torch.Tensor` by given keys. + + The dimension order of input image is (H, W, C). The pipeline will convert + it to (C, H, W). If only 2 dimension (H, W) is given, the output would be + (1, H, W). + + Args: + keys (Sequence[str]): Key of images to be converted to Tensor. + """ + + def __init__(self, keys): + self.keys = keys + + def __call__(self, results): + """Call function to convert image in results to :obj:`torch.Tensor` and + transpose the channel order. + + Args: + results (dict): Result dict contains the image data to convert. + + Returns: + dict: The result dict contains the image converted + to :obj:`torch.Tensor` and transposed to (C, H, W) order. + """ + + for key in self.keys: + img = results[key] + if len(img.shape) < 3: + img = np.expand_dims(img, -1) + results[key] = to_tensor(img.transpose(2, 0, 1)) + return results + + def __repr__(self): + return self.__class__.__name__ + f'(keys={self.keys})' + + +@PIPELINES.register_module() +class Transpose(object): + """Transpose some results by given keys. + + Args: + keys (Sequence[str]): Keys of results to be transposed. + order (Sequence[int]): Order of transpose. + """ + + def __init__(self, keys, order): + self.keys = keys + self.order = order + + def __call__(self, results): + """Call function to convert image in results to :obj:`torch.Tensor` and + transpose the channel order. + + Args: + results (dict): Result dict contains the image data to convert. + + Returns: + dict: The result dict contains the image converted + to :obj:`torch.Tensor` and transposed to (C, H, W) order. + """ + + for key in self.keys: + results[key] = results[key].transpose(self.order) + return results + + def __repr__(self): + return self.__class__.__name__ + \ + f'(keys={self.keys}, order={self.order})' + + +@PIPELINES.register_module() +class ToDataContainer(object): + """Convert results to :obj:`mmcv.DataContainer` by given fields. + + Args: + fields (Sequence[dict]): Each field is a dict like + ``dict(key='xxx', **kwargs)``. The ``key`` in result will + be converted to :obj:`mmcv.DataContainer` with ``**kwargs``. + Default: ``(dict(key='img', stack=True), + dict(key='gt_semantic_seg'))``. + """ + + def __init__(self, + fields=(dict(key='img', + stack=True), dict(key='gt_semantic_seg'))): + self.fields = fields + + def __call__(self, results): + """Call function to convert data in results to + :obj:`mmcv.DataContainer`. + + Args: + results (dict): Result dict contains the data to convert. + + Returns: + dict: The result dict contains the data converted to + :obj:`mmcv.DataContainer`. + """ + + for field in self.fields: + field = field.copy() + key = field.pop('key') + results[key] = DC(results[key], **field) + return results + + def __repr__(self): + return self.__class__.__name__ + f'(fields={self.fields})' + + +@PIPELINES.register_module() +class DefaultFormatBundle(object): + """Default formatting bundle. + + It simplifies the pipeline of formatting common fields, including "img" + and "gt_semantic_seg". These fields are formatted as follows. + + - img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True) + - gt_semantic_seg: (1)unsqueeze dim-0 (2)to tensor, + (3)to DataContainer (stack=True) + """ + + def __call__(self, results): + """Call function to transform and format common fields in results. + + Args: + results (dict): Result dict contains the data to convert. + + Returns: + dict: The result dict contains the data that is formatted with + default bundle. + """ + + if 'img' in results: + img = results['img'] + if len(img.shape) < 3: + img = np.expand_dims(img, -1) + img = np.ascontiguousarray(img.transpose(2, 0, 1)) + results['img'] = DC(to_tensor(img), stack=True) + if 'gt_semantic_seg' in results: + # convert to long + results['gt_semantic_seg'] = DC( + to_tensor(results['gt_semantic_seg'][None, + ...].astype(np.int64)), + stack=True) + return results + + def __repr__(self): + return self.__class__.__name__ + + +@PIPELINES.register_module() +class Collect(object): + """Collect data from the loader relevant to the specific task. + + This is usually the last stage of the data loader pipeline. Typically keys + is set to some subset of "img", "gt_semantic_seg". + + The "img_meta" item is always populated. The contents of the "img_meta" + dictionary depends on "meta_keys". By default this includes: + + - "img_shape": shape of the image input to the network as a tuple + (h, w, c). Note that images may be zero padded on the bottom/right + if the batch tensor is larger than this shape. + + - "scale_factor": a float indicating the preprocessing scale + + - "flip": a boolean indicating if image flip transform was used + + - "filename": path to the image file + + - "ori_shape": original shape of the image as a tuple (h, w, c) + + - "pad_shape": image shape after padding + + - "img_norm_cfg": a dict of normalization information: + - mean - per channel mean subtraction + - std - per channel std divisor + - to_rgb - bool indicating if bgr was converted to rgb + + Args: + keys (Sequence[str]): Keys of results to be collected in ``data``. + meta_keys (Sequence[str], optional): Meta keys to be converted to + ``mmcv.DataContainer`` and collected in ``data[img_metas]``. + Default: ``('filename', 'ori_filename', 'ori_shape', 'img_shape', + 'pad_shape', 'scale_factor', 'flip', 'flip_direction', + 'img_norm_cfg')`` + """ + + def __init__(self, + keys, + meta_keys=('filename', 'ori_filename', 'ori_shape', + 'img_shape', 'pad_shape', 'scale_factor', 'flip', + 'flip_direction', 'img_norm_cfg')): + self.keys = keys + self.meta_keys = meta_keys + + def __call__(self, results): + """Call function to collect keys in results. The keys in ``meta_keys`` + will be converted to :obj:mmcv.DataContainer. + + Args: + results (dict): Result dict contains the data to collect. + + Returns: + dict: The result dict contains the following keys + - keys in``self.keys`` + - ``img_metas`` + """ + + data = {} + img_meta = {} + for key in self.meta_keys: + img_meta[key] = results[key] + data['img_metas'] = DC(img_meta, cpu_only=True) + for key in self.keys: + data[key] = results[key] + return data + + def __repr__(self): + return self.__class__.__name__ + \ + f'(keys={self.keys}, meta_keys={self.meta_keys})' diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/loading.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/loading.py new file mode 100644 index 0000000000000000000000000000000000000000..3ad8c2cb67cb1d2b593217fb1fb2e0ca5834c24f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/loading.py @@ -0,0 +1,153 @@ +import os.path as osp + +import annotator.mmpkg.mmcv as mmcv +import numpy as np + +from ..builder import PIPELINES + + +@PIPELINES.register_module() +class LoadImageFromFile(object): + """Load an image from file. + + Required keys are "img_prefix" and "img_info" (a dict that must contain the + key "filename"). Added or updated keys are "filename", "img", "img_shape", + "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`), + "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1). + + Args: + to_float32 (bool): Whether to convert the loaded image to a float32 + numpy array. If set to False, the loaded image is an uint8 array. + Defaults to False. + color_type (str): The flag argument for :func:`mmcv.imfrombytes`. + Defaults to 'color'. + file_client_args (dict): Arguments to instantiate a FileClient. + See :class:`mmcv.fileio.FileClient` for details. + Defaults to ``dict(backend='disk')``. + imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: + 'cv2' + """ + + def __init__(self, + to_float32=False, + color_type='color', + file_client_args=dict(backend='disk'), + imdecode_backend='cv2'): + self.to_float32 = to_float32 + self.color_type = color_type + self.file_client_args = file_client_args.copy() + self.file_client = None + self.imdecode_backend = imdecode_backend + + def __call__(self, results): + """Call functions to load image and get image meta information. + + Args: + results (dict): Result dict from :obj:`mmseg.CustomDataset`. + + Returns: + dict: The dict contains loaded image and meta information. + """ + + if self.file_client is None: + self.file_client = mmcv.FileClient(**self.file_client_args) + + if results.get('img_prefix') is not None: + filename = osp.join(results['img_prefix'], + results['img_info']['filename']) + else: + filename = results['img_info']['filename'] + img_bytes = self.file_client.get(filename) + img = mmcv.imfrombytes( + img_bytes, flag=self.color_type, backend=self.imdecode_backend) + if self.to_float32: + img = img.astype(np.float32) + + results['filename'] = filename + results['ori_filename'] = results['img_info']['filename'] + results['img'] = img + results['img_shape'] = img.shape + results['ori_shape'] = img.shape + # Set initial values for default meta_keys + results['pad_shape'] = img.shape + results['scale_factor'] = 1.0 + num_channels = 1 if len(img.shape) < 3 else img.shape[2] + results['img_norm_cfg'] = dict( + mean=np.zeros(num_channels, dtype=np.float32), + std=np.ones(num_channels, dtype=np.float32), + to_rgb=False) + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + repr_str += f'(to_float32={self.to_float32},' + repr_str += f"color_type='{self.color_type}'," + repr_str += f"imdecode_backend='{self.imdecode_backend}')" + return repr_str + + +@PIPELINES.register_module() +class LoadAnnotations(object): + """Load annotations for semantic segmentation. + + Args: + reduce_zero_label (bool): Whether reduce all label value by 1. + Usually used for datasets where 0 is background label. + Default: False. + file_client_args (dict): Arguments to instantiate a FileClient. + See :class:`mmcv.fileio.FileClient` for details. + Defaults to ``dict(backend='disk')``. + imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: + 'pillow' + """ + + def __init__(self, + reduce_zero_label=False, + file_client_args=dict(backend='disk'), + imdecode_backend='pillow'): + self.reduce_zero_label = reduce_zero_label + self.file_client_args = file_client_args.copy() + self.file_client = None + self.imdecode_backend = imdecode_backend + + def __call__(self, results): + """Call function to load multiple types annotations. + + Args: + results (dict): Result dict from :obj:`mmseg.CustomDataset`. + + Returns: + dict: The dict contains loaded semantic segmentation annotations. + """ + + if self.file_client is None: + self.file_client = mmcv.FileClient(**self.file_client_args) + + if results.get('seg_prefix', None) is not None: + filename = osp.join(results['seg_prefix'], + results['ann_info']['seg_map']) + else: + filename = results['ann_info']['seg_map'] + img_bytes = self.file_client.get(filename) + gt_semantic_seg = mmcv.imfrombytes( + img_bytes, flag='unchanged', + backend=self.imdecode_backend).squeeze().astype(np.uint8) + # modify if custom classes + if results.get('label_map', None) is not None: + for old_id, new_id in results['label_map'].items(): + gt_semantic_seg[gt_semantic_seg == old_id] = new_id + # reduce zero_label + if self.reduce_zero_label: + # avoid using underflow conversion + gt_semantic_seg[gt_semantic_seg == 0] = 255 + gt_semantic_seg = gt_semantic_seg - 1 + gt_semantic_seg[gt_semantic_seg == 254] = 255 + results['gt_semantic_seg'] = gt_semantic_seg + results['seg_fields'].append('gt_semantic_seg') + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + repr_str += f'(reduce_zero_label={self.reduce_zero_label},' + repr_str += f"imdecode_backend='{self.imdecode_backend}')" + return repr_str diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/test_time_aug.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/test_time_aug.py new file mode 100644 index 0000000000000000000000000000000000000000..fb781d928ed71aceb1abcaef44d3889c00d2261e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/test_time_aug.py @@ -0,0 +1,133 @@ +import warnings + +import annotator.mmpkg.mmcv as mmcv + +from ..builder import PIPELINES +from .compose import Compose + + +@PIPELINES.register_module() +class MultiScaleFlipAug(object): + """Test-time augmentation with multiple scales and flipping. + + An example configuration is as followed: + + .. code-block:: + + img_scale=(2048, 1024), + img_ratios=[0.5, 1.0], + flip=True, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size_divisor=32), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']), + ] + + After MultiScaleFLipAug with above configuration, the results are wrapped + into lists of the same length as followed: + + .. code-block:: + + dict( + img=[...], + img_shape=[...], + scale=[(1024, 512), (1024, 512), (2048, 1024), (2048, 1024)] + flip=[False, True, False, True] + ... + ) + + Args: + transforms (list[dict]): Transforms to apply in each augmentation. + img_scale (None | tuple | list[tuple]): Images scales for resizing. + img_ratios (float | list[float]): Image ratios for resizing + flip (bool): Whether apply flip augmentation. Default: False. + flip_direction (str | list[str]): Flip augmentation directions, + options are "horizontal" and "vertical". If flip_direction is list, + multiple flip augmentations will be applied. + It has no effect when flip == False. Default: "horizontal". + """ + + def __init__(self, + transforms, + img_scale, + img_ratios=None, + flip=False, + flip_direction='horizontal'): + self.transforms = Compose(transforms) + if img_ratios is not None: + img_ratios = img_ratios if isinstance(img_ratios, + list) else [img_ratios] + assert mmcv.is_list_of(img_ratios, float) + if img_scale is None: + # mode 1: given img_scale=None and a range of image ratio + self.img_scale = None + assert mmcv.is_list_of(img_ratios, float) + elif isinstance(img_scale, tuple) and mmcv.is_list_of( + img_ratios, float): + assert len(img_scale) == 2 + # mode 2: given a scale and a range of image ratio + self.img_scale = [(int(img_scale[0] * ratio), + int(img_scale[1] * ratio)) + for ratio in img_ratios] + else: + # mode 3: given multiple scales + self.img_scale = img_scale if isinstance(img_scale, + list) else [img_scale] + assert mmcv.is_list_of(self.img_scale, tuple) or self.img_scale is None + self.flip = flip + self.img_ratios = img_ratios + self.flip_direction = flip_direction if isinstance( + flip_direction, list) else [flip_direction] + assert mmcv.is_list_of(self.flip_direction, str) + if not self.flip and self.flip_direction != ['horizontal']: + warnings.warn( + 'flip_direction has no effect when flip is set to False') + if (self.flip + and not any([t['type'] == 'RandomFlip' for t in transforms])): + warnings.warn( + 'flip has no effect when RandomFlip is not in transforms') + + def __call__(self, results): + """Call function to apply test time augment transforms on results. + + Args: + results (dict): Result dict contains the data to transform. + + Returns: + dict[str: list]: The augmented data, where each value is wrapped + into a list. + """ + + aug_data = [] + if self.img_scale is None and mmcv.is_list_of(self.img_ratios, float): + h, w = results['img'].shape[:2] + img_scale = [(int(w * ratio), int(h * ratio)) + for ratio in self.img_ratios] + else: + img_scale = self.img_scale + flip_aug = [False, True] if self.flip else [False] + for scale in img_scale: + for flip in flip_aug: + for direction in self.flip_direction: + _results = results.copy() + _results['scale'] = scale + _results['flip'] = flip + _results['flip_direction'] = direction + data = self.transforms(_results) + aug_data.append(data) + # list of dict to dict of list + aug_data_dict = {key: [] for key in aug_data[0]} + for data in aug_data: + for key, val in data.items(): + aug_data_dict[key].append(val) + return aug_data_dict + + def __repr__(self): + repr_str = self.__class__.__name__ + repr_str += f'(transforms={self.transforms}, ' + repr_str += f'img_scale={self.img_scale}, flip={self.flip})' + repr_str += f'flip_direction={self.flip_direction}' + return repr_str diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/transforms.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/transforms.py new file mode 100644 index 0000000000000000000000000000000000000000..842763db97685dd9280424204d62ee65993fdd5a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/pipelines/transforms.py @@ -0,0 +1,889 @@ +import annotator.mmpkg.mmcv as mmcv +import numpy as np +from annotator.mmpkg.mmcv.utils import deprecated_api_warning, is_tuple_of +from numpy import random + +from ..builder import PIPELINES + + +@PIPELINES.register_module() +class Resize(object): + """Resize images & seg. + + This transform resizes the input image to some scale. If the input dict + contains the key "scale", then the scale in the input dict is used, + otherwise the specified scale in the init method is used. + + ``img_scale`` can be None, a tuple (single-scale) or a list of tuple + (multi-scale). There are 4 multiscale modes: + + - ``ratio_range is not None``: + 1. When img_scale is None, img_scale is the shape of image in results + (img_scale = results['img'].shape[:2]) and the image is resized based + on the original size. (mode 1) + 2. When img_scale is a tuple (single-scale), randomly sample a ratio from + the ratio range and multiply it with the image scale. (mode 2) + + - ``ratio_range is None and multiscale_mode == "range"``: randomly sample a + scale from the a range. (mode 3) + + - ``ratio_range is None and multiscale_mode == "value"``: randomly sample a + scale from multiple scales. (mode 4) + + Args: + img_scale (tuple or list[tuple]): Images scales for resizing. + multiscale_mode (str): Either "range" or "value". + ratio_range (tuple[float]): (min_ratio, max_ratio) + keep_ratio (bool): Whether to keep the aspect ratio when resizing the + image. + """ + + def __init__(self, + img_scale=None, + multiscale_mode='range', + ratio_range=None, + keep_ratio=True): + if img_scale is None: + self.img_scale = None + else: + if isinstance(img_scale, list): + self.img_scale = img_scale + else: + self.img_scale = [img_scale] + assert mmcv.is_list_of(self.img_scale, tuple) + + if ratio_range is not None: + # mode 1: given img_scale=None and a range of image ratio + # mode 2: given a scale and a range of image ratio + assert self.img_scale is None or len(self.img_scale) == 1 + else: + # mode 3 and 4: given multiple scales or a range of scales + assert multiscale_mode in ['value', 'range'] + + self.multiscale_mode = multiscale_mode + self.ratio_range = ratio_range + self.keep_ratio = keep_ratio + + @staticmethod + def random_select(img_scales): + """Randomly select an img_scale from given candidates. + + Args: + img_scales (list[tuple]): Images scales for selection. + + Returns: + (tuple, int): Returns a tuple ``(img_scale, scale_dix)``, + where ``img_scale`` is the selected image scale and + ``scale_idx`` is the selected index in the given candidates. + """ + + assert mmcv.is_list_of(img_scales, tuple) + scale_idx = np.random.randint(len(img_scales)) + img_scale = img_scales[scale_idx] + return img_scale, scale_idx + + @staticmethod + def random_sample(img_scales): + """Randomly sample an img_scale when ``multiscale_mode=='range'``. + + Args: + img_scales (list[tuple]): Images scale range for sampling. + There must be two tuples in img_scales, which specify the lower + and upper bound of image scales. + + Returns: + (tuple, None): Returns a tuple ``(img_scale, None)``, where + ``img_scale`` is sampled scale and None is just a placeholder + to be consistent with :func:`random_select`. + """ + + assert mmcv.is_list_of(img_scales, tuple) and len(img_scales) == 2 + img_scale_long = [max(s) for s in img_scales] + img_scale_short = [min(s) for s in img_scales] + long_edge = np.random.randint( + min(img_scale_long), + max(img_scale_long) + 1) + short_edge = np.random.randint( + min(img_scale_short), + max(img_scale_short) + 1) + img_scale = (long_edge, short_edge) + return img_scale, None + + @staticmethod + def random_sample_ratio(img_scale, ratio_range): + """Randomly sample an img_scale when ``ratio_range`` is specified. + + A ratio will be randomly sampled from the range specified by + ``ratio_range``. Then it would be multiplied with ``img_scale`` to + generate sampled scale. + + Args: + img_scale (tuple): Images scale base to multiply with ratio. + ratio_range (tuple[float]): The minimum and maximum ratio to scale + the ``img_scale``. + + Returns: + (tuple, None): Returns a tuple ``(scale, None)``, where + ``scale`` is sampled ratio multiplied with ``img_scale`` and + None is just a placeholder to be consistent with + :func:`random_select`. + """ + + assert isinstance(img_scale, tuple) and len(img_scale) == 2 + min_ratio, max_ratio = ratio_range + assert min_ratio <= max_ratio + ratio = np.random.random_sample() * (max_ratio - min_ratio) + min_ratio + scale = int(img_scale[0] * ratio), int(img_scale[1] * ratio) + return scale, None + + def _random_scale(self, results): + """Randomly sample an img_scale according to ``ratio_range`` and + ``multiscale_mode``. + + If ``ratio_range`` is specified, a ratio will be sampled and be + multiplied with ``img_scale``. + If multiple scales are specified by ``img_scale``, a scale will be + sampled according to ``multiscale_mode``. + Otherwise, single scale will be used. + + Args: + results (dict): Result dict from :obj:`dataset`. + + Returns: + dict: Two new keys 'scale` and 'scale_idx` are added into + ``results``, which would be used by subsequent pipelines. + """ + + if self.ratio_range is not None: + if self.img_scale is None: + h, w = results['img'].shape[:2] + scale, scale_idx = self.random_sample_ratio((w, h), + self.ratio_range) + else: + scale, scale_idx = self.random_sample_ratio( + self.img_scale[0], self.ratio_range) + elif len(self.img_scale) == 1: + scale, scale_idx = self.img_scale[0], 0 + elif self.multiscale_mode == 'range': + scale, scale_idx = self.random_sample(self.img_scale) + elif self.multiscale_mode == 'value': + scale, scale_idx = self.random_select(self.img_scale) + else: + raise NotImplementedError + + results['scale'] = scale + results['scale_idx'] = scale_idx + + def _resize_img(self, results): + """Resize images with ``results['scale']``.""" + if self.keep_ratio: + img, scale_factor = mmcv.imrescale( + results['img'], results['scale'], return_scale=True) + # the w_scale and h_scale has minor difference + # a real fix should be done in the mmcv.imrescale in the future + new_h, new_w = img.shape[:2] + h, w = results['img'].shape[:2] + w_scale = new_w / w + h_scale = new_h / h + else: + img, w_scale, h_scale = mmcv.imresize( + results['img'], results['scale'], return_scale=True) + scale_factor = np.array([w_scale, h_scale, w_scale, h_scale], + dtype=np.float32) + results['img'] = img + results['img_shape'] = img.shape + results['pad_shape'] = img.shape # in case that there is no padding + results['scale_factor'] = scale_factor + results['keep_ratio'] = self.keep_ratio + + def _resize_seg(self, results): + """Resize semantic segmentation map with ``results['scale']``.""" + for key in results.get('seg_fields', []): + if self.keep_ratio: + gt_seg = mmcv.imrescale( + results[key], results['scale'], interpolation='nearest') + else: + gt_seg = mmcv.imresize( + results[key], results['scale'], interpolation='nearest') + results[key] = gt_seg + + def __call__(self, results): + """Call function to resize images, bounding boxes, masks, semantic + segmentation map. + + Args: + results (dict): Result dict from loading pipeline. + + Returns: + dict: Resized results, 'img_shape', 'pad_shape', 'scale_factor', + 'keep_ratio' keys are added into result dict. + """ + + if 'scale' not in results: + self._random_scale(results) + self._resize_img(results) + self._resize_seg(results) + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + repr_str += (f'(img_scale={self.img_scale}, ' + f'multiscale_mode={self.multiscale_mode}, ' + f'ratio_range={self.ratio_range}, ' + f'keep_ratio={self.keep_ratio})') + return repr_str + + +@PIPELINES.register_module() +class RandomFlip(object): + """Flip the image & seg. + + If the input dict contains the key "flip", then the flag will be used, + otherwise it will be randomly decided by a ratio specified in the init + method. + + Args: + prob (float, optional): The flipping probability. Default: None. + direction(str, optional): The flipping direction. Options are + 'horizontal' and 'vertical'. Default: 'horizontal'. + """ + + @deprecated_api_warning({'flip_ratio': 'prob'}, cls_name='RandomFlip') + def __init__(self, prob=None, direction='horizontal'): + self.prob = prob + self.direction = direction + if prob is not None: + assert prob >= 0 and prob <= 1 + assert direction in ['horizontal', 'vertical'] + + def __call__(self, results): + """Call function to flip bounding boxes, masks, semantic segmentation + maps. + + Args: + results (dict): Result dict from loading pipeline. + + Returns: + dict: Flipped results, 'flip', 'flip_direction' keys are added into + result dict. + """ + + if 'flip' not in results: + flip = True if np.random.rand() < self.prob else False + results['flip'] = flip + if 'flip_direction' not in results: + results['flip_direction'] = self.direction + if results['flip']: + # flip image + results['img'] = mmcv.imflip( + results['img'], direction=results['flip_direction']) + + # flip segs + for key in results.get('seg_fields', []): + # use copy() to make numpy stride positive + results[key] = mmcv.imflip( + results[key], direction=results['flip_direction']).copy() + return results + + def __repr__(self): + return self.__class__.__name__ + f'(prob={self.prob})' + + +@PIPELINES.register_module() +class Pad(object): + """Pad the image & mask. + + There are two padding modes: (1) pad to a fixed size and (2) pad to the + minimum size that is divisible by some number. + Added keys are "pad_shape", "pad_fixed_size", "pad_size_divisor", + + Args: + size (tuple, optional): Fixed padding size. + size_divisor (int, optional): The divisor of padded size. + pad_val (float, optional): Padding value. Default: 0. + seg_pad_val (float, optional): Padding value of segmentation map. + Default: 255. + """ + + def __init__(self, + size=None, + size_divisor=None, + pad_val=0, + seg_pad_val=255): + self.size = size + self.size_divisor = size_divisor + self.pad_val = pad_val + self.seg_pad_val = seg_pad_val + # only one of size and size_divisor should be valid + assert size is not None or size_divisor is not None + assert size is None or size_divisor is None + + def _pad_img(self, results): + """Pad images according to ``self.size``.""" + if self.size is not None: + padded_img = mmcv.impad( + results['img'], shape=self.size, pad_val=self.pad_val) + elif self.size_divisor is not None: + padded_img = mmcv.impad_to_multiple( + results['img'], self.size_divisor, pad_val=self.pad_val) + results['img'] = padded_img + results['pad_shape'] = padded_img.shape + results['pad_fixed_size'] = self.size + results['pad_size_divisor'] = self.size_divisor + + def _pad_seg(self, results): + """Pad masks according to ``results['pad_shape']``.""" + for key in results.get('seg_fields', []): + results[key] = mmcv.impad( + results[key], + shape=results['pad_shape'][:2], + pad_val=self.seg_pad_val) + + def __call__(self, results): + """Call function to pad images, masks, semantic segmentation maps. + + Args: + results (dict): Result dict from loading pipeline. + + Returns: + dict: Updated result dict. + """ + + self._pad_img(results) + self._pad_seg(results) + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + repr_str += f'(size={self.size}, size_divisor={self.size_divisor}, ' \ + f'pad_val={self.pad_val})' + return repr_str + + +@PIPELINES.register_module() +class Normalize(object): + """Normalize the image. + + Added key is "img_norm_cfg". + + Args: + mean (sequence): Mean values of 3 channels. + std (sequence): Std values of 3 channels. + to_rgb (bool): Whether to convert the image from BGR to RGB, + default is true. + """ + + def __init__(self, mean, std, to_rgb=True): + self.mean = np.array(mean, dtype=np.float32) + self.std = np.array(std, dtype=np.float32) + self.to_rgb = to_rgb + + def __call__(self, results): + """Call function to normalize images. + + Args: + results (dict): Result dict from loading pipeline. + + Returns: + dict: Normalized results, 'img_norm_cfg' key is added into + result dict. + """ + + results['img'] = mmcv.imnormalize(results['img'], self.mean, self.std, + self.to_rgb) + results['img_norm_cfg'] = dict( + mean=self.mean, std=self.std, to_rgb=self.to_rgb) + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + repr_str += f'(mean={self.mean}, std={self.std}, to_rgb=' \ + f'{self.to_rgb})' + return repr_str + + +@PIPELINES.register_module() +class Rerange(object): + """Rerange the image pixel value. + + Args: + min_value (float or int): Minimum value of the reranged image. + Default: 0. + max_value (float or int): Maximum value of the reranged image. + Default: 255. + """ + + def __init__(self, min_value=0, max_value=255): + assert isinstance(min_value, float) or isinstance(min_value, int) + assert isinstance(max_value, float) or isinstance(max_value, int) + assert min_value < max_value + self.min_value = min_value + self.max_value = max_value + + def __call__(self, results): + """Call function to rerange images. + + Args: + results (dict): Result dict from loading pipeline. + Returns: + dict: Reranged results. + """ + + img = results['img'] + img_min_value = np.min(img) + img_max_value = np.max(img) + + assert img_min_value < img_max_value + # rerange to [0, 1] + img = (img - img_min_value) / (img_max_value - img_min_value) + # rerange to [min_value, max_value] + img = img * (self.max_value - self.min_value) + self.min_value + results['img'] = img + + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + repr_str += f'(min_value={self.min_value}, max_value={self.max_value})' + return repr_str + + +@PIPELINES.register_module() +class CLAHE(object): + """Use CLAHE method to process the image. + + See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. + Graphics Gems, 1994:474-485.` for more information. + + Args: + clip_limit (float): Threshold for contrast limiting. Default: 40.0. + tile_grid_size (tuple[int]): Size of grid for histogram equalization. + Input image will be divided into equally sized rectangular tiles. + It defines the number of tiles in row and column. Default: (8, 8). + """ + + def __init__(self, clip_limit=40.0, tile_grid_size=(8, 8)): + assert isinstance(clip_limit, (float, int)) + self.clip_limit = clip_limit + assert is_tuple_of(tile_grid_size, int) + assert len(tile_grid_size) == 2 + self.tile_grid_size = tile_grid_size + + def __call__(self, results): + """Call function to Use CLAHE method process images. + + Args: + results (dict): Result dict from loading pipeline. + + Returns: + dict: Processed results. + """ + + for i in range(results['img'].shape[2]): + results['img'][:, :, i] = mmcv.clahe( + np.array(results['img'][:, :, i], dtype=np.uint8), + self.clip_limit, self.tile_grid_size) + + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + repr_str += f'(clip_limit={self.clip_limit}, '\ + f'tile_grid_size={self.tile_grid_size})' + return repr_str + + +@PIPELINES.register_module() +class RandomCrop(object): + """Random crop the image & seg. + + Args: + crop_size (tuple): Expected size after cropping, (h, w). + cat_max_ratio (float): The maximum ratio that single category could + occupy. + """ + + def __init__(self, crop_size, cat_max_ratio=1., ignore_index=255): + assert crop_size[0] > 0 and crop_size[1] > 0 + self.crop_size = crop_size + self.cat_max_ratio = cat_max_ratio + self.ignore_index = ignore_index + + def get_crop_bbox(self, img): + """Randomly get a crop bounding box.""" + margin_h = max(img.shape[0] - self.crop_size[0], 0) + margin_w = max(img.shape[1] - self.crop_size[1], 0) + offset_h = np.random.randint(0, margin_h + 1) + offset_w = np.random.randint(0, margin_w + 1) + crop_y1, crop_y2 = offset_h, offset_h + self.crop_size[0] + crop_x1, crop_x2 = offset_w, offset_w + self.crop_size[1] + + return crop_y1, crop_y2, crop_x1, crop_x2 + + def crop(self, img, crop_bbox): + """Crop from ``img``""" + crop_y1, crop_y2, crop_x1, crop_x2 = crop_bbox + img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...] + return img + + def __call__(self, results): + """Call function to randomly crop images, semantic segmentation maps. + + Args: + results (dict): Result dict from loading pipeline. + + Returns: + dict: Randomly cropped results, 'img_shape' key in result dict is + updated according to crop size. + """ + + img = results['img'] + crop_bbox = self.get_crop_bbox(img) + if self.cat_max_ratio < 1.: + # Repeat 10 times + for _ in range(10): + seg_temp = self.crop(results['gt_semantic_seg'], crop_bbox) + labels, cnt = np.unique(seg_temp, return_counts=True) + cnt = cnt[labels != self.ignore_index] + if len(cnt) > 1 and np.max(cnt) / np.sum( + cnt) < self.cat_max_ratio: + break + crop_bbox = self.get_crop_bbox(img) + + # crop the image + img = self.crop(img, crop_bbox) + img_shape = img.shape + results['img'] = img + results['img_shape'] = img_shape + + # crop semantic seg + for key in results.get('seg_fields', []): + results[key] = self.crop(results[key], crop_bbox) + + return results + + def __repr__(self): + return self.__class__.__name__ + f'(crop_size={self.crop_size})' + + +@PIPELINES.register_module() +class RandomRotate(object): + """Rotate the image & seg. + + Args: + prob (float): The rotation probability. + degree (float, tuple[float]): Range of degrees to select from. If + degree is a number instead of tuple like (min, max), + the range of degree will be (``-degree``, ``+degree``) + pad_val (float, optional): Padding value of image. Default: 0. + seg_pad_val (float, optional): Padding value of segmentation map. + Default: 255. + center (tuple[float], optional): Center point (w, h) of the rotation in + the source image. If not specified, the center of the image will be + used. Default: None. + auto_bound (bool): Whether to adjust the image size to cover the whole + rotated image. Default: False + """ + + def __init__(self, + prob, + degree, + pad_val=0, + seg_pad_val=255, + center=None, + auto_bound=False): + self.prob = prob + assert prob >= 0 and prob <= 1 + if isinstance(degree, (float, int)): + assert degree > 0, f'degree {degree} should be positive' + self.degree = (-degree, degree) + else: + self.degree = degree + assert len(self.degree) == 2, f'degree {self.degree} should be a ' \ + f'tuple of (min, max)' + self.pal_val = pad_val + self.seg_pad_val = seg_pad_val + self.center = center + self.auto_bound = auto_bound + + def __call__(self, results): + """Call function to rotate image, semantic segmentation maps. + + Args: + results (dict): Result dict from loading pipeline. + + Returns: + dict: Rotated results. + """ + + rotate = True if np.random.rand() < self.prob else False + degree = np.random.uniform(min(*self.degree), max(*self.degree)) + if rotate: + # rotate image + results['img'] = mmcv.imrotate( + results['img'], + angle=degree, + border_value=self.pal_val, + center=self.center, + auto_bound=self.auto_bound) + + # rotate segs + for key in results.get('seg_fields', []): + results[key] = mmcv.imrotate( + results[key], + angle=degree, + border_value=self.seg_pad_val, + center=self.center, + auto_bound=self.auto_bound, + interpolation='nearest') + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + repr_str += f'(prob={self.prob}, ' \ + f'degree={self.degree}, ' \ + f'pad_val={self.pal_val}, ' \ + f'seg_pad_val={self.seg_pad_val}, ' \ + f'center={self.center}, ' \ + f'auto_bound={self.auto_bound})' + return repr_str + + +@PIPELINES.register_module() +class RGB2Gray(object): + """Convert RGB image to grayscale image. + + This transform calculate the weighted mean of input image channels with + ``weights`` and then expand the channels to ``out_channels``. When + ``out_channels`` is None, the number of output channels is the same as + input channels. + + Args: + out_channels (int): Expected number of output channels after + transforming. Default: None. + weights (tuple[float]): The weights to calculate the weighted mean. + Default: (0.299, 0.587, 0.114). + """ + + def __init__(self, out_channels=None, weights=(0.299, 0.587, 0.114)): + assert out_channels is None or out_channels > 0 + self.out_channels = out_channels + assert isinstance(weights, tuple) + for item in weights: + assert isinstance(item, (float, int)) + self.weights = weights + + def __call__(self, results): + """Call function to convert RGB image to grayscale image. + + Args: + results (dict): Result dict from loading pipeline. + + Returns: + dict: Result dict with grayscale image. + """ + img = results['img'] + assert len(img.shape) == 3 + assert img.shape[2] == len(self.weights) + weights = np.array(self.weights).reshape((1, 1, -1)) + img = (img * weights).sum(2, keepdims=True) + if self.out_channels is None: + img = img.repeat(weights.shape[2], axis=2) + else: + img = img.repeat(self.out_channels, axis=2) + + results['img'] = img + results['img_shape'] = img.shape + + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + repr_str += f'(out_channels={self.out_channels}, ' \ + f'weights={self.weights})' + return repr_str + + +@PIPELINES.register_module() +class AdjustGamma(object): + """Using gamma correction to process the image. + + Args: + gamma (float or int): Gamma value used in gamma correction. + Default: 1.0. + """ + + def __init__(self, gamma=1.0): + assert isinstance(gamma, float) or isinstance(gamma, int) + assert gamma > 0 + self.gamma = gamma + inv_gamma = 1.0 / gamma + self.table = np.array([(i / 255.0)**inv_gamma * 255 + for i in np.arange(256)]).astype('uint8') + + def __call__(self, results): + """Call function to process the image with gamma correction. + + Args: + results (dict): Result dict from loading pipeline. + + Returns: + dict: Processed results. + """ + + results['img'] = mmcv.lut_transform( + np.array(results['img'], dtype=np.uint8), self.table) + + return results + + def __repr__(self): + return self.__class__.__name__ + f'(gamma={self.gamma})' + + +@PIPELINES.register_module() +class SegRescale(object): + """Rescale semantic segmentation maps. + + Args: + scale_factor (float): The scale factor of the final output. + """ + + def __init__(self, scale_factor=1): + self.scale_factor = scale_factor + + def __call__(self, results): + """Call function to scale the semantic segmentation map. + + Args: + results (dict): Result dict from loading pipeline. + + Returns: + dict: Result dict with semantic segmentation map scaled. + """ + for key in results.get('seg_fields', []): + if self.scale_factor != 1: + results[key] = mmcv.imrescale( + results[key], self.scale_factor, interpolation='nearest') + return results + + def __repr__(self): + return self.__class__.__name__ + f'(scale_factor={self.scale_factor})' + + +@PIPELINES.register_module() +class PhotoMetricDistortion(object): + """Apply photometric distortion to image sequentially, every transformation + is applied with a probability of 0.5. The position of random contrast is in + second or second to last. + + 1. random brightness + 2. random contrast (mode 0) + 3. convert color from BGR to HSV + 4. random saturation + 5. random hue + 6. convert color from HSV to BGR + 7. random contrast (mode 1) + + Args: + brightness_delta (int): delta of brightness. + contrast_range (tuple): range of contrast. + saturation_range (tuple): range of saturation. + hue_delta (int): delta of hue. + """ + + def __init__(self, + brightness_delta=32, + contrast_range=(0.5, 1.5), + saturation_range=(0.5, 1.5), + hue_delta=18): + self.brightness_delta = brightness_delta + self.contrast_lower, self.contrast_upper = contrast_range + self.saturation_lower, self.saturation_upper = saturation_range + self.hue_delta = hue_delta + + def convert(self, img, alpha=1, beta=0): + """Multiple with alpha and add beat with clip.""" + img = img.astype(np.float32) * alpha + beta + img = np.clip(img, 0, 255) + return img.astype(np.uint8) + + def brightness(self, img): + """Brightness distortion.""" + if random.randint(2): + return self.convert( + img, + beta=random.uniform(-self.brightness_delta, + self.brightness_delta)) + return img + + def contrast(self, img): + """Contrast distortion.""" + if random.randint(2): + return self.convert( + img, + alpha=random.uniform(self.contrast_lower, self.contrast_upper)) + return img + + def saturation(self, img): + """Saturation distortion.""" + if random.randint(2): + img = mmcv.bgr2hsv(img) + img[:, :, 1] = self.convert( + img[:, :, 1], + alpha=random.uniform(self.saturation_lower, + self.saturation_upper)) + img = mmcv.hsv2bgr(img) + return img + + def hue(self, img): + """Hue distortion.""" + if random.randint(2): + img = mmcv.bgr2hsv(img) + img[:, :, + 0] = (img[:, :, 0].astype(int) + + random.randint(-self.hue_delta, self.hue_delta)) % 180 + img = mmcv.hsv2bgr(img) + return img + + def __call__(self, results): + """Call function to perform photometric distortion on images. + + Args: + results (dict): Result dict from loading pipeline. + + Returns: + dict: Result dict with images distorted. + """ + + img = results['img'] + # random brightness + img = self.brightness(img) + + # mode == 0 --> do random contrast first + # mode == 1 --> do random contrast last + mode = random.randint(2) + if mode == 1: + img = self.contrast(img) + + # random saturation + img = self.saturation(img) + + # random hue + img = self.hue(img) + + # random contrast + if mode == 0: + img = self.contrast(img) + + results['img'] = img + return results + + def __repr__(self): + repr_str = self.__class__.__name__ + repr_str += (f'(brightness_delta={self.brightness_delta}, ' + f'contrast_range=({self.contrast_lower}, ' + f'{self.contrast_upper}), ' + f'saturation_range=({self.saturation_lower}, ' + f'{self.saturation_upper}), ' + f'hue_delta={self.hue_delta})') + return repr_str diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/stare.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/stare.py new file mode 100644 index 0000000000000000000000000000000000000000..cbd14e0920e7f6a73baff1432e5a32ccfdb0dfae --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/stare.py @@ -0,0 +1,27 @@ +import os.path as osp + +from .builder import DATASETS +from .custom import CustomDataset + + +@DATASETS.register_module() +class STAREDataset(CustomDataset): + """STARE dataset. + + In segmentation map annotation for STARE, 0 stands for background, which is + included in 2 categories. ``reduce_zero_label`` is fixed to False. The + ``img_suffix`` is fixed to '.png' and ``seg_map_suffix`` is fixed to + '.ah.png'. + """ + + CLASSES = ('background', 'vessel') + + PALETTE = [[120, 120, 120], [6, 230, 230]] + + def __init__(self, **kwargs): + super(STAREDataset, self).__init__( + img_suffix='.png', + seg_map_suffix='.ah.png', + reduce_zero_label=False, + **kwargs) + assert osp.exists(self.img_dir) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/voc.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/voc.py new file mode 100644 index 0000000000000000000000000000000000000000..a8855203b14ee0dc4da9099a2945d4aedcffbcd6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/datasets/voc.py @@ -0,0 +1,29 @@ +import os.path as osp + +from .builder import DATASETS +from .custom import CustomDataset + + +@DATASETS.register_module() +class PascalVOCDataset(CustomDataset): + """Pascal VOC dataset. + + Args: + split (str): Split txt file for Pascal VOC. + """ + + CLASSES = ('background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', + 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', + 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', + 'train', 'tvmonitor') + + PALETTE = [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], + [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], + [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], + [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], + [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] + + def __init__(self, split, **kwargs): + super(PascalVOCDataset, self).__init__( + img_suffix='.jpg', seg_map_suffix='.png', split=split, **kwargs) + assert osp.exists(self.img_dir) and self.split is not None diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3cf93f8bec9cf0cef0a3bd76ca3ca92eb188f535 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/__init__.py @@ -0,0 +1,12 @@ +from .backbones import * # noqa: F401,F403 +from .builder import (BACKBONES, HEADS, LOSSES, SEGMENTORS, build_backbone, + build_head, build_loss, build_segmentor) +from .decode_heads import * # noqa: F401,F403 +from .losses import * # noqa: F401,F403 +from .necks import * # noqa: F401,F403 +from .segmentors import * # noqa: F401,F403 + +__all__ = [ + 'BACKBONES', 'HEADS', 'LOSSES', 'SEGMENTORS', 'build_backbone', + 'build_head', 'build_loss', 'build_segmentor' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a1116c00a17c8bd9ed7f18743baee22b3b7d3f8d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/__init__.py @@ -0,0 +1,16 @@ +from .cgnet import CGNet +# from .fast_scnn import FastSCNN +from .hrnet import HRNet +from .mobilenet_v2 import MobileNetV2 +from .mobilenet_v3 import MobileNetV3 +from .resnest import ResNeSt +from .resnet import ResNet, ResNetV1c, ResNetV1d +from .resnext import ResNeXt +from .unet import UNet +from .vit import VisionTransformer + +__all__ = [ + 'ResNet', 'ResNetV1c', 'ResNetV1d', 'ResNeXt', 'HRNet', + 'ResNeSt', 'MobileNetV2', 'UNet', 'CGNet', 'MobileNetV3', + 'VisionTransformer' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/cgnet.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/cgnet.py new file mode 100644 index 0000000000000000000000000000000000000000..45c235e2e7fcef21e933ecb3ff88a37fa953abe6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/cgnet.py @@ -0,0 +1,367 @@ +import torch +import torch.nn as nn +import torch.utils.checkpoint as cp +from annotator.mmpkg.mmcv.cnn import (ConvModule, build_conv_layer, build_norm_layer, + constant_init, kaiming_init) +from annotator.mmpkg.mmcv.runner import load_checkpoint +from annotator.mmpkg.mmcv.utils.parrots_wrapper import _BatchNorm + +from annotator.mmpkg.mmseg.utils import get_root_logger +from ..builder import BACKBONES + + +class GlobalContextExtractor(nn.Module): + """Global Context Extractor for CGNet. + + This class is employed to refine the joint feature of both local feature + and surrounding context. + + Args: + channel (int): Number of input feature channels. + reduction (int): Reductions for global context extractor. Default: 16. + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. Default: False. + """ + + def __init__(self, channel, reduction=16, with_cp=False): + super(GlobalContextExtractor, self).__init__() + self.channel = channel + self.reduction = reduction + assert reduction >= 1 and channel >= reduction + self.with_cp = with_cp + self.avg_pool = nn.AdaptiveAvgPool2d(1) + self.fc = nn.Sequential( + nn.Linear(channel, channel // reduction), nn.ReLU(inplace=True), + nn.Linear(channel // reduction, channel), nn.Sigmoid()) + + def forward(self, x): + + def _inner_forward(x): + num_batch, num_channel = x.size()[:2] + y = self.avg_pool(x).view(num_batch, num_channel) + y = self.fc(y).view(num_batch, num_channel, 1, 1) + return x * y + + if self.with_cp and x.requires_grad: + out = cp.checkpoint(_inner_forward, x) + else: + out = _inner_forward(x) + + return out + + +class ContextGuidedBlock(nn.Module): + """Context Guided Block for CGNet. + + This class consists of four components: local feature extractor, + surrounding feature extractor, joint feature extractor and global + context extractor. + + Args: + in_channels (int): Number of input feature channels. + out_channels (int): Number of output feature channels. + dilation (int): Dilation rate for surrounding context extractor. + Default: 2. + reduction (int): Reduction for global context extractor. Default: 16. + skip_connect (bool): Add input to output or not. Default: True. + downsample (bool): Downsample the input to 1/2 or not. Default: False. + conv_cfg (dict): Config dict for convolution layer. + Default: None, which means using conv2d. + norm_cfg (dict): Config dict for normalization layer. + Default: dict(type='BN', requires_grad=True). + act_cfg (dict): Config dict for activation layer. + Default: dict(type='PReLU'). + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. Default: False. + """ + + def __init__(self, + in_channels, + out_channels, + dilation=2, + reduction=16, + skip_connect=True, + downsample=False, + conv_cfg=None, + norm_cfg=dict(type='BN', requires_grad=True), + act_cfg=dict(type='PReLU'), + with_cp=False): + super(ContextGuidedBlock, self).__init__() + self.with_cp = with_cp + self.downsample = downsample + + channels = out_channels if downsample else out_channels // 2 + if 'type' in act_cfg and act_cfg['type'] == 'PReLU': + act_cfg['num_parameters'] = channels + kernel_size = 3 if downsample else 1 + stride = 2 if downsample else 1 + padding = (kernel_size - 1) // 2 + + self.conv1x1 = ConvModule( + in_channels, + channels, + kernel_size, + stride, + padding, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + + self.f_loc = build_conv_layer( + conv_cfg, + channels, + channels, + kernel_size=3, + padding=1, + groups=channels, + bias=False) + self.f_sur = build_conv_layer( + conv_cfg, + channels, + channels, + kernel_size=3, + padding=dilation, + groups=channels, + dilation=dilation, + bias=False) + + self.bn = build_norm_layer(norm_cfg, 2 * channels)[1] + self.activate = nn.PReLU(2 * channels) + + if downsample: + self.bottleneck = build_conv_layer( + conv_cfg, + 2 * channels, + out_channels, + kernel_size=1, + bias=False) + + self.skip_connect = skip_connect and not downsample + self.f_glo = GlobalContextExtractor(out_channels, reduction, with_cp) + + def forward(self, x): + + def _inner_forward(x): + out = self.conv1x1(x) + loc = self.f_loc(out) + sur = self.f_sur(out) + + joi_feat = torch.cat([loc, sur], 1) # the joint feature + joi_feat = self.bn(joi_feat) + joi_feat = self.activate(joi_feat) + if self.downsample: + joi_feat = self.bottleneck(joi_feat) # channel = out_channels + # f_glo is employed to refine the joint feature + out = self.f_glo(joi_feat) + + if self.skip_connect: + return x + out + else: + return out + + if self.with_cp and x.requires_grad: + out = cp.checkpoint(_inner_forward, x) + else: + out = _inner_forward(x) + + return out + + +class InputInjection(nn.Module): + """Downsampling module for CGNet.""" + + def __init__(self, num_downsampling): + super(InputInjection, self).__init__() + self.pool = nn.ModuleList() + for i in range(num_downsampling): + self.pool.append(nn.AvgPool2d(3, stride=2, padding=1)) + + def forward(self, x): + for pool in self.pool: + x = pool(x) + return x + + +@BACKBONES.register_module() +class CGNet(nn.Module): + """CGNet backbone. + + A Light-weight Context Guided Network for Semantic Segmentation + arXiv: https://arxiv.org/abs/1811.08201 + + Args: + in_channels (int): Number of input image channels. Normally 3. + num_channels (tuple[int]): Numbers of feature channels at each stages. + Default: (32, 64, 128). + num_blocks (tuple[int]): Numbers of CG blocks at stage 1 and stage 2. + Default: (3, 21). + dilations (tuple[int]): Dilation rate for surrounding context + extractors at stage 1 and stage 2. Default: (2, 4). + reductions (tuple[int]): Reductions for global context extractors at + stage 1 and stage 2. Default: (8, 16). + conv_cfg (dict): Config dict for convolution layer. + Default: None, which means using conv2d. + norm_cfg (dict): Config dict for normalization layer. + Default: dict(type='BN', requires_grad=True). + act_cfg (dict): Config dict for activation layer. + Default: dict(type='PReLU'). + norm_eval (bool): Whether to set norm layers to eval mode, namely, + freeze running stats (mean and var). Note: Effect on Batch Norm + and its variants only. Default: False. + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. Default: False. + """ + + def __init__(self, + in_channels=3, + num_channels=(32, 64, 128), + num_blocks=(3, 21), + dilations=(2, 4), + reductions=(8, 16), + conv_cfg=None, + norm_cfg=dict(type='BN', requires_grad=True), + act_cfg=dict(type='PReLU'), + norm_eval=False, + with_cp=False): + + super(CGNet, self).__init__() + self.in_channels = in_channels + self.num_channels = num_channels + assert isinstance(self.num_channels, tuple) and len( + self.num_channels) == 3 + self.num_blocks = num_blocks + assert isinstance(self.num_blocks, tuple) and len(self.num_blocks) == 2 + self.dilations = dilations + assert isinstance(self.dilations, tuple) and len(self.dilations) == 2 + self.reductions = reductions + assert isinstance(self.reductions, tuple) and len(self.reductions) == 2 + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + if 'type' in self.act_cfg and self.act_cfg['type'] == 'PReLU': + self.act_cfg['num_parameters'] = num_channels[0] + self.norm_eval = norm_eval + self.with_cp = with_cp + + cur_channels = in_channels + self.stem = nn.ModuleList() + for i in range(3): + self.stem.append( + ConvModule( + cur_channels, + num_channels[0], + 3, + 2 if i == 0 else 1, + padding=1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg)) + cur_channels = num_channels[0] + + self.inject_2x = InputInjection(1) # down-sample for Input, factor=2 + self.inject_4x = InputInjection(2) # down-sample for Input, factor=4 + + cur_channels += in_channels + self.norm_prelu_0 = nn.Sequential( + build_norm_layer(norm_cfg, cur_channels)[1], + nn.PReLU(cur_channels)) + + # stage 1 + self.level1 = nn.ModuleList() + for i in range(num_blocks[0]): + self.level1.append( + ContextGuidedBlock( + cur_channels if i == 0 else num_channels[1], + num_channels[1], + dilations[0], + reductions[0], + downsample=(i == 0), + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg, + with_cp=with_cp)) # CG block + + cur_channels = 2 * num_channels[1] + in_channels + self.norm_prelu_1 = nn.Sequential( + build_norm_layer(norm_cfg, cur_channels)[1], + nn.PReLU(cur_channels)) + + # stage 2 + self.level2 = nn.ModuleList() + for i in range(num_blocks[1]): + self.level2.append( + ContextGuidedBlock( + cur_channels if i == 0 else num_channels[2], + num_channels[2], + dilations[1], + reductions[1], + downsample=(i == 0), + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg, + with_cp=with_cp)) # CG block + + cur_channels = 2 * num_channels[2] + self.norm_prelu_2 = nn.Sequential( + build_norm_layer(norm_cfg, cur_channels)[1], + nn.PReLU(cur_channels)) + + def forward(self, x): + output = [] + + # stage 0 + inp_2x = self.inject_2x(x) + inp_4x = self.inject_4x(x) + for layer in self.stem: + x = layer(x) + x = self.norm_prelu_0(torch.cat([x, inp_2x], 1)) + output.append(x) + + # stage 1 + for i, layer in enumerate(self.level1): + x = layer(x) + if i == 0: + down1 = x + x = self.norm_prelu_1(torch.cat([x, down1, inp_4x], 1)) + output.append(x) + + # stage 2 + for i, layer in enumerate(self.level2): + x = layer(x) + if i == 0: + down2 = x + x = self.norm_prelu_2(torch.cat([down2, x], 1)) + output.append(x) + + return output + + def init_weights(self, pretrained=None): + """Initialize the weights in backbone. + + Args: + pretrained (str, optional): Path to pre-trained weights. + Defaults to None. + """ + if isinstance(pretrained, str): + logger = get_root_logger() + load_checkpoint(self, pretrained, strict=False, logger=logger) + elif pretrained is None: + for m in self.modules(): + if isinstance(m, (nn.Conv2d, nn.Linear)): + kaiming_init(m) + elif isinstance(m, (_BatchNorm, nn.GroupNorm)): + constant_init(m, 1) + elif isinstance(m, nn.PReLU): + constant_init(m, 0) + else: + raise TypeError('pretrained must be a str or None') + + def train(self, mode=True): + """Convert the model into training mode will keeping the normalization + layer freezed.""" + super(CGNet, self).train(mode) + if mode and self.norm_eval: + for m in self.modules(): + # trick: eval have effect on BatchNorm only + if isinstance(m, _BatchNorm): + m.eval() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/fast_scnn.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/fast_scnn.py new file mode 100644 index 0000000000000000000000000000000000000000..417114417ebc830ea11ae7216aa12d8f7a79e5cb --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/fast_scnn.py @@ -0,0 +1,375 @@ +import torch +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import (ConvModule, DepthwiseSeparableConvModule, constant_init, + kaiming_init) +from torch.nn.modules.batchnorm import _BatchNorm + +from annotator.mmpkg.mmseg.models.decode_heads.psp_head import PPM +from annotator.mmpkg.mmseg.ops import resize +from ..builder import BACKBONES +from ..utils.inverted_residual import InvertedResidual + + +class LearningToDownsample(nn.Module): + """Learning to downsample module. + + Args: + in_channels (int): Number of input channels. + dw_channels (tuple[int]): Number of output channels of the first and + the second depthwise conv (dwconv) layers. + out_channels (int): Number of output channels of the whole + 'learning to downsample' module. + conv_cfg (dict | None): Config of conv layers. Default: None + norm_cfg (dict | None): Config of norm layers. Default: + dict(type='BN') + act_cfg (dict): Config of activation layers. Default: + dict(type='ReLU') + """ + + def __init__(self, + in_channels, + dw_channels, + out_channels, + conv_cfg=None, + norm_cfg=dict(type='BN'), + act_cfg=dict(type='ReLU')): + super(LearningToDownsample, self).__init__() + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + dw_channels1 = dw_channels[0] + dw_channels2 = dw_channels[1] + + self.conv = ConvModule( + in_channels, + dw_channels1, + 3, + stride=2, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.dsconv1 = DepthwiseSeparableConvModule( + dw_channels1, + dw_channels2, + kernel_size=3, + stride=2, + padding=1, + norm_cfg=self.norm_cfg) + self.dsconv2 = DepthwiseSeparableConvModule( + dw_channels2, + out_channels, + kernel_size=3, + stride=2, + padding=1, + norm_cfg=self.norm_cfg) + + def forward(self, x): + x = self.conv(x) + x = self.dsconv1(x) + x = self.dsconv2(x) + return x + + +class GlobalFeatureExtractor(nn.Module): + """Global feature extractor module. + + Args: + in_channels (int): Number of input channels of the GFE module. + Default: 64 + block_channels (tuple[int]): Tuple of ints. Each int specifies the + number of output channels of each Inverted Residual module. + Default: (64, 96, 128) + out_channels(int): Number of output channels of the GFE module. + Default: 128 + expand_ratio (int): Adjusts number of channels of the hidden layer + in InvertedResidual by this amount. + Default: 6 + num_blocks (tuple[int]): Tuple of ints. Each int specifies the + number of times each Inverted Residual module is repeated. + The repeated Inverted Residual modules are called a 'group'. + Default: (3, 3, 3) + strides (tuple[int]): Tuple of ints. Each int specifies + the downsampling factor of each 'group'. + Default: (2, 2, 1) + pool_scales (tuple[int]): Tuple of ints. Each int specifies + the parameter required in 'global average pooling' within PPM. + Default: (1, 2, 3, 6) + conv_cfg (dict | None): Config of conv layers. Default: None + norm_cfg (dict | None): Config of norm layers. Default: + dict(type='BN') + act_cfg (dict): Config of activation layers. Default: + dict(type='ReLU') + align_corners (bool): align_corners argument of F.interpolate. + Default: False + """ + + def __init__(self, + in_channels=64, + block_channels=(64, 96, 128), + out_channels=128, + expand_ratio=6, + num_blocks=(3, 3, 3), + strides=(2, 2, 1), + pool_scales=(1, 2, 3, 6), + conv_cfg=None, + norm_cfg=dict(type='BN'), + act_cfg=dict(type='ReLU'), + align_corners=False): + super(GlobalFeatureExtractor, self).__init__() + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + assert len(block_channels) == len(num_blocks) == 3 + self.bottleneck1 = self._make_layer(in_channels, block_channels[0], + num_blocks[0], strides[0], + expand_ratio) + self.bottleneck2 = self._make_layer(block_channels[0], + block_channels[1], num_blocks[1], + strides[1], expand_ratio) + self.bottleneck3 = self._make_layer(block_channels[1], + block_channels[2], num_blocks[2], + strides[2], expand_ratio) + self.ppm = PPM( + pool_scales, + block_channels[2], + block_channels[2] // 4, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg, + align_corners=align_corners) + self.out = ConvModule( + block_channels[2] * 2, + out_channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def _make_layer(self, + in_channels, + out_channels, + blocks, + stride=1, + expand_ratio=6): + layers = [ + InvertedResidual( + in_channels, + out_channels, + stride, + expand_ratio, + norm_cfg=self.norm_cfg) + ] + for i in range(1, blocks): + layers.append( + InvertedResidual( + out_channels, + out_channels, + 1, + expand_ratio, + norm_cfg=self.norm_cfg)) + return nn.Sequential(*layers) + + def forward(self, x): + x = self.bottleneck1(x) + x = self.bottleneck2(x) + x = self.bottleneck3(x) + x = torch.cat([x, *self.ppm(x)], dim=1) + x = self.out(x) + return x + + +class FeatureFusionModule(nn.Module): + """Feature fusion module. + + Args: + higher_in_channels (int): Number of input channels of the + higher-resolution branch. + lower_in_channels (int): Number of input channels of the + lower-resolution branch. + out_channels (int): Number of output channels. + conv_cfg (dict | None): Config of conv layers. Default: None + norm_cfg (dict | None): Config of norm layers. Default: + dict(type='BN') + act_cfg (dict): Config of activation layers. Default: + dict(type='ReLU') + align_corners (bool): align_corners argument of F.interpolate. + Default: False + """ + + def __init__(self, + higher_in_channels, + lower_in_channels, + out_channels, + conv_cfg=None, + norm_cfg=dict(type='BN'), + act_cfg=dict(type='ReLU'), + align_corners=False): + super(FeatureFusionModule, self).__init__() + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + self.align_corners = align_corners + self.dwconv = ConvModule( + lower_in_channels, + out_channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.conv_lower_res = ConvModule( + out_channels, + out_channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=None) + self.conv_higher_res = ConvModule( + higher_in_channels, + out_channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=None) + self.relu = nn.ReLU(True) + + def forward(self, higher_res_feature, lower_res_feature): + lower_res_feature = resize( + lower_res_feature, + size=higher_res_feature.size()[2:], + mode='bilinear', + align_corners=self.align_corners) + lower_res_feature = self.dwconv(lower_res_feature) + lower_res_feature = self.conv_lower_res(lower_res_feature) + + higher_res_feature = self.conv_higher_res(higher_res_feature) + out = higher_res_feature + lower_res_feature + return self.relu(out) + + +@BACKBONES.register_module() +class FastSCNN(nn.Module): + """Fast-SCNN Backbone. + + Args: + in_channels (int): Number of input image channels. Default: 3. + downsample_dw_channels (tuple[int]): Number of output channels after + the first conv layer & the second conv layer in + Learning-To-Downsample (LTD) module. + Default: (32, 48). + global_in_channels (int): Number of input channels of + Global Feature Extractor(GFE). + Equal to number of output channels of LTD. + Default: 64. + global_block_channels (tuple[int]): Tuple of integers that describe + the output channels for each of the MobileNet-v2 bottleneck + residual blocks in GFE. + Default: (64, 96, 128). + global_block_strides (tuple[int]): Tuple of integers + that describe the strides (downsampling factors) for each of the + MobileNet-v2 bottleneck residual blocks in GFE. + Default: (2, 2, 1). + global_out_channels (int): Number of output channels of GFE. + Default: 128. + higher_in_channels (int): Number of input channels of the higher + resolution branch in FFM. + Equal to global_in_channels. + Default: 64. + lower_in_channels (int): Number of input channels of the lower + resolution branch in FFM. + Equal to global_out_channels. + Default: 128. + fusion_out_channels (int): Number of output channels of FFM. + Default: 128. + out_indices (tuple): Tuple of indices of list + [higher_res_features, lower_res_features, fusion_output]. + Often set to (0,1,2) to enable aux. heads. + Default: (0, 1, 2). + conv_cfg (dict | None): Config of conv layers. Default: None + norm_cfg (dict | None): Config of norm layers. Default: + dict(type='BN') + act_cfg (dict): Config of activation layers. Default: + dict(type='ReLU') + align_corners (bool): align_corners argument of F.interpolate. + Default: False + """ + + def __init__(self, + in_channels=3, + downsample_dw_channels=(32, 48), + global_in_channels=64, + global_block_channels=(64, 96, 128), + global_block_strides=(2, 2, 1), + global_out_channels=128, + higher_in_channels=64, + lower_in_channels=128, + fusion_out_channels=128, + out_indices=(0, 1, 2), + conv_cfg=None, + norm_cfg=dict(type='BN'), + act_cfg=dict(type='ReLU'), + align_corners=False): + + super(FastSCNN, self).__init__() + if global_in_channels != higher_in_channels: + raise AssertionError('Global Input Channels must be the same \ + with Higher Input Channels!') + elif global_out_channels != lower_in_channels: + raise AssertionError('Global Output Channels must be the same \ + with Lower Input Channels!') + + self.in_channels = in_channels + self.downsample_dw_channels1 = downsample_dw_channels[0] + self.downsample_dw_channels2 = downsample_dw_channels[1] + self.global_in_channels = global_in_channels + self.global_block_channels = global_block_channels + self.global_block_strides = global_block_strides + self.global_out_channels = global_out_channels + self.higher_in_channels = higher_in_channels + self.lower_in_channels = lower_in_channels + self.fusion_out_channels = fusion_out_channels + self.out_indices = out_indices + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + self.align_corners = align_corners + self.learning_to_downsample = LearningToDownsample( + in_channels, + downsample_dw_channels, + global_in_channels, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.global_feature_extractor = GlobalFeatureExtractor( + global_in_channels, + global_block_channels, + global_out_channels, + strides=self.global_block_strides, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg, + align_corners=self.align_corners) + self.feature_fusion = FeatureFusionModule( + higher_in_channels, + lower_in_channels, + fusion_out_channels, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg, + align_corners=self.align_corners) + + def init_weights(self, pretrained=None): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + kaiming_init(m) + elif isinstance(m, (_BatchNorm, nn.GroupNorm)): + constant_init(m, 1) + + def forward(self, x): + higher_res_features = self.learning_to_downsample(x) + lower_res_features = self.global_feature_extractor(higher_res_features) + fusion_output = self.feature_fusion(higher_res_features, + lower_res_features) + + outs = [higher_res_features, lower_res_features, fusion_output] + outs = [outs[i] for i in self.out_indices] + return tuple(outs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/hrnet.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/hrnet.py new file mode 100644 index 0000000000000000000000000000000000000000..8d77fd6eadeec25a6b84619f0d7efa7c577b0464 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/hrnet.py @@ -0,0 +1,555 @@ +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import (build_conv_layer, build_norm_layer, constant_init, + kaiming_init) +from annotator.mmpkg.mmcv.runner import load_checkpoint +from annotator.mmpkg.mmcv.utils.parrots_wrapper import _BatchNorm + +from annotator.mmpkg.mmseg.ops import Upsample, resize +from annotator.mmpkg.mmseg.utils import get_root_logger +from ..builder import BACKBONES +from .resnet import BasicBlock, Bottleneck + + +class HRModule(nn.Module): + """High-Resolution Module for HRNet. + + In this module, every branch has 4 BasicBlocks/Bottlenecks. Fusion/Exchange + is in this module. + """ + + def __init__(self, + num_branches, + blocks, + num_blocks, + in_channels, + num_channels, + multiscale_output=True, + with_cp=False, + conv_cfg=None, + norm_cfg=dict(type='BN', requires_grad=True)): + super(HRModule, self).__init__() + self._check_branches(num_branches, num_blocks, in_channels, + num_channels) + + self.in_channels = in_channels + self.num_branches = num_branches + + self.multiscale_output = multiscale_output + self.norm_cfg = norm_cfg + self.conv_cfg = conv_cfg + self.with_cp = with_cp + self.branches = self._make_branches(num_branches, blocks, num_blocks, + num_channels) + self.fuse_layers = self._make_fuse_layers() + self.relu = nn.ReLU(inplace=False) + + def _check_branches(self, num_branches, num_blocks, in_channels, + num_channels): + """Check branches configuration.""" + if num_branches != len(num_blocks): + error_msg = f'NUM_BRANCHES({num_branches}) <> NUM_BLOCKS(' \ + f'{len(num_blocks)})' + raise ValueError(error_msg) + + if num_branches != len(num_channels): + error_msg = f'NUM_BRANCHES({num_branches}) <> NUM_CHANNELS(' \ + f'{len(num_channels)})' + raise ValueError(error_msg) + + if num_branches != len(in_channels): + error_msg = f'NUM_BRANCHES({num_branches}) <> NUM_INCHANNELS(' \ + f'{len(in_channels)})' + raise ValueError(error_msg) + + def _make_one_branch(self, + branch_index, + block, + num_blocks, + num_channels, + stride=1): + """Build one branch.""" + downsample = None + if stride != 1 or \ + self.in_channels[branch_index] != \ + num_channels[branch_index] * block.expansion: + downsample = nn.Sequential( + build_conv_layer( + self.conv_cfg, + self.in_channels[branch_index], + num_channels[branch_index] * block.expansion, + kernel_size=1, + stride=stride, + bias=False), + build_norm_layer(self.norm_cfg, num_channels[branch_index] * + block.expansion)[1]) + + layers = [] + layers.append( + block( + self.in_channels[branch_index], + num_channels[branch_index], + stride, + downsample=downsample, + with_cp=self.with_cp, + norm_cfg=self.norm_cfg, + conv_cfg=self.conv_cfg)) + self.in_channels[branch_index] = \ + num_channels[branch_index] * block.expansion + for i in range(1, num_blocks[branch_index]): + layers.append( + block( + self.in_channels[branch_index], + num_channels[branch_index], + with_cp=self.with_cp, + norm_cfg=self.norm_cfg, + conv_cfg=self.conv_cfg)) + + return nn.Sequential(*layers) + + def _make_branches(self, num_branches, block, num_blocks, num_channels): + """Build multiple branch.""" + branches = [] + + for i in range(num_branches): + branches.append( + self._make_one_branch(i, block, num_blocks, num_channels)) + + return nn.ModuleList(branches) + + def _make_fuse_layers(self): + """Build fuse layer.""" + if self.num_branches == 1: + return None + + num_branches = self.num_branches + in_channels = self.in_channels + fuse_layers = [] + num_out_branches = num_branches if self.multiscale_output else 1 + for i in range(num_out_branches): + fuse_layer = [] + for j in range(num_branches): + if j > i: + fuse_layer.append( + nn.Sequential( + build_conv_layer( + self.conv_cfg, + in_channels[j], + in_channels[i], + kernel_size=1, + stride=1, + padding=0, + bias=False), + build_norm_layer(self.norm_cfg, in_channels[i])[1], + # we set align_corners=False for HRNet + Upsample( + scale_factor=2**(j - i), + mode='bilinear', + align_corners=False))) + elif j == i: + fuse_layer.append(None) + else: + conv_downsamples = [] + for k in range(i - j): + if k == i - j - 1: + conv_downsamples.append( + nn.Sequential( + build_conv_layer( + self.conv_cfg, + in_channels[j], + in_channels[i], + kernel_size=3, + stride=2, + padding=1, + bias=False), + build_norm_layer(self.norm_cfg, + in_channels[i])[1])) + else: + conv_downsamples.append( + nn.Sequential( + build_conv_layer( + self.conv_cfg, + in_channels[j], + in_channels[j], + kernel_size=3, + stride=2, + padding=1, + bias=False), + build_norm_layer(self.norm_cfg, + in_channels[j])[1], + nn.ReLU(inplace=False))) + fuse_layer.append(nn.Sequential(*conv_downsamples)) + fuse_layers.append(nn.ModuleList(fuse_layer)) + + return nn.ModuleList(fuse_layers) + + def forward(self, x): + """Forward function.""" + if self.num_branches == 1: + return [self.branches[0](x[0])] + + for i in range(self.num_branches): + x[i] = self.branches[i](x[i]) + + x_fuse = [] + for i in range(len(self.fuse_layers)): + y = 0 + for j in range(self.num_branches): + if i == j: + y += x[j] + elif j > i: + y = y + resize( + self.fuse_layers[i][j](x[j]), + size=x[i].shape[2:], + mode='bilinear', + align_corners=False) + else: + y += self.fuse_layers[i][j](x[j]) + x_fuse.append(self.relu(y)) + return x_fuse + + +@BACKBONES.register_module() +class HRNet(nn.Module): + """HRNet backbone. + + High-Resolution Representations for Labeling Pixels and Regions + arXiv: https://arxiv.org/abs/1904.04514 + + Args: + extra (dict): detailed configuration for each stage of HRNet. + in_channels (int): Number of input image channels. Normally 3. + conv_cfg (dict): dictionary to construct and config conv layer. + norm_cfg (dict): dictionary to construct and config norm layer. + norm_eval (bool): Whether to set norm layers to eval mode, namely, + freeze running stats (mean and var). Note: Effect on Batch Norm + and its variants only. + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. + zero_init_residual (bool): whether to use zero init for last norm layer + in resblocks to let them behave as identity. + + Example: + >>> from annotator.mmpkg.mmseg.models import HRNet + >>> import torch + >>> extra = dict( + >>> stage1=dict( + >>> num_modules=1, + >>> num_branches=1, + >>> block='BOTTLENECK', + >>> num_blocks=(4, ), + >>> num_channels=(64, )), + >>> stage2=dict( + >>> num_modules=1, + >>> num_branches=2, + >>> block='BASIC', + >>> num_blocks=(4, 4), + >>> num_channels=(32, 64)), + >>> stage3=dict( + >>> num_modules=4, + >>> num_branches=3, + >>> block='BASIC', + >>> num_blocks=(4, 4, 4), + >>> num_channels=(32, 64, 128)), + >>> stage4=dict( + >>> num_modules=3, + >>> num_branches=4, + >>> block='BASIC', + >>> num_blocks=(4, 4, 4, 4), + >>> num_channels=(32, 64, 128, 256))) + >>> self = HRNet(extra, in_channels=1) + >>> self.eval() + >>> inputs = torch.rand(1, 1, 32, 32) + >>> level_outputs = self.forward(inputs) + >>> for level_out in level_outputs: + ... print(tuple(level_out.shape)) + (1, 32, 8, 8) + (1, 64, 4, 4) + (1, 128, 2, 2) + (1, 256, 1, 1) + """ + + blocks_dict = {'BASIC': BasicBlock, 'BOTTLENECK': Bottleneck} + + def __init__(self, + extra, + in_channels=3, + conv_cfg=None, + norm_cfg=dict(type='BN', requires_grad=True), + norm_eval=False, + with_cp=False, + zero_init_residual=False): + super(HRNet, self).__init__() + self.extra = extra + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.norm_eval = norm_eval + self.with_cp = with_cp + self.zero_init_residual = zero_init_residual + + # stem net + self.norm1_name, norm1 = build_norm_layer(self.norm_cfg, 64, postfix=1) + self.norm2_name, norm2 = build_norm_layer(self.norm_cfg, 64, postfix=2) + + self.conv1 = build_conv_layer( + self.conv_cfg, + in_channels, + 64, + kernel_size=3, + stride=2, + padding=1, + bias=False) + + self.add_module(self.norm1_name, norm1) + self.conv2 = build_conv_layer( + self.conv_cfg, + 64, + 64, + kernel_size=3, + stride=2, + padding=1, + bias=False) + + self.add_module(self.norm2_name, norm2) + self.relu = nn.ReLU(inplace=True) + + # stage 1 + self.stage1_cfg = self.extra['stage1'] + num_channels = self.stage1_cfg['num_channels'][0] + block_type = self.stage1_cfg['block'] + num_blocks = self.stage1_cfg['num_blocks'][0] + + block = self.blocks_dict[block_type] + stage1_out_channels = num_channels * block.expansion + self.layer1 = self._make_layer(block, 64, num_channels, num_blocks) + + # stage 2 + self.stage2_cfg = self.extra['stage2'] + num_channels = self.stage2_cfg['num_channels'] + block_type = self.stage2_cfg['block'] + + block = self.blocks_dict[block_type] + num_channels = [channel * block.expansion for channel in num_channels] + self.transition1 = self._make_transition_layer([stage1_out_channels], + num_channels) + self.stage2, pre_stage_channels = self._make_stage( + self.stage2_cfg, num_channels) + + # stage 3 + self.stage3_cfg = self.extra['stage3'] + num_channels = self.stage3_cfg['num_channels'] + block_type = self.stage3_cfg['block'] + + block = self.blocks_dict[block_type] + num_channels = [channel * block.expansion for channel in num_channels] + self.transition2 = self._make_transition_layer(pre_stage_channels, + num_channels) + self.stage3, pre_stage_channels = self._make_stage( + self.stage3_cfg, num_channels) + + # stage 4 + self.stage4_cfg = self.extra['stage4'] + num_channels = self.stage4_cfg['num_channels'] + block_type = self.stage4_cfg['block'] + + block = self.blocks_dict[block_type] + num_channels = [channel * block.expansion for channel in num_channels] + self.transition3 = self._make_transition_layer(pre_stage_channels, + num_channels) + self.stage4, pre_stage_channels = self._make_stage( + self.stage4_cfg, num_channels) + + @property + def norm1(self): + """nn.Module: the normalization layer named "norm1" """ + return getattr(self, self.norm1_name) + + @property + def norm2(self): + """nn.Module: the normalization layer named "norm2" """ + return getattr(self, self.norm2_name) + + def _make_transition_layer(self, num_channels_pre_layer, + num_channels_cur_layer): + """Make transition layer.""" + num_branches_cur = len(num_channels_cur_layer) + num_branches_pre = len(num_channels_pre_layer) + + transition_layers = [] + for i in range(num_branches_cur): + if i < num_branches_pre: + if num_channels_cur_layer[i] != num_channels_pre_layer[i]: + transition_layers.append( + nn.Sequential( + build_conv_layer( + self.conv_cfg, + num_channels_pre_layer[i], + num_channels_cur_layer[i], + kernel_size=3, + stride=1, + padding=1, + bias=False), + build_norm_layer(self.norm_cfg, + num_channels_cur_layer[i])[1], + nn.ReLU(inplace=True))) + else: + transition_layers.append(None) + else: + conv_downsamples = [] + for j in range(i + 1 - num_branches_pre): + in_channels = num_channels_pre_layer[-1] + out_channels = num_channels_cur_layer[i] \ + if j == i - num_branches_pre else in_channels + conv_downsamples.append( + nn.Sequential( + build_conv_layer( + self.conv_cfg, + in_channels, + out_channels, + kernel_size=3, + stride=2, + padding=1, + bias=False), + build_norm_layer(self.norm_cfg, out_channels)[1], + nn.ReLU(inplace=True))) + transition_layers.append(nn.Sequential(*conv_downsamples)) + + return nn.ModuleList(transition_layers) + + def _make_layer(self, block, inplanes, planes, blocks, stride=1): + """Make each layer.""" + downsample = None + if stride != 1 or inplanes != planes * block.expansion: + downsample = nn.Sequential( + build_conv_layer( + self.conv_cfg, + inplanes, + planes * block.expansion, + kernel_size=1, + stride=stride, + bias=False), + build_norm_layer(self.norm_cfg, planes * block.expansion)[1]) + + layers = [] + layers.append( + block( + inplanes, + planes, + stride, + downsample=downsample, + with_cp=self.with_cp, + norm_cfg=self.norm_cfg, + conv_cfg=self.conv_cfg)) + inplanes = planes * block.expansion + for i in range(1, blocks): + layers.append( + block( + inplanes, + planes, + with_cp=self.with_cp, + norm_cfg=self.norm_cfg, + conv_cfg=self.conv_cfg)) + + return nn.Sequential(*layers) + + def _make_stage(self, layer_config, in_channels, multiscale_output=True): + """Make each stage.""" + num_modules = layer_config['num_modules'] + num_branches = layer_config['num_branches'] + num_blocks = layer_config['num_blocks'] + num_channels = layer_config['num_channels'] + block = self.blocks_dict[layer_config['block']] + + hr_modules = [] + for i in range(num_modules): + # multi_scale_output is only used for the last module + if not multiscale_output and i == num_modules - 1: + reset_multiscale_output = False + else: + reset_multiscale_output = True + + hr_modules.append( + HRModule( + num_branches, + block, + num_blocks, + in_channels, + num_channels, + reset_multiscale_output, + with_cp=self.with_cp, + norm_cfg=self.norm_cfg, + conv_cfg=self.conv_cfg)) + + return nn.Sequential(*hr_modules), in_channels + + def init_weights(self, pretrained=None): + """Initialize the weights in backbone. + + Args: + pretrained (str, optional): Path to pre-trained weights. + Defaults to None. + """ + if isinstance(pretrained, str): + logger = get_root_logger() + load_checkpoint(self, pretrained, strict=False, logger=logger) + elif pretrained is None: + for m in self.modules(): + if isinstance(m, nn.Conv2d): + kaiming_init(m) + elif isinstance(m, (_BatchNorm, nn.GroupNorm)): + constant_init(m, 1) + + if self.zero_init_residual: + for m in self.modules(): + if isinstance(m, Bottleneck): + constant_init(m.norm3, 0) + elif isinstance(m, BasicBlock): + constant_init(m.norm2, 0) + else: + raise TypeError('pretrained must be a str or None') + + def forward(self, x): + """Forward function.""" + + x = self.conv1(x) + x = self.norm1(x) + x = self.relu(x) + x = self.conv2(x) + x = self.norm2(x) + x = self.relu(x) + x = self.layer1(x) + + x_list = [] + for i in range(self.stage2_cfg['num_branches']): + if self.transition1[i] is not None: + x_list.append(self.transition1[i](x)) + else: + x_list.append(x) + y_list = self.stage2(x_list) + + x_list = [] + for i in range(self.stage3_cfg['num_branches']): + if self.transition2[i] is not None: + x_list.append(self.transition2[i](y_list[-1])) + else: + x_list.append(y_list[i]) + y_list = self.stage3(x_list) + + x_list = [] + for i in range(self.stage4_cfg['num_branches']): + if self.transition3[i] is not None: + x_list.append(self.transition3[i](y_list[-1])) + else: + x_list.append(y_list[i]) + y_list = self.stage4(x_list) + + return y_list + + def train(self, mode=True): + """Convert the model into training mode will keeping the normalization + layer freezed.""" + super(HRNet, self).train(mode) + if mode and self.norm_eval: + for m in self.modules(): + # trick: eval have effect on BatchNorm only + if isinstance(m, _BatchNorm): + m.eval() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/mobilenet_v2.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/mobilenet_v2.py new file mode 100644 index 0000000000000000000000000000000000000000..7b5b6cd6d04c9da04669550d7f1fd24381460bf3 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/mobilenet_v2.py @@ -0,0 +1,180 @@ +import logging + +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import ConvModule, constant_init, kaiming_init +from annotator.mmpkg.mmcv.runner import load_checkpoint +from torch.nn.modules.batchnorm import _BatchNorm + +from ..builder import BACKBONES +from ..utils import InvertedResidual, make_divisible + + +@BACKBONES.register_module() +class MobileNetV2(nn.Module): + """MobileNetV2 backbone. + + Args: + widen_factor (float): Width multiplier, multiply number of + channels in each layer by this amount. Default: 1.0. + strides (Sequence[int], optional): Strides of the first block of each + layer. If not specified, default config in ``arch_setting`` will + be used. + dilations (Sequence[int]): Dilation of each layer. + out_indices (None or Sequence[int]): Output from which stages. + Default: (7, ). + frozen_stages (int): Stages to be frozen (all param fixed). + Default: -1, which means not freezing any parameters. + conv_cfg (dict): Config dict for convolution layer. + Default: None, which means using conv2d. + norm_cfg (dict): Config dict for normalization layer. + Default: dict(type='BN'). + act_cfg (dict): Config dict for activation layer. + Default: dict(type='ReLU6'). + norm_eval (bool): Whether to set norm layers to eval mode, namely, + freeze running stats (mean and var). Note: Effect on Batch Norm + and its variants only. Default: False. + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. Default: False. + """ + + # Parameters to build layers. 3 parameters are needed to construct a + # layer, from left to right: expand_ratio, channel, num_blocks. + arch_settings = [[1, 16, 1], [6, 24, 2], [6, 32, 3], [6, 64, 4], + [6, 96, 3], [6, 160, 3], [6, 320, 1]] + + def __init__(self, + widen_factor=1., + strides=(1, 2, 2, 2, 1, 2, 1), + dilations=(1, 1, 1, 1, 1, 1, 1), + out_indices=(1, 2, 4, 6), + frozen_stages=-1, + conv_cfg=None, + norm_cfg=dict(type='BN'), + act_cfg=dict(type='ReLU6'), + norm_eval=False, + with_cp=False): + super(MobileNetV2, self).__init__() + self.widen_factor = widen_factor + self.strides = strides + self.dilations = dilations + assert len(strides) == len(dilations) == len(self.arch_settings) + self.out_indices = out_indices + for index in out_indices: + if index not in range(0, 7): + raise ValueError('the item in out_indices must in ' + f'range(0, 8). But received {index}') + + if frozen_stages not in range(-1, 7): + raise ValueError('frozen_stages must be in range(-1, 7). ' + f'But received {frozen_stages}') + self.out_indices = out_indices + self.frozen_stages = frozen_stages + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + self.norm_eval = norm_eval + self.with_cp = with_cp + + self.in_channels = make_divisible(32 * widen_factor, 8) + + self.conv1 = ConvModule( + in_channels=3, + out_channels=self.in_channels, + kernel_size=3, + stride=2, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + self.layers = [] + + for i, layer_cfg in enumerate(self.arch_settings): + expand_ratio, channel, num_blocks = layer_cfg + stride = self.strides[i] + dilation = self.dilations[i] + out_channels = make_divisible(channel * widen_factor, 8) + inverted_res_layer = self.make_layer( + out_channels=out_channels, + num_blocks=num_blocks, + stride=stride, + dilation=dilation, + expand_ratio=expand_ratio) + layer_name = f'layer{i + 1}' + self.add_module(layer_name, inverted_res_layer) + self.layers.append(layer_name) + + def make_layer(self, out_channels, num_blocks, stride, dilation, + expand_ratio): + """Stack InvertedResidual blocks to build a layer for MobileNetV2. + + Args: + out_channels (int): out_channels of block. + num_blocks (int): Number of blocks. + stride (int): Stride of the first block. + dilation (int): Dilation of the first block. + expand_ratio (int): Expand the number of channels of the + hidden layer in InvertedResidual by this ratio. + """ + layers = [] + for i in range(num_blocks): + layers.append( + InvertedResidual( + self.in_channels, + out_channels, + stride if i == 0 else 1, + expand_ratio=expand_ratio, + dilation=dilation if i == 0 else 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg, + with_cp=self.with_cp)) + self.in_channels = out_channels + + return nn.Sequential(*layers) + + def init_weights(self, pretrained=None): + if isinstance(pretrained, str): + logger = logging.getLogger() + load_checkpoint(self, pretrained, strict=False, logger=logger) + elif pretrained is None: + for m in self.modules(): + if isinstance(m, nn.Conv2d): + kaiming_init(m) + elif isinstance(m, (_BatchNorm, nn.GroupNorm)): + constant_init(m, 1) + else: + raise TypeError('pretrained must be a str or None') + + def forward(self, x): + x = self.conv1(x) + + outs = [] + for i, layer_name in enumerate(self.layers): + layer = getattr(self, layer_name) + x = layer(x) + if i in self.out_indices: + outs.append(x) + + if len(outs) == 1: + return outs[0] + else: + return tuple(outs) + + def _freeze_stages(self): + if self.frozen_stages >= 0: + for param in self.conv1.parameters(): + param.requires_grad = False + for i in range(1, self.frozen_stages + 1): + layer = getattr(self, f'layer{i}') + layer.eval() + for param in layer.parameters(): + param.requires_grad = False + + def train(self, mode=True): + super(MobileNetV2, self).train(mode) + self._freeze_stages() + if mode and self.norm_eval: + for m in self.modules(): + if isinstance(m, _BatchNorm): + m.eval() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/mobilenet_v3.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/mobilenet_v3.py new file mode 100644 index 0000000000000000000000000000000000000000..e3c22bdd22356a600454f14c2ed12e7ef72c8ca1 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/mobilenet_v3.py @@ -0,0 +1,255 @@ +import logging + +import annotator.mmpkg.mmcv as mmcv +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import ConvModule, constant_init, kaiming_init +from annotator.mmpkg.mmcv.cnn.bricks import Conv2dAdaptivePadding +from annotator.mmpkg.mmcv.runner import load_checkpoint +from torch.nn.modules.batchnorm import _BatchNorm + +from ..builder import BACKBONES +from ..utils import InvertedResidualV3 as InvertedResidual + + +@BACKBONES.register_module() +class MobileNetV3(nn.Module): + """MobileNetV3 backbone. + + This backbone is the improved implementation of `Searching for MobileNetV3 + `_. + + Args: + arch (str): Architecture of mobilnetv3, from {'small', 'large'}. + Default: 'small'. + conv_cfg (dict): Config dict for convolution layer. + Default: None, which means using conv2d. + norm_cfg (dict): Config dict for normalization layer. + Default: dict(type='BN'). + out_indices (tuple[int]): Output from which layer. + Default: (0, 1, 12). + frozen_stages (int): Stages to be frozen (all param fixed). + Default: -1, which means not freezing any parameters. + norm_eval (bool): Whether to set norm layers to eval mode, namely, + freeze running stats (mean and var). Note: Effect on Batch Norm + and its variants only. Default: False. + with_cp (bool): Use checkpoint or not. Using checkpoint will save + some memory while slowing down the training speed. + Default: False. + """ + # Parameters to build each block: + # [kernel size, mid channels, out channels, with_se, act type, stride] + arch_settings = { + 'small': [[3, 16, 16, True, 'ReLU', 2], # block0 layer1 os=4 + [3, 72, 24, False, 'ReLU', 2], # block1 layer2 os=8 + [3, 88, 24, False, 'ReLU', 1], + [5, 96, 40, True, 'HSwish', 2], # block2 layer4 os=16 + [5, 240, 40, True, 'HSwish', 1], + [5, 240, 40, True, 'HSwish', 1], + [5, 120, 48, True, 'HSwish', 1], # block3 layer7 os=16 + [5, 144, 48, True, 'HSwish', 1], + [5, 288, 96, True, 'HSwish', 2], # block4 layer9 os=32 + [5, 576, 96, True, 'HSwish', 1], + [5, 576, 96, True, 'HSwish', 1]], + 'large': [[3, 16, 16, False, 'ReLU', 1], # block0 layer1 os=2 + [3, 64, 24, False, 'ReLU', 2], # block1 layer2 os=4 + [3, 72, 24, False, 'ReLU', 1], + [5, 72, 40, True, 'ReLU', 2], # block2 layer4 os=8 + [5, 120, 40, True, 'ReLU', 1], + [5, 120, 40, True, 'ReLU', 1], + [3, 240, 80, False, 'HSwish', 2], # block3 layer7 os=16 + [3, 200, 80, False, 'HSwish', 1], + [3, 184, 80, False, 'HSwish', 1], + [3, 184, 80, False, 'HSwish', 1], + [3, 480, 112, True, 'HSwish', 1], # block4 layer11 os=16 + [3, 672, 112, True, 'HSwish', 1], + [5, 672, 160, True, 'HSwish', 2], # block5 layer13 os=32 + [5, 960, 160, True, 'HSwish', 1], + [5, 960, 160, True, 'HSwish', 1]] + } # yapf: disable + + def __init__(self, + arch='small', + conv_cfg=None, + norm_cfg=dict(type='BN'), + out_indices=(0, 1, 12), + frozen_stages=-1, + reduction_factor=1, + norm_eval=False, + with_cp=False): + super(MobileNetV3, self).__init__() + assert arch in self.arch_settings + assert isinstance(reduction_factor, int) and reduction_factor > 0 + assert mmcv.is_tuple_of(out_indices, int) + for index in out_indices: + if index not in range(0, len(self.arch_settings[arch]) + 2): + raise ValueError( + 'the item in out_indices must in ' + f'range(0, {len(self.arch_settings[arch])+2}). ' + f'But received {index}') + + if frozen_stages not in range(-1, len(self.arch_settings[arch]) + 2): + raise ValueError('frozen_stages must be in range(-1, ' + f'{len(self.arch_settings[arch])+2}). ' + f'But received {frozen_stages}') + self.arch = arch + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.out_indices = out_indices + self.frozen_stages = frozen_stages + self.reduction_factor = reduction_factor + self.norm_eval = norm_eval + self.with_cp = with_cp + self.layers = self._make_layer() + + def _make_layer(self): + layers = [] + + # build the first layer (layer0) + in_channels = 16 + layer = ConvModule( + in_channels=3, + out_channels=in_channels, + kernel_size=3, + stride=2, + padding=1, + conv_cfg=dict(type='Conv2dAdaptivePadding'), + norm_cfg=self.norm_cfg, + act_cfg=dict(type='HSwish')) + self.add_module('layer0', layer) + layers.append('layer0') + + layer_setting = self.arch_settings[self.arch] + for i, params in enumerate(layer_setting): + (kernel_size, mid_channels, out_channels, with_se, act, + stride) = params + + if self.arch == 'large' and i >= 12 or self.arch == 'small' and \ + i >= 8: + mid_channels = mid_channels // self.reduction_factor + out_channels = out_channels // self.reduction_factor + + if with_se: + se_cfg = dict( + channels=mid_channels, + ratio=4, + act_cfg=(dict(type='ReLU'), + dict(type='HSigmoid', bias=3.0, divisor=6.0))) + else: + se_cfg = None + + layer = InvertedResidual( + in_channels=in_channels, + out_channels=out_channels, + mid_channels=mid_channels, + kernel_size=kernel_size, + stride=stride, + se_cfg=se_cfg, + with_expand_conv=(in_channels != mid_channels), + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=dict(type=act), + with_cp=self.with_cp) + in_channels = out_channels + layer_name = 'layer{}'.format(i + 1) + self.add_module(layer_name, layer) + layers.append(layer_name) + + # build the last layer + # block5 layer12 os=32 for small model + # block6 layer16 os=32 for large model + layer = ConvModule( + in_channels=in_channels, + out_channels=576 if self.arch == 'small' else 960, + kernel_size=1, + stride=1, + dilation=4, + padding=0, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=dict(type='HSwish')) + layer_name = 'layer{}'.format(len(layer_setting) + 1) + self.add_module(layer_name, layer) + layers.append(layer_name) + + # next, convert backbone MobileNetV3 to a semantic segmentation version + if self.arch == 'small': + self.layer4.depthwise_conv.conv.stride = (1, 1) + self.layer9.depthwise_conv.conv.stride = (1, 1) + for i in range(4, len(layers)): + layer = getattr(self, layers[i]) + if isinstance(layer, InvertedResidual): + modified_module = layer.depthwise_conv.conv + else: + modified_module = layer.conv + + if i < 9: + modified_module.dilation = (2, 2) + pad = 2 + else: + modified_module.dilation = (4, 4) + pad = 4 + + if not isinstance(modified_module, Conv2dAdaptivePadding): + # Adjust padding + pad *= (modified_module.kernel_size[0] - 1) // 2 + modified_module.padding = (pad, pad) + else: + self.layer7.depthwise_conv.conv.stride = (1, 1) + self.layer13.depthwise_conv.conv.stride = (1, 1) + for i in range(7, len(layers)): + layer = getattr(self, layers[i]) + if isinstance(layer, InvertedResidual): + modified_module = layer.depthwise_conv.conv + else: + modified_module = layer.conv + + if i < 13: + modified_module.dilation = (2, 2) + pad = 2 + else: + modified_module.dilation = (4, 4) + pad = 4 + + if not isinstance(modified_module, Conv2dAdaptivePadding): + # Adjust padding + pad *= (modified_module.kernel_size[0] - 1) // 2 + modified_module.padding = (pad, pad) + + return layers + + def init_weights(self, pretrained=None): + if isinstance(pretrained, str): + logger = logging.getLogger() + load_checkpoint(self, pretrained, strict=False, logger=logger) + elif pretrained is None: + for m in self.modules(): + if isinstance(m, nn.Conv2d): + kaiming_init(m) + elif isinstance(m, nn.BatchNorm2d): + constant_init(m, 1) + else: + raise TypeError('pretrained must be a str or None') + + def forward(self, x): + outs = [] + for i, layer_name in enumerate(self.layers): + layer = getattr(self, layer_name) + x = layer(x) + if i in self.out_indices: + outs.append(x) + return outs + + def _freeze_stages(self): + for i in range(self.frozen_stages + 1): + layer = getattr(self, f'layer{i}') + layer.eval() + for param in layer.parameters(): + param.requires_grad = False + + def train(self, mode=True): + super(MobileNetV3, self).train(mode) + self._freeze_stages() + if mode and self.norm_eval: + for m in self.modules(): + if isinstance(m, _BatchNorm): + m.eval() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/resnest.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/resnest.py new file mode 100644 index 0000000000000000000000000000000000000000..076ef62195bac2a9660261446b5756c3880dfdf2 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/resnest.py @@ -0,0 +1,314 @@ +import math + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.utils.checkpoint as cp +from annotator.mmpkg.mmcv.cnn import build_conv_layer, build_norm_layer + +from ..builder import BACKBONES +from ..utils import ResLayer +from .resnet import Bottleneck as _Bottleneck +from .resnet import ResNetV1d + + +class RSoftmax(nn.Module): + """Radix Softmax module in ``SplitAttentionConv2d``. + + Args: + radix (int): Radix of input. + groups (int): Groups of input. + """ + + def __init__(self, radix, groups): + super().__init__() + self.radix = radix + self.groups = groups + + def forward(self, x): + batch = x.size(0) + if self.radix > 1: + x = x.view(batch, self.groups, self.radix, -1).transpose(1, 2) + x = F.softmax(x, dim=1) + x = x.reshape(batch, -1) + else: + x = torch.sigmoid(x) + return x + + +class SplitAttentionConv2d(nn.Module): + """Split-Attention Conv2d in ResNeSt. + + Args: + in_channels (int): Same as nn.Conv2d. + out_channels (int): Same as nn.Conv2d. + kernel_size (int | tuple[int]): Same as nn.Conv2d. + stride (int | tuple[int]): Same as nn.Conv2d. + padding (int | tuple[int]): Same as nn.Conv2d. + dilation (int | tuple[int]): Same as nn.Conv2d. + groups (int): Same as nn.Conv2d. + radix (int): Radix of SpltAtConv2d. Default: 2 + reduction_factor (int): Reduction factor of inter_channels. Default: 4. + conv_cfg (dict): Config dict for convolution layer. Default: None, + which means using conv2d. + norm_cfg (dict): Config dict for normalization layer. Default: None. + dcn (dict): Config dict for DCN. Default: None. + """ + + def __init__(self, + in_channels, + channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + radix=2, + reduction_factor=4, + conv_cfg=None, + norm_cfg=dict(type='BN'), + dcn=None): + super(SplitAttentionConv2d, self).__init__() + inter_channels = max(in_channels * radix // reduction_factor, 32) + self.radix = radix + self.groups = groups + self.channels = channels + self.with_dcn = dcn is not None + self.dcn = dcn + fallback_on_stride = False + if self.with_dcn: + fallback_on_stride = self.dcn.pop('fallback_on_stride', False) + if self.with_dcn and not fallback_on_stride: + assert conv_cfg is None, 'conv_cfg must be None for DCN' + conv_cfg = dcn + self.conv = build_conv_layer( + conv_cfg, + in_channels, + channels * radix, + kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups * radix, + bias=False) + self.norm0_name, norm0 = build_norm_layer( + norm_cfg, channels * radix, postfix=0) + self.add_module(self.norm0_name, norm0) + self.relu = nn.ReLU(inplace=True) + self.fc1 = build_conv_layer( + None, channels, inter_channels, 1, groups=self.groups) + self.norm1_name, norm1 = build_norm_layer( + norm_cfg, inter_channels, postfix=1) + self.add_module(self.norm1_name, norm1) + self.fc2 = build_conv_layer( + None, inter_channels, channels * radix, 1, groups=self.groups) + self.rsoftmax = RSoftmax(radix, groups) + + @property + def norm0(self): + """nn.Module: the normalization layer named "norm0" """ + return getattr(self, self.norm0_name) + + @property + def norm1(self): + """nn.Module: the normalization layer named "norm1" """ + return getattr(self, self.norm1_name) + + def forward(self, x): + x = self.conv(x) + x = self.norm0(x) + x = self.relu(x) + + batch, rchannel = x.shape[:2] + batch = x.size(0) + if self.radix > 1: + splits = x.view(batch, self.radix, -1, *x.shape[2:]) + gap = splits.sum(dim=1) + else: + gap = x + gap = F.adaptive_avg_pool2d(gap, 1) + gap = self.fc1(gap) + + gap = self.norm1(gap) + gap = self.relu(gap) + + atten = self.fc2(gap) + atten = self.rsoftmax(atten).view(batch, -1, 1, 1) + + if self.radix > 1: + attens = atten.view(batch, self.radix, -1, *atten.shape[2:]) + out = torch.sum(attens * splits, dim=1) + else: + out = atten * x + return out.contiguous() + + +class Bottleneck(_Bottleneck): + """Bottleneck block for ResNeSt. + + Args: + inplane (int): Input planes of this block. + planes (int): Middle planes of this block. + groups (int): Groups of conv2. + width_per_group (int): Width per group of conv2. 64x4d indicates + ``groups=64, width_per_group=4`` and 32x8d indicates + ``groups=32, width_per_group=8``. + radix (int): Radix of SpltAtConv2d. Default: 2 + reduction_factor (int): Reduction factor of inter_channels in + SplitAttentionConv2d. Default: 4. + avg_down_stride (bool): Whether to use average pool for stride in + Bottleneck. Default: True. + kwargs (dict): Key word arguments for base class. + """ + expansion = 4 + + def __init__(self, + inplanes, + planes, + groups=1, + base_width=4, + base_channels=64, + radix=2, + reduction_factor=4, + avg_down_stride=True, + **kwargs): + """Bottleneck block for ResNeSt.""" + super(Bottleneck, self).__init__(inplanes, planes, **kwargs) + + if groups == 1: + width = self.planes + else: + width = math.floor(self.planes * + (base_width / base_channels)) * groups + + self.avg_down_stride = avg_down_stride and self.conv2_stride > 1 + + self.norm1_name, norm1 = build_norm_layer( + self.norm_cfg, width, postfix=1) + self.norm3_name, norm3 = build_norm_layer( + self.norm_cfg, self.planes * self.expansion, postfix=3) + + self.conv1 = build_conv_layer( + self.conv_cfg, + self.inplanes, + width, + kernel_size=1, + stride=self.conv1_stride, + bias=False) + self.add_module(self.norm1_name, norm1) + self.with_modulated_dcn = False + self.conv2 = SplitAttentionConv2d( + width, + width, + kernel_size=3, + stride=1 if self.avg_down_stride else self.conv2_stride, + padding=self.dilation, + dilation=self.dilation, + groups=groups, + radix=radix, + reduction_factor=reduction_factor, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + dcn=self.dcn) + delattr(self, self.norm2_name) + + if self.avg_down_stride: + self.avd_layer = nn.AvgPool2d(3, self.conv2_stride, padding=1) + + self.conv3 = build_conv_layer( + self.conv_cfg, + width, + self.planes * self.expansion, + kernel_size=1, + bias=False) + self.add_module(self.norm3_name, norm3) + + def forward(self, x): + + def _inner_forward(x): + identity = x + + out = self.conv1(x) + out = self.norm1(out) + out = self.relu(out) + + if self.with_plugins: + out = self.forward_plugin(out, self.after_conv1_plugin_names) + + out = self.conv2(out) + + if self.avg_down_stride: + out = self.avd_layer(out) + + if self.with_plugins: + out = self.forward_plugin(out, self.after_conv2_plugin_names) + + out = self.conv3(out) + out = self.norm3(out) + + if self.with_plugins: + out = self.forward_plugin(out, self.after_conv3_plugin_names) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + + return out + + if self.with_cp and x.requires_grad: + out = cp.checkpoint(_inner_forward, x) + else: + out = _inner_forward(x) + + out = self.relu(out) + + return out + + +@BACKBONES.register_module() +class ResNeSt(ResNetV1d): + """ResNeSt backbone. + + Args: + groups (int): Number of groups of Bottleneck. Default: 1 + base_width (int): Base width of Bottleneck. Default: 4 + radix (int): Radix of SpltAtConv2d. Default: 2 + reduction_factor (int): Reduction factor of inter_channels in + SplitAttentionConv2d. Default: 4. + avg_down_stride (bool): Whether to use average pool for stride in + Bottleneck. Default: True. + kwargs (dict): Keyword arguments for ResNet. + """ + + arch_settings = { + 50: (Bottleneck, (3, 4, 6, 3)), + 101: (Bottleneck, (3, 4, 23, 3)), + 152: (Bottleneck, (3, 8, 36, 3)), + 200: (Bottleneck, (3, 24, 36, 3)) + } + + def __init__(self, + groups=1, + base_width=4, + radix=2, + reduction_factor=4, + avg_down_stride=True, + **kwargs): + self.groups = groups + self.base_width = base_width + self.radix = radix + self.reduction_factor = reduction_factor + self.avg_down_stride = avg_down_stride + super(ResNeSt, self).__init__(**kwargs) + + def make_res_layer(self, **kwargs): + """Pack all blocks in a stage into a ``ResLayer``.""" + return ResLayer( + groups=self.groups, + base_width=self.base_width, + base_channels=self.base_channels, + radix=self.radix, + reduction_factor=self.reduction_factor, + avg_down_stride=self.avg_down_stride, + **kwargs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/resnet.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/resnet.py new file mode 100644 index 0000000000000000000000000000000000000000..b3304dc5238110adcf21fa4c0a4e230158894fea --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/resnet.py @@ -0,0 +1,688 @@ +import torch.nn as nn +import torch.utils.checkpoint as cp +from annotator.mmpkg.mmcv.cnn import (build_conv_layer, build_norm_layer, build_plugin_layer, + constant_init, kaiming_init) +from annotator.mmpkg.mmcv.runner import load_checkpoint +from annotator.mmpkg.mmcv.utils.parrots_wrapper import _BatchNorm + +from annotator.mmpkg.mmseg.utils import get_root_logger +from ..builder import BACKBONES +from ..utils import ResLayer + + +class BasicBlock(nn.Module): + """Basic block for ResNet.""" + + expansion = 1 + + def __init__(self, + inplanes, + planes, + stride=1, + dilation=1, + downsample=None, + style='pytorch', + with_cp=False, + conv_cfg=None, + norm_cfg=dict(type='BN'), + dcn=None, + plugins=None): + super(BasicBlock, self).__init__() + assert dcn is None, 'Not implemented yet.' + assert plugins is None, 'Not implemented yet.' + + self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) + self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) + + self.conv1 = build_conv_layer( + conv_cfg, + inplanes, + planes, + 3, + stride=stride, + padding=dilation, + dilation=dilation, + bias=False) + self.add_module(self.norm1_name, norm1) + self.conv2 = build_conv_layer( + conv_cfg, planes, planes, 3, padding=1, bias=False) + self.add_module(self.norm2_name, norm2) + + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + self.dilation = dilation + self.with_cp = with_cp + + @property + def norm1(self): + """nn.Module: normalization layer after the first convolution layer""" + return getattr(self, self.norm1_name) + + @property + def norm2(self): + """nn.Module: normalization layer after the second convolution layer""" + return getattr(self, self.norm2_name) + + def forward(self, x): + """Forward function.""" + + def _inner_forward(x): + identity = x + + out = self.conv1(x) + out = self.norm1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.norm2(out) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + + return out + + if self.with_cp and x.requires_grad: + out = cp.checkpoint(_inner_forward, x) + else: + out = _inner_forward(x) + + out = self.relu(out) + + return out + + +class Bottleneck(nn.Module): + """Bottleneck block for ResNet. + + If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is + "caffe", the stride-two layer is the first 1x1 conv layer. + """ + + expansion = 4 + + def __init__(self, + inplanes, + planes, + stride=1, + dilation=1, + downsample=None, + style='pytorch', + with_cp=False, + conv_cfg=None, + norm_cfg=dict(type='BN'), + dcn=None, + plugins=None): + super(Bottleneck, self).__init__() + assert style in ['pytorch', 'caffe'] + assert dcn is None or isinstance(dcn, dict) + assert plugins is None or isinstance(plugins, list) + if plugins is not None: + allowed_position = ['after_conv1', 'after_conv2', 'after_conv3'] + assert all(p['position'] in allowed_position for p in plugins) + + self.inplanes = inplanes + self.planes = planes + self.stride = stride + self.dilation = dilation + self.style = style + self.with_cp = with_cp + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.dcn = dcn + self.with_dcn = dcn is not None + self.plugins = plugins + self.with_plugins = plugins is not None + + if self.with_plugins: + # collect plugins for conv1/conv2/conv3 + self.after_conv1_plugins = [ + plugin['cfg'] for plugin in plugins + if plugin['position'] == 'after_conv1' + ] + self.after_conv2_plugins = [ + plugin['cfg'] for plugin in plugins + if plugin['position'] == 'after_conv2' + ] + self.after_conv3_plugins = [ + plugin['cfg'] for plugin in plugins + if plugin['position'] == 'after_conv3' + ] + + if self.style == 'pytorch': + self.conv1_stride = 1 + self.conv2_stride = stride + else: + self.conv1_stride = stride + self.conv2_stride = 1 + + self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) + self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) + self.norm3_name, norm3 = build_norm_layer( + norm_cfg, planes * self.expansion, postfix=3) + + self.conv1 = build_conv_layer( + conv_cfg, + inplanes, + planes, + kernel_size=1, + stride=self.conv1_stride, + bias=False) + self.add_module(self.norm1_name, norm1) + fallback_on_stride = False + if self.with_dcn: + fallback_on_stride = dcn.pop('fallback_on_stride', False) + if not self.with_dcn or fallback_on_stride: + self.conv2 = build_conv_layer( + conv_cfg, + planes, + planes, + kernel_size=3, + stride=self.conv2_stride, + padding=dilation, + dilation=dilation, + bias=False) + else: + assert self.conv_cfg is None, 'conv_cfg must be None for DCN' + self.conv2 = build_conv_layer( + dcn, + planes, + planes, + kernel_size=3, + stride=self.conv2_stride, + padding=dilation, + dilation=dilation, + bias=False) + + self.add_module(self.norm2_name, norm2) + self.conv3 = build_conv_layer( + conv_cfg, + planes, + planes * self.expansion, + kernel_size=1, + bias=False) + self.add_module(self.norm3_name, norm3) + + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + + if self.with_plugins: + self.after_conv1_plugin_names = self.make_block_plugins( + planes, self.after_conv1_plugins) + self.after_conv2_plugin_names = self.make_block_plugins( + planes, self.after_conv2_plugins) + self.after_conv3_plugin_names = self.make_block_plugins( + planes * self.expansion, self.after_conv3_plugins) + + def make_block_plugins(self, in_channels, plugins): + """make plugins for block. + + Args: + in_channels (int): Input channels of plugin. + plugins (list[dict]): List of plugins cfg to build. + + Returns: + list[str]: List of the names of plugin. + """ + assert isinstance(plugins, list) + plugin_names = [] + for plugin in plugins: + plugin = plugin.copy() + name, layer = build_plugin_layer( + plugin, + in_channels=in_channels, + postfix=plugin.pop('postfix', '')) + assert not hasattr(self, name), f'duplicate plugin {name}' + self.add_module(name, layer) + plugin_names.append(name) + return plugin_names + + def forward_plugin(self, x, plugin_names): + """Forward function for plugins.""" + out = x + for name in plugin_names: + out = getattr(self, name)(x) + return out + + @property + def norm1(self): + """nn.Module: normalization layer after the first convolution layer""" + return getattr(self, self.norm1_name) + + @property + def norm2(self): + """nn.Module: normalization layer after the second convolution layer""" + return getattr(self, self.norm2_name) + + @property + def norm3(self): + """nn.Module: normalization layer after the third convolution layer""" + return getattr(self, self.norm3_name) + + def forward(self, x): + """Forward function.""" + + def _inner_forward(x): + identity = x + + out = self.conv1(x) + out = self.norm1(out) + out = self.relu(out) + + if self.with_plugins: + out = self.forward_plugin(out, self.after_conv1_plugin_names) + + out = self.conv2(out) + out = self.norm2(out) + out = self.relu(out) + + if self.with_plugins: + out = self.forward_plugin(out, self.after_conv2_plugin_names) + + out = self.conv3(out) + out = self.norm3(out) + + if self.with_plugins: + out = self.forward_plugin(out, self.after_conv3_plugin_names) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + + return out + + if self.with_cp and x.requires_grad: + out = cp.checkpoint(_inner_forward, x) + else: + out = _inner_forward(x) + + out = self.relu(out) + + return out + + +@BACKBONES.register_module() +class ResNet(nn.Module): + """ResNet backbone. + + Args: + depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. + in_channels (int): Number of input image channels. Default" 3. + stem_channels (int): Number of stem channels. Default: 64. + base_channels (int): Number of base channels of res layer. Default: 64. + num_stages (int): Resnet stages, normally 4. + strides (Sequence[int]): Strides of the first block of each stage. + dilations (Sequence[int]): Dilation of each stage. + out_indices (Sequence[int]): Output from which stages. + style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two + layer is the 3x3 conv layer, otherwise the stride-two layer is + the first 1x1 conv layer. + deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv + avg_down (bool): Use AvgPool instead of stride conv when + downsampling in the bottleneck. + frozen_stages (int): Stages to be frozen (stop grad and set eval mode). + -1 means not freezing any parameters. + norm_cfg (dict): Dictionary to construct and config norm layer. + norm_eval (bool): Whether to set norm layers to eval mode, namely, + freeze running stats (mean and var). Note: Effect on Batch Norm + and its variants only. + plugins (list[dict]): List of plugins for stages, each dict contains: + + - cfg (dict, required): Cfg dict to build plugin. + + - position (str, required): Position inside block to insert plugin, + options: 'after_conv1', 'after_conv2', 'after_conv3'. + + - stages (tuple[bool], optional): Stages to apply plugin, length + should be same as 'num_stages' + multi_grid (Sequence[int]|None): Multi grid dilation rates of last + stage. Default: None + contract_dilation (bool): Whether contract first dilation of each layer + Default: False + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. + zero_init_residual (bool): Whether to use zero init for last norm layer + in resblocks to let them behave as identity. + + Example: + >>> from annotator.mmpkg.mmseg.models import ResNet + >>> import torch + >>> self = ResNet(depth=18) + >>> self.eval() + >>> inputs = torch.rand(1, 3, 32, 32) + >>> level_outputs = self.forward(inputs) + >>> for level_out in level_outputs: + ... print(tuple(level_out.shape)) + (1, 64, 8, 8) + (1, 128, 4, 4) + (1, 256, 2, 2) + (1, 512, 1, 1) + """ + + arch_settings = { + 18: (BasicBlock, (2, 2, 2, 2)), + 34: (BasicBlock, (3, 4, 6, 3)), + 50: (Bottleneck, (3, 4, 6, 3)), + 101: (Bottleneck, (3, 4, 23, 3)), + 152: (Bottleneck, (3, 8, 36, 3)) + } + + def __init__(self, + depth, + in_channels=3, + stem_channels=64, + base_channels=64, + num_stages=4, + strides=(1, 2, 2, 2), + dilations=(1, 1, 1, 1), + out_indices=(0, 1, 2, 3), + style='pytorch', + deep_stem=False, + avg_down=False, + frozen_stages=-1, + conv_cfg=None, + norm_cfg=dict(type='BN', requires_grad=True), + norm_eval=False, + dcn=None, + stage_with_dcn=(False, False, False, False), + plugins=None, + multi_grid=None, + contract_dilation=False, + with_cp=False, + zero_init_residual=True): + super(ResNet, self).__init__() + if depth not in self.arch_settings: + raise KeyError(f'invalid depth {depth} for resnet') + self.depth = depth + self.stem_channels = stem_channels + self.base_channels = base_channels + self.num_stages = num_stages + assert num_stages >= 1 and num_stages <= 4 + self.strides = strides + self.dilations = dilations + assert len(strides) == len(dilations) == num_stages + self.out_indices = out_indices + assert max(out_indices) < num_stages + self.style = style + self.deep_stem = deep_stem + self.avg_down = avg_down + self.frozen_stages = frozen_stages + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.with_cp = with_cp + self.norm_eval = norm_eval + self.dcn = dcn + self.stage_with_dcn = stage_with_dcn + if dcn is not None: + assert len(stage_with_dcn) == num_stages + self.plugins = plugins + self.multi_grid = multi_grid + self.contract_dilation = contract_dilation + self.zero_init_residual = zero_init_residual + self.block, stage_blocks = self.arch_settings[depth] + self.stage_blocks = stage_blocks[:num_stages] + self.inplanes = stem_channels + + self._make_stem_layer(in_channels, stem_channels) + + self.res_layers = [] + for i, num_blocks in enumerate(self.stage_blocks): + stride = strides[i] + dilation = dilations[i] + dcn = self.dcn if self.stage_with_dcn[i] else None + if plugins is not None: + stage_plugins = self.make_stage_plugins(plugins, i) + else: + stage_plugins = None + # multi grid is applied to last layer only + stage_multi_grid = multi_grid if i == len( + self.stage_blocks) - 1 else None + planes = base_channels * 2**i + res_layer = self.make_res_layer( + block=self.block, + inplanes=self.inplanes, + planes=planes, + num_blocks=num_blocks, + stride=stride, + dilation=dilation, + style=self.style, + avg_down=self.avg_down, + with_cp=with_cp, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + dcn=dcn, + plugins=stage_plugins, + multi_grid=stage_multi_grid, + contract_dilation=contract_dilation) + self.inplanes = planes * self.block.expansion + layer_name = f'layer{i+1}' + self.add_module(layer_name, res_layer) + self.res_layers.append(layer_name) + + self._freeze_stages() + + self.feat_dim = self.block.expansion * base_channels * 2**( + len(self.stage_blocks) - 1) + + def make_stage_plugins(self, plugins, stage_idx): + """make plugins for ResNet 'stage_idx'th stage . + + Currently we support to insert 'context_block', + 'empirical_attention_block', 'nonlocal_block' into the backbone like + ResNet/ResNeXt. They could be inserted after conv1/conv2/conv3 of + Bottleneck. + + An example of plugins format could be : + >>> plugins=[ + ... dict(cfg=dict(type='xxx', arg1='xxx'), + ... stages=(False, True, True, True), + ... position='after_conv2'), + ... dict(cfg=dict(type='yyy'), + ... stages=(True, True, True, True), + ... position='after_conv3'), + ... dict(cfg=dict(type='zzz', postfix='1'), + ... stages=(True, True, True, True), + ... position='after_conv3'), + ... dict(cfg=dict(type='zzz', postfix='2'), + ... stages=(True, True, True, True), + ... position='after_conv3') + ... ] + >>> self = ResNet(depth=18) + >>> stage_plugins = self.make_stage_plugins(plugins, 0) + >>> assert len(stage_plugins) == 3 + + Suppose 'stage_idx=0', the structure of blocks in the stage would be: + conv1-> conv2->conv3->yyy->zzz1->zzz2 + Suppose 'stage_idx=1', the structure of blocks in the stage would be: + conv1-> conv2->xxx->conv3->yyy->zzz1->zzz2 + + If stages is missing, the plugin would be applied to all stages. + + Args: + plugins (list[dict]): List of plugins cfg to build. The postfix is + required if multiple same type plugins are inserted. + stage_idx (int): Index of stage to build + + Returns: + list[dict]: Plugins for current stage + """ + stage_plugins = [] + for plugin in plugins: + plugin = plugin.copy() + stages = plugin.pop('stages', None) + assert stages is None or len(stages) == self.num_stages + # whether to insert plugin into current stage + if stages is None or stages[stage_idx]: + stage_plugins.append(plugin) + + return stage_plugins + + def make_res_layer(self, **kwargs): + """Pack all blocks in a stage into a ``ResLayer``.""" + return ResLayer(**kwargs) + + @property + def norm1(self): + """nn.Module: the normalization layer named "norm1" """ + return getattr(self, self.norm1_name) + + def _make_stem_layer(self, in_channels, stem_channels): + """Make stem layer for ResNet.""" + if self.deep_stem: + self.stem = nn.Sequential( + build_conv_layer( + self.conv_cfg, + in_channels, + stem_channels // 2, + kernel_size=3, + stride=2, + padding=1, + bias=False), + build_norm_layer(self.norm_cfg, stem_channels // 2)[1], + nn.ReLU(inplace=True), + build_conv_layer( + self.conv_cfg, + stem_channels // 2, + stem_channels // 2, + kernel_size=3, + stride=1, + padding=1, + bias=False), + build_norm_layer(self.norm_cfg, stem_channels // 2)[1], + nn.ReLU(inplace=True), + build_conv_layer( + self.conv_cfg, + stem_channels // 2, + stem_channels, + kernel_size=3, + stride=1, + padding=1, + bias=False), + build_norm_layer(self.norm_cfg, stem_channels)[1], + nn.ReLU(inplace=True)) + else: + self.conv1 = build_conv_layer( + self.conv_cfg, + in_channels, + stem_channels, + kernel_size=7, + stride=2, + padding=3, + bias=False) + self.norm1_name, norm1 = build_norm_layer( + self.norm_cfg, stem_channels, postfix=1) + self.add_module(self.norm1_name, norm1) + self.relu = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + + def _freeze_stages(self): + """Freeze stages param and norm stats.""" + if self.frozen_stages >= 0: + if self.deep_stem: + self.stem.eval() + for param in self.stem.parameters(): + param.requires_grad = False + else: + self.norm1.eval() + for m in [self.conv1, self.norm1]: + for param in m.parameters(): + param.requires_grad = False + + for i in range(1, self.frozen_stages + 1): + m = getattr(self, f'layer{i}') + m.eval() + for param in m.parameters(): + param.requires_grad = False + + def init_weights(self, pretrained=None): + """Initialize the weights in backbone. + + Args: + pretrained (str, optional): Path to pre-trained weights. + Defaults to None. + """ + if isinstance(pretrained, str): + logger = get_root_logger() + load_checkpoint(self, pretrained, strict=False, logger=logger) + elif pretrained is None: + for m in self.modules(): + if isinstance(m, nn.Conv2d): + kaiming_init(m) + elif isinstance(m, (_BatchNorm, nn.GroupNorm)): + constant_init(m, 1) + + if self.dcn is not None: + for m in self.modules(): + if isinstance(m, Bottleneck) and hasattr( + m, 'conv2_offset'): + constant_init(m.conv2_offset, 0) + + if self.zero_init_residual: + for m in self.modules(): + if isinstance(m, Bottleneck): + constant_init(m.norm3, 0) + elif isinstance(m, BasicBlock): + constant_init(m.norm2, 0) + else: + raise TypeError('pretrained must be a str or None') + + def forward(self, x): + """Forward function.""" + if self.deep_stem: + x = self.stem(x) + else: + x = self.conv1(x) + x = self.norm1(x) + x = self.relu(x) + x = self.maxpool(x) + outs = [] + for i, layer_name in enumerate(self.res_layers): + res_layer = getattr(self, layer_name) + x = res_layer(x) + if i in self.out_indices: + outs.append(x) + return tuple(outs) + + def train(self, mode=True): + """Convert the model into training mode while keep normalization layer + freezed.""" + super(ResNet, self).train(mode) + self._freeze_stages() + if mode and self.norm_eval: + for m in self.modules(): + # trick: eval have effect on BatchNorm only + if isinstance(m, _BatchNorm): + m.eval() + + +@BACKBONES.register_module() +class ResNetV1c(ResNet): + """ResNetV1c variant described in [1]_. + + Compared with default ResNet(ResNetV1b), ResNetV1c replaces the 7x7 conv + in the input stem with three 3x3 convs. + + References: + .. [1] https://arxiv.org/pdf/1812.01187.pdf + """ + + def __init__(self, **kwargs): + super(ResNetV1c, self).__init__( + deep_stem=True, avg_down=False, **kwargs) + + +@BACKBONES.register_module() +class ResNetV1d(ResNet): + """ResNetV1d variant described in [1]_. + + Compared with default ResNet(ResNetV1b), ResNetV1d replaces the 7x7 conv in + the input stem with three 3x3 convs. And in the downsampling block, a 2x2 + avg_pool with stride 2 is added before conv, whose stride is changed to 1. + """ + + def __init__(self, **kwargs): + super(ResNetV1d, self).__init__( + deep_stem=True, avg_down=True, **kwargs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/resnext.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/resnext.py new file mode 100644 index 0000000000000000000000000000000000000000..be0194da1714e8431309a9dd8a42afebdbc1baf5 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/resnext.py @@ -0,0 +1,145 @@ +import math + +from annotator.mmpkg.mmcv.cnn import build_conv_layer, build_norm_layer + +from ..builder import BACKBONES +from ..utils import ResLayer +from .resnet import Bottleneck as _Bottleneck +from .resnet import ResNet + + +class Bottleneck(_Bottleneck): + """Bottleneck block for ResNeXt. + + If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is + "caffe", the stride-two layer is the first 1x1 conv layer. + """ + + def __init__(self, + inplanes, + planes, + groups=1, + base_width=4, + base_channels=64, + **kwargs): + super(Bottleneck, self).__init__(inplanes, planes, **kwargs) + + if groups == 1: + width = self.planes + else: + width = math.floor(self.planes * + (base_width / base_channels)) * groups + + self.norm1_name, norm1 = build_norm_layer( + self.norm_cfg, width, postfix=1) + self.norm2_name, norm2 = build_norm_layer( + self.norm_cfg, width, postfix=2) + self.norm3_name, norm3 = build_norm_layer( + self.norm_cfg, self.planes * self.expansion, postfix=3) + + self.conv1 = build_conv_layer( + self.conv_cfg, + self.inplanes, + width, + kernel_size=1, + stride=self.conv1_stride, + bias=False) + self.add_module(self.norm1_name, norm1) + fallback_on_stride = False + self.with_modulated_dcn = False + if self.with_dcn: + fallback_on_stride = self.dcn.pop('fallback_on_stride', False) + if not self.with_dcn or fallback_on_stride: + self.conv2 = build_conv_layer( + self.conv_cfg, + width, + width, + kernel_size=3, + stride=self.conv2_stride, + padding=self.dilation, + dilation=self.dilation, + groups=groups, + bias=False) + else: + assert self.conv_cfg is None, 'conv_cfg must be None for DCN' + self.conv2 = build_conv_layer( + self.dcn, + width, + width, + kernel_size=3, + stride=self.conv2_stride, + padding=self.dilation, + dilation=self.dilation, + groups=groups, + bias=False) + + self.add_module(self.norm2_name, norm2) + self.conv3 = build_conv_layer( + self.conv_cfg, + width, + self.planes * self.expansion, + kernel_size=1, + bias=False) + self.add_module(self.norm3_name, norm3) + + +@BACKBONES.register_module() +class ResNeXt(ResNet): + """ResNeXt backbone. + + Args: + depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. + in_channels (int): Number of input image channels. Normally 3. + num_stages (int): Resnet stages, normally 4. + groups (int): Group of resnext. + base_width (int): Base width of resnext. + strides (Sequence[int]): Strides of the first block of each stage. + dilations (Sequence[int]): Dilation of each stage. + out_indices (Sequence[int]): Output from which stages. + style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two + layer is the 3x3 conv layer, otherwise the stride-two layer is + the first 1x1 conv layer. + frozen_stages (int): Stages to be frozen (all param fixed). -1 means + not freezing any parameters. + norm_cfg (dict): dictionary to construct and config norm layer. + norm_eval (bool): Whether to set norm layers to eval mode, namely, + freeze running stats (mean and var). Note: Effect on Batch Norm + and its variants only. + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. + zero_init_residual (bool): whether to use zero init for last norm layer + in resblocks to let them behave as identity. + + Example: + >>> from annotator.mmpkg.mmseg.models import ResNeXt + >>> import torch + >>> self = ResNeXt(depth=50) + >>> self.eval() + >>> inputs = torch.rand(1, 3, 32, 32) + >>> level_outputs = self.forward(inputs) + >>> for level_out in level_outputs: + ... print(tuple(level_out.shape)) + (1, 256, 8, 8) + (1, 512, 4, 4) + (1, 1024, 2, 2) + (1, 2048, 1, 1) + """ + + arch_settings = { + 50: (Bottleneck, (3, 4, 6, 3)), + 101: (Bottleneck, (3, 4, 23, 3)), + 152: (Bottleneck, (3, 8, 36, 3)) + } + + def __init__(self, groups=1, base_width=4, **kwargs): + self.groups = groups + self.base_width = base_width + super(ResNeXt, self).__init__(**kwargs) + + def make_res_layer(self, **kwargs): + """Pack all blocks in a stage into a ``ResLayer``""" + return ResLayer( + groups=self.groups, + base_width=self.base_width, + base_channels=self.base_channels, + **kwargs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/unet.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/unet.py new file mode 100644 index 0000000000000000000000000000000000000000..3d19902ba273af02f8c9ce60f6632634633c1101 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/unet.py @@ -0,0 +1,429 @@ +import torch.nn as nn +import torch.utils.checkpoint as cp +from annotator.mmpkg.mmcv.cnn import (UPSAMPLE_LAYERS, ConvModule, build_activation_layer, + build_norm_layer, constant_init, kaiming_init) +from annotator.mmpkg.mmcv.runner import load_checkpoint +from annotator.mmpkg.mmcv.utils.parrots_wrapper import _BatchNorm + +from annotator.mmpkg.mmseg.utils import get_root_logger +from ..builder import BACKBONES +from ..utils import UpConvBlock + + +class BasicConvBlock(nn.Module): + """Basic convolutional block for UNet. + + This module consists of several plain convolutional layers. + + Args: + in_channels (int): Number of input channels. + out_channels (int): Number of output channels. + num_convs (int): Number of convolutional layers. Default: 2. + stride (int): Whether use stride convolution to downsample + the input feature map. If stride=2, it only uses stride convolution + in the first convolutional layer to downsample the input feature + map. Options are 1 or 2. Default: 1. + dilation (int): Whether use dilated convolution to expand the + receptive field. Set dilation rate of each convolutional layer and + the dilation rate of the first convolutional layer is always 1. + Default: 1. + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. Default: False. + conv_cfg (dict | None): Config dict for convolution layer. + Default: None. + norm_cfg (dict | None): Config dict for normalization layer. + Default: dict(type='BN'). + act_cfg (dict | None): Config dict for activation layer in ConvModule. + Default: dict(type='ReLU'). + dcn (bool): Use deformable convolution in convolutional layer or not. + Default: None. + plugins (dict): plugins for convolutional layers. Default: None. + """ + + def __init__(self, + in_channels, + out_channels, + num_convs=2, + stride=1, + dilation=1, + with_cp=False, + conv_cfg=None, + norm_cfg=dict(type='BN'), + act_cfg=dict(type='ReLU'), + dcn=None, + plugins=None): + super(BasicConvBlock, self).__init__() + assert dcn is None, 'Not implemented yet.' + assert plugins is None, 'Not implemented yet.' + + self.with_cp = with_cp + convs = [] + for i in range(num_convs): + convs.append( + ConvModule( + in_channels=in_channels if i == 0 else out_channels, + out_channels=out_channels, + kernel_size=3, + stride=stride if i == 0 else 1, + dilation=1 if i == 0 else dilation, + padding=1 if i == 0 else dilation, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg)) + + self.convs = nn.Sequential(*convs) + + def forward(self, x): + """Forward function.""" + + if self.with_cp and x.requires_grad: + out = cp.checkpoint(self.convs, x) + else: + out = self.convs(x) + return out + + +@UPSAMPLE_LAYERS.register_module() +class DeconvModule(nn.Module): + """Deconvolution upsample module in decoder for UNet (2X upsample). + + This module uses deconvolution to upsample feature map in the decoder + of UNet. + + Args: + in_channels (int): Number of input channels. + out_channels (int): Number of output channels. + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. Default: False. + norm_cfg (dict | None): Config dict for normalization layer. + Default: dict(type='BN'). + act_cfg (dict | None): Config dict for activation layer in ConvModule. + Default: dict(type='ReLU'). + kernel_size (int): Kernel size of the convolutional layer. Default: 4. + """ + + def __init__(self, + in_channels, + out_channels, + with_cp=False, + norm_cfg=dict(type='BN'), + act_cfg=dict(type='ReLU'), + *, + kernel_size=4, + scale_factor=2): + super(DeconvModule, self).__init__() + + assert (kernel_size - scale_factor >= 0) and\ + (kernel_size - scale_factor) % 2 == 0,\ + f'kernel_size should be greater than or equal to scale_factor '\ + f'and (kernel_size - scale_factor) should be even numbers, '\ + f'while the kernel size is {kernel_size} and scale_factor is '\ + f'{scale_factor}.' + + stride = scale_factor + padding = (kernel_size - scale_factor) // 2 + self.with_cp = with_cp + deconv = nn.ConvTranspose2d( + in_channels, + out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding) + + norm_name, norm = build_norm_layer(norm_cfg, out_channels) + activate = build_activation_layer(act_cfg) + self.deconv_upsamping = nn.Sequential(deconv, norm, activate) + + def forward(self, x): + """Forward function.""" + + if self.with_cp and x.requires_grad: + out = cp.checkpoint(self.deconv_upsamping, x) + else: + out = self.deconv_upsamping(x) + return out + + +@UPSAMPLE_LAYERS.register_module() +class InterpConv(nn.Module): + """Interpolation upsample module in decoder for UNet. + + This module uses interpolation to upsample feature map in the decoder + of UNet. It consists of one interpolation upsample layer and one + convolutional layer. It can be one interpolation upsample layer followed + by one convolutional layer (conv_first=False) or one convolutional layer + followed by one interpolation upsample layer (conv_first=True). + + Args: + in_channels (int): Number of input channels. + out_channels (int): Number of output channels. + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. Default: False. + norm_cfg (dict | None): Config dict for normalization layer. + Default: dict(type='BN'). + act_cfg (dict | None): Config dict for activation layer in ConvModule. + Default: dict(type='ReLU'). + conv_cfg (dict | None): Config dict for convolution layer. + Default: None. + conv_first (bool): Whether convolutional layer or interpolation + upsample layer first. Default: False. It means interpolation + upsample layer followed by one convolutional layer. + kernel_size (int): Kernel size of the convolutional layer. Default: 1. + stride (int): Stride of the convolutional layer. Default: 1. + padding (int): Padding of the convolutional layer. Default: 1. + upsample_cfg (dict): Interpolation config of the upsample layer. + Default: dict( + scale_factor=2, mode='bilinear', align_corners=False). + """ + + def __init__(self, + in_channels, + out_channels, + with_cp=False, + norm_cfg=dict(type='BN'), + act_cfg=dict(type='ReLU'), + *, + conv_cfg=None, + conv_first=False, + kernel_size=1, + stride=1, + padding=0, + upsample_cfg=dict( + scale_factor=2, mode='bilinear', align_corners=False)): + super(InterpConv, self).__init__() + + self.with_cp = with_cp + conv = ConvModule( + in_channels, + out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + upsample = nn.Upsample(**upsample_cfg) + if conv_first: + self.interp_upsample = nn.Sequential(conv, upsample) + else: + self.interp_upsample = nn.Sequential(upsample, conv) + + def forward(self, x): + """Forward function.""" + + if self.with_cp and x.requires_grad: + out = cp.checkpoint(self.interp_upsample, x) + else: + out = self.interp_upsample(x) + return out + + +@BACKBONES.register_module() +class UNet(nn.Module): + """UNet backbone. + U-Net: Convolutional Networks for Biomedical Image Segmentation. + https://arxiv.org/pdf/1505.04597.pdf + + Args: + in_channels (int): Number of input image channels. Default" 3. + base_channels (int): Number of base channels of each stage. + The output channels of the first stage. Default: 64. + num_stages (int): Number of stages in encoder, normally 5. Default: 5. + strides (Sequence[int 1 | 2]): Strides of each stage in encoder. + len(strides) is equal to num_stages. Normally the stride of the + first stage in encoder is 1. If strides[i]=2, it uses stride + convolution to downsample in the correspondence encoder stage. + Default: (1, 1, 1, 1, 1). + enc_num_convs (Sequence[int]): Number of convolutional layers in the + convolution block of the correspondence encoder stage. + Default: (2, 2, 2, 2, 2). + dec_num_convs (Sequence[int]): Number of convolutional layers in the + convolution block of the correspondence decoder stage. + Default: (2, 2, 2, 2). + downsamples (Sequence[int]): Whether use MaxPool to downsample the + feature map after the first stage of encoder + (stages: [1, num_stages)). If the correspondence encoder stage use + stride convolution (strides[i]=2), it will never use MaxPool to + downsample, even downsamples[i-1]=True. + Default: (True, True, True, True). + enc_dilations (Sequence[int]): Dilation rate of each stage in encoder. + Default: (1, 1, 1, 1, 1). + dec_dilations (Sequence[int]): Dilation rate of each stage in decoder. + Default: (1, 1, 1, 1). + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. Default: False. + conv_cfg (dict | None): Config dict for convolution layer. + Default: None. + norm_cfg (dict | None): Config dict for normalization layer. + Default: dict(type='BN'). + act_cfg (dict | None): Config dict for activation layer in ConvModule. + Default: dict(type='ReLU'). + upsample_cfg (dict): The upsample config of the upsample module in + decoder. Default: dict(type='InterpConv'). + norm_eval (bool): Whether to set norm layers to eval mode, namely, + freeze running stats (mean and var). Note: Effect on Batch Norm + and its variants only. Default: False. + dcn (bool): Use deformable convolution in convolutional layer or not. + Default: None. + plugins (dict): plugins for convolutional layers. Default: None. + + Notice: + The input image size should be divisible by the whole downsample rate + of the encoder. More detail of the whole downsample rate can be found + in UNet._check_input_divisible. + + """ + + def __init__(self, + in_channels=3, + base_channels=64, + num_stages=5, + strides=(1, 1, 1, 1, 1), + enc_num_convs=(2, 2, 2, 2, 2), + dec_num_convs=(2, 2, 2, 2), + downsamples=(True, True, True, True), + enc_dilations=(1, 1, 1, 1, 1), + dec_dilations=(1, 1, 1, 1), + with_cp=False, + conv_cfg=None, + norm_cfg=dict(type='BN'), + act_cfg=dict(type='ReLU'), + upsample_cfg=dict(type='InterpConv'), + norm_eval=False, + dcn=None, + plugins=None): + super(UNet, self).__init__() + assert dcn is None, 'Not implemented yet.' + assert plugins is None, 'Not implemented yet.' + assert len(strides) == num_stages, \ + 'The length of strides should be equal to num_stages, '\ + f'while the strides is {strides}, the length of '\ + f'strides is {len(strides)}, and the num_stages is '\ + f'{num_stages}.' + assert len(enc_num_convs) == num_stages, \ + 'The length of enc_num_convs should be equal to num_stages, '\ + f'while the enc_num_convs is {enc_num_convs}, the length of '\ + f'enc_num_convs is {len(enc_num_convs)}, and the num_stages is '\ + f'{num_stages}.' + assert len(dec_num_convs) == (num_stages-1), \ + 'The length of dec_num_convs should be equal to (num_stages-1), '\ + f'while the dec_num_convs is {dec_num_convs}, the length of '\ + f'dec_num_convs is {len(dec_num_convs)}, and the num_stages is '\ + f'{num_stages}.' + assert len(downsamples) == (num_stages-1), \ + 'The length of downsamples should be equal to (num_stages-1), '\ + f'while the downsamples is {downsamples}, the length of '\ + f'downsamples is {len(downsamples)}, and the num_stages is '\ + f'{num_stages}.' + assert len(enc_dilations) == num_stages, \ + 'The length of enc_dilations should be equal to num_stages, '\ + f'while the enc_dilations is {enc_dilations}, the length of '\ + f'enc_dilations is {len(enc_dilations)}, and the num_stages is '\ + f'{num_stages}.' + assert len(dec_dilations) == (num_stages-1), \ + 'The length of dec_dilations should be equal to (num_stages-1), '\ + f'while the dec_dilations is {dec_dilations}, the length of '\ + f'dec_dilations is {len(dec_dilations)}, and the num_stages is '\ + f'{num_stages}.' + self.num_stages = num_stages + self.strides = strides + self.downsamples = downsamples + self.norm_eval = norm_eval + self.base_channels = base_channels + + self.encoder = nn.ModuleList() + self.decoder = nn.ModuleList() + + for i in range(num_stages): + enc_conv_block = [] + if i != 0: + if strides[i] == 1 and downsamples[i - 1]: + enc_conv_block.append(nn.MaxPool2d(kernel_size=2)) + upsample = (strides[i] != 1 or downsamples[i - 1]) + self.decoder.append( + UpConvBlock( + conv_block=BasicConvBlock, + in_channels=base_channels * 2**i, + skip_channels=base_channels * 2**(i - 1), + out_channels=base_channels * 2**(i - 1), + num_convs=dec_num_convs[i - 1], + stride=1, + dilation=dec_dilations[i - 1], + with_cp=with_cp, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg, + upsample_cfg=upsample_cfg if upsample else None, + dcn=None, + plugins=None)) + + enc_conv_block.append( + BasicConvBlock( + in_channels=in_channels, + out_channels=base_channels * 2**i, + num_convs=enc_num_convs[i], + stride=strides[i], + dilation=enc_dilations[i], + with_cp=with_cp, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg, + dcn=None, + plugins=None)) + self.encoder.append((nn.Sequential(*enc_conv_block))) + in_channels = base_channels * 2**i + + def forward(self, x): + self._check_input_divisible(x) + enc_outs = [] + for enc in self.encoder: + x = enc(x) + enc_outs.append(x) + dec_outs = [x] + for i in reversed(range(len(self.decoder))): + x = self.decoder[i](enc_outs[i], x) + dec_outs.append(x) + + return dec_outs + + def train(self, mode=True): + """Convert the model into training mode while keep normalization layer + freezed.""" + super(UNet, self).train(mode) + if mode and self.norm_eval: + for m in self.modules(): + # trick: eval have effect on BatchNorm only + if isinstance(m, _BatchNorm): + m.eval() + + def _check_input_divisible(self, x): + h, w = x.shape[-2:] + whole_downsample_rate = 1 + for i in range(1, self.num_stages): + if self.strides[i] == 2 or self.downsamples[i - 1]: + whole_downsample_rate *= 2 + assert (h % whole_downsample_rate == 0) \ + and (w % whole_downsample_rate == 0),\ + f'The input image size {(h, w)} should be divisible by the whole '\ + f'downsample rate {whole_downsample_rate}, when num_stages is '\ + f'{self.num_stages}, strides is {self.strides}, and downsamples '\ + f'is {self.downsamples}.' + + def init_weights(self, pretrained=None): + """Initialize the weights in backbone. + + Args: + pretrained (str, optional): Path to pre-trained weights. + Defaults to None. + """ + if isinstance(pretrained, str): + logger = get_root_logger() + load_checkpoint(self, pretrained, strict=False, logger=logger) + elif pretrained is None: + for m in self.modules(): + if isinstance(m, nn.Conv2d): + kaiming_init(m) + elif isinstance(m, (_BatchNorm, nn.GroupNorm)): + constant_init(m, 1) + else: + raise TypeError('pretrained must be a str or None') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/vit.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..ab1a393741b21c8185f4204946b751b1913ef98c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/backbones/vit.py @@ -0,0 +1,459 @@ +"""Modified from https://github.com/rwightman/pytorch-image- +models/blob/master/timm/models/vision_transformer.py.""" + +import math + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.utils.checkpoint as cp +from annotator.mmpkg.mmcv.cnn import (Conv2d, Linear, build_activation_layer, build_norm_layer, + constant_init, kaiming_init, normal_init) +from annotator.mmpkg.mmcv.runner import _load_checkpoint +from annotator.mmpkg.mmcv.utils.parrots_wrapper import _BatchNorm + +from annotator.mmpkg.mmseg.utils import get_root_logger +from ..builder import BACKBONES +from ..utils import DropPath, trunc_normal_ + + +class Mlp(nn.Module): + """MLP layer for Encoder block. + + Args: + in_features(int): Input dimension for the first fully + connected layer. + hidden_features(int): Output dimension for the first fully + connected layer. + out_features(int): Output dementsion for the second fully + connected layer. + act_cfg(dict): Config dict for activation layer. + Default: dict(type='GELU'). + drop(float): Drop rate for the dropout layer. Dropout rate has + to be between 0 and 1. Default: 0. + """ + + def __init__(self, + in_features, + hidden_features=None, + out_features=None, + act_cfg=dict(type='GELU'), + drop=0.): + super(Mlp, self).__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = Linear(in_features, hidden_features) + self.act = build_activation_layer(act_cfg) + self.fc2 = Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +class Attention(nn.Module): + """Attention layer for Encoder block. + + Args: + dim (int): Dimension for the input vector. + num_heads (int): Number of parallel attention heads. + qkv_bias (bool): Enable bias for qkv if True. Default: False. + qk_scale (float): Override default qk scale of head_dim ** -0.5 if set. + attn_drop (float): Drop rate for attention output weights. + Default: 0. + proj_drop (float): Drop rate for output weights. Default: 0. + """ + + def __init__(self, + dim, + num_heads=8, + qkv_bias=False, + qk_scale=None, + attn_drop=0., + proj_drop=0.): + super(Attention, self).__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + self.scale = qk_scale or head_dim**-0.5 + + self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + + def forward(self, x): + b, n, c = x.shape + qkv = self.qkv(x).reshape(b, n, 3, self.num_heads, + c // self.num_heads).permute(2, 0, 3, 1, 4) + q, k, v = qkv[0], qkv[1], qkv[2] + + attn = (q @ k.transpose(-2, -1)) * self.scale + attn = attn.softmax(dim=-1) + attn = self.attn_drop(attn) + + x = (attn @ v).transpose(1, 2).reshape(b, n, c) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Block(nn.Module): + """Implements encoder block with residual connection. + + Args: + dim (int): The feature dimension. + num_heads (int): Number of parallel attention heads. + mlp_ratio (int): Ratio of mlp hidden dim to embedding dim. + qk_scale (float): Override default qk scale of head_dim ** -0.5 if set. + drop (float): Drop rate for mlp output weights. Default: 0. + attn_drop (float): Drop rate for attention output weights. + Default: 0. + proj_drop (float): Drop rate for attn layer output weights. + Default: 0. + drop_path (float): Drop rate for paths of model. + Default: 0. + act_cfg (dict): Config dict for activation layer. + Default: dict(type='GELU'). + norm_cfg (dict): Config dict for normalization layer. + Default: dict(type='LN', requires_grad=True). + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. Default: False. + """ + + def __init__(self, + dim, + num_heads, + mlp_ratio=4, + qkv_bias=False, + qk_scale=None, + drop=0., + attn_drop=0., + proj_drop=0., + drop_path=0., + act_cfg=dict(type='GELU'), + norm_cfg=dict(type='LN', eps=1e-6), + with_cp=False): + super(Block, self).__init__() + self.with_cp = with_cp + _, self.norm1 = build_norm_layer(norm_cfg, dim) + self.attn = Attention(dim, num_heads, qkv_bias, qk_scale, attn_drop, + proj_drop) + self.drop_path = DropPath( + drop_path) if drop_path > 0. else nn.Identity() + _, self.norm2 = build_norm_layer(norm_cfg, dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp( + in_features=dim, + hidden_features=mlp_hidden_dim, + act_cfg=act_cfg, + drop=drop) + + def forward(self, x): + + def _inner_forward(x): + out = x + self.drop_path(self.attn(self.norm1(x))) + out = out + self.drop_path(self.mlp(self.norm2(out))) + return out + + if self.with_cp and x.requires_grad: + out = cp.checkpoint(_inner_forward, x) + else: + out = _inner_forward(x) + + return out + + +class PatchEmbed(nn.Module): + """Image to Patch Embedding. + + Args: + img_size (int | tuple): Input image size. + default: 224. + patch_size (int): Width and height for a patch. + default: 16. + in_channels (int): Input channels for images. Default: 3. + embed_dim (int): The embedding dimension. Default: 768. + """ + + def __init__(self, + img_size=224, + patch_size=16, + in_channels=3, + embed_dim=768): + super(PatchEmbed, self).__init__() + if isinstance(img_size, int): + self.img_size = (img_size, img_size) + elif isinstance(img_size, tuple): + self.img_size = img_size + else: + raise TypeError('img_size must be type of int or tuple') + h, w = self.img_size + self.patch_size = (patch_size, patch_size) + self.num_patches = (h // patch_size) * (w // patch_size) + self.proj = Conv2d( + in_channels, embed_dim, kernel_size=patch_size, stride=patch_size) + + def forward(self, x): + return self.proj(x).flatten(2).transpose(1, 2) + + +@BACKBONES.register_module() +class VisionTransformer(nn.Module): + """Vision transformer backbone. + + A PyTorch impl of : `An Image is Worth 16x16 Words: Transformers for + Image Recognition at Scale` - https://arxiv.org/abs/2010.11929 + + Args: + img_size (tuple): input image size. Default: (224, 224). + patch_size (int, tuple): patch size. Default: 16. + in_channels (int): number of input channels. Default: 3. + embed_dim (int): embedding dimension. Default: 768. + depth (int): depth of transformer. Default: 12. + num_heads (int): number of attention heads. Default: 12. + mlp_ratio (int): ratio of mlp hidden dim to embedding dim. + Default: 4. + out_indices (list | tuple | int): Output from which stages. + Default: -1. + qkv_bias (bool): enable bias for qkv if True. Default: True. + qk_scale (float): override default qk scale of head_dim ** -0.5 if set. + drop_rate (float): dropout rate. Default: 0. + attn_drop_rate (float): attention dropout rate. Default: 0. + drop_path_rate (float): Rate of DropPath. Default: 0. + norm_cfg (dict): Config dict for normalization layer. + Default: dict(type='LN', eps=1e-6, requires_grad=True). + act_cfg (dict): Config dict for activation layer. + Default: dict(type='GELU'). + norm_eval (bool): Whether to set norm layers to eval mode, namely, + freeze running stats (mean and var). Note: Effect on Batch Norm + and its variants only. Default: False. + final_norm (bool): Whether to add a additional layer to normalize + final feature map. Default: False. + interpolate_mode (str): Select the interpolate mode for position + embeding vector resize. Default: bicubic. + with_cls_token (bool): If concatenating class token into image tokens + as transformer input. Default: True. + with_cp (bool): Use checkpoint or not. Using checkpoint + will save some memory while slowing down the training speed. + Default: False. + """ + + def __init__(self, + img_size=(224, 224), + patch_size=16, + in_channels=3, + embed_dim=768, + depth=12, + num_heads=12, + mlp_ratio=4, + out_indices=11, + qkv_bias=True, + qk_scale=None, + drop_rate=0., + attn_drop_rate=0., + drop_path_rate=0., + norm_cfg=dict(type='LN', eps=1e-6, requires_grad=True), + act_cfg=dict(type='GELU'), + norm_eval=False, + final_norm=False, + with_cls_token=True, + interpolate_mode='bicubic', + with_cp=False): + super(VisionTransformer, self).__init__() + self.img_size = img_size + self.patch_size = patch_size + self.features = self.embed_dim = embed_dim + self.patch_embed = PatchEmbed( + img_size=img_size, + patch_size=patch_size, + in_channels=in_channels, + embed_dim=embed_dim) + + self.with_cls_token = with_cls_token + self.cls_token = nn.Parameter(torch.zeros(1, 1, self.embed_dim)) + self.pos_embed = nn.Parameter( + torch.zeros(1, self.patch_embed.num_patches + 1, embed_dim)) + self.pos_drop = nn.Dropout(p=drop_rate) + + if isinstance(out_indices, int): + self.out_indices = [out_indices] + elif isinstance(out_indices, list) or isinstance(out_indices, tuple): + self.out_indices = out_indices + else: + raise TypeError('out_indices must be type of int, list or tuple') + + dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth) + ] # stochastic depth decay rule + self.blocks = nn.ModuleList([ + Block( + dim=embed_dim, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=dpr[i], + attn_drop=attn_drop_rate, + act_cfg=act_cfg, + norm_cfg=norm_cfg, + with_cp=with_cp) for i in range(depth) + ]) + + self.interpolate_mode = interpolate_mode + self.final_norm = final_norm + if final_norm: + _, self.norm = build_norm_layer(norm_cfg, embed_dim) + + self.norm_eval = norm_eval + self.with_cp = with_cp + + def init_weights(self, pretrained=None): + if isinstance(pretrained, str): + logger = get_root_logger() + checkpoint = _load_checkpoint(pretrained, logger=logger) + if 'state_dict' in checkpoint: + state_dict = checkpoint['state_dict'] + else: + state_dict = checkpoint + + if 'pos_embed' in state_dict.keys(): + if self.pos_embed.shape != state_dict['pos_embed'].shape: + logger.info(msg=f'Resize the pos_embed shape from \ +{state_dict["pos_embed"].shape} to {self.pos_embed.shape}') + h, w = self.img_size + pos_size = int( + math.sqrt(state_dict['pos_embed'].shape[1] - 1)) + state_dict['pos_embed'] = self.resize_pos_embed( + state_dict['pos_embed'], (h, w), (pos_size, pos_size), + self.patch_size, self.interpolate_mode) + + self.load_state_dict(state_dict, False) + + elif pretrained is None: + # We only implement the 'jax_impl' initialization implemented at + # https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/vision_transformer.py#L353 # noqa: E501 + trunc_normal_(self.pos_embed, std=.02) + trunc_normal_(self.cls_token, std=.02) + for n, m in self.named_modules(): + if isinstance(m, Linear): + trunc_normal_(m.weight, std=.02) + if m.bias is not None: + if 'mlp' in n: + normal_init(m.bias, std=1e-6) + else: + constant_init(m.bias, 0) + elif isinstance(m, Conv2d): + kaiming_init(m.weight, mode='fan_in') + if m.bias is not None: + constant_init(m.bias, 0) + elif isinstance(m, (_BatchNorm, nn.GroupNorm, nn.LayerNorm)): + constant_init(m.bias, 0) + constant_init(m.weight, 1.0) + else: + raise TypeError('pretrained must be a str or None') + + def _pos_embeding(self, img, patched_img, pos_embed): + """Positiong embeding method. + + Resize the pos_embed, if the input image size doesn't match + the training size. + Args: + img (torch.Tensor): The inference image tensor, the shape + must be [B, C, H, W]. + patched_img (torch.Tensor): The patched image, it should be + shape of [B, L1, C]. + pos_embed (torch.Tensor): The pos_embed weighs, it should be + shape of [B, L2, c]. + Return: + torch.Tensor: The pos encoded image feature. + """ + assert patched_img.ndim == 3 and pos_embed.ndim == 3, \ + 'the shapes of patched_img and pos_embed must be [B, L, C]' + x_len, pos_len = patched_img.shape[1], pos_embed.shape[1] + if x_len != pos_len: + if pos_len == (self.img_size[0] // self.patch_size) * ( + self.img_size[1] // self.patch_size) + 1: + pos_h = self.img_size[0] // self.patch_size + pos_w = self.img_size[1] // self.patch_size + else: + raise ValueError( + 'Unexpected shape of pos_embed, got {}.'.format( + pos_embed.shape)) + pos_embed = self.resize_pos_embed(pos_embed, img.shape[2:], + (pos_h, pos_w), self.patch_size, + self.interpolate_mode) + return self.pos_drop(patched_img + pos_embed) + + @staticmethod + def resize_pos_embed(pos_embed, input_shpae, pos_shape, patch_size, mode): + """Resize pos_embed weights. + + Resize pos_embed using bicubic interpolate method. + Args: + pos_embed (torch.Tensor): pos_embed weights. + input_shpae (tuple): Tuple for (input_h, intput_w). + pos_shape (tuple): Tuple for (pos_h, pos_w). + patch_size (int): Patch size. + Return: + torch.Tensor: The resized pos_embed of shape [B, L_new, C] + """ + assert pos_embed.ndim == 3, 'shape of pos_embed must be [B, L, C]' + input_h, input_w = input_shpae + pos_h, pos_w = pos_shape + cls_token_weight = pos_embed[:, 0] + pos_embed_weight = pos_embed[:, (-1 * pos_h * pos_w):] + pos_embed_weight = pos_embed_weight.reshape( + 1, pos_h, pos_w, pos_embed.shape[2]).permute(0, 3, 1, 2) + pos_embed_weight = F.interpolate( + pos_embed_weight, + size=[input_h // patch_size, input_w // patch_size], + align_corners=False, + mode=mode) + cls_token_weight = cls_token_weight.unsqueeze(1) + pos_embed_weight = torch.flatten(pos_embed_weight, 2).transpose(1, 2) + pos_embed = torch.cat((cls_token_weight, pos_embed_weight), dim=1) + return pos_embed + + def forward(self, inputs): + B = inputs.shape[0] + + x = self.patch_embed(inputs) + + cls_tokens = self.cls_token.expand(B, -1, -1) + x = torch.cat((cls_tokens, x), dim=1) + x = self._pos_embeding(inputs, x, self.pos_embed) + + if not self.with_cls_token: + # Remove class token for transformer input + x = x[:, 1:] + + outs = [] + for i, blk in enumerate(self.blocks): + x = blk(x) + if i == len(self.blocks) - 1: + if self.final_norm: + x = self.norm(x) + if i in self.out_indices: + if self.with_cls_token: + # Remove class token and reshape token for decoder head + out = x[:, 1:] + else: + out = x + B, _, C = out.shape + out = out.reshape(B, inputs.shape[2] // self.patch_size, + inputs.shape[3] // self.patch_size, + C).permute(0, 3, 1, 2) + outs.append(out) + + return tuple(outs) + + def train(self, mode=True): + super(VisionTransformer, self).train(mode) + if mode and self.norm_eval: + for m in self.modules(): + if isinstance(m, nn.LayerNorm): + m.eval() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/builder.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/builder.py new file mode 100644 index 0000000000000000000000000000000000000000..fd29ff66d523b854c739b580137db6f4155fc550 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/builder.py @@ -0,0 +1,46 @@ +import warnings + +from annotator.mmpkg.mmcv.cnn import MODELS as MMCV_MODELS +from annotator.mmpkg.mmcv.utils import Registry + +MODELS = Registry('models', parent=MMCV_MODELS) + +BACKBONES = MODELS +NECKS = MODELS +HEADS = MODELS +LOSSES = MODELS +SEGMENTORS = MODELS + + +def build_backbone(cfg): + """Build backbone.""" + return BACKBONES.build(cfg) + + +def build_neck(cfg): + """Build neck.""" + return NECKS.build(cfg) + + +def build_head(cfg): + """Build head.""" + return HEADS.build(cfg) + + +def build_loss(cfg): + """Build loss.""" + return LOSSES.build(cfg) + + +def build_segmentor(cfg, train_cfg=None, test_cfg=None): + """Build segmentor.""" + if train_cfg is not None or test_cfg is not None: + warnings.warn( + 'train_cfg and test_cfg is deprecated, ' + 'please specify them in model', UserWarning) + assert cfg.get('train_cfg') is None or train_cfg is None, \ + 'train_cfg specified in both outer field and model field ' + assert cfg.get('test_cfg') is None or test_cfg is None, \ + 'test_cfg specified in both outer field and model field ' + return SEGMENTORS.build( + cfg, default_args=dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ac66d3cfe0ea04af45c0f3594bf135841c3812e3 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/__init__.py @@ -0,0 +1,28 @@ +from .ann_head import ANNHead +from .apc_head import APCHead +from .aspp_head import ASPPHead +from .cc_head import CCHead +from .da_head import DAHead +from .dm_head import DMHead +from .dnl_head import DNLHead +from .ema_head import EMAHead +from .enc_head import EncHead +from .fcn_head import FCNHead +from .fpn_head import FPNHead +from .gc_head import GCHead +from .lraspp_head import LRASPPHead +from .nl_head import NLHead +from .ocr_head import OCRHead +# from .point_head import PointHead +from .psa_head import PSAHead +from .psp_head import PSPHead +from .sep_aspp_head import DepthwiseSeparableASPPHead +from .sep_fcn_head import DepthwiseSeparableFCNHead +from .uper_head import UPerHead + +__all__ = [ + 'FCNHead', 'PSPHead', 'ASPPHead', 'PSAHead', 'NLHead', 'GCHead', 'CCHead', + 'UPerHead', 'DepthwiseSeparableASPPHead', 'ANNHead', 'DAHead', 'OCRHead', + 'EncHead', 'DepthwiseSeparableFCNHead', 'FPNHead', 'EMAHead', 'DNLHead', + 'APCHead', 'DMHead', 'LRASPPHead' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/ann_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/ann_head.py new file mode 100644 index 0000000000000000000000000000000000000000..958c88e0ca4b9acdaf146b836462b9a101b2cdad --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/ann_head.py @@ -0,0 +1,245 @@ +import torch +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import ConvModule + +from ..builder import HEADS +from ..utils import SelfAttentionBlock as _SelfAttentionBlock +from .decode_head import BaseDecodeHead + + +class PPMConcat(nn.ModuleList): + """Pyramid Pooling Module that only concat the features of each layer. + + Args: + pool_scales (tuple[int]): Pooling scales used in Pooling Pyramid + Module. + """ + + def __init__(self, pool_scales=(1, 3, 6, 8)): + super(PPMConcat, self).__init__( + [nn.AdaptiveAvgPool2d(pool_scale) for pool_scale in pool_scales]) + + def forward(self, feats): + """Forward function.""" + ppm_outs = [] + for ppm in self: + ppm_out = ppm(feats) + ppm_outs.append(ppm_out.view(*feats.shape[:2], -1)) + concat_outs = torch.cat(ppm_outs, dim=2) + return concat_outs + + +class SelfAttentionBlock(_SelfAttentionBlock): + """Make a ANN used SelfAttentionBlock. + + Args: + low_in_channels (int): Input channels of lower level feature, + which is the key feature for self-attention. + high_in_channels (int): Input channels of higher level feature, + which is the query feature for self-attention. + channels (int): Output channels of key/query transform. + out_channels (int): Output channels. + share_key_query (bool): Whether share projection weight between key + and query projection. + query_scale (int): The scale of query feature map. + key_pool_scales (tuple[int]): Pooling scales used in Pooling Pyramid + Module of key feature. + conv_cfg (dict|None): Config of conv layers. + norm_cfg (dict|None): Config of norm layers. + act_cfg (dict|None): Config of activation layers. + """ + + def __init__(self, low_in_channels, high_in_channels, channels, + out_channels, share_key_query, query_scale, key_pool_scales, + conv_cfg, norm_cfg, act_cfg): + key_psp = PPMConcat(key_pool_scales) + if query_scale > 1: + query_downsample = nn.MaxPool2d(kernel_size=query_scale) + else: + query_downsample = None + super(SelfAttentionBlock, self).__init__( + key_in_channels=low_in_channels, + query_in_channels=high_in_channels, + channels=channels, + out_channels=out_channels, + share_key_query=share_key_query, + query_downsample=query_downsample, + key_downsample=key_psp, + key_query_num_convs=1, + key_query_norm=True, + value_out_num_convs=1, + value_out_norm=False, + matmul_norm=True, + with_out=True, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + + +class AFNB(nn.Module): + """Asymmetric Fusion Non-local Block(AFNB) + + Args: + low_in_channels (int): Input channels of lower level feature, + which is the key feature for self-attention. + high_in_channels (int): Input channels of higher level feature, + which is the query feature for self-attention. + channels (int): Output channels of key/query transform. + out_channels (int): Output channels. + and query projection. + query_scales (tuple[int]): The scales of query feature map. + Default: (1,) + key_pool_scales (tuple[int]): Pooling scales used in Pooling Pyramid + Module of key feature. + conv_cfg (dict|None): Config of conv layers. + norm_cfg (dict|None): Config of norm layers. + act_cfg (dict|None): Config of activation layers. + """ + + def __init__(self, low_in_channels, high_in_channels, channels, + out_channels, query_scales, key_pool_scales, conv_cfg, + norm_cfg, act_cfg): + super(AFNB, self).__init__() + self.stages = nn.ModuleList() + for query_scale in query_scales: + self.stages.append( + SelfAttentionBlock( + low_in_channels=low_in_channels, + high_in_channels=high_in_channels, + channels=channels, + out_channels=out_channels, + share_key_query=False, + query_scale=query_scale, + key_pool_scales=key_pool_scales, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg)) + self.bottleneck = ConvModule( + out_channels + high_in_channels, + out_channels, + 1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=None) + + def forward(self, low_feats, high_feats): + """Forward function.""" + priors = [stage(high_feats, low_feats) for stage in self.stages] + context = torch.stack(priors, dim=0).sum(dim=0) + output = self.bottleneck(torch.cat([context, high_feats], 1)) + return output + + +class APNB(nn.Module): + """Asymmetric Pyramid Non-local Block (APNB) + + Args: + in_channels (int): Input channels of key/query feature, + which is the key feature for self-attention. + channels (int): Output channels of key/query transform. + out_channels (int): Output channels. + query_scales (tuple[int]): The scales of query feature map. + Default: (1,) + key_pool_scales (tuple[int]): Pooling scales used in Pooling Pyramid + Module of key feature. + conv_cfg (dict|None): Config of conv layers. + norm_cfg (dict|None): Config of norm layers. + act_cfg (dict|None): Config of activation layers. + """ + + def __init__(self, in_channels, channels, out_channels, query_scales, + key_pool_scales, conv_cfg, norm_cfg, act_cfg): + super(APNB, self).__init__() + self.stages = nn.ModuleList() + for query_scale in query_scales: + self.stages.append( + SelfAttentionBlock( + low_in_channels=in_channels, + high_in_channels=in_channels, + channels=channels, + out_channels=out_channels, + share_key_query=True, + query_scale=query_scale, + key_pool_scales=key_pool_scales, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg)) + self.bottleneck = ConvModule( + 2 * in_channels, + out_channels, + 1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + + def forward(self, feats): + """Forward function.""" + priors = [stage(feats, feats) for stage in self.stages] + context = torch.stack(priors, dim=0).sum(dim=0) + output = self.bottleneck(torch.cat([context, feats], 1)) + return output + + +@HEADS.register_module() +class ANNHead(BaseDecodeHead): + """Asymmetric Non-local Neural Networks for Semantic Segmentation. + + This head is the implementation of `ANNNet + `_. + + Args: + project_channels (int): Projection channels for Nonlocal. + query_scales (tuple[int]): The scales of query feature map. + Default: (1,) + key_pool_scales (tuple[int]): The pooling scales of key feature map. + Default: (1, 3, 6, 8). + """ + + def __init__(self, + project_channels, + query_scales=(1, ), + key_pool_scales=(1, 3, 6, 8), + **kwargs): + super(ANNHead, self).__init__( + input_transform='multiple_select', **kwargs) + assert len(self.in_channels) == 2 + low_in_channels, high_in_channels = self.in_channels + self.project_channels = project_channels + self.fusion = AFNB( + low_in_channels=low_in_channels, + high_in_channels=high_in_channels, + out_channels=high_in_channels, + channels=project_channels, + query_scales=query_scales, + key_pool_scales=key_pool_scales, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.bottleneck = ConvModule( + high_in_channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.context = APNB( + in_channels=self.channels, + out_channels=self.channels, + channels=project_channels, + query_scales=query_scales, + key_pool_scales=key_pool_scales, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def forward(self, inputs): + """Forward function.""" + low_feats, high_feats = self._transform_inputs(inputs) + output = self.fusion(low_feats, high_feats) + output = self.dropout(output) + output = self.bottleneck(output) + output = self.context(output) + output = self.cls_seg(output) + + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/apc_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/apc_head.py new file mode 100644 index 0000000000000000000000000000000000000000..4f363dba391c3eb6fb5a4d61c145fd4976a5717d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/apc_head.py @@ -0,0 +1,158 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +from annotator.mmpkg.mmcv.cnn import ConvModule + +from annotator.mmpkg.mmseg.ops import resize +from ..builder import HEADS +from .decode_head import BaseDecodeHead + + +class ACM(nn.Module): + """Adaptive Context Module used in APCNet. + + Args: + pool_scale (int): Pooling scale used in Adaptive Context + Module to extract region features. + fusion (bool): Add one conv to fuse residual feature. + in_channels (int): Input channels. + channels (int): Channels after modules, before conv_seg. + conv_cfg (dict | None): Config of conv layers. + norm_cfg (dict | None): Config of norm layers. + act_cfg (dict): Config of activation layers. + """ + + def __init__(self, pool_scale, fusion, in_channels, channels, conv_cfg, + norm_cfg, act_cfg): + super(ACM, self).__init__() + self.pool_scale = pool_scale + self.fusion = fusion + self.in_channels = in_channels + self.channels = channels + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + self.pooled_redu_conv = ConvModule( + self.in_channels, + self.channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + self.input_redu_conv = ConvModule( + self.in_channels, + self.channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + self.global_info = ConvModule( + self.channels, + self.channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + self.gla = nn.Conv2d(self.channels, self.pool_scale**2, 1, 1, 0) + + self.residual_conv = ConvModule( + self.channels, + self.channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + if self.fusion: + self.fusion_conv = ConvModule( + self.channels, + self.channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def forward(self, x): + """Forward function.""" + pooled_x = F.adaptive_avg_pool2d(x, self.pool_scale) + # [batch_size, channels, h, w] + x = self.input_redu_conv(x) + # [batch_size, channels, pool_scale, pool_scale] + pooled_x = self.pooled_redu_conv(pooled_x) + batch_size = x.size(0) + # [batch_size, pool_scale * pool_scale, channels] + pooled_x = pooled_x.view(batch_size, self.channels, + -1).permute(0, 2, 1).contiguous() + # [batch_size, h * w, pool_scale * pool_scale] + affinity_matrix = self.gla(x + resize( + self.global_info(F.adaptive_avg_pool2d(x, 1)), size=x.shape[2:]) + ).permute(0, 2, 3, 1).reshape( + batch_size, -1, self.pool_scale**2) + affinity_matrix = F.sigmoid(affinity_matrix) + # [batch_size, h * w, channels] + z_out = torch.matmul(affinity_matrix, pooled_x) + # [batch_size, channels, h * w] + z_out = z_out.permute(0, 2, 1).contiguous() + # [batch_size, channels, h, w] + z_out = z_out.view(batch_size, self.channels, x.size(2), x.size(3)) + z_out = self.residual_conv(z_out) + z_out = F.relu(z_out + x) + if self.fusion: + z_out = self.fusion_conv(z_out) + + return z_out + + +@HEADS.register_module() +class APCHead(BaseDecodeHead): + """Adaptive Pyramid Context Network for Semantic Segmentation. + + This head is the implementation of + `APCNet `_. + + Args: + pool_scales (tuple[int]): Pooling scales used in Adaptive Context + Module. Default: (1, 2, 3, 6). + fusion (bool): Add one conv to fuse residual feature. + """ + + def __init__(self, pool_scales=(1, 2, 3, 6), fusion=True, **kwargs): + super(APCHead, self).__init__(**kwargs) + assert isinstance(pool_scales, (list, tuple)) + self.pool_scales = pool_scales + self.fusion = fusion + acm_modules = [] + for pool_scale in self.pool_scales: + acm_modules.append( + ACM(pool_scale, + self.fusion, + self.in_channels, + self.channels, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg)) + self.acm_modules = nn.ModuleList(acm_modules) + self.bottleneck = ConvModule( + self.in_channels + len(pool_scales) * self.channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + acm_outs = [x] + for acm_module in self.acm_modules: + acm_outs.append(acm_module(x)) + acm_outs = torch.cat(acm_outs, dim=1) + output = self.bottleneck(acm_outs) + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/aspp_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/aspp_head.py new file mode 100644 index 0000000000000000000000000000000000000000..3c0aadb2b097a604d96ba1c99c05663b7884b6e0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/aspp_head.py @@ -0,0 +1,107 @@ +import torch +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import ConvModule + +from annotator.mmpkg.mmseg.ops import resize +from ..builder import HEADS +from .decode_head import BaseDecodeHead + + +class ASPPModule(nn.ModuleList): + """Atrous Spatial Pyramid Pooling (ASPP) Module. + + Args: + dilations (tuple[int]): Dilation rate of each layer. + in_channels (int): Input channels. + channels (int): Channels after modules, before conv_seg. + conv_cfg (dict|None): Config of conv layers. + norm_cfg (dict|None): Config of norm layers. + act_cfg (dict): Config of activation layers. + """ + + def __init__(self, dilations, in_channels, channels, conv_cfg, norm_cfg, + act_cfg): + super(ASPPModule, self).__init__() + self.dilations = dilations + self.in_channels = in_channels + self.channels = channels + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + for dilation in dilations: + self.append( + ConvModule( + self.in_channels, + self.channels, + 1 if dilation == 1 else 3, + dilation=dilation, + padding=0 if dilation == 1 else dilation, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg)) + + def forward(self, x): + """Forward function.""" + aspp_outs = [] + for aspp_module in self: + aspp_outs.append(aspp_module(x)) + + return aspp_outs + + +@HEADS.register_module() +class ASPPHead(BaseDecodeHead): + """Rethinking Atrous Convolution for Semantic Image Segmentation. + + This head is the implementation of `DeepLabV3 + `_. + + Args: + dilations (tuple[int]): Dilation rates for ASPP module. + Default: (1, 6, 12, 18). + """ + + def __init__(self, dilations=(1, 6, 12, 18), **kwargs): + super(ASPPHead, self).__init__(**kwargs) + assert isinstance(dilations, (list, tuple)) + self.dilations = dilations + self.image_pool = nn.Sequential( + nn.AdaptiveAvgPool2d(1), + ConvModule( + self.in_channels, + self.channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg)) + self.aspp_modules = ASPPModule( + dilations, + self.in_channels, + self.channels, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.bottleneck = ConvModule( + (len(dilations) + 1) * self.channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + aspp_outs = [ + resize( + self.image_pool(x), + size=x.size()[2:], + mode='bilinear', + align_corners=self.align_corners) + ] + aspp_outs.extend(self.aspp_modules(x)) + aspp_outs = torch.cat(aspp_outs, dim=1) + output = self.bottleneck(aspp_outs) + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/cascade_decode_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/cascade_decode_head.py new file mode 100644 index 0000000000000000000000000000000000000000..d02122ca0e68743b1bf7a893afae96042f23838c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/cascade_decode_head.py @@ -0,0 +1,57 @@ +from abc import ABCMeta, abstractmethod + +from .decode_head import BaseDecodeHead + + +class BaseCascadeDecodeHead(BaseDecodeHead, metaclass=ABCMeta): + """Base class for cascade decode head used in + :class:`CascadeEncoderDecoder.""" + + def __init__(self, *args, **kwargs): + super(BaseCascadeDecodeHead, self).__init__(*args, **kwargs) + + @abstractmethod + def forward(self, inputs, prev_output): + """Placeholder of forward function.""" + pass + + def forward_train(self, inputs, prev_output, img_metas, gt_semantic_seg, + train_cfg): + """Forward function for training. + Args: + inputs (list[Tensor]): List of multi-level img features. + prev_output (Tensor): The output of previous decode head. + img_metas (list[dict]): List of image info dict where each dict + has: 'img_shape', 'scale_factor', 'flip', and may also contain + 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. + For details on the values of these keys see + `mmseg/datasets/pipelines/formatting.py:Collect`. + gt_semantic_seg (Tensor): Semantic segmentation masks + used if the architecture supports semantic segmentation task. + train_cfg (dict): The training config. + + Returns: + dict[str, Tensor]: a dictionary of loss components + """ + seg_logits = self.forward(inputs, prev_output) + losses = self.losses(seg_logits, gt_semantic_seg) + + return losses + + def forward_test(self, inputs, prev_output, img_metas, test_cfg): + """Forward function for testing. + + Args: + inputs (list[Tensor]): List of multi-level img features. + prev_output (Tensor): The output of previous decode head. + img_metas (list[dict]): List of image info dict where each dict + has: 'img_shape', 'scale_factor', 'flip', and may also contain + 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. + For details on the values of these keys see + `mmseg/datasets/pipelines/formatting.py:Collect`. + test_cfg (dict): The testing config. + + Returns: + Tensor: Output segmentation map. + """ + return self.forward(inputs, prev_output) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/cc_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/cc_head.py new file mode 100644 index 0000000000000000000000000000000000000000..1f4f5b052445a4071952aa04274274da7d897c2c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/cc_head.py @@ -0,0 +1,45 @@ +import torch + +from ..builder import HEADS +from .fcn_head import FCNHead + +try: + try: + from mmcv.ops import CrissCrossAttention + except ImportError: + from annotator.mmpkg.mmcv.ops import CrissCrossAttention +except ModuleNotFoundError: + CrissCrossAttention = None + + +@HEADS.register_module() +class CCHead(FCNHead): + """CCNet: Criss-Cross Attention for Semantic Segmentation. + + This head is the implementation of `CCNet + `_. + + Args: + recurrence (int): Number of recurrence of Criss Cross Attention + module. Default: 2. + """ + + def __init__(self, recurrence=2, **kwargs): + if CrissCrossAttention is None: + raise RuntimeError('Please install mmcv-full for ' + 'CrissCrossAttention ops') + super(CCHead, self).__init__(num_convs=2, **kwargs) + self.recurrence = recurrence + self.cca = CrissCrossAttention(self.channels) + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + output = self.convs[0](x) + for _ in range(self.recurrence): + output = self.cca(output) + output = self.convs[1](output) + if self.concat_input: + output = self.conv_cat(torch.cat([x, output], dim=1)) + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/da_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/da_head.py new file mode 100644 index 0000000000000000000000000000000000000000..b0b7616501c04cc0faf92accac9d3fdb6807f9e1 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/da_head.py @@ -0,0 +1,178 @@ +import torch +import torch.nn.functional as F +from annotator.mmpkg.mmcv.cnn import ConvModule, Scale +from torch import nn + +from annotator.mmpkg.mmseg.core import add_prefix +from ..builder import HEADS +from ..utils import SelfAttentionBlock as _SelfAttentionBlock +from .decode_head import BaseDecodeHead + + +class PAM(_SelfAttentionBlock): + """Position Attention Module (PAM) + + Args: + in_channels (int): Input channels of key/query feature. + channels (int): Output channels of key/query transform. + """ + + def __init__(self, in_channels, channels): + super(PAM, self).__init__( + key_in_channels=in_channels, + query_in_channels=in_channels, + channels=channels, + out_channels=in_channels, + share_key_query=False, + query_downsample=None, + key_downsample=None, + key_query_num_convs=1, + key_query_norm=False, + value_out_num_convs=1, + value_out_norm=False, + matmul_norm=False, + with_out=False, + conv_cfg=None, + norm_cfg=None, + act_cfg=None) + + self.gamma = Scale(0) + + def forward(self, x): + """Forward function.""" + out = super(PAM, self).forward(x, x) + + out = self.gamma(out) + x + return out + + +class CAM(nn.Module): + """Channel Attention Module (CAM)""" + + def __init__(self): + super(CAM, self).__init__() + self.gamma = Scale(0) + + def forward(self, x): + """Forward function.""" + batch_size, channels, height, width = x.size() + proj_query = x.view(batch_size, channels, -1) + proj_key = x.view(batch_size, channels, -1).permute(0, 2, 1) + energy = torch.bmm(proj_query, proj_key) + energy_new = torch.max( + energy, -1, keepdim=True)[0].expand_as(energy) - energy + attention = F.softmax(energy_new, dim=-1) + proj_value = x.view(batch_size, channels, -1) + + out = torch.bmm(attention, proj_value) + out = out.view(batch_size, channels, height, width) + + out = self.gamma(out) + x + return out + + +@HEADS.register_module() +class DAHead(BaseDecodeHead): + """Dual Attention Network for Scene Segmentation. + + This head is the implementation of `DANet + `_. + + Args: + pam_channels (int): The channels of Position Attention Module(PAM). + """ + + def __init__(self, pam_channels, **kwargs): + super(DAHead, self).__init__(**kwargs) + self.pam_channels = pam_channels + self.pam_in_conv = ConvModule( + self.in_channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.pam = PAM(self.channels, pam_channels) + self.pam_out_conv = ConvModule( + self.channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.pam_conv_seg = nn.Conv2d( + self.channels, self.num_classes, kernel_size=1) + + self.cam_in_conv = ConvModule( + self.in_channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.cam = CAM() + self.cam_out_conv = ConvModule( + self.channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.cam_conv_seg = nn.Conv2d( + self.channels, self.num_classes, kernel_size=1) + + def pam_cls_seg(self, feat): + """PAM feature classification.""" + if self.dropout is not None: + feat = self.dropout(feat) + output = self.pam_conv_seg(feat) + return output + + def cam_cls_seg(self, feat): + """CAM feature classification.""" + if self.dropout is not None: + feat = self.dropout(feat) + output = self.cam_conv_seg(feat) + return output + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + pam_feat = self.pam_in_conv(x) + pam_feat = self.pam(pam_feat) + pam_feat = self.pam_out_conv(pam_feat) + pam_out = self.pam_cls_seg(pam_feat) + + cam_feat = self.cam_in_conv(x) + cam_feat = self.cam(cam_feat) + cam_feat = self.cam_out_conv(cam_feat) + cam_out = self.cam_cls_seg(cam_feat) + + feat_sum = pam_feat + cam_feat + pam_cam_out = self.cls_seg(feat_sum) + + return pam_cam_out, pam_out, cam_out + + def forward_test(self, inputs, img_metas, test_cfg): + """Forward function for testing, only ``pam_cam`` is used.""" + return self.forward(inputs)[0] + + def losses(self, seg_logit, seg_label): + """Compute ``pam_cam``, ``pam``, ``cam`` loss.""" + pam_cam_seg_logit, pam_seg_logit, cam_seg_logit = seg_logit + loss = dict() + loss.update( + add_prefix( + super(DAHead, self).losses(pam_cam_seg_logit, seg_label), + 'pam_cam')) + loss.update( + add_prefix( + super(DAHead, self).losses(pam_seg_logit, seg_label), 'pam')) + loss.update( + add_prefix( + super(DAHead, self).losses(cam_seg_logit, seg_label), 'cam')) + return loss diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/decode_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/decode_head.py new file mode 100644 index 0000000000000000000000000000000000000000..a74c89f2ef1274ffe947995722576ab2c78eaec1 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/decode_head.py @@ -0,0 +1,234 @@ +from abc import ABCMeta, abstractmethod + +import torch +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import normal_init +from annotator.mmpkg.mmcv.runner import auto_fp16, force_fp32 + +from annotator.mmpkg.mmseg.core import build_pixel_sampler +from annotator.mmpkg.mmseg.ops import resize +from ..builder import build_loss +from ..losses import accuracy + + +class BaseDecodeHead(nn.Module, metaclass=ABCMeta): + """Base class for BaseDecodeHead. + + Args: + in_channels (int|Sequence[int]): Input channels. + channels (int): Channels after modules, before conv_seg. + num_classes (int): Number of classes. + dropout_ratio (float): Ratio of dropout layer. Default: 0.1. + conv_cfg (dict|None): Config of conv layers. Default: None. + norm_cfg (dict|None): Config of norm layers. Default: None. + act_cfg (dict): Config of activation layers. + Default: dict(type='ReLU') + in_index (int|Sequence[int]): Input feature index. Default: -1 + input_transform (str|None): Transformation type of input features. + Options: 'resize_concat', 'multiple_select', None. + 'resize_concat': Multiple feature maps will be resize to the + same size as first one and than concat together. + Usually used in FCN head of HRNet. + 'multiple_select': Multiple feature maps will be bundle into + a list and passed into decode head. + None: Only one select feature map is allowed. + Default: None. + loss_decode (dict): Config of decode loss. + Default: dict(type='CrossEntropyLoss'). + ignore_index (int | None): The label index to be ignored. When using + masked BCE loss, ignore_index should be set to None. Default: 255 + sampler (dict|None): The config of segmentation map sampler. + Default: None. + align_corners (bool): align_corners argument of F.interpolate. + Default: False. + """ + + def __init__(self, + in_channels, + channels, + *, + num_classes, + dropout_ratio=0.1, + conv_cfg=None, + norm_cfg=None, + act_cfg=dict(type='ReLU'), + in_index=-1, + input_transform=None, + loss_decode=dict( + type='CrossEntropyLoss', + use_sigmoid=False, + loss_weight=1.0), + ignore_index=255, + sampler=None, + align_corners=False): + super(BaseDecodeHead, self).__init__() + self._init_inputs(in_channels, in_index, input_transform) + self.channels = channels + self.num_classes = num_classes + self.dropout_ratio = dropout_ratio + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + self.in_index = in_index + self.loss_decode = build_loss(loss_decode) + self.ignore_index = ignore_index + self.align_corners = align_corners + if sampler is not None: + self.sampler = build_pixel_sampler(sampler, context=self) + else: + self.sampler = None + + self.conv_seg = nn.Conv2d(channels, num_classes, kernel_size=1) + if dropout_ratio > 0: + self.dropout = nn.Dropout2d(dropout_ratio) + else: + self.dropout = None + self.fp16_enabled = False + + def extra_repr(self): + """Extra repr.""" + s = f'input_transform={self.input_transform}, ' \ + f'ignore_index={self.ignore_index}, ' \ + f'align_corners={self.align_corners}' + return s + + def _init_inputs(self, in_channels, in_index, input_transform): + """Check and initialize input transforms. + + The in_channels, in_index and input_transform must match. + Specifically, when input_transform is None, only single feature map + will be selected. So in_channels and in_index must be of type int. + When input_transform + + Args: + in_channels (int|Sequence[int]): Input channels. + in_index (int|Sequence[int]): Input feature index. + input_transform (str|None): Transformation type of input features. + Options: 'resize_concat', 'multiple_select', None. + 'resize_concat': Multiple feature maps will be resize to the + same size as first one and than concat together. + Usually used in FCN head of HRNet. + 'multiple_select': Multiple feature maps will be bundle into + a list and passed into decode head. + None: Only one select feature map is allowed. + """ + + if input_transform is not None: + assert input_transform in ['resize_concat', 'multiple_select'] + self.input_transform = input_transform + self.in_index = in_index + if input_transform is not None: + assert isinstance(in_channels, (list, tuple)) + assert isinstance(in_index, (list, tuple)) + assert len(in_channels) == len(in_index) + if input_transform == 'resize_concat': + self.in_channels = sum(in_channels) + else: + self.in_channels = in_channels + else: + assert isinstance(in_channels, int) + assert isinstance(in_index, int) + self.in_channels = in_channels + + def init_weights(self): + """Initialize weights of classification layer.""" + normal_init(self.conv_seg, mean=0, std=0.01) + + def _transform_inputs(self, inputs): + """Transform inputs for decoder. + + Args: + inputs (list[Tensor]): List of multi-level img features. + + Returns: + Tensor: The transformed inputs + """ + + if self.input_transform == 'resize_concat': + inputs = [inputs[i] for i in self.in_index] + upsampled_inputs = [ + resize( + input=x, + size=inputs[0].shape[2:], + mode='bilinear', + align_corners=self.align_corners) for x in inputs + ] + inputs = torch.cat(upsampled_inputs, dim=1) + elif self.input_transform == 'multiple_select': + inputs = [inputs[i] for i in self.in_index] + else: + inputs = inputs[self.in_index] + + return inputs + + @auto_fp16() + @abstractmethod + def forward(self, inputs): + """Placeholder of forward function.""" + pass + + def forward_train(self, inputs, img_metas, gt_semantic_seg, train_cfg): + """Forward function for training. + Args: + inputs (list[Tensor]): List of multi-level img features. + img_metas (list[dict]): List of image info dict where each dict + has: 'img_shape', 'scale_factor', 'flip', and may also contain + 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. + For details on the values of these keys see + `mmseg/datasets/pipelines/formatting.py:Collect`. + gt_semantic_seg (Tensor): Semantic segmentation masks + used if the architecture supports semantic segmentation task. + train_cfg (dict): The training config. + + Returns: + dict[str, Tensor]: a dictionary of loss components + """ + seg_logits = self.forward(inputs) + losses = self.losses(seg_logits, gt_semantic_seg) + return losses + + def forward_test(self, inputs, img_metas, test_cfg): + """Forward function for testing. + + Args: + inputs (list[Tensor]): List of multi-level img features. + img_metas (list[dict]): List of image info dict where each dict + has: 'img_shape', 'scale_factor', 'flip', and may also contain + 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. + For details on the values of these keys see + `mmseg/datasets/pipelines/formatting.py:Collect`. + test_cfg (dict): The testing config. + + Returns: + Tensor: Output segmentation map. + """ + return self.forward(inputs) + + def cls_seg(self, feat): + """Classify each pixel.""" + if self.dropout is not None: + feat = self.dropout(feat) + output = self.conv_seg(feat) + return output + + @force_fp32(apply_to=('seg_logit', )) + def losses(self, seg_logit, seg_label): + """Compute segmentation loss.""" + loss = dict() + seg_logit = resize( + input=seg_logit, + size=seg_label.shape[2:], + mode='bilinear', + align_corners=self.align_corners) + if self.sampler is not None: + seg_weight = self.sampler.sample(seg_logit, seg_label) + else: + seg_weight = None + seg_label = seg_label.squeeze(1) + loss['loss_seg'] = self.loss_decode( + seg_logit, + seg_label, + weight=seg_weight, + ignore_index=self.ignore_index) + loss['acc_seg'] = accuracy(seg_logit, seg_label) + return loss diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/dm_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/dm_head.py new file mode 100644 index 0000000000000000000000000000000000000000..de6d0f6390d96c1eef4242cdc9aed91ec7714c6a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/dm_head.py @@ -0,0 +1,140 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +from annotator.mmpkg.mmcv.cnn import ConvModule, build_activation_layer, build_norm_layer + +from ..builder import HEADS +from .decode_head import BaseDecodeHead + + +class DCM(nn.Module): + """Dynamic Convolutional Module used in DMNet. + + Args: + filter_size (int): The filter size of generated convolution kernel + used in Dynamic Convolutional Module. + fusion (bool): Add one conv to fuse DCM output feature. + in_channels (int): Input channels. + channels (int): Channels after modules, before conv_seg. + conv_cfg (dict | None): Config of conv layers. + norm_cfg (dict | None): Config of norm layers. + act_cfg (dict): Config of activation layers. + """ + + def __init__(self, filter_size, fusion, in_channels, channels, conv_cfg, + norm_cfg, act_cfg): + super(DCM, self).__init__() + self.filter_size = filter_size + self.fusion = fusion + self.in_channels = in_channels + self.channels = channels + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + self.filter_gen_conv = nn.Conv2d(self.in_channels, self.channels, 1, 1, + 0) + + self.input_redu_conv = ConvModule( + self.in_channels, + self.channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + if self.norm_cfg is not None: + self.norm = build_norm_layer(self.norm_cfg, self.channels)[1] + else: + self.norm = None + self.activate = build_activation_layer(self.act_cfg) + + if self.fusion: + self.fusion_conv = ConvModule( + self.channels, + self.channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def forward(self, x): + """Forward function.""" + generated_filter = self.filter_gen_conv( + F.adaptive_avg_pool2d(x, self.filter_size)) + x = self.input_redu_conv(x) + b, c, h, w = x.shape + # [1, b * c, h, w], c = self.channels + x = x.view(1, b * c, h, w) + # [b * c, 1, filter_size, filter_size] + generated_filter = generated_filter.view(b * c, 1, self.filter_size, + self.filter_size) + pad = (self.filter_size - 1) // 2 + if (self.filter_size - 1) % 2 == 0: + p2d = (pad, pad, pad, pad) + else: + p2d = (pad + 1, pad, pad + 1, pad) + x = F.pad(input=x, pad=p2d, mode='constant', value=0) + # [1, b * c, h, w] + output = F.conv2d(input=x, weight=generated_filter, groups=b * c) + # [b, c, h, w] + output = output.view(b, c, h, w) + if self.norm is not None: + output = self.norm(output) + output = self.activate(output) + + if self.fusion: + output = self.fusion_conv(output) + + return output + + +@HEADS.register_module() +class DMHead(BaseDecodeHead): + """Dynamic Multi-scale Filters for Semantic Segmentation. + + This head is the implementation of + `DMNet `_. + + Args: + filter_sizes (tuple[int]): The size of generated convolutional filters + used in Dynamic Convolutional Module. Default: (1, 3, 5, 7). + fusion (bool): Add one conv to fuse DCM output feature. + """ + + def __init__(self, filter_sizes=(1, 3, 5, 7), fusion=False, **kwargs): + super(DMHead, self).__init__(**kwargs) + assert isinstance(filter_sizes, (list, tuple)) + self.filter_sizes = filter_sizes + self.fusion = fusion + dcm_modules = [] + for filter_size in self.filter_sizes: + dcm_modules.append( + DCM(filter_size, + self.fusion, + self.in_channels, + self.channels, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg)) + self.dcm_modules = nn.ModuleList(dcm_modules) + self.bottleneck = ConvModule( + self.in_channels + len(filter_sizes) * self.channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + dcm_outs = [x] + for dcm_module in self.dcm_modules: + dcm_outs.append(dcm_module(x)) + dcm_outs = torch.cat(dcm_outs, dim=1) + output = self.bottleneck(dcm_outs) + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/dnl_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/dnl_head.py new file mode 100644 index 0000000000000000000000000000000000000000..b3bb1de1499ad043cc51b2269b4d970d07c16076 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/dnl_head.py @@ -0,0 +1,131 @@ +import torch +from annotator.mmpkg.mmcv.cnn import NonLocal2d +from torch import nn + +from ..builder import HEADS +from .fcn_head import FCNHead + + +class DisentangledNonLocal2d(NonLocal2d): + """Disentangled Non-Local Blocks. + + Args: + temperature (float): Temperature to adjust attention. Default: 0.05 + """ + + def __init__(self, *arg, temperature, **kwargs): + super().__init__(*arg, **kwargs) + self.temperature = temperature + self.conv_mask = nn.Conv2d(self.in_channels, 1, kernel_size=1) + + def embedded_gaussian(self, theta_x, phi_x): + """Embedded gaussian with temperature.""" + + # NonLocal2d pairwise_weight: [N, HxW, HxW] + pairwise_weight = torch.matmul(theta_x, phi_x) + if self.use_scale: + # theta_x.shape[-1] is `self.inter_channels` + pairwise_weight /= theta_x.shape[-1]**0.5 + pairwise_weight /= self.temperature + pairwise_weight = pairwise_weight.softmax(dim=-1) + return pairwise_weight + + def forward(self, x): + # x: [N, C, H, W] + n = x.size(0) + + # g_x: [N, HxW, C] + g_x = self.g(x).view(n, self.inter_channels, -1) + g_x = g_x.permute(0, 2, 1) + + # theta_x: [N, HxW, C], phi_x: [N, C, HxW] + if self.mode == 'gaussian': + theta_x = x.view(n, self.in_channels, -1) + theta_x = theta_x.permute(0, 2, 1) + if self.sub_sample: + phi_x = self.phi(x).view(n, self.in_channels, -1) + else: + phi_x = x.view(n, self.in_channels, -1) + elif self.mode == 'concatenation': + theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) + phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) + else: + theta_x = self.theta(x).view(n, self.inter_channels, -1) + theta_x = theta_x.permute(0, 2, 1) + phi_x = self.phi(x).view(n, self.inter_channels, -1) + + # subtract mean + theta_x -= theta_x.mean(dim=-2, keepdim=True) + phi_x -= phi_x.mean(dim=-1, keepdim=True) + + pairwise_func = getattr(self, self.mode) + # pairwise_weight: [N, HxW, HxW] + pairwise_weight = pairwise_func(theta_x, phi_x) + + # y: [N, HxW, C] + y = torch.matmul(pairwise_weight, g_x) + # y: [N, C, H, W] + y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, + *x.size()[2:]) + + # unary_mask: [N, 1, HxW] + unary_mask = self.conv_mask(x) + unary_mask = unary_mask.view(n, 1, -1) + unary_mask = unary_mask.softmax(dim=-1) + # unary_x: [N, 1, C] + unary_x = torch.matmul(unary_mask, g_x) + # unary_x: [N, C, 1, 1] + unary_x = unary_x.permute(0, 2, 1).contiguous().reshape( + n, self.inter_channels, 1, 1) + + output = x + self.conv_out(y + unary_x) + + return output + + +@HEADS.register_module() +class DNLHead(FCNHead): + """Disentangled Non-Local Neural Networks. + + This head is the implementation of `DNLNet + `_. + + Args: + reduction (int): Reduction factor of projection transform. Default: 2. + use_scale (bool): Whether to scale pairwise_weight by + sqrt(1/inter_channels). Default: False. + mode (str): The nonlocal mode. Options are 'embedded_gaussian', + 'dot_product'. Default: 'embedded_gaussian.'. + temperature (float): Temperature to adjust attention. Default: 0.05 + """ + + def __init__(self, + reduction=2, + use_scale=True, + mode='embedded_gaussian', + temperature=0.05, + **kwargs): + super(DNLHead, self).__init__(num_convs=2, **kwargs) + self.reduction = reduction + self.use_scale = use_scale + self.mode = mode + self.temperature = temperature + self.dnl_block = DisentangledNonLocal2d( + in_channels=self.channels, + reduction=self.reduction, + use_scale=self.use_scale, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + mode=self.mode, + temperature=self.temperature) + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + output = self.convs[0](x) + output = self.dnl_block(output) + output = self.convs[1](output) + if self.concat_input: + output = self.conv_cat(torch.cat([x, output], dim=1)) + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/ema_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/ema_head.py new file mode 100644 index 0000000000000000000000000000000000000000..aaebae7b25579cabcd3967da765568a282869a49 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/ema_head.py @@ -0,0 +1,168 @@ +import math + +import torch +import torch.distributed as dist +import torch.nn as nn +import torch.nn.functional as F +from annotator.mmpkg.mmcv.cnn import ConvModule + +from ..builder import HEADS +from .decode_head import BaseDecodeHead + + +def reduce_mean(tensor): + """Reduce mean when distributed training.""" + if not (dist.is_available() and dist.is_initialized()): + return tensor + tensor = tensor.clone() + dist.all_reduce(tensor.div_(dist.get_world_size()), op=dist.ReduceOp.SUM) + return tensor + + +class EMAModule(nn.Module): + """Expectation Maximization Attention Module used in EMANet. + + Args: + channels (int): Channels of the whole module. + num_bases (int): Number of bases. + num_stages (int): Number of the EM iterations. + """ + + def __init__(self, channels, num_bases, num_stages, momentum): + super(EMAModule, self).__init__() + assert num_stages >= 1, 'num_stages must be at least 1!' + self.num_bases = num_bases + self.num_stages = num_stages + self.momentum = momentum + + bases = torch.zeros(1, channels, self.num_bases) + bases.normal_(0, math.sqrt(2. / self.num_bases)) + # [1, channels, num_bases] + bases = F.normalize(bases, dim=1, p=2) + self.register_buffer('bases', bases) + + def forward(self, feats): + """Forward function.""" + batch_size, channels, height, width = feats.size() + # [batch_size, channels, height*width] + feats = feats.view(batch_size, channels, height * width) + # [batch_size, channels, num_bases] + bases = self.bases.repeat(batch_size, 1, 1) + + with torch.no_grad(): + for i in range(self.num_stages): + # [batch_size, height*width, num_bases] + attention = torch.einsum('bcn,bck->bnk', feats, bases) + attention = F.softmax(attention, dim=2) + # l1 norm + attention_normed = F.normalize(attention, dim=1, p=1) + # [batch_size, channels, num_bases] + bases = torch.einsum('bcn,bnk->bck', feats, attention_normed) + # l2 norm + bases = F.normalize(bases, dim=1, p=2) + + feats_recon = torch.einsum('bck,bnk->bcn', bases, attention) + feats_recon = feats_recon.view(batch_size, channels, height, width) + + if self.training: + bases = bases.mean(dim=0, keepdim=True) + bases = reduce_mean(bases) + # l2 norm + bases = F.normalize(bases, dim=1, p=2) + self.bases = (1 - + self.momentum) * self.bases + self.momentum * bases + + return feats_recon + + +@HEADS.register_module() +class EMAHead(BaseDecodeHead): + """Expectation Maximization Attention Networks for Semantic Segmentation. + + This head is the implementation of `EMANet + `_. + + Args: + ema_channels (int): EMA module channels + num_bases (int): Number of bases. + num_stages (int): Number of the EM iterations. + concat_input (bool): Whether concat the input and output of convs + before classification layer. Default: True + momentum (float): Momentum to update the base. Default: 0.1. + """ + + def __init__(self, + ema_channels, + num_bases, + num_stages, + concat_input=True, + momentum=0.1, + **kwargs): + super(EMAHead, self).__init__(**kwargs) + self.ema_channels = ema_channels + self.num_bases = num_bases + self.num_stages = num_stages + self.concat_input = concat_input + self.momentum = momentum + self.ema_module = EMAModule(self.ema_channels, self.num_bases, + self.num_stages, self.momentum) + + self.ema_in_conv = ConvModule( + self.in_channels, + self.ema_channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + # project (0, inf) -> (-inf, inf) + self.ema_mid_conv = ConvModule( + self.ema_channels, + self.ema_channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=None, + act_cfg=None) + for param in self.ema_mid_conv.parameters(): + param.requires_grad = False + + self.ema_out_conv = ConvModule( + self.ema_channels, + self.ema_channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=None) + self.bottleneck = ConvModule( + self.ema_channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + if self.concat_input: + self.conv_cat = ConvModule( + self.in_channels + self.channels, + self.channels, + kernel_size=3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + feats = self.ema_in_conv(x) + identity = feats + feats = self.ema_mid_conv(feats) + recon = self.ema_module(feats) + recon = F.relu(recon, inplace=True) + recon = self.ema_out_conv(recon) + output = F.relu(identity + recon, inplace=True) + output = self.bottleneck(output) + if self.concat_input: + output = self.conv_cat(torch.cat([x, output], dim=1)) + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/enc_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/enc_head.py new file mode 100644 index 0000000000000000000000000000000000000000..4c2a22a90b26f3264f63234694f0f290a7891ea2 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/enc_head.py @@ -0,0 +1,187 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +from annotator.mmpkg.mmcv.cnn import ConvModule, build_norm_layer + +from annotator.mmpkg.mmseg.ops import Encoding, resize +from ..builder import HEADS, build_loss +from .decode_head import BaseDecodeHead + + +class EncModule(nn.Module): + """Encoding Module used in EncNet. + + Args: + in_channels (int): Input channels. + num_codes (int): Number of code words. + conv_cfg (dict|None): Config of conv layers. + norm_cfg (dict|None): Config of norm layers. + act_cfg (dict): Config of activation layers. + """ + + def __init__(self, in_channels, num_codes, conv_cfg, norm_cfg, act_cfg): + super(EncModule, self).__init__() + self.encoding_project = ConvModule( + in_channels, + in_channels, + 1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + # TODO: resolve this hack + # change to 1d + if norm_cfg is not None: + encoding_norm_cfg = norm_cfg.copy() + if encoding_norm_cfg['type'] in ['BN', 'IN']: + encoding_norm_cfg['type'] += '1d' + else: + encoding_norm_cfg['type'] = encoding_norm_cfg['type'].replace( + '2d', '1d') + else: + # fallback to BN1d + encoding_norm_cfg = dict(type='BN1d') + self.encoding = nn.Sequential( + Encoding(channels=in_channels, num_codes=num_codes), + build_norm_layer(encoding_norm_cfg, num_codes)[1], + nn.ReLU(inplace=True)) + self.fc = nn.Sequential( + nn.Linear(in_channels, in_channels), nn.Sigmoid()) + + def forward(self, x): + """Forward function.""" + encoding_projection = self.encoding_project(x) + encoding_feat = self.encoding(encoding_projection).mean(dim=1) + batch_size, channels, _, _ = x.size() + gamma = self.fc(encoding_feat) + y = gamma.view(batch_size, channels, 1, 1) + output = F.relu_(x + x * y) + return encoding_feat, output + + +@HEADS.register_module() +class EncHead(BaseDecodeHead): + """Context Encoding for Semantic Segmentation. + + This head is the implementation of `EncNet + `_. + + Args: + num_codes (int): Number of code words. Default: 32. + use_se_loss (bool): Whether use Semantic Encoding Loss (SE-loss) to + regularize the training. Default: True. + add_lateral (bool): Whether use lateral connection to fuse features. + Default: False. + loss_se_decode (dict): Config of decode loss. + Default: dict(type='CrossEntropyLoss', use_sigmoid=True). + """ + + def __init__(self, + num_codes=32, + use_se_loss=True, + add_lateral=False, + loss_se_decode=dict( + type='CrossEntropyLoss', + use_sigmoid=True, + loss_weight=0.2), + **kwargs): + super(EncHead, self).__init__( + input_transform='multiple_select', **kwargs) + self.use_se_loss = use_se_loss + self.add_lateral = add_lateral + self.num_codes = num_codes + self.bottleneck = ConvModule( + self.in_channels[-1], + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + if add_lateral: + self.lateral_convs = nn.ModuleList() + for in_channels in self.in_channels[:-1]: # skip the last one + self.lateral_convs.append( + ConvModule( + in_channels, + self.channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg)) + self.fusion = ConvModule( + len(self.in_channels) * self.channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.enc_module = EncModule( + self.channels, + num_codes=num_codes, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + if self.use_se_loss: + self.loss_se_decode = build_loss(loss_se_decode) + self.se_layer = nn.Linear(self.channels, self.num_classes) + + def forward(self, inputs): + """Forward function.""" + inputs = self._transform_inputs(inputs) + feat = self.bottleneck(inputs[-1]) + if self.add_lateral: + laterals = [ + resize( + lateral_conv(inputs[i]), + size=feat.shape[2:], + mode='bilinear', + align_corners=self.align_corners) + for i, lateral_conv in enumerate(self.lateral_convs) + ] + feat = self.fusion(torch.cat([feat, *laterals], 1)) + encode_feat, output = self.enc_module(feat) + output = self.cls_seg(output) + if self.use_se_loss: + se_output = self.se_layer(encode_feat) + return output, se_output + else: + return output + + def forward_test(self, inputs, img_metas, test_cfg): + """Forward function for testing, ignore se_loss.""" + if self.use_se_loss: + return self.forward(inputs)[0] + else: + return self.forward(inputs) + + @staticmethod + def _convert_to_onehot_labels(seg_label, num_classes): + """Convert segmentation label to onehot. + + Args: + seg_label (Tensor): Segmentation label of shape (N, H, W). + num_classes (int): Number of classes. + + Returns: + Tensor: Onehot labels of shape (N, num_classes). + """ + + batch_size = seg_label.size(0) + onehot_labels = seg_label.new_zeros((batch_size, num_classes)) + for i in range(batch_size): + hist = seg_label[i].float().histc( + bins=num_classes, min=0, max=num_classes - 1) + onehot_labels[i] = hist > 0 + return onehot_labels + + def losses(self, seg_logit, seg_label): + """Compute segmentation and semantic encoding loss.""" + seg_logit, se_seg_logit = seg_logit + loss = dict() + loss.update(super(EncHead, self).losses(seg_logit, seg_label)) + se_loss = self.loss_se_decode( + se_seg_logit, + self._convert_to_onehot_labels(seg_label, self.num_classes)) + loss['loss_se'] = se_loss + return loss diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/fcn_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/fcn_head.py new file mode 100644 index 0000000000000000000000000000000000000000..c4583c57246e8e3b1d15d240b943d046afa5cba5 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/fcn_head.py @@ -0,0 +1,81 @@ +import torch +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import ConvModule + +from ..builder import HEADS +from .decode_head import BaseDecodeHead + + +@HEADS.register_module() +class FCNHead(BaseDecodeHead): + """Fully Convolution Networks for Semantic Segmentation. + + This head is implemented of `FCNNet `_. + + Args: + num_convs (int): Number of convs in the head. Default: 2. + kernel_size (int): The kernel size for convs in the head. Default: 3. + concat_input (bool): Whether concat the input and output of convs + before classification layer. + dilation (int): The dilation rate for convs in the head. Default: 1. + """ + + def __init__(self, + num_convs=2, + kernel_size=3, + concat_input=True, + dilation=1, + **kwargs): + assert num_convs >= 0 and dilation > 0 and isinstance(dilation, int) + self.num_convs = num_convs + self.concat_input = concat_input + self.kernel_size = kernel_size + super(FCNHead, self).__init__(**kwargs) + if num_convs == 0: + assert self.in_channels == self.channels + + conv_padding = (kernel_size // 2) * dilation + convs = [] + convs.append( + ConvModule( + self.in_channels, + self.channels, + kernel_size=kernel_size, + padding=conv_padding, + dilation=dilation, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg)) + for i in range(num_convs - 1): + convs.append( + ConvModule( + self.channels, + self.channels, + kernel_size=kernel_size, + padding=conv_padding, + dilation=dilation, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg)) + if num_convs == 0: + self.convs = nn.Identity() + else: + self.convs = nn.Sequential(*convs) + if self.concat_input: + self.conv_cat = ConvModule( + self.in_channels + self.channels, + self.channels, + kernel_size=kernel_size, + padding=kernel_size // 2, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + output = self.convs(x) + if self.concat_input: + output = self.conv_cat(torch.cat([x, output], dim=1)) + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/fpn_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/fpn_head.py new file mode 100644 index 0000000000000000000000000000000000000000..1a9ba39eebc406bfa422dc98eeaa32a800008a83 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/fpn_head.py @@ -0,0 +1,68 @@ +import numpy as np +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import ConvModule + +from annotator.mmpkg.mmseg.ops import resize +from ..builder import HEADS +from .decode_head import BaseDecodeHead + + +@HEADS.register_module() +class FPNHead(BaseDecodeHead): + """Panoptic Feature Pyramid Networks. + + This head is the implementation of `Semantic FPN + `_. + + Args: + feature_strides (tuple[int]): The strides for input feature maps. + stack_lateral. All strides suppose to be power of 2. The first + one is of largest resolution. + """ + + def __init__(self, feature_strides, **kwargs): + super(FPNHead, self).__init__( + input_transform='multiple_select', **kwargs) + assert len(feature_strides) == len(self.in_channels) + assert min(feature_strides) == feature_strides[0] + self.feature_strides = feature_strides + + self.scale_heads = nn.ModuleList() + for i in range(len(feature_strides)): + head_length = max( + 1, + int(np.log2(feature_strides[i]) - np.log2(feature_strides[0]))) + scale_head = [] + for k in range(head_length): + scale_head.append( + ConvModule( + self.in_channels[i] if k == 0 else self.channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg)) + if feature_strides[i] != feature_strides[0]: + scale_head.append( + nn.Upsample( + scale_factor=2, + mode='bilinear', + align_corners=self.align_corners)) + self.scale_heads.append(nn.Sequential(*scale_head)) + + def forward(self, inputs): + + x = self._transform_inputs(inputs) + + output = self.scale_heads[0](x[0]) + for i in range(1, len(self.feature_strides)): + # non inplace + output = output + resize( + self.scale_heads[i](x[i]), + size=output.shape[2:], + mode='bilinear', + align_corners=self.align_corners) + + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/gc_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/gc_head.py new file mode 100644 index 0000000000000000000000000000000000000000..6342811f67e4affac7886c8fc745a28abcc32c55 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/gc_head.py @@ -0,0 +1,47 @@ +import torch +from annotator.mmpkg.mmcv.cnn import ContextBlock + +from ..builder import HEADS +from .fcn_head import FCNHead + + +@HEADS.register_module() +class GCHead(FCNHead): + """GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond. + + This head is the implementation of `GCNet + `_. + + Args: + ratio (float): Multiplier of channels ratio. Default: 1/4. + pooling_type (str): The pooling type of context aggregation. + Options are 'att', 'avg'. Default: 'avg'. + fusion_types (tuple[str]): The fusion type for feature fusion. + Options are 'channel_add', 'channel_mul'. Default: ('channel_add',) + """ + + def __init__(self, + ratio=1 / 4., + pooling_type='att', + fusion_types=('channel_add', ), + **kwargs): + super(GCHead, self).__init__(num_convs=2, **kwargs) + self.ratio = ratio + self.pooling_type = pooling_type + self.fusion_types = fusion_types + self.gc_block = ContextBlock( + in_channels=self.channels, + ratio=self.ratio, + pooling_type=self.pooling_type, + fusion_types=self.fusion_types) + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + output = self.convs[0](x) + output = self.gc_block(output) + output = self.convs[1](output) + if self.concat_input: + output = self.conv_cat(torch.cat([x, output], dim=1)) + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/lraspp_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/lraspp_head.py new file mode 100644 index 0000000000000000000000000000000000000000..b29d80e77d05cc0c12118e335e266a73bda99ed0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/lraspp_head.py @@ -0,0 +1,90 @@ +import torch +import torch.nn as nn +from annotator.mmpkg.mmcv import is_tuple_of +from annotator.mmpkg.mmcv.cnn import ConvModule + +from annotator.mmpkg.mmseg.ops import resize +from ..builder import HEADS +from .decode_head import BaseDecodeHead + + +@HEADS.register_module() +class LRASPPHead(BaseDecodeHead): + """Lite R-ASPP (LRASPP) head is proposed in Searching for MobileNetV3. + + This head is the improved implementation of `Searching for MobileNetV3 + `_. + + Args: + branch_channels (tuple[int]): The number of output channels in every + each branch. Default: (32, 64). + """ + + def __init__(self, branch_channels=(32, 64), **kwargs): + super(LRASPPHead, self).__init__(**kwargs) + if self.input_transform != 'multiple_select': + raise ValueError('in Lite R-ASPP (LRASPP) head, input_transform ' + f'must be \'multiple_select\'. But received ' + f'\'{self.input_transform}\'') + assert is_tuple_of(branch_channels, int) + assert len(branch_channels) == len(self.in_channels) - 1 + self.branch_channels = branch_channels + + self.convs = nn.Sequential() + self.conv_ups = nn.Sequential() + for i in range(len(branch_channels)): + self.convs.add_module( + f'conv{i}', + nn.Conv2d( + self.in_channels[i], branch_channels[i], 1, bias=False)) + self.conv_ups.add_module( + f'conv_up{i}', + ConvModule( + self.channels + branch_channels[i], + self.channels, + 1, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg, + bias=False)) + + self.conv_up_input = nn.Conv2d(self.channels, self.channels, 1) + + self.aspp_conv = ConvModule( + self.in_channels[-1], + self.channels, + 1, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg, + bias=False) + self.image_pool = nn.Sequential( + nn.AvgPool2d(kernel_size=49, stride=(16, 20)), + ConvModule( + self.in_channels[2], + self.channels, + 1, + act_cfg=dict(type='Sigmoid'), + bias=False)) + + def forward(self, inputs): + """Forward function.""" + inputs = self._transform_inputs(inputs) + + x = inputs[-1] + + x = self.aspp_conv(x) * resize( + self.image_pool(x), + size=x.size()[2:], + mode='bilinear', + align_corners=self.align_corners) + x = self.conv_up_input(x) + + for i in range(len(self.branch_channels) - 1, -1, -1): + x = resize( + x, + size=inputs[i].size()[2:], + mode='bilinear', + align_corners=self.align_corners) + x = torch.cat([x, self.convs[i](inputs[i])], 1) + x = self.conv_ups[i](x) + + return self.cls_seg(x) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/nl_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/nl_head.py new file mode 100644 index 0000000000000000000000000000000000000000..5990df1b8b0d57cfa772ec1b6b6be20a8f667ce7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/nl_head.py @@ -0,0 +1,49 @@ +import torch +from annotator.mmpkg.mmcv.cnn import NonLocal2d + +from ..builder import HEADS +from .fcn_head import FCNHead + + +@HEADS.register_module() +class NLHead(FCNHead): + """Non-local Neural Networks. + + This head is the implementation of `NLNet + `_. + + Args: + reduction (int): Reduction factor of projection transform. Default: 2. + use_scale (bool): Whether to scale pairwise_weight by + sqrt(1/inter_channels). Default: True. + mode (str): The nonlocal mode. Options are 'embedded_gaussian', + 'dot_product'. Default: 'embedded_gaussian.'. + """ + + def __init__(self, + reduction=2, + use_scale=True, + mode='embedded_gaussian', + **kwargs): + super(NLHead, self).__init__(num_convs=2, **kwargs) + self.reduction = reduction + self.use_scale = use_scale + self.mode = mode + self.nl_block = NonLocal2d( + in_channels=self.channels, + reduction=self.reduction, + use_scale=self.use_scale, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + mode=self.mode) + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + output = self.convs[0](x) + output = self.nl_block(output) + output = self.convs[1](output) + if self.concat_input: + output = self.conv_cat(torch.cat([x, output], dim=1)) + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/ocr_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/ocr_head.py new file mode 100644 index 0000000000000000000000000000000000000000..c46d10e5baff54e182af0426a1ecfea9ca190a9f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/ocr_head.py @@ -0,0 +1,127 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +from annotator.mmpkg.mmcv.cnn import ConvModule + +from annotator.mmpkg.mmseg.ops import resize +from ..builder import HEADS +from ..utils import SelfAttentionBlock as _SelfAttentionBlock +from .cascade_decode_head import BaseCascadeDecodeHead + + +class SpatialGatherModule(nn.Module): + """Aggregate the context features according to the initial predicted + probability distribution. + + Employ the soft-weighted method to aggregate the context. + """ + + def __init__(self, scale): + super(SpatialGatherModule, self).__init__() + self.scale = scale + + def forward(self, feats, probs): + """Forward function.""" + batch_size, num_classes, height, width = probs.size() + channels = feats.size(1) + probs = probs.view(batch_size, num_classes, -1) + feats = feats.view(batch_size, channels, -1) + # [batch_size, height*width, num_classes] + feats = feats.permute(0, 2, 1) + # [batch_size, channels, height*width] + probs = F.softmax(self.scale * probs, dim=2) + # [batch_size, channels, num_classes] + ocr_context = torch.matmul(probs, feats) + ocr_context = ocr_context.permute(0, 2, 1).contiguous().unsqueeze(3) + return ocr_context + + +class ObjectAttentionBlock(_SelfAttentionBlock): + """Make a OCR used SelfAttentionBlock.""" + + def __init__(self, in_channels, channels, scale, conv_cfg, norm_cfg, + act_cfg): + if scale > 1: + query_downsample = nn.MaxPool2d(kernel_size=scale) + else: + query_downsample = None + super(ObjectAttentionBlock, self).__init__( + key_in_channels=in_channels, + query_in_channels=in_channels, + channels=channels, + out_channels=in_channels, + share_key_query=False, + query_downsample=query_downsample, + key_downsample=None, + key_query_num_convs=2, + key_query_norm=True, + value_out_num_convs=1, + value_out_norm=True, + matmul_norm=True, + with_out=True, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + self.bottleneck = ConvModule( + in_channels * 2, + in_channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def forward(self, query_feats, key_feats): + """Forward function.""" + context = super(ObjectAttentionBlock, + self).forward(query_feats, key_feats) + output = self.bottleneck(torch.cat([context, query_feats], dim=1)) + if self.query_downsample is not None: + output = resize(query_feats) + + return output + + +@HEADS.register_module() +class OCRHead(BaseCascadeDecodeHead): + """Object-Contextual Representations for Semantic Segmentation. + + This head is the implementation of `OCRNet + `_. + + Args: + ocr_channels (int): The intermediate channels of OCR block. + scale (int): The scale of probability map in SpatialGatherModule in + Default: 1. + """ + + def __init__(self, ocr_channels, scale=1, **kwargs): + super(OCRHead, self).__init__(**kwargs) + self.ocr_channels = ocr_channels + self.scale = scale + self.object_context_block = ObjectAttentionBlock( + self.channels, + self.ocr_channels, + self.scale, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.spatial_gather_module = SpatialGatherModule(self.scale) + + self.bottleneck = ConvModule( + self.in_channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def forward(self, inputs, prev_output): + """Forward function.""" + x = self._transform_inputs(inputs) + feats = self.bottleneck(x) + context = self.spatial_gather_module(feats, prev_output) + object_context = self.object_context_block(feats, context) + output = self.cls_seg(object_context) + + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/point_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/point_head.py new file mode 100644 index 0000000000000000000000000000000000000000..c6782763e30386d99115977ebe5a4d9291bae8d9 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/point_head.py @@ -0,0 +1,354 @@ +# Modified from https://github.com/facebookresearch/detectron2/tree/master/projects/PointRend/point_head/point_head.py # noqa + +import torch +import torch.nn as nn + +try: + from mmcv.cnn import ConvModule, normal_init + from mmcv.ops import point_sample +except ImportError: + from annotator.mmpkg.mmcv.cnn import ConvModule, normal_init + from annotator.mmpkg.mmcv.ops import point_sample + +from annotator.mmpkg.mmseg.models.builder import HEADS +from annotator.mmpkg.mmseg.ops import resize +from ..losses import accuracy +from .cascade_decode_head import BaseCascadeDecodeHead + + +def calculate_uncertainty(seg_logits): + """Estimate uncertainty based on seg logits. + + For each location of the prediction ``seg_logits`` we estimate + uncertainty as the difference between top first and top second + predicted logits. + + Args: + seg_logits (Tensor): Semantic segmentation logits, + shape (batch_size, num_classes, height, width). + + Returns: + scores (Tensor): T uncertainty scores with the most uncertain + locations having the highest uncertainty score, shape ( + batch_size, 1, height, width) + """ + top2_scores = torch.topk(seg_logits, k=2, dim=1)[0] + return (top2_scores[:, 1] - top2_scores[:, 0]).unsqueeze(1) + + +@HEADS.register_module() +class PointHead(BaseCascadeDecodeHead): + """A mask point head use in PointRend. + + ``PointHead`` use shared multi-layer perceptron (equivalent to + nn.Conv1d) to predict the logit of input points. The fine-grained feature + and coarse feature will be concatenate together for predication. + + Args: + num_fcs (int): Number of fc layers in the head. Default: 3. + in_channels (int): Number of input channels. Default: 256. + fc_channels (int): Number of fc channels. Default: 256. + num_classes (int): Number of classes for logits. Default: 80. + class_agnostic (bool): Whether use class agnostic classification. + If so, the output channels of logits will be 1. Default: False. + coarse_pred_each_layer (bool): Whether concatenate coarse feature with + the output of each fc layer. Default: True. + conv_cfg (dict|None): Dictionary to construct and config conv layer. + Default: dict(type='Conv1d')) + norm_cfg (dict|None): Dictionary to construct and config norm layer. + Default: None. + loss_point (dict): Dictionary to construct and config loss layer of + point head. Default: dict(type='CrossEntropyLoss', use_mask=True, + loss_weight=1.0). + """ + + def __init__(self, + num_fcs=3, + coarse_pred_each_layer=True, + conv_cfg=dict(type='Conv1d'), + norm_cfg=None, + act_cfg=dict(type='ReLU', inplace=False), + **kwargs): + super(PointHead, self).__init__( + input_transform='multiple_select', + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg, + **kwargs) + + self.num_fcs = num_fcs + self.coarse_pred_each_layer = coarse_pred_each_layer + + fc_in_channels = sum(self.in_channels) + self.num_classes + fc_channels = self.channels + self.fcs = nn.ModuleList() + for k in range(num_fcs): + fc = ConvModule( + fc_in_channels, + fc_channels, + kernel_size=1, + stride=1, + padding=0, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + self.fcs.append(fc) + fc_in_channels = fc_channels + fc_in_channels += self.num_classes if self.coarse_pred_each_layer \ + else 0 + self.fc_seg = nn.Conv1d( + fc_in_channels, + self.num_classes, + kernel_size=1, + stride=1, + padding=0) + if self.dropout_ratio > 0: + self.dropout = nn.Dropout(self.dropout_ratio) + delattr(self, 'conv_seg') + + def init_weights(self): + """Initialize weights of classification layer.""" + normal_init(self.fc_seg, std=0.001) + + def cls_seg(self, feat): + """Classify each pixel with fc.""" + if self.dropout is not None: + feat = self.dropout(feat) + output = self.fc_seg(feat) + return output + + def forward(self, fine_grained_point_feats, coarse_point_feats): + x = torch.cat([fine_grained_point_feats, coarse_point_feats], dim=1) + for fc in self.fcs: + x = fc(x) + if self.coarse_pred_each_layer: + x = torch.cat((x, coarse_point_feats), dim=1) + return self.cls_seg(x) + + def _get_fine_grained_point_feats(self, x, points): + """Sample from fine grained features. + + Args: + x (list[Tensor]): Feature pyramid from by neck or backbone. + points (Tensor): Point coordinates, shape (batch_size, + num_points, 2). + + Returns: + fine_grained_feats (Tensor): Sampled fine grained feature, + shape (batch_size, sum(channels of x), num_points). + """ + + fine_grained_feats_list = [ + point_sample(_, points, align_corners=self.align_corners) + for _ in x + ] + if len(fine_grained_feats_list) > 1: + fine_grained_feats = torch.cat(fine_grained_feats_list, dim=1) + else: + fine_grained_feats = fine_grained_feats_list[0] + + return fine_grained_feats + + def _get_coarse_point_feats(self, prev_output, points): + """Sample from fine grained features. + + Args: + prev_output (list[Tensor]): Prediction of previous decode head. + points (Tensor): Point coordinates, shape (batch_size, + num_points, 2). + + Returns: + coarse_feats (Tensor): Sampled coarse feature, shape (batch_size, + num_classes, num_points). + """ + + coarse_feats = point_sample( + prev_output, points, align_corners=self.align_corners) + + return coarse_feats + + def forward_train(self, inputs, prev_output, img_metas, gt_semantic_seg, + train_cfg): + """Forward function for training. + Args: + inputs (list[Tensor]): List of multi-level img features. + prev_output (Tensor): The output of previous decode head. + img_metas (list[dict]): List of image info dict where each dict + has: 'img_shape', 'scale_factor', 'flip', and may also contain + 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. + For details on the values of these keys see + `mmseg/datasets/pipelines/formatting.py:Collect`. + gt_semantic_seg (Tensor): Semantic segmentation masks + used if the architecture supports semantic segmentation task. + train_cfg (dict): The training config. + + Returns: + dict[str, Tensor]: a dictionary of loss components + """ + x = self._transform_inputs(inputs) + with torch.no_grad(): + points = self.get_points_train( + prev_output, calculate_uncertainty, cfg=train_cfg) + fine_grained_point_feats = self._get_fine_grained_point_feats( + x, points) + coarse_point_feats = self._get_coarse_point_feats(prev_output, points) + point_logits = self.forward(fine_grained_point_feats, + coarse_point_feats) + point_label = point_sample( + gt_semantic_seg.float(), + points, + mode='nearest', + align_corners=self.align_corners) + point_label = point_label.squeeze(1).long() + + losses = self.losses(point_logits, point_label) + + return losses + + def forward_test(self, inputs, prev_output, img_metas, test_cfg): + """Forward function for testing. + + Args: + inputs (list[Tensor]): List of multi-level img features. + prev_output (Tensor): The output of previous decode head. + img_metas (list[dict]): List of image info dict where each dict + has: 'img_shape', 'scale_factor', 'flip', and may also contain + 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. + For details on the values of these keys see + `mmseg/datasets/pipelines/formatting.py:Collect`. + test_cfg (dict): The testing config. + + Returns: + Tensor: Output segmentation map. + """ + + x = self._transform_inputs(inputs) + refined_seg_logits = prev_output.clone() + for _ in range(test_cfg.subdivision_steps): + refined_seg_logits = resize( + refined_seg_logits, + scale_factor=test_cfg.scale_factor, + mode='bilinear', + align_corners=self.align_corners) + batch_size, channels, height, width = refined_seg_logits.shape + point_indices, points = self.get_points_test( + refined_seg_logits, calculate_uncertainty, cfg=test_cfg) + fine_grained_point_feats = self._get_fine_grained_point_feats( + x, points) + coarse_point_feats = self._get_coarse_point_feats( + prev_output, points) + point_logits = self.forward(fine_grained_point_feats, + coarse_point_feats) + + point_indices = point_indices.unsqueeze(1).expand(-1, channels, -1) + refined_seg_logits = refined_seg_logits.reshape( + batch_size, channels, height * width) + refined_seg_logits = refined_seg_logits.scatter_( + 2, point_indices, point_logits) + refined_seg_logits = refined_seg_logits.view( + batch_size, channels, height, width) + + return refined_seg_logits + + def losses(self, point_logits, point_label): + """Compute segmentation loss.""" + loss = dict() + loss['loss_point'] = self.loss_decode( + point_logits, point_label, ignore_index=self.ignore_index) + loss['acc_point'] = accuracy(point_logits, point_label) + return loss + + def get_points_train(self, seg_logits, uncertainty_func, cfg): + """Sample points for training. + + Sample points in [0, 1] x [0, 1] coordinate space based on their + uncertainty. The uncertainties are calculated for each point using + 'uncertainty_func' function that takes point's logit prediction as + input. + + Args: + seg_logits (Tensor): Semantic segmentation logits, shape ( + batch_size, num_classes, height, width). + uncertainty_func (func): uncertainty calculation function. + cfg (dict): Training config of point head. + + Returns: + point_coords (Tensor): A tensor of shape (batch_size, num_points, + 2) that contains the coordinates of ``num_points`` sampled + points. + """ + num_points = cfg.num_points + oversample_ratio = cfg.oversample_ratio + importance_sample_ratio = cfg.importance_sample_ratio + assert oversample_ratio >= 1 + assert 0 <= importance_sample_ratio <= 1 + batch_size = seg_logits.shape[0] + num_sampled = int(num_points * oversample_ratio) + point_coords = torch.rand( + batch_size, num_sampled, 2, device=seg_logits.device) + point_logits = point_sample(seg_logits, point_coords) + # It is crucial to calculate uncertainty based on the sampled + # prediction value for the points. Calculating uncertainties of the + # coarse predictions first and sampling them for points leads to + # incorrect results. To illustrate this: assume uncertainty func( + # logits)=-abs(logits), a sampled point between two coarse + # predictions with -1 and 1 logits has 0 logits, and therefore 0 + # uncertainty value. However, if we calculate uncertainties for the + # coarse predictions first, both will have -1 uncertainty, + # and sampled point will get -1 uncertainty. + point_uncertainties = uncertainty_func(point_logits) + num_uncertain_points = int(importance_sample_ratio * num_points) + num_random_points = num_points - num_uncertain_points + idx = torch.topk( + point_uncertainties[:, 0, :], k=num_uncertain_points, dim=1)[1] + shift = num_sampled * torch.arange( + batch_size, dtype=torch.long, device=seg_logits.device) + idx += shift[:, None] + point_coords = point_coords.view(-1, 2)[idx.view(-1), :].view( + batch_size, num_uncertain_points, 2) + if num_random_points > 0: + rand_point_coords = torch.rand( + batch_size, num_random_points, 2, device=seg_logits.device) + point_coords = torch.cat((point_coords, rand_point_coords), dim=1) + return point_coords + + def get_points_test(self, seg_logits, uncertainty_func, cfg): + """Sample points for testing. + + Find ``num_points`` most uncertain points from ``uncertainty_map``. + + Args: + seg_logits (Tensor): A tensor of shape (batch_size, num_classes, + height, width) for class-specific or class-agnostic prediction. + uncertainty_func (func): uncertainty calculation function. + cfg (dict): Testing config of point head. + + Returns: + point_indices (Tensor): A tensor of shape (batch_size, num_points) + that contains indices from [0, height x width) of the most + uncertain points. + point_coords (Tensor): A tensor of shape (batch_size, num_points, + 2) that contains [0, 1] x [0, 1] normalized coordinates of the + most uncertain points from the ``height x width`` grid . + """ + + num_points = cfg.subdivision_num_points + uncertainty_map = uncertainty_func(seg_logits) + batch_size, _, height, width = uncertainty_map.shape + h_step = 1.0 / height + w_step = 1.0 / width + + uncertainty_map = uncertainty_map.view(batch_size, height * width) + num_points = min(height * width, num_points) + point_indices = uncertainty_map.topk(num_points, dim=1)[1] + point_coords = torch.zeros( + batch_size, + num_points, + 2, + dtype=torch.float, + device=seg_logits.device) + point_coords[:, :, 0] = w_step / 2.0 + (point_indices % + width).float() * w_step + point_coords[:, :, 1] = h_step / 2.0 + (point_indices // + width).float() * h_step + return point_indices, point_coords diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/psa_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/psa_head.py new file mode 100644 index 0000000000000000000000000000000000000000..ba6fe3a8b8f8dc7c4e4d3b9bc09e9642c0b3732f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/psa_head.py @@ -0,0 +1,199 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +from annotator.mmpkg.mmcv.cnn import ConvModule + +from annotator.mmpkg.mmseg.ops import resize +from ..builder import HEADS +from .decode_head import BaseDecodeHead + +try: + try: + from mmcv.ops import PSAMask + except ImportError: + from annotator.mmpkg.mmcv.ops import PSAMask +except ModuleNotFoundError: + PSAMask = None + + +@HEADS.register_module() +class PSAHead(BaseDecodeHead): + """Point-wise Spatial Attention Network for Scene Parsing. + + This head is the implementation of `PSANet + `_. + + Args: + mask_size (tuple[int]): The PSA mask size. It usually equals input + size. + psa_type (str): The type of psa module. Options are 'collect', + 'distribute', 'bi-direction'. Default: 'bi-direction' + compact (bool): Whether use compact map for 'collect' mode. + Default: True. + shrink_factor (int): The downsample factors of psa mask. Default: 2. + normalization_factor (float): The normalize factor of attention. + psa_softmax (bool): Whether use softmax for attention. + """ + + def __init__(self, + mask_size, + psa_type='bi-direction', + compact=False, + shrink_factor=2, + normalization_factor=1.0, + psa_softmax=True, + **kwargs): + if PSAMask is None: + raise RuntimeError('Please install mmcv-full for PSAMask ops') + super(PSAHead, self).__init__(**kwargs) + assert psa_type in ['collect', 'distribute', 'bi-direction'] + self.psa_type = psa_type + self.compact = compact + self.shrink_factor = shrink_factor + self.mask_size = mask_size + mask_h, mask_w = mask_size + self.psa_softmax = psa_softmax + if normalization_factor is None: + normalization_factor = mask_h * mask_w + self.normalization_factor = normalization_factor + + self.reduce = ConvModule( + self.in_channels, + self.channels, + kernel_size=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.attention = nn.Sequential( + ConvModule( + self.channels, + self.channels, + kernel_size=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg), + nn.Conv2d( + self.channels, mask_h * mask_w, kernel_size=1, bias=False)) + if psa_type == 'bi-direction': + self.reduce_p = ConvModule( + self.in_channels, + self.channels, + kernel_size=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.attention_p = nn.Sequential( + ConvModule( + self.channels, + self.channels, + kernel_size=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg), + nn.Conv2d( + self.channels, mask_h * mask_w, kernel_size=1, bias=False)) + self.psamask_collect = PSAMask('collect', mask_size) + self.psamask_distribute = PSAMask('distribute', mask_size) + else: + self.psamask = PSAMask(psa_type, mask_size) + self.proj = ConvModule( + self.channels * (2 if psa_type == 'bi-direction' else 1), + self.in_channels, + kernel_size=1, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + self.bottleneck = ConvModule( + self.in_channels * 2, + self.channels, + kernel_size=3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + identity = x + align_corners = self.align_corners + if self.psa_type in ['collect', 'distribute']: + out = self.reduce(x) + n, c, h, w = out.size() + if self.shrink_factor != 1: + if h % self.shrink_factor and w % self.shrink_factor: + h = (h - 1) // self.shrink_factor + 1 + w = (w - 1) // self.shrink_factor + 1 + align_corners = True + else: + h = h // self.shrink_factor + w = w // self.shrink_factor + align_corners = False + out = resize( + out, + size=(h, w), + mode='bilinear', + align_corners=align_corners) + y = self.attention(out) + if self.compact: + if self.psa_type == 'collect': + y = y.view(n, h * w, + h * w).transpose(1, 2).view(n, h * w, h, w) + else: + y = self.psamask(y) + if self.psa_softmax: + y = F.softmax(y, dim=1) + out = torch.bmm( + out.view(n, c, h * w), y.view(n, h * w, h * w)).view( + n, c, h, w) * (1.0 / self.normalization_factor) + else: + x_col = self.reduce(x) + x_dis = self.reduce_p(x) + n, c, h, w = x_col.size() + if self.shrink_factor != 1: + if h % self.shrink_factor and w % self.shrink_factor: + h = (h - 1) // self.shrink_factor + 1 + w = (w - 1) // self.shrink_factor + 1 + align_corners = True + else: + h = h // self.shrink_factor + w = w // self.shrink_factor + align_corners = False + x_col = resize( + x_col, + size=(h, w), + mode='bilinear', + align_corners=align_corners) + x_dis = resize( + x_dis, + size=(h, w), + mode='bilinear', + align_corners=align_corners) + y_col = self.attention(x_col) + y_dis = self.attention_p(x_dis) + if self.compact: + y_dis = y_dis.view(n, h * w, + h * w).transpose(1, 2).view(n, h * w, h, w) + else: + y_col = self.psamask_collect(y_col) + y_dis = self.psamask_distribute(y_dis) + if self.psa_softmax: + y_col = F.softmax(y_col, dim=1) + y_dis = F.softmax(y_dis, dim=1) + x_col = torch.bmm( + x_col.view(n, c, h * w), y_col.view(n, h * w, h * w)).view( + n, c, h, w) * (1.0 / self.normalization_factor) + x_dis = torch.bmm( + x_dis.view(n, c, h * w), y_dis.view(n, h * w, h * w)).view( + n, c, h, w) * (1.0 / self.normalization_factor) + out = torch.cat([x_col, x_dis], 1) + out = self.proj(out) + out = resize( + out, + size=identity.shape[2:], + mode='bilinear', + align_corners=align_corners) + out = self.bottleneck(torch.cat((identity, out), dim=1)) + out = self.cls_seg(out) + return out diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/psp_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/psp_head.py new file mode 100644 index 0000000000000000000000000000000000000000..2a88d807bfe11fe224305f8de745cde3aa739db0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/psp_head.py @@ -0,0 +1,101 @@ +import torch +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import ConvModule + +from annotator.mmpkg.mmseg.ops import resize +from ..builder import HEADS +from .decode_head import BaseDecodeHead + + +class PPM(nn.ModuleList): + """Pooling Pyramid Module used in PSPNet. + + Args: + pool_scales (tuple[int]): Pooling scales used in Pooling Pyramid + Module. + in_channels (int): Input channels. + channels (int): Channels after modules, before conv_seg. + conv_cfg (dict|None): Config of conv layers. + norm_cfg (dict|None): Config of norm layers. + act_cfg (dict): Config of activation layers. + align_corners (bool): align_corners argument of F.interpolate. + """ + + def __init__(self, pool_scales, in_channels, channels, conv_cfg, norm_cfg, + act_cfg, align_corners): + super(PPM, self).__init__() + self.pool_scales = pool_scales + self.align_corners = align_corners + self.in_channels = in_channels + self.channels = channels + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + for pool_scale in pool_scales: + self.append( + nn.Sequential( + nn.AdaptiveAvgPool2d(pool_scale), + ConvModule( + self.in_channels, + self.channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg))) + + def forward(self, x): + """Forward function.""" + ppm_outs = [] + for ppm in self: + ppm_out = ppm(x) + upsampled_ppm_out = resize( + ppm_out, + size=x.size()[2:], + mode='bilinear', + align_corners=self.align_corners) + ppm_outs.append(upsampled_ppm_out) + return ppm_outs + + +@HEADS.register_module() +class PSPHead(BaseDecodeHead): + """Pyramid Scene Parsing Network. + + This head is the implementation of + `PSPNet `_. + + Args: + pool_scales (tuple[int]): Pooling scales used in Pooling Pyramid + Module. Default: (1, 2, 3, 6). + """ + + def __init__(self, pool_scales=(1, 2, 3, 6), **kwargs): + super(PSPHead, self).__init__(**kwargs) + assert isinstance(pool_scales, (list, tuple)) + self.pool_scales = pool_scales + self.psp_modules = PPM( + self.pool_scales, + self.in_channels, + self.channels, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg, + align_corners=self.align_corners) + self.bottleneck = ConvModule( + self.in_channels + len(pool_scales) * self.channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + psp_outs = [x] + psp_outs.extend(self.psp_modules(x)) + psp_outs = torch.cat(psp_outs, dim=1) + output = self.bottleneck(psp_outs) + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/sep_aspp_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/sep_aspp_head.py new file mode 100644 index 0000000000000000000000000000000000000000..a23970699df7afd86f483316be3c8d1a34d43c18 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/sep_aspp_head.py @@ -0,0 +1,101 @@ +import torch +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import ConvModule, DepthwiseSeparableConvModule + +from annotator.mmpkg.mmseg.ops import resize +from ..builder import HEADS +from .aspp_head import ASPPHead, ASPPModule + + +class DepthwiseSeparableASPPModule(ASPPModule): + """Atrous Spatial Pyramid Pooling (ASPP) Module with depthwise separable + conv.""" + + def __init__(self, **kwargs): + super(DepthwiseSeparableASPPModule, self).__init__(**kwargs) + for i, dilation in enumerate(self.dilations): + if dilation > 1: + self[i] = DepthwiseSeparableConvModule( + self.in_channels, + self.channels, + 3, + dilation=dilation, + padding=dilation, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + +@HEADS.register_module() +class DepthwiseSeparableASPPHead(ASPPHead): + """Encoder-Decoder with Atrous Separable Convolution for Semantic Image + Segmentation. + + This head is the implementation of `DeepLabV3+ + `_. + + Args: + c1_in_channels (int): The input channels of c1 decoder. If is 0, + the no decoder will be used. + c1_channels (int): The intermediate channels of c1 decoder. + """ + + def __init__(self, c1_in_channels, c1_channels, **kwargs): + super(DepthwiseSeparableASPPHead, self).__init__(**kwargs) + assert c1_in_channels >= 0 + self.aspp_modules = DepthwiseSeparableASPPModule( + dilations=self.dilations, + in_channels=self.in_channels, + channels=self.channels, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + if c1_in_channels > 0: + self.c1_bottleneck = ConvModule( + c1_in_channels, + c1_channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + else: + self.c1_bottleneck = None + self.sep_bottleneck = nn.Sequential( + DepthwiseSeparableConvModule( + self.channels + c1_channels, + self.channels, + 3, + padding=1, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg), + DepthwiseSeparableConvModule( + self.channels, + self.channels, + 3, + padding=1, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg)) + + def forward(self, inputs): + """Forward function.""" + x = self._transform_inputs(inputs) + aspp_outs = [ + resize( + self.image_pool(x), + size=x.size()[2:], + mode='bilinear', + align_corners=self.align_corners) + ] + aspp_outs.extend(self.aspp_modules(x)) + aspp_outs = torch.cat(aspp_outs, dim=1) + output = self.bottleneck(aspp_outs) + if self.c1_bottleneck is not None: + c1_output = self.c1_bottleneck(inputs[0]) + output = resize( + input=output, + size=c1_output.shape[2:], + mode='bilinear', + align_corners=self.align_corners) + output = torch.cat([output, c1_output], dim=1) + output = self.sep_bottleneck(output) + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/sep_fcn_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/sep_fcn_head.py new file mode 100644 index 0000000000000000000000000000000000000000..3ea198ab8a96919dfb6974fd73b1476aa488aef2 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/sep_fcn_head.py @@ -0,0 +1,51 @@ +from annotator.mmpkg.mmcv.cnn import DepthwiseSeparableConvModule + +from ..builder import HEADS +from .fcn_head import FCNHead + + +@HEADS.register_module() +class DepthwiseSeparableFCNHead(FCNHead): + """Depthwise-Separable Fully Convolutional Network for Semantic + Segmentation. + + This head is implemented according to Fast-SCNN paper. + Args: + in_channels(int): Number of output channels of FFM. + channels(int): Number of middle-stage channels in the decode head. + concat_input(bool): Whether to concatenate original decode input into + the result of several consecutive convolution layers. + Default: True. + num_classes(int): Used to determine the dimension of + final prediction tensor. + in_index(int): Correspond with 'out_indices' in FastSCNN backbone. + norm_cfg (dict | None): Config of norm layers. + align_corners (bool): align_corners argument of F.interpolate. + Default: False. + loss_decode(dict): Config of loss type and some + relevant additional options. + """ + + def __init__(self, **kwargs): + super(DepthwiseSeparableFCNHead, self).__init__(**kwargs) + self.convs[0] = DepthwiseSeparableConvModule( + self.in_channels, + self.channels, + kernel_size=self.kernel_size, + padding=self.kernel_size // 2, + norm_cfg=self.norm_cfg) + for i in range(1, self.num_convs): + self.convs[i] = DepthwiseSeparableConvModule( + self.channels, + self.channels, + kernel_size=self.kernel_size, + padding=self.kernel_size // 2, + norm_cfg=self.norm_cfg) + + if self.concat_input: + self.conv_cat = DepthwiseSeparableConvModule( + self.in_channels + self.channels, + self.channels, + kernel_size=self.kernel_size, + padding=self.kernel_size // 2, + norm_cfg=self.norm_cfg) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/uper_head.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/uper_head.py new file mode 100644 index 0000000000000000000000000000000000000000..952473578c1f5b903f5fc7f9d13a4e40ea5dec87 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/decode_heads/uper_head.py @@ -0,0 +1,126 @@ +import torch +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import ConvModule + +from annotator.mmpkg.mmseg.ops import resize +from ..builder import HEADS +from .decode_head import BaseDecodeHead +from .psp_head import PPM + + +@HEADS.register_module() +class UPerHead(BaseDecodeHead): + """Unified Perceptual Parsing for Scene Understanding. + + This head is the implementation of `UPerNet + `_. + + Args: + pool_scales (tuple[int]): Pooling scales used in Pooling Pyramid + Module applied on the last feature. Default: (1, 2, 3, 6). + """ + + def __init__(self, pool_scales=(1, 2, 3, 6), **kwargs): + super(UPerHead, self).__init__( + input_transform='multiple_select', **kwargs) + # PSP Module + self.psp_modules = PPM( + pool_scales, + self.in_channels[-1], + self.channels, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg, + align_corners=self.align_corners) + self.bottleneck = ConvModule( + self.in_channels[-1] + len(pool_scales) * self.channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + # FPN Module + self.lateral_convs = nn.ModuleList() + self.fpn_convs = nn.ModuleList() + for in_channels in self.in_channels[:-1]: # skip the top layer + l_conv = ConvModule( + in_channels, + self.channels, + 1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg, + inplace=False) + fpn_conv = ConvModule( + self.channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg, + inplace=False) + self.lateral_convs.append(l_conv) + self.fpn_convs.append(fpn_conv) + + self.fpn_bottleneck = ConvModule( + len(self.in_channels) * self.channels, + self.channels, + 3, + padding=1, + conv_cfg=self.conv_cfg, + norm_cfg=self.norm_cfg, + act_cfg=self.act_cfg) + + def psp_forward(self, inputs): + """Forward function of PSP module.""" + x = inputs[-1] + psp_outs = [x] + psp_outs.extend(self.psp_modules(x)) + psp_outs = torch.cat(psp_outs, dim=1) + output = self.bottleneck(psp_outs) + + return output + + def forward(self, inputs): + """Forward function.""" + + inputs = self._transform_inputs(inputs) + + # build laterals + laterals = [ + lateral_conv(inputs[i]) + for i, lateral_conv in enumerate(self.lateral_convs) + ] + + laterals.append(self.psp_forward(inputs)) + + # build top-down path + used_backbone_levels = len(laterals) + for i in range(used_backbone_levels - 1, 0, -1): + prev_shape = laterals[i - 1].shape[2:] + laterals[i - 1] += resize( + laterals[i], + size=prev_shape, + mode='bilinear', + align_corners=self.align_corners) + + # build outputs + fpn_outs = [ + self.fpn_convs[i](laterals[i]) + for i in range(used_backbone_levels - 1) + ] + # append psp feature + fpn_outs.append(laterals[-1]) + + for i in range(used_backbone_levels - 1, 0, -1): + fpn_outs[i] = resize( + fpn_outs[i], + size=fpn_outs[0].shape[2:], + mode='bilinear', + align_corners=self.align_corners) + fpn_outs = torch.cat(fpn_outs, dim=1) + output = self.fpn_bottleneck(fpn_outs) + output = self.cls_seg(output) + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..beca72045694273d63465bac2f27dbc6672271db --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/__init__.py @@ -0,0 +1,12 @@ +from .accuracy import Accuracy, accuracy +from .cross_entropy_loss import (CrossEntropyLoss, binary_cross_entropy, + cross_entropy, mask_cross_entropy) +from .dice_loss import DiceLoss +from .lovasz_loss import LovaszLoss +from .utils import reduce_loss, weight_reduce_loss, weighted_loss + +__all__ = [ + 'accuracy', 'Accuracy', 'cross_entropy', 'binary_cross_entropy', + 'mask_cross_entropy', 'CrossEntropyLoss', 'reduce_loss', + 'weight_reduce_loss', 'weighted_loss', 'LovaszLoss', 'DiceLoss' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/accuracy.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/accuracy.py new file mode 100644 index 0000000000000000000000000000000000000000..c0fd2e7e74a0f721c4a814c09d6e453e5956bb38 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/accuracy.py @@ -0,0 +1,78 @@ +import torch.nn as nn + + +def accuracy(pred, target, topk=1, thresh=None): + """Calculate accuracy according to the prediction and target. + + Args: + pred (torch.Tensor): The model prediction, shape (N, num_class, ...) + target (torch.Tensor): The target of each prediction, shape (N, , ...) + topk (int | tuple[int], optional): If the predictions in ``topk`` + matches the target, the predictions will be regarded as + correct ones. Defaults to 1. + thresh (float, optional): If not None, predictions with scores under + this threshold are considered incorrect. Default to None. + + Returns: + float | tuple[float]: If the input ``topk`` is a single integer, + the function will return a single float as accuracy. If + ``topk`` is a tuple containing multiple integers, the + function will return a tuple containing accuracies of + each ``topk`` number. + """ + assert isinstance(topk, (int, tuple)) + if isinstance(topk, int): + topk = (topk, ) + return_single = True + else: + return_single = False + + maxk = max(topk) + if pred.size(0) == 0: + accu = [pred.new_tensor(0.) for i in range(len(topk))] + return accu[0] if return_single else accu + assert pred.ndim == target.ndim + 1 + assert pred.size(0) == target.size(0) + assert maxk <= pred.size(1), \ + f'maxk {maxk} exceeds pred dimension {pred.size(1)}' + pred_value, pred_label = pred.topk(maxk, dim=1) + # transpose to shape (maxk, N, ...) + pred_label = pred_label.transpose(0, 1) + correct = pred_label.eq(target.unsqueeze(0).expand_as(pred_label)) + if thresh is not None: + # Only prediction values larger than thresh are counted as correct + correct = correct & (pred_value > thresh).t() + res = [] + for k in topk: + correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) + res.append(correct_k.mul_(100.0 / target.numel())) + return res[0] if return_single else res + + +class Accuracy(nn.Module): + """Accuracy calculation module.""" + + def __init__(self, topk=(1, ), thresh=None): + """Module to calculate the accuracy. + + Args: + topk (tuple, optional): The criterion used to calculate the + accuracy. Defaults to (1,). + thresh (float, optional): If not None, predictions with scores + under this threshold are considered incorrect. Default to None. + """ + super().__init__() + self.topk = topk + self.thresh = thresh + + def forward(self, pred, target): + """Forward function to calculate accuracy. + + Args: + pred (torch.Tensor): Prediction of models. + target (torch.Tensor): Target for each prediction. + + Returns: + tuple[float]: The accuracies under different topk criterions. + """ + return accuracy(pred, target, self.topk, self.thresh) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/cross_entropy_loss.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/cross_entropy_loss.py new file mode 100644 index 0000000000000000000000000000000000000000..42c0790c98616bb69621deed55547fc04c7392ef --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/cross_entropy_loss.py @@ -0,0 +1,198 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +from ..builder import LOSSES +from .utils import get_class_weight, weight_reduce_loss + + +def cross_entropy(pred, + label, + weight=None, + class_weight=None, + reduction='mean', + avg_factor=None, + ignore_index=-100): + """The wrapper function for :func:`F.cross_entropy`""" + # class_weight is a manual rescaling weight given to each class. + # If given, has to be a Tensor of size C element-wise losses + loss = F.cross_entropy( + pred, + label, + weight=class_weight, + reduction='none', + ignore_index=ignore_index) + + # apply weights and do the reduction + if weight is not None: + weight = weight.float() + loss = weight_reduce_loss( + loss, weight=weight, reduction=reduction, avg_factor=avg_factor) + + return loss + + +def _expand_onehot_labels(labels, label_weights, target_shape, ignore_index): + """Expand onehot labels to match the size of prediction.""" + bin_labels = labels.new_zeros(target_shape) + valid_mask = (labels >= 0) & (labels != ignore_index) + inds = torch.nonzero(valid_mask, as_tuple=True) + + if inds[0].numel() > 0: + if labels.dim() == 3: + bin_labels[inds[0], labels[valid_mask], inds[1], inds[2]] = 1 + else: + bin_labels[inds[0], labels[valid_mask]] = 1 + + valid_mask = valid_mask.unsqueeze(1).expand(target_shape).float() + if label_weights is None: + bin_label_weights = valid_mask + else: + bin_label_weights = label_weights.unsqueeze(1).expand(target_shape) + bin_label_weights *= valid_mask + + return bin_labels, bin_label_weights + + +def binary_cross_entropy(pred, + label, + weight=None, + reduction='mean', + avg_factor=None, + class_weight=None, + ignore_index=255): + """Calculate the binary CrossEntropy loss. + + Args: + pred (torch.Tensor): The prediction with shape (N, 1). + label (torch.Tensor): The learning label of the prediction. + weight (torch.Tensor, optional): Sample-wise loss weight. + reduction (str, optional): The method used to reduce the loss. + Options are "none", "mean" and "sum". + avg_factor (int, optional): Average factor that is used to average + the loss. Defaults to None. + class_weight (list[float], optional): The weight for each class. + ignore_index (int | None): The label index to be ignored. Default: 255 + + Returns: + torch.Tensor: The calculated loss + """ + if pred.dim() != label.dim(): + assert (pred.dim() == 2 and label.dim() == 1) or ( + pred.dim() == 4 and label.dim() == 3), \ + 'Only pred shape [N, C], label shape [N] or pred shape [N, C, ' \ + 'H, W], label shape [N, H, W] are supported' + label, weight = _expand_onehot_labels(label, weight, pred.shape, + ignore_index) + + # weighted element-wise losses + if weight is not None: + weight = weight.float() + loss = F.binary_cross_entropy_with_logits( + pred, label.float(), pos_weight=class_weight, reduction='none') + # do the reduction for the weighted loss + loss = weight_reduce_loss( + loss, weight, reduction=reduction, avg_factor=avg_factor) + + return loss + + +def mask_cross_entropy(pred, + target, + label, + reduction='mean', + avg_factor=None, + class_weight=None, + ignore_index=None): + """Calculate the CrossEntropy loss for masks. + + Args: + pred (torch.Tensor): The prediction with shape (N, C), C is the number + of classes. + target (torch.Tensor): The learning label of the prediction. + label (torch.Tensor): ``label`` indicates the class label of the mask' + corresponding object. This will be used to select the mask in the + of the class which the object belongs to when the mask prediction + if not class-agnostic. + reduction (str, optional): The method used to reduce the loss. + Options are "none", "mean" and "sum". + avg_factor (int, optional): Average factor that is used to average + the loss. Defaults to None. + class_weight (list[float], optional): The weight for each class. + ignore_index (None): Placeholder, to be consistent with other loss. + Default: None. + + Returns: + torch.Tensor: The calculated loss + """ + assert ignore_index is None, 'BCE loss does not support ignore_index' + # TODO: handle these two reserved arguments + assert reduction == 'mean' and avg_factor is None + num_rois = pred.size()[0] + inds = torch.arange(0, num_rois, dtype=torch.long, device=pred.device) + pred_slice = pred[inds, label].squeeze(1) + return F.binary_cross_entropy_with_logits( + pred_slice, target, weight=class_weight, reduction='mean')[None] + + +@LOSSES.register_module() +class CrossEntropyLoss(nn.Module): + """CrossEntropyLoss. + + Args: + use_sigmoid (bool, optional): Whether the prediction uses sigmoid + of softmax. Defaults to False. + use_mask (bool, optional): Whether to use mask cross entropy loss. + Defaults to False. + reduction (str, optional): . Defaults to 'mean'. + Options are "none", "mean" and "sum". + class_weight (list[float] | str, optional): Weight of each class. If in + str format, read them from a file. Defaults to None. + loss_weight (float, optional): Weight of the loss. Defaults to 1.0. + """ + + def __init__(self, + use_sigmoid=False, + use_mask=False, + reduction='mean', + class_weight=None, + loss_weight=1.0): + super(CrossEntropyLoss, self).__init__() + assert (use_sigmoid is False) or (use_mask is False) + self.use_sigmoid = use_sigmoid + self.use_mask = use_mask + self.reduction = reduction + self.loss_weight = loss_weight + self.class_weight = get_class_weight(class_weight) + + if self.use_sigmoid: + self.cls_criterion = binary_cross_entropy + elif self.use_mask: + self.cls_criterion = mask_cross_entropy + else: + self.cls_criterion = cross_entropy + + def forward(self, + cls_score, + label, + weight=None, + avg_factor=None, + reduction_override=None, + **kwargs): + """Forward function.""" + assert reduction_override in (None, 'none', 'mean', 'sum') + reduction = ( + reduction_override if reduction_override else self.reduction) + if self.class_weight is not None: + class_weight = cls_score.new_tensor(self.class_weight) + else: + class_weight = None + loss_cls = self.loss_weight * self.cls_criterion( + cls_score, + label, + weight, + class_weight=class_weight, + reduction=reduction, + avg_factor=avg_factor, + **kwargs) + return loss_cls diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/dice_loss.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/dice_loss.py new file mode 100644 index 0000000000000000000000000000000000000000..27a77b962d7d8b3079c7d6cd9db52280c6fb4970 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/dice_loss.py @@ -0,0 +1,119 @@ +"""Modified from https://github.com/LikeLy-Journey/SegmenTron/blob/master/ +segmentron/solver/loss.py (Apache-2.0 License)""" +import torch +import torch.nn as nn +import torch.nn.functional as F + +from ..builder import LOSSES +from .utils import get_class_weight, weighted_loss + + +@weighted_loss +def dice_loss(pred, + target, + valid_mask, + smooth=1, + exponent=2, + class_weight=None, + ignore_index=255): + assert pred.shape[0] == target.shape[0] + total_loss = 0 + num_classes = pred.shape[1] + for i in range(num_classes): + if i != ignore_index: + dice_loss = binary_dice_loss( + pred[:, i], + target[..., i], + valid_mask=valid_mask, + smooth=smooth, + exponent=exponent) + if class_weight is not None: + dice_loss *= class_weight[i] + total_loss += dice_loss + return total_loss / num_classes + + +@weighted_loss +def binary_dice_loss(pred, target, valid_mask, smooth=1, exponent=2, **kwards): + assert pred.shape[0] == target.shape[0] + pred = pred.reshape(pred.shape[0], -1) + target = target.reshape(target.shape[0], -1) + valid_mask = valid_mask.reshape(valid_mask.shape[0], -1) + + num = torch.sum(torch.mul(pred, target) * valid_mask, dim=1) * 2 + smooth + den = torch.sum(pred.pow(exponent) + target.pow(exponent), dim=1) + smooth + + return 1 - num / den + + +@LOSSES.register_module() +class DiceLoss(nn.Module): + """DiceLoss. + + This loss is proposed in `V-Net: Fully Convolutional Neural Networks for + Volumetric Medical Image Segmentation `_. + + Args: + loss_type (str, optional): Binary or multi-class loss. + Default: 'multi_class'. Options are "binary" and "multi_class". + smooth (float): A float number to smooth loss, and avoid NaN error. + Default: 1 + exponent (float): An float number to calculate denominator + value: \\sum{x^exponent} + \\sum{y^exponent}. Default: 2. + reduction (str, optional): The method used to reduce the loss. Options + are "none", "mean" and "sum". This parameter only works when + per_image is True. Default: 'mean'. + class_weight (list[float] | str, optional): Weight of each class. If in + str format, read them from a file. Defaults to None. + loss_weight (float, optional): Weight of the loss. Default to 1.0. + ignore_index (int | None): The label index to be ignored. Default: 255. + """ + + def __init__(self, + smooth=1, + exponent=2, + reduction='mean', + class_weight=None, + loss_weight=1.0, + ignore_index=255, + **kwards): + super(DiceLoss, self).__init__() + self.smooth = smooth + self.exponent = exponent + self.reduction = reduction + self.class_weight = get_class_weight(class_weight) + self.loss_weight = loss_weight + self.ignore_index = ignore_index + + def forward(self, + pred, + target, + avg_factor=None, + reduction_override=None, + **kwards): + assert reduction_override in (None, 'none', 'mean', 'sum') + reduction = ( + reduction_override if reduction_override else self.reduction) + if self.class_weight is not None: + class_weight = pred.new_tensor(self.class_weight) + else: + class_weight = None + + pred = F.softmax(pred, dim=1) + num_classes = pred.shape[1] + one_hot_target = F.one_hot( + torch.clamp(target.long(), 0, num_classes - 1), + num_classes=num_classes) + valid_mask = (target != self.ignore_index).long() + + loss = self.loss_weight * dice_loss( + pred, + one_hot_target, + valid_mask=valid_mask, + reduction=reduction, + avg_factor=avg_factor, + smooth=self.smooth, + exponent=self.exponent, + class_weight=class_weight, + ignore_index=self.ignore_index) + return loss diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/lovasz_loss.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/lovasz_loss.py new file mode 100644 index 0000000000000000000000000000000000000000..50f0f70fd432316b081a0114c28df61d320b5a47 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/lovasz_loss.py @@ -0,0 +1,303 @@ +"""Modified from https://github.com/bermanmaxim/LovaszSoftmax/blob/master/pytor +ch/lovasz_losses.py Lovasz-Softmax and Jaccard hinge loss in PyTorch Maxim +Berman 2018 ESAT-PSI KU Leuven (MIT License)""" + +import annotator.mmpkg.mmcv as mmcv +import torch +import torch.nn as nn +import torch.nn.functional as F + +from ..builder import LOSSES +from .utils import get_class_weight, weight_reduce_loss + + +def lovasz_grad(gt_sorted): + """Computes gradient of the Lovasz extension w.r.t sorted errors. + + See Alg. 1 in paper. + """ + p = len(gt_sorted) + gts = gt_sorted.sum() + intersection = gts - gt_sorted.float().cumsum(0) + union = gts + (1 - gt_sorted).float().cumsum(0) + jaccard = 1. - intersection / union + if p > 1: # cover 1-pixel case + jaccard[1:p] = jaccard[1:p] - jaccard[0:-1] + return jaccard + + +def flatten_binary_logits(logits, labels, ignore_index=None): + """Flattens predictions in the batch (binary case) Remove labels equal to + 'ignore_index'.""" + logits = logits.view(-1) + labels = labels.view(-1) + if ignore_index is None: + return logits, labels + valid = (labels != ignore_index) + vlogits = logits[valid] + vlabels = labels[valid] + return vlogits, vlabels + + +def flatten_probs(probs, labels, ignore_index=None): + """Flattens predictions in the batch.""" + if probs.dim() == 3: + # assumes output of a sigmoid layer + B, H, W = probs.size() + probs = probs.view(B, 1, H, W) + B, C, H, W = probs.size() + probs = probs.permute(0, 2, 3, 1).contiguous().view(-1, C) # B*H*W, C=P,C + labels = labels.view(-1) + if ignore_index is None: + return probs, labels + valid = (labels != ignore_index) + vprobs = probs[valid.nonzero().squeeze()] + vlabels = labels[valid] + return vprobs, vlabels + + +def lovasz_hinge_flat(logits, labels): + """Binary Lovasz hinge loss. + + Args: + logits (torch.Tensor): [P], logits at each prediction + (between -infty and +infty). + labels (torch.Tensor): [P], binary ground truth labels (0 or 1). + + Returns: + torch.Tensor: The calculated loss. + """ + if len(labels) == 0: + # only void pixels, the gradients should be 0 + return logits.sum() * 0. + signs = 2. * labels.float() - 1. + errors = (1. - logits * signs) + errors_sorted, perm = torch.sort(errors, dim=0, descending=True) + perm = perm.data + gt_sorted = labels[perm] + grad = lovasz_grad(gt_sorted) + loss = torch.dot(F.relu(errors_sorted), grad) + return loss + + +def lovasz_hinge(logits, + labels, + classes='present', + per_image=False, + class_weight=None, + reduction='mean', + avg_factor=None, + ignore_index=255): + """Binary Lovasz hinge loss. + + Args: + logits (torch.Tensor): [B, H, W], logits at each pixel + (between -infty and +infty). + labels (torch.Tensor): [B, H, W], binary ground truth masks (0 or 1). + classes (str | list[int], optional): Placeholder, to be consistent with + other loss. Default: None. + per_image (bool, optional): If per_image is True, compute the loss per + image instead of per batch. Default: False. + class_weight (list[float], optional): Placeholder, to be consistent + with other loss. Default: None. + reduction (str, optional): The method used to reduce the loss. Options + are "none", "mean" and "sum". This parameter only works when + per_image is True. Default: 'mean'. + avg_factor (int, optional): Average factor that is used to average + the loss. This parameter only works when per_image is True. + Default: None. + ignore_index (int | None): The label index to be ignored. Default: 255. + + Returns: + torch.Tensor: The calculated loss. + """ + if per_image: + loss = [ + lovasz_hinge_flat(*flatten_binary_logits( + logit.unsqueeze(0), label.unsqueeze(0), ignore_index)) + for logit, label in zip(logits, labels) + ] + loss = weight_reduce_loss( + torch.stack(loss), None, reduction, avg_factor) + else: + loss = lovasz_hinge_flat( + *flatten_binary_logits(logits, labels, ignore_index)) + return loss + + +def lovasz_softmax_flat(probs, labels, classes='present', class_weight=None): + """Multi-class Lovasz-Softmax loss. + + Args: + probs (torch.Tensor): [P, C], class probabilities at each prediction + (between 0 and 1). + labels (torch.Tensor): [P], ground truth labels (between 0 and C - 1). + classes (str | list[int], optional): Classes chosen to calculate loss. + 'all' for all classes, 'present' for classes present in labels, or + a list of classes to average. Default: 'present'. + class_weight (list[float], optional): The weight for each class. + Default: None. + + Returns: + torch.Tensor: The calculated loss. + """ + if probs.numel() == 0: + # only void pixels, the gradients should be 0 + return probs * 0. + C = probs.size(1) + losses = [] + class_to_sum = list(range(C)) if classes in ['all', 'present'] else classes + for c in class_to_sum: + fg = (labels == c).float() # foreground for class c + if (classes == 'present' and fg.sum() == 0): + continue + if C == 1: + if len(classes) > 1: + raise ValueError('Sigmoid output possible only with 1 class') + class_pred = probs[:, 0] + else: + class_pred = probs[:, c] + errors = (fg - class_pred).abs() + errors_sorted, perm = torch.sort(errors, 0, descending=True) + perm = perm.data + fg_sorted = fg[perm] + loss = torch.dot(errors_sorted, lovasz_grad(fg_sorted)) + if class_weight is not None: + loss *= class_weight[c] + losses.append(loss) + return torch.stack(losses).mean() + + +def lovasz_softmax(probs, + labels, + classes='present', + per_image=False, + class_weight=None, + reduction='mean', + avg_factor=None, + ignore_index=255): + """Multi-class Lovasz-Softmax loss. + + Args: + probs (torch.Tensor): [B, C, H, W], class probabilities at each + prediction (between 0 and 1). + labels (torch.Tensor): [B, H, W], ground truth labels (between 0 and + C - 1). + classes (str | list[int], optional): Classes chosen to calculate loss. + 'all' for all classes, 'present' for classes present in labels, or + a list of classes to average. Default: 'present'. + per_image (bool, optional): If per_image is True, compute the loss per + image instead of per batch. Default: False. + class_weight (list[float], optional): The weight for each class. + Default: None. + reduction (str, optional): The method used to reduce the loss. Options + are "none", "mean" and "sum". This parameter only works when + per_image is True. Default: 'mean'. + avg_factor (int, optional): Average factor that is used to average + the loss. This parameter only works when per_image is True. + Default: None. + ignore_index (int | None): The label index to be ignored. Default: 255. + + Returns: + torch.Tensor: The calculated loss. + """ + + if per_image: + loss = [ + lovasz_softmax_flat( + *flatten_probs( + prob.unsqueeze(0), label.unsqueeze(0), ignore_index), + classes=classes, + class_weight=class_weight) + for prob, label in zip(probs, labels) + ] + loss = weight_reduce_loss( + torch.stack(loss), None, reduction, avg_factor) + else: + loss = lovasz_softmax_flat( + *flatten_probs(probs, labels, ignore_index), + classes=classes, + class_weight=class_weight) + return loss + + +@LOSSES.register_module() +class LovaszLoss(nn.Module): + """LovaszLoss. + + This loss is proposed in `The Lovasz-Softmax loss: A tractable surrogate + for the optimization of the intersection-over-union measure in neural + networks `_. + + Args: + loss_type (str, optional): Binary or multi-class loss. + Default: 'multi_class'. Options are "binary" and "multi_class". + classes (str | list[int], optional): Classes chosen to calculate loss. + 'all' for all classes, 'present' for classes present in labels, or + a list of classes to average. Default: 'present'. + per_image (bool, optional): If per_image is True, compute the loss per + image instead of per batch. Default: False. + reduction (str, optional): The method used to reduce the loss. Options + are "none", "mean" and "sum". This parameter only works when + per_image is True. Default: 'mean'. + class_weight (list[float] | str, optional): Weight of each class. If in + str format, read them from a file. Defaults to None. + loss_weight (float, optional): Weight of the loss. Defaults to 1.0. + """ + + def __init__(self, + loss_type='multi_class', + classes='present', + per_image=False, + reduction='mean', + class_weight=None, + loss_weight=1.0): + super(LovaszLoss, self).__init__() + assert loss_type in ('binary', 'multi_class'), "loss_type should be \ + 'binary' or 'multi_class'." + + if loss_type == 'binary': + self.cls_criterion = lovasz_hinge + else: + self.cls_criterion = lovasz_softmax + assert classes in ('all', 'present') or mmcv.is_list_of(classes, int) + if not per_image: + assert reduction == 'none', "reduction should be 'none' when \ + per_image is False." + + self.classes = classes + self.per_image = per_image + self.reduction = reduction + self.loss_weight = loss_weight + self.class_weight = get_class_weight(class_weight) + + def forward(self, + cls_score, + label, + weight=None, + avg_factor=None, + reduction_override=None, + **kwargs): + """Forward function.""" + assert reduction_override in (None, 'none', 'mean', 'sum') + reduction = ( + reduction_override if reduction_override else self.reduction) + if self.class_weight is not None: + class_weight = cls_score.new_tensor(self.class_weight) + else: + class_weight = None + + # if multi-class loss, transform logits to probs + if self.cls_criterion == lovasz_softmax: + cls_score = F.softmax(cls_score, dim=1) + + loss_cls = self.loss_weight * self.cls_criterion( + cls_score, + label, + self.classes, + self.per_image, + class_weight=class_weight, + reduction=reduction, + avg_factor=avg_factor, + **kwargs) + return loss_cls diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/utils.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..2afb477a153ba9dead71066fa66ee024482afd82 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/losses/utils.py @@ -0,0 +1,121 @@ +import functools + +import annotator.mmpkg.mmcv as mmcv +import numpy as np +import torch.nn.functional as F + + +def get_class_weight(class_weight): + """Get class weight for loss function. + + Args: + class_weight (list[float] | str | None): If class_weight is a str, + take it as a file name and read from it. + """ + if isinstance(class_weight, str): + # take it as a file path + if class_weight.endswith('.npy'): + class_weight = np.load(class_weight) + else: + # pkl, json or yaml + class_weight = mmcv.load(class_weight) + + return class_weight + + +def reduce_loss(loss, reduction): + """Reduce loss as specified. + + Args: + loss (Tensor): Elementwise loss tensor. + reduction (str): Options are "none", "mean" and "sum". + + Return: + Tensor: Reduced loss tensor. + """ + reduction_enum = F._Reduction.get_enum(reduction) + # none: 0, elementwise_mean:1, sum: 2 + if reduction_enum == 0: + return loss + elif reduction_enum == 1: + return loss.mean() + elif reduction_enum == 2: + return loss.sum() + + +def weight_reduce_loss(loss, weight=None, reduction='mean', avg_factor=None): + """Apply element-wise weight and reduce loss. + + Args: + loss (Tensor): Element-wise loss. + weight (Tensor): Element-wise weights. + reduction (str): Same as built-in losses of PyTorch. + avg_factor (float): Avarage factor when computing the mean of losses. + + Returns: + Tensor: Processed loss values. + """ + # if weight is specified, apply element-wise weight + if weight is not None: + assert weight.dim() == loss.dim() + if weight.dim() > 1: + assert weight.size(1) == 1 or weight.size(1) == loss.size(1) + loss = loss * weight + + # if avg_factor is not specified, just reduce the loss + if avg_factor is None: + loss = reduce_loss(loss, reduction) + else: + # if reduction is mean, then average the loss by avg_factor + if reduction == 'mean': + loss = loss.sum() / avg_factor + # if reduction is 'none', then do nothing, otherwise raise an error + elif reduction != 'none': + raise ValueError('avg_factor can not be used with reduction="sum"') + return loss + + +def weighted_loss(loss_func): + """Create a weighted version of a given loss function. + + To use this decorator, the loss function must have the signature like + `loss_func(pred, target, **kwargs)`. The function only needs to compute + element-wise loss without any reduction. This decorator will add weight + and reduction arguments to the function. The decorated function will have + the signature like `loss_func(pred, target, weight=None, reduction='mean', + avg_factor=None, **kwargs)`. + + :Example: + + >>> import torch + >>> @weighted_loss + >>> def l1_loss(pred, target): + >>> return (pred - target).abs() + + >>> pred = torch.Tensor([0, 2, 3]) + >>> target = torch.Tensor([1, 1, 1]) + >>> weight = torch.Tensor([1, 0, 1]) + + >>> l1_loss(pred, target) + tensor(1.3333) + >>> l1_loss(pred, target, weight) + tensor(1.) + >>> l1_loss(pred, target, reduction='none') + tensor([1., 1., 2.]) + >>> l1_loss(pred, target, weight, avg_factor=2) + tensor(1.5000) + """ + + @functools.wraps(loss_func) + def wrapper(pred, + target, + weight=None, + reduction='mean', + avg_factor=None, + **kwargs): + # get element-wise loss + loss = loss_func(pred, target, **kwargs) + loss = weight_reduce_loss(loss, weight, reduction, avg_factor) + return loss + + return wrapper diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/necks/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/necks/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9b9d3d5b3fe80247642d962edd6fb787537d01d6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/necks/__init__.py @@ -0,0 +1,4 @@ +from .fpn import FPN +from .multilevel_neck import MultiLevelNeck + +__all__ = ['FPN', 'MultiLevelNeck'] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/necks/fpn.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/necks/fpn.py new file mode 100644 index 0000000000000000000000000000000000000000..ba47bbe1a0225587315627ac288e5ddf6497a244 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/necks/fpn.py @@ -0,0 +1,212 @@ +import torch.nn as nn +import torch.nn.functional as F +from annotator.mmpkg.mmcv.cnn import ConvModule, xavier_init + +from ..builder import NECKS + + +@NECKS.register_module() +class FPN(nn.Module): + """Feature Pyramid Network. + + This is an implementation of - Feature Pyramid Networks for Object + Detection (https://arxiv.org/abs/1612.03144) + + Args: + in_channels (List[int]): Number of input channels per scale. + out_channels (int): Number of output channels (used at each scale) + num_outs (int): Number of output scales. + start_level (int): Index of the start input backbone level used to + build the feature pyramid. Default: 0. + end_level (int): Index of the end input backbone level (exclusive) to + build the feature pyramid. Default: -1, which means the last level. + add_extra_convs (bool | str): If bool, it decides whether to add conv + layers on top of the original feature maps. Default to False. + If True, its actual mode is specified by `extra_convs_on_inputs`. + If str, it specifies the source feature map of the extra convs. + Only the following options are allowed + + - 'on_input': Last feat map of neck inputs (i.e. backbone feature). + - 'on_lateral': Last feature map after lateral convs. + - 'on_output': The last output feature map after fpn convs. + extra_convs_on_inputs (bool, deprecated): Whether to apply extra convs + on the original feature from the backbone. If True, + it is equivalent to `add_extra_convs='on_input'`. If False, it is + equivalent to set `add_extra_convs='on_output'`. Default to True. + relu_before_extra_convs (bool): Whether to apply relu before the extra + conv. Default: False. + no_norm_on_lateral (bool): Whether to apply norm on lateral. + Default: False. + conv_cfg (dict): Config dict for convolution layer. Default: None. + norm_cfg (dict): Config dict for normalization layer. Default: None. + act_cfg (str): Config dict for activation layer in ConvModule. + Default: None. + upsample_cfg (dict): Config dict for interpolate layer. + Default: `dict(mode='nearest')` + + Example: + >>> import torch + >>> in_channels = [2, 3, 5, 7] + >>> scales = [340, 170, 84, 43] + >>> inputs = [torch.rand(1, c, s, s) + ... for c, s in zip(in_channels, scales)] + >>> self = FPN(in_channels, 11, len(in_channels)).eval() + >>> outputs = self.forward(inputs) + >>> for i in range(len(outputs)): + ... print(f'outputs[{i}].shape = {outputs[i].shape}') + outputs[0].shape = torch.Size([1, 11, 340, 340]) + outputs[1].shape = torch.Size([1, 11, 170, 170]) + outputs[2].shape = torch.Size([1, 11, 84, 84]) + outputs[3].shape = torch.Size([1, 11, 43, 43]) + """ + + def __init__(self, + in_channels, + out_channels, + num_outs, + start_level=0, + end_level=-1, + add_extra_convs=False, + extra_convs_on_inputs=False, + relu_before_extra_convs=False, + no_norm_on_lateral=False, + conv_cfg=None, + norm_cfg=None, + act_cfg=None, + upsample_cfg=dict(mode='nearest')): + super(FPN, self).__init__() + assert isinstance(in_channels, list) + self.in_channels = in_channels + self.out_channels = out_channels + self.num_ins = len(in_channels) + self.num_outs = num_outs + self.relu_before_extra_convs = relu_before_extra_convs + self.no_norm_on_lateral = no_norm_on_lateral + self.fp16_enabled = False + self.upsample_cfg = upsample_cfg.copy() + + if end_level == -1: + self.backbone_end_level = self.num_ins + assert num_outs >= self.num_ins - start_level + else: + # if end_level < inputs, no extra level is allowed + self.backbone_end_level = end_level + assert end_level <= len(in_channels) + assert num_outs == end_level - start_level + self.start_level = start_level + self.end_level = end_level + self.add_extra_convs = add_extra_convs + assert isinstance(add_extra_convs, (str, bool)) + if isinstance(add_extra_convs, str): + # Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output' + assert add_extra_convs in ('on_input', 'on_lateral', 'on_output') + elif add_extra_convs: # True + if extra_convs_on_inputs: + # For compatibility with previous release + # TODO: deprecate `extra_convs_on_inputs` + self.add_extra_convs = 'on_input' + else: + self.add_extra_convs = 'on_output' + + self.lateral_convs = nn.ModuleList() + self.fpn_convs = nn.ModuleList() + + for i in range(self.start_level, self.backbone_end_level): + l_conv = ConvModule( + in_channels[i], + out_channels, + 1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg if not self.no_norm_on_lateral else None, + act_cfg=act_cfg, + inplace=False) + fpn_conv = ConvModule( + out_channels, + out_channels, + 3, + padding=1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg, + inplace=False) + + self.lateral_convs.append(l_conv) + self.fpn_convs.append(fpn_conv) + + # add extra conv layers (e.g., RetinaNet) + extra_levels = num_outs - self.backbone_end_level + self.start_level + if self.add_extra_convs and extra_levels >= 1: + for i in range(extra_levels): + if i == 0 and self.add_extra_convs == 'on_input': + in_channels = self.in_channels[self.backbone_end_level - 1] + else: + in_channels = out_channels + extra_fpn_conv = ConvModule( + in_channels, + out_channels, + 3, + stride=2, + padding=1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg, + inplace=False) + self.fpn_convs.append(extra_fpn_conv) + + # default init_weights for conv(msra) and norm in ConvModule + def init_weights(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + xavier_init(m, distribution='uniform') + + def forward(self, inputs): + assert len(inputs) == len(self.in_channels) + + # build laterals + laterals = [ + lateral_conv(inputs[i + self.start_level]) + for i, lateral_conv in enumerate(self.lateral_convs) + ] + + # build top-down path + used_backbone_levels = len(laterals) + for i in range(used_backbone_levels - 1, 0, -1): + # In some cases, fixing `scale factor` (e.g. 2) is preferred, but + # it cannot co-exist with `size` in `F.interpolate`. + if 'scale_factor' in self.upsample_cfg: + laterals[i - 1] += F.interpolate(laterals[i], + **self.upsample_cfg) + else: + prev_shape = laterals[i - 1].shape[2:] + laterals[i - 1] += F.interpolate( + laterals[i], size=prev_shape, **self.upsample_cfg) + + # build outputs + # part 1: from original levels + outs = [ + self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels) + ] + # part 2: add extra levels + if self.num_outs > len(outs): + # use max pool to get more levels on top of outputs + # (e.g., Faster R-CNN, Mask R-CNN) + if not self.add_extra_convs: + for i in range(self.num_outs - used_backbone_levels): + outs.append(F.max_pool2d(outs[-1], 1, stride=2)) + # add conv layers on top of original feature maps (RetinaNet) + else: + if self.add_extra_convs == 'on_input': + extra_source = inputs[self.backbone_end_level - 1] + elif self.add_extra_convs == 'on_lateral': + extra_source = laterals[-1] + elif self.add_extra_convs == 'on_output': + extra_source = outs[-1] + else: + raise NotImplementedError + outs.append(self.fpn_convs[used_backbone_levels](extra_source)) + for i in range(used_backbone_levels + 1, self.num_outs): + if self.relu_before_extra_convs: + outs.append(self.fpn_convs[i](F.relu(outs[-1]))) + else: + outs.append(self.fpn_convs[i](outs[-1])) + return tuple(outs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/necks/multilevel_neck.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/necks/multilevel_neck.py new file mode 100644 index 0000000000000000000000000000000000000000..0b86c073cd1a72354d2426846125e80f7ab20dbc --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/necks/multilevel_neck.py @@ -0,0 +1,70 @@ +import torch.nn as nn +import torch.nn.functional as F +from annotator.mmpkg.mmcv.cnn import ConvModule + +from ..builder import NECKS + + +@NECKS.register_module() +class MultiLevelNeck(nn.Module): + """MultiLevelNeck. + + A neck structure connect vit backbone and decoder_heads. + Args: + in_channels (List[int]): Number of input channels per scale. + out_channels (int): Number of output channels (used at each scale). + scales (List[int]): Scale factors for each input feature map. + norm_cfg (dict): Config dict for normalization layer. Default: None. + act_cfg (dict): Config dict for activation layer in ConvModule. + Default: None. + """ + + def __init__(self, + in_channels, + out_channels, + scales=[0.5, 1, 2, 4], + norm_cfg=None, + act_cfg=None): + super(MultiLevelNeck, self).__init__() + assert isinstance(in_channels, list) + self.in_channels = in_channels + self.out_channels = out_channels + self.scales = scales + self.num_outs = len(scales) + self.lateral_convs = nn.ModuleList() + self.convs = nn.ModuleList() + for in_channel in in_channels: + self.lateral_convs.append( + ConvModule( + in_channel, + out_channels, + kernel_size=1, + norm_cfg=norm_cfg, + act_cfg=act_cfg)) + for _ in range(self.num_outs): + self.convs.append( + ConvModule( + out_channels, + out_channels, + kernel_size=3, + padding=1, + stride=1, + norm_cfg=norm_cfg, + act_cfg=act_cfg)) + + def forward(self, inputs): + assert len(inputs) == len(self.in_channels) + print(inputs[0].shape) + inputs = [ + lateral_conv(inputs[i]) + for i, lateral_conv in enumerate(self.lateral_convs) + ] + # for len(inputs) not equal to self.num_outs + if len(inputs) == 1: + inputs = [inputs[0] for _ in range(self.num_outs)] + outs = [] + for i in range(self.num_outs): + x_resize = F.interpolate( + inputs[i], scale_factor=self.scales[i], mode='bilinear') + outs.append(self.convs[i](x_resize)) + return tuple(outs) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/segmentors/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/segmentors/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..dca2f09405330743c476e190896bee39c45498ea --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/segmentors/__init__.py @@ -0,0 +1,5 @@ +from .base import BaseSegmentor +from .cascade_encoder_decoder import CascadeEncoderDecoder +from .encoder_decoder import EncoderDecoder + +__all__ = ['BaseSegmentor', 'EncoderDecoder', 'CascadeEncoderDecoder'] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/segmentors/base.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/segmentors/base.py new file mode 100644 index 0000000000000000000000000000000000000000..a12d8beb8ea40bfa234197eddb4d3ef40dbfeb6f --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/segmentors/base.py @@ -0,0 +1,273 @@ +import logging +import warnings +from abc import ABCMeta, abstractmethod +from collections import OrderedDict + +import annotator.mmpkg.mmcv as mmcv +import numpy as np +import torch +import torch.distributed as dist +import torch.nn as nn +from annotator.mmpkg.mmcv.runner import auto_fp16 + + +class BaseSegmentor(nn.Module): + """Base class for segmentors.""" + + __metaclass__ = ABCMeta + + def __init__(self): + super(BaseSegmentor, self).__init__() + self.fp16_enabled = False + + @property + def with_neck(self): + """bool: whether the segmentor has neck""" + return hasattr(self, 'neck') and self.neck is not None + + @property + def with_auxiliary_head(self): + """bool: whether the segmentor has auxiliary head""" + return hasattr(self, + 'auxiliary_head') and self.auxiliary_head is not None + + @property + def with_decode_head(self): + """bool: whether the segmentor has decode head""" + return hasattr(self, 'decode_head') and self.decode_head is not None + + @abstractmethod + def extract_feat(self, imgs): + """Placeholder for extract features from images.""" + pass + + @abstractmethod + def encode_decode(self, img, img_metas): + """Placeholder for encode images with backbone and decode into a + semantic segmentation map of the same size as input.""" + pass + + @abstractmethod + def forward_train(self, imgs, img_metas, **kwargs): + """Placeholder for Forward function for training.""" + pass + + @abstractmethod + def simple_test(self, img, img_meta, **kwargs): + """Placeholder for single image test.""" + pass + + @abstractmethod + def aug_test(self, imgs, img_metas, **kwargs): + """Placeholder for augmentation test.""" + pass + + def init_weights(self, pretrained=None): + """Initialize the weights in segmentor. + + Args: + pretrained (str, optional): Path to pre-trained weights. + Defaults to None. + """ + if pretrained is not None: + logger = logging.getLogger() + logger.info(f'load model from: {pretrained}') + + def forward_test(self, imgs, img_metas, **kwargs): + """ + Args: + imgs (List[Tensor]): the outer list indicates test-time + augmentations and inner Tensor should have a shape NxCxHxW, + which contains all images in the batch. + img_metas (List[List[dict]]): the outer list indicates test-time + augs (multiscale, flip, etc.) and the inner list indicates + images in a batch. + """ + for var, name in [(imgs, 'imgs'), (img_metas, 'img_metas')]: + if not isinstance(var, list): + raise TypeError(f'{name} must be a list, but got ' + f'{type(var)}') + + num_augs = len(imgs) + if num_augs != len(img_metas): + raise ValueError(f'num of augmentations ({len(imgs)}) != ' + f'num of image meta ({len(img_metas)})') + # all images in the same aug batch all of the same ori_shape and pad + # shape + for img_meta in img_metas: + ori_shapes = [_['ori_shape'] for _ in img_meta] + assert all(shape == ori_shapes[0] for shape in ori_shapes) + img_shapes = [_['img_shape'] for _ in img_meta] + assert all(shape == img_shapes[0] for shape in img_shapes) + pad_shapes = [_['pad_shape'] for _ in img_meta] + assert all(shape == pad_shapes[0] for shape in pad_shapes) + + if num_augs == 1: + return self.simple_test(imgs[0], img_metas[0], **kwargs) + else: + return self.aug_test(imgs, img_metas, **kwargs) + + @auto_fp16(apply_to=('img', )) + def forward(self, img, img_metas, return_loss=True, **kwargs): + """Calls either :func:`forward_train` or :func:`forward_test` depending + on whether ``return_loss`` is ``True``. + + Note this setting will change the expected inputs. When + ``return_loss=True``, img and img_meta are single-nested (i.e. Tensor + and List[dict]), and when ``resturn_loss=False``, img and img_meta + should be double nested (i.e. List[Tensor], List[List[dict]]), with + the outer list indicating test time augmentations. + """ + if return_loss: + return self.forward_train(img, img_metas, **kwargs) + else: + return self.forward_test(img, img_metas, **kwargs) + + def train_step(self, data_batch, optimizer, **kwargs): + """The iteration step during training. + + This method defines an iteration step during training, except for the + back propagation and optimizer updating, which are done in an optimizer + hook. Note that in some complicated cases or models, the whole process + including back propagation and optimizer updating is also defined in + this method, such as GAN. + + Args: + data (dict): The output of dataloader. + optimizer (:obj:`torch.optim.Optimizer` | dict): The optimizer of + runner is passed to ``train_step()``. This argument is unused + and reserved. + + Returns: + dict: It should contain at least 3 keys: ``loss``, ``log_vars``, + ``num_samples``. + ``loss`` is a tensor for back propagation, which can be a + weighted sum of multiple losses. + ``log_vars`` contains all the variables to be sent to the + logger. + ``num_samples`` indicates the batch size (when the model is + DDP, it means the batch size on each GPU), which is used for + averaging the logs. + """ + losses = self(**data_batch) + loss, log_vars = self._parse_losses(losses) + + outputs = dict( + loss=loss, + log_vars=log_vars, + num_samples=len(data_batch['img_metas'])) + + return outputs + + def val_step(self, data_batch, **kwargs): + """The iteration step during validation. + + This method shares the same signature as :func:`train_step`, but used + during val epochs. Note that the evaluation after training epochs is + not implemented with this method, but an evaluation hook. + """ + output = self(**data_batch, **kwargs) + return output + + @staticmethod + def _parse_losses(losses): + """Parse the raw outputs (losses) of the network. + + Args: + losses (dict): Raw output of the network, which usually contain + losses and other necessary information. + + Returns: + tuple[Tensor, dict]: (loss, log_vars), loss is the loss tensor + which may be a weighted sum of all losses, log_vars contains + all the variables to be sent to the logger. + """ + log_vars = OrderedDict() + for loss_name, loss_value in losses.items(): + if isinstance(loss_value, torch.Tensor): + log_vars[loss_name] = loss_value.mean() + elif isinstance(loss_value, list): + log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) + else: + raise TypeError( + f'{loss_name} is not a tensor or list of tensors') + + loss = sum(_value for _key, _value in log_vars.items() + if 'loss' in _key) + + log_vars['loss'] = loss + for loss_name, loss_value in log_vars.items(): + # reduce loss when distributed training + if dist.is_available() and dist.is_initialized(): + loss_value = loss_value.data.clone() + dist.all_reduce(loss_value.div_(dist.get_world_size())) + log_vars[loss_name] = loss_value.item() + + return loss, log_vars + + def show_result(self, + img, + result, + palette=None, + win_name='', + show=False, + wait_time=0, + out_file=None, + opacity=0.5): + """Draw `result` over `img`. + + Args: + img (str or Tensor): The image to be displayed. + result (Tensor): The semantic segmentation results to draw over + `img`. + palette (list[list[int]]] | np.ndarray | None): The palette of + segmentation map. If None is given, random palette will be + generated. Default: None + win_name (str): The window name. + wait_time (int): Value of waitKey param. + Default: 0. + show (bool): Whether to show the image. + Default: False. + out_file (str or None): The filename to write the image. + Default: None. + opacity(float): Opacity of painted segmentation map. + Default 0.5. + Must be in (0, 1] range. + Returns: + img (Tensor): Only if not `show` or `out_file` + """ + img = mmcv.imread(img) + img = img.copy() + seg = result[0] + if palette is None: + if self.PALETTE is None: + palette = np.random.randint( + 0, 255, size=(len(self.CLASSES), 3)) + else: + palette = self.PALETTE + palette = np.array(palette) + assert palette.shape[0] == len(self.CLASSES) + assert palette.shape[1] == 3 + assert len(palette.shape) == 2 + assert 0 < opacity <= 1.0 + color_seg = np.zeros((seg.shape[0], seg.shape[1], 3), dtype=np.uint8) + for label, color in enumerate(palette): + color_seg[seg == label, :] = color + # convert to BGR + color_seg = color_seg[..., ::-1] + + img = img * (1 - opacity) + color_seg * opacity + img = img.astype(np.uint8) + # if out_file specified, do not show image in window + if out_file is not None: + show = False + + if show: + mmcv.imshow(img, win_name, wait_time) + if out_file is not None: + mmcv.imwrite(img, out_file) + + if not (show or out_file): + warnings.warn('show==False and out_file is not specified, only ' + 'result image will be returned') + return img diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/segmentors/cascade_encoder_decoder.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/segmentors/cascade_encoder_decoder.py new file mode 100644 index 0000000000000000000000000000000000000000..74547f0fb01da9fe32c1d142768eb788b7e8673c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/segmentors/cascade_encoder_decoder.py @@ -0,0 +1,98 @@ +from torch import nn + +from annotator.mmpkg.mmseg.core import add_prefix +from annotator.mmpkg.mmseg.ops import resize +from .. import builder +from ..builder import SEGMENTORS +from .encoder_decoder import EncoderDecoder + + +@SEGMENTORS.register_module() +class CascadeEncoderDecoder(EncoderDecoder): + """Cascade Encoder Decoder segmentors. + + CascadeEncoderDecoder almost the same as EncoderDecoder, while decoders of + CascadeEncoderDecoder are cascaded. The output of previous decoder_head + will be the input of next decoder_head. + """ + + def __init__(self, + num_stages, + backbone, + decode_head, + neck=None, + auxiliary_head=None, + train_cfg=None, + test_cfg=None, + pretrained=None): + self.num_stages = num_stages + super(CascadeEncoderDecoder, self).__init__( + backbone=backbone, + decode_head=decode_head, + neck=neck, + auxiliary_head=auxiliary_head, + train_cfg=train_cfg, + test_cfg=test_cfg, + pretrained=pretrained) + + def _init_decode_head(self, decode_head): + """Initialize ``decode_head``""" + assert isinstance(decode_head, list) + assert len(decode_head) == self.num_stages + self.decode_head = nn.ModuleList() + for i in range(self.num_stages): + self.decode_head.append(builder.build_head(decode_head[i])) + self.align_corners = self.decode_head[-1].align_corners + self.num_classes = self.decode_head[-1].num_classes + + def init_weights(self, pretrained=None): + """Initialize the weights in backbone and heads. + + Args: + pretrained (str, optional): Path to pre-trained weights. + Defaults to None. + """ + self.backbone.init_weights(pretrained=pretrained) + for i in range(self.num_stages): + self.decode_head[i].init_weights() + if self.with_auxiliary_head: + if isinstance(self.auxiliary_head, nn.ModuleList): + for aux_head in self.auxiliary_head: + aux_head.init_weights() + else: + self.auxiliary_head.init_weights() + + def encode_decode(self, img, img_metas): + """Encode images with backbone and decode into a semantic segmentation + map of the same size as input.""" + x = self.extract_feat(img) + out = self.decode_head[0].forward_test(x, img_metas, self.test_cfg) + for i in range(1, self.num_stages): + out = self.decode_head[i].forward_test(x, out, img_metas, + self.test_cfg) + out = resize( + input=out, + size=img.shape[2:], + mode='bilinear', + align_corners=self.align_corners) + return out + + def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): + """Run forward function and calculate loss for decode head in + training.""" + losses = dict() + + loss_decode = self.decode_head[0].forward_train( + x, img_metas, gt_semantic_seg, self.train_cfg) + + losses.update(add_prefix(loss_decode, 'decode_0')) + + for i in range(1, self.num_stages): + # forward test again, maybe unnecessary for most methods. + prev_outputs = self.decode_head[i - 1].forward_test( + x, img_metas, self.test_cfg) + loss_decode = self.decode_head[i].forward_train( + x, prev_outputs, img_metas, gt_semantic_seg, self.train_cfg) + losses.update(add_prefix(loss_decode, f'decode_{i}')) + + return losses diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/segmentors/encoder_decoder.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/segmentors/encoder_decoder.py new file mode 100644 index 0000000000000000000000000000000000000000..30c25f35a15e65e45f9221a3f19ace8579f73301 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/segmentors/encoder_decoder.py @@ -0,0 +1,298 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +from annotator.mmpkg.mmseg.core import add_prefix +from annotator.mmpkg.mmseg.ops import resize +from .. import builder +from ..builder import SEGMENTORS +from .base import BaseSegmentor + + +@SEGMENTORS.register_module() +class EncoderDecoder(BaseSegmentor): + """Encoder Decoder segmentors. + + EncoderDecoder typically consists of backbone, decode_head, auxiliary_head. + Note that auxiliary_head is only used for deep supervision during training, + which could be dumped during inference. + """ + + def __init__(self, + backbone, + decode_head, + neck=None, + auxiliary_head=None, + train_cfg=None, + test_cfg=None, + pretrained=None): + super(EncoderDecoder, self).__init__() + self.backbone = builder.build_backbone(backbone) + if neck is not None: + self.neck = builder.build_neck(neck) + self._init_decode_head(decode_head) + self._init_auxiliary_head(auxiliary_head) + + self.train_cfg = train_cfg + self.test_cfg = test_cfg + + self.init_weights(pretrained=pretrained) + + assert self.with_decode_head + + def _init_decode_head(self, decode_head): + """Initialize ``decode_head``""" + self.decode_head = builder.build_head(decode_head) + self.align_corners = self.decode_head.align_corners + self.num_classes = self.decode_head.num_classes + + def _init_auxiliary_head(self, auxiliary_head): + """Initialize ``auxiliary_head``""" + if auxiliary_head is not None: + if isinstance(auxiliary_head, list): + self.auxiliary_head = nn.ModuleList() + for head_cfg in auxiliary_head: + self.auxiliary_head.append(builder.build_head(head_cfg)) + else: + self.auxiliary_head = builder.build_head(auxiliary_head) + + def init_weights(self, pretrained=None): + """Initialize the weights in backbone and heads. + + Args: + pretrained (str, optional): Path to pre-trained weights. + Defaults to None. + """ + + super(EncoderDecoder, self).init_weights(pretrained) + self.backbone.init_weights(pretrained=pretrained) + self.decode_head.init_weights() + if self.with_auxiliary_head: + if isinstance(self.auxiliary_head, nn.ModuleList): + for aux_head in self.auxiliary_head: + aux_head.init_weights() + else: + self.auxiliary_head.init_weights() + + def extract_feat(self, img): + """Extract features from images.""" + x = self.backbone(img) + if self.with_neck: + x = self.neck(x) + return x + + def encode_decode(self, img, img_metas): + """Encode images with backbone and decode into a semantic segmentation + map of the same size as input.""" + x = self.extract_feat(img) + out = self._decode_head_forward_test(x, img_metas) + out = resize( + input=out, + size=img.shape[2:], + mode='bilinear', + align_corners=self.align_corners) + return out + + def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): + """Run forward function and calculate loss for decode head in + training.""" + losses = dict() + loss_decode = self.decode_head.forward_train(x, img_metas, + gt_semantic_seg, + self.train_cfg) + + losses.update(add_prefix(loss_decode, 'decode')) + return losses + + def _decode_head_forward_test(self, x, img_metas): + """Run forward function and calculate loss for decode head in + inference.""" + seg_logits = self.decode_head.forward_test(x, img_metas, self.test_cfg) + return seg_logits + + def _auxiliary_head_forward_train(self, x, img_metas, gt_semantic_seg): + """Run forward function and calculate loss for auxiliary head in + training.""" + losses = dict() + if isinstance(self.auxiliary_head, nn.ModuleList): + for idx, aux_head in enumerate(self.auxiliary_head): + loss_aux = aux_head.forward_train(x, img_metas, + gt_semantic_seg, + self.train_cfg) + losses.update(add_prefix(loss_aux, f'aux_{idx}')) + else: + loss_aux = self.auxiliary_head.forward_train( + x, img_metas, gt_semantic_seg, self.train_cfg) + losses.update(add_prefix(loss_aux, 'aux')) + + return losses + + def forward_dummy(self, img): + """Dummy forward function.""" + seg_logit = self.encode_decode(img, None) + + return seg_logit + + def forward_train(self, img, img_metas, gt_semantic_seg): + """Forward function for training. + + Args: + img (Tensor): Input images. + img_metas (list[dict]): List of image info dict where each dict + has: 'img_shape', 'scale_factor', 'flip', and may also contain + 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. + For details on the values of these keys see + `mmseg/datasets/pipelines/formatting.py:Collect`. + gt_semantic_seg (Tensor): Semantic segmentation masks + used if the architecture supports semantic segmentation task. + + Returns: + dict[str, Tensor]: a dictionary of loss components + """ + + x = self.extract_feat(img) + + losses = dict() + + loss_decode = self._decode_head_forward_train(x, img_metas, + gt_semantic_seg) + losses.update(loss_decode) + + if self.with_auxiliary_head: + loss_aux = self._auxiliary_head_forward_train( + x, img_metas, gt_semantic_seg) + losses.update(loss_aux) + + return losses + + # TODO refactor + def slide_inference(self, img, img_meta, rescale): + """Inference by sliding-window with overlap. + + If h_crop > h_img or w_crop > w_img, the small patch will be used to + decode without padding. + """ + + h_stride, w_stride = self.test_cfg.stride + h_crop, w_crop = self.test_cfg.crop_size + batch_size, _, h_img, w_img = img.size() + num_classes = self.num_classes + h_grids = max(h_img - h_crop + h_stride - 1, 0) // h_stride + 1 + w_grids = max(w_img - w_crop + w_stride - 1, 0) // w_stride + 1 + preds = img.new_zeros((batch_size, num_classes, h_img, w_img)) + count_mat = img.new_zeros((batch_size, 1, h_img, w_img)) + for h_idx in range(h_grids): + for w_idx in range(w_grids): + y1 = h_idx * h_stride + x1 = w_idx * w_stride + y2 = min(y1 + h_crop, h_img) + x2 = min(x1 + w_crop, w_img) + y1 = max(y2 - h_crop, 0) + x1 = max(x2 - w_crop, 0) + crop_img = img[:, :, y1:y2, x1:x2] + crop_seg_logit = self.encode_decode(crop_img, img_meta) + preds += F.pad(crop_seg_logit, + (int(x1), int(preds.shape[3] - x2), int(y1), + int(preds.shape[2] - y2))) + + count_mat[:, :, y1:y2, x1:x2] += 1 + assert (count_mat == 0).sum() == 0 + if torch.onnx.is_in_onnx_export(): + # cast count_mat to constant while exporting to ONNX + count_mat = torch.from_numpy( + count_mat.cpu().detach().numpy()).to(device=img.device) + preds = preds / count_mat + if rescale: + preds = resize( + preds, + size=img_meta[0]['ori_shape'][:2], + mode='bilinear', + align_corners=self.align_corners, + warning=False) + return preds + + def whole_inference(self, img, img_meta, rescale): + """Inference with full image.""" + + seg_logit = self.encode_decode(img, img_meta) + if rescale: + # support dynamic shape for onnx + if torch.onnx.is_in_onnx_export(): + size = img.shape[2:] + else: + size = img_meta[0]['ori_shape'][:2] + seg_logit = resize( + seg_logit, + size=size, + mode='bilinear', + align_corners=self.align_corners, + warning=False) + + return seg_logit + + def inference(self, img, img_meta, rescale): + """Inference with slide/whole style. + + Args: + img (Tensor): The input image of shape (N, 3, H, W). + img_meta (dict): Image info dict where each dict has: 'img_shape', + 'scale_factor', 'flip', and may also contain + 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. + For details on the values of these keys see + `mmseg/datasets/pipelines/formatting.py:Collect`. + rescale (bool): Whether rescale back to original shape. + + Returns: + Tensor: The output segmentation map. + """ + + assert self.test_cfg.mode in ['slide', 'whole'] + ori_shape = img_meta[0]['ori_shape'] + assert all(_['ori_shape'] == ori_shape for _ in img_meta) + if self.test_cfg.mode == 'slide': + seg_logit = self.slide_inference(img, img_meta, rescale) + else: + seg_logit = self.whole_inference(img, img_meta, rescale) + output = F.softmax(seg_logit, dim=1) + flip = img_meta[0]['flip'] + if flip: + flip_direction = img_meta[0]['flip_direction'] + assert flip_direction in ['horizontal', 'vertical'] + if flip_direction == 'horizontal': + output = output.flip(dims=(3, )) + elif flip_direction == 'vertical': + output = output.flip(dims=(2, )) + + return output + + def simple_test(self, img, img_meta, rescale=True): + """Simple test with single image.""" + seg_logit = self.inference(img, img_meta, rescale) + seg_pred = seg_logit.argmax(dim=1) + if torch.onnx.is_in_onnx_export(): + # our inference backend only support 4D output + seg_pred = seg_pred.unsqueeze(0) + return seg_pred + seg_pred = seg_pred.cpu().numpy() + # unravel batch dim + seg_pred = list(seg_pred) + return seg_pred + + def aug_test(self, imgs, img_metas, rescale=True): + """Test with augmentations. + + Only rescale=True is supported. + """ + # aug_test rescale all imgs back to ori_shape for now + assert rescale + # to save memory, we get augmented seg logit inplace + seg_logit = self.inference(imgs[0], img_metas[0], rescale) + for i in range(1, len(imgs)): + cur_seg_logit = self.inference(imgs[i], img_metas[i], rescale) + seg_logit += cur_seg_logit + seg_logit /= len(imgs) + seg_pred = seg_logit.argmax(dim=1) + seg_pred = seg_pred.cpu().numpy() + # unravel batch dim + seg_pred = list(seg_pred) + return seg_pred diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3d3bdd349b9f2ae499a2fcb2ac1d2e3c77befebe --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/__init__.py @@ -0,0 +1,13 @@ +from .drop import DropPath +from .inverted_residual import InvertedResidual, InvertedResidualV3 +from .make_divisible import make_divisible +from .res_layer import ResLayer +from .se_layer import SELayer +from .self_attention_block import SelfAttentionBlock +from .up_conv_block import UpConvBlock +from .weight_init import trunc_normal_ + +__all__ = [ + 'ResLayer', 'SelfAttentionBlock', 'make_divisible', 'InvertedResidual', + 'UpConvBlock', 'InvertedResidualV3', 'SELayer', 'DropPath', 'trunc_normal_' +] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/drop.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/drop.py new file mode 100644 index 0000000000000000000000000000000000000000..4520b0ff407d2a95a864086bdbca0065f222aa63 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/drop.py @@ -0,0 +1,31 @@ +"""Modified from https://github.com/rwightman/pytorch-image- +models/blob/master/timm/models/layers/drop.py.""" + +import torch +from torch import nn + + +class DropPath(nn.Module): + """Drop paths (Stochastic Depth) per sample (when applied in main path of + residual blocks). + + Args: + drop_prob (float): Drop rate for paths of model. Dropout rate has + to be between 0 and 1. Default: 0. + """ + + def __init__(self, drop_prob=0.): + super(DropPath, self).__init__() + self.drop_prob = drop_prob + self.keep_prob = 1 - drop_prob + + def forward(self, x): + if self.drop_prob == 0. or not self.training: + return x + shape = (x.shape[0], ) + (1, ) * ( + x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets + random_tensor = self.keep_prob + torch.rand( + shape, dtype=x.dtype, device=x.device) + random_tensor.floor_() # binarize + output = x.div(self.keep_prob) * random_tensor + return output diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/inverted_residual.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/inverted_residual.py new file mode 100644 index 0000000000000000000000000000000000000000..2df5ebd7c94c0a66b0d05ef9e200ddbeabfa79f6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/inverted_residual.py @@ -0,0 +1,208 @@ +from annotator.mmpkg.mmcv.cnn import ConvModule +from torch import nn +from torch.utils import checkpoint as cp + +from .se_layer import SELayer + + +class InvertedResidual(nn.Module): + """InvertedResidual block for MobileNetV2. + + Args: + in_channels (int): The input channels of the InvertedResidual block. + out_channels (int): The output channels of the InvertedResidual block. + stride (int): Stride of the middle (first) 3x3 convolution. + expand_ratio (int): Adjusts number of channels of the hidden layer + in InvertedResidual by this amount. + dilation (int): Dilation rate of depthwise conv. Default: 1 + conv_cfg (dict): Config dict for convolution layer. + Default: None, which means using conv2d. + norm_cfg (dict): Config dict for normalization layer. + Default: dict(type='BN'). + act_cfg (dict): Config dict for activation layer. + Default: dict(type='ReLU6'). + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. Default: False. + + Returns: + Tensor: The output tensor. + """ + + def __init__(self, + in_channels, + out_channels, + stride, + expand_ratio, + dilation=1, + conv_cfg=None, + norm_cfg=dict(type='BN'), + act_cfg=dict(type='ReLU6'), + with_cp=False): + super(InvertedResidual, self).__init__() + self.stride = stride + assert stride in [1, 2], f'stride must in [1, 2]. ' \ + f'But received {stride}.' + self.with_cp = with_cp + self.use_res_connect = self.stride == 1 and in_channels == out_channels + hidden_dim = int(round(in_channels * expand_ratio)) + + layers = [] + if expand_ratio != 1: + layers.append( + ConvModule( + in_channels=in_channels, + out_channels=hidden_dim, + kernel_size=1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg)) + layers.extend([ + ConvModule( + in_channels=hidden_dim, + out_channels=hidden_dim, + kernel_size=3, + stride=stride, + padding=dilation, + dilation=dilation, + groups=hidden_dim, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg), + ConvModule( + in_channels=hidden_dim, + out_channels=out_channels, + kernel_size=1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=None) + ]) + self.conv = nn.Sequential(*layers) + + def forward(self, x): + + def _inner_forward(x): + if self.use_res_connect: + return x + self.conv(x) + else: + return self.conv(x) + + if self.with_cp and x.requires_grad: + out = cp.checkpoint(_inner_forward, x) + else: + out = _inner_forward(x) + + return out + + +class InvertedResidualV3(nn.Module): + """Inverted Residual Block for MobileNetV3. + + Args: + in_channels (int): The input channels of this Module. + out_channels (int): The output channels of this Module. + mid_channels (int): The input channels of the depthwise convolution. + kernel_size (int): The kernel size of the depthwise convolution. + Default: 3. + stride (int): The stride of the depthwise convolution. Default: 1. + se_cfg (dict): Config dict for se layer. Default: None, which means no + se layer. + with_expand_conv (bool): Use expand conv or not. If set False, + mid_channels must be the same with in_channels. Default: True. + conv_cfg (dict): Config dict for convolution layer. Default: None, + which means using conv2d. + norm_cfg (dict): Config dict for normalization layer. + Default: dict(type='BN'). + act_cfg (dict): Config dict for activation layer. + Default: dict(type='ReLU'). + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. Default: False. + + Returns: + Tensor: The output tensor. + """ + + def __init__(self, + in_channels, + out_channels, + mid_channels, + kernel_size=3, + stride=1, + se_cfg=None, + with_expand_conv=True, + conv_cfg=None, + norm_cfg=dict(type='BN'), + act_cfg=dict(type='ReLU'), + with_cp=False): + super(InvertedResidualV3, self).__init__() + self.with_res_shortcut = (stride == 1 and in_channels == out_channels) + assert stride in [1, 2] + self.with_cp = with_cp + self.with_se = se_cfg is not None + self.with_expand_conv = with_expand_conv + + if self.with_se: + assert isinstance(se_cfg, dict) + if not self.with_expand_conv: + assert mid_channels == in_channels + + if self.with_expand_conv: + self.expand_conv = ConvModule( + in_channels=in_channels, + out_channels=mid_channels, + kernel_size=1, + stride=1, + padding=0, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + self.depthwise_conv = ConvModule( + in_channels=mid_channels, + out_channels=mid_channels, + kernel_size=kernel_size, + stride=stride, + padding=kernel_size // 2, + groups=mid_channels, + conv_cfg=dict( + type='Conv2dAdaptivePadding') if stride == 2 else conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + + if self.with_se: + self.se = SELayer(**se_cfg) + + self.linear_conv = ConvModule( + in_channels=mid_channels, + out_channels=out_channels, + kernel_size=1, + stride=1, + padding=0, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=None) + + def forward(self, x): + + def _inner_forward(x): + out = x + + if self.with_expand_conv: + out = self.expand_conv(out) + + out = self.depthwise_conv(out) + + if self.with_se: + out = self.se(out) + + out = self.linear_conv(out) + + if self.with_res_shortcut: + return x + out + else: + return out + + if self.with_cp and x.requires_grad: + out = cp.checkpoint(_inner_forward, x) + else: + out = _inner_forward(x) + + return out diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/make_divisible.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/make_divisible.py new file mode 100644 index 0000000000000000000000000000000000000000..75ad756052529f52fe83bb95dd1f0ecfc9a13078 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/make_divisible.py @@ -0,0 +1,27 @@ +def make_divisible(value, divisor, min_value=None, min_ratio=0.9): + """Make divisible function. + + This function rounds the channel number to the nearest value that can be + divisible by the divisor. It is taken from the original tf repo. It ensures + that all layers have a channel number that is divisible by divisor. It can + be seen here: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py # noqa + + Args: + value (int): The original channel number. + divisor (int): The divisor to fully divide the channel number. + min_value (int): The minimum value of the output channel. + Default: None, means that the minimum value equal to the divisor. + min_ratio (float): The minimum ratio of the rounded channel number to + the original channel number. Default: 0.9. + + Returns: + int: The modified output channel number. + """ + + if min_value is None: + min_value = divisor + new_value = max(min_value, int(value + divisor / 2) // divisor * divisor) + # Make sure that round down does not go down by more than (1-min_ratio). + if new_value < min_ratio * value: + new_value += divisor + return new_value diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/res_layer.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/res_layer.py new file mode 100644 index 0000000000000000000000000000000000000000..d41075a57356b4fd802bc4ff199e55e63678b589 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/res_layer.py @@ -0,0 +1,94 @@ +from annotator.mmpkg.mmcv.cnn import build_conv_layer, build_norm_layer +from torch import nn as nn + + +class ResLayer(nn.Sequential): + """ResLayer to build ResNet style backbone. + + Args: + block (nn.Module): block used to build ResLayer. + inplanes (int): inplanes of block. + planes (int): planes of block. + num_blocks (int): number of blocks. + stride (int): stride of the first block. Default: 1 + avg_down (bool): Use AvgPool instead of stride conv when + downsampling in the bottleneck. Default: False + conv_cfg (dict): dictionary to construct and config conv layer. + Default: None + norm_cfg (dict): dictionary to construct and config norm layer. + Default: dict(type='BN') + multi_grid (int | None): Multi grid dilation rates of last + stage. Default: None + contract_dilation (bool): Whether contract first dilation of each layer + Default: False + """ + + def __init__(self, + block, + inplanes, + planes, + num_blocks, + stride=1, + dilation=1, + avg_down=False, + conv_cfg=None, + norm_cfg=dict(type='BN'), + multi_grid=None, + contract_dilation=False, + **kwargs): + self.block = block + + downsample = None + if stride != 1 or inplanes != planes * block.expansion: + downsample = [] + conv_stride = stride + if avg_down: + conv_stride = 1 + downsample.append( + nn.AvgPool2d( + kernel_size=stride, + stride=stride, + ceil_mode=True, + count_include_pad=False)) + downsample.extend([ + build_conv_layer( + conv_cfg, + inplanes, + planes * block.expansion, + kernel_size=1, + stride=conv_stride, + bias=False), + build_norm_layer(norm_cfg, planes * block.expansion)[1] + ]) + downsample = nn.Sequential(*downsample) + + layers = [] + if multi_grid is None: + if dilation > 1 and contract_dilation: + first_dilation = dilation // 2 + else: + first_dilation = dilation + else: + first_dilation = multi_grid[0] + layers.append( + block( + inplanes=inplanes, + planes=planes, + stride=stride, + dilation=first_dilation, + downsample=downsample, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + **kwargs)) + inplanes = planes * block.expansion + for i in range(1, num_blocks): + layers.append( + block( + inplanes=inplanes, + planes=planes, + stride=1, + dilation=dilation if multi_grid is None else multi_grid[i], + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + **kwargs)) + super(ResLayer, self).__init__(*layers) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/se_layer.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/se_layer.py new file mode 100644 index 0000000000000000000000000000000000000000..42ab005e1fe2211e9ecb651d31de128cf95cfec7 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/se_layer.py @@ -0,0 +1,57 @@ +import annotator.mmpkg.mmcv as mmcv +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import ConvModule + +from .make_divisible import make_divisible + + +class SELayer(nn.Module): + """Squeeze-and-Excitation Module. + + Args: + channels (int): The input (and output) channels of the SE layer. + ratio (int): Squeeze ratio in SELayer, the intermediate channel will be + ``int(channels/ratio)``. Default: 16. + conv_cfg (None or dict): Config dict for convolution layer. + Default: None, which means using conv2d. + act_cfg (dict or Sequence[dict]): Config dict for activation layer. + If act_cfg is a dict, two activation layers will be configured + by this dict. If act_cfg is a sequence of dicts, the first + activation layer will be configured by the first dict and the + second activation layer will be configured by the second dict. + Default: (dict(type='ReLU'), dict(type='HSigmoid', bias=3.0, + divisor=6.0)). + """ + + def __init__(self, + channels, + ratio=16, + conv_cfg=None, + act_cfg=(dict(type='ReLU'), + dict(type='HSigmoid', bias=3.0, divisor=6.0))): + super(SELayer, self).__init__() + if isinstance(act_cfg, dict): + act_cfg = (act_cfg, act_cfg) + assert len(act_cfg) == 2 + assert mmcv.is_tuple_of(act_cfg, dict) + self.global_avgpool = nn.AdaptiveAvgPool2d(1) + self.conv1 = ConvModule( + in_channels=channels, + out_channels=make_divisible(channels // ratio, 8), + kernel_size=1, + stride=1, + conv_cfg=conv_cfg, + act_cfg=act_cfg[0]) + self.conv2 = ConvModule( + in_channels=make_divisible(channels // ratio, 8), + out_channels=channels, + kernel_size=1, + stride=1, + conv_cfg=conv_cfg, + act_cfg=act_cfg[1]) + + def forward(self, x): + out = self.global_avgpool(x) + out = self.conv1(out) + out = self.conv2(out) + return x * out diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/self_attention_block.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/self_attention_block.py new file mode 100644 index 0000000000000000000000000000000000000000..a342e2b29ad53916c98d0342bde8f0f6cb10197a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/self_attention_block.py @@ -0,0 +1,159 @@ +import torch +from annotator.mmpkg.mmcv.cnn import ConvModule, constant_init +from torch import nn as nn +from torch.nn import functional as F + + +class SelfAttentionBlock(nn.Module): + """General self-attention block/non-local block. + + Please refer to https://arxiv.org/abs/1706.03762 for details about key, + query and value. + + Args: + key_in_channels (int): Input channels of key feature. + query_in_channels (int): Input channels of query feature. + channels (int): Output channels of key/query transform. + out_channels (int): Output channels. + share_key_query (bool): Whether share projection weight between key + and query projection. + query_downsample (nn.Module): Query downsample module. + key_downsample (nn.Module): Key downsample module. + key_query_num_convs (int): Number of convs for key/query projection. + value_num_convs (int): Number of convs for value projection. + matmul_norm (bool): Whether normalize attention map with sqrt of + channels + with_out (bool): Whether use out projection. + conv_cfg (dict|None): Config of conv layers. + norm_cfg (dict|None): Config of norm layers. + act_cfg (dict|None): Config of activation layers. + """ + + def __init__(self, key_in_channels, query_in_channels, channels, + out_channels, share_key_query, query_downsample, + key_downsample, key_query_num_convs, value_out_num_convs, + key_query_norm, value_out_norm, matmul_norm, with_out, + conv_cfg, norm_cfg, act_cfg): + super(SelfAttentionBlock, self).__init__() + if share_key_query: + assert key_in_channels == query_in_channels + self.key_in_channels = key_in_channels + self.query_in_channels = query_in_channels + self.out_channels = out_channels + self.channels = channels + self.share_key_query = share_key_query + self.conv_cfg = conv_cfg + self.norm_cfg = norm_cfg + self.act_cfg = act_cfg + self.key_project = self.build_project( + key_in_channels, + channels, + num_convs=key_query_num_convs, + use_conv_module=key_query_norm, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + if share_key_query: + self.query_project = self.key_project + else: + self.query_project = self.build_project( + query_in_channels, + channels, + num_convs=key_query_num_convs, + use_conv_module=key_query_norm, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + self.value_project = self.build_project( + key_in_channels, + channels if with_out else out_channels, + num_convs=value_out_num_convs, + use_conv_module=value_out_norm, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + if with_out: + self.out_project = self.build_project( + channels, + out_channels, + num_convs=value_out_num_convs, + use_conv_module=value_out_norm, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + else: + self.out_project = None + + self.query_downsample = query_downsample + self.key_downsample = key_downsample + self.matmul_norm = matmul_norm + + self.init_weights() + + def init_weights(self): + """Initialize weight of later layer.""" + if self.out_project is not None: + if not isinstance(self.out_project, ConvModule): + constant_init(self.out_project, 0) + + def build_project(self, in_channels, channels, num_convs, use_conv_module, + conv_cfg, norm_cfg, act_cfg): + """Build projection layer for key/query/value/out.""" + if use_conv_module: + convs = [ + ConvModule( + in_channels, + channels, + 1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + ] + for _ in range(num_convs - 1): + convs.append( + ConvModule( + channels, + channels, + 1, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg)) + else: + convs = [nn.Conv2d(in_channels, channels, 1)] + for _ in range(num_convs - 1): + convs.append(nn.Conv2d(channels, channels, 1)) + if len(convs) > 1: + convs = nn.Sequential(*convs) + else: + convs = convs[0] + return convs + + def forward(self, query_feats, key_feats): + """Forward function.""" + batch_size = query_feats.size(0) + query = self.query_project(query_feats) + if self.query_downsample is not None: + query = self.query_downsample(query) + query = query.reshape(*query.shape[:2], -1) + query = query.permute(0, 2, 1).contiguous() + + key = self.key_project(key_feats) + value = self.value_project(key_feats) + if self.key_downsample is not None: + key = self.key_downsample(key) + value = self.key_downsample(value) + key = key.reshape(*key.shape[:2], -1) + value = value.reshape(*value.shape[:2], -1) + value = value.permute(0, 2, 1).contiguous() + + sim_map = torch.matmul(query, key) + if self.matmul_norm: + sim_map = (self.channels**-.5) * sim_map + sim_map = F.softmax(sim_map, dim=-1) + + context = torch.matmul(sim_map, value) + context = context.permute(0, 2, 1).contiguous() + context = context.reshape(batch_size, -1, *query_feats.shape[2:]) + if self.out_project is not None: + context = self.out_project(context) + return context diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/up_conv_block.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/up_conv_block.py new file mode 100644 index 0000000000000000000000000000000000000000..86328011a9704d17e9f9d0d54994719ead5caa56 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/up_conv_block.py @@ -0,0 +1,101 @@ +import torch +import torch.nn as nn +from annotator.mmpkg.mmcv.cnn import ConvModule, build_upsample_layer + + +class UpConvBlock(nn.Module): + """Upsample convolution block in decoder for UNet. + + This upsample convolution block consists of one upsample module + followed by one convolution block. The upsample module expands the + high-level low-resolution feature map and the convolution block fuses + the upsampled high-level low-resolution feature map and the low-level + high-resolution feature map from encoder. + + Args: + conv_block (nn.Sequential): Sequential of convolutional layers. + in_channels (int): Number of input channels of the high-level + skip_channels (int): Number of input channels of the low-level + high-resolution feature map from encoder. + out_channels (int): Number of output channels. + num_convs (int): Number of convolutional layers in the conv_block. + Default: 2. + stride (int): Stride of convolutional layer in conv_block. Default: 1. + dilation (int): Dilation rate of convolutional layer in conv_block. + Default: 1. + with_cp (bool): Use checkpoint or not. Using checkpoint will save some + memory while slowing down the training speed. Default: False. + conv_cfg (dict | None): Config dict for convolution layer. + Default: None. + norm_cfg (dict | None): Config dict for normalization layer. + Default: dict(type='BN'). + act_cfg (dict | None): Config dict for activation layer in ConvModule. + Default: dict(type='ReLU'). + upsample_cfg (dict): The upsample config of the upsample module in + decoder. Default: dict(type='InterpConv'). If the size of + high-level feature map is the same as that of skip feature map + (low-level feature map from encoder), it does not need upsample the + high-level feature map and the upsample_cfg is None. + dcn (bool): Use deformable convolution in convolutional layer or not. + Default: None. + plugins (dict): plugins for convolutional layers. Default: None. + """ + + def __init__(self, + conv_block, + in_channels, + skip_channels, + out_channels, + num_convs=2, + stride=1, + dilation=1, + with_cp=False, + conv_cfg=None, + norm_cfg=dict(type='BN'), + act_cfg=dict(type='ReLU'), + upsample_cfg=dict(type='InterpConv'), + dcn=None, + plugins=None): + super(UpConvBlock, self).__init__() + assert dcn is None, 'Not implemented yet.' + assert plugins is None, 'Not implemented yet.' + + self.conv_block = conv_block( + in_channels=2 * skip_channels, + out_channels=out_channels, + num_convs=num_convs, + stride=stride, + dilation=dilation, + with_cp=with_cp, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg, + dcn=None, + plugins=None) + if upsample_cfg is not None: + self.upsample = build_upsample_layer( + cfg=upsample_cfg, + in_channels=in_channels, + out_channels=skip_channels, + with_cp=with_cp, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + else: + self.upsample = ConvModule( + in_channels, + skip_channels, + kernel_size=1, + stride=1, + padding=0, + conv_cfg=conv_cfg, + norm_cfg=norm_cfg, + act_cfg=act_cfg) + + def forward(self, skip, x): + """Forward function.""" + + x = self.upsample(x) + out = torch.cat([skip, x], dim=1) + out = self.conv_block(out) + + return out diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/weight_init.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/weight_init.py new file mode 100644 index 0000000000000000000000000000000000000000..38141ba3d61f64ddfc0a31574b4648cbad96d7dd --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/models/utils/weight_init.py @@ -0,0 +1,62 @@ +"""Modified from https://github.com/rwightman/pytorch-image- +models/blob/master/timm/models/layers/drop.py.""" + +import math +import warnings + +import torch + + +def _no_grad_trunc_normal_(tensor, mean, std, a, b): + """Reference: https://people.sc.fsu.edu/~jburkardt/presentations + /truncated_normal.pdf""" + + def norm_cdf(x): + # Computes standard normal cumulative distribution function + return (1. + math.erf(x / math.sqrt(2.))) / 2. + + if (mean < a - 2 * std) or (mean > b + 2 * std): + warnings.warn( + 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' + 'The distribution of values may be incorrect.', + stacklevel=2) + + with torch.no_grad(): + # Values are generated by using a truncated uniform distribution and + # then using the inverse CDF for the normal distribution. + # Get upper and lower cdf values + lower_bound = norm_cdf((a - mean) / std) + upper_bound = norm_cdf((b - mean) / std) + + # Uniformly fill tensor with values from [l, u], then translate to + # [2l-1, 2u-1]. + tensor.uniform_(2 * lower_bound - 1, 2 * upper_bound - 1) + + # Use inverse cdf transform for normal distribution to get truncated + # standard normal + tensor.erfinv_() + + # Transform to proper mean, std + tensor.mul_(std * math.sqrt(2.)) + tensor.add_(mean) + + # Clamp to ensure it's in the proper range + tensor.clamp_(min=a, max=b) + return tensor + + +def trunc_normal_(tensor, mean=0., std=1., a=-2., b=2.): + r"""Fills the input Tensor with values drawn from a truncated + normal distribution. The values are effectively drawn from the + normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` + with values outside :math:`[a, b]` redrawn until they are within + the bounds. The method used for generating the random values works + best when :math:`a \leq \text{mean} \leq b`. + Args: + tensor (``torch.Tensor``): an n-dimensional `torch.Tensor` + mean (float): the mean of the normal distribution + std (float): the standard deviation of the normal distribution + a (float): the minimum cutoff value + b (float): the maximum cutoff value + """ + return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/ops/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/ops/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..bec51c75b9363a9a19e9fb5c35f4e7dbd6f7751c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/ops/__init__.py @@ -0,0 +1,4 @@ +from .encoding import Encoding +from .wrappers import Upsample, resize + +__all__ = ['Upsample', 'resize', 'Encoding'] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/ops/encoding.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/ops/encoding.py new file mode 100644 index 0000000000000000000000000000000000000000..7eb3629a6426550b8e4c537ee1ff4341893e489e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/ops/encoding.py @@ -0,0 +1,74 @@ +import torch +from torch import nn +from torch.nn import functional as F + + +class Encoding(nn.Module): + """Encoding Layer: a learnable residual encoder. + + Input is of shape (batch_size, channels, height, width). + Output is of shape (batch_size, num_codes, channels). + + Args: + channels: dimension of the features or feature channels + num_codes: number of code words + """ + + def __init__(self, channels, num_codes): + super(Encoding, self).__init__() + # init codewords and smoothing factor + self.channels, self.num_codes = channels, num_codes + std = 1. / ((num_codes * channels)**0.5) + # [num_codes, channels] + self.codewords = nn.Parameter( + torch.empty(num_codes, channels, + dtype=torch.float).uniform_(-std, std), + requires_grad=True) + # [num_codes] + self.scale = nn.Parameter( + torch.empty(num_codes, dtype=torch.float).uniform_(-1, 0), + requires_grad=True) + + @staticmethod + def scaled_l2(x, codewords, scale): + num_codes, channels = codewords.size() + batch_size = x.size(0) + reshaped_scale = scale.view((1, 1, num_codes)) + expanded_x = x.unsqueeze(2).expand( + (batch_size, x.size(1), num_codes, channels)) + reshaped_codewords = codewords.view((1, 1, num_codes, channels)) + + scaled_l2_norm = reshaped_scale * ( + expanded_x - reshaped_codewords).pow(2).sum(dim=3) + return scaled_l2_norm + + @staticmethod + def aggregate(assignment_weights, x, codewords): + num_codes, channels = codewords.size() + reshaped_codewords = codewords.view((1, 1, num_codes, channels)) + batch_size = x.size(0) + + expanded_x = x.unsqueeze(2).expand( + (batch_size, x.size(1), num_codes, channels)) + encoded_feat = (assignment_weights.unsqueeze(3) * + (expanded_x - reshaped_codewords)).sum(dim=1) + return encoded_feat + + def forward(self, x): + assert x.dim() == 4 and x.size(1) == self.channels + # [batch_size, channels, height, width] + batch_size = x.size(0) + # [batch_size, height x width, channels] + x = x.view(batch_size, self.channels, -1).transpose(1, 2).contiguous() + # assignment_weights: [batch_size, channels, num_codes] + assignment_weights = F.softmax( + self.scaled_l2(x, self.codewords, self.scale), dim=2) + # aggregate + encoded_feat = self.aggregate(assignment_weights, x, self.codewords) + return encoded_feat + + def __repr__(self): + repr_str = self.__class__.__name__ + repr_str += f'(Nx{self.channels}xHxW =>Nx{self.num_codes}' \ + f'x{self.channels})' + return repr_str diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/ops/wrappers.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/ops/wrappers.py new file mode 100644 index 0000000000000000000000000000000000000000..0ed9a0cb8d7c0e0ec2748dd89c652756653cac78 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/ops/wrappers.py @@ -0,0 +1,50 @@ +import warnings + +import torch.nn as nn +import torch.nn.functional as F + + +def resize(input, + size=None, + scale_factor=None, + mode='nearest', + align_corners=None, + warning=True): + if warning: + if size is not None and align_corners: + input_h, input_w = tuple(int(x) for x in input.shape[2:]) + output_h, output_w = tuple(int(x) for x in size) + if output_h > input_h or output_w > output_h: + if ((output_h > 1 and output_w > 1 and input_h > 1 + and input_w > 1) and (output_h - 1) % (input_h - 1) + and (output_w - 1) % (input_w - 1)): + warnings.warn( + f'When align_corners={align_corners}, ' + 'the output would more aligned if ' + f'input size {(input_h, input_w)} is `x+1` and ' + f'out size {(output_h, output_w)} is `nx+1`') + return F.interpolate(input, size, scale_factor, mode, align_corners) + + +class Upsample(nn.Module): + + def __init__(self, + size=None, + scale_factor=None, + mode='nearest', + align_corners=None): + super(Upsample, self).__init__() + self.size = size + if isinstance(scale_factor, tuple): + self.scale_factor = tuple(float(factor) for factor in scale_factor) + else: + self.scale_factor = float(scale_factor) if scale_factor else None + self.mode = mode + self.align_corners = align_corners + + def forward(self, x): + if not self.size: + size = [int(t * self.scale_factor) for t in x.shape[-2:]] + else: + size = self.size + return resize(x, size, None, self.mode, self.align_corners) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/utils/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ac489e2dbbc0e6fa87f5088b4edcc20f8cadc1a6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/utils/__init__.py @@ -0,0 +1,4 @@ +from .collect_env import collect_env +from .logger import get_root_logger + +__all__ = ['get_root_logger', 'collect_env'] diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/utils/collect_env.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/utils/collect_env.py new file mode 100644 index 0000000000000000000000000000000000000000..015d5a6b4f3ff31859cca36584879f646b3864d4 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/utils/collect_env.py @@ -0,0 +1,17 @@ +from annotator.mmpkg.mmcv.utils import collect_env as collect_base_env +from annotator.mmpkg.mmcv.utils import get_git_hash + +import annotator.mmpkg.mmseg as mmseg + + +def collect_env(): + """Collect the information of the running environments.""" + env_info = collect_base_env() + env_info['MMSegmentation'] = f'{mmseg.__version__}+{get_git_hash()[:7]}' + + return env_info + + +if __name__ == '__main__': + for name, val in collect_env().items(): + print('{}: {}'.format(name, val)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/utils/logger.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/utils/logger.py new file mode 100644 index 0000000000000000000000000000000000000000..0c37733358e3e21479b41f54220bfe34b482009c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/mmpkg/mmseg/utils/logger.py @@ -0,0 +1,27 @@ +import logging + +from annotator.mmpkg.mmcv.utils import get_logger + + +def get_root_logger(log_file=None, log_level=logging.INFO): + """Get the root logger. + + The logger will be initialized if it has not been initialized. By default a + StreamHandler will be added. If `log_file` is specified, a FileHandler will + also be added. The name of the root logger is the top-level package name, + e.g., "mmseg". + + Args: + log_file (str | None): The log filename. If specified, a FileHandler + will be added to the root logger. + log_level (int): The root logger level. Note that only the process of + rank 0 is affected, while other processes will set the level to + "Error" and be silent most of the time. + + Returns: + logging.Logger: The root logger. + """ + + logger = get_logger(name='mmseg', log_file=log_file, log_level=log_level) + + return logger diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5ed4b41dcdab8a0a7fde2f58314e84a8aca25f53 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/__init__.py @@ -0,0 +1,62 @@ +import os +os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE" + +import torch +import numpy as np +from . import util +from .body import Body +from .hand import Hand +from modules.paths import models_path + +body_estimation = None +hand_estimation = None + +body_model_path = "https://huggingface.co/lllyasviel/ControlNet/resolve/main/annotator/ckpts/body_pose_model.pth" +hand_model_path = "https://huggingface.co/lllyasviel/ControlNet/resolve/main/annotator/ckpts/hand_pose_model.pth" +modeldir = os.path.join(models_path, "openpose") +old_modeldir = os.path.dirname(os.path.realpath(__file__)) + +def unload_openpose_model(): + global body_estimation, hand_estimation + if body_estimation is not None: + body_estimation.model.cpu() + hand_estimation.model.cpu() + +def apply_openpose(oriImg, hand=False): + global body_estimation, hand_estimation + if body_estimation is None: + body_modelpath = os.path.join(modeldir, "body_pose_model.pth") + hand_modelpath = os.path.join(modeldir, "hand_pose_model.pth") + old_body_modelpath = os.path.join(old_modeldir, "body_pose_model.pth") + old_hand_modelpath = os.path.join(old_modeldir, "hand_pose_model.pth") + + if os.path.exists(old_body_modelpath): + body_modelpath = old_body_modelpath + elif not os.path.exists(hand_modelpath): + from basicsr.utils.download_util import load_file_from_url + load_file_from_url(hand_model_path, model_dir=modeldir) + + if os.path.exists(old_hand_modelpath): + hand_modelpath = old_hand_modelpath + elif not os.path.exists(body_model_path): + from basicsr.utils.download_util import load_file_from_url + load_file_from_url(body_model_path, model_dir=modeldir) + + body_estimation = Body(body_modelpath) + hand_estimation = Hand(hand_modelpath) + + oriImg = oriImg[:, :, ::-1].copy() + with torch.no_grad(): + candidate, subset = body_estimation(oriImg) + canvas = np.zeros_like(oriImg) + canvas = util.draw_bodypose(canvas, candidate, subset) + if hand: + hands_list = util.handDetect(candidate, subset, oriImg) + all_hand_peaks = [] + for x, y, w, is_left in hands_list: + peaks = hand_estimation(oriImg[y:y+w, x:x+w, :]) + peaks[:, 0] = np.where(peaks[:, 0] == 0, peaks[:, 0], peaks[:, 0] + x) + peaks[:, 1] = np.where(peaks[:, 1] == 0, peaks[:, 1], peaks[:, 1] + y) + all_hand_peaks.append(peaks) + canvas = util.draw_handpose(canvas, all_hand_peaks) + return canvas, dict(candidate=candidate.tolist(), subset=subset.tolist()) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/body.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/body.py new file mode 100644 index 0000000000000000000000000000000000000000..f85ee675e93b14b56d0ad8ef6c648707b6d35637 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/body.py @@ -0,0 +1,221 @@ +import os +import cv2 +import numpy as np +import math +import time +from scipy.ndimage.filters import gaussian_filter +import matplotlib.pyplot as plt +import matplotlib +import torch +from torchvision import transforms + +from . import util +from .model import bodypose_model +from modules import devices +from modules.paths import models_path + +class Body(object): + def __init__(self, model_path): + self.model = bodypose_model() + self.model = self.model.to(devices.get_device_for("controlnet")) + model_dict = util.transfer(self.model, torch.load(model_path)) + self.model.load_state_dict(model_dict) + self.model.eval() + + def __call__(self, oriImg): + self.model = self.model.to(devices.get_device_for("controlnet")) + + # scale_search = [0.5, 1.0, 1.5, 2.0] + scale_search = [0.5] + boxsize = 368 + stride = 8 + padValue = 128 + thre1 = 0.1 + thre2 = 0.05 + multiplier = [x * boxsize / oriImg.shape[0] for x in scale_search] + heatmap_avg = np.zeros((oriImg.shape[0], oriImg.shape[1], 19)) + paf_avg = np.zeros((oriImg.shape[0], oriImg.shape[1], 38)) + + for m in range(len(multiplier)): + scale = multiplier[m] + imageToTest = cv2.resize(oriImg, (0, 0), fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC) + imageToTest_padded, pad = util.padRightDownCorner(imageToTest, stride, padValue) + im = np.transpose(np.float32(imageToTest_padded[:, :, :, np.newaxis]), (3, 2, 0, 1)) / 256 - 0.5 + im = np.ascontiguousarray(im) + + data = torch.from_numpy(im).float() + data = data.to(devices.get_device_for("controlnet")) + # data = data.permute([2, 0, 1]).unsqueeze(0).float() + with torch.no_grad(): + Mconv7_stage6_L1, Mconv7_stage6_L2 = self.model(data) + Mconv7_stage6_L1 = Mconv7_stage6_L1.cpu().numpy() + Mconv7_stage6_L2 = Mconv7_stage6_L2.cpu().numpy() + + # extract outputs, resize, and remove padding + # heatmap = np.transpose(np.squeeze(net.blobs[output_blobs.keys()[1]].data), (1, 2, 0)) # output 1 is heatmaps + heatmap = np.transpose(np.squeeze(Mconv7_stage6_L2), (1, 2, 0)) # output 1 is heatmaps + heatmap = cv2.resize(heatmap, (0, 0), fx=stride, fy=stride, interpolation=cv2.INTER_CUBIC) + heatmap = heatmap[:imageToTest_padded.shape[0] - pad[2], :imageToTest_padded.shape[1] - pad[3], :] + heatmap = cv2.resize(heatmap, (oriImg.shape[1], oriImg.shape[0]), interpolation=cv2.INTER_CUBIC) + + # paf = np.transpose(np.squeeze(net.blobs[output_blobs.keys()[0]].data), (1, 2, 0)) # output 0 is PAFs + paf = np.transpose(np.squeeze(Mconv7_stage6_L1), (1, 2, 0)) # output 0 is PAFs + paf = cv2.resize(paf, (0, 0), fx=stride, fy=stride, interpolation=cv2.INTER_CUBIC) + paf = paf[:imageToTest_padded.shape[0] - pad[2], :imageToTest_padded.shape[1] - pad[3], :] + paf = cv2.resize(paf, (oriImg.shape[1], oriImg.shape[0]), interpolation=cv2.INTER_CUBIC) + + heatmap_avg += heatmap_avg + heatmap / len(multiplier) + paf_avg += + paf / len(multiplier) + + all_peaks = [] + peak_counter = 0 + + for part in range(18): + map_ori = heatmap_avg[:, :, part] + one_heatmap = gaussian_filter(map_ori, sigma=3) + + map_left = np.zeros(one_heatmap.shape) + map_left[1:, :] = one_heatmap[:-1, :] + map_right = np.zeros(one_heatmap.shape) + map_right[:-1, :] = one_heatmap[1:, :] + map_up = np.zeros(one_heatmap.shape) + map_up[:, 1:] = one_heatmap[:, :-1] + map_down = np.zeros(one_heatmap.shape) + map_down[:, :-1] = one_heatmap[:, 1:] + + peaks_binary = np.logical_and.reduce( + (one_heatmap >= map_left, one_heatmap >= map_right, one_heatmap >= map_up, one_heatmap >= map_down, one_heatmap > thre1)) + peaks = list(zip(np.nonzero(peaks_binary)[1], np.nonzero(peaks_binary)[0])) # note reverse + peaks_with_score = [x + (map_ori[x[1], x[0]],) for x in peaks] + peak_id = range(peak_counter, peak_counter + len(peaks)) + peaks_with_score_and_id = [peaks_with_score[i] + (peak_id[i],) for i in range(len(peak_id))] + + all_peaks.append(peaks_with_score_and_id) + peak_counter += len(peaks) + + # find connection in the specified sequence, center 29 is in the position 15 + limbSeq = [[2, 3], [2, 6], [3, 4], [4, 5], [6, 7], [7, 8], [2, 9], [9, 10], \ + [10, 11], [2, 12], [12, 13], [13, 14], [2, 1], [1, 15], [15, 17], \ + [1, 16], [16, 18], [3, 17], [6, 18]] + # the middle joints heatmap correpondence + mapIdx = [[31, 32], [39, 40], [33, 34], [35, 36], [41, 42], [43, 44], [19, 20], [21, 22], \ + [23, 24], [25, 26], [27, 28], [29, 30], [47, 48], [49, 50], [53, 54], [51, 52], \ + [55, 56], [37, 38], [45, 46]] + + connection_all = [] + special_k = [] + mid_num = 10 + + for k in range(len(mapIdx)): + score_mid = paf_avg[:, :, [x - 19 for x in mapIdx[k]]] + candA = all_peaks[limbSeq[k][0] - 1] + candB = all_peaks[limbSeq[k][1] - 1] + nA = len(candA) + nB = len(candB) + indexA, indexB = limbSeq[k] + if (nA != 0 and nB != 0): + connection_candidate = [] + for i in range(nA): + for j in range(nB): + vec = np.subtract(candB[j][:2], candA[i][:2]) + norm = math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]) + norm = max(0.001, norm) + vec = np.divide(vec, norm) + + startend = list(zip(np.linspace(candA[i][0], candB[j][0], num=mid_num), \ + np.linspace(candA[i][1], candB[j][1], num=mid_num))) + + vec_x = np.array([score_mid[int(round(startend[I][1])), int(round(startend[I][0])), 0] \ + for I in range(len(startend))]) + vec_y = np.array([score_mid[int(round(startend[I][1])), int(round(startend[I][0])), 1] \ + for I in range(len(startend))]) + + score_midpts = np.multiply(vec_x, vec[0]) + np.multiply(vec_y, vec[1]) + score_with_dist_prior = sum(score_midpts) / len(score_midpts) + min( + 0.5 * oriImg.shape[0] / norm - 1, 0) + criterion1 = len(np.nonzero(score_midpts > thre2)[0]) > 0.8 * len(score_midpts) + criterion2 = score_with_dist_prior > 0 + if criterion1 and criterion2: + connection_candidate.append( + [i, j, score_with_dist_prior, score_with_dist_prior + candA[i][2] + candB[j][2]]) + + connection_candidate = sorted(connection_candidate, key=lambda x: x[2], reverse=True) + connection = np.zeros((0, 5)) + for c in range(len(connection_candidate)): + i, j, s = connection_candidate[c][0:3] + if (i not in connection[:, 3] and j not in connection[:, 4]): + connection = np.vstack([connection, [candA[i][3], candB[j][3], s, i, j]]) + if (len(connection) >= min(nA, nB)): + break + + connection_all.append(connection) + else: + special_k.append(k) + connection_all.append([]) + + # last number in each row is the total parts number of that person + # the second last number in each row is the score of the overall configuration + subset = -1 * np.ones((0, 20)) + candidate = np.array([item for sublist in all_peaks for item in sublist]) + + for k in range(len(mapIdx)): + if k not in special_k: + partAs = connection_all[k][:, 0] + partBs = connection_all[k][:, 1] + indexA, indexB = np.array(limbSeq[k]) - 1 + + for i in range(len(connection_all[k])): # = 1:size(temp,1) + found = 0 + subset_idx = [-1, -1] + for j in range(len(subset)): # 1:size(subset,1): + if subset[j][indexA] == partAs[i] or subset[j][indexB] == partBs[i]: + subset_idx[found] = j + found += 1 + + if found == 1: + j = subset_idx[0] + if subset[j][indexB] != partBs[i]: + subset[j][indexB] = partBs[i] + subset[j][-1] += 1 + subset[j][-2] += candidate[partBs[i].astype(int), 2] + connection_all[k][i][2] + elif found == 2: # if found 2 and disjoint, merge them + j1, j2 = subset_idx + membership = ((subset[j1] >= 0).astype(int) + (subset[j2] >= 0).astype(int))[:-2] + if len(np.nonzero(membership == 2)[0]) == 0: # merge + subset[j1][:-2] += (subset[j2][:-2] + 1) + subset[j1][-2:] += subset[j2][-2:] + subset[j1][-2] += connection_all[k][i][2] + subset = np.delete(subset, j2, 0) + else: # as like found == 1 + subset[j1][indexB] = partBs[i] + subset[j1][-1] += 1 + subset[j1][-2] += candidate[partBs[i].astype(int), 2] + connection_all[k][i][2] + + # if find no partA in the subset, create a new subset + elif not found and k < 17: + row = -1 * np.ones(20) + row[indexA] = partAs[i] + row[indexB] = partBs[i] + row[-1] = 2 + row[-2] = sum(candidate[connection_all[k][i, :2].astype(int), 2]) + connection_all[k][i][2] + subset = np.vstack([subset, row]) + # delete some rows of subset which has few parts occur + deleteIdx = [] + for i in range(len(subset)): + if subset[i][-1] < 4 or subset[i][-2] / subset[i][-1] < 0.4: + deleteIdx.append(i) + subset = np.delete(subset, deleteIdx, axis=0) + + # subset: n*20 array, 0-17 is the index in candidate, 18 is the total score, 19 is the total parts + # candidate: x, y, score, id + return candidate, subset + +if __name__ == "__main__": + body_estimation = Body(os.path.join(models_path, "openpose", "body_pose_model.pth")) + + test_image = '../images/ski.jpg' + oriImg = cv2.imread(test_image) # B,G,R order + candidate, subset = body_estimation(oriImg) + canvas = util.draw_bodypose(oriImg, candidate, subset) + plt.imshow(canvas[:, :, [2, 1, 0]]) + plt.show() diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/hand.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/hand.py new file mode 100644 index 0000000000000000000000000000000000000000..b44baa35e4b266e5da64eb095d9c2cead09e71b6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/hand.py @@ -0,0 +1,87 @@ +import cv2 +import json +import numpy as np +import math +import time +from scipy.ndimage.filters import gaussian_filter +import matplotlib.pyplot as plt +import matplotlib +import torch +from skimage.measure import label + +from .model import handpose_model +from . import util +from modules import devices +from modules.paths import models_path + +class Hand(object): + def __init__(self, model_path): + self.model = handpose_model() + self.model = self.model.to(devices.get_device_for("controlnet")) + model_dict = util.transfer(self.model, torch.load(model_path)) + self.model.load_state_dict(model_dict) + self.model.eval() + + def __call__(self, oriImg): + self.model = self.model.to(devices.get_device_for("controlnet")) + + scale_search = [0.5, 1.0, 1.5, 2.0] + # scale_search = [0.5] + boxsize = 368 + stride = 8 + padValue = 128 + thre = 0.05 + multiplier = [x * boxsize / oriImg.shape[0] for x in scale_search] + heatmap_avg = np.zeros((oriImg.shape[0], oriImg.shape[1], 22)) + # paf_avg = np.zeros((oriImg.shape[0], oriImg.shape[1], 38)) + + for m in range(len(multiplier)): + scale = multiplier[m] + imageToTest = cv2.resize(oriImg, (0, 0), fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC) + imageToTest_padded, pad = util.padRightDownCorner(imageToTest, stride, padValue) + im = np.transpose(np.float32(imageToTest_padded[:, :, :, np.newaxis]), (3, 2, 0, 1)) / 256 - 0.5 + im = np.ascontiguousarray(im) + + data = torch.from_numpy(im).float() + data = data.to(devices.get_device_for("controlnet")) + # data = data.permute([2, 0, 1]).unsqueeze(0).float() + with torch.no_grad(): + output = self.model(data).cpu().numpy() + # output = self.model(data).numpy()q + + # extract outputs, resize, and remove padding + heatmap = np.transpose(np.squeeze(output), (1, 2, 0)) # output 1 is heatmaps + heatmap = cv2.resize(heatmap, (0, 0), fx=stride, fy=stride, interpolation=cv2.INTER_CUBIC) + heatmap = heatmap[:imageToTest_padded.shape[0] - pad[2], :imageToTest_padded.shape[1] - pad[3], :] + heatmap = cv2.resize(heatmap, (oriImg.shape[1], oriImg.shape[0]), interpolation=cv2.INTER_CUBIC) + + heatmap_avg += heatmap / len(multiplier) + + all_peaks = [] + for part in range(21): + map_ori = heatmap_avg[:, :, part] + one_heatmap = gaussian_filter(map_ori, sigma=3) + binary = np.ascontiguousarray(one_heatmap > thre, dtype=np.uint8) + # 全部小于阈值 + if np.sum(binary) == 0: + all_peaks.append([0, 0]) + continue + label_img, label_numbers = label(binary, return_num=True, connectivity=binary.ndim) + max_index = np.argmax([np.sum(map_ori[label_img == i]) for i in range(1, label_numbers + 1)]) + 1 + label_img[label_img != max_index] = 0 + map_ori[label_img == 0] = 0 + + y, x = util.npmax(map_ori) + all_peaks.append([x, y]) + return np.array(all_peaks) + +if __name__ == "__main__": + hand_estimation = Hand(os.path.join(models_path, "openpose", "hand_pose_model.pth")) + + # test_image = '../images/hand.jpg' + test_image = '../images/hand.jpg' + oriImg = cv2.imread(test_image) # B,G,R order + peaks = hand_estimation(oriImg) + canvas = util.draw_handpose(oriImg, peaks, True) + cv2.imshow('', canvas) + cv2.waitKey(0) \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/model.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/model.py new file mode 100644 index 0000000000000000000000000000000000000000..5dfc80de827a17beccb9b0f3f7588545be78c9de --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/model.py @@ -0,0 +1,219 @@ +import torch +from collections import OrderedDict + +import torch +import torch.nn as nn + +def make_layers(block, no_relu_layers): + layers = [] + for layer_name, v in block.items(): + if 'pool' in layer_name: + layer = nn.MaxPool2d(kernel_size=v[0], stride=v[1], + padding=v[2]) + layers.append((layer_name, layer)) + else: + conv2d = nn.Conv2d(in_channels=v[0], out_channels=v[1], + kernel_size=v[2], stride=v[3], + padding=v[4]) + layers.append((layer_name, conv2d)) + if layer_name not in no_relu_layers: + layers.append(('relu_'+layer_name, nn.ReLU(inplace=True))) + + return nn.Sequential(OrderedDict(layers)) + +class bodypose_model(nn.Module): + def __init__(self): + super(bodypose_model, self).__init__() + + # these layers have no relu layer + no_relu_layers = ['conv5_5_CPM_L1', 'conv5_5_CPM_L2', 'Mconv7_stage2_L1',\ + 'Mconv7_stage2_L2', 'Mconv7_stage3_L1', 'Mconv7_stage3_L2',\ + 'Mconv7_stage4_L1', 'Mconv7_stage4_L2', 'Mconv7_stage5_L1',\ + 'Mconv7_stage5_L2', 'Mconv7_stage6_L1', 'Mconv7_stage6_L1'] + blocks = {} + block0 = OrderedDict([ + ('conv1_1', [3, 64, 3, 1, 1]), + ('conv1_2', [64, 64, 3, 1, 1]), + ('pool1_stage1', [2, 2, 0]), + ('conv2_1', [64, 128, 3, 1, 1]), + ('conv2_2', [128, 128, 3, 1, 1]), + ('pool2_stage1', [2, 2, 0]), + ('conv3_1', [128, 256, 3, 1, 1]), + ('conv3_2', [256, 256, 3, 1, 1]), + ('conv3_3', [256, 256, 3, 1, 1]), + ('conv3_4', [256, 256, 3, 1, 1]), + ('pool3_stage1', [2, 2, 0]), + ('conv4_1', [256, 512, 3, 1, 1]), + ('conv4_2', [512, 512, 3, 1, 1]), + ('conv4_3_CPM', [512, 256, 3, 1, 1]), + ('conv4_4_CPM', [256, 128, 3, 1, 1]) + ]) + + + # Stage 1 + block1_1 = OrderedDict([ + ('conv5_1_CPM_L1', [128, 128, 3, 1, 1]), + ('conv5_2_CPM_L1', [128, 128, 3, 1, 1]), + ('conv5_3_CPM_L1', [128, 128, 3, 1, 1]), + ('conv5_4_CPM_L1', [128, 512, 1, 1, 0]), + ('conv5_5_CPM_L1', [512, 38, 1, 1, 0]) + ]) + + block1_2 = OrderedDict([ + ('conv5_1_CPM_L2', [128, 128, 3, 1, 1]), + ('conv5_2_CPM_L2', [128, 128, 3, 1, 1]), + ('conv5_3_CPM_L2', [128, 128, 3, 1, 1]), + ('conv5_4_CPM_L2', [128, 512, 1, 1, 0]), + ('conv5_5_CPM_L2', [512, 19, 1, 1, 0]) + ]) + blocks['block1_1'] = block1_1 + blocks['block1_2'] = block1_2 + + self.model0 = make_layers(block0, no_relu_layers) + + # Stages 2 - 6 + for i in range(2, 7): + blocks['block%d_1' % i] = OrderedDict([ + ('Mconv1_stage%d_L1' % i, [185, 128, 7, 1, 3]), + ('Mconv2_stage%d_L1' % i, [128, 128, 7, 1, 3]), + ('Mconv3_stage%d_L1' % i, [128, 128, 7, 1, 3]), + ('Mconv4_stage%d_L1' % i, [128, 128, 7, 1, 3]), + ('Mconv5_stage%d_L1' % i, [128, 128, 7, 1, 3]), + ('Mconv6_stage%d_L1' % i, [128, 128, 1, 1, 0]), + ('Mconv7_stage%d_L1' % i, [128, 38, 1, 1, 0]) + ]) + + blocks['block%d_2' % i] = OrderedDict([ + ('Mconv1_stage%d_L2' % i, [185, 128, 7, 1, 3]), + ('Mconv2_stage%d_L2' % i, [128, 128, 7, 1, 3]), + ('Mconv3_stage%d_L2' % i, [128, 128, 7, 1, 3]), + ('Mconv4_stage%d_L2' % i, [128, 128, 7, 1, 3]), + ('Mconv5_stage%d_L2' % i, [128, 128, 7, 1, 3]), + ('Mconv6_stage%d_L2' % i, [128, 128, 1, 1, 0]), + ('Mconv7_stage%d_L2' % i, [128, 19, 1, 1, 0]) + ]) + + for k in blocks.keys(): + blocks[k] = make_layers(blocks[k], no_relu_layers) + + self.model1_1 = blocks['block1_1'] + self.model2_1 = blocks['block2_1'] + self.model3_1 = blocks['block3_1'] + self.model4_1 = blocks['block4_1'] + self.model5_1 = blocks['block5_1'] + self.model6_1 = blocks['block6_1'] + + self.model1_2 = blocks['block1_2'] + self.model2_2 = blocks['block2_2'] + self.model3_2 = blocks['block3_2'] + self.model4_2 = blocks['block4_2'] + self.model5_2 = blocks['block5_2'] + self.model6_2 = blocks['block6_2'] + + + def forward(self, x): + + out1 = self.model0(x) + + out1_1 = self.model1_1(out1) + out1_2 = self.model1_2(out1) + out2 = torch.cat([out1_1, out1_2, out1], 1) + + out2_1 = self.model2_1(out2) + out2_2 = self.model2_2(out2) + out3 = torch.cat([out2_1, out2_2, out1], 1) + + out3_1 = self.model3_1(out3) + out3_2 = self.model3_2(out3) + out4 = torch.cat([out3_1, out3_2, out1], 1) + + out4_1 = self.model4_1(out4) + out4_2 = self.model4_2(out4) + out5 = torch.cat([out4_1, out4_2, out1], 1) + + out5_1 = self.model5_1(out5) + out5_2 = self.model5_2(out5) + out6 = torch.cat([out5_1, out5_2, out1], 1) + + out6_1 = self.model6_1(out6) + out6_2 = self.model6_2(out6) + + return out6_1, out6_2 + +class handpose_model(nn.Module): + def __init__(self): + super(handpose_model, self).__init__() + + # these layers have no relu layer + no_relu_layers = ['conv6_2_CPM', 'Mconv7_stage2', 'Mconv7_stage3',\ + 'Mconv7_stage4', 'Mconv7_stage5', 'Mconv7_stage6'] + # stage 1 + block1_0 = OrderedDict([ + ('conv1_1', [3, 64, 3, 1, 1]), + ('conv1_2', [64, 64, 3, 1, 1]), + ('pool1_stage1', [2, 2, 0]), + ('conv2_1', [64, 128, 3, 1, 1]), + ('conv2_2', [128, 128, 3, 1, 1]), + ('pool2_stage1', [2, 2, 0]), + ('conv3_1', [128, 256, 3, 1, 1]), + ('conv3_2', [256, 256, 3, 1, 1]), + ('conv3_3', [256, 256, 3, 1, 1]), + ('conv3_4', [256, 256, 3, 1, 1]), + ('pool3_stage1', [2, 2, 0]), + ('conv4_1', [256, 512, 3, 1, 1]), + ('conv4_2', [512, 512, 3, 1, 1]), + ('conv4_3', [512, 512, 3, 1, 1]), + ('conv4_4', [512, 512, 3, 1, 1]), + ('conv5_1', [512, 512, 3, 1, 1]), + ('conv5_2', [512, 512, 3, 1, 1]), + ('conv5_3_CPM', [512, 128, 3, 1, 1]) + ]) + + block1_1 = OrderedDict([ + ('conv6_1_CPM', [128, 512, 1, 1, 0]), + ('conv6_2_CPM', [512, 22, 1, 1, 0]) + ]) + + blocks = {} + blocks['block1_0'] = block1_0 + blocks['block1_1'] = block1_1 + + # stage 2-6 + for i in range(2, 7): + blocks['block%d' % i] = OrderedDict([ + ('Mconv1_stage%d' % i, [150, 128, 7, 1, 3]), + ('Mconv2_stage%d' % i, [128, 128, 7, 1, 3]), + ('Mconv3_stage%d' % i, [128, 128, 7, 1, 3]), + ('Mconv4_stage%d' % i, [128, 128, 7, 1, 3]), + ('Mconv5_stage%d' % i, [128, 128, 7, 1, 3]), + ('Mconv6_stage%d' % i, [128, 128, 1, 1, 0]), + ('Mconv7_stage%d' % i, [128, 22, 1, 1, 0]) + ]) + + for k in blocks.keys(): + blocks[k] = make_layers(blocks[k], no_relu_layers) + + self.model1_0 = blocks['block1_0'] + self.model1_1 = blocks['block1_1'] + self.model2 = blocks['block2'] + self.model3 = blocks['block3'] + self.model4 = blocks['block4'] + self.model5 = blocks['block5'] + self.model6 = blocks['block6'] + + def forward(self, x): + out1_0 = self.model1_0(x) + out1_1 = self.model1_1(out1_0) + concat_stage2 = torch.cat([out1_1, out1_0], 1) + out_stage2 = self.model2(concat_stage2) + concat_stage3 = torch.cat([out_stage2, out1_0], 1) + out_stage3 = self.model3(concat_stage3) + concat_stage4 = torch.cat([out_stage3, out1_0], 1) + out_stage4 = self.model4(concat_stage4) + concat_stage5 = torch.cat([out_stage4, out1_0], 1) + out_stage5 = self.model5(concat_stage5) + concat_stage6 = torch.cat([out_stage5, out1_0], 1) + out_stage6 = self.model6(concat_stage6) + return out_stage6 + + diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/util.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/util.py new file mode 100644 index 0000000000000000000000000000000000000000..6f91ae0e65abaf0cbd62d803f56498991141e61b --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/openpose/util.py @@ -0,0 +1,164 @@ +import math +import numpy as np +import matplotlib +import cv2 + + +def padRightDownCorner(img, stride, padValue): + h = img.shape[0] + w = img.shape[1] + + pad = 4 * [None] + pad[0] = 0 # up + pad[1] = 0 # left + pad[2] = 0 if (h % stride == 0) else stride - (h % stride) # down + pad[3] = 0 if (w % stride == 0) else stride - (w % stride) # right + + img_padded = img + pad_up = np.tile(img_padded[0:1, :, :]*0 + padValue, (pad[0], 1, 1)) + img_padded = np.concatenate((pad_up, img_padded), axis=0) + pad_left = np.tile(img_padded[:, 0:1, :]*0 + padValue, (1, pad[1], 1)) + img_padded = np.concatenate((pad_left, img_padded), axis=1) + pad_down = np.tile(img_padded[-2:-1, :, :]*0 + padValue, (pad[2], 1, 1)) + img_padded = np.concatenate((img_padded, pad_down), axis=0) + pad_right = np.tile(img_padded[:, -2:-1, :]*0 + padValue, (1, pad[3], 1)) + img_padded = np.concatenate((img_padded, pad_right), axis=1) + + return img_padded, pad + +# transfer caffe model to pytorch which will match the layer name +def transfer(model, model_weights): + transfered_model_weights = {} + for weights_name in model.state_dict().keys(): + transfered_model_weights[weights_name] = model_weights['.'.join(weights_name.split('.')[1:])] + return transfered_model_weights + +# draw the body keypoint and lims +def draw_bodypose(canvas, candidate, subset): + stickwidth = 4 + limbSeq = [[2, 3], [2, 6], [3, 4], [4, 5], [6, 7], [7, 8], [2, 9], [9, 10], \ + [10, 11], [2, 12], [12, 13], [13, 14], [2, 1], [1, 15], [15, 17], \ + [1, 16], [16, 18], [3, 17], [6, 18]] + + colors = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0], [85, 255, 0], [0, 255, 0], \ + [0, 255, 85], [0, 255, 170], [0, 255, 255], [0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], \ + [170, 0, 255], [255, 0, 255], [255, 0, 170], [255, 0, 85]] + for i in range(18): + for n in range(len(subset)): + index = int(subset[n][i]) + if index == -1: + continue + x, y = candidate[index][0:2] + cv2.circle(canvas, (int(x), int(y)), 4, colors[i], thickness=-1) + for i in range(17): + for n in range(len(subset)): + index = subset[n][np.array(limbSeq[i]) - 1] + if -1 in index: + continue + cur_canvas = canvas.copy() + Y = candidate[index.astype(int), 0] + X = candidate[index.astype(int), 1] + mX = np.mean(X) + mY = np.mean(Y) + length = ((X[0] - X[1]) ** 2 + (Y[0] - Y[1]) ** 2) ** 0.5 + angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1])) + polygon = cv2.ellipse2Poly((int(mY), int(mX)), (int(length / 2), stickwidth), int(angle), 0, 360, 1) + cv2.fillConvexPoly(cur_canvas, polygon, colors[i]) + canvas = cv2.addWeighted(canvas, 0.4, cur_canvas, 0.6, 0) + # plt.imsave("preview.jpg", canvas[:, :, [2, 1, 0]]) + # plt.imshow(canvas[:, :, [2, 1, 0]]) + return canvas + + +# image drawed by opencv is not good. +def draw_handpose(canvas, all_hand_peaks, show_number=False): + edges = [[0, 1], [1, 2], [2, 3], [3, 4], [0, 5], [5, 6], [6, 7], [7, 8], [0, 9], [9, 10], \ + [10, 11], [11, 12], [0, 13], [13, 14], [14, 15], [15, 16], [0, 17], [17, 18], [18, 19], [19, 20]] + + for peaks in all_hand_peaks: + for ie, e in enumerate(edges): + if np.sum(np.all(peaks[e], axis=1)==0)==0: + x1, y1 = peaks[e[0]] + x2, y2 = peaks[e[1]] + cv2.line(canvas, (x1, y1), (x2, y2), matplotlib.colors.hsv_to_rgb([ie/float(len(edges)), 1.0, 1.0])*255, thickness=2) + + for i, keyponit in enumerate(peaks): + x, y = keyponit + cv2.circle(canvas, (x, y), 4, (0, 0, 255), thickness=-1) + if show_number: + cv2.putText(canvas, str(i), (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.3, (0, 0, 0), lineType=cv2.LINE_AA) + return canvas + +# detect hand according to body pose keypoints +# please refer to https://github.com/CMU-Perceptual-Computing-Lab/openpose/blob/master/src/openpose/hand/handDetector.cpp +def handDetect(candidate, subset, oriImg): + # right hand: wrist 4, elbow 3, shoulder 2 + # left hand: wrist 7, elbow 6, shoulder 5 + ratioWristElbow = 0.33 + detect_result = [] + image_height, image_width = oriImg.shape[0:2] + for person in subset.astype(int): + # if any of three not detected + has_left = np.sum(person[[5, 6, 7]] == -1) == 0 + has_right = np.sum(person[[2, 3, 4]] == -1) == 0 + if not (has_left or has_right): + continue + hands = [] + #left hand + if has_left: + left_shoulder_index, left_elbow_index, left_wrist_index = person[[5, 6, 7]] + x1, y1 = candidate[left_shoulder_index][:2] + x2, y2 = candidate[left_elbow_index][:2] + x3, y3 = candidate[left_wrist_index][:2] + hands.append([x1, y1, x2, y2, x3, y3, True]) + # right hand + if has_right: + right_shoulder_index, right_elbow_index, right_wrist_index = person[[2, 3, 4]] + x1, y1 = candidate[right_shoulder_index][:2] + x2, y2 = candidate[right_elbow_index][:2] + x3, y3 = candidate[right_wrist_index][:2] + hands.append([x1, y1, x2, y2, x3, y3, False]) + + for x1, y1, x2, y2, x3, y3, is_left in hands: + # pos_hand = pos_wrist + ratio * (pos_wrist - pos_elbox) = (1 + ratio) * pos_wrist - ratio * pos_elbox + # handRectangle.x = posePtr[wrist*3] + ratioWristElbow * (posePtr[wrist*3] - posePtr[elbow*3]); + # handRectangle.y = posePtr[wrist*3+1] + ratioWristElbow * (posePtr[wrist*3+1] - posePtr[elbow*3+1]); + # const auto distanceWristElbow = getDistance(poseKeypoints, person, wrist, elbow); + # const auto distanceElbowShoulder = getDistance(poseKeypoints, person, elbow, shoulder); + # handRectangle.width = 1.5f * fastMax(distanceWristElbow, 0.9f * distanceElbowShoulder); + x = x3 + ratioWristElbow * (x3 - x2) + y = y3 + ratioWristElbow * (y3 - y2) + distanceWristElbow = math.sqrt((x3 - x2) ** 2 + (y3 - y2) ** 2) + distanceElbowShoulder = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) + width = 1.5 * max(distanceWristElbow, 0.9 * distanceElbowShoulder) + # x-y refers to the center --> offset to topLeft point + # handRectangle.x -= handRectangle.width / 2.f; + # handRectangle.y -= handRectangle.height / 2.f; + x -= width / 2 + y -= width / 2 # width = height + # overflow the image + if x < 0: x = 0 + if y < 0: y = 0 + width1 = width + width2 = width + if x + width > image_width: width1 = image_width - x + if y + width > image_height: width2 = image_height - y + width = min(width1, width2) + # the max hand box value is 20 pixels + if width >= 20: + detect_result.append([int(x), int(y), int(width), is_left]) + + ''' + return value: [[x, y, w, True if left hand else False]]. + width=height since the network require squared input. + x, y is the coordinate of top left + ''' + return detect_result + +# get max index of 2d array +def npmax(array): + arrayindex = array.argmax(1) + arrayvalue = array.max(1) + i = arrayvalue.argmax() + j = arrayindex[i] + return i, j diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/pidinet/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/pidinet/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..02554d532ebd18e22e1fa33f2e6d18aebc1f7fd9 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/pidinet/__init__.py @@ -0,0 +1,46 @@ +import os +import torch +import numpy as np +from einops import rearrange +from annotator.pidinet.model import pidinet +from modules import devices +from modules.paths import models_path +from scripts.utils import load_state_dict + +netNetwork = None +remote_model_path = "https://github.com/TencentARC/T2I-Adapter/raw/main/models/table5_pidinet.pth" +modeldir = os.path.join(models_path, "pidinet") +old_modeldir = os.path.dirname(os.path.realpath(__file__)) + +def apply_pidinet(input_image): + global netNetwork + if netNetwork is None: + modelpath = os.path.join(modeldir, "table5_pidinet.pth") + old_modelpath = os.path.join(old_modeldir, "table5_pidinet.pth") + if os.path.exists(old_modelpath): + modelpath = old_modelpath + elif not os.path.exists(modelpath): + from basicsr.utils.download_util import load_file_from_url + load_file_from_url(remote_model_path, model_dir=modeldir) + netNetwork = pidinet() + ckp = load_state_dict(modelpath) + netNetwork.load_state_dict({k.replace('module.',''):v for k, v in ckp.items()}) + + netNetwork = netNetwork.to(devices.get_device_for("controlnet")) + netNetwork.eval() + assert input_image.ndim == 3 + input_image = input_image[:, :, ::-1].copy() + with torch.no_grad(): + image_pidi = torch.from_numpy(input_image).float().to(devices.get_device_for("controlnet")) + image_pidi = image_pidi / 255.0 + image_pidi = rearrange(image_pidi, 'h w c -> 1 c h w') + edge = netNetwork(image_pidi)[-1] + edge = edge>0.5 + edge = (edge * 255.0).clip(0, 255).cpu().numpy().astype(np.uint8) + + return edge[0][0] + +def unload_pid_model(): + global netNetwork + if netNetwork is not None: + netNetwork.cpu() \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/pidinet/model.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/pidinet/model.py new file mode 100644 index 0000000000000000000000000000000000000000..eaed557dea23d56f2aea6045f6a350bee03083ce --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/pidinet/model.py @@ -0,0 +1,653 @@ +""" +Author: Zhuo Su, Wenzhe Liu +Date: Feb 18, 2021 +""" + +import math + +import cv2 +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +from basicsr.utils import img2tensor + +nets = { + 'baseline': { + 'layer0': 'cv', + 'layer1': 'cv', + 'layer2': 'cv', + 'layer3': 'cv', + 'layer4': 'cv', + 'layer5': 'cv', + 'layer6': 'cv', + 'layer7': 'cv', + 'layer8': 'cv', + 'layer9': 'cv', + 'layer10': 'cv', + 'layer11': 'cv', + 'layer12': 'cv', + 'layer13': 'cv', + 'layer14': 'cv', + 'layer15': 'cv', + }, + 'c-v15': { + 'layer0': 'cd', + 'layer1': 'cv', + 'layer2': 'cv', + 'layer3': 'cv', + 'layer4': 'cv', + 'layer5': 'cv', + 'layer6': 'cv', + 'layer7': 'cv', + 'layer8': 'cv', + 'layer9': 'cv', + 'layer10': 'cv', + 'layer11': 'cv', + 'layer12': 'cv', + 'layer13': 'cv', + 'layer14': 'cv', + 'layer15': 'cv', + }, + 'a-v15': { + 'layer0': 'ad', + 'layer1': 'cv', + 'layer2': 'cv', + 'layer3': 'cv', + 'layer4': 'cv', + 'layer5': 'cv', + 'layer6': 'cv', + 'layer7': 'cv', + 'layer8': 'cv', + 'layer9': 'cv', + 'layer10': 'cv', + 'layer11': 'cv', + 'layer12': 'cv', + 'layer13': 'cv', + 'layer14': 'cv', + 'layer15': 'cv', + }, + 'r-v15': { + 'layer0': 'rd', + 'layer1': 'cv', + 'layer2': 'cv', + 'layer3': 'cv', + 'layer4': 'cv', + 'layer5': 'cv', + 'layer6': 'cv', + 'layer7': 'cv', + 'layer8': 'cv', + 'layer9': 'cv', + 'layer10': 'cv', + 'layer11': 'cv', + 'layer12': 'cv', + 'layer13': 'cv', + 'layer14': 'cv', + 'layer15': 'cv', + }, + 'cvvv4': { + 'layer0': 'cd', + 'layer1': 'cv', + 'layer2': 'cv', + 'layer3': 'cv', + 'layer4': 'cd', + 'layer5': 'cv', + 'layer6': 'cv', + 'layer7': 'cv', + 'layer8': 'cd', + 'layer9': 'cv', + 'layer10': 'cv', + 'layer11': 'cv', + 'layer12': 'cd', + 'layer13': 'cv', + 'layer14': 'cv', + 'layer15': 'cv', + }, + 'avvv4': { + 'layer0': 'ad', + 'layer1': 'cv', + 'layer2': 'cv', + 'layer3': 'cv', + 'layer4': 'ad', + 'layer5': 'cv', + 'layer6': 'cv', + 'layer7': 'cv', + 'layer8': 'ad', + 'layer9': 'cv', + 'layer10': 'cv', + 'layer11': 'cv', + 'layer12': 'ad', + 'layer13': 'cv', + 'layer14': 'cv', + 'layer15': 'cv', + }, + 'rvvv4': { + 'layer0': 'rd', + 'layer1': 'cv', + 'layer2': 'cv', + 'layer3': 'cv', + 'layer4': 'rd', + 'layer5': 'cv', + 'layer6': 'cv', + 'layer7': 'cv', + 'layer8': 'rd', + 'layer9': 'cv', + 'layer10': 'cv', + 'layer11': 'cv', + 'layer12': 'rd', + 'layer13': 'cv', + 'layer14': 'cv', + 'layer15': 'cv', + }, + 'cccv4': { + 'layer0': 'cd', + 'layer1': 'cd', + 'layer2': 'cd', + 'layer3': 'cv', + 'layer4': 'cd', + 'layer5': 'cd', + 'layer6': 'cd', + 'layer7': 'cv', + 'layer8': 'cd', + 'layer9': 'cd', + 'layer10': 'cd', + 'layer11': 'cv', + 'layer12': 'cd', + 'layer13': 'cd', + 'layer14': 'cd', + 'layer15': 'cv', + }, + 'aaav4': { + 'layer0': 'ad', + 'layer1': 'ad', + 'layer2': 'ad', + 'layer3': 'cv', + 'layer4': 'ad', + 'layer5': 'ad', + 'layer6': 'ad', + 'layer7': 'cv', + 'layer8': 'ad', + 'layer9': 'ad', + 'layer10': 'ad', + 'layer11': 'cv', + 'layer12': 'ad', + 'layer13': 'ad', + 'layer14': 'ad', + 'layer15': 'cv', + }, + 'rrrv4': { + 'layer0': 'rd', + 'layer1': 'rd', + 'layer2': 'rd', + 'layer3': 'cv', + 'layer4': 'rd', + 'layer5': 'rd', + 'layer6': 'rd', + 'layer7': 'cv', + 'layer8': 'rd', + 'layer9': 'rd', + 'layer10': 'rd', + 'layer11': 'cv', + 'layer12': 'rd', + 'layer13': 'rd', + 'layer14': 'rd', + 'layer15': 'cv', + }, + 'c16': { + 'layer0': 'cd', + 'layer1': 'cd', + 'layer2': 'cd', + 'layer3': 'cd', + 'layer4': 'cd', + 'layer5': 'cd', + 'layer6': 'cd', + 'layer7': 'cd', + 'layer8': 'cd', + 'layer9': 'cd', + 'layer10': 'cd', + 'layer11': 'cd', + 'layer12': 'cd', + 'layer13': 'cd', + 'layer14': 'cd', + 'layer15': 'cd', + }, + 'a16': { + 'layer0': 'ad', + 'layer1': 'ad', + 'layer2': 'ad', + 'layer3': 'ad', + 'layer4': 'ad', + 'layer5': 'ad', + 'layer6': 'ad', + 'layer7': 'ad', + 'layer8': 'ad', + 'layer9': 'ad', + 'layer10': 'ad', + 'layer11': 'ad', + 'layer12': 'ad', + 'layer13': 'ad', + 'layer14': 'ad', + 'layer15': 'ad', + }, + 'r16': { + 'layer0': 'rd', + 'layer1': 'rd', + 'layer2': 'rd', + 'layer3': 'rd', + 'layer4': 'rd', + 'layer5': 'rd', + 'layer6': 'rd', + 'layer7': 'rd', + 'layer8': 'rd', + 'layer9': 'rd', + 'layer10': 'rd', + 'layer11': 'rd', + 'layer12': 'rd', + 'layer13': 'rd', + 'layer14': 'rd', + 'layer15': 'rd', + }, + 'carv4': { + 'layer0': 'cd', + 'layer1': 'ad', + 'layer2': 'rd', + 'layer3': 'cv', + 'layer4': 'cd', + 'layer5': 'ad', + 'layer6': 'rd', + 'layer7': 'cv', + 'layer8': 'cd', + 'layer9': 'ad', + 'layer10': 'rd', + 'layer11': 'cv', + 'layer12': 'cd', + 'layer13': 'ad', + 'layer14': 'rd', + 'layer15': 'cv', + }, + } + +def createConvFunc(op_type): + assert op_type in ['cv', 'cd', 'ad', 'rd'], 'unknown op type: %s' % str(op_type) + if op_type == 'cv': + return F.conv2d + + if op_type == 'cd': + def func(x, weights, bias=None, stride=1, padding=0, dilation=1, groups=1): + assert dilation in [1, 2], 'dilation for cd_conv should be in 1 or 2' + assert weights.size(2) == 3 and weights.size(3) == 3, 'kernel size for cd_conv should be 3x3' + assert padding == dilation, 'padding for cd_conv set wrong' + + weights_c = weights.sum(dim=[2, 3], keepdim=True) + yc = F.conv2d(x, weights_c, stride=stride, padding=0, groups=groups) + y = F.conv2d(x, weights, bias, stride=stride, padding=padding, dilation=dilation, groups=groups) + return y - yc + return func + elif op_type == 'ad': + def func(x, weights, bias=None, stride=1, padding=0, dilation=1, groups=1): + assert dilation in [1, 2], 'dilation for ad_conv should be in 1 or 2' + assert weights.size(2) == 3 and weights.size(3) == 3, 'kernel size for ad_conv should be 3x3' + assert padding == dilation, 'padding for ad_conv set wrong' + + shape = weights.shape + weights = weights.view(shape[0], shape[1], -1) + weights_conv = (weights - weights[:, :, [3, 0, 1, 6, 4, 2, 7, 8, 5]]).view(shape) # clock-wise + y = F.conv2d(x, weights_conv, bias, stride=stride, padding=padding, dilation=dilation, groups=groups) + return y + return func + elif op_type == 'rd': + def func(x, weights, bias=None, stride=1, padding=0, dilation=1, groups=1): + assert dilation in [1, 2], 'dilation for rd_conv should be in 1 or 2' + assert weights.size(2) == 3 and weights.size(3) == 3, 'kernel size for rd_conv should be 3x3' + padding = 2 * dilation + + shape = weights.shape + if weights.is_cuda: + buffer = torch.cuda.FloatTensor(shape[0], shape[1], 5 * 5).fill_(0) + else: + buffer = torch.zeros(shape[0], shape[1], 5 * 5) + weights = weights.view(shape[0], shape[1], -1) + buffer[:, :, [0, 2, 4, 10, 14, 20, 22, 24]] = weights[:, :, 1:] + buffer[:, :, [6, 7, 8, 11, 13, 16, 17, 18]] = -weights[:, :, 1:] + buffer[:, :, 12] = 0 + buffer = buffer.view(shape[0], shape[1], 5, 5) + y = F.conv2d(x, buffer, bias, stride=stride, padding=padding, dilation=dilation, groups=groups) + return y + return func + else: + print('impossible to be here unless you force that') + return None + +class Conv2d(nn.Module): + def __init__(self, pdc, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=False): + super(Conv2d, self).__init__() + if in_channels % groups != 0: + raise ValueError('in_channels must be divisible by groups') + if out_channels % groups != 0: + raise ValueError('out_channels must be divisible by groups') + self.in_channels = in_channels + self.out_channels = out_channels + self.kernel_size = kernel_size + self.stride = stride + self.padding = padding + self.dilation = dilation + self.groups = groups + self.weight = nn.Parameter(torch.Tensor(out_channels, in_channels // groups, kernel_size, kernel_size)) + if bias: + self.bias = nn.Parameter(torch.Tensor(out_channels)) + else: + self.register_parameter('bias', None) + self.reset_parameters() + self.pdc = pdc + + def reset_parameters(self): + nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5)) + if self.bias is not None: + fan_in, _ = nn.init._calculate_fan_in_and_fan_out(self.weight) + bound = 1 / math.sqrt(fan_in) + nn.init.uniform_(self.bias, -bound, bound) + + def forward(self, input): + + return self.pdc(input, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups) + +class CSAM(nn.Module): + """ + Compact Spatial Attention Module + """ + def __init__(self, channels): + super(CSAM, self).__init__() + + mid_channels = 4 + self.relu1 = nn.ReLU() + self.conv1 = nn.Conv2d(channels, mid_channels, kernel_size=1, padding=0) + self.conv2 = nn.Conv2d(mid_channels, 1, kernel_size=3, padding=1, bias=False) + self.sigmoid = nn.Sigmoid() + nn.init.constant_(self.conv1.bias, 0) + + def forward(self, x): + y = self.relu1(x) + y = self.conv1(y) + y = self.conv2(y) + y = self.sigmoid(y) + + return x * y + +class CDCM(nn.Module): + """ + Compact Dilation Convolution based Module + """ + def __init__(self, in_channels, out_channels): + super(CDCM, self).__init__() + + self.relu1 = nn.ReLU() + self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=1, padding=0) + self.conv2_1 = nn.Conv2d(out_channels, out_channels, kernel_size=3, dilation=5, padding=5, bias=False) + self.conv2_2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, dilation=7, padding=7, bias=False) + self.conv2_3 = nn.Conv2d(out_channels, out_channels, kernel_size=3, dilation=9, padding=9, bias=False) + self.conv2_4 = nn.Conv2d(out_channels, out_channels, kernel_size=3, dilation=11, padding=11, bias=False) + nn.init.constant_(self.conv1.bias, 0) + + def forward(self, x): + x = self.relu1(x) + x = self.conv1(x) + x1 = self.conv2_1(x) + x2 = self.conv2_2(x) + x3 = self.conv2_3(x) + x4 = self.conv2_4(x) + return x1 + x2 + x3 + x4 + + +class MapReduce(nn.Module): + """ + Reduce feature maps into a single edge map + """ + def __init__(self, channels): + super(MapReduce, self).__init__() + self.conv = nn.Conv2d(channels, 1, kernel_size=1, padding=0) + nn.init.constant_(self.conv.bias, 0) + + def forward(self, x): + return self.conv(x) + + +class PDCBlock(nn.Module): + def __init__(self, pdc, inplane, ouplane, stride=1): + super(PDCBlock, self).__init__() + self.stride=stride + + self.stride=stride + if self.stride > 1: + self.pool = nn.MaxPool2d(kernel_size=2, stride=2) + self.shortcut = nn.Conv2d(inplane, ouplane, kernel_size=1, padding=0) + self.conv1 = Conv2d(pdc, inplane, inplane, kernel_size=3, padding=1, groups=inplane, bias=False) + self.relu2 = nn.ReLU() + self.conv2 = nn.Conv2d(inplane, ouplane, kernel_size=1, padding=0, bias=False) + + def forward(self, x): + if self.stride > 1: + x = self.pool(x) + y = self.conv1(x) + y = self.relu2(y) + y = self.conv2(y) + if self.stride > 1: + x = self.shortcut(x) + y = y + x + return y + +class PDCBlock_converted(nn.Module): + """ + CPDC, APDC can be converted to vanilla 3x3 convolution + RPDC can be converted to vanilla 5x5 convolution + """ + def __init__(self, pdc, inplane, ouplane, stride=1): + super(PDCBlock_converted, self).__init__() + self.stride=stride + + if self.stride > 1: + self.pool = nn.MaxPool2d(kernel_size=2, stride=2) + self.shortcut = nn.Conv2d(inplane, ouplane, kernel_size=1, padding=0) + if pdc == 'rd': + self.conv1 = nn.Conv2d(inplane, inplane, kernel_size=5, padding=2, groups=inplane, bias=False) + else: + self.conv1 = nn.Conv2d(inplane, inplane, kernel_size=3, padding=1, groups=inplane, bias=False) + self.relu2 = nn.ReLU() + self.conv2 = nn.Conv2d(inplane, ouplane, kernel_size=1, padding=0, bias=False) + + def forward(self, x): + if self.stride > 1: + x = self.pool(x) + y = self.conv1(x) + y = self.relu2(y) + y = self.conv2(y) + if self.stride > 1: + x = self.shortcut(x) + y = y + x + return y + +class PiDiNet(nn.Module): + def __init__(self, inplane, pdcs, dil=None, sa=False, convert=False): + super(PiDiNet, self).__init__() + self.sa = sa + if dil is not None: + assert isinstance(dil, int), 'dil should be an int' + self.dil = dil + + self.fuseplanes = [] + + self.inplane = inplane + if convert: + if pdcs[0] == 'rd': + init_kernel_size = 5 + init_padding = 2 + else: + init_kernel_size = 3 + init_padding = 1 + self.init_block = nn.Conv2d(3, self.inplane, + kernel_size=init_kernel_size, padding=init_padding, bias=False) + block_class = PDCBlock_converted + else: + self.init_block = Conv2d(pdcs[0], 3, self.inplane, kernel_size=3, padding=1) + block_class = PDCBlock + + self.block1_1 = block_class(pdcs[1], self.inplane, self.inplane) + self.block1_2 = block_class(pdcs[2], self.inplane, self.inplane) + self.block1_3 = block_class(pdcs[3], self.inplane, self.inplane) + self.fuseplanes.append(self.inplane) # C + + inplane = self.inplane + self.inplane = self.inplane * 2 + self.block2_1 = block_class(pdcs[4], inplane, self.inplane, stride=2) + self.block2_2 = block_class(pdcs[5], self.inplane, self.inplane) + self.block2_3 = block_class(pdcs[6], self.inplane, self.inplane) + self.block2_4 = block_class(pdcs[7], self.inplane, self.inplane) + self.fuseplanes.append(self.inplane) # 2C + + inplane = self.inplane + self.inplane = self.inplane * 2 + self.block3_1 = block_class(pdcs[8], inplane, self.inplane, stride=2) + self.block3_2 = block_class(pdcs[9], self.inplane, self.inplane) + self.block3_3 = block_class(pdcs[10], self.inplane, self.inplane) + self.block3_4 = block_class(pdcs[11], self.inplane, self.inplane) + self.fuseplanes.append(self.inplane) # 4C + + self.block4_1 = block_class(pdcs[12], self.inplane, self.inplane, stride=2) + self.block4_2 = block_class(pdcs[13], self.inplane, self.inplane) + self.block4_3 = block_class(pdcs[14], self.inplane, self.inplane) + self.block4_4 = block_class(pdcs[15], self.inplane, self.inplane) + self.fuseplanes.append(self.inplane) # 4C + + self.conv_reduces = nn.ModuleList() + if self.sa and self.dil is not None: + self.attentions = nn.ModuleList() + self.dilations = nn.ModuleList() + for i in range(4): + self.dilations.append(CDCM(self.fuseplanes[i], self.dil)) + self.attentions.append(CSAM(self.dil)) + self.conv_reduces.append(MapReduce(self.dil)) + elif self.sa: + self.attentions = nn.ModuleList() + for i in range(4): + self.attentions.append(CSAM(self.fuseplanes[i])) + self.conv_reduces.append(MapReduce(self.fuseplanes[i])) + elif self.dil is not None: + self.dilations = nn.ModuleList() + for i in range(4): + self.dilations.append(CDCM(self.fuseplanes[i], self.dil)) + self.conv_reduces.append(MapReduce(self.dil)) + else: + for i in range(4): + self.conv_reduces.append(MapReduce(self.fuseplanes[i])) + + self.classifier = nn.Conv2d(4, 1, kernel_size=1) # has bias + nn.init.constant_(self.classifier.weight, 0.25) + nn.init.constant_(self.classifier.bias, 0) + + # print('initialization done') + + def get_weights(self): + conv_weights = [] + bn_weights = [] + relu_weights = [] + for pname, p in self.named_parameters(): + if 'bn' in pname: + bn_weights.append(p) + elif 'relu' in pname: + relu_weights.append(p) + else: + conv_weights.append(p) + + return conv_weights, bn_weights, relu_weights + + def forward(self, x): + H, W = x.size()[2:] + + x = self.init_block(x) + + x1 = self.block1_1(x) + x1 = self.block1_2(x1) + x1 = self.block1_3(x1) + + x2 = self.block2_1(x1) + x2 = self.block2_2(x2) + x2 = self.block2_3(x2) + x2 = self.block2_4(x2) + + x3 = self.block3_1(x2) + x3 = self.block3_2(x3) + x3 = self.block3_3(x3) + x3 = self.block3_4(x3) + + x4 = self.block4_1(x3) + x4 = self.block4_2(x4) + x4 = self.block4_3(x4) + x4 = self.block4_4(x4) + + x_fuses = [] + if self.sa and self.dil is not None: + for i, xi in enumerate([x1, x2, x3, x4]): + x_fuses.append(self.attentions[i](self.dilations[i](xi))) + elif self.sa: + for i, xi in enumerate([x1, x2, x3, x4]): + x_fuses.append(self.attentions[i](xi)) + elif self.dil is not None: + for i, xi in enumerate([x1, x2, x3, x4]): + x_fuses.append(self.dilations[i](xi)) + else: + x_fuses = [x1, x2, x3, x4] + + e1 = self.conv_reduces[0](x_fuses[0]) + e1 = F.interpolate(e1, (H, W), mode="bilinear", align_corners=False) + + e2 = self.conv_reduces[1](x_fuses[1]) + e2 = F.interpolate(e2, (H, W), mode="bilinear", align_corners=False) + + e3 = self.conv_reduces[2](x_fuses[2]) + e3 = F.interpolate(e3, (H, W), mode="bilinear", align_corners=False) + + e4 = self.conv_reduces[3](x_fuses[3]) + e4 = F.interpolate(e4, (H, W), mode="bilinear", align_corners=False) + + outputs = [e1, e2, e3, e4] + + output = self.classifier(torch.cat(outputs, dim=1)) + #if not self.training: + # return torch.sigmoid(output) + + outputs.append(output) + outputs = [torch.sigmoid(r) for r in outputs] + return outputs + +def config_model(model): + model_options = list(nets.keys()) + assert model in model_options, \ + 'unrecognized model, please choose from %s' % str(model_options) + + # print(str(nets[model])) + + pdcs = [] + for i in range(16): + layer_name = 'layer%d' % i + op = nets[model][layer_name] + pdcs.append(createConvFunc(op)) + + return pdcs + +def pidinet(): + pdcs = config_model('carv4') + dil = 24 #if args.dil else None + return PiDiNet(60, pdcs, dil=dil, sa=True) + + +if __name__ == '__main__': + model = pidinet() + ckp = torch.load('table5_pidinet.pth')['state_dict'] + model.load_state_dict({k.replace('module.',''):v for k, v in ckp.items()}) + im = cv2.imread('examples/test_my/cat_v4.png') + im = img2tensor(im).unsqueeze(0)/255. + res = model(im)[-1] + res = res>0.5 + res = res.float() + res = (res[0,0].cpu().data.numpy()*255.).astype(np.uint8) + print(res.shape) + cv2.imwrite('edge.png', res) \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9ee8b4edaf1c2153fb9436706e572f5aa49d9803 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/__init__.py @@ -0,0 +1,56 @@ +import os +from modules.paths import models_path +from modules import devices +from annotator.uniformer.inference import init_segmentor, inference_segmentor, show_result_pyplot + +try: + from mmseg.core.evaluation import get_palette +except ImportError: + from annotator.mmpkg.mmseg.core.evaluation import get_palette + +modeldir = os.path.join(models_path, "uniformer") +checkpoint_file = "https://huggingface.co/lllyasviel/ControlNet/resolve/main/annotator/ckpts/upernet_global_small.pth" +config_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "upernet_global_small.py") +old_modeldir = os.path.dirname(os.path.realpath(__file__)) +model = None + +def unload_uniformer_model(): + global model + if model is not None: + model = model.cpu() + +def apply_uniformer(img): + global model + if model is None: + modelpath = os.path.join(modeldir, "upernet_global_small.pth") + old_modelpath = os.path.join(old_modeldir, "upernet_global_small.pth") + if os.path.exists(old_modelpath): + modelpath = old_modelpath + elif not os.path.exists(modelpath): + from basicsr.utils.download_util import load_file_from_url + load_file_from_url(checkpoint_file, model_dir=modeldir) + + model = init_segmentor(config_file, modelpath) + model = model.to(devices.get_device_for("controlnet")) + + if devices.get_device_for("controlnet").type == 'mps': + # adaptive_avg_pool2d can fail on MPS, workaround with CPU + import torch.nn.functional + + orig_adaptive_avg_pool2d = torch.nn.functional.adaptive_avg_pool2d + def cpu_if_exception(input, *args, **kwargs): + try: + return orig_adaptive_avg_pool2d(input, *args, **kwargs) + except: + return orig_adaptive_avg_pool2d(input.cpu(), *args, **kwargs).to(input.device) + + try: + torch.nn.functional.adaptive_avg_pool2d = cpu_if_exception + result = inference_segmentor(model, img) + finally: + torch.nn.functional.adaptive_avg_pool2d = orig_adaptive_avg_pool2d + else: + result = inference_segmentor(model, img) + + res_img = show_result_pyplot(model, img, result, get_palette('ade'), opacity=1) + return res_img diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/ade20k.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/ade20k.py new file mode 100644 index 0000000000000000000000000000000000000000..efc8b4bb20c981f3db6df7eb52b3dc0744c94cc0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/ade20k.py @@ -0,0 +1,54 @@ +# dataset settings +dataset_type = 'ADE20KDataset' +data_root = 'data/ade/ADEChallengeData2016' +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) +crop_size = (512, 512) +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='LoadAnnotations', reduce_zero_label=True), + dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), + dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), + dict(type='RandomFlip', prob=0.5), + dict(type='PhotoMetricDistortion'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_semantic_seg']), +] +test_pipeline = [ + dict(type='LoadImageFromFile'), + dict( + type='MultiScaleFlipAug', + img_scale=(2048, 512), + # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']), + ]) +] +data = dict( + samples_per_gpu=4, + workers_per_gpu=4, + train=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/training', + ann_dir='annotations/training', + pipeline=train_pipeline), + val=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/validation', + ann_dir='annotations/validation', + pipeline=test_pipeline), + test=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/validation', + ann_dir='annotations/validation', + pipeline=test_pipeline)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/chase_db1.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/chase_db1.py new file mode 100644 index 0000000000000000000000000000000000000000..298594ea925f87f22b37094a2ec50e370aec96a0 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/chase_db1.py @@ -0,0 +1,59 @@ +# dataset settings +dataset_type = 'ChaseDB1Dataset' +data_root = 'data/CHASE_DB1' +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) +img_scale = (960, 999) +crop_size = (128, 128) +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='LoadAnnotations'), + dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), + dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), + dict(type='RandomFlip', prob=0.5), + dict(type='PhotoMetricDistortion'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_semantic_seg']) +] +test_pipeline = [ + dict(type='LoadImageFromFile'), + dict( + type='MultiScaleFlipAug', + img_scale=img_scale, + # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0], + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']) + ]) +] + +data = dict( + samples_per_gpu=4, + workers_per_gpu=4, + train=dict( + type='RepeatDataset', + times=40000, + dataset=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/training', + ann_dir='annotations/training', + pipeline=train_pipeline)), + val=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/validation', + ann_dir='annotations/validation', + pipeline=test_pipeline), + test=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/validation', + ann_dir='annotations/validation', + pipeline=test_pipeline)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/cityscapes.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/cityscapes.py new file mode 100644 index 0000000000000000000000000000000000000000..f21867c63e1835f6fceb61f066e802fd8fd2a735 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/cityscapes.py @@ -0,0 +1,54 @@ +# dataset settings +dataset_type = 'CityscapesDataset' +data_root = 'data/cityscapes/' +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) +crop_size = (512, 1024) +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='LoadAnnotations'), + dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), + dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), + dict(type='RandomFlip', prob=0.5), + dict(type='PhotoMetricDistortion'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_semantic_seg']), +] +test_pipeline = [ + dict(type='LoadImageFromFile'), + dict( + type='MultiScaleFlipAug', + img_scale=(2048, 1024), + # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']), + ]) +] +data = dict( + samples_per_gpu=2, + workers_per_gpu=2, + train=dict( + type=dataset_type, + data_root=data_root, + img_dir='leftImg8bit/train', + ann_dir='gtFine/train', + pipeline=train_pipeline), + val=dict( + type=dataset_type, + data_root=data_root, + img_dir='leftImg8bit/val', + ann_dir='gtFine/val', + pipeline=test_pipeline), + test=dict( + type=dataset_type, + data_root=data_root, + img_dir='leftImg8bit/val', + ann_dir='gtFine/val', + pipeline=test_pipeline)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/cityscapes_769x769.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/cityscapes_769x769.py new file mode 100644 index 0000000000000000000000000000000000000000..336c7b254fe392b4703039fec86a83acdbd2e1a5 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/cityscapes_769x769.py @@ -0,0 +1,35 @@ +_base_ = './cityscapes.py' +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) +crop_size = (769, 769) +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='LoadAnnotations'), + dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)), + dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), + dict(type='RandomFlip', prob=0.5), + dict(type='PhotoMetricDistortion'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_semantic_seg']), +] +test_pipeline = [ + dict(type='LoadImageFromFile'), + dict( + type='MultiScaleFlipAug', + img_scale=(2049, 1025), + # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']), + ]) +] +data = dict( + train=dict(pipeline=train_pipeline), + val=dict(pipeline=test_pipeline), + test=dict(pipeline=test_pipeline)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/drive.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/drive.py new file mode 100644 index 0000000000000000000000000000000000000000..06e8ff606e0d2a4514ec8b7d2c6c436a32efcbf4 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/drive.py @@ -0,0 +1,59 @@ +# dataset settings +dataset_type = 'DRIVEDataset' +data_root = 'data/DRIVE' +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) +img_scale = (584, 565) +crop_size = (64, 64) +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='LoadAnnotations'), + dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), + dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), + dict(type='RandomFlip', prob=0.5), + dict(type='PhotoMetricDistortion'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_semantic_seg']) +] +test_pipeline = [ + dict(type='LoadImageFromFile'), + dict( + type='MultiScaleFlipAug', + img_scale=img_scale, + # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0], + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']) + ]) +] + +data = dict( + samples_per_gpu=4, + workers_per_gpu=4, + train=dict( + type='RepeatDataset', + times=40000, + dataset=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/training', + ann_dir='annotations/training', + pipeline=train_pipeline)), + val=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/validation', + ann_dir='annotations/validation', + pipeline=test_pipeline), + test=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/validation', + ann_dir='annotations/validation', + pipeline=test_pipeline)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/hrf.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/hrf.py new file mode 100644 index 0000000000000000000000000000000000000000..242d790eb1b83e75cf6b7eaa7a35c674099311ad --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/hrf.py @@ -0,0 +1,59 @@ +# dataset settings +dataset_type = 'HRFDataset' +data_root = 'data/HRF' +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) +img_scale = (2336, 3504) +crop_size = (256, 256) +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='LoadAnnotations'), + dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), + dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), + dict(type='RandomFlip', prob=0.5), + dict(type='PhotoMetricDistortion'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_semantic_seg']) +] +test_pipeline = [ + dict(type='LoadImageFromFile'), + dict( + type='MultiScaleFlipAug', + img_scale=img_scale, + # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0], + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']) + ]) +] + +data = dict( + samples_per_gpu=4, + workers_per_gpu=4, + train=dict( + type='RepeatDataset', + times=40000, + dataset=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/training', + ann_dir='annotations/training', + pipeline=train_pipeline)), + val=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/validation', + ann_dir='annotations/validation', + pipeline=test_pipeline), + test=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/validation', + ann_dir='annotations/validation', + pipeline=test_pipeline)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/pascal_context.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/pascal_context.py new file mode 100644 index 0000000000000000000000000000000000000000..ff65bad1b86d7e3a5980bb5b9fc55798dc8df5f4 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/pascal_context.py @@ -0,0 +1,60 @@ +# dataset settings +dataset_type = 'PascalContextDataset' +data_root = 'data/VOCdevkit/VOC2010/' +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) + +img_scale = (520, 520) +crop_size = (480, 480) + +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='LoadAnnotations'), + dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), + dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), + dict(type='RandomFlip', prob=0.5), + dict(type='PhotoMetricDistortion'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_semantic_seg']), +] +test_pipeline = [ + dict(type='LoadImageFromFile'), + dict( + type='MultiScaleFlipAug', + img_scale=img_scale, + # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']), + ]) +] +data = dict( + samples_per_gpu=4, + workers_per_gpu=4, + train=dict( + type=dataset_type, + data_root=data_root, + img_dir='JPEGImages', + ann_dir='SegmentationClassContext', + split='ImageSets/SegmentationContext/train.txt', + pipeline=train_pipeline), + val=dict( + type=dataset_type, + data_root=data_root, + img_dir='JPEGImages', + ann_dir='SegmentationClassContext', + split='ImageSets/SegmentationContext/val.txt', + pipeline=test_pipeline), + test=dict( + type=dataset_type, + data_root=data_root, + img_dir='JPEGImages', + ann_dir='SegmentationClassContext', + split='ImageSets/SegmentationContext/val.txt', + pipeline=test_pipeline)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/pascal_context_59.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/pascal_context_59.py new file mode 100644 index 0000000000000000000000000000000000000000..37585abab89834b95cd5bdd993b994fca1db65f6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/pascal_context_59.py @@ -0,0 +1,60 @@ +# dataset settings +dataset_type = 'PascalContextDataset59' +data_root = 'data/VOCdevkit/VOC2010/' +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) + +img_scale = (520, 520) +crop_size = (480, 480) + +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='LoadAnnotations', reduce_zero_label=True), + dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), + dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), + dict(type='RandomFlip', prob=0.5), + dict(type='PhotoMetricDistortion'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_semantic_seg']), +] +test_pipeline = [ + dict(type='LoadImageFromFile'), + dict( + type='MultiScaleFlipAug', + img_scale=img_scale, + # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']), + ]) +] +data = dict( + samples_per_gpu=4, + workers_per_gpu=4, + train=dict( + type=dataset_type, + data_root=data_root, + img_dir='JPEGImages', + ann_dir='SegmentationClassContext', + split='ImageSets/SegmentationContext/train.txt', + pipeline=train_pipeline), + val=dict( + type=dataset_type, + data_root=data_root, + img_dir='JPEGImages', + ann_dir='SegmentationClassContext', + split='ImageSets/SegmentationContext/val.txt', + pipeline=test_pipeline), + test=dict( + type=dataset_type, + data_root=data_root, + img_dir='JPEGImages', + ann_dir='SegmentationClassContext', + split='ImageSets/SegmentationContext/val.txt', + pipeline=test_pipeline)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/pascal_voc12.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/pascal_voc12.py new file mode 100644 index 0000000000000000000000000000000000000000..ba1d42d0c5781f56dc177d860d856bb34adce555 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/pascal_voc12.py @@ -0,0 +1,57 @@ +# dataset settings +dataset_type = 'PascalVOCDataset' +data_root = 'data/VOCdevkit/VOC2012' +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) +crop_size = (512, 512) +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='LoadAnnotations'), + dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), + dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), + dict(type='RandomFlip', prob=0.5), + dict(type='PhotoMetricDistortion'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_semantic_seg']), +] +test_pipeline = [ + dict(type='LoadImageFromFile'), + dict( + type='MultiScaleFlipAug', + img_scale=(2048, 512), + # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']), + ]) +] +data = dict( + samples_per_gpu=4, + workers_per_gpu=4, + train=dict( + type=dataset_type, + data_root=data_root, + img_dir='JPEGImages', + ann_dir='SegmentationClass', + split='ImageSets/Segmentation/train.txt', + pipeline=train_pipeline), + val=dict( + type=dataset_type, + data_root=data_root, + img_dir='JPEGImages', + ann_dir='SegmentationClass', + split='ImageSets/Segmentation/val.txt', + pipeline=test_pipeline), + test=dict( + type=dataset_type, + data_root=data_root, + img_dir='JPEGImages', + ann_dir='SegmentationClass', + split='ImageSets/Segmentation/val.txt', + pipeline=test_pipeline)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/pascal_voc12_aug.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/pascal_voc12_aug.py new file mode 100644 index 0000000000000000000000000000000000000000..3f23b6717d53ad29f02dd15046802a2631a5076b --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/pascal_voc12_aug.py @@ -0,0 +1,9 @@ +_base_ = './pascal_voc12.py' +# dataset settings +data = dict( + train=dict( + ann_dir=['SegmentationClass', 'SegmentationClassAug'], + split=[ + 'ImageSets/Segmentation/train.txt', + 'ImageSets/Segmentation/aug.txt' + ])) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/stare.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/stare.py new file mode 100644 index 0000000000000000000000000000000000000000..3f71b25488cc11a6b4d582ac52b5a24e1ad1cf8e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/datasets/stare.py @@ -0,0 +1,59 @@ +# dataset settings +dataset_type = 'STAREDataset' +data_root = 'data/STARE' +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) +img_scale = (605, 700) +crop_size = (128, 128) +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='LoadAnnotations'), + dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), + dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), + dict(type='RandomFlip', prob=0.5), + dict(type='PhotoMetricDistortion'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_semantic_seg']) +] +test_pipeline = [ + dict(type='LoadImageFromFile'), + dict( + type='MultiScaleFlipAug', + img_scale=img_scale, + # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0], + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']) + ]) +] + +data = dict( + samples_per_gpu=4, + workers_per_gpu=4, + train=dict( + type='RepeatDataset', + times=40000, + dataset=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/training', + ann_dir='annotations/training', + pipeline=train_pipeline)), + val=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/validation', + ann_dir='annotations/validation', + pipeline=test_pipeline), + test=dict( + type=dataset_type, + data_root=data_root, + img_dir='images/validation', + ann_dir='annotations/validation', + pipeline=test_pipeline)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/default_runtime.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/default_runtime.py new file mode 100644 index 0000000000000000000000000000000000000000..b564cc4e7e7d9a67dacaaddecb100e4d8f5c005b --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/default_runtime.py @@ -0,0 +1,14 @@ +# yapf:disable +log_config = dict( + interval=50, + hooks=[ + dict(type='TextLoggerHook', by_epoch=False), + # dict(type='TensorboardLoggerHook') + ]) +# yapf:enable +dist_params = dict(backend='nccl') +log_level = 'INFO' +load_from = None +resume_from = None +workflow = [('train', 1)] +cudnn_benchmark = True diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/ann_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/ann_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..a2cb653827e44e6015b3b83bc578003e614a6aa1 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/ann_r50-d8.py @@ -0,0 +1,46 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='ANNHead', + in_channels=[1024, 2048], + in_index=[2, 3], + channels=512, + project_channels=256, + query_scales=(1, ), + key_pool_scales=(1, 3, 6, 8), + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/apcnet_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/apcnet_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..c8f5316cbcf3896ba9de7ca2c801eba512f01d5e --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/apcnet_r50-d8.py @@ -0,0 +1,44 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='APCHead', + in_channels=2048, + in_index=3, + channels=512, + pool_scales=(1, 2, 3, 6), + dropout_ratio=0.1, + num_classes=19, + norm_cfg=dict(type='SyncBN', requires_grad=True), + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/ccnet_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/ccnet_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..794148f576b9e215c3c6963e73dffe98204b7717 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/ccnet_r50-d8.py @@ -0,0 +1,44 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='CCHead', + in_channels=2048, + in_index=3, + channels=512, + recurrence=2, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/cgnet.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/cgnet.py new file mode 100644 index 0000000000000000000000000000000000000000..eff8d9458c877c5db894957e0b1b4597e40da6ab --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/cgnet.py @@ -0,0 +1,35 @@ +# model settings +norm_cfg = dict(type='SyncBN', eps=1e-03, requires_grad=True) +model = dict( + type='EncoderDecoder', + backbone=dict( + type='CGNet', + norm_cfg=norm_cfg, + in_channels=3, + num_channels=(32, 64, 128), + num_blocks=(3, 21), + dilations=(2, 4), + reductions=(8, 16)), + decode_head=dict( + type='FCNHead', + in_channels=256, + in_index=2, + channels=256, + num_convs=0, + concat_input=False, + dropout_ratio=0, + num_classes=19, + norm_cfg=norm_cfg, + loss_decode=dict( + type='CrossEntropyLoss', + use_sigmoid=False, + loss_weight=1.0, + class_weight=[ + 2.5959933, 6.7415504, 3.5354059, 9.8663225, 9.690899, 9.369352, + 10.289121, 9.953208, 4.3097677, 9.490387, 7.674431, 9.396905, + 10.347791, 6.3927646, 10.226669, 10.241062, 10.280587, + 10.396974, 10.055647 + ])), + # model training and testing settings + train_cfg=dict(sampler=None), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/danet_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/danet_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..2c934939fac48525f22ad86f489a041dd7db7d09 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/danet_r50-d8.py @@ -0,0 +1,44 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='DAHead', + in_channels=2048, + in_index=3, + channels=512, + pam_channels=64, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/deeplabv3_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/deeplabv3_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..d7a43bee01422ad4795dd27874e0cd4bb6cbfecf --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/deeplabv3_r50-d8.py @@ -0,0 +1,44 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='ASPPHead', + in_channels=2048, + in_index=3, + channels=512, + dilations=(1, 12, 24, 36), + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/deeplabv3_unet_s5-d16.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/deeplabv3_unet_s5-d16.py new file mode 100644 index 0000000000000000000000000000000000000000..0cd262999d8b2cb8e14a5c32190ae73f479d8e81 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/deeplabv3_unet_s5-d16.py @@ -0,0 +1,50 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained=None, + backbone=dict( + type='UNet', + in_channels=3, + base_channels=64, + num_stages=5, + strides=(1, 1, 1, 1, 1), + enc_num_convs=(2, 2, 2, 2, 2), + dec_num_convs=(2, 2, 2, 2), + downsamples=(True, True, True, True), + enc_dilations=(1, 1, 1, 1, 1), + dec_dilations=(1, 1, 1, 1), + with_cp=False, + conv_cfg=None, + norm_cfg=norm_cfg, + act_cfg=dict(type='ReLU'), + upsample_cfg=dict(type='InterpConv'), + norm_eval=False), + decode_head=dict( + type='ASPPHead', + in_channels=64, + in_index=4, + channels=16, + dilations=(1, 12, 24, 36), + dropout_ratio=0.1, + num_classes=2, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=128, + in_index=3, + channels=64, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=2, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='slide', crop_size=256, stride=170)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/deeplabv3plus_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/deeplabv3plus_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..050e39e091d816df9028d23aa3ecf9db74e441e1 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/deeplabv3plus_r50-d8.py @@ -0,0 +1,46 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='DepthwiseSeparableASPPHead', + in_channels=2048, + in_index=3, + channels=512, + dilations=(1, 12, 24, 36), + c1_in_channels=256, + c1_channels=48, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/dmnet_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/dmnet_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..d22ba52640bebd805b3b8d07025e276dfb023759 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/dmnet_r50-d8.py @@ -0,0 +1,44 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='DMHead', + in_channels=2048, + in_index=3, + channels=512, + filter_sizes=(1, 3, 5, 7), + dropout_ratio=0.1, + num_classes=19, + norm_cfg=dict(type='SyncBN', requires_grad=True), + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/dnl_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/dnl_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..edb4c174c51e34c103737ba39bfc48bf831e561d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/dnl_r50-d8.py @@ -0,0 +1,46 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='DNLHead', + in_channels=2048, + in_index=3, + channels=512, + dropout_ratio=0.1, + reduction=2, + use_scale=True, + mode='embedded_gaussian', + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/emanet_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/emanet_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..26adcd430926de0862204a71d345f2543167f27b --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/emanet_r50-d8.py @@ -0,0 +1,47 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='EMAHead', + in_channels=2048, + in_index=3, + channels=256, + ema_channels=512, + num_bases=64, + num_stages=3, + momentum=0.1, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/encnet_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/encnet_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..be777123a886503172a95fe0719e956a147bbd68 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/encnet_r50-d8.py @@ -0,0 +1,48 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='EncHead', + in_channels=[512, 1024, 2048], + in_index=(1, 2, 3), + channels=512, + num_codes=32, + use_se_loss=True, + add_lateral=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0), + loss_se_decode=dict( + type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.2)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fast_scnn.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fast_scnn.py new file mode 100644 index 0000000000000000000000000000000000000000..32fdeb659355a5ce5ef2cc7c2f30742703811cdf --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fast_scnn.py @@ -0,0 +1,57 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True, momentum=0.01) +model = dict( + type='EncoderDecoder', + backbone=dict( + type='FastSCNN', + downsample_dw_channels=(32, 48), + global_in_channels=64, + global_block_channels=(64, 96, 128), + global_block_strides=(2, 2, 1), + global_out_channels=128, + higher_in_channels=64, + lower_in_channels=128, + fusion_out_channels=128, + out_indices=(0, 1, 2), + norm_cfg=norm_cfg, + align_corners=False), + decode_head=dict( + type='DepthwiseSeparableFCNHead', + in_channels=128, + channels=128, + concat_input=False, + num_classes=19, + in_index=-1, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.4)), + auxiliary_head=[ + dict( + type='FCNHead', + in_channels=128, + channels=32, + num_convs=1, + num_classes=19, + in_index=-2, + norm_cfg=norm_cfg, + concat_input=False, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.4)), + dict( + type='FCNHead', + in_channels=64, + channels=32, + num_convs=1, + num_classes=19, + in_index=-3, + norm_cfg=norm_cfg, + concat_input=False, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.4)), + ], + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fcn_hr18.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fcn_hr18.py new file mode 100644 index 0000000000000000000000000000000000000000..c3e299bc89ada56ca14bbffcbdb08a586b8ed9e9 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fcn_hr18.py @@ -0,0 +1,52 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://msra/hrnetv2_w18', + backbone=dict( + type='HRNet', + norm_cfg=norm_cfg, + norm_eval=False, + extra=dict( + stage1=dict( + num_modules=1, + num_branches=1, + block='BOTTLENECK', + num_blocks=(4, ), + num_channels=(64, )), + stage2=dict( + num_modules=1, + num_branches=2, + block='BASIC', + num_blocks=(4, 4), + num_channels=(18, 36)), + stage3=dict( + num_modules=4, + num_branches=3, + block='BASIC', + num_blocks=(4, 4, 4), + num_channels=(18, 36, 72)), + stage4=dict( + num_modules=3, + num_branches=4, + block='BASIC', + num_blocks=(4, 4, 4, 4), + num_channels=(18, 36, 72, 144)))), + decode_head=dict( + type='FCNHead', + in_channels=[18, 36, 72, 144], + in_index=(0, 1, 2, 3), + channels=sum([18, 36, 72, 144]), + input_transform='resize_concat', + kernel_size=1, + num_convs=1, + concat_input=False, + dropout_ratio=-1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fcn_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fcn_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..5e98f6cc918b6146fc6d613c6918e825ef1355c3 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fcn_r50-d8.py @@ -0,0 +1,45 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='FCNHead', + in_channels=2048, + in_index=3, + channels=512, + num_convs=2, + concat_input=True, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fcn_unet_s5-d16.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fcn_unet_s5-d16.py new file mode 100644 index 0000000000000000000000000000000000000000..a33e7972877f902d0e7d18401ca675e3e4e60a18 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fcn_unet_s5-d16.py @@ -0,0 +1,51 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained=None, + backbone=dict( + type='UNet', + in_channels=3, + base_channels=64, + num_stages=5, + strides=(1, 1, 1, 1, 1), + enc_num_convs=(2, 2, 2, 2, 2), + dec_num_convs=(2, 2, 2, 2), + downsamples=(True, True, True, True), + enc_dilations=(1, 1, 1, 1, 1), + dec_dilations=(1, 1, 1, 1), + with_cp=False, + conv_cfg=None, + norm_cfg=norm_cfg, + act_cfg=dict(type='ReLU'), + upsample_cfg=dict(type='InterpConv'), + norm_eval=False), + decode_head=dict( + type='FCNHead', + in_channels=64, + in_index=4, + channels=64, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=2, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=128, + in_index=3, + channels=64, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=2, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='slide', crop_size=256, stride=170)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fpn_r50.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fpn_r50.py new file mode 100644 index 0000000000000000000000000000000000000000..86ab327db92e44c14822d65f1c9277cb007f17c1 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fpn_r50.py @@ -0,0 +1,36 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 1, 1), + strides=(1, 2, 2, 2), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + neck=dict( + type='FPN', + in_channels=[256, 512, 1024, 2048], + out_channels=256, + num_outs=4), + decode_head=dict( + type='FPNHead', + in_channels=[256, 256, 256, 256], + in_index=[0, 1, 2, 3], + feature_strides=[4, 8, 16, 32], + channels=128, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fpn_uniformer.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fpn_uniformer.py new file mode 100644 index 0000000000000000000000000000000000000000..8aae98c5991055bfcc08e82ccdc09f8b1d9f8a8d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/fpn_uniformer.py @@ -0,0 +1,35 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + backbone=dict( + type='UniFormer', + embed_dim=[64, 128, 320, 512], + layers=[3, 4, 8, 3], + head_dim=64, + mlp_ratio=4., + qkv_bias=True, + drop_rate=0., + attn_drop_rate=0., + drop_path_rate=0.1), + neck=dict( + type='FPN', + in_channels=[64, 128, 320, 512], + out_channels=256, + num_outs=4), + decode_head=dict( + type='FPNHead', + in_channels=[256, 256, 256, 256], + in_index=[0, 1, 2, 3], + feature_strides=[4, 8, 16, 32], + channels=128, + dropout_ratio=0.1, + num_classes=150, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole') +) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/gcnet_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/gcnet_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..3d2ad69f5c22adfe79d5fdabf920217628987166 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/gcnet_r50-d8.py @@ -0,0 +1,46 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='GCHead', + in_channels=2048, + in_index=3, + channels=512, + ratio=1 / 4., + pooling_type='att', + fusion_types=('channel_add', ), + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/lraspp_m-v3-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/lraspp_m-v3-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..93258242a90695cc94a7c6bd41562d6a75988771 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/lraspp_m-v3-d8.py @@ -0,0 +1,25 @@ +# model settings +norm_cfg = dict(type='SyncBN', eps=0.001, requires_grad=True) +model = dict( + type='EncoderDecoder', + backbone=dict( + type='MobileNetV3', + arch='large', + out_indices=(1, 3, 16), + norm_cfg=norm_cfg), + decode_head=dict( + type='LRASPPHead', + in_channels=(16, 24, 960), + in_index=(0, 1, 2), + channels=128, + input_transform='multiple_select', + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + act_cfg=dict(type='ReLU'), + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/nonlocal_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/nonlocal_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..5674a39854cafd1f2e363bac99c58ccae62f24da --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/nonlocal_r50-d8.py @@ -0,0 +1,46 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='NLHead', + in_channels=2048, + in_index=3, + channels=512, + dropout_ratio=0.1, + reduction=2, + use_scale=True, + mode='embedded_gaussian', + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/ocrnet_hr18.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/ocrnet_hr18.py new file mode 100644 index 0000000000000000000000000000000000000000..c60f62a7cdf3f5c5096a7a7e725e8268fddcb057 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/ocrnet_hr18.py @@ -0,0 +1,68 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='CascadeEncoderDecoder', + num_stages=2, + pretrained='open-mmlab://msra/hrnetv2_w18', + backbone=dict( + type='HRNet', + norm_cfg=norm_cfg, + norm_eval=False, + extra=dict( + stage1=dict( + num_modules=1, + num_branches=1, + block='BOTTLENECK', + num_blocks=(4, ), + num_channels=(64, )), + stage2=dict( + num_modules=1, + num_branches=2, + block='BASIC', + num_blocks=(4, 4), + num_channels=(18, 36)), + stage3=dict( + num_modules=4, + num_branches=3, + block='BASIC', + num_blocks=(4, 4, 4), + num_channels=(18, 36, 72)), + stage4=dict( + num_modules=3, + num_branches=4, + block='BASIC', + num_blocks=(4, 4, 4, 4), + num_channels=(18, 36, 72, 144)))), + decode_head=[ + dict( + type='FCNHead', + in_channels=[18, 36, 72, 144], + channels=sum([18, 36, 72, 144]), + in_index=(0, 1, 2, 3), + input_transform='resize_concat', + kernel_size=1, + num_convs=1, + concat_input=False, + dropout_ratio=-1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + dict( + type='OCRHead', + in_channels=[18, 36, 72, 144], + in_index=(0, 1, 2, 3), + input_transform='resize_concat', + channels=512, + ocr_channels=256, + dropout_ratio=-1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + ], + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/ocrnet_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/ocrnet_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..615aa3ff703942b6c22b2d6e9642504dd3e41ebd --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/ocrnet_r50-d8.py @@ -0,0 +1,47 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='CascadeEncoderDecoder', + num_stages=2, + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=[ + dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + dict( + type='OCRHead', + in_channels=2048, + in_index=3, + channels=512, + ocr_channels=256, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)) + ], + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/pointrend_r50.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/pointrend_r50.py new file mode 100644 index 0000000000000000000000000000000000000000..9d323dbf9466d41e0800aa57ef84045f3d874bdf --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/pointrend_r50.py @@ -0,0 +1,56 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='CascadeEncoderDecoder', + num_stages=2, + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 1, 1), + strides=(1, 2, 2, 2), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + neck=dict( + type='FPN', + in_channels=[256, 512, 1024, 2048], + out_channels=256, + num_outs=4), + decode_head=[ + dict( + type='FPNHead', + in_channels=[256, 256, 256, 256], + in_index=[0, 1, 2, 3], + feature_strides=[4, 8, 16, 32], + channels=128, + dropout_ratio=-1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + dict( + type='PointHead', + in_channels=[256], + in_index=[0], + channels=256, + num_fcs=3, + coarse_pred_each_layer=True, + dropout_ratio=-1, + num_classes=19, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)) + ], + # model training and testing settings + train_cfg=dict( + num_points=2048, oversample_ratio=3, importance_sample_ratio=0.75), + test_cfg=dict( + mode='whole', + subdivision_steps=2, + subdivision_num_points=8196, + scale_factor=2)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/psanet_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/psanet_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..689513fa9d2a40f14bf0ae4ae61f38f0dcc1b3da --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/psanet_r50-d8.py @@ -0,0 +1,49 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='PSAHead', + in_channels=2048, + in_index=3, + channels=512, + mask_size=(97, 97), + psa_type='bi-direction', + compact=False, + shrink_factor=2, + normalization_factor=1.0, + psa_softmax=True, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/pspnet_r50-d8.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/pspnet_r50-d8.py new file mode 100644 index 0000000000000000000000000000000000000000..f451e08ad2eb0732dcb806b1851eb978d4acf136 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/pspnet_r50-d8.py @@ -0,0 +1,44 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 2, 4), + strides=(1, 2, 1, 1), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='PSPHead', + in_channels=2048, + in_index=3, + channels=512, + pool_scales=(1, 2, 3, 6), + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/pspnet_unet_s5-d16.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/pspnet_unet_s5-d16.py new file mode 100644 index 0000000000000000000000000000000000000000..fcff9ec4f41fad158344ecd77313dc14564f3682 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/pspnet_unet_s5-d16.py @@ -0,0 +1,50 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained=None, + backbone=dict( + type='UNet', + in_channels=3, + base_channels=64, + num_stages=5, + strides=(1, 1, 1, 1, 1), + enc_num_convs=(2, 2, 2, 2, 2), + dec_num_convs=(2, 2, 2, 2), + downsamples=(True, True, True, True), + enc_dilations=(1, 1, 1, 1, 1), + dec_dilations=(1, 1, 1, 1), + with_cp=False, + conv_cfg=None, + norm_cfg=norm_cfg, + act_cfg=dict(type='ReLU'), + upsample_cfg=dict(type='InterpConv'), + norm_eval=False), + decode_head=dict( + type='PSPHead', + in_channels=64, + in_index=4, + channels=16, + pool_scales=(1, 2, 3, 6), + dropout_ratio=0.1, + num_classes=2, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=128, + in_index=3, + channels=64, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=2, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='slide', crop_size=256, stride=170)) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/upernet_r50.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/upernet_r50.py new file mode 100644 index 0000000000000000000000000000000000000000..10974962fdd7136031fd06de1700f497d355ceaa --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/upernet_r50.py @@ -0,0 +1,44 @@ +# model settings +norm_cfg = dict(type='SyncBN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained='open-mmlab://resnet50_v1c', + backbone=dict( + type='ResNetV1c', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + dilations=(1, 1, 1, 1), + strides=(1, 2, 2, 2), + norm_cfg=norm_cfg, + norm_eval=False, + style='pytorch', + contract_dilation=True), + decode_head=dict( + type='UPerHead', + in_channels=[256, 512, 1024, 2048], + in_index=[0, 1, 2, 3], + pool_scales=(1, 2, 3, 6), + channels=512, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=1024, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/upernet_uniformer.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/upernet_uniformer.py new file mode 100644 index 0000000000000000000000000000000000000000..41aa4db809dc6e2c508e98051f61807d07477903 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/models/upernet_uniformer.py @@ -0,0 +1,43 @@ +# model settings +norm_cfg = dict(type='BN', requires_grad=True) +model = dict( + type='EncoderDecoder', + pretrained=None, + backbone=dict( + type='UniFormer', + embed_dim=[64, 128, 320, 512], + layers=[3, 4, 8, 3], + head_dim=64, + mlp_ratio=4., + qkv_bias=True, + drop_rate=0., + attn_drop_rate=0., + drop_path_rate=0.1), + decode_head=dict( + type='UPerHead', + in_channels=[64, 128, 320, 512], + in_index=[0, 1, 2, 3], + pool_scales=(1, 2, 3, 6), + channels=512, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), + auxiliary_head=dict( + type='FCNHead', + in_channels=320, + in_index=2, + channels=256, + num_convs=1, + concat_input=False, + dropout_ratio=0.1, + num_classes=19, + norm_cfg=norm_cfg, + align_corners=False, + loss_decode=dict( + type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), + # model training and testing settings + train_cfg=dict(), + test_cfg=dict(mode='whole')) \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/schedules/schedule_160k.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/schedules/schedule_160k.py new file mode 100644 index 0000000000000000000000000000000000000000..52603890b10f25faf8eec9f9e5a4468fae09b811 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/schedules/schedule_160k.py @@ -0,0 +1,9 @@ +# optimizer +optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) +optimizer_config = dict() +# learning policy +lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) +# runtime settings +runner = dict(type='IterBasedRunner', max_iters=160000) +checkpoint_config = dict(by_epoch=False, interval=16000) +evaluation = dict(interval=16000, metric='mIoU') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/schedules/schedule_20k.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/schedules/schedule_20k.py new file mode 100644 index 0000000000000000000000000000000000000000..bf780a1b6f6521833c6a5859675147824efa599d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/schedules/schedule_20k.py @@ -0,0 +1,9 @@ +# optimizer +optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) +optimizer_config = dict() +# learning policy +lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) +# runtime settings +runner = dict(type='IterBasedRunner', max_iters=20000) +checkpoint_config = dict(by_epoch=False, interval=2000) +evaluation = dict(interval=2000, metric='mIoU') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/schedules/schedule_40k.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/schedules/schedule_40k.py new file mode 100644 index 0000000000000000000000000000000000000000..cdbf841abcb26eed87bf76ab816aff4bae0630ee --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/schedules/schedule_40k.py @@ -0,0 +1,9 @@ +# optimizer +optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) +optimizer_config = dict() +# learning policy +lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) +# runtime settings +runner = dict(type='IterBasedRunner', max_iters=40000) +checkpoint_config = dict(by_epoch=False, interval=4000) +evaluation = dict(interval=4000, metric='mIoU') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/schedules/schedule_80k.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/schedules/schedule_80k.py new file mode 100644 index 0000000000000000000000000000000000000000..c190cee6bdc7922b688ea75dc8f152fa15c24617 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/configs/_base_/schedules/schedule_80k.py @@ -0,0 +1,9 @@ +# optimizer +optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) +optimizer_config = dict() +# learning policy +lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) +# runtime settings +runner = dict(type='IterBasedRunner', max_iters=80000) +checkpoint_config = dict(by_epoch=False, interval=8000) +evaluation = dict(interval=8000, metric='mIoU') diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/inference.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/inference.py new file mode 100644 index 0000000000000000000000000000000000000000..de5955984b2997e2dee5dbacbdc34816888b4f12 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/inference.py @@ -0,0 +1,142 @@ + +import torch + +try: + import mmcv as mmcv + from mmcv.parallel import collate, scatter + from mmcv.runner import load_checkpoint + from mmseg.datasets.pipelines import Compose + from mmseg.models import build_segmentor +except ImportError: + import annotator.mmpkg.mmcv as mmcv + from annotator.mmpkg.mmcv.parallel import collate, scatter + from annotator.mmpkg.mmcv.runner import load_checkpoint + from annotator.mmpkg.mmseg.datasets.pipelines import Compose + from annotator.mmpkg.mmseg.models import build_segmentor + +def init_segmentor(config, checkpoint=None, device='cuda:0'): + """Initialize a segmentor from config file. + + Args: + config (str or :obj:`mmcv.Config`): Config file path or the config + object. + checkpoint (str, optional): Checkpoint path. If left as None, the model + will not load any weights. + device (str, optional) CPU/CUDA device option. Default 'cuda:0'. + Use 'cpu' for loading model on CPU. + Returns: + nn.Module: The constructed segmentor. + """ + if isinstance(config, str): + config = mmcv.Config.fromfile(config) + elif not isinstance(config, mmcv.Config): + raise TypeError('config must be a filename or Config object, ' + 'but got {}'.format(type(config))) + config.model.pretrained = None + config.model.train_cfg = None + model = build_segmentor(config.model, test_cfg=config.get('test_cfg')) + if checkpoint is not None: + checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') + model.CLASSES = checkpoint['meta']['CLASSES'] + model.PALETTE = checkpoint['meta']['PALETTE'] + model.cfg = config # save the config in the model for convenience + model.to(device) + model.eval() + return model + + +class LoadImage: + """A simple pipeline to load image.""" + + def __call__(self, results): + """Call function to load images into results. + + Args: + results (dict): A result dict contains the file name + of the image to be read. + + Returns: + dict: ``results`` will be returned containing loaded image. + """ + + if isinstance(results['img'], str): + results['filename'] = results['img'] + results['ori_filename'] = results['img'] + else: + results['filename'] = None + results['ori_filename'] = None + img = mmcv.imread(results['img']) + results['img'] = img + results['img_shape'] = img.shape + results['ori_shape'] = img.shape + return results + + +def inference_segmentor(model, img): + """Inference image(s) with the segmentor. + + Args: + model (nn.Module): The loaded segmentor. + imgs (str/ndarray or list[str/ndarray]): Either image files or loaded + images. + + Returns: + (list[Tensor]): The segmentation result. + """ + cfg = model.cfg + device = next(model.parameters()).device # model device + # build the data pipeline + test_pipeline = [LoadImage()] + cfg.data.test.pipeline[1:] + test_pipeline = Compose(test_pipeline) + # prepare data + data = dict(img=img) + data = test_pipeline(data) + data = collate([data], samples_per_gpu=1) + if next(model.parameters()).is_cuda: + # scatter to specified GPU + data = scatter(data, [device])[0] + else: + data['img_metas'] = [i.data[0] for i in data['img_metas']] + + # forward the model + with torch.no_grad(): + result = model(return_loss=False, rescale=True, **data) + return result + + +def show_result_pyplot(model, + img, + result, + palette=None, + fig_size=(15, 10), + opacity=0.5, + title='', + block=True): + """Visualize the segmentation results on the image. + + Args: + model (nn.Module): The loaded segmentor. + img (str or np.ndarray): Image filename or loaded image. + result (list): The segmentation result. + palette (list[list[int]]] | None): The palette of segmentation + map. If None is given, random palette will be generated. + Default: None + fig_size (tuple): Figure size of the pyplot figure. + opacity(float): Opacity of painted segmentation map. + Default 0.5. + Must be in (0, 1] range. + title (str): The title of pyplot figure. + Default is ''. + block (bool): Whether to block the pyplot figure. + Default is True. + """ + if hasattr(model, 'module'): + model = model.module + img = model.show_result( + img, result, palette=palette, show=False, opacity=opacity) + # plt.figure(figsize=fig_size) + # plt.imshow(mmcv.bgr2rgb(img)) + # plt.title(title) + # plt.tight_layout() + # plt.show(block=block) + return mmcv.bgr2rgb(img) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/mmcv_custom/__init__.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/mmcv_custom/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4b958738b9fd93bfcec239c550df1d9a44b8c536 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/mmcv_custom/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +from .checkpoint import load_checkpoint + +__all__ = ['load_checkpoint'] \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/mmcv_custom/checkpoint.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/mmcv_custom/checkpoint.py new file mode 100644 index 0000000000000000000000000000000000000000..48c1b16b53107cb1301edf6cc07ccfe6f7010da6 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/mmcv_custom/checkpoint.py @@ -0,0 +1,508 @@ +# Copyright (c) Open-MMLab. All rights reserved. +import io +import os +import os.path as osp +import pkgutil +import time +import warnings +from collections import OrderedDict +from importlib import import_module +from tempfile import TemporaryDirectory + +import torch +import torchvision +from torch.optim import Optimizer +from torch.utils import model_zoo +from torch.nn import functional as F + +try: + import mmcv as mmcv + from mmcv.fileio import FileClient + from mmcv.fileio import load as load_file + from mmcv.parallel import is_module_wrapper + from mmcv.utils import mkdir_or_exist + from mmcv.runner import get_dist_info +except ImportError: + import annotator.mmpkg.mmcv as mmcv + from annotator.mmpkg.mmcv.fileio import FileClient + from annotator.mmpkg.mmcv.fileio import load as load_file + from annotator.mmpkg.mmcv.parallel import is_module_wrapper + from annotator.mmpkg.mmcv.utils import mkdir_or_exist + from annotator.mmpkg.mmcv.runner import get_dist_info + +ENV_MMCV_HOME = 'MMCV_HOME' +ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' +DEFAULT_CACHE_DIR = '~/.cache' + + +def _get_mmcv_home(): + mmcv_home = os.path.expanduser( + os.getenv( + ENV_MMCV_HOME, + os.path.join( + os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) + + mkdir_or_exist(mmcv_home) + return mmcv_home + + +def load_state_dict(module, state_dict, strict=False, logger=None): + """Load state_dict to a module. + + This method is modified from :meth:`torch.nn.Module.load_state_dict`. + Default value for ``strict`` is set to ``False`` and the message for + param mismatch will be shown even if strict is False. + + Args: + module (Module): Module that receives the state_dict. + state_dict (OrderedDict): Weights. + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. + logger (:obj:`logging.Logger`, optional): Logger to log the error + message. If not specified, print function will be used. + """ + unexpected_keys = [] + all_missing_keys = [] + err_msg = [] + + metadata = getattr(state_dict, '_metadata', None) + state_dict = state_dict.copy() + if metadata is not None: + state_dict._metadata = metadata + + # use _load_from_state_dict to enable checkpoint version control + def load(module, prefix=''): + # recursively check parallel module in case that the model has a + # complicated structure, e.g., nn.Module(nn.Module(DDP)) + if is_module_wrapper(module): + module = module.module + local_metadata = {} if metadata is None else metadata.get( + prefix[:-1], {}) + module._load_from_state_dict(state_dict, prefix, local_metadata, True, + all_missing_keys, unexpected_keys, + err_msg) + for name, child in module._modules.items(): + if child is not None: + load(child, prefix + name + '.') + + load(module) + load = None # break load->load reference cycle + + # ignore "num_batches_tracked" of BN layers + missing_keys = [ + key for key in all_missing_keys if 'num_batches_tracked' not in key + ] + + if unexpected_keys: + err_msg.append('unexpected key in source ' + f'state_dict: {", ".join(unexpected_keys)}\n') + if missing_keys: + err_msg.append( + f'missing keys in source state_dict: {", ".join(missing_keys)}\n') + + rank, _ = get_dist_info() + if len(err_msg) > 0 and rank == 0: + err_msg.insert( + 0, 'The model and loaded state dict do not match exactly\n') + err_msg = '\n'.join(err_msg) + if strict: + raise RuntimeError(err_msg) + elif logger is not None: + logger.warning(err_msg) + else: + print(err_msg) + + +def load_url_dist(url, model_dir=None): + """In distributed setting, this function only download checkpoint at local + rank 0.""" + rank, world_size = get_dist_info() + rank = int(os.environ.get('LOCAL_RANK', rank)) + if rank == 0: + checkpoint = model_zoo.load_url(url, model_dir=model_dir) + if world_size > 1: + torch.distributed.barrier() + if rank > 0: + checkpoint = model_zoo.load_url(url, model_dir=model_dir) + return checkpoint + + +def load_pavimodel_dist(model_path, map_location=None): + """In distributed setting, this function only download checkpoint at local + rank 0.""" + try: + from pavi import modelcloud + except ImportError: + raise ImportError( + 'Please install pavi to load checkpoint from modelcloud.') + rank, world_size = get_dist_info() + rank = int(os.environ.get('LOCAL_RANK', rank)) + if rank == 0: + model = modelcloud.get(model_path) + with TemporaryDirectory() as tmp_dir: + downloaded_file = osp.join(tmp_dir, model.name) + model.download(downloaded_file) + checkpoint = torch.load(downloaded_file, map_location=map_location) + if world_size > 1: + torch.distributed.barrier() + if rank > 0: + model = modelcloud.get(model_path) + with TemporaryDirectory() as tmp_dir: + downloaded_file = osp.join(tmp_dir, model.name) + model.download(downloaded_file) + checkpoint = torch.load( + downloaded_file, map_location=map_location) + return checkpoint + + +def load_fileclient_dist(filename, backend, map_location): + """In distributed setting, this function only download checkpoint at local + rank 0.""" + rank, world_size = get_dist_info() + rank = int(os.environ.get('LOCAL_RANK', rank)) + allowed_backends = ['ceph'] + if backend not in allowed_backends: + raise ValueError(f'Load from Backend {backend} is not supported.') + if rank == 0: + fileclient = FileClient(backend=backend) + buffer = io.BytesIO(fileclient.get(filename)) + checkpoint = torch.load(buffer, map_location=map_location) + if world_size > 1: + torch.distributed.barrier() + if rank > 0: + fileclient = FileClient(backend=backend) + buffer = io.BytesIO(fileclient.get(filename)) + checkpoint = torch.load(buffer, map_location=map_location) + return checkpoint + + +def get_torchvision_models(): + model_urls = dict() + for _, name, ispkg in pkgutil.walk_packages(torchvision.models.__path__): + if ispkg: + continue + _zoo = import_module(f'torchvision.models.{name}') + if hasattr(_zoo, 'model_urls'): + _urls = getattr(_zoo, 'model_urls') + model_urls.update(_urls) + return model_urls + + +def get_external_models(): + mmcv_home = _get_mmcv_home() + default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') + default_urls = load_file(default_json_path) + assert isinstance(default_urls, dict) + external_json_path = osp.join(mmcv_home, 'open_mmlab.json') + if osp.exists(external_json_path): + external_urls = load_file(external_json_path) + assert isinstance(external_urls, dict) + default_urls.update(external_urls) + + return default_urls + + +def get_mmcls_models(): + mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') + mmcls_urls = load_file(mmcls_json_path) + + return mmcls_urls + + +def get_deprecated_model_names(): + deprecate_json_path = osp.join(mmcv.__path__[0], + 'model_zoo/deprecated.json') + deprecate_urls = load_file(deprecate_json_path) + assert isinstance(deprecate_urls, dict) + + return deprecate_urls + + +def _process_mmcls_checkpoint(checkpoint): + state_dict = checkpoint['state_dict'] + new_state_dict = OrderedDict() + for k, v in state_dict.items(): + if k.startswith('backbone.'): + new_state_dict[k[9:]] = v + new_checkpoint = dict(state_dict=new_state_dict) + + return new_checkpoint + + +def _load_checkpoint(filename, map_location=None): + """Load checkpoint from somewhere (modelzoo, file, url). + + Args: + filename (str): Accept local filepath, URL, ``torchvision://xxx``, + ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for + details. + map_location (str | None): Same as :func:`torch.load`. Default: None. + + Returns: + dict | OrderedDict: The loaded checkpoint. It can be either an + OrderedDict storing model weights or a dict containing other + information, which depends on the checkpoint. + """ + if filename.startswith('modelzoo://'): + warnings.warn('The URL scheme of "modelzoo://" is deprecated, please ' + 'use "torchvision://" instead') + model_urls = get_torchvision_models() + model_name = filename[11:] + checkpoint = load_url_dist(model_urls[model_name]) + elif filename.startswith('torchvision://'): + model_urls = get_torchvision_models() + model_name = filename[14:] + checkpoint = load_url_dist(model_urls[model_name]) + elif filename.startswith('open-mmlab://'): + model_urls = get_external_models() + model_name = filename[13:] + deprecated_urls = get_deprecated_model_names() + if model_name in deprecated_urls: + warnings.warn(f'open-mmlab://{model_name} is deprecated in favor ' + f'of open-mmlab://{deprecated_urls[model_name]}') + model_name = deprecated_urls[model_name] + model_url = model_urls[model_name] + # check if is url + if model_url.startswith(('http://', 'https://')): + checkpoint = load_url_dist(model_url) + else: + filename = osp.join(_get_mmcv_home(), model_url) + if not osp.isfile(filename): + raise IOError(f'{filename} is not a checkpoint file') + checkpoint = torch.load(filename, map_location=map_location) + elif filename.startswith('mmcls://'): + model_urls = get_mmcls_models() + model_name = filename[8:] + checkpoint = load_url_dist(model_urls[model_name]) + checkpoint = _process_mmcls_checkpoint(checkpoint) + elif filename.startswith(('http://', 'https://')): + checkpoint = load_url_dist(filename) + elif filename.startswith('pavi://'): + model_path = filename[7:] + checkpoint = load_pavimodel_dist(model_path, map_location=map_location) + elif filename.startswith('s3://'): + checkpoint = load_fileclient_dist( + filename, backend='ceph', map_location=map_location) + else: + if not osp.isfile(filename): + raise IOError(f'{filename} is not a checkpoint file') + checkpoint = torch.load(filename, map_location=map_location) + return checkpoint + + +def load_checkpoint(model, + filename, + map_location='cpu', + strict=False, + logger=None): + """Load checkpoint from a file or URI. + + Args: + model (Module): Module to load checkpoint. + filename (str): Accept local filepath, URL, ``torchvision://xxx``, + ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for + details. + map_location (str): Same as :func:`torch.load`. + strict (bool): Whether to allow different params for the model and + checkpoint. + logger (:mod:`logging.Logger` or None): The logger for error message. + + Returns: + dict or OrderedDict: The loaded checkpoint. + """ + checkpoint = _load_checkpoint(filename, map_location) + # OrderedDict is a subclass of dict + if not isinstance(checkpoint, dict): + raise RuntimeError( + f'No state_dict found in checkpoint file {filename}') + # get state_dict from checkpoint + if 'state_dict' in checkpoint: + state_dict = checkpoint['state_dict'] + elif 'model' in checkpoint: + state_dict = checkpoint['model'] + else: + state_dict = checkpoint + # strip prefix of state_dict + if list(state_dict.keys())[0].startswith('module.'): + state_dict = {k[7:]: v for k, v in state_dict.items()} + + # for MoBY, load model of online branch + if sorted(list(state_dict.keys()))[0].startswith('encoder'): + state_dict = {k.replace('encoder.', ''): v for k, v in state_dict.items() if k.startswith('encoder.')} + + # reshape absolute position embedding + if state_dict.get('absolute_pos_embed') is not None: + absolute_pos_embed = state_dict['absolute_pos_embed'] + N1, L, C1 = absolute_pos_embed.size() + N2, C2, H, W = model.absolute_pos_embed.size() + if N1 != N2 or C1 != C2 or L != H*W: + logger.warning("Error in loading absolute_pos_embed, pass") + else: + state_dict['absolute_pos_embed'] = absolute_pos_embed.view(N2, H, W, C2).permute(0, 3, 1, 2) + + # interpolate position bias table if needed + relative_position_bias_table_keys = [k for k in state_dict.keys() if "relative_position_bias_table" in k] + for table_key in relative_position_bias_table_keys: + table_pretrained = state_dict[table_key] + table_current = model.state_dict()[table_key] + L1, nH1 = table_pretrained.size() + L2, nH2 = table_current.size() + if nH1 != nH2: + logger.warning(f"Error in loading {table_key}, pass") + else: + if L1 != L2: + S1 = int(L1 ** 0.5) + S2 = int(L2 ** 0.5) + table_pretrained_resized = F.interpolate( + table_pretrained.permute(1, 0).view(1, nH1, S1, S1), + size=(S2, S2), mode='bicubic') + state_dict[table_key] = table_pretrained_resized.view(nH2, L2).permute(1, 0) + + # load state_dict + load_state_dict(model, state_dict, strict, logger) + return checkpoint + + +def weights_to_cpu(state_dict): + """Copy a model state_dict to cpu. + + Args: + state_dict (OrderedDict): Model weights on GPU. + + Returns: + OrderedDict: Model weights on GPU. + """ + state_dict_cpu = OrderedDict() + for key, val in state_dict.items(): + state_dict_cpu[key] = val.cpu() + return state_dict_cpu + + +def _save_to_state_dict(module, destination, prefix, keep_vars): + """Saves module state to `destination` dictionary. + + This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. + + Args: + module (nn.Module): The module to generate state_dict. + destination (dict): A dict where state will be stored. + prefix (str): The prefix for parameters and buffers used in this + module. + """ + for name, param in module._parameters.items(): + if param is not None: + destination[prefix + name] = param if keep_vars else param.detach() + for name, buf in module._buffers.items(): + # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d + if buf is not None: + destination[prefix + name] = buf if keep_vars else buf.detach() + + +def get_state_dict(module, destination=None, prefix='', keep_vars=False): + """Returns a dictionary containing a whole state of the module. + + Both parameters and persistent buffers (e.g. running averages) are + included. Keys are corresponding parameter and buffer names. + + This method is modified from :meth:`torch.nn.Module.state_dict` to + recursively check parallel module in case that the model has a complicated + structure, e.g., nn.Module(nn.Module(DDP)). + + Args: + module (nn.Module): The module to generate state_dict. + destination (OrderedDict): Returned dict for the state of the + module. + prefix (str): Prefix of the key. + keep_vars (bool): Whether to keep the variable property of the + parameters. Default: False. + + Returns: + dict: A dictionary containing a whole state of the module. + """ + # recursively check parallel module in case that the model has a + # complicated structure, e.g., nn.Module(nn.Module(DDP)) + if is_module_wrapper(module): + module = module.module + + # below is the same as torch.nn.Module.state_dict() + if destination is None: + destination = OrderedDict() + destination._metadata = OrderedDict() + destination._metadata[prefix[:-1]] = local_metadata = dict( + version=module._version) + _save_to_state_dict(module, destination, prefix, keep_vars) + for name, child in module._modules.items(): + if child is not None: + get_state_dict( + child, destination, prefix + name + '.', keep_vars=keep_vars) + for hook in module._state_dict_hooks.values(): + hook_result = hook(module, destination, prefix, local_metadata) + if hook_result is not None: + destination = hook_result + return destination + + +def save_checkpoint(model, filename, optimizer=None, meta=None): + """Save checkpoint to file. + + The checkpoint will have 3 fields: ``meta``, ``state_dict`` and + ``optimizer``. By default ``meta`` will contain version and time info. + + Args: + model (Module): Module whose params are to be saved. + filename (str): Checkpoint filename. + optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. + meta (dict, optional): Metadata to be saved in checkpoint. + """ + if meta is None: + meta = {} + elif not isinstance(meta, dict): + raise TypeError(f'meta must be a dict or None, but got {type(meta)}') + meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) + + if is_module_wrapper(model): + model = model.module + + if hasattr(model, 'CLASSES') and model.CLASSES is not None: + # save class name to the meta + meta.update(CLASSES=model.CLASSES) + + checkpoint = { + 'meta': meta, + 'state_dict': weights_to_cpu(get_state_dict(model)) + } + # save optimizer state dict in the checkpoint + if isinstance(optimizer, Optimizer): + checkpoint['optimizer'] = optimizer.state_dict() + elif isinstance(optimizer, dict): + checkpoint['optimizer'] = {} + for name, optim in optimizer.items(): + checkpoint['optimizer'][name] = optim.state_dict() + + if filename.startswith('pavi://'): + try: + from pavi import modelcloud + from pavi.exception import NodeNotFoundError + except ImportError: + raise ImportError( + 'Please install pavi to load checkpoint from modelcloud.') + model_path = filename[7:] + root = modelcloud.Folder() + model_dir, model_name = osp.split(model_path) + try: + model = modelcloud.get(model_dir) + except NodeNotFoundError: + model = root.create_training_model(model_dir) + with TemporaryDirectory() as tmp_dir: + checkpoint_file = osp.join(tmp_dir, model_name) + with open(checkpoint_file, 'wb') as f: + torch.save(checkpoint, f) + f.flush() + model.create_file(checkpoint_file, name=model_name) + else: + mmcv.mkdir_or_exist(osp.dirname(filename)) + # immediately flush buffer + with open(filename, 'wb') as f: + torch.save(checkpoint, f) + f.flush() \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/uniformer.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/uniformer.py new file mode 100644 index 0000000000000000000000000000000000000000..f5726fbe63888e0d7a85563308ffd2ab526fed32 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/uniformer.py @@ -0,0 +1,426 @@ +# -------------------------------------------------------- +# UniFormer +# Copyright (c) 2022 SenseTime X-Lab +# Licensed under The MIT License [see LICENSE for details] +# Written by Kunchang Li +# -------------------------------------------------------- + + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.utils.checkpoint as checkpoint + +from functools import partial +from collections import OrderedDict +from timm.models.layers import DropPath, to_2tuple, trunc_normal_ + +try: + from mmseg.utils import get_root_logger + from mmseg.models.builder import BACKBONES +except ImportError: + from annotator.mmpkg.mmseg.utils import get_root_logger + from annotator.mmpkg.mmseg.models.builder import BACKBONES + +from annotator.uniformer.mmcv_custom import load_checkpoint + + +class Mlp(nn.Module): + def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +class CMlp(nn.Module): + def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Conv2d(in_features, hidden_features, 1) + self.act = act_layer() + self.fc2 = nn.Conv2d(hidden_features, out_features, 1) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +class CBlock(nn.Module): + def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop=0., attn_drop=0., + drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm): + super().__init__() + self.pos_embed = nn.Conv2d(dim, dim, 3, padding=1, groups=dim) + self.norm1 = nn.BatchNorm2d(dim) + self.conv1 = nn.Conv2d(dim, dim, 1) + self.conv2 = nn.Conv2d(dim, dim, 1) + self.attn = nn.Conv2d(dim, dim, 5, padding=2, groups=dim) + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + self.norm2 = nn.BatchNorm2d(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = CMlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) + + def forward(self, x): + x = x + self.pos_embed(x) + x = x + self.drop_path(self.conv2(self.attn(self.conv1(self.norm1(x))))) + x = x + self.drop_path(self.mlp(self.norm2(x))) + return x + + +class Attention(nn.Module): + def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.): + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + # NOTE scale factor was wrong in my original version, can set manually to be compat with prev weights + self.scale = qk_scale or head_dim ** -0.5 + + self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + + def forward(self, x): + B, N, C = x.shape + qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4) + q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) + + attn = (q @ k.transpose(-2, -1)) * self.scale + attn = attn.softmax(dim=-1) + attn = self.attn_drop(attn) + + x = (attn @ v).transpose(1, 2).reshape(B, N, C) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class SABlock(nn.Module): + def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop=0., attn_drop=0., + drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm): + super().__init__() + self.pos_embed = nn.Conv2d(dim, dim, 3, padding=1, groups=dim) + self.norm1 = norm_layer(dim) + self.attn = Attention( + dim, + num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, + attn_drop=attn_drop, proj_drop=drop) + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) + + def forward(self, x): + x = x + self.pos_embed(x) + B, N, H, W = x.shape + x = x.flatten(2).transpose(1, 2) + x = x + self.drop_path(self.attn(self.norm1(x))) + x = x + self.drop_path(self.mlp(self.norm2(x))) + x = x.transpose(1, 2).reshape(B, N, H, W) + return x + + +def window_partition(x, window_size): + """ + Args: + x: (B, H, W, C) + window_size (int): window size + Returns: + windows: (num_windows*B, window_size, window_size, C) + """ + B, H, W, C = x.shape + x = x.view(B, H // window_size, window_size, W // window_size, window_size, C) + windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C) + return windows + + +def window_reverse(windows, window_size, H, W): + """ + Args: + windows: (num_windows*B, window_size, window_size, C) + window_size (int): Window size + H (int): Height of image + W (int): Width of image + Returns: + x: (B, H, W, C) + """ + B = int(windows.shape[0] / (H * W / window_size / window_size)) + x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1) + x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1) + return x + + +class SABlock_Windows(nn.Module): + def __init__(self, dim, num_heads, window_size=14, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop=0., attn_drop=0., + drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm): + super().__init__() + self.window_size=window_size + self.pos_embed = nn.Conv2d(dim, dim, 3, padding=1, groups=dim) + self.norm1 = norm_layer(dim) + self.attn = Attention( + dim, + num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, + attn_drop=attn_drop, proj_drop=drop) + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) + + def forward(self, x): + x = x + self.pos_embed(x) + x = x.permute(0, 2, 3, 1) + B, H, W, C = x.shape + shortcut = x + x = self.norm1(x) + + pad_l = pad_t = 0 + pad_r = (self.window_size - W % self.window_size) % self.window_size + pad_b = (self.window_size - H % self.window_size) % self.window_size + x = F.pad(x, (0, 0, pad_l, pad_r, pad_t, pad_b)) + _, Hp, Wp, _ = x.shape + + x_windows = window_partition(x, self.window_size) # nW*B, window_size, window_size, C + x_windows = x_windows.view(-1, self.window_size * self.window_size, C) # nW*B, window_size*window_size, C + + # W-MSA/SW-MSA + attn_windows = self.attn(x_windows) # nW*B, window_size*window_size, C + + # merge windows + attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C) + x = window_reverse(attn_windows, self.window_size, Hp, Wp) # B H' W' C + + # reverse cyclic shift + if pad_r > 0 or pad_b > 0: + x = x[:, :H, :W, :].contiguous() + + x = shortcut + self.drop_path(x) + x = x + self.drop_path(self.mlp(self.norm2(x))) + x = x.permute(0, 3, 1, 2).reshape(B, C, H, W) + return x + + +class PatchEmbed(nn.Module): + """ Image to Patch Embedding + """ + def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768): + super().__init__() + img_size = to_2tuple(img_size) + patch_size = to_2tuple(patch_size) + num_patches = (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0]) + self.img_size = img_size + self.patch_size = patch_size + self.num_patches = num_patches + self.norm = nn.LayerNorm(embed_dim) + self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size) + + def forward(self, x): + B, _, H, W = x.shape + x = self.proj(x) + B, _, H, W = x.shape + x = x.flatten(2).transpose(1, 2) + x = self.norm(x) + x = x.reshape(B, H, W, -1).permute(0, 3, 1, 2).contiguous() + return x + + +@BACKBONES.register_module() +class UniFormer(nn.Module): + """ Vision Transformer + A PyTorch impl of : `An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale` - + https://arxiv.org/abs/2010.11929 + """ + def __init__(self, layers=[3, 4, 8, 3], img_size=224, in_chans=3, num_classes=80, embed_dim=[64, 128, 320, 512], + head_dim=64, mlp_ratio=4., qkv_bias=True, qk_scale=None, representation_size=None, + drop_rate=0., attn_drop_rate=0., drop_path_rate=0., norm_layer=partial(nn.LayerNorm, eps=1e-6), + pretrained_path=None, use_checkpoint=False, checkpoint_num=[0, 0, 0, 0], + windows=False, hybrid=False, window_size=14): + """ + Args: + layer (list): number of block in each layer + img_size (int, tuple): input image size + in_chans (int): number of input channels + num_classes (int): number of classes for classification head + embed_dim (int): embedding dimension + head_dim (int): dimension of attention heads + mlp_ratio (int): ratio of mlp hidden dim to embedding dim + qkv_bias (bool): enable bias for qkv if True + qk_scale (float): override default qk scale of head_dim ** -0.5 if set + representation_size (Optional[int]): enable and set representation layer (pre-logits) to this value if set + drop_rate (float): dropout rate + attn_drop_rate (float): attention dropout rate + drop_path_rate (float): stochastic depth rate + norm_layer (nn.Module): normalization layer + pretrained_path (str): path of pretrained model + use_checkpoint (bool): whether use checkpoint + checkpoint_num (list): index for using checkpoint in every stage + windows (bool): whether use window MHRA + hybrid (bool): whether use hybrid MHRA + window_size (int): size of window (>14) + """ + super().__init__() + self.num_classes = num_classes + self.use_checkpoint = use_checkpoint + self.checkpoint_num = checkpoint_num + self.windows = windows + print(f'Use Checkpoint: {self.use_checkpoint}') + print(f'Checkpoint Number: {self.checkpoint_num}') + self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models + norm_layer = norm_layer or partial(nn.LayerNorm, eps=1e-6) + + self.patch_embed1 = PatchEmbed( + img_size=img_size, patch_size=4, in_chans=in_chans, embed_dim=embed_dim[0]) + self.patch_embed2 = PatchEmbed( + img_size=img_size // 4, patch_size=2, in_chans=embed_dim[0], embed_dim=embed_dim[1]) + self.patch_embed3 = PatchEmbed( + img_size=img_size // 8, patch_size=2, in_chans=embed_dim[1], embed_dim=embed_dim[2]) + self.patch_embed4 = PatchEmbed( + img_size=img_size // 16, patch_size=2, in_chans=embed_dim[2], embed_dim=embed_dim[3]) + + self.pos_drop = nn.Dropout(p=drop_rate) + dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(layers))] # stochastic depth decay rule + num_heads = [dim // head_dim for dim in embed_dim] + self.blocks1 = nn.ModuleList([ + CBlock( + dim=embed_dim[0], num_heads=num_heads[0], mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i], norm_layer=norm_layer) + for i in range(layers[0])]) + self.norm1=norm_layer(embed_dim[0]) + self.blocks2 = nn.ModuleList([ + CBlock( + dim=embed_dim[1], num_heads=num_heads[1], mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i+layers[0]], norm_layer=norm_layer) + for i in range(layers[1])]) + self.norm2 = norm_layer(embed_dim[1]) + if self.windows: + print('Use local window for all blocks in stage3') + self.blocks3 = nn.ModuleList([ + SABlock_Windows( + dim=embed_dim[2], num_heads=num_heads[2], window_size=window_size, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i+layers[0]+layers[1]], norm_layer=norm_layer) + for i in range(layers[2])]) + elif hybrid: + print('Use hybrid window for blocks in stage3') + block3 = [] + for i in range(layers[2]): + if (i + 1) % 4 == 0: + block3.append(SABlock( + dim=embed_dim[2], num_heads=num_heads[2], mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i+layers[0]+layers[1]], norm_layer=norm_layer)) + else: + block3.append(SABlock_Windows( + dim=embed_dim[2], num_heads=num_heads[2], window_size=window_size, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i+layers[0]+layers[1]], norm_layer=norm_layer)) + self.blocks3 = nn.ModuleList(block3) + else: + print('Use global window for all blocks in stage3') + self.blocks3 = nn.ModuleList([ + SABlock( + dim=embed_dim[2], num_heads=num_heads[2], mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i+layers[0]+layers[1]], norm_layer=norm_layer) + for i in range(layers[2])]) + self.norm3 = norm_layer(embed_dim[2]) + self.blocks4 = nn.ModuleList([ + SABlock( + dim=embed_dim[3], num_heads=num_heads[3], mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i+layers[0]+layers[1]+layers[2]], norm_layer=norm_layer) + for i in range(layers[3])]) + self.norm4 = norm_layer(embed_dim[3]) + + # Representation layer + if representation_size: + self.num_features = representation_size + self.pre_logits = nn.Sequential(OrderedDict([ + ('fc', nn.Linear(embed_dim, representation_size)), + ('act', nn.Tanh()) + ])) + else: + self.pre_logits = nn.Identity() + + self.apply(self._init_weights) + self.init_weights(pretrained=pretrained_path) + + def init_weights(self, pretrained): + if isinstance(pretrained, str): + logger = get_root_logger() + load_checkpoint(self, pretrained, map_location='cpu', strict=False, logger=logger) + print(f'Load pretrained model from {pretrained}') + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight, std=.02) + if isinstance(m, nn.Linear) and m.bias is not None: + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.LayerNorm): + nn.init.constant_(m.bias, 0) + nn.init.constant_(m.weight, 1.0) + + @torch.jit.ignore + def no_weight_decay(self): + return {'pos_embed', 'cls_token'} + + def get_classifier(self): + return self.head + + def reset_classifier(self, num_classes, global_pool=''): + self.num_classes = num_classes + self.head = nn.Linear(self.embed_dim, num_classes) if num_classes > 0 else nn.Identity() + + def forward_features(self, x): + out = [] + x = self.patch_embed1(x) + x = self.pos_drop(x) + for i, blk in enumerate(self.blocks1): + if self.use_checkpoint and i < self.checkpoint_num[0]: + x = checkpoint.checkpoint(blk, x) + else: + x = blk(x) + x_out = self.norm1(x.permute(0, 2, 3, 1)) + out.append(x_out.permute(0, 3, 1, 2).contiguous()) + x = self.patch_embed2(x) + for i, blk in enumerate(self.blocks2): + if self.use_checkpoint and i < self.checkpoint_num[1]: + x = checkpoint.checkpoint(blk, x) + else: + x = blk(x) + x_out = self.norm2(x.permute(0, 2, 3, 1)) + out.append(x_out.permute(0, 3, 1, 2).contiguous()) + x = self.patch_embed3(x) + for i, blk in enumerate(self.blocks3): + if self.use_checkpoint and i < self.checkpoint_num[2]: + x = checkpoint.checkpoint(blk, x) + else: + x = blk(x) + x_out = self.norm3(x.permute(0, 2, 3, 1)) + out.append(x_out.permute(0, 3, 1, 2).contiguous()) + x = self.patch_embed4(x) + for i, blk in enumerate(self.blocks4): + if self.use_checkpoint and i < self.checkpoint_num[3]: + x = checkpoint.checkpoint(blk, x) + else: + x = blk(x) + x_out = self.norm4(x.permute(0, 2, 3, 1)) + out.append(x_out.permute(0, 3, 1, 2).contiguous()) + return tuple(out) + + def forward(self, x): + x = self.forward_features(x) + return x diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/upernet_global_small.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/upernet_global_small.py new file mode 100644 index 0000000000000000000000000000000000000000..16b14768b80035b52a9a975af67c23c1c7693265 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/uniformer/upernet_global_small.py @@ -0,0 +1,44 @@ +_base_ = [ + 'configs/_base_/models/upernet_uniformer.py', + 'configs/_base_/datasets/ade20k.py', + 'configs/_base_/default_runtime.py', + 'configs/_base_/schedules/schedule_160k.py' +] + +custom_imports = dict( + imports=['annotator.uniformer.uniformer'], + allow_failed_imports=False +) + +model = dict( + backbone=dict( + type='UniFormer', + embed_dim=[64, 128, 320, 512], + layers=[3, 4, 8, 3], + head_dim=64, + drop_path_rate=0.25, + windows=False, + hybrid=False + ), + decode_head=dict( + in_channels=[64, 128, 320, 512], + num_classes=150 + ), + auxiliary_head=dict( + in_channels=320, + num_classes=150 + )) + +# AdamW optimizer, no weight decay for position embedding & layer norm in backbone +optimizer = dict(_delete_=True, type='AdamW', lr=0.00006, betas=(0.9, 0.999), weight_decay=0.01, + paramwise_cfg=dict(custom_keys={'absolute_pos_embed': dict(decay_mult=0.), + 'relative_position_bias_table': dict(decay_mult=0.), + 'norm': dict(decay_mult=0.)})) + +lr_config = dict(_delete_=True, policy='poly', + warmup='linear', + warmup_iters=1500, + warmup_ratio=1e-6, + power=1.0, min_lr=0.0, by_epoch=False) + +data=dict(samples_per_gpu=2) \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/util.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/util.py new file mode 100644 index 0000000000000000000000000000000000000000..7cde937016b7a24b4081dc0565b53c16a87939d2 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/util.py @@ -0,0 +1,34 @@ +import numpy as np +import cv2 + + +def HWC3(x): + assert x.dtype == np.uint8 + if x.ndim == 2: + x = x[:, :, None] + assert x.ndim == 3 + H, W, C = x.shape + assert C == 1 or C == 3 or C == 4 + if C == 3: + return x + if C == 1: + return np.concatenate([x, x, x], axis=2) + if C == 4: + color = x[:, :, 0:3].astype(np.float32) + alpha = x[:, :, 3:4].astype(np.float32) / 255.0 + y = color * alpha + 255.0 * (1.0 - alpha) + y = y.clip(0, 255).astype(np.uint8) + return y + + +def resize_image(input_image, resolution): + H, W, C = input_image.shape + H = float(H) + W = float(W) + k = float(resolution) / min(H, W) + H *= k + W *= k + H = int(np.round(H / 64.0)) * 64 + W = int(np.round(W / 64.0)) * 64 + img = cv2.resize(input_image, (W, H), interpolation=cv2.INTER_LANCZOS4 if k > 1 else cv2.INTER_AREA) + return img diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/example/api_img2img.ipynb b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/example/api_img2img.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..07f244096c292be46c9d92c19c820633d6c1b41c --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/example/api_img2img.ipynb @@ -0,0 +1,105 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# controlnet + img2img\n", + "# enable `Allow other script to control this extension` in settings\n", + "\n", + "import requests\n", + "import cv2\n", + "from base64 import b64encode\n", + "\n", + "def readImage(path):\n", + " img = cv2.imread(path)\n", + " retval, buffer = cv2.imencode('.jpg', img)\n", + " b64img = b64encode(buffer).decode(\"utf-8\")\n", + " return b64img\n", + "\n", + "b64img = readImage(\"/root/workspace/nahida/0e17302b9bfa15402f783c29c0d1d34f.jpg\")\n", + "\n", + "class controlnetRequest():\n", + " def __init__(self, prompt):\n", + " self.url = \"http://localhost:7860/controlnet/img2img\"\n", + " self.body = {\n", + " \"init_images\": [b64img],\n", + " \"prompt\": prompt,\n", + " \"negative_prompt\": \"\",\n", + " \"seed\": -1,\n", + " \"subseed\": -1,\n", + " \"subseed_strength\": 0,\n", + " \"batch_size\": 1,\n", + " \"n_iter\": 1,\n", + " \"steps\": 20,\n", + " \"cfg_scale\": 7,\n", + " \"width\": 512,\n", + " \"height\": 768,\n", + " \"restore_faces\": True,\n", + " \"eta\": 0,\n", + " \"sampler_index\": \"Euler a\",\n", + " \"controlnet_input_image\": [b64img],\n", + " \"controlnet_module\": 'canny',\n", + " \"controlnet_model\": 'control_canny-fp16 [e3fe7712]',\n", + " \"controlnet_guidance\": 1.0,\n", + " }\n", + "\n", + " def sendRequest(self):\n", + " r = requests.post(self.url, json=self.body)\n", + " return r.json()\n", + "\n", + "js = controlnetRequest(\"walter white\").sendRequest()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import io, base64\n", + "import matplotlib.pyplot as plt\n", + "from PIL import Image\n", + "\n", + "pil_img = Image.open('/root/workspace/nahida/0e17302b9bfa15402f783c29c0d1d34f.jpg')\n", + "image = Image.open(io.BytesIO(base64.b64decode(js[\"images\"][0])))\n", + "mask_image = Image.open(io.BytesIO(base64.b64decode(js[\"images\"][1])))\n", + "\n", + "plt.figure()\n", + "f, axarr = plt.subplots(1,3) \n", + "axarr[0].imshow(pil_img) \n", + "axarr[1].imshow(image) \n", + "axarr[2].imshow(mask_image) " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pynb", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "d73345514d8c18d9a1da7351d222dbd2834c7f4a09e728a0d1f4c4580fbec206" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/example/api_txt2img.ipynb b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/example/api_txt2img.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a52c8f4de2870a10d01a92ee0a0aa9cecfb038a1 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/example/api_txt2img.ipynb @@ -0,0 +1,104 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# controlnet + txt2img\n", + "# enable `Allow other script to control this extension` in settings\n", + "\n", + "import requests\n", + "import cv2\n", + "from base64 import b64encode\n", + "\n", + "def readImage(path):\n", + " img = cv2.imread(path)\n", + " retval, buffer = cv2.imencode('.jpg', img)\n", + " b64img = b64encode(buffer).decode(\"utf-8\")\n", + " return b64img\n", + "\n", + "b64img = readImage(\"/root/workspace/nahida/0e17302b9bfa15402f783c29c0d1d34f.jpg\")\n", + "\n", + "class controlnetRequest():\n", + " def __init__(self, prompt):\n", + " self.url = \"http://localhost:7860/controlnet/txt2img\"\n", + " self.body = {\n", + " \"prompt\": prompt,\n", + " \"negative_prompt\": \"\",\n", + " \"seed\": -1,\n", + " \"subseed\": -1,\n", + " \"subseed_strength\": 0,\n", + " \"batch_size\": 1,\n", + " \"n_iter\": 1,\n", + " \"steps\": 15,\n", + " \"cfg_scale\": 7,\n", + " \"width\": 512,\n", + " \"height\": 768,\n", + " \"restore_faces\": True,\n", + " \"eta\": 0,\n", + " \"sampler_index\": \"Euler a\",\n", + " \"controlnet_input_image\": [b64img],\n", + " \"controlnet_module\": 'canny',\n", + " \"controlnet_model\": 'control_canny-fp16 [e3fe7712]',\n", + " \"controlnet_guidance\": 1.0,\n", + " }\n", + "\n", + " def sendRequest(self):\n", + " r = requests.post(self.url, json=self.body)\n", + " return r.json()\n", + "\n", + "js = controlnetRequest(\"walter white\").sendRequest()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import io, base64\n", + "import matplotlib.pyplot as plt\n", + "from PIL import Image\n", + "\n", + "pil_img = Image.open('/root/workspace/nahida/0e17302b9bfa15402f783c29c0d1d34f.jpg')\n", + "image = Image.open(io.BytesIO(base64.b64decode(js[\"images\"][0])))\n", + "mask_image = Image.open(io.BytesIO(base64.b64decode(js[\"images\"][1])))\n", + "\n", + "plt.figure()\n", + "f, axarr = plt.subplots(1,3) \n", + "axarr[0].imshow(pil_img) \n", + "axarr[1].imshow(image) \n", + "axarr[2].imshow(mask_image) " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pynb", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "d73345514d8c18d9a1da7351d222dbd2834c7f4a09e728a0d1f4c4580fbec206" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/extract_controlnet.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/extract_controlnet.py new file mode 100644 index 0000000000000000000000000000000000000000..d8752b534098b168c123ff253083d280f2aba907 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/extract_controlnet.py @@ -0,0 +1,25 @@ +import argparse +import torch +from safetensors.torch import load_file, save_file + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--src", default=None, type=str, required=True, help="Path to the model to convert.") + parser.add_argument("--dst", default=None, type=str, required=True, help="Path to the output model.") + args = parser.parse_args() + + assert args.src is not None, "Must provide a model path!" + assert args.dst is not None, "Must provide a checkpoint path!" + + if args.src.endswith(".safetensors"): + state_dict = load_file(args.src) + else: + state_dict = torch.load(args.src) + + if any([k.startswith("control_model.") for k, v in state_dict.items()]): + state_dict = {k.replace("control_model.", ""): v for k, v in state_dict.items() if k.startswith("control_model.")} + + if args.dst.endswith(".safetensors"): + save_file(state_dict, args.dst) + else: + torch.save({"state_dict": state_dict}, args.dst) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/extract_controlnet_diff.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/extract_controlnet_diff.py new file mode 100644 index 0000000000000000000000000000000000000000..dc2ca118d7664a9990834e485dc2539d2ce45f24 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/extract_controlnet_diff.py @@ -0,0 +1,91 @@ +import argparse +import torch +from safetensors.torch import load_file, save_file + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--sd15", default=None, type=str, required=True, help="Path to the original sd15.") + parser.add_argument("--control", default=None, type=str, required=True, help="Path to the sd15 with control.") + parser.add_argument("--dst", default=None, type=str, required=True, help="Path to the output difference model.") + parser.add_argument("--fp16", action="store_true", help="Save as fp16.") + parser.add_argument("--bf16", action="store_true", help="Save as bf16.") + args = parser.parse_args() + + assert args.sd15 is not None, "Must provide a original sd15 model path!" + assert args.control is not None, "Must provide a sd15 with control model path!" + assert args.dst is not None, "Must provide a output path!" + + # make differences: copy from https://github.com/lllyasviel/ControlNet/blob/main/tool_transfer_control.py + + def get_node_name(name, parent_name): + if len(name) <= len(parent_name): + return False, '' + p = name[:len(parent_name)] + if p != parent_name: + return False, '' + return True, name[len(parent_name):] + + # remove first/cond stage from sd to reduce memory usage + def remove_first_and_cond(sd): + keys = list(sd.keys()) + for key in keys: + is_first_stage, _ = get_node_name(key, 'first_stage_model') + is_cond_stage, _ = get_node_name(key, 'cond_stage_model') + if is_first_stage or is_cond_stage: + sd.pop(key, None) + return sd + + print(f"loading: {args.sd15}") + if args.sd15.endswith(".safetensors"): + sd15_state_dict = load_file(args.sd15) + else: + sd15_state_dict = torch.load(args.sd15) + sd15_state_dict = sd15_state_dict.pop("state_dict", sd15_state_dict) + sd15_state_dict = remove_first_and_cond(sd15_state_dict) + + print(f"loading: {args.control}") + if args.control.endswith(".safetensors"): + control_state_dict = load_file(args.control) + else: + control_state_dict = torch.load(args.control) + control_state_dict = remove_first_and_cond(control_state_dict) + + # make diff of original and control + print(f"create difference") + keys = list(control_state_dict.keys()) + final_state_dict = {"difference": torch.tensor(1.0)} # indicates difference + for key in keys: + p = control_state_dict.pop(key) + + is_control, node_name = get_node_name(key, 'control_') + if not is_control: + continue + + sd15_key_name = 'model.diffusion_' + node_name + if sd15_key_name in sd15_state_dict: # part of U-Net + # print("in sd15", key, sd15_key_name) + p_new = p - sd15_state_dict.pop(sd15_key_name) + if torch.max(torch.abs(p_new)) < 1e-6: # no difference? + print("no diff", key, sd15_key_name) + continue + else: + # print("not in sd15", key, sd15_key_name) + p_new = p # hint or zero_conv + + final_state_dict[key] = p_new + + save_dtype = None + if args.fp16: + save_dtype = torch.float16 + elif args.bf16: + save_dtype = torch.bfloat16 + if save_dtype is not None: + for key in final_state_dict.keys(): + final_state_dict[key] = final_state_dict[key].to(save_dtype) + + print("saving difference.") + if args.dst.endswith(".safetensors"): + save_file(final_state_dict, args.dst) + else: + torch.save({"state_dict": final_state_dict}, args.dst) + print("done!") diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/install.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/install.py new file mode 100644 index 0000000000000000000000000000000000000000..f60af764af3478ba51b529d9c89bb7fe4b057655 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/install.py @@ -0,0 +1,10 @@ +import launch +import os + +req_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "requirements.txt") + +with open(req_file) as file: + for lib in file: + lib = lib.strip() + if not launch.is_installed(lib): + launch.run_pip(f"install {lib}", f"sd-webui-controlnet requirement: {lib}") \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/cldm_v15.yaml b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/cldm_v15.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fde1825577acd46dc90d8d7c6730e22be762fccb --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/cldm_v15.yaml @@ -0,0 +1,79 @@ +model: + target: cldm.cldm.ControlLDM + params: + linear_start: 0.00085 + linear_end: 0.0120 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + control_key: "hint" + image_size: 64 + channels: 4 + cond_stage_trainable: false + conditioning_key: crossattn + monitor: val/loss_simple_ema + scale_factor: 0.18215 + use_ema: False + only_mid_control: False + + control_stage_config: + target: cldm.cldm.ControlNet + params: + image_size: 32 # unused + in_channels: 4 + hint_channels: 3 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_heads: 8 + use_spatial_transformer: True + transformer_depth: 1 + context_dim: 768 + use_checkpoint: True + legacy: False + + unet_config: + target: cldm.cldm.ControlledUnetModel + params: + image_size: 32 # unused + in_channels: 4 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_heads: 8 + use_spatial_transformer: True + transformer_depth: 1 + context_dim: 768 + use_checkpoint: True + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenCLIPEmbedder diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/cldm_v21.yaml b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/cldm_v21.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fc65193647e476e108fce5977f11250d55919106 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/cldm_v21.yaml @@ -0,0 +1,85 @@ +model: + target: cldm.cldm.ControlLDM + params: + linear_start: 0.00085 + linear_end: 0.0120 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + control_key: "hint" + image_size: 64 + channels: 4 + cond_stage_trainable: false + conditioning_key: crossattn + monitor: val/loss_simple_ema + scale_factor: 0.18215 + use_ema: False + only_mid_control: False + + control_stage_config: + target: cldm.cldm.ControlNet + params: + use_checkpoint: True + image_size: 32 # unused + in_channels: 4 + hint_channels: 3 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_head_channels: 64 # need to fix for flash-attn + use_spatial_transformer: True + use_linear_in_transformer: True + transformer_depth: 1 + context_dim: 1024 + legacy: False + + unet_config: + target: cldm.cldm.ControlledUnetModel + params: + use_checkpoint: True + image_size: 32 # unused + in_channels: 4 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_head_channels: 64 # need to fix for flash-attn + use_spatial_transformer: True + use_linear_in_transformer: True + transformer_depth: 1 + context_dim: 1024 + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + #attn_type: "vanilla-xformers" + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder + params: + freeze: True + layer: "penultimate" diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/image_adapter_v14.yaml b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/image_adapter_v14.yaml new file mode 100644 index 0000000000000000000000000000000000000000..439d33cc53a349c9b8c1a0091cbd3643359216d5 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/image_adapter_v14.yaml @@ -0,0 +1,9 @@ +model: + target: tencentarc.t21_adapter + params: + channels: [320, 640, 1280, 1280] + nums_rb: 2 + ksize: 1 + sk: true + cin: 192 + use_conv: false \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/sketch_adapter_v14.yaml b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/sketch_adapter_v14.yaml new file mode 100644 index 0000000000000000000000000000000000000000..686c5f172bf941ffaaee58b912245d6ffb36f4d3 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/sketch_adapter_v14.yaml @@ -0,0 +1,9 @@ +model: + target: tencentarc.t21_adapter + params: + channels: [320, 640, 1280, 1280] + nums_rb: 2 + ksize: 1 + sk: true + cin: 64 + use_conv: false \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/t2iadapter_color_sd14v1.yaml b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/t2iadapter_color_sd14v1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6780dd94ca6abfe58b4e3dcce1206b902fc3d540 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/t2iadapter_color_sd14v1.yaml @@ -0,0 +1,6 @@ +model: + target: scripts.adapter.Adapter_light + params: + channels: [320, 640, 1280, 1280] + nums_rb: 4 + cin: 192 \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/t2iadapter_keypose_sd14v1.yaml b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/t2iadapter_keypose_sd14v1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..439d33cc53a349c9b8c1a0091cbd3643359216d5 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/t2iadapter_keypose_sd14v1.yaml @@ -0,0 +1,9 @@ +model: + target: tencentarc.t21_adapter + params: + channels: [320, 640, 1280, 1280] + nums_rb: 2 + ksize: 1 + sk: true + cin: 192 + use_conv: false \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/t2iadapter_style_sd14v1.yaml b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/t2iadapter_style_sd14v1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1f634fbe7e46b9e4057298af395e0a28ac1516cf --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/models/t2iadapter_style_sd14v1.yaml @@ -0,0 +1,8 @@ +model: + target: scripts.adapter.StyleAdapter + params: + width: 1024 + context_dim: 768 + num_head: 8 + n_layes: 3 + num_token: 8 \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/preload.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/preload.py new file mode 100644 index 0000000000000000000000000000000000000000..868801e1c81028eb30114439fab1d421ce9de45d --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/preload.py @@ -0,0 +1,3 @@ +def preload(parser): + parser.add_argument("--controlnet-dir", type=str, help="Path to directory with ControlNet models", default=None) + parser.add_argument("--no-half-controlnet", action='store_true', help="do not switch the ControlNet models to 16-bit floats (only needed without --no-half)", default=None) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/requirements.txt b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..7567a8990a313e3c39564f208c4e4f7ff3a04116 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/requirements.txt @@ -0,0 +1 @@ +svglib \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/an-gen.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/an-gen.png new file mode 100644 index 0000000000000000000000000000000000000000..128292ec8e536e80e10a79e19b8b8dd234b3dd5f Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/an-gen.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/an-pose.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/an-pose.png new file mode 100644 index 0000000000000000000000000000000000000000..83b92e38fa105876be558aaf47ea16dc483ee8c1 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/an-pose.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/an-source.jpg b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/an-source.jpg new file mode 100644 index 0000000000000000000000000000000000000000..01e2bddb52386de3b9e15890da1bc9c9ad2dfdaa Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/an-source.jpg differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/bal-gen.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/bal-gen.png new file mode 100644 index 0000000000000000000000000000000000000000..a3ac24285b27df4e2775fda6ff8405323efa99fd Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/bal-gen.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/bal-source.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/bal-source.png new file mode 100644 index 0000000000000000000000000000000000000000..7f77950fb260156378e82a95354fab15176ccd0b Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/bal-source.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/cat_out-2.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/cat_out-2.png new file mode 100644 index 0000000000000000000000000000000000000000..b0e5509a68c741e6bd8222178ff1cef3e331696a Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/cat_out-2.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/cat_sk-2.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/cat_sk-2.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf583585aa874fb98a7a2167313c3e510a13c65 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/cat_sk-2.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/dog_out-2.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/dog_out-2.png new file mode 100644 index 0000000000000000000000000000000000000000..a6781d6b1f44e742c944a1a89348d93c897105a0 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/dog_out-2.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/dog_rel.jpg b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/dog_rel.jpg new file mode 100644 index 0000000000000000000000000000000000000000..78a6d812a6c9b0d10ef325e19fdd40b53d728569 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/dog_rel.jpg differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/dog_rel.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/dog_rel.png new file mode 100644 index 0000000000000000000000000000000000000000..a67da581a1086fffdc03f289104e98d6bd123698 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/dog_rel.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/dog_sk-2.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/dog_sk-2.png new file mode 100644 index 0000000000000000000000000000000000000000..b272f79dabf656daf6f43cf8d74d419801aa0526 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/dog_sk-2.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/evt_gen.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/evt_gen.png new file mode 100644 index 0000000000000000000000000000000000000000..5d0dbf1c62b5b7cb0b76140ea890389105fc5789 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/evt_gen.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/evt_hed.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/evt_hed.png new file mode 100644 index 0000000000000000000000000000000000000000..fa7feb782fd4938f22afa31cdd55d9cb738113b1 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/evt_hed.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/evt_source.jpg b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/evt_source.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0a21210a73535e56649948be7a92dcf9da15b47f Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/evt_source.jpg differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/fs_input.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/fs_input.png new file mode 100644 index 0000000000000000000000000000000000000000..4ec7b353dd0d7f4612884d4ada75c5f08d7fbea0 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/fs_input.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/fs_output.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/fs_output.png new file mode 100644 index 0000000000000000000000000000000000000000..44717b870c27379e8d44ed12caee7c896c095b44 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/fs_output.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/kp_a-2.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/kp_a-2.png new file mode 100644 index 0000000000000000000000000000000000000000..12a77f8e581201dfb46282da3724e4d99677c712 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/kp_a-2.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/kp_a2-2.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/kp_a2-2.png new file mode 100644 index 0000000000000000000000000000000000000000..8db29aed95122542fb01dc786f3226a62d6e8e9e Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/kp_a2-2.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/kp_o-2.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/kp_o-2.png new file mode 100644 index 0000000000000000000000000000000000000000..2dbaa80589467b7644578fbcba3bd5df861bcaaa Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/kp_o-2.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/kp_o2-2.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/kp_o2-2.png new file mode 100644 index 0000000000000000000000000000000000000000..2c44f3ddc7f1bcca8a7df95bd0f8bea1d8f5a616 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/kp_o2-2.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/mahiro-out.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/mahiro-out.png new file mode 100644 index 0000000000000000000000000000000000000000..d1eb02503a54e300b60fe42cb147fbd9f0da2f8b Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/mahiro-out.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/mahiro_canny.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/mahiro_canny.png new file mode 100644 index 0000000000000000000000000000000000000000..318f4fae461389d67c9552d372b4aa0cc5997efa Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/mahiro_canny.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/mahiro_input.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/mahiro_input.png new file mode 100644 index 0000000000000000000000000000000000000000..0ee95dde70fc94916b1506cb276604e5d12aea02 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/mahiro_input.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/nm-gen.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/nm-gen.png new file mode 100644 index 0000000000000000000000000000000000000000..ce7321fe85e44caf95acef442a892ec1731f106e Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/nm-gen.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/nm-out.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/nm-out.png new file mode 100644 index 0000000000000000000000000000000000000000..75bb68c07eccba205ec4245be478e199a0f2b442 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/nm-out.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/nm-src.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/nm-src.png new file mode 100644 index 0000000000000000000000000000000000000000..125f13aa39f8b70c1aac987efd8ceb3c0147ea8e Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/nm-src.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/sk-b-dep.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/sk-b-dep.png new file mode 100644 index 0000000000000000000000000000000000000000..1896956386c1416cdf17b5be6e24995a122527aa Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/sk-b-dep.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/sk-b-out.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/sk-b-out.png new file mode 100644 index 0000000000000000000000000000000000000000..6bf4670c01c5e22e27fba2223ce51cf3931aa5de Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/sk-b-out.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/sk-b-src.png b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/sk-b-src.png new file mode 100644 index 0000000000000000000000000000000000000000..1bece79872480e2f5d60777688fbfe42e6215647 Binary files /dev/null and b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/samples/sk-b-src.png differ diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/adapter.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/adapter.py new file mode 100644 index 0000000000000000000000000000000000000000..c7809e8e28108359af345ecbb85c79c824a63a16 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/adapter.py @@ -0,0 +1,397 @@ + + +import torch +import torch.nn as nn +import importlib +from collections import OrderedDict + +from omegaconf import OmegaConf +from copy import deepcopy +from modules import devices, lowvram, shared, scripts +from ldm.modules.diffusionmodules.util import timestep_embedding +from ldm.modules.diffusionmodules.openaimodel import UNetModel + + +class TorchHijackForUnet: + """ + This is torch, but with cat that resizes tensors to appropriate dimensions if they do not match; + this makes it possible to create pictures with dimensions that are multiples of 8 rather than 64 + """ + + def __getattr__(self, item): + if item == 'cat': + return self.cat + + if hasattr(torch, item): + return getattr(torch, item) + + raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, item)) + + def cat(self, tensors, *args, **kwargs): + if len(tensors) == 2: + a, b = tensors + if a.shape[-2:] != b.shape[-2:]: + a = torch.nn.functional.interpolate(a, b.shape[-2:], mode="nearest") + + tensors = (a, b) + + return torch.cat(tensors, *args, **kwargs) + + +th = TorchHijackForUnet() + + +def align(hint, size): + b, c, h1, w1 = hint.shape + h, w = size + if h != h1 or w != w1: + hint = th.nn.functional.interpolate(hint, size=size, mode="nearest") + return hint + + +def get_node_name(name, parent_name): + if len(name) <= len(parent_name): + return False, '' + p = name[:len(parent_name)] + if p != parent_name: + return False, '' + return True, name[len(parent_name):] + + +def get_obj_from_str(string, reload=False): + module, cls = string.rsplit(".", 1) + if reload: + module_imp = importlib.import_module(module) + importlib.reload(module_imp) + return getattr(importlib.import_module(module, package=None), cls) + + +class PlugableAdapter(nn.Module): + def __init__(self, state_dict, config_path, lowvram=False, base_model=None) -> None: + super().__init__() + config = OmegaConf.load(config_path) + model = Adapter + try: + self.target = config.model.target + model = get_obj_from_str(config.model.target) + except ImportError: + pass + + self.control_model = model(**config.model.params) + self.control_model.load_state_dict(state_dict) + self.lowvram = lowvram + self.control = None + self.hint_cond = None + + if not self.lowvram: + self.control_model.to(devices.get_device_for("controlnet")) + + def reset(self): + self.control = None + self.hint_cond = None + + def forward(self, hint=None, *args, **kwargs): + if self.control is not None: + return deepcopy(self.control) + + self.hint_cond = hint + hint_in = hint + if hasattr(self.control_model, 'conv_in') and self.control_model.conv_in.in_channels == 64: + hint_in = hint_in[0].unsqueeze(0).unsqueeze(0) + else: + hint_in = hint_in.unsqueeze(0) + + self.control = self.control_model(hint_in) + return deepcopy(self.control) + + +def conv_nd(dims, *args, **kwargs): + """ + Create a 1D, 2D, or 3D convolution module. + """ + if dims == 1: + return nn.Conv1d(*args, **kwargs) + elif dims == 2: + return nn.Conv2d(*args, **kwargs) + elif dims == 3: + return nn.Conv3d(*args, **kwargs) + raise ValueError(f"unsupported dimensions: {dims}") + +def avg_pool_nd(dims, *args, **kwargs): + """ + Create a 1D, 2D, or 3D average pooling module. + """ + if dims == 1: + return nn.AvgPool1d(*args, **kwargs) + elif dims == 2: + return nn.AvgPool2d(*args, **kwargs) + elif dims == 3: + return nn.AvgPool3d(*args, **kwargs) + raise ValueError(f"unsupported dimensions: {dims}") + + +class Downsample(nn.Module): + """ + A downsampling layer with an optional convolution. + :param channels: channels in the inputs and outputs. + :param use_conv: a bool determining if a convolution is applied. + :param dims: determines if the signal is 1D, 2D, or 3D. If 3D, then + downsampling occurs in the inner-two dimensions. + """ + + def __init__(self, channels, use_conv, dims=2, out_channels=None,padding=1): + super().__init__() + self.channels = channels + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.dims = dims + stride = 2 if dims != 3 else (1, 2, 2) + if use_conv: + self.op = conv_nd( + dims, self.channels, self.out_channels, 3, stride=stride, padding=padding + ) + else: + assert self.channels == self.out_channels + self.op = avg_pool_nd(dims, kernel_size=stride, stride=stride) + + def forward(self, x): + assert x.shape[1] == self.channels + return self.op(x) + + +class ResnetBlock(nn.Module): + def __init__(self, in_c, out_c, down, ksize=3, sk=False, use_conv=True): + super().__init__() + ps = ksize//2 + if in_c != out_c or sk==False: + self.in_conv = nn.Conv2d(in_c, out_c, ksize, 1, ps) + else: + # print('n_in') + self.in_conv = None + self.block1 = nn.Conv2d(out_c, out_c, 3, 1, 1) + self.act = nn.ReLU() + self.block2 = nn.Conv2d(out_c, out_c, ksize, 1, ps) + if sk==False: + self.skep = nn.Conv2d(in_c, out_c, ksize, 1, ps) + else: + # print('n_sk') + self.skep = None + + self.down = down + if self.down == True: + self.down_opt = Downsample(in_c, use_conv=use_conv) + + def forward(self, x): + if self.down == True: + x = self.down_opt(x) + if self.in_conv is not None: # edit + h = self.in_conv(x) + # x = self.in_conv(x) + # else: + # x = x + + h = self.block1(h) + h = self.act(h) + h = self.block2(h) + if self.skep is not None: + return h + self.skep(x) + else: + return h + x + + +class ResnetBlock(nn.Module): + def __init__(self, in_c, out_c, down, ksize=3, sk=False, use_conv=True): + super().__init__() + ps = ksize//2 + if in_c != out_c or sk==False: + self.in_conv = nn.Conv2d(in_c, out_c, ksize, 1, ps) + else: + # print('n_in') + self.in_conv = None + self.block1 = nn.Conv2d(out_c, out_c, 3, 1, 1) + self.act = nn.ReLU() + self.block2 = nn.Conv2d(out_c, out_c, ksize, 1, ps) + if sk==False: + self.skep = nn.Conv2d(in_c, out_c, ksize, 1, ps) + else: + self.skep = None + + self.down = down + if self.down == True: + self.down_opt = Downsample(in_c, use_conv=use_conv) + + def forward(self, x): + if self.down == True: + x = self.down_opt(x) + if self.in_conv is not None: # edit + x = self.in_conv(x) + + h = self.block1(x) + h = self.act(h) + h = self.block2(h) + if self.skep is not None: + return h + self.skep(x) + else: + return h + x + + +class Adapter(nn.Module): + def __init__(self, channels=[320, 640, 1280, 1280], nums_rb=3, cin=64, ksize=3, sk=False, use_conv=True): + super(Adapter, self).__init__() + self.unshuffle = nn.PixelUnshuffle(8) + self.channels = channels + self.nums_rb = nums_rb + self.body = [] + for i in range(len(channels)): + for j in range(nums_rb): + if (i!=0) and (j==0): + self.body.append(ResnetBlock(channels[i-1], channels[i], down=True, ksize=ksize, sk=sk, use_conv=use_conv)) + else: + self.body.append(ResnetBlock(channels[i], channels[i], down=False, ksize=ksize, sk=sk, use_conv=use_conv)) + self.body = nn.ModuleList(self.body) + self.conv_in = nn.Conv2d(cin, channels[0], 3, 1, 1) + + def forward(self, x): + # unshuffle + x = self.unshuffle(x) + # extract features + features = [] + x = self.conv_in(x) + for i in range(len(self.channels)): + for j in range(self.nums_rb): + idx = i*self.nums_rb +j + x = self.body[idx](x) + features.append(x) + + return features + +class LayerNorm(nn.LayerNorm): + """Subclass torch's LayerNorm to handle fp16.""" + + def forward(self, x: torch.Tensor): + orig_type = x.dtype + ret = super().forward(x.type(torch.float32)) + return ret.type(orig_type) + + +class QuickGELU(nn.Module): + + def forward(self, x: torch.Tensor): + return x * torch.sigmoid(1.702 * x) + + +class ResidualAttentionBlock(nn.Module): + + def __init__(self, d_model: int, n_head: int, attn_mask: torch.Tensor = None): + super().__init__() + + self.attn = nn.MultiheadAttention(d_model, n_head) + self.ln_1 = LayerNorm(d_model) + self.mlp = nn.Sequential( + OrderedDict([("c_fc", nn.Linear(d_model, d_model * 4)), ("gelu", QuickGELU()), + ("c_proj", nn.Linear(d_model * 4, d_model))])) + self.ln_2 = LayerNorm(d_model) + self.attn_mask = attn_mask + + def attention(self, x: torch.Tensor): + self.attn_mask = self.attn_mask.to(dtype=x.dtype, device=x.device) if self.attn_mask is not None else None + return self.attn(x, x, x, need_weights=False, attn_mask=self.attn_mask)[0] + + def forward(self, x: torch.Tensor): + x = x + self.attention(self.ln_1(x)) + x = x + self.mlp(self.ln_2(x)) + return x + + +class StyleAdapter(nn.Module): + + def __init__(self, width=1024, context_dim=768, num_head=8, n_layes=3, num_token=4): + super().__init__() + + scale = width ** -0.5 + self.transformer_layes = nn.Sequential(*[ResidualAttentionBlock(width, num_head) for _ in range(n_layes)]) + self.num_token = num_token + self.style_embedding = nn.Parameter(torch.randn(1, num_token, width) * scale) + self.ln_post = LayerNorm(width) + self.ln_pre = LayerNorm(width) + self.proj = nn.Parameter(scale * torch.randn(width, context_dim)) + + def forward(self, x): + # x shape [N, HW+1, C] + style_embedding = self.style_embedding + torch.zeros( + (x.shape[0], self.num_token, self.style_embedding.shape[-1]), device=x.device) + + x = torch.cat([x, style_embedding], dim=1) + x = self.ln_pre(x) + x = x.permute(1, 0, 2) # NLD -> LND + x = self.transformer_layes(x) + x = x.permute(1, 0, 2) # LND -> NLD + + x = self.ln_post(x[:, -self.num_token:, :]) + x = x @ self.proj + + return x + + +class ResnetBlock_light(nn.Module): + def __init__(self, in_c): + super().__init__() + self.block1 = nn.Conv2d(in_c, in_c, 3, 1, 1) + self.act = nn.ReLU() + self.block2 = nn.Conv2d(in_c, in_c, 3, 1, 1) + + def forward(self, x): + h = self.block1(x) + h = self.act(h) + h = self.block2(h) + + return h + x + + +class extractor(nn.Module): + def __init__(self, in_c, inter_c, out_c, nums_rb, down=False): + super().__init__() + self.in_conv = nn.Conv2d(in_c, inter_c, 1, 1, 0) + self.body = [] + for _ in range(nums_rb): + self.body.append(ResnetBlock_light(inter_c)) + self.body = nn.Sequential(*self.body) + self.out_conv = nn.Conv2d(inter_c, out_c, 1, 1, 0) + self.down = down + if self.down == True: + self.down_opt = Downsample(in_c, use_conv=False) + + def forward(self, x): + if self.down == True: + x = self.down_opt(x) + x = self.in_conv(x) + x = self.body(x) + x = self.out_conv(x) + + return x + + +class Adapter_light(nn.Module): + def __init__(self, channels=[320, 640, 1280, 1280], nums_rb=3, cin=64): + super(Adapter_light, self).__init__() + self.unshuffle = nn.PixelUnshuffle(8) + self.channels = channels + self.nums_rb = nums_rb + self.body = [] + for i in range(len(channels)): + if i == 0: + self.body.append(extractor(in_c=cin, inter_c=channels[i]//4, out_c=channels[i], nums_rb=nums_rb, down=False)) + else: + self.body.append(extractor(in_c=channels[i-1], inter_c=channels[i]//4, out_c=channels[i], nums_rb=nums_rb, down=True)) + self.body = nn.ModuleList(self.body) + + def forward(self, x): + # unshuffle + x = self.unshuffle(x) + # extract features + features = [] + for i in range(len(self.channels)): + x = self.body[i](x) + features.append(x) + + return features diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/api.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/api.py new file mode 100644 index 0000000000000000000000000000000000000000..752fd46331940920b0c03636ecca53ab7a06f339 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/api.py @@ -0,0 +1,330 @@ +from typing import Union + +import numpy as np +from fastapi import FastAPI, Body +from PIL import Image +import copy +import contextlib +import pydantic +import sys + +import gradio as gr + +from modules import ui +from modules.api.models import * +from modules.api import api +from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img + +import modules.scripts as scripts + +from scripts import external_code +from scripts.processor import * + +def to_base64_nparray(encoding: str): + return np.array(api.decode_base64_to_image(encoding)).astype('uint8') + +def encode_to_base64(image): + if type(image) is str: + return image + elif type(image) is Image.Image: + return api.encode_pil_to_base64(image) + elif type(image) is np.ndarray: + return encode_np_to_base64(image) + else: + return "" + +def encode_np_to_base64(image): + pil = Image.fromarray(image) + return api.encode_pil_to_base64(pil) + +cn_root_field_prefix = 'controlnet_' +cn_fields = { + "input_image": (str, Field(default="", title='ControlNet Input Image')), + "mask": (str, Field(default="", title='ControlNet Input Mask')), + "module": (str, Field(default="none", title='Controlnet Module')), + "model": (str, Field(default="None", title='Controlnet Model')), + "weight": (float, Field(default=1.0, title='Controlnet Weight')), + "resize_mode": (Union[int, str], Field(default="Scale to Fit (Inner Fit)", title='Controlnet Resize Mode')), + "lowvram": (bool, Field(default=False, title='Controlnet Low VRAM')), + "processor_res": (int, Field(default=64, title='Controlnet Processor Res')), + "threshold_a": (float, Field(default=64, title='Controlnet Threshold a')), + "threshold_b": (float, Field(default=64, title='Controlnet Threshold b')), + "guidance": (float, Field(default=1.0, title='ControlNet Guidance Strength')), + "guidance_start": (float, Field(0.0, title='ControlNet Guidance Start')), + "guidance_end": (float, Field(1.0, title='ControlNet Guidance End')), + "guessmode": (bool, Field(default=True, title="Guess Mode")), +} + +def get_deprecated_cn_field(field_name: str, field): + field_type, field = field + field = copy.copy(field) + field.default = None + field.extra['_deprecated'] = True + if field_name in ('input_image', 'mask'): + field_type = List[field_type] + return f'{cn_root_field_prefix}{field_name}', (field_type, field) + +def get_deprecated_field_default(field_name: str): + if field_name in ('input_image', 'mask'): + return [] + return cn_fields[field_name][-1].default + +ControlNetUnitRequest = pydantic.create_model('ControlNetUnitRequest', **cn_fields) + +def create_controlnet_request_model(p_api_class): + class RequestModel(p_api_class): + class Config(p_api_class.__config__): + @staticmethod + def schema_extra(schema: dict, _): + props = {} + for k, v in schema.get('properties', {}).items(): + if not v.get('_deprecated', False): + props[k] = v + if v.get('docs_default', None) is not None: + v['default'] = v['docs_default'] + if props: + schema['properties'] = props + + additional_fields = { + 'controlnet_units': (List[ControlNetUnitRequest], Field(default=[], docs_default=[ControlNetUnitRequest()], description="ControlNet Processing Units")), + **dict(get_deprecated_cn_field(k, v) for k, v in cn_fields.items()) + } + + return pydantic.create_model( + f'ControlNet{p_api_class.__name__}', + __base__=RequestModel, + **additional_fields) + +ControlNetTxt2ImgRequest = create_controlnet_request_model(StableDiffusionTxt2ImgProcessingAPI) +ControlNetImg2ImgRequest = create_controlnet_request_model(StableDiffusionImg2ImgProcessingAPI) + +class ApiHijack(api.Api): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.add_api_route("/controlnet/txt2img", self.controlnet_txt2img, methods=["POST"], response_model=TextToImageResponse) + self.add_api_route("/controlnet/img2img", self.controlnet_img2img, methods=["POST"], response_model=ImageToImageResponse) + + def controlnet_txt2img(self, txt2img_request: ControlNetTxt2ImgRequest): + return self.controlnet_any2img( + any2img_request=txt2img_request, + original_callback=ApiHijack.text2imgapi, + p_class=StableDiffusionProcessingTxt2Img, + script_runner=scripts.scripts_txt2img, + is_img2img=False, + ) + + def controlnet_img2img(self, img2img_request: ControlNetImg2ImgRequest): + return self.controlnet_any2img( + any2img_request=img2img_request, + original_callback=ApiHijack.img2imgapi, + p_class=StableDiffusionProcessingImg2Img, + script_runner=scripts.scripts_img2img, + is_img2img=True, + ) + + def controlnet_any2img(self, any2img_request, original_callback, p_class, script_runner, is_img2img): + any2img_request = nest_deprecated_cn_fields(any2img_request) + script_runner = create_cn_script_runner(script_runner, any2img_request.controlnet_units, is_img2img) + delattr(any2img_request, 'controlnet_units') + with self.queue_lock: + self_copy = copy.copy(self) + self_copy.queue_lock = contextlib.nullcontext() + with OverrideInit(p_class, scripts=script_runner): + return original_callback(self_copy, any2img_request) + +api.Api = ApiHijack + +class OverrideInit: + def __init__(self, cls, **kwargs): + self.cls = cls + self.kwargs = kwargs + self.original_init = None + + def __enter__(self): + def init_hijack(p, *args, **kwargs): + self.original_init(p, *args, **kwargs) + for k, v in self.kwargs.items(): + setattr(p, k, v) + + self.original_init = self.cls.__init__ + self.cls.__init__ = init_hijack + + def __exit__(self, exc_type, exc_val, exc_tb): + self.cls.__init__ = self.original_init + +def nest_deprecated_cn_fields(any2img_request): + deprecated_cn_fields = {k: v for k, v in vars(any2img_request).items() + if k.startswith(cn_root_field_prefix) and k != 'controlnet_units'} + + any2img_request = copy.copy(any2img_request) + for k in deprecated_cn_fields.keys(): + delattr(any2img_request, k) + + if all(v is None for v in deprecated_cn_fields.values()): + return any2img_request + + warn_deprecated_cn_params() + deprecated_cn_fields = {k[len(cn_root_field_prefix):]: v for k, v in deprecated_cn_fields.items()} + for k, v in deprecated_cn_fields.items(): + if v is None: + deprecated_cn_fields[k] = get_deprecated_field_default(k) + + for k in ('input_image', 'mask'): + deprecated_cn_fields[k] = deprecated_cn_fields[k][0] if deprecated_cn_fields[k] else "" + + any2img_request.controlnet_units.insert(0, ControlNetUnitRequest(**deprecated_cn_fields)) + return any2img_request + +def create_cn_script_runner(script_runner: scripts.ScriptRunner, control_unit_requests: List[ControlNetUnitRequest], is_img2img: bool): + if not script_runner.scripts: + script_runner.initialize_scripts(False) + ui.create_ui() + + cn_script = external_code.find_cn_script(script_runner) + cn_script_runner = copy.copy(script_runner) + cn_script_runner.alwayson_scripts = [cn_script] + cn_script_args = [None] * cn_script.args_from + cn_units = [to_api_cn_unit(control_unit_request) for control_unit_request in control_unit_requests] + external_code.update_cn_script_in_place( + script_runner=cn_script_runner, + script_args=cn_script_args, + cn_units=cn_units, + is_img2img=is_img2img, + ) + + def make_script_runner_f_hijack(fixed_original_f): + def script_runner_f_hijack(p, *args, **kwargs): + original_script_args = p.script_args + try: + p.script_args = cn_script_args + fixed_original_f(p, *args, **kwargs) + finally: + p.script_args = original_script_args + + return script_runner_f_hijack + + for k in ('process', 'process_batch', 'postprocess', 'postprocess_batch', 'postprocess_image'): + original_f = getattr(cn_script_runner, k, None) + if original_f is None: + continue + + setattr(cn_script_runner, k, make_script_runner_f_hijack(original_f)) + + return cn_script_runner + +def to_api_cn_unit(unit_request: ControlNetUnitRequest) -> external_code.ControlNetUnit: + input_image = to_base64_nparray(unit_request.input_image) if unit_request.input_image else None + mask = to_base64_nparray(unit_request.mask) if unit_request.mask else None + if input_image is not None and mask is not None: + input_image = (input_image, mask) + + if unit_request.guidance < 1.0: + unit_request.guidance_end = unit_request.guidance + + return external_code.ControlNetUnit( + module=unit_request.module, + model=unit_request.model, + weight=unit_request.weight, + image=input_image, + resize_mode=unit_request.resize_mode, + low_vram=unit_request.lowvram, + processor_res=unit_request.processor_res, + threshold_a=unit_request.threshold_a, + threshold_b=unit_request.threshold_b, + guidance_start=unit_request.guidance_start, + guidance_end=unit_request.guidance_end, + guess_mode=unit_request.guessmode, + ) + +def warn_deprecated_cn_params(): + warning_prefix = '[ControlNet] warning: ' + print(f"{warning_prefix}using deprecated '{cn_root_field_prefix}*' request params", file=sys.stderr) + print(f"{warning_prefix}consider using the 'control_units' request param instead", file=sys.stderr) + +def controlnet_api(_: gr.Blocks, app: FastAPI): + @app.get("/controlnet/model_list") + async def model_list(): + up_to_date_model_list = external_code.get_models(update=True) + print(up_to_date_model_list) + return {"model_list": up_to_date_model_list} + + @app.post("/controlnet/detect") + async def detect( + controlnet_module: str = Body("None", title='Controlnet Module'), + controlnet_input_images: List[str] = Body([], title='Controlnet Input Images'), + controlnet_processor_res: int = Body(512, title='Controlnet Processor Resolution'), + controlnet_threshold_a: float = Body(64, title='Controlnet Threshold a'), + controlnet_threshold_b: float = Body(64, title='Controlnet Threshold b') + ): + + available_modules = [ + "canny", + "depth", + "depth_leres", + "fake_scribble", + "hed", + "mlsd", + "normal_map", + "openpose", + "segmentation", + "binary", + "color" + ] + + if controlnet_module not in available_modules: + return {"images": [], "info": "Module not available"} + if len(controlnet_input_images) == 0: + return {"images": [], "info": "No image selected"} + + print(f"Detecting {str(len(controlnet_input_images))} images with the {controlnet_module} module.") + + results = [] + + for input_image in controlnet_input_images: + img = to_base64_nparray(input_image) + + if controlnet_module == "canny": + results.append(canny(img, controlnet_processor_res, controlnet_threshold_a, controlnet_threshold_b)[0]) + elif controlnet_module == "hed": + results.append(hed(img, controlnet_processor_res)[0]) + elif controlnet_module == "mlsd": + results.append(mlsd(img, controlnet_processor_res, controlnet_threshold_a, controlnet_threshold_b)[0]) + elif controlnet_module == "depth": + results.append(midas(img, controlnet_processor_res, np.pi * 2.0)[0]) + elif controlnet_module == "normal_map": + results.append(midas_normal(img, controlnet_processor_res, np.pi * 2.0, controlnet_threshold_a)[0]) + elif controlnet_module == "depth_leres": + results.append(leres(img, controlnet_processor_res, np.pi * 2.0, controlnet_threshold_a, controlnet_threshold_b)[0]) + elif controlnet_module == "openpose": + results.append(openpose(img, controlnet_processor_res, False)[0]) + elif controlnet_module == "fake_scribble": + results.append(fake_scribble(img, controlnet_processor_res)[0]) + elif controlnet_module == "segmentation": + results.append(uniformer(img, controlnet_processor_res)[0]) + elif controlnet_module == "binary": + results.append(binary(img, controlnet_processor_res, controlnet_threshold_a)[0]) + elif controlnet_module == "color": + results.append(color(img, controlnet_processor_res)[0]) + + if controlnet_module == "hed": + unload_hed() + elif controlnet_module == "mlsd": + unload_mlsd() + elif controlnet_module == "depth" or controlnet_module == "normal_map": + unload_midas() + elif controlnet_module == "depth_leres": + unload_leres() + elif controlnet_module == "openpose": + unload_openpose() + elif controlnet_module == "segmentation": + unload_uniformer() + + results64 = list(map(encode_to_base64, results)) + return {"images": results64, "info": "Success"} + +try: + import modules.script_callbacks as script_callbacks + + script_callbacks.on_app_started(controlnet_api) +except: + pass diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/cldm.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/cldm.py new file mode 100644 index 0000000000000000000000000000000000000000..8619f41d01142f16d93cb222d0df781144e87896 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/cldm.py @@ -0,0 +1,389 @@ +import torch +import torch.nn as nn +from omegaconf import OmegaConf +from modules import devices, lowvram, shared, scripts + +cond_cast_unet = getattr(devices, 'cond_cast_unet', lambda x: x) + +from ldm.util import exists +from ldm.modules.attention import SpatialTransformer +from ldm.modules.diffusionmodules.util import conv_nd, linear, zero_module, timestep_embedding +from ldm.modules.diffusionmodules.openaimodel import UNetModel, TimestepEmbedSequential, ResBlock, Downsample, AttentionBlock + + +class TorchHijackForUnet: + """ + This is torch, but with cat that resizes tensors to appropriate dimensions if they do not match; + this makes it possible to create pictures with dimensions that are multiples of 8 rather than 64 + """ + + def __getattr__(self, item): + if item == 'cat': + return self.cat + + if hasattr(torch, item): + return getattr(torch, item) + + raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, item)) + + def cat(self, tensors, *args, **kwargs): + if len(tensors) == 2: + a, b = tensors + if a.shape[-2:] != b.shape[-2:]: + a = torch.nn.functional.interpolate(a, b.shape[-2:], mode="nearest") + + tensors = (a, b) + + return torch.cat(tensors, *args, **kwargs) + + +th = TorchHijackForUnet() + + +def align(hint, size): + b, c, h1, w1 = hint.shape + h, w = size + if h != h1 or w != w1: + hint = th.nn.functional.interpolate(hint, size=size, mode="nearest") + return hint + + +def get_node_name(name, parent_name): + if len(name) <= len(parent_name): + return False, '' + p = name[:len(parent_name)] + if p != parent_name: + return False, '' + return True, name[len(parent_name):] + + +class PlugableControlModel(nn.Module): + def __init__(self, state_dict, config_path, lowvram=False, base_model=None) -> None: + super().__init__() + config = OmegaConf.load(config_path) + self.control_model = ControlNet(**config.model.params.control_stage_config.params) + + if any([k.startswith("control_model.") for k, v in state_dict.items()]): + + is_diff_model = 'difference' in state_dict + transfer_ctrl_opt = shared.opts.data.get("control_net_control_transfer", False) and \ + any([k.startswith("model.diffusion_model.") for k, v in state_dict.items()]) + + if (is_diff_model or transfer_ctrl_opt) and base_model is not None: + # apply transfer control - https://github.com/lllyasviel/ControlNet/blob/main/tool_transfer_control.py + + unet_state_dict = base_model.state_dict() + unet_state_dict_keys = unet_state_dict.keys() + final_state_dict = {} + counter = 0 + for key in state_dict.keys(): + if not key.startswith("control_model."): + continue + + p = state_dict[key] + is_control, node_name = get_node_name(key, 'control_') + key_name = node_name.replace("model.", "") if is_control else key + + if key_name in unet_state_dict_keys: + if is_diff_model: + # transfer control by make difference in advance + p_new = p + unet_state_dict[key_name].clone().cpu() + else: + # transfer control by calculate offsets from (delta = p + current_unet_encoder - frozen_unet_encoder) + p_new = p + unet_state_dict[key_name].clone().cpu() - state_dict["model.diffusion_model."+key_name] + counter += 1 + else: + p_new = p + final_state_dict[key] = p_new + + print(f'Offset cloned: {counter} values') + state_dict = final_state_dict + + state_dict = {k.replace("control_model.", ""): v for k, v in state_dict.items() if k.startswith("control_model.")} + else: + # assume that model is done by user + pass + + self.control_model.load_state_dict(state_dict) + if not lowvram: + self.control_model.to(devices.get_device_for("controlnet")) + + def reset(self): + pass + + def forward(self, *args, **kwargs): + return self.control_model(*args, **kwargs) + + +class ControlNet(nn.Module): + def __init__( + self, + image_size, + in_channels, + model_channels, + hint_channels, + num_res_blocks, + attention_resolutions, + dropout=0, + channel_mult=(1, 2, 4, 8), + conv_resample=True, + dims=2, + use_checkpoint=False, + use_fp16=False, + num_heads=-1, + num_head_channels=-1, + num_heads_upsample=-1, + use_scale_shift_norm=False, + resblock_updown=False, + use_new_attention_order=False, + use_spatial_transformer=False, # custom transformer support + transformer_depth=1, # custom transformer support + context_dim=None, # custom transformer support + # custom support for prediction of discrete ids into codebook of first stage vq model + n_embed=None, + legacy=True, + disable_self_attentions=None, + num_attention_blocks=None, + disable_middle_self_attn=False, + use_linear_in_transformer=False, + ): + use_fp16 = getattr(devices, 'dtype_unet', devices.dtype) == th.float16 and not getattr(shared.cmd_opts, "no_half_controlnet", False) + + super().__init__() + if use_spatial_transformer: + assert context_dim is not None, 'Fool!! You forgot to include the dimension of your cross-attention conditioning...' + + if context_dim is not None: + assert use_spatial_transformer, 'Fool!! You forgot to use the spatial transformer for your cross-attention conditioning...' + from omegaconf.listconfig import ListConfig + if type(context_dim) == ListConfig: + context_dim = list(context_dim) + + if num_heads_upsample == -1: + num_heads_upsample = num_heads + + if num_heads == -1: + assert num_head_channels != -1, 'Either num_heads or num_head_channels has to be set' + + if num_head_channels == -1: + assert num_heads != -1, 'Either num_heads or num_head_channels has to be set' + + self.dims = dims + self.image_size = image_size + self.in_channels = in_channels + self.model_channels = model_channels + if isinstance(num_res_blocks, int): + self.num_res_blocks = len(channel_mult) * [num_res_blocks] + else: + if len(num_res_blocks) != len(channel_mult): + raise ValueError("provide num_res_blocks either as an int (globally constant) or " + "as a list/tuple (per-level) with the same length as channel_mult") + self.num_res_blocks = num_res_blocks + if disable_self_attentions is not None: + # should be a list of booleans, indicating whether to disable self-attention in TransformerBlocks or not + assert len(disable_self_attentions) == len(channel_mult) + if num_attention_blocks is not None: + assert len(num_attention_blocks) == len(self.num_res_blocks) + assert all(map(lambda i: self.num_res_blocks[i] >= num_attention_blocks[i], range( + len(num_attention_blocks)))) + print(f"Constructor of UNetModel received num_attention_blocks={num_attention_blocks}. " + f"This option has LESS priority than attention_resolutions {attention_resolutions}, " + f"i.e., in cases where num_attention_blocks[i] > 0 but 2**i not in attention_resolutions, " + f"attention will still not be set.") + + self.attention_resolutions = attention_resolutions + self.dropout = dropout + self.channel_mult = channel_mult + self.conv_resample = conv_resample + self.use_checkpoint = use_checkpoint + self.dtype = th.float16 if use_fp16 else th.float32 + self.num_heads = num_heads + self.num_head_channels = num_head_channels + self.num_heads_upsample = num_heads_upsample + self.predict_codebook_ids = n_embed is not None + + time_embed_dim = model_channels * 4 + self.time_embed = nn.Sequential( + linear(model_channels, time_embed_dim), + nn.SiLU(), + linear(time_embed_dim, time_embed_dim), + ) + + self.input_blocks = nn.ModuleList( + [ + TimestepEmbedSequential( + conv_nd(dims, in_channels, model_channels, 3, padding=1) + ) + ] + ) + self.zero_convs = nn.ModuleList([self.make_zero_conv(model_channels)]) + + self.input_hint_block = TimestepEmbedSequential( + conv_nd(dims, hint_channels, 16, 3, padding=1), + nn.SiLU(), + conv_nd(dims, 16, 16, 3, padding=1), + nn.SiLU(), + conv_nd(dims, 16, 32, 3, padding=1, stride=2), + nn.SiLU(), + conv_nd(dims, 32, 32, 3, padding=1), + nn.SiLU(), + conv_nd(dims, 32, 96, 3, padding=1, stride=2), + nn.SiLU(), + conv_nd(dims, 96, 96, 3, padding=1), + nn.SiLU(), + conv_nd(dims, 96, 256, 3, padding=1, stride=2), + nn.SiLU(), + zero_module(conv_nd(dims, 256, model_channels, 3, padding=1)) + ) + + self._feature_size = model_channels + input_block_chans = [model_channels] + ch = model_channels + ds = 1 + for level, mult in enumerate(channel_mult): + for nr in range(self.num_res_blocks[level]): + layers = [ + ResBlock( + ch, + time_embed_dim, + dropout, + out_channels=mult * model_channels, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ) + ] + ch = mult * model_channels + if ds in attention_resolutions: + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + if exists(disable_self_attentions): + disabled_sa = disable_self_attentions[level] + else: + disabled_sa = False + + if not exists(num_attention_blocks) or nr < num_attention_blocks[level]: + layers.append( + AttentionBlock( + ch, + use_checkpoint=use_checkpoint, + num_heads=num_heads, + num_head_channels=dim_head, + use_new_attention_order=use_new_attention_order, + ) if not use_spatial_transformer else SpatialTransformer( + ch, num_heads, dim_head, depth=transformer_depth, context_dim=context_dim, + disable_self_attn=disabled_sa, use_linear=use_linear_in_transformer, + use_checkpoint=use_checkpoint + ) + ) + self.input_blocks.append(TimestepEmbedSequential(*layers)) + self.zero_convs.append(self.make_zero_conv(ch)) + self._feature_size += ch + input_block_chans.append(ch) + if level != len(channel_mult) - 1: + out_ch = ch + self.input_blocks.append( + TimestepEmbedSequential( + ResBlock( + ch, + time_embed_dim, + dropout, + out_channels=out_ch, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + down=True, + ) + if resblock_updown + else Downsample( + ch, conv_resample, dims=dims, out_channels=out_ch + ) + ) + ) + ch = out_ch + input_block_chans.append(ch) + self.zero_convs.append(self.make_zero_conv(ch)) + ds *= 2 + self._feature_size += ch + + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + self.middle_block = TimestepEmbedSequential( + ResBlock( + ch, + time_embed_dim, + dropout, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ), + AttentionBlock( + ch, + use_checkpoint=use_checkpoint, + num_heads=num_heads, + num_head_channels=dim_head, + use_new_attention_order=use_new_attention_order, + # always uses a self-attn + ) if not use_spatial_transformer else SpatialTransformer( + ch, num_heads, dim_head, depth=transformer_depth, context_dim=context_dim, + disable_self_attn=disable_middle_self_attn, use_linear=use_linear_in_transformer, + use_checkpoint=use_checkpoint + ), + ResBlock( + ch, + time_embed_dim, + dropout, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ), + ) + self.middle_block_out = self.make_zero_conv(ch) + self._feature_size += ch + + def make_zero_conv(self, channels): + return TimestepEmbedSequential(zero_module(conv_nd(self.dims, channels, channels, 1, padding=0))) + + def align(self, hint, h, w): + c, h1, w1 = hint.shape + if h != h1 or w != w1: + hint = align(hint.unsqueeze(0), (h, w)) + return hint.squeeze(0) + return hint + + def forward(self, x, hint, timesteps, context, **kwargs): + t_emb = cond_cast_unet(timestep_embedding(timesteps, self.model_channels, repeat_only=False)) + emb = self.time_embed(t_emb) + + guided_hint = self.input_hint_block(cond_cast_unet(hint), emb, context) + outs = [] + + h1, w1 = x.shape[-2:] + guided_hint = self.align(guided_hint, h1, w1) + + h = x.type(self.dtype) + for module, zero_conv in zip(self.input_blocks, self.zero_convs): + if guided_hint is not None: + h = module(h, emb, context) + h += guided_hint + guided_hint = None + else: + h = module(h, emb, context) + outs.append(zero_conv(h, emb, context)) + + h = self.middle_block(h, emb, context) + outs.append(self.middle_block_out(h, emb, context)) + + return outs diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/controlnet.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/controlnet.py new file mode 100644 index 0000000000000000000000000000000000000000..6eb54a7355b4c2c9d4e0bbb70d59afcd40589787 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/controlnet.py @@ -0,0 +1,915 @@ +import gc +import os +import stat +from collections import OrderedDict +from enum import Enum +from typing import Union + +import torch + +import modules.scripts as scripts +from modules import shared, devices, script_callbacks, processing, masking, images +import gradio as gr +import numpy as np + +from einops import rearrange +from scripts.cldm import PlugableControlModel +from scripts.processor import * +from scripts.adapter import PlugableAdapter +from scripts.utils import load_state_dict +from scripts.hook import ControlParams, UnetHook +from modules import sd_models +from modules.paths import models_path +from modules.processing import StableDiffusionProcessingImg2Img +from modules.images import save_image +from PIL import Image +from torchvision.transforms import Resize, InterpolationMode, CenterCrop, Compose + +gradio_compat = True +try: + from distutils.version import LooseVersion + from importlib_metadata import version + if LooseVersion(version("gradio")) < LooseVersion("3.10"): + gradio_compat = False +except ImportError: + pass + +# svgsupports +svgsupport = False +try: + import io + import base64 + from svglib.svglib import svg2rlg + from reportlab.graphics import renderPM + svgsupport = True +except ImportError: + pass + +CN_MODEL_EXTS = [".pt", ".pth", ".ckpt", ".safetensors"] +cn_models = OrderedDict() # "My_Lora(abcd1234)" -> C:/path/to/model.safetensors +cn_models_names = {} # "my_lora" -> "My_Lora(abcd1234)" +cn_models_dir = os.path.join(models_path, "ControlNet") +cn_models_dir_old = os.path.join(scripts.basedir(), "models") + +default_conf = os.path.join("models", "cldm_v15.yaml") +default_conf_adapter = os.path.join("models", "sketch_adapter_v14.yaml") +cn_detectedmap_dir = os.path.join("detected_maps") +default_detectedmap_dir = cn_detectedmap_dir +script_dir = scripts.basedir() + +os.makedirs(cn_models_dir, exist_ok=True) +os.makedirs(cn_detectedmap_dir, exist_ok=True) + +refresh_symbol = '\U0001f504' # 🔄 +switch_values_symbol = '\U000021C5' # ⇅ +camera_symbol = '\U0001F4F7' # 📷 +reverse_symbol = '\U000021C4' # ⇄ +tossup_symbol = '\u2934' + +webcam_enabled = False +webcam_mirrored = False + +PARAM_COUNT = 15 + + +class ToolButton(gr.Button, gr.components.FormComponent): + """Small button with single emoji as text, fits inside gradio forms""" + + def __init__(self, **kwargs): + super().__init__(variant="tool", **kwargs) + + def get_block_name(self): + return "button" + + +def traverse_all_files(curr_path, model_list): + f_list = [(os.path.join(curr_path, entry.name), entry.stat()) + for entry in os.scandir(curr_path)] + for f_info in f_list: + fname, fstat = f_info + if os.path.splitext(fname)[1] in CN_MODEL_EXTS: + model_list.append(f_info) + elif stat.S_ISDIR(fstat.st_mode): + model_list = traverse_all_files(fname, model_list) + return model_list + + +def get_all_models(sort_by, filter_by, path): + res = OrderedDict() + fileinfos = traverse_all_files(path, []) + filter_by = filter_by.strip(" ") + if len(filter_by) != 0: + fileinfos = [x for x in fileinfos if filter_by.lower() + in os.path.basename(x[0]).lower()] + if sort_by == "name": + fileinfos = sorted(fileinfos, key=lambda x: os.path.basename(x[0])) + elif sort_by == "date": + fileinfos = sorted(fileinfos, key=lambda x: -x[1].st_mtime) + elif sort_by == "path name": + fileinfos = sorted(fileinfos) + + for finfo in fileinfos: + filename = finfo[0] + name = os.path.splitext(os.path.basename(filename))[0] + # Prevent a hypothetical "None.pt" from being listed. + if name != "None": + res[name + f" [{sd_models.model_hash(filename)}]"] = filename + + return res + + +def find_closest_lora_model_name(search: str): + if not search: + return None + if search in cn_models: + return search + search = search.lower() + if search in cn_models_names: + return cn_models_names.get(search) + applicable = [name for name in cn_models_names.keys() + if search in name.lower()] + if not applicable: + return None + applicable = sorted(applicable, key=lambda name: len(name)) + return cn_models_names[applicable[0]] + + +def swap_img2img_pipeline(p: processing.StableDiffusionProcessingImg2Img): + p.__class__ = processing.StableDiffusionProcessingTxt2Img + dummy = processing.StableDiffusionProcessingTxt2Img() + for k,v in dummy.__dict__.items(): + if hasattr(p, k): + continue + setattr(p, k, v) + + +def update_cn_models(): + cn_models.clear() + ext_dirs = (shared.opts.data.get("control_net_models_path", None), getattr(shared.cmd_opts, 'controlnet_dir', None)) + extra_lora_paths = (extra_lora_path for extra_lora_path in ext_dirs + if extra_lora_path is not None and os.path.exists(extra_lora_path)) + paths = [cn_models_dir, cn_models_dir_old, *extra_lora_paths] + + for path in paths: + sort_by = shared.opts.data.get( + "control_net_models_sort_models_by", "name") + filter_by = shared.opts.data.get("control_net_models_name_filter", "") + found = get_all_models(sort_by, filter_by, path) + cn_models.update({**found, **cn_models}) + + # insert "None" at the beginning of `cn_models` in-place + cn_models_copy = OrderedDict(cn_models) + cn_models.clear() + cn_models.update({**{"None": None}, **cn_models_copy}) + + cn_models_names.clear() + for name_and_hash, filename in cn_models.items(): + if filename is None: + continue + name = os.path.splitext(os.path.basename(filename))[0].lower() + cn_models_names[name] = name_and_hash + + +update_cn_models() + + +class ResizeMode(Enum): + RESIZE = "Just Resize" + INNER_FIT = "Scale to Fit (Inner Fit)" + OUTER_FIT = "Envelope (Outer Fit)" + + +def resize_mode_from_value(value: Union[str, int, ResizeMode]) -> ResizeMode: + if isinstance(value, str): + return ResizeMode(value) + elif isinstance(value, int): + return [e for e in ResizeMode][value] + else: + return value + +class Script(scripts.Script): + model_cache = OrderedDict() + + def __init__(self) -> None: + super().__init__() + self.latest_network = None + self.preprocessor = { + "none": lambda x, *args, **kwargs: (x, True), + "canny": canny, + "depth": midas, + "depth_leres": leres, + "hed": hed, + "mlsd": mlsd, + "normal_map": midas_normal, + "openpose": openpose, + "openpose_hand": openpose_hand, + "clip_vision": clip, + "color": color, + "pidinet": pidinet, + "scribble": simple_scribble, + "fake_scribble": fake_scribble, + "segmentation": uniformer, + "binary": binary, + } + self.unloadable = { + "hed": unload_hed, + "fake_scribble": unload_hed, + "mlsd": unload_mlsd, + "clip": unload_clip, + "depth": unload_midas, + "depth_leres": unload_leres, + "normal_map": unload_midas, + "pidinet": unload_pidinet, + "openpose": unload_openpose, + "openpose_hand": unload_openpose, + "segmentation": unload_uniformer, + } + self.input_image = None + self.latest_model_hash = "" + self.txt2img_w_slider = gr.Slider() + self.txt2img_h_slider = gr.Slider() + self.img2img_w_slider = gr.Slider() + self.img2img_h_slider = gr.Slider() + + def title(self): + return "ControlNet" + + def show(self, is_img2img): + # if is_img2img: + # return False + return scripts.AlwaysVisible + + def after_component(self, component, **kwargs): + if component.elem_id == "txt2img_width": + self.txt2img_w_slider = component + return self.txt2img_w_slider + if component.elem_id == "txt2img_height": + self.txt2img_h_slider = component + return self.txt2img_h_slider + if component.elem_id == "img2img_width": + self.img2img_w_slider = component + return self.img2img_w_slider + if component.elem_id == "img2img_height": + self.img2img_h_slider = component + return self.img2img_h_slider + + def get_threshold_block(self, proc): + pass + + def uigroup(self, is_img2img): + ctrls = () + infotext_fields = [] + with gr.Row(): + input_image = gr.Image(source='upload', mirror_webcam=False, type='numpy', tool='sketch') + generated_image = gr.Image(label="Annotator result", visible=False) + + with gr.Row(): + gr.HTML(value='

Invert colors if your image has white background.
Change your brush width to make it thinner if you want to draw something.

') + webcam_enable = ToolButton(value=camera_symbol) + webcam_mirror = ToolButton(value=reverse_symbol) + send_dimen_button = ToolButton(value=tossup_symbol) + + with gr.Row(): + enabled = gr.Checkbox(label='Enable', value=False) + scribble_mode = gr.Checkbox(label='Invert Input Color', value=False) + rgbbgr_mode = gr.Checkbox(label='RGB to BGR', value=False) + lowvram = gr.Checkbox(label='Low VRAM', value=False) + guess_mode = gr.Checkbox(label='Guess Mode', value=False) + + ctrls += (enabled,) + # infotext_fields.append((enabled, "ControlNet Enabled")) + + def send_dimensions(image): + def closesteight(num): + rem = num % 8 + if rem <= 4: + return round(num - rem) + else: + return round(num + (8 - rem)) + if(image): + interm = np.asarray(image.get('image')) + return closesteight(interm.shape[1]), closesteight(interm.shape[0]) + else: + return gr.Slider.update(), gr.Slider.update() + + def webcam_toggle(): + global webcam_enabled + webcam_enabled = not webcam_enabled + return {"value": None, "source": "webcam" if webcam_enabled else "upload", "__type__": "update"} + + def webcam_mirror_toggle(): + global webcam_mirrored + webcam_mirrored = not webcam_mirrored + return {"mirror_webcam": webcam_mirrored, "__type__": "update"} + + webcam_enable.click(fn=webcam_toggle, inputs=None, outputs=input_image) + webcam_mirror.click(fn=webcam_mirror_toggle, inputs=None, outputs=input_image) + + def refresh_all_models(*inputs): + update_cn_models() + + dd = inputs[0] + selected = dd if dd in cn_models else "None" + return gr.Dropdown.update(value=selected, choices=list(cn_models.keys())) + + with gr.Row(): + module = gr.Dropdown(list(self.preprocessor.keys()), label=f"Preprocessor", value="none") + model = gr.Dropdown(list(cn_models.keys()), label=f"Model", value="None") + refresh_models = ToolButton(value=refresh_symbol) + refresh_models.click(refresh_all_models, model, model) + # ctrls += (refresh_models, ) + with gr.Row(): + weight = gr.Slider(label=f"Weight", value=1.0, minimum=0.0, maximum=2.0, step=.05) + guidance_start = gr.Slider(label="Guidance Start (T)", value=0.0, minimum=0.0, maximum=1.0, interactive=True) + guidance_end = gr.Slider(label="Guidance End (T)", value=1.0, minimum=0.0, maximum=1.0, interactive=True) + + ctrls += (module, model, weight,) + # model_dropdowns.append(model) + def build_sliders(module): + if module == "canny": + return [ + gr.update(label="Annotator resolution", value=512, minimum=64, maximum=2048, step=1, interactive=True), + gr.update(label="Canny low threshold", minimum=1, maximum=255, value=100, step=1, interactive=True), + gr.update(label="Canny high threshold", minimum=1, maximum=255, value=200, step=1, interactive=True), + gr.update(visible=True) + ] + elif module == "mlsd": #Hough + return [ + gr.update(label="Hough Resolution", minimum=64, maximum=2048, value=512, step=1, interactive=True), + gr.update(label="Hough value threshold (MLSD)", minimum=0.01, maximum=2.0, value=0.1, step=0.01, interactive=True), + gr.update(label="Hough distance threshold (MLSD)", minimum=0.01, maximum=20.0, value=0.1, step=0.01, interactive=True), + gr.update(visible=True) + ] + elif module in ["hed", "fake_scribble"]: + return [ + gr.update(label="HED Resolution", minimum=64, maximum=2048, value=512, step=1, interactive=True), + gr.update(label="Threshold A", value=64, minimum=64, maximum=1024, interactive=False), + gr.update(label="Threshold B", value=64, minimum=64, maximum=1024, interactive=False), + gr.update(visible=True) + ] + elif module in ["openpose", "openpose_hand", "segmentation"]: + return [ + gr.update(label="Annotator Resolution", minimum=64, maximum=2048, value=512, step=1, interactive=True), + gr.update(label="Threshold A", value=64, minimum=64, maximum=1024, interactive=False), + gr.update(label="Threshold B", value=64, minimum=64, maximum=1024, interactive=False), + gr.update(visible=True) + ] + elif module == "depth": + return [ + gr.update(label="Midas Resolution", minimum=64, maximum=2048, value=384, step=1, interactive=True), + gr.update(label="Threshold A", value=64, minimum=64, maximum=1024, interactive=False), + gr.update(label="Threshold B", value=64, minimum=64, maximum=1024, interactive=False), + gr.update(visible=True) + ] + elif module in ["depth_leres", "depth_leres_boost"]: + return [ + gr.update(label="LeReS Resolution", minimum=64, maximum=2048, value=512, step=1, interactive=True), + gr.update(label="Remove Near %", value=0, minimum=0, maximum=100, step=0.1, interactive=True), + gr.update(label="Remove Background %", value=0, minimum=0, maximum=100, step=0.1, interactive=True), + gr.update(visible=True) + ] + elif module == "normal_map": + return [ + gr.update(label="Normal Resolution", minimum=64, maximum=2048, value=512, step=1, interactive=True), + gr.update(label="Normal background threshold", minimum=0.0, maximum=1.0, value=0.4, step=0.01, interactive=True), + gr.update(label="Threshold B", value=64, minimum=64, maximum=1024, interactive=False), + gr.update(visible=True) + ] + elif module == "binary": + return [ + gr.update(label="Annotator resolution", value=512, minimum=64, maximum=2048, step=1, interactive=True), + gr.update(label="Binary threshold", minimum=0, maximum=255, value=0, step=1, interactive=True), + gr.update(label="Threshold B", value=64, minimum=64, maximum=1024, interactive=False), + gr.update(visible=True) + ] + elif module == "none": + return [ + gr.update(label="Normal Resolution", value=64, minimum=64, maximum=2048, interactive=False), + gr.update(label="Threshold A", value=64, minimum=64, maximum=1024, interactive=False), + gr.update(label="Threshold B", value=64, minimum=64, maximum=1024, interactive=False), + gr.update(visible=False) + ] + else: + return [ + gr.update(label="Annotator resolution", value=512, minimum=64, maximum=2048, step=1, interactive=True), + gr.update(label="Threshold A", value=64, minimum=64, maximum=1024, interactive=False), + gr.update(label="Threshold B", value=64, minimum=64, maximum=1024, interactive=False), + gr.update(visible=True) + ] + + # advanced options + advanced = gr.Column(visible=False) + with advanced: + processor_res = gr.Slider(label="Annotator resolution", value=64, minimum=64, maximum=2048, interactive=False) + threshold_a = gr.Slider(label="Threshold A", value=64, minimum=64, maximum=1024, interactive=False) + threshold_b = gr.Slider(label="Threshold B", value=64, minimum=64, maximum=1024, interactive=False) + + if gradio_compat: + module.change(build_sliders, inputs=[module], outputs=[processor_res, threshold_a, threshold_b, advanced]) + + # infotext_fields.extend((module, model, weight)) + + def create_canvas(h, w): + return np.zeros(shape=(h, w, 3), dtype=np.uint8) + 255 + + def svgPreprocess(inputs): + if (inputs): + if (inputs['image'].startswith("data:image/svg+xml;base64,") and svgsupport): + svg_data = base64.b64decode(inputs['image'].replace('data:image/svg+xml;base64,','')) + drawing = svg2rlg(io.BytesIO(svg_data)) + png_data = renderPM.drawToString(drawing, fmt='PNG') + encoded_string = base64.b64encode(png_data) + base64_str = str(encoded_string, "utf-8") + base64_str = "data:image/png;base64,"+ base64_str + inputs['image'] = base64_str + return input_image.orgpreprocess(inputs) + return None + + resize_mode = gr.Radio(choices=[e.value for e in ResizeMode], value=ResizeMode.INNER_FIT.value, label="Resize Mode") + with gr.Row(): + with gr.Column(): + canvas_width = gr.Slider(label="Canvas Width", minimum=256, maximum=1024, value=512, step=64) + canvas_height = gr.Slider(label="Canvas Height", minimum=256, maximum=1024, value=512, step=64) + + if gradio_compat: + canvas_swap_res = ToolButton(value=switch_values_symbol) + canvas_swap_res.click(lambda w, h: (h, w), inputs=[canvas_width, canvas_height], outputs=[canvas_width, canvas_height]) + + create_button = gr.Button(value="Create blank canvas") + create_button.click(fn=create_canvas, inputs=[canvas_height, canvas_width], outputs=[input_image]) + + def run_annotator(image, module, pres, pthr_a, pthr_b): + img = HWC3(image['image']) + if not ((image['mask'][:, :, 0]==0).all() or (image['mask'][:, :, 0]==255).all()): + img = HWC3(image['mask'][:, :, 0]) + preprocessor = self.preprocessor[module] + result = None + if pres > 64: + result, is_image = preprocessor(img, res=pres, thr_a=pthr_a, thr_b=pthr_b) + else: + result, is_image = preprocessor(img) + + if is_image: + return gr.update(value=result, visible=True, interactive=False) + + with gr.Row(): + annotator_button = gr.Button(value="Preview annotator result") + annotator_button_hide = gr.Button(value="Hide annotator result") + + annotator_button.click(fn=run_annotator, inputs=[input_image, module, processor_res, threshold_a, threshold_b], outputs=[generated_image]) + annotator_button_hide.click(fn=lambda: gr.update(visible=False), inputs=None, outputs=[generated_image]) + + if is_img2img: + send_dimen_button.click(fn=send_dimensions, inputs=[input_image], outputs=[self.img2img_w_slider, self.img2img_h_slider]) + else: + send_dimen_button.click(fn=send_dimensions, inputs=[input_image], outputs=[self.txt2img_w_slider, self.txt2img_h_slider]) + + ctrls += (input_image, scribble_mode, resize_mode, rgbbgr_mode) + ctrls += (lowvram,) + ctrls += (processor_res, threshold_a, threshold_b, guidance_start, guidance_end, guess_mode) + + input_image.orgpreprocess=input_image.preprocess + input_image.preprocess=svgPreprocess + + return ctrls + + + def ui(self, is_img2img): + """this function should create gradio UI elements. See https://gradio.app/docs/#components + The return value should be an array of all components that are used in processing. + Values of those returned components will be passed to run() and process() functions. + """ + self.infotext_fields = [] + ctrls_group = ( + gr.State(is_img2img), + gr.State(True), # is_ui + ) + max_models = shared.opts.data.get("control_net_max_models_num", 1) + with gr.Group(): + with gr.Accordion("ControlNet", open = False, elem_id="controlnet"): + if max_models > 1: + with gr.Tabs(): + for i in range(max_models): + with gr.Tab(f"Control Model - {i}"): + ctrls = self.uigroup(is_img2img) + self.register_modules(f"ControlNet-{i}", ctrls) + ctrls_group += ctrls + else: + with gr.Column(): + ctrls = self.uigroup(is_img2img) + self.register_modules(f"ControlNet", ctrls) + ctrls_group += ctrls + + return ctrls_group + + def register_modules(self, tabname, params): + enabled, module, model, weight = params[:4] + guidance_start, guidance_end, guess_mode = params[-3:] + + self.infotext_fields.extend([ + (enabled, f"{tabname} Enabled"), + (module, f"{tabname} Preprocessor"), + (model, f"{tabname} Model"), + (weight, f"{tabname} Weight"), + (guidance_start, f"{tabname} Guidance Start"), + (guidance_end, f"{tabname} Guidance End"), + ]) + + def clear_control_model_cache(self): + Script.model_cache.clear() + gc.collect() + devices.torch_gc() + + def load_control_model(self, p, unet, model, lowvram): + if model in Script.model_cache: + print(f"Loading model from cache: {model}") + return Script.model_cache[model] + + # Remove model from cache to clear space before building another model + if len(Script.model_cache) > 0 and len(Script.model_cache) >= shared.opts.data.get("control_net_model_cache_size", 2): + Script.model_cache.popitem(last=False) + gc.collect() + devices.torch_gc() + + model_net = self.build_control_model(p, unet, model, lowvram) + + if shared.opts.data.get("control_net_model_cache_size", 2) > 0: + Script.model_cache[model] = model_net + + return model_net + + def build_control_model(self, p, unet, model, lowvram): + + model_path = cn_models.get(model, None) + if model_path is None: + raise RuntimeError(f"model not found: {model}") + + # trim '"' at start/end + if model_path.startswith("\"") and model_path.endswith("\""): + model_path = model_path[1:-1] + + if not os.path.exists(model_path): + raise ValueError(f"file not found: {model_path}") + + print(f"Loading model: {model}") + state_dict = load_state_dict(model_path) + network_module = PlugableControlModel + network_config = shared.opts.data.get("control_net_model_config", default_conf) + if not os.path.isabs(network_config): + network_config = os.path.join(script_dir, network_config) + + if any([k.startswith("body.") or k == 'style_embedding' for k, v in state_dict.items()]): + # adapter model + network_module = PlugableAdapter + network_config = shared.opts.data.get("control_net_model_adapter_config", default_conf_adapter) + if not os.path.isabs(network_config): + network_config = os.path.join(script_dir, network_config) + + override_config = os.path.splitext(model_path)[0] + ".yaml" + if os.path.exists(override_config): + network_config = override_config + + network = network_module( + state_dict=state_dict, + config_path=network_config, + lowvram=lowvram, + base_model=unet, + ) + network.to(p.sd_model.device, dtype=p.sd_model.dtype) + print(f"ControlNet model {model} loaded.") + return network + + @staticmethod + def get_remote_call(p, attribute, default=None, idx=0, strict=False, force=False): + if not force and not shared.opts.data.get("control_net_allow_script_control", False): + return default + + def get_element(obj, idx, strict=False): + if not isinstance(obj, list): + return obj if not strict or idx == 0 else None + elif idx < len(obj): + return obj[idx] + else: + return None + + attribute_value = get_element(getattr(p, attribute, None), idx, strict) + default_value = get_element(default, idx) + return attribute_value if attribute_value is not None else default_value + + def parse_remote_call(self, p, params, idx): + if params is None: + params = [None] * PARAM_COUNT + + enabled, module, model, weight, image, scribble_mode, \ + resize_mode, rgbbgr_mode, lowvram, pres, pthr_a, pthr_b, guidance_start, guidance_end, guess_mode = params + + selector = self.get_remote_call + + enabled = selector(p, "control_net_enabled", enabled, idx, strict=True) + module = selector(p, "control_net_module", module, idx) + model = selector(p, "control_net_model", model, idx) + weight = selector(p, "control_net_weight", weight, idx) + image = selector(p, "control_net_image", image, idx) + scribble_mode = selector(p, "control_net_scribble_mode", scribble_mode, idx) + resize_mode = selector(p, "control_net_resize_mode", resize_mode, idx) + rgbbgr_mode = selector(p, "control_net_rgbbgr_mode", rgbbgr_mode, idx) + lowvram = selector(p, "control_net_lowvram", lowvram, idx) + pres = selector(p, "control_net_pres", pres, idx) + pthr_a = selector(p, "control_net_pthr_a", pthr_a, idx) + pthr_b = selector(p, "control_net_pthr_b", pthr_b, idx) + guidance_strength = selector(p, "control_net_guidance_strength", 1.0, idx) + guidance_start = selector(p, "control_net_guidance_start", guidance_start, idx) + guidance_end = selector(p, "control_net_guidance_end", guidance_end, idx) + guess_mode = selector(p, "control_net_guess_mode", guess_mode, idx) + if guidance_strength < 1.0: + # for backward compatible + guidance_end = guidance_strength + + input_image = selector(p, "control_net_input_image", None, idx) + + return (enabled, module, model, weight, image, scribble_mode, \ + resize_mode, rgbbgr_mode, lowvram, pres, pthr_a, pthr_b, guidance_start, guidance_end, guess_mode), input_image + + def detectmap_proc(self, detected_map, module, rgbbgr_mode, resize_mode, h, w): + detected_map = HWC3(detected_map) + if module == "normal_map" or rgbbgr_mode: + control = torch.from_numpy(detected_map[:, :, ::-1].copy()).float().to(devices.get_device_for("controlnet")) / 255.0 + else: + control = torch.from_numpy(detected_map.copy()).float().to(devices.get_device_for("controlnet")) / 255.0 + + control = rearrange(control, 'h w c -> c h w') + detected_map = rearrange(torch.from_numpy(detected_map), 'h w c -> c h w') + + if resize_mode == ResizeMode.INNER_FIT: + transform = Compose([ + Resize(h if hw else w, interpolation=InterpolationMode.BICUBIC), + CenterCrop(size=(h, w)) + ]) + control = transform(control) + detected_map = transform(detected_map) + else: + control = Resize((h,w), interpolation=InterpolationMode.BICUBIC)(control) + detected_map = Resize((h,w), interpolation=InterpolationMode.BICUBIC)(detected_map) + + # for log use + detected_map = rearrange(detected_map, 'c h w -> h w c').numpy().astype(np.uint8) + return control, detected_map + + def process(self, p, is_img2img=False, is_ui=False, *args): + """ + This function is called before processing begins for AlwaysVisible scripts. + You can modify the processing object (p) here, inject hooks, etc. + args contains all values returned by components from ui() + """ + unet = p.sd_model.model.diffusion_model + if self.latest_network is not None: + # always restore (~0.05s) + self.latest_network.restore(unet) + + control_groups = [] + params_group = [args[i:i + PARAM_COUNT] for i in range(0, len(args), PARAM_COUNT)] + if len(params_group) == 0: + # fill a null group + params, _ = self.parse_remote_call(p, None, 0) + if params[0]: # enabled + params_group.append(params) + + for idx, params in enumerate(params_group): + params, _ = self.parse_remote_call(p, params, idx) + enabled, module, model, weight, image, scribble_mode, \ + resize_mode, rgbbgr_mode, lowvram, pres, pthr_a, pthr_b, guidance_start, guidance_end, guess_mode = params + + if not enabled: + continue + control_groups.append((module, model, params)) + if len(params_group) != 1: + prefix = f"ControlNet-{idx}" + else: + prefix = "ControlNet" + p.extra_generation_params.update({ + f"{prefix} Enabled": True, + f"{prefix} Module": module, + f"{prefix} Model": model, + f"{prefix} Weight": weight, + f"{prefix} Guidance Start": guidance_start, + f"{prefix} Guidance End": guidance_end, + }) + + if len(params_group) == 0: + self.latest_network = None + return + + detected_maps = [] + forward_params = [] + hook_lowvram = False + + # cache stuff + if self.latest_model_hash != p.sd_model.sd_model_hash: + self.clear_control_model_cache() + + # unload unused preproc + module_list = [mod[0] for mod in control_groups] + for key in self.unloadable: + if key not in module_list: + self.unloadable.get(module, lambda:None)() + + self.latest_model_hash = p.sd_model.sd_model_hash + for idx, contents in enumerate(control_groups): + module, model, params = contents + _, input_image = self.parse_remote_call(p, params, idx) + enabled, module, model, weight, image, scribble_mode, \ + resize_mode, rgbbgr_mode, lowvram, pres, pthr_a, pthr_b, guidance_start, guidance_end, guess_mode = params + + resize_mode = resize_mode_from_value(resize_mode) + + if lowvram: + hook_lowvram = True + + model_net = self.load_control_model(p, unet, model, lowvram) + model_net.reset() + + is_img2img_batch_tab = is_img2img and img2img_tab_tracker.submit_img2img_tab == 'img2img_batch_tab' + if is_img2img_batch_tab and hasattr(p, "image_control") and p.image_control is not None: + input_image = HWC3(np.asarray(p.image_control)) + elif input_image is not None: + input_image = HWC3(np.asarray(input_image)) + elif image is not None: + input_image = HWC3(image['image']) + if not ((image['mask'][:, :, 0]==0).all() or (image['mask'][:, :, 0]==255).all()): + print("using mask as input") + input_image = HWC3(image['mask'][:, :, 0]) + scribble_mode = True + else: + # use img2img init_image as default + input_image = getattr(p, "init_images", [None])[0] + if input_image is None: + raise ValueError('controlnet is enabled but no input image is given') + input_image = HWC3(np.asarray(input_image)) + + if issubclass(type(p), StableDiffusionProcessingImg2Img) and p.inpaint_full_res == True and p.image_mask is not None: + input_image = Image.fromarray(input_image) + mask = p.image_mask.convert('L') + crop_region = masking.get_crop_region(np.array(mask), p.inpaint_full_res_padding) + crop_region = masking.expand_crop_region(crop_region, p.width, p.height, mask.width, mask.height) + + input_image = input_image.crop(crop_region) + input_image = images.resize_image(2, input_image, p.width, p.height) + input_image = HWC3(np.asarray(input_image)) + + if scribble_mode: + detected_map = np.zeros_like(input_image, dtype=np.uint8) + detected_map[np.min(input_image, axis=2) < 127] = 255 + input_image = detected_map + + print(f"Loading preprocessor: {module}") + preprocessor = self.preprocessor[module] + h, w, bsz = p.height, p.width, p.batch_size + if pres > 64: + detected_map, is_image = preprocessor(input_image, res=pres, thr_a=pthr_a, thr_b=pthr_b) + else: + detected_map, is_image = preprocessor(input_image) + + if is_image: + control, detected_map = self.detectmap_proc(detected_map, module, rgbbgr_mode, resize_mode, h, w) + detected_maps.append((detected_map, module)) + else: + control = detected_map + + forward_param = ControlParams( + control_model=model_net, + hint_cond=control, + guess_mode=guess_mode, + weight=weight, + guidance_stopped=False, + start_guidance_percent=guidance_start, + stop_guidance_percent=guidance_end, + advanced_weighting=None, + is_adapter=isinstance(model_net, PlugableAdapter), + is_extra_cond=getattr(model_net, "target", "") == "scripts.adapter.StyleAdapter" + ) + forward_params.append(forward_param) + + del model_net + + self.latest_network = UnetHook(lowvram=hook_lowvram) + self.latest_network.hook(unet) + self.latest_network.notify(forward_params, p.sampler_name in ["DDIM", "PLMS"]) + self.detected_map = detected_maps + + if len(control_groups) > 0 and shared.opts.data.get("control_net_skip_img2img_processing") and hasattr(p, "init_images"): + swap_img2img_pipeline(p) + + def postprocess(self, p, processed, is_img2img=False, is_ui=False, *args): + if shared.opts.data.get("control_net_detectmap_autosaving", False) and self.latest_network is not None: + for detect_map, module in self.detected_map: + detectmap_dir = os.path.join(shared.opts.data.get("control_net_detectedmap_dir", False), module) + if not os.path.isabs(detectmap_dir): + detectmap_dir = os.path.join(p.outpath_samples, detectmap_dir) + if module != "none": + os.makedirs(detectmap_dir, exist_ok=True) + img = Image.fromarray(detect_map) + save_image(img, detectmap_dir, module) + + is_img2img_batch_tab = is_ui and is_img2img and img2img_tab_tracker.submit_img2img_tab == 'img2img_batch_tab' + no_detectmap_opt = shared.opts.data.get("control_net_no_detectmap", False) + if self.latest_network is None or no_detectmap_opt or is_img2img_batch_tab: + return + + if hasattr(self, "detected_map") and self.detected_map is not None: + for detect_map, module in self.detected_map: + if module in ["canny", "mlsd", "scribble", "fake_scribble", "pidinet", "binary"]: + detect_map = 255-detect_map + processed.images.extend([Image.fromarray(detect_map)]) + + self.input_image = None + self.latest_network.restore(p.sd_model.model.diffusion_model) + self.latest_network = None + + gc.collect() + devices.torch_gc() + +def update_script_args(p, value, arg_idx): + for s in scripts.scripts_txt2img.alwayson_scripts: + if isinstance(s, Script): + args = list(p.script_args) + # print(f"Changed arg {arg_idx} from {args[s.args_from + arg_idx - 1]} to {value}") + args[s.args_from + arg_idx] = value + p.script_args = tuple(args) + break + + +def on_ui_settings(): + section = ('control_net', "ControlNet") + shared.opts.add_option("control_net_model_config", shared.OptionInfo( + default_conf, "Config file for Control Net models", section=section)) + shared.opts.add_option("control_net_model_adapter_config", shared.OptionInfo( + default_conf_adapter, "Config file for Adapter models", section=section)) + shared.opts.add_option("control_net_detectedmap_dir", shared.OptionInfo( + default_detectedmap_dir, "Directory for detected maps auto saving", section=section)) + shared.opts.add_option("control_net_models_path", shared.OptionInfo( + "", "Extra path to scan for ControlNet models (e.g. training output directory)", section=section)) + shared.opts.add_option("control_net_max_models_num", shared.OptionInfo( + 1, "Multi ControlNet: Max models amount (requires restart)", gr.Slider, {"minimum": 1, "maximum": 10, "step": 1}, section=section)) + shared.opts.add_option("control_net_model_cache_size", shared.OptionInfo( + 1, "Model cache size (requires restart)", gr.Slider, {"minimum": 1, "maximum": 5, "step": 1}, section=section)) + shared.opts.add_option("control_net_control_transfer", shared.OptionInfo( + False, "Apply transfer control when loading models", gr.Checkbox, {"interactive": True}, section=section)) + shared.opts.add_option("control_net_no_detectmap", shared.OptionInfo( + False, "Do not append detectmap to output", gr.Checkbox, {"interactive": True}, section=section)) + shared.opts.add_option("control_net_detectmap_autosaving", shared.OptionInfo( + False, "Allow detectmap auto saving", gr.Checkbox, {"interactive": True}, section=section)) + shared.opts.add_option("control_net_only_midctrl_hires", shared.OptionInfo( + True, "Use mid-control on highres pass (second pass)", gr.Checkbox, {"interactive": True}, section=section)) + shared.opts.add_option("control_net_allow_script_control", shared.OptionInfo( + False, "Allow other script to control this extension", gr.Checkbox, {"interactive": True}, section=section)) + shared.opts.add_option("control_net_skip_img2img_processing", shared.OptionInfo( + False, "Skip img2img processing when using img2img initial image", gr.Checkbox, {"interactive": True}, section=section)) + shared.opts.add_option("control_net_monocular_depth_optim", shared.OptionInfo( + False, "Enable optimized monocular depth estimation", gr.Checkbox, {"interactive": True}, section=section)) + shared.opts.add_option("control_net_only_mid_control", shared.OptionInfo( + False, "Only use mid-control when inference", gr.Checkbox, {"interactive": True}, section=section)) + shared.opts.add_option("control_net_cfg_based_guidance", shared.OptionInfo( + False, "Enable CFG-Based guidance", gr.Checkbox, {"interactive": True}, section=section)) + # shared.opts.add_option("control_net_advanced_weighting", shared.OptionInfo( + # False, "Enable advanced weight tuning", gr.Checkbox, {"interactive": False}, section=section)) + + +class Img2ImgTabTracker: + def __init__(self): + self.img2img_tabs = set() + self.active_img2img_tab = 'img2img_img2img_tab' + self.submit_img2img_tab = None + + def save_submit_img2img_tab(self): + self.submit_img2img_tab = self.active_img2img_tab + + def set_active_img2img_tab(self, tab): + self.active_img2img_tab = tab.elem_id + + def on_after_component_callback(self, component, **_kwargs): + if type(component) is gr.State: + return + + if type(component) is gr.Button and component.elem_id == 'img2img_generate': + component.click(fn=self.save_submit_img2img_tab, inputs=[], outputs=[]) + return + + tab = getattr(component, 'parent', None) + is_tab = type(tab) is gr.Tab and getattr(tab, 'elem_id', None) is not None + is_img2img_tab = is_tab and getattr(tab, 'parent', None) is not None and getattr(tab.parent, 'elem_id', None) == 'mode_img2img' + if is_img2img_tab and tab.elem_id not in self.img2img_tabs: + tab.select(fn=self.set_active_img2img_tab, inputs=gr.State(tab), outputs=[]) + self.img2img_tabs.add(tab.elem_id) + return + + +img2img_tab_tracker = Img2ImgTabTracker() +script_callbacks.on_ui_settings(on_ui_settings) +script_callbacks.on_after_component(img2img_tab_tracker.on_after_component_callback) diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/external_code.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/external_code.py new file mode 100644 index 0000000000000000000000000000000000000000..b433da50c46ecfe2001590aa8182e3cd47374ebc --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/external_code.py @@ -0,0 +1,232 @@ +from typing import List, Any, Optional, Union, Tuple, Dict +from modules import scripts, processing, shared +from scripts.controlnet import ResizeMode, update_cn_models, cn_models_names, PARAM_COUNT +import numpy as np + + +""" +Resize modes for ControlNet input images. +""" +ResizeMode = ResizeMode + + +class ControlNetUnit: + """ + Represents an entire ControlNet processing unit. + """ + + def __init__( + self, + enabled: bool=True, + module: Optional[str]=None, + model: Optional[str]=None, + weight: float=1.0, + image: Optional[Union[Dict[str, np.ndarray], Tuple[np.ndarray, np.ndarray], np.ndarray]]=None, + invert_image: bool=False, + resize_mode: Union[ResizeMode, int, str]=ResizeMode.INNER_FIT, + rgbbgr_mode: bool=False, + low_vram: bool=False, + processor_res: int=64, + threshold_a: float=64, + threshold_b: float=64, + guidance_start: float=0.0, + guidance_end: float=1.0, + guess_mode: bool=True, + ): + if image is not None: + if isinstance(image, tuple): + image = {'image': image[0], 'mask': image[1]} + elif isinstance(image, np.ndarray): + image = {'image': image, 'mask': np.zeros_like(image, dtype=np.uint8)} + + while len(image['mask'].shape) < 3: + image['mask'] = image['mask'][..., np.newaxis] + + self.enabled = enabled + self.module = module + self.model = model + self.weight = weight + self.image = image + self.invert_image = invert_image + self.resize_mode = resize_mode + self.rgbbgr_mode = rgbbgr_mode + self.low_vram = low_vram + self.processor_res = processor_res + self.threshold_a = threshold_a + self.threshold_b = threshold_b + self.guidance_start = guidance_start + self.guidance_end = guidance_end + self.guess_mode = guess_mode + + +def get_all_units_in_processing(p: processing.StableDiffusionProcessing) -> List[ControlNetUnit]: + """ + Fetch ControlNet processing units from a StableDiffusionProcessing. + """ + + return get_all_units(p.scripts, p.script_args) + + +def get_all_units(script_runner: scripts.ScriptRunner, script_args: List[Any]) -> List[ControlNetUnit]: + """ + Fetch ControlNet processing units from an existing script runner. + Use this function to fetch units from the list of all scripts arguments. + """ + + cn_script = find_cn_script(script_runner) + if cn_script: + return get_all_units_from(script_args[cn_script.args_from:cn_script.args_to]) + + return [] + + +def get_all_units_from(script_args: List[Any], strip_positional_args=True) -> List[ControlNetUnit]: + """ + Fetch ControlNet processing units from ControlNet script arguments. + Use `external_code.get_all_units` to fetch units from the list of all scripts arguments. + + Keyword arguments: + strip_positional_args -- Whether positional arguments are present in `script_args`. (default True) + """ + + if strip_positional_args: + script_args = script_args[2:] + + res = [] + for i in range(len(script_args) // PARAM_COUNT): + res.append(get_single_unit_from(script_args, i)) + + return res + + +def get_single_unit_from(script_args: List[Any], index: int=0) -> ControlNetUnit: + """ + Fetch a single ControlNet processing unit from ControlNet script arguments. + The list must not contain script positional arguments. It must only consist of flattened processing unit parameters. + """ + + index_from = index * PARAM_COUNT + index_to = index_from + PARAM_COUNT + return ControlNetUnit(*script_args[index_from:index_to]) + + +def update_cn_script_in_processing( + p: processing.StableDiffusionProcessing, + cn_units: List[ControlNetUnit], + is_img2img: Optional[bool] = None, + is_ui: Optional[bool] = None +): + """ + Update the arguments of the ControlNet script in `p.script_args` in place, reading from `cn_units`. + `cn_units` and its elements are not modified. You can call this function repeatedly, as many times as you want. + + Does not update `p.script_args` if any of the folling is true: + - ControlNet is not present in `p.scripts` + - `p.script_args` is not filled with script arguments for scripts that are processed before ControlNet + + Keyword arguments: + is_img2img -- whether to run the script as img2img. In general, this should be set to the appropriate value depending on the `StableDiffusionProcessing` subclass used for generating. If set to None, do not change existing value. (default None) + is_ui -- whether to run the script as if from the gradio interface. If set to None, do not change existing value. (default None) + """ + + cn_units_type = type(cn_units) if type(cn_units) in (list, tuple) else list + script_args = list(p.script_args) + update_cn_script_in_place(p.scripts, script_args, cn_units, is_img2img, is_ui) + p.script_args = cn_units_type(script_args) + + +def update_cn_script_in_place( + script_runner: scripts.ScriptRunner, + script_args: List[Any], + cn_units: List[ControlNetUnit], + is_img2img: Optional[bool] = None, + is_ui: Optional[bool] = None, +): + """ + Update the arguments of the ControlNet script in `script_args` in place, reading from `cn_units`. + `cn_units` and its elements are not modified. You can call this function repeatedly, as many times as you want. + + Does not update `script_args` if any of the folling is true: + - ControlNet is not present in `script_runner` + - `script_args` is not filled with script arguments for scripts that are processed before ControlNet + + Keyword arguments: + is_img2img -- whether to run the script as img2img. In general, this should be set to the appropriate value depending on the `StableDiffusionProcessing` subclass used for generating. If set to None, do not change existing value. (default None) + is_ui -- whether to run the script as if from the gradio interface. If set to None, do not change existing value. (default None) + """ + + cn_script = find_cn_script(script_runner) + if cn_script is None or len(script_args) < cn_script.args_from: + return + + cn_script_has_args = len(script_args[cn_script.args_from:cn_script.args_to]) > 0 + if is_img2img is None: + is_img2img = script_args[cn_script.args_from] if cn_script_has_args else False + if is_ui is None: + is_ui = script_args[cn_script.args_from + 1] if cn_script_has_args else False + + # fill in remaining parameters to satisfy max models, just in case script needs it. + max_models = shared.opts.data.get("control_net_max_models_num", 1) + cn_units = cn_units + [ControlNetUnit(enabled=False)] * max(max_models - len(cn_units), 0) + + flattened_cn_args: List[Any] = [is_img2img, is_ui] + for unit in cn_units: + flattened_cn_args.extend(( + unit.enabled, + unit.module if unit.module is not None else "none", + unit.model if unit.model is not None else "None", + unit.weight, + unit.image, + unit.invert_image, + unit.resize_mode, + unit.rgbbgr_mode, + unit.low_vram, + unit.processor_res, + unit.threshold_a, + unit.threshold_b, + unit.guidance_start, + unit.guidance_end, + unit.guess_mode)) + + cn_script_args_diff = 0 + for script in script_runner.alwayson_scripts: + if script is cn_script: + cn_script_args_diff = len(flattened_cn_args) - (cn_script.args_to - cn_script.args_from) + script_args[script.args_from:script.args_to] = flattened_cn_args + script.args_to = script.args_from + len(flattened_cn_args) + else: + script.args_from += cn_script_args_diff + script.args_to += cn_script_args_diff + + +def get_models(update: bool=False) -> List[str]: + """ + Fetch the list of available models. + Each value is a valid candidate of `ControlNetUnit.model`. + + Keyword arguments: + update -- Whether to refresh the list from disk. (default False) + """ + + if update: + update_cn_models() + + return list(cn_models_names.values()) + + +def find_cn_script(script_runner: scripts.ScriptRunner) -> Optional[scripts.Script]: + """ + Find the ControlNet script in `script_runner`. Returns `None` if `script_runner` does not contain a ControlNet script. + """ + + for script in script_runner.alwayson_scripts: + if is_cn_script(script): + return script + + +def is_cn_script(script: scripts.Script) -> bool: + """ + Determine whether `script` is a ControlNet script. + """ + + return script.title().lower() == 'controlnet' diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/hook.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/hook.py new file mode 100644 index 0000000000000000000000000000000000000000..725adb0f7fcbfd296897476ce8b5aa1c78523f27 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/hook.py @@ -0,0 +1,251 @@ + +import torch +import torch.nn as nn +from modules import devices, lowvram, shared, scripts + +cond_cast_unet = getattr(devices, 'cond_cast_unet', lambda x: x) + +from ldm.modules.diffusionmodules.util import timestep_embedding +from ldm.modules.diffusionmodules.openaimodel import UNetModel + + +class TorchHijackForUnet: + """ + This is torch, but with cat that resizes tensors to appropriate dimensions if they do not match; + this makes it possible to create pictures with dimensions that are multiples of 8 rather than 64 + """ + + def __getattr__(self, item): + if item == 'cat': + return self.cat + + if hasattr(torch, item): + return getattr(torch, item) + + raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, item)) + + def cat(self, tensors, *args, **kwargs): + if len(tensors) == 2: + a, b = tensors + if a.shape[-2:] != b.shape[-2:]: + a = torch.nn.functional.interpolate(a, b.shape[-2:], mode="nearest") + + tensors = (a, b) + + return torch.cat(tensors, *args, **kwargs) + + +th = TorchHijackForUnet() + + +class ControlParams: + def __init__( + self, + control_model, + hint_cond, + guess_mode, + weight, + guidance_stopped, + start_guidance_percent, + stop_guidance_percent, + advanced_weighting, + is_adapter, + is_extra_cond + ): + self.control_model = control_model + self.hint_cond = hint_cond + self.guess_mode = guess_mode + self.weight = weight + self.guidance_stopped = guidance_stopped + self.start_guidance_percent = start_guidance_percent + self.stop_guidance_percent = stop_guidance_percent + self.advanced_weighting = advanced_weighting + self.is_adapter = is_adapter + self.is_extra_cond = is_extra_cond + + +class UnetHook(nn.Module): + def __init__(self, lowvram=False) -> None: + super().__init__() + self.lowvram = lowvram + self.batch_cond_available = True + self.only_mid_control = shared.opts.data.get("control_net_only_mid_control", False) + + def hook(self, model): + outer = self + + def guidance_schedule_handler(x): + for param in self.control_params: + current_sampling_percent = (x.sampling_step / x.total_sampling_steps) + param.guidance_stopped = current_sampling_percent < param.start_guidance_percent or current_sampling_percent > param.stop_guidance_percent + + def cfg_based_adder(base, x, require_autocast, is_adapter=False): + if isinstance(x, float): + return base + x + + if require_autocast: + zeros = torch.zeros_like(base) + zeros[:, :x.shape[1], ...] = x + x = zeros + + # assume the input format is [cond, uncond] and they have same shape + # see https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/0cc0ee1bcb4c24a8c9715f66cede06601bfc00c8/modules/sd_samplers_kdiffusion.py#L114 + if base.shape[0] % 2 == 0 and (self.guess_mode or shared.opts.data.get("control_net_cfg_based_guidance", False)): + if self.is_vanilla_samplers: + uncond, cond = base.chunk(2) + if x.shape[0] % 2 == 0: + _, x_cond = x.chunk(2) + return torch.cat([uncond, cond + x_cond], dim=0) + if is_adapter: + return torch.cat([uncond, cond + x], dim=0) + else: + cond, uncond = base.chunk(2) + if x.shape[0] % 2 == 0: + x_cond, _ = x.chunk(2) + return torch.cat([cond + x_cond, uncond], dim=0) + if is_adapter: + return torch.cat([cond + x, uncond], dim=0) + + return base + x + + def forward(self, x, timesteps=None, context=None, **kwargs): + total_control = [0.0] * 13 + total_adapter = [0.0] * 4 + total_extra_cond = torch.zeros([0, context.shape[-1]]).to(devices.get_device_for("controlnet")) + only_mid_control = outer.only_mid_control + require_inpaint_hijack = False + + # handle external cond first + for param in outer.control_params: + if param.guidance_stopped or not param.is_extra_cond: + continue + if outer.lowvram: + param.control_model.to(devices.get_device_for("controlnet")) + control = param.control_model(x=x, hint=param.hint_cond, timesteps=timesteps, context=context) + total_extra_cond = torch.cat([total_extra_cond, control.clone().squeeze(0) * param.weight]) + + # check if it's non-batch-cond mode (lowvram, edit model etc) + if context.shape[0] % 2 != 0 and outer.batch_cond_available: + outer.batch_cond_available = False + if len(total_extra_cond) > 0 or outer.guess_mode or shared.opts.data.get("control_net_cfg_based_guidance", False): + print("Warning: StyleAdapter and cfg/guess mode may not works due to non-batch-cond inference") + + # concat styleadapter to cond, pad uncond to same length + if len(total_extra_cond) > 0 and outer.batch_cond_available: + total_extra_cond = torch.repeat_interleave(total_extra_cond.unsqueeze(0), context.shape[0] // 2, dim=0) + if outer.is_vanilla_samplers: + uncond, cond = context.chunk(2) + cond = torch.cat([cond, total_extra_cond], dim=1) + uncond = torch.cat([uncond, uncond[:, -total_extra_cond.shape[1]:, :]], dim=1) + context = torch.cat([uncond, cond], dim=0) + else: + cond, uncond = context.chunk(2) + cond = torch.cat([cond, total_extra_cond], dim=1) + uncond = torch.cat([uncond, uncond[:, -total_extra_cond.shape[1]:, :]], dim=1) + context = torch.cat([cond, uncond], dim=0) + + # handle unet injection stuff + for param in outer.control_params: + if param.guidance_stopped or param.is_extra_cond: + continue + if outer.lowvram: + param.control_model.to(devices.get_device_for("controlnet")) + + # hires stuffs + # note that this method may not works if hr_scale < 1.1 + if abs(x.shape[-1] - param.hint_cond.shape[-1] // 8) > 8: + only_mid_control = shared.opts.data.get("control_net_only_midctrl_hires", True) + # If you want to completely disable control net, uncomment this. + # return self._original_forward(x, timesteps=timesteps, context=context, **kwargs) + + # inpaint model workaround + x_in = x + control_model = param.control_model.control_model + if not param.is_adapter and x.shape[1] != control_model.input_blocks[0][0].in_channels and x.shape[1] == 9: + # inpaint_model: 4 data + 4 downscaled image + 1 mask + x_in = x[:, :4, ...] + require_inpaint_hijack = True + + assert param.hint_cond is not None, f"Controlnet is enabled but no input image is given" + control = param.control_model(x=x_in, hint=param.hint_cond, timesteps=timesteps, context=context) + control_scales = ([param.weight] * 13) + + if outer.lowvram: + param.control_model.to("cpu") + if param.guess_mode: + if param.is_adapter: + # see https://github.com/Mikubill/sd-webui-controlnet/issues/269 + control_scales = param.weight * [0.25, 0.62, 0.825, 1.0] + else: + control_scales = [param.weight * (0.825 ** float(12 - i)) for i in range(13)] + if param.advanced_weighting is not None: + control_scales = param.advanced_weighting + + control = [c * scale for c, scale in zip(control, control_scales)] + for idx, item in enumerate(control): + target = total_adapter if param.is_adapter else total_control + target[idx] += item + + control = total_control + assert timesteps is not None, ValueError(f"insufficient timestep: {timesteps}") + hs = [] + with th.no_grad(): + t_emb = cond_cast_unet(timestep_embedding(timesteps, self.model_channels, repeat_only=False)) + emb = self.time_embed(t_emb) + h = x.type(self.dtype) + for i, module in enumerate(self.input_blocks): + h = module(h, emb, context) + + # t2i-adatper, same as openaimodel.py:744 + if ((i+1)%3 == 0) and len(total_adapter): + h = cfg_based_adder(h, total_adapter.pop(0), require_inpaint_hijack, is_adapter=True) + + hs.append(h) + h = self.middle_block(h, emb, context) + + control_in = control.pop() + h = cfg_based_adder(h, control_in, require_inpaint_hijack) + + for i, module in enumerate(self.output_blocks): + if only_mid_control: + hs_input = hs.pop() + h = th.cat([h, hs_input], dim=1) + else: + hs_input, control_input = hs.pop(), control.pop() + h = th.cat([h, cfg_based_adder(hs_input, control_input, require_inpaint_hijack)], dim=1) + h = module(h, emb, context) + + h = h.type(x.dtype) + return self.out(h) + + def forward2(*args, **kwargs): + # webui will handle other compoments + try: + if shared.cmd_opts.lowvram: + lowvram.send_everything_to_cpu() + + return forward(*args, **kwargs) + finally: + if self.lowvram: + [param.control_model.to("cpu") for param in self.control_params] + + model._original_forward = model.forward + model.forward = forward2.__get__(model, UNetModel) + scripts.script_callbacks.on_cfg_denoiser(guidance_schedule_handler) + + def notify(self, params, is_vanilla_samplers): # lint: list[ControlParams] + self.is_vanilla_samplers = is_vanilla_samplers + self.control_params = params + self.guess_mode = any([param.guess_mode for param in params]) + + def restore(self, model): + scripts.script_callbacks.remove_current_script_callbacks() + if hasattr(self, "control_params"): + del self.control_params + + if not hasattr(model, "_original_forward"): + # no such handle, ignore + return + + model.forward = model._original_forward + del model._original_forward diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/movie2movie.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/movie2movie.py new file mode 100644 index 0000000000000000000000000000000000000000..9d132a1d4a3a2daf96684dd4d4c04474194d9505 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/movie2movie.py @@ -0,0 +1,117 @@ +import copy +import os +import shutil + +import cv2 +import gradio as gr +import modules.scripts as scripts + +from modules import images +from modules.processing import process_images +from modules.shared import opts +from PIL import Image + + +def get_all_frames(video_path): + if video_path is None: + return None + cap = cv2.VideoCapture(video_path) + frame_list = [] + if not cap.isOpened(): + return + while True: + ret, frame = cap.read() + if ret: + frame_list.append(frame) + else: + return frame_list + +def get_min_frame_num(video_list): + min_frame_num = -1 + for video in video_list: + if video is None: + continue + else: + frame_num = len(video) + print(frame_num) + if min_frame_num < 0: + min_frame_num = frame_num + elif frame_num < min_frame_num: + min_frame_num = frame_num + return min_frame_num + +def save_gif(path, image_list, name, duration): + tmp_dir = path + "/tmp/" + if os.path.isdir(tmp_dir): + shutil.rmtree(tmp_dir) + os.mkdir(tmp_dir) + for i, image in enumerate(image_list): + images.save_image(image, tmp_dir, f"output_{i}") + + os.makedirs(path + "/controlnet-m2m", exist_ok=True) + + image_list[0].save(path + f"/controlnet-m2m/{name}.gif", save_all=True, append_images=image_list[1:], optimize=False, duration=duration, loop=0) + + +class Script(scripts.Script): + + def title(self): + return "controlnet m2m" + + def show(self, is_img2img): + return True + + def ui(self, is_img2img): + # How the script's is displayed in the UI. See https://gradio.app/docs/#components + # for the different UI components you can use and how to create them. + # Most UI components can return a value, such as a boolean for a checkbox. + # The returned values are passed to the run method as parameters. + + ctrls_group = () + max_models = opts.data.get("control_net_max_models_num", 1) + + with gr.Group(): + with gr.Accordion("ControlNet-M2M", open = False): + with gr.Tabs(): + for i in range(max_models): + with gr.Tab(f"ControlNet-{i}", open=False): + ctrls_group += (gr.Video(format='mp4', source='upload', elem_id = f"video_{i}"), ) + + duration = gr.Slider(label=f"Duration", value=50.0, minimum=10.0, maximum=200.0, step=10, interactive=True) + ctrls_group += (duration,) + + return ctrls_group + + def run(self, p, *args): + # This is where the additional processing is implemented. The parameters include + # self, the model object "p" (a StableDiffusionProcessing class, see + # processing.py), and the parameters returned by the ui method. + # Custom functions can be defined here, and additional libraries can be imported + # to be used in processing. The return value should be a Processed object, which is + # what is returned by the process_images method. + video_num = opts.data.get("control_net_max_models_num", 1) + video_list = [get_all_frames(video) for video in args[:video_num]] + duration, = args[video_num:] + + frame_num = get_min_frame_num(video_list) + if frame_num > 0: + output_image_list = [] + for frame in range(frame_num): + copy_p = copy.copy(p) + copy_p.control_net_input_image = [] + for video in video_list: + if video is None: + continue + copy_p.control_net_input_image.append(video[frame]) + proc = process_images(copy_p) + img = proc.images[0] + output_image_list.append(img) + copy_p.close() + # TODO: Generate new name for each movie2movie output + save_gif(p.outpath_samples, output_image_list, "animation", duration) + proc.images = [p.outpath_samples + "/controlnet-m2m/animation.gif"] + + else: + proc = process_images(p) + + return proc \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/processor.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/processor.py new file mode 100644 index 0000000000000000000000000000000000000000..52d35da34caa9739714f07cd58a887f2623e7335 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/processor.py @@ -0,0 +1,228 @@ + +import numpy as np +from annotator.util import resize_image, HWC3 + + +model_canny = None + + +def canny(img, res=512, thr_a=100, thr_b=200, **kwargs): + l, h = thr_a, thr_b + img = resize_image(HWC3(img), res) + global model_canny + if model_canny is None: + from annotator.canny import apply_canny + model_canny = apply_canny + result = model_canny(img, l, h) + return result, True + +def simple_scribble(img, res=512, **kwargs): + img = resize_image(HWC3(img), res) + result = np.zeros_like(img, dtype=np.uint8) + result[np.min(img, axis=2) < 127] = 255 + return result, True + + +model_hed = None + + +def hed(img, res=512, **kwargs): + img = resize_image(HWC3(img), res) + global model_hed + if model_hed is None: + from annotator.hed import apply_hed + model_hed = apply_hed + result = model_hed(img) + return result, True + +def unload_hed(): + global model_hed + if model_hed is not None: + from annotator.hed import unload_hed_model + unload_hed_model() + +def fake_scribble(img, res=512, **kwargs): + result, _ = hed(img, res) + import cv2 + from annotator.hed import nms + result = nms(result, 127, 3.0) + result = cv2.GaussianBlur(result, (0, 0), 3.0) + result[result > 10] = 255 + result[result < 255] = 0 + return result, True + + +model_mlsd = None + + +def mlsd(img, res=512, thr_a=0.1, thr_b=0.1, **kwargs): + thr_v, thr_d = thr_a, thr_b + img = resize_image(HWC3(img), res) + global model_mlsd + if model_mlsd is None: + from annotator.mlsd import apply_mlsd + model_mlsd = apply_mlsd + result = model_mlsd(img, thr_v, thr_d) + return result, True + +def unload_mlsd(): + global model_mlsd + if model_mlsd is not None: + from annotator.mlsd import unload_mlsd_model + unload_mlsd_model() + + +model_midas = None + + +def midas(img, res=512, a=np.pi * 2.0, **kwargs): + img = resize_image(HWC3(img), res) + global model_midas + if model_midas is None: + from annotator.midas import apply_midas + model_midas = apply_midas + results, _ = model_midas(img, a) + return results, True + +def midas_normal(img, res=512, a=np.pi * 2.0, thr_a=0.4, **kwargs): # bg_th -> thr_a + bg_th = thr_a + img = resize_image(HWC3(img), res) + global model_midas + if model_midas is None: + from annotator.midas import apply_midas + model_midas = apply_midas + _, results = model_midas(img, a, bg_th) + return results, True + +def unload_midas(): + global model_midas + if model_midas is not None: + from annotator.midas import unload_midas_model + unload_midas_model() + +model_leres = None + +def leres(img, res=512, a=np.pi * 2.0, thr_a=0, thr_b=0, **kwargs): + img = resize_image(HWC3(img), res) + global model_leres + if model_leres is None: + from annotator.leres import apply_leres + model_leres = apply_leres + results = model_leres(img, thr_a, thr_b) + return results, True + +def unload_leres(): + global model_leres + if model_leres is not None: + from annotator.leres import unload_leres_model + unload_leres_model() + +model_openpose = None + + +def openpose(img, res=512, has_hand=False, **kwargs): + img = resize_image(HWC3(img), res) + global model_openpose + if model_openpose is None: + from annotator.openpose import apply_openpose + model_openpose = apply_openpose + result, _ = model_openpose(img, has_hand) + return result, True + +def openpose_hand(img, res=512, has_hand=True, **kwargs): + img = resize_image(HWC3(img), res) + global model_openpose + if model_openpose is None: + from annotator.openpose import apply_openpose + model_openpose = apply_openpose + result, _ = model_openpose(img, has_hand) + return result, True + +def unload_openpose(): + global model_openpose + if model_openpose is not None: + from annotator.openpose import unload_openpose_model + unload_openpose_model() + + +model_uniformer = None + + +def uniformer(img, res=512, **kwargs): + img = resize_image(HWC3(img), res) + global model_uniformer + if model_uniformer is None: + from annotator.uniformer import apply_uniformer + model_uniformer = apply_uniformer + result = model_uniformer(img) + return result, True + +def unload_uniformer(): + global model_uniformer + if model_uniformer is not None: + from annotator.uniformer import unload_uniformer_model + unload_uniformer_model() + + +model_pidinet = None + + +def pidinet(img, res=512, **kwargs): + img = resize_image(HWC3(img), res) + global model_pidinet + if model_pidinet is None: + from annotator.pidinet import apply_pidinet + model_pidinet = apply_pidinet + result = model_pidinet(img) + return result, True + +def unload_pidinet(): + global model_pidinet + if model_pidinet is not None: + from annotator.pidinet import unload_pid_model + unload_pid_model() + + +clip_encoder = None + + +def clip(img, res=512, **kwargs): + img = resize_image(HWC3(img), res) + global clip_encoder + if clip_encoder is None: + from annotator.clip import apply_clip + clip_encoder = apply_clip + result = clip_encoder(img).squeeze(0) + return result, False + + +def unload_clip(): + global clip_encoder + if clip_encoder is not None: + from annotator.clip import unload_clip_model + unload_clip_model() + + +model_color = None + + +def color(img, res=512, **kwargs): + global model_color + if model_color is None: + from annotator.color import apply_color + model_color = apply_color + result = model_color(img, res=res) + return result, True + + +model_binary = None + + +def binary(img, res=512, thr_a=0, **kwargs): + img = resize_image(HWC3(img), res) + global model_binary + if model_binary is None: + from annotator.binary import apply_binary + model_binary = apply_binary + result = model_binary(img, thr_a) + return result, True \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/utils.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..915e638caece30dece3428ddb2c871f8c7199563 --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/utils.py @@ -0,0 +1,19 @@ +import torch +import os + + +def load_state_dict(ckpt_path, location='cpu'): + _, extension = os.path.splitext(ckpt_path) + if extension.lower() == ".safetensors": + import safetensors.torch + state_dict = safetensors.torch.load_file(ckpt_path, device=location) + else: + state_dict = get_state_dict(torch.load( + ckpt_path, map_location=torch.device(location))) + state_dict = get_state_dict(state_dict) + print(f'Loaded state_dict from [{ckpt_path}]') + return state_dict + + +def get_state_dict(d): + return d.get('state_dict', d) \ No newline at end of file diff --git a/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/xyz_grid_support.py b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/xyz_grid_support.py new file mode 100644 index 0000000000000000000000000000000000000000..68693b6b4dc5c6417816e3345409420010a70c8a --- /dev/null +++ b/sd/stable-diffusion-webui/extensions/sd-webui-controlnet/scripts/xyz_grid_support.py @@ -0,0 +1,467 @@ +import re +import numpy as np + +from modules import scripts, shared + +try: + from scripts import controlnet +except ImportError: + import_error = True +else: + import_error = False + +DEBUG_MODE = False + + +def debug_info(func): + def debug_info_(*args, **kwargs): + if DEBUG_MODE: + print(f"Debug info: {func.__name__}, {args}") + return func(*args, **kwargs) + return debug_info_ + + +def find_dict(dict_list, keyword, search_key="name", stop=False): + result = next((d for d in dict_list if d[search_key] == keyword), None) + if result or not stop: + return result + else: + raise ValueError(f"Dictionary with value '{keyword}' in key '{search_key}' not found.") + + +def flatten_list(lst): + result = [] + for element in lst: + if isinstance(element, list): + result.extend(flatten_list(element)) + else: + result.append(element) + return result + + +def is_all_included(target_list, check_list, allow_blank=False, stop=False): + for element in flatten_list(target_list): + if allow_blank and str(element) in ["None", ""]: + continue + elif element not in check_list: + if not stop: + return False + else: + raise ValueError(f"'{element}' is not included in check list.") + return True + + +class ListParser(): + """This class restores a broken list caused by the following process + in the xyz_grid module. + -> valslist = [x.strip() for x in chain.from_iterable( + csv.reader(StringIO(vals)))] + It also performs type conversion, + adjusts the number of elements in the list, and other operations. + + This class directly modifies the received list. + """ + numeric_pattern = { + int: { + "range": r"\s*([+-]?\s*\d+)\s*-\s*([+-]?\s*\d+)(?:\s*\(([+-]\d+)\s*\))?\s*", + "count": r"\s*([+-]?\s*\d+)\s*-\s*([+-]?\s*\d+)(?:\s*\[(\d+)\s*\])?\s*" + }, + float: { + "range": r"\s*([+-]?\s*\d+(?:\.\d*)?)\s*-\s*([+-]?\s*\d+(?:\.\d*)?)(?:\s*\(([+-]\d+(?:\.\d*)?)\s*\))?\s*", + "count": r"\s*([+-]?\s*\d+(?:\.\d*)?)\s*-\s*([+-]?\s*\d+(?:\.\d*)?)(?:\s*\[(\d+(?:\.\d*)?)\s*\])?\s*" + } + } + + ################################################ + # + # Initialization method from here. + # + ################################################ + + def __init__(self, my_list, converter=None, allow_blank=True, exclude_list=None, run=True): + self.my_list = my_list + self.converter = converter + self.allow_blank = allow_blank + self.exclude_list = exclude_list + self.re_bracket_start = None + self.re_bracket_start_precheck = None + self.re_bracket_end = None + self.re_bracket_end_precheck = None + self.re_range = None + self.re_count = None + self.compile_regex() + if run: + self.auto_normalize() + + def compile_regex(self): + exclude_pattern = "|".join(self.exclude_list) if self.exclude_list else None + if exclude_pattern is None: + self.re_bracket_start = re.compile(r"^\[") + self.re_bracket_end = re.compile(r"\]$") + else: + self.re_bracket_start = re.compile(fr"^\[(?!(?:{exclude_pattern})\])") + self.re_bracket_end = re.compile(fr"(? valslist = [opt.type(x) for x in valslist] + # Perform type conversion using the function + # set to the confirm attribute instead. + # + def identity(x): + return x + + ################################################ + # The confirm function defined in this module + # enables list notation and performs type conversion. + # + # Example: + # any = [any, any, any, ...] + # [any] = [any, None, None, ...] + # [None, None, any] = [None, None, any] + # [,,any] = [None, None, any] + # any, [,any,] = [any, any, any, ...], [None, any, None] + # + # Enabled Only: + # any = [any] = [any, None, None, ...] + # (any and [any] are considered equivalent) + # + def confirm(func_or_str): + @debug_info + def confirm_(p, xs): + if callable(func_or_str): # func_or_str is converter + ListParser(xs, func_or_str, allow_blank=True) + return + + elif isinstance(func_or_str, str): # func_or_str is keyword + valid_data = find_dict(validation_data, func_or_str, stop=True) + converter = valid_data["type"] + exclude_list = valid_data["exclude"]() if valid_data["exclude"] else None + check_list = valid_data["check"]() + + ListParser(xs, converter, allow_blank=True, exclude_list=exclude_list) + is_all_included(xs, check_list, allow_blank=True, stop=True) + return + + else: + raise TypeError(f"Argument must be callable or str, not {type(func_or_str).__name__}.") + + return confirm_ + + def bool_(string): + string = str(string) + if string in ["None", ""]: + return None + elif string.lower() in ["true", "1"]: + return True + elif string.lower() in ["false", "0"]: + return False + else: + raise ValueError(f"Could not convert string to boolean: {string}") + + def choices_bool(): + return ["False", "True"] + + def choices_model(): + controlnet.update_cn_models() + return list(controlnet.cn_models_names.values()) + + def choices_resize_mode(): + return ["Envelope (Outer Fit)", "Scale to Fit (Inner Fit)", "Just Resize"] + + def choices_preprocessor(): + return list(controlnet.Script().preprocessor) + + def make_excluded_list(): + pattern = re.compile(r"\[(\w+)\]") + return [match.group(1) for s in choices_model() + for match in pattern.finditer(s)] + + validation_data = [ + {"name": "model", "type": str, "check": choices_model, "exclude": make_excluded_list}, + {"name": "resize_mode", "type": str, "check": choices_resize_mode, "exclude": None}, + {"name": "preprocessor", "type": str, "check": choices_preprocessor, "exclude": None}, + ] + + extra_axis_options = [ + AxisOption("[ControlNet] Enabled", identity, apply_field("control_net_enabled"), confirm=confirm(bool_), choices=choices_bool), + AxisOption("[ControlNet] Model", identity, apply_field("control_net_model"), confirm=confirm("model"), choices=choices_model, cost=0.9), + AxisOption("[ControlNet] Weight", identity, apply_field("control_net_weight"), confirm=confirm(float)), + AxisOption("[ControlNet] Guidance Start", identity, apply_field("control_net_guidance_start"), confirm=confirm(float)), + AxisOption("[ControlNet] Guidance End", identity, apply_field("control_net_guidance_end"), confirm=confirm(float)), + AxisOption("[ControlNet] Resize Mode", identity, apply_field("control_net_resize_mode"), confirm=confirm("resize_mode"), choices=choices_resize_mode), + AxisOption("[ControlNet] Preprocessor", identity, apply_field("control_net_module"), confirm=confirm("preprocessor"), choices=choices_preprocessor), + AxisOption("[ControlNet] Pre Resolution", identity, apply_field("control_net_pres"), confirm=confirm(int)), + AxisOption("[ControlNet] Pre Threshold A", identity, apply_field("control_net_pthr_a"), confirm=confirm(float)), + AxisOption("[ControlNet] Pre Threshold B", identity, apply_field("control_net_pthr_b"), confirm=confirm(float)), + ] + + xyz_grid.axis_options.extend(extra_axis_options) + + +def run(): + xyz_grid = find_module("xyz_grid.py, xy_grid.py") + if xyz_grid: + add_axis_options(xyz_grid) + + +if not import_error: + run() diff --git a/sd/stable-diffusion-webui/html/card-no-preview.png b/sd/stable-diffusion-webui/html/card-no-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..e2beb2692067db56ac5f7bd5bfc3d895d9063371 Binary files /dev/null and b/sd/stable-diffusion-webui/html/card-no-preview.png differ diff --git a/sd/stable-diffusion-webui/html/extra-networks-card.html b/sd/stable-diffusion-webui/html/extra-networks-card.html new file mode 100644 index 0000000000000000000000000000000000000000..8a5e2fbd223e71abacca9a602bd1be154f5fb520 --- /dev/null +++ b/sd/stable-diffusion-webui/html/extra-networks-card.html @@ -0,0 +1,12 @@ +
+
+
+ + +
+ {name} +
+
+ diff --git a/sd/stable-diffusion-webui/html/extra-networks-no-cards.html b/sd/stable-diffusion-webui/html/extra-networks-no-cards.html new file mode 100644 index 0000000000000000000000000000000000000000..389358d6c4b383fdc3c5686e029e7b3b1ae9a493 --- /dev/null +++ b/sd/stable-diffusion-webui/html/extra-networks-no-cards.html @@ -0,0 +1,8 @@ +
+

Nothing here. Add some content to the following directories:

+ +
    +{dirs} +
+
+ diff --git a/sd/stable-diffusion-webui/html/footer.html b/sd/stable-diffusion-webui/html/footer.html new file mode 100644 index 0000000000000000000000000000000000000000..f26e32e9304aedb5a55b0b46a913396f16375f7a --- /dev/null +++ b/sd/stable-diffusion-webui/html/footer.html @@ -0,0 +1,13 @@ +
+ API +  •  + Github +  •  + Gradio +  •  + Reload UI +
+
+
+{versions} +
diff --git a/sd/stable-diffusion-webui/html/image-update.svg b/sd/stable-diffusion-webui/html/image-update.svg new file mode 100644 index 0000000000000000000000000000000000000000..3abf12df0f7774c13203e3c49ec3544649df42f4 --- /dev/null +++ b/sd/stable-diffusion-webui/html/image-update.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/sd/stable-diffusion-webui/html/licenses.html b/sd/stable-diffusion-webui/html/licenses.html new file mode 100644 index 0000000000000000000000000000000000000000..f59c352510f95a5d57df7808459c5eb5b21367a9 --- /dev/null +++ b/sd/stable-diffusion-webui/html/licenses.html @@ -0,0 +1,419 @@ + + +

CodeFormer

+Parts of CodeFormer code had to be copied to be compatible with GFPGAN. +
+S-Lab License 1.0
+
+Copyright 2022 S-Lab
+
+Redistribution and use for non-commercial purpose in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived
+   from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+In the event that redistribution and/or use for commercial purpose in
+source or binary forms, with or without modification is required,
+please contact the contributor(s) of the work.
+
+ + +

ESRGAN

+Code for architecture and reading models copied. +
+MIT License
+
+Copyright (c) 2021 victorca25
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+ +

Real-ESRGAN

+Some code is copied to support ESRGAN models. +
+BSD 3-Clause License
+
+Copyright (c) 2021, Xintao Wang
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ +

InvokeAI

+Some code for compatibility with OSX is taken from lstein's repository. +
+MIT License
+
+Copyright (c) 2022 InvokeAI Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+ +

LDSR

+Code added by contirubtors, most likely copied from this repository. +
+MIT License
+
+Copyright (c) 2022 Machine Vision and Learning Group, LMU Munich
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+ +

CLIP Interrogator

+Some small amounts of code borrowed and reworked. +
+MIT License
+
+Copyright (c) 2022 pharmapsychotic
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+ +

SwinIR

+Code added by contributors, most likely copied from this repository. + +
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [2021] [SwinIR Authors]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ +

Memory Efficient Attention

+The sub-quadratic cross attention optimization uses modified code from the Memory Efficient Attention package that Alex Birch optimized for 3D tensors. This license is updated to reflect that. +
+MIT License
+
+Copyright (c) 2023 Alex Birch
+Copyright (c) 2023 Amin Rezaei
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+ diff --git a/sd/stable-diffusion-webui/javascript/aspectRatioOverlay.js b/sd/stable-diffusion-webui/javascript/aspectRatioOverlay.js new file mode 100644 index 0000000000000000000000000000000000000000..50c81054269c7d75c9e29aa0f18260d1df0939eb --- /dev/null +++ b/sd/stable-diffusion-webui/javascript/aspectRatioOverlay.js @@ -0,0 +1,113 @@ + +let currentWidth = null; +let currentHeight = null; +let arFrameTimeout = setTimeout(function(){},0); + +function dimensionChange(e, is_width, is_height){ + + if(is_width){ + currentWidth = e.target.value*1.0 + } + if(is_height){ + currentHeight = e.target.value*1.0 + } + + var inImg2img = Boolean(gradioApp().querySelector("button.rounded-t-lg.border-gray-200")) + + if(!inImg2img){ + return; + } + + var targetElement = null; + + var tabIndex = get_tab_index('mode_img2img') + if(tabIndex == 0){ // img2img + targetElement = gradioApp().querySelector('div[data-testid=image] img'); + } else if(tabIndex == 1){ //Sketch + targetElement = gradioApp().querySelector('#img2img_sketch div[data-testid=image] img'); + } else if(tabIndex == 2){ // Inpaint + targetElement = gradioApp().querySelector('#img2maskimg div[data-testid=image] img'); + } else if(tabIndex == 3){ // Inpaint sketch + targetElement = gradioApp().querySelector('#inpaint_sketch div[data-testid=image] img'); + } + + + if(targetElement){ + + var arPreviewRect = gradioApp().querySelector('#imageARPreview'); + if(!arPreviewRect){ + arPreviewRect = document.createElement('div') + arPreviewRect.id = "imageARPreview"; + gradioApp().getRootNode().appendChild(arPreviewRect) + } + + + + var viewportOffset = targetElement.getBoundingClientRect(); + + viewportscale = Math.min( targetElement.clientWidth/targetElement.naturalWidth, targetElement.clientHeight/targetElement.naturalHeight ) + + scaledx = targetElement.naturalWidth*viewportscale + scaledy = targetElement.naturalHeight*viewportscale + + cleintRectTop = (viewportOffset.top+window.scrollY) + cleintRectLeft = (viewportOffset.left+window.scrollX) + cleintRectCentreY = cleintRectTop + (targetElement.clientHeight/2) + cleintRectCentreX = cleintRectLeft + (targetElement.clientWidth/2) + + viewRectTop = cleintRectCentreY-(scaledy/2) + viewRectLeft = cleintRectCentreX-(scaledx/2) + arRectWidth = scaledx + arRectHeight = scaledy + + arscale = Math.min( arRectWidth/currentWidth, arRectHeight/currentHeight ) + arscaledx = currentWidth*arscale + arscaledy = currentHeight*arscale + + arRectTop = cleintRectCentreY-(arscaledy/2) + arRectLeft = cleintRectCentreX-(arscaledx/2) + arRectWidth = arscaledx + arRectHeight = arscaledy + + arPreviewRect.style.top = arRectTop+'px'; + arPreviewRect.style.left = arRectLeft+'px'; + arPreviewRect.style.width = arRectWidth+'px'; + arPreviewRect.style.height = arRectHeight+'px'; + + clearTimeout(arFrameTimeout); + arFrameTimeout = setTimeout(function(){ + arPreviewRect.style.display = 'none'; + },2000); + + arPreviewRect.style.display = 'block'; + + } + +} + + +onUiUpdate(function(){ + var arPreviewRect = gradioApp().querySelector('#imageARPreview'); + if(arPreviewRect){ + arPreviewRect.style.display = 'none'; + } + var inImg2img = Boolean(gradioApp().querySelector("button.rounded-t-lg.border-gray-200")) + if(inImg2img){ + let inputs = gradioApp().querySelectorAll('input'); + inputs.forEach(function(e){ + var is_width = e.parentElement.id == "img2img_width" + var is_height = e.parentElement.id == "img2img_height" + + if((is_width || is_height) && !e.classList.contains('scrollwatch')){ + e.addEventListener('input', function(e){dimensionChange(e, is_width, is_height)} ) + e.classList.add('scrollwatch') + } + if(is_width){ + currentWidth = e.value*1.0 + } + if(is_height){ + currentHeight = e.value*1.0 + } + }) + } +}); diff --git a/sd/stable-diffusion-webui/javascript/contextMenus.js b/sd/stable-diffusion-webui/javascript/contextMenus.js new file mode 100644 index 0000000000000000000000000000000000000000..163743c9debc160d46bd3a5a43dec6879e86a43e --- /dev/null +++ b/sd/stable-diffusion-webui/javascript/contextMenus.js @@ -0,0 +1,177 @@ + +contextMenuInit = function(){ + let eventListenerApplied=false; + let menuSpecs = new Map(); + + const uid = function(){ + return Date.now().toString(36) + Math.random().toString(36).substr(2); + } + + function showContextMenu(event,element,menuEntries){ + let posx = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; + let posy = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; + + let oldMenu = gradioApp().querySelector('#context-menu') + if(oldMenu){ + oldMenu.remove() + } + + let tabButton = uiCurrentTab + let baseStyle = window.getComputedStyle(tabButton) + + const contextMenu = document.createElement('nav') + contextMenu.id = "context-menu" + contextMenu.style.background = baseStyle.background + contextMenu.style.color = baseStyle.color + contextMenu.style.fontFamily = baseStyle.fontFamily + contextMenu.style.top = posy+'px' + contextMenu.style.left = posx+'px' + + + + const contextMenuList = document.createElement('ul') + contextMenuList.className = 'context-menu-items'; + contextMenu.append(contextMenuList); + + menuEntries.forEach(function(entry){ + let contextMenuEntry = document.createElement('a') + contextMenuEntry.innerHTML = entry['name'] + contextMenuEntry.addEventListener("click", function(e) { + entry['func'](); + }) + contextMenuList.append(contextMenuEntry); + + }) + + gradioApp().getRootNode().appendChild(contextMenu) + + let menuWidth = contextMenu.offsetWidth + 4; + let menuHeight = contextMenu.offsetHeight + 4; + + let windowWidth = window.innerWidth; + let windowHeight = window.innerHeight; + + if ( (windowWidth - posx) < menuWidth ) { + contextMenu.style.left = windowWidth - menuWidth + "px"; + } + + if ( (windowHeight - posy) < menuHeight ) { + contextMenu.style.top = windowHeight - menuHeight + "px"; + } + + } + + function appendContextMenuOption(targetElementSelector,entryName,entryFunction){ + + currentItems = menuSpecs.get(targetElementSelector) + + if(!currentItems){ + currentItems = [] + menuSpecs.set(targetElementSelector,currentItems); + } + let newItem = {'id':targetElementSelector+'_'+uid(), + 'name':entryName, + 'func':entryFunction, + 'isNew':true} + + currentItems.push(newItem) + return newItem['id'] + } + + function removeContextMenuOption(uid){ + menuSpecs.forEach(function(v,k) { + let index = -1 + v.forEach(function(e,ei){if(e['id']==uid){index=ei}}) + if(index>=0){ + v.splice(index, 1); + } + }) + } + + function addContextMenuEventListener(){ + if(eventListenerApplied){ + return; + } + gradioApp().addEventListener("click", function(e) { + let source = e.composedPath()[0] + if(source.id && source.id.indexOf('check_progress')>-1){ + return + } + + let oldMenu = gradioApp().querySelector('#context-menu') + if(oldMenu){ + oldMenu.remove() + } + }); + gradioApp().addEventListener("contextmenu", function(e) { + let oldMenu = gradioApp().querySelector('#context-menu') + if(oldMenu){ + oldMenu.remove() + } + menuSpecs.forEach(function(v,k) { + if(e.composedPath()[0].matches(k)){ + showContextMenu(e,e.composedPath()[0],v) + e.preventDefault() + return + } + }) + }); + eventListenerApplied=true + + } + + return [appendContextMenuOption, removeContextMenuOption, addContextMenuEventListener] +} + +initResponse = contextMenuInit(); +appendContextMenuOption = initResponse[0]; +removeContextMenuOption = initResponse[1]; +addContextMenuEventListener = initResponse[2]; + +(function(){ + //Start example Context Menu Items + let generateOnRepeat = function(genbuttonid,interruptbuttonid){ + let genbutton = gradioApp().querySelector(genbuttonid); + let interruptbutton = gradioApp().querySelector(interruptbuttonid); + if(!interruptbutton.offsetParent){ + genbutton.click(); + } + clearInterval(window.generateOnRepeatInterval) + window.generateOnRepeatInterval = setInterval(function(){ + if(!interruptbutton.offsetParent){ + genbutton.click(); + } + }, + 500) + } + + appendContextMenuOption('#txt2img_generate','Generate forever',function(){ + generateOnRepeat('#txt2img_generate','#txt2img_interrupt'); + }) + appendContextMenuOption('#img2img_generate','Generate forever',function(){ + generateOnRepeat('#img2img_generate','#img2img_interrupt'); + }) + + let cancelGenerateForever = function(){ + clearInterval(window.generateOnRepeatInterval) + } + + appendContextMenuOption('#txt2img_interrupt','Cancel generate forever',cancelGenerateForever) + appendContextMenuOption('#txt2img_generate', 'Cancel generate forever',cancelGenerateForever) + appendContextMenuOption('#img2img_interrupt','Cancel generate forever',cancelGenerateForever) + appendContextMenuOption('#img2img_generate', 'Cancel generate forever',cancelGenerateForever) + + appendContextMenuOption('#roll','Roll three', + function(){ + let rollbutton = get_uiCurrentTabContent().querySelector('#roll'); + setTimeout(function(){rollbutton.click()},100) + setTimeout(function(){rollbutton.click()},200) + setTimeout(function(){rollbutton.click()},300) + } + ) +})(); +//End example Context Menu Items + +onUiUpdate(function(){ + addContextMenuEventListener() +}); diff --git a/sd/stable-diffusion-webui/javascript/dragdrop.js b/sd/stable-diffusion-webui/javascript/dragdrop.js new file mode 100644 index 0000000000000000000000000000000000000000..fe00892481a8ff8b997759b21cb36eb364db788b --- /dev/null +++ b/sd/stable-diffusion-webui/javascript/dragdrop.js @@ -0,0 +1,97 @@ +// allows drag-dropping files into gradio image elements, and also pasting images from clipboard + +function isValidImageList( files ) { + return files && files?.length === 1 && ['image/png', 'image/gif', 'image/jpeg'].includes(files[0].type); +} + +function dropReplaceImage( imgWrap, files ) { + if ( ! isValidImageList( files ) ) { + return; + } + + const tmpFile = files[0]; + + imgWrap.querySelector('.modify-upload button + button, .touch-none + div button + button')?.click(); + const callback = () => { + const fileInput = imgWrap.querySelector('input[type="file"]'); + if ( fileInput ) { + if ( files.length === 0 ) { + files = new DataTransfer(); + files.items.add(tmpFile); + fileInput.files = files.files; + } else { + fileInput.files = files; + } + fileInput.dispatchEvent(new Event('change')); + } + }; + + if ( imgWrap.closest('#pnginfo_image') ) { + // special treatment for PNG Info tab, wait for fetch request to finish + const oldFetch = window.fetch; + window.fetch = async (input, options) => { + const response = await oldFetch(input, options); + if ( 'api/predict/' === input ) { + const content = await response.text(); + window.fetch = oldFetch; + window.requestAnimationFrame( () => callback() ); + return new Response(content, { + status: response.status, + statusText: response.statusText, + headers: response.headers + }) + } + return response; + }; + } else { + window.requestAnimationFrame( () => callback() ); + } +} + +window.document.addEventListener('dragover', e => { + const target = e.composedPath()[0]; + const imgWrap = target.closest('[data-testid="image"]'); + if ( !imgWrap && target.placeholder && target.placeholder.indexOf("Prompt") == -1) { + return; + } + e.stopPropagation(); + e.preventDefault(); + e.dataTransfer.dropEffect = 'copy'; +}); + +window.document.addEventListener('drop', e => { + const target = e.composedPath()[0]; + if (target.placeholder.indexOf("Prompt") == -1) { + return; + } + const imgWrap = target.closest('[data-testid="image"]'); + if ( !imgWrap ) { + return; + } + e.stopPropagation(); + e.preventDefault(); + const files = e.dataTransfer.files; + dropReplaceImage( imgWrap, files ); +}); + +window.addEventListener('paste', e => { + const files = e.clipboardData.files; + if ( ! isValidImageList( files ) ) { + return; + } + + const visibleImageFields = [...gradioApp().querySelectorAll('[data-testid="image"]')] + .filter(el => uiElementIsVisible(el)); + if ( ! visibleImageFields.length ) { + return; + } + + const firstFreeImageField = visibleImageFields + .filter(el => el.querySelector('input[type=file]'))?.[0]; + + dropReplaceImage( + firstFreeImageField ? + firstFreeImageField : + visibleImageFields[visibleImageFields.length - 1] + , files ); +}); diff --git a/sd/stable-diffusion-webui/javascript/edit-attention.js b/sd/stable-diffusion-webui/javascript/edit-attention.js new file mode 100644 index 0000000000000000000000000000000000000000..6e6905daf536d164cc2a246886ca8198603ce61a --- /dev/null +++ b/sd/stable-diffusion-webui/javascript/edit-attention.js @@ -0,0 +1,96 @@ +function keyupEditAttention(event){ + let target = event.originalTarget || event.composedPath()[0]; + if (!target.matches("[id*='_toprow'] textarea.gr-text-input[placeholder]")) return; + if (! (event.metaKey || event.ctrlKey)) return; + + let isPlus = event.key == "ArrowUp" + let isMinus = event.key == "ArrowDown" + if (!isPlus && !isMinus) return; + + let selectionStart = target.selectionStart; + let selectionEnd = target.selectionEnd; + let text = target.value; + + function selectCurrentParenthesisBlock(OPEN, CLOSE){ + if (selectionStart !== selectionEnd) return false; + + // Find opening parenthesis around current cursor + const before = text.substring(0, selectionStart); + let beforeParen = before.lastIndexOf(OPEN); + if (beforeParen == -1) return false; + let beforeParenClose = before.lastIndexOf(CLOSE); + while (beforeParenClose !== -1 && beforeParenClose > beforeParen) { + beforeParen = before.lastIndexOf(OPEN, beforeParen - 1); + beforeParenClose = before.lastIndexOf(CLOSE, beforeParenClose - 1); + } + + // Find closing parenthesis around current cursor + const after = text.substring(selectionStart); + let afterParen = after.indexOf(CLOSE); + if (afterParen == -1) return false; + let afterParenOpen = after.indexOf(OPEN); + while (afterParenOpen !== -1 && afterParen > afterParenOpen) { + afterParen = after.indexOf(CLOSE, afterParen + 1); + afterParenOpen = after.indexOf(OPEN, afterParenOpen + 1); + } + if (beforeParen === -1 || afterParen === -1) return false; + + // Set the selection to the text between the parenthesis + const parenContent = text.substring(beforeParen + 1, selectionStart + afterParen); + const lastColon = parenContent.lastIndexOf(":"); + selectionStart = beforeParen + 1; + selectionEnd = selectionStart + lastColon; + target.setSelectionRange(selectionStart, selectionEnd); + return true; + } + + // If the user hasn't selected anything, let's select their current parenthesis block + if(! selectCurrentParenthesisBlock('<', '>')){ + selectCurrentParenthesisBlock('(', ')') + } + + event.preventDefault(); + + closeCharacter = ')' + delta = opts.keyedit_precision_attention + + if (selectionStart > 0 && text[selectionStart - 1] == '<'){ + closeCharacter = '>' + delta = opts.keyedit_precision_extra + } else if (selectionStart == 0 || text[selectionStart - 1] != "(") { + + // do not include spaces at the end + while(selectionEnd > selectionStart && text[selectionEnd-1] == ' '){ + selectionEnd -= 1; + } + if(selectionStart == selectionEnd){ + return + } + + text = text.slice(0, selectionStart) + "(" + text.slice(selectionStart, selectionEnd) + ":1.0)" + text.slice(selectionEnd); + + selectionStart += 1; + selectionEnd += 1; + } + + end = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1; + weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + 1 + end)); + if (isNaN(weight)) return; + + weight += isPlus ? delta : -delta; + weight = parseFloat(weight.toPrecision(12)); + if(String(weight).length == 1) weight += ".0" + + text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + 1 + end - 1); + + target.focus(); + target.value = text; + target.selectionStart = selectionStart; + target.selectionEnd = selectionEnd; + + updateInput(target) +} + +addEventListener('keydown', (event) => { + keyupEditAttention(event); +}); \ No newline at end of file diff --git a/sd/stable-diffusion-webui/javascript/extensions.js b/sd/stable-diffusion-webui/javascript/extensions.js new file mode 100644 index 0000000000000000000000000000000000000000..8a0580f706a9511e3391b9170e6684c2655b893a --- /dev/null +++ b/sd/stable-diffusion-webui/javascript/extensions.js @@ -0,0 +1,49 @@ + +function extensions_apply(_, _){ + var disable = [] + var update = [] + + gradioApp().querySelectorAll('#extensions input[type="checkbox"]').forEach(function(x){ + if(x.name.startsWith("enable_") && ! x.checked) + disable.push(x.name.substr(7)) + + if(x.name.startsWith("update_") && x.checked) + update.push(x.name.substr(7)) + }) + + restart_reload() + + return [JSON.stringify(disable), JSON.stringify(update)] +} + +function extensions_check(){ + var disable = [] + + gradioApp().querySelectorAll('#extensions input[type="checkbox"]').forEach(function(x){ + if(x.name.startsWith("enable_") && ! x.checked) + disable.push(x.name.substr(7)) + }) + + gradioApp().querySelectorAll('#extensions .extension_status').forEach(function(x){ + x.innerHTML = "Loading..." + }) + + + var id = randomId() + requestProgress(id, gradioApp().getElementById('extensions_installed_top'), null, function(){ + + }) + + return [id, JSON.stringify(disable)] +} + +function install_extension_from_index(button, url){ + button.disabled = "disabled" + button.value = "Installing..." + + textarea = gradioApp().querySelector('#extension_to_install textarea') + textarea.value = url + updateInput(textarea) + + gradioApp().querySelector('#install_extension_button').click() +} diff --git a/sd/stable-diffusion-webui/javascript/extraNetworks.js b/sd/stable-diffusion-webui/javascript/extraNetworks.js new file mode 100644 index 0000000000000000000000000000000000000000..b15758b91ebd271604ac8ad342a19537b2efc760 --- /dev/null +++ b/sd/stable-diffusion-webui/javascript/extraNetworks.js @@ -0,0 +1,107 @@ + +function setupExtraNetworksForTab(tabname){ + gradioApp().querySelector('#'+tabname+'_extra_tabs').classList.add('extra-networks') + + var tabs = gradioApp().querySelector('#'+tabname+'_extra_tabs > div') + var search = gradioApp().querySelector('#'+tabname+'_extra_search textarea') + var refresh = gradioApp().getElementById(tabname+'_extra_refresh') + var close = gradioApp().getElementById(tabname+'_extra_close') + + search.classList.add('search') + tabs.appendChild(search) + tabs.appendChild(refresh) + tabs.appendChild(close) + + search.addEventListener("input", function(evt){ + searchTerm = search.value.toLowerCase() + + gradioApp().querySelectorAll('#'+tabname+'_extra_tabs div.card').forEach(function(elem){ + text = elem.querySelector('.name').textContent.toLowerCase() + " " + elem.querySelector('.search_term').textContent.toLowerCase() + elem.style.display = text.indexOf(searchTerm) == -1 ? "none" : "" + }) + }); +} + +var activePromptTextarea = {}; + +function setupExtraNetworks(){ + setupExtraNetworksForTab('txt2img') + setupExtraNetworksForTab('img2img') + + function registerPrompt(tabname, id){ + var textarea = gradioApp().querySelector("#" + id + " > label > textarea"); + + if (! activePromptTextarea[tabname]){ + activePromptTextarea[tabname] = textarea + } + + textarea.addEventListener("focus", function(){ + activePromptTextarea[tabname] = textarea; + }); + } + + registerPrompt('txt2img', 'txt2img_prompt') + registerPrompt('txt2img', 'txt2img_neg_prompt') + registerPrompt('img2img', 'img2img_prompt') + registerPrompt('img2img', 'img2img_neg_prompt') +} + +onUiLoaded(setupExtraNetworks) + +var re_extranet = /<([^:]+:[^:]+):[\d\.]+>/; +var re_extranet_g = /\s+<([^:]+:[^:]+):[\d\.]+>/g; + +function tryToRemoveExtraNetworkFromPrompt(textarea, text){ + var m = text.match(re_extranet) + if(! m) return false + + var partToSearch = m[1] + var replaced = false + var newTextareaText = textarea.value.replaceAll(re_extranet_g, function(found, index){ + m = found.match(re_extranet); + if(m[1] == partToSearch){ + replaced = true; + return "" + } + return found; + }) + + if(replaced){ + textarea.value = newTextareaText + return true; + } + + return false +} + +function cardClicked(tabname, textToAdd, allowNegativePrompt){ + var textarea = allowNegativePrompt ? activePromptTextarea[tabname] : gradioApp().querySelector("#" + tabname + "_prompt > label > textarea") + + if(! tryToRemoveExtraNetworkFromPrompt(textarea, textToAdd)){ + textarea.value = textarea.value + " " + textToAdd + } + + updateInput(textarea) +} + +function saveCardPreview(event, tabname, filename){ + var textarea = gradioApp().querySelector("#" + tabname + '_preview_filename > label > textarea') + var button = gradioApp().getElementById(tabname + '_save_preview') + + textarea.value = filename + updateInput(textarea) + + button.click() + + event.stopPropagation() + event.preventDefault() +} + +function extraNetworksSearchButton(tabs_id, event){ + searchTextarea = gradioApp().querySelector("#" + tabs_id + ' > div > textarea') + button = event.target + text = button.classList.contains("search-all") ? "" : button.textContent.trim() + + searchTextarea.value = text + updateInput(searchTextarea) +} \ No newline at end of file diff --git a/sd/stable-diffusion-webui/javascript/generationParams.js b/sd/stable-diffusion-webui/javascript/generationParams.js new file mode 100644 index 0000000000000000000000000000000000000000..95f050939b72a8d09d62de8d725caf1e7d15d3c0 --- /dev/null +++ b/sd/stable-diffusion-webui/javascript/generationParams.js @@ -0,0 +1,33 @@ +// attaches listeners to the txt2img and img2img galleries to update displayed generation param text when the image changes + +let txt2img_gallery, img2img_gallery, modal = undefined; +onUiUpdate(function(){ + if (!txt2img_gallery) { + txt2img_gallery = attachGalleryListeners("txt2img") + } + if (!img2img_gallery) { + img2img_gallery = attachGalleryListeners("img2img") + } + if (!modal) { + modal = gradioApp().getElementById('lightboxModal') + modalObserver.observe(modal, { attributes : true, attributeFilter : ['style'] }); + } +}); + +let modalObserver = new MutationObserver(function(mutations) { + mutations.forEach(function(mutationRecord) { + let selectedTab = gradioApp().querySelector('#tabs div button.bg-white')?.innerText + if (mutationRecord.target.style.display === 'none' && selectedTab === 'txt2img' || selectedTab === 'img2img') + gradioApp().getElementById(selectedTab+"_generation_info_button").click() + }); +}); + +function attachGalleryListeners(tab_name) { + gallery = gradioApp().querySelector('#'+tab_name+'_gallery') + gallery?.addEventListener('click', () => gradioApp().getElementById(tab_name+"_generation_info_button").click()); + gallery?.addEventListener('keydown', (e) => { + if (e.keyCode == 37 || e.keyCode == 39) // left or right arrow + gradioApp().getElementById(tab_name+"_generation_info_button").click() + }); + return gallery; +} diff --git a/sd/stable-diffusion-webui/javascript/hints.js b/sd/stable-diffusion-webui/javascript/hints.js new file mode 100644 index 0000000000000000000000000000000000000000..f1199009b181b83713bb1d136e41d4b29e183634 --- /dev/null +++ b/sd/stable-diffusion-webui/javascript/hints.js @@ -0,0 +1,146 @@ +// mouseover tooltips for various UI elements + +titles = { + "Sampling steps": "How many times to improve the generated image iteratively; higher values take longer; very low values can produce bad results", + "Sampling method": "Which algorithm to use to produce the image", + "GFPGAN": "Restore low quality faces using GFPGAN neural network", + "Euler a": "Euler Ancestral - very creative, each can get a completely different picture depending on step count, setting steps higher than 30-40 does not help", + "DDIM": "Denoising Diffusion Implicit Models - best at inpainting", + "DPM adaptive": "Ignores step count - uses a number of steps determined by the CFG and resolution", + + "Batch count": "How many batches of images to create (has no impact on generation performance or VRAM usage)", + "Batch size": "How many image to create in a single batch (increases generation performance at cost of higher VRAM usage)", + "CFG Scale": "Classifier Free Guidance Scale - how strongly the image should conform to prompt - lower values produce more creative results", + "Seed": "A value that determines the output of random number generator - if you create an image with same parameters and seed as another image, you'll get the same result", + "\u{1f3b2}\ufe0f": "Set seed to -1, which will cause a new random number to be used every time", + "\u267b\ufe0f": "Reuse seed from last generation, mostly useful if it was randomed", + "\u2199\ufe0f": "Read generation parameters from prompt or last generation if prompt is empty into user interface.", + "\u{1f4c2}": "Open images output directory", + "\u{1f4be}": "Save style", + "\u{1f5d1}": "Clear prompt", + "\u{1f4cb}": "Apply selected styles to current prompt", + "\u{1f4d2}": "Paste available values into the field", + "\u{1f3b4}": "Show extra networks", + + + "Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt", + "SD upscale": "Upscale image normally, split result into tiles, improve each tile using img2img, merge whole image back", + + "Just resize": "Resize image to target resolution. Unless height and width match, you will get incorrect aspect ratio.", + "Crop and resize": "Resize the image so that entirety of target resolution is filled with the image. Crop parts that stick out.", + "Resize and fill": "Resize the image so that entirety of image is inside target resolution. Fill empty space with image's colors.", + + "Mask blur": "How much to blur the mask before processing, in pixels.", + "Masked content": "What to put inside the masked area before processing it with Stable Diffusion.", + "fill": "fill it with colors of the image", + "original": "keep whatever was there originally", + "latent noise": "fill it with latent space noise", + "latent nothing": "fill it with latent space zeroes", + "Inpaint at full resolution": "Upscale masked region to target resolution, do inpainting, downscale back and paste into original image", + + "Denoising strength": "Determines how little respect the algorithm should have for image's content. At 0, nothing will change, and at 1 you'll get an unrelated image. With values below 1.0, processing will take less steps than the Sampling Steps slider specifies.", + "Denoising strength change factor": "In loopback mode, on each loop the denoising strength is multiplied by this value. <1 means decreasing variety so your sequence will converge on a fixed picture. >1 means increasing variety so your sequence will become more and more chaotic.", + + "Skip": "Stop processing current image and continue processing.", + "Interrupt": "Stop processing images and return any results accumulated so far.", + "Save": "Write image to a directory (default - log/images) and generation parameters into csv file.", + + "X values": "Separate values for X axis using commas.", + "Y values": "Separate values for Y axis using commas.", + + "None": "Do not do anything special", + "Prompt matrix": "Separate prompts into parts using vertical pipe character (|) and the script will create a picture for every combination of them (except for the first part, which will be present in all combinations)", + "X/Y/Z plot": "Create grid(s) where images will have different parameters. Use inputs below to specify which parameters will be shared by columns and rows", + "Custom code": "Run Python code. Advanced user only. Must run program with --allow-code for this to work", + + "Prompt S/R": "Separate a list of words with commas, and the first word will be used as a keyword: script will search for this word in the prompt, and replace it with others", + "Prompt order": "Separate a list of words with commas, and the script will make a variation of prompt with those words for their every possible order", + + "Tiling": "Produce an image that can be tiled.", + "Tile overlap": "For SD upscale, how much overlap in pixels should there be between tiles. Tiles overlap so that when they are merged back into one picture, there is no clearly visible seam.", + + "Variation seed": "Seed of a different picture to be mixed into the generation.", + "Variation strength": "How strong of a variation to produce. At 0, there will be no effect. At 1, you will get the complete picture with variation seed (except for ancestral samplers, where you will just get something).", + "Resize seed from height": "Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution", + "Resize seed from width": "Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution", + + "Interrogate": "Reconstruct prompt from existing image and put it into the prompt field.", + + "Images filename pattern": "Use following tags to define how filenames for images are chosen: [steps], [cfg], [prompt_hash], [prompt], [prompt_no_styles], [prompt_spaces], [width], [height], [styles], [sampler], [seed], [model_hash], [model_name], [prompt_words], [date], [datetime], [datetime], [datetime
', None) + + self.token_mults = {} + tokens_with_parens = [(k, v) for k, v in vocab.items() if '(' in k or ')' in k or '[' in k or ']' in k] + for text, ident in tokens_with_parens: + mult = 1.0 + for c in text: + if c == '[': + mult /= 1.1 + if c == ']': + mult *= 1.1 + if c == '(': + mult *= 1.1 + if c == ')': + mult /= 1.1 + + if mult != 1.0: + self.token_mults[ident] = mult + + self.id_start = self.wrapped.tokenizer.bos_token_id + self.id_end = self.wrapped.tokenizer.eos_token_id + self.id_pad = self.id_end + + def tokenize(self, texts): + tokenized = self.wrapped.tokenizer(texts, truncation=False, add_special_tokens=False)["input_ids"] + + return tokenized + + def encode_with_transformers(self, tokens): + outputs = self.wrapped.transformer(input_ids=tokens, output_hidden_states=-opts.CLIP_stop_at_last_layers) + + if opts.CLIP_stop_at_last_layers > 1: + z = outputs.hidden_states[-opts.CLIP_stop_at_last_layers] + z = self.wrapped.transformer.text_model.final_layer_norm(z) + else: + z = outputs.last_hidden_state + + return z + + def encode_embedding_init_text(self, init_text, nvpt): + embedding_layer = self.wrapped.transformer.text_model.embeddings + ids = self.wrapped.tokenizer(init_text, max_length=nvpt, return_tensors="pt", add_special_tokens=False)["input_ids"] + embedded = embedding_layer.token_embedding.wrapped(ids.to(embedding_layer.token_embedding.wrapped.weight.device)).squeeze(0) + + return embedded diff --git a/sd/stable-diffusion-webui/modules/sd_hijack_clip_old.py b/sd/stable-diffusion-webui/modules/sd_hijack_clip_old.py new file mode 100644 index 0000000000000000000000000000000000000000..433d8c3da8e33aa09833dcd1793395f420e984d7 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_hijack_clip_old.py @@ -0,0 +1,81 @@ +from modules import sd_hijack_clip +from modules import shared + + +def process_text_old(self: sd_hijack_clip.FrozenCLIPEmbedderWithCustomWordsBase, texts): + id_start = self.id_start + id_end = self.id_end + maxlen = self.wrapped.max_length # you get to stay at 77 + used_custom_terms = [] + remade_batch_tokens = [] + hijack_comments = [] + hijack_fixes = [] + token_count = 0 + + cache = {} + batch_tokens = self.tokenize(texts) + batch_multipliers = [] + for tokens in batch_tokens: + tuple_tokens = tuple(tokens) + + if tuple_tokens in cache: + remade_tokens, fixes, multipliers = cache[tuple_tokens] + else: + fixes = [] + remade_tokens = [] + multipliers = [] + mult = 1.0 + + i = 0 + while i < len(tokens): + token = tokens[i] + + embedding, embedding_length_in_tokens = self.hijack.embedding_db.find_embedding_at_position(tokens, i) + + mult_change = self.token_mults.get(token) if shared.opts.enable_emphasis else None + if mult_change is not None: + mult *= mult_change + i += 1 + elif embedding is None: + remade_tokens.append(token) + multipliers.append(mult) + i += 1 + else: + emb_len = int(embedding.vec.shape[0]) + fixes.append((len(remade_tokens), embedding)) + remade_tokens += [0] * emb_len + multipliers += [mult] * emb_len + used_custom_terms.append((embedding.name, embedding.checksum())) + i += embedding_length_in_tokens + + if len(remade_tokens) > maxlen - 2: + vocab = {v: k for k, v in self.wrapped.tokenizer.get_vocab().items()} + ovf = remade_tokens[maxlen - 2:] + overflowing_words = [vocab.get(int(x), "") for x in ovf] + overflowing_text = self.wrapped.tokenizer.convert_tokens_to_string(''.join(overflowing_words)) + hijack_comments.append(f"Warning: too many input tokens; some ({len(overflowing_words)}) have been truncated:\n{overflowing_text}\n") + + token_count = len(remade_tokens) + remade_tokens = remade_tokens + [id_end] * (maxlen - 2 - len(remade_tokens)) + remade_tokens = [id_start] + remade_tokens[0:maxlen - 2] + [id_end] + cache[tuple_tokens] = (remade_tokens, fixes, multipliers) + + multipliers = multipliers + [1.0] * (maxlen - 2 - len(multipliers)) + multipliers = [1.0] + multipliers[0:maxlen - 2] + [1.0] + + remade_batch_tokens.append(remade_tokens) + hijack_fixes.append(fixes) + batch_multipliers.append(multipliers) + return batch_multipliers, remade_batch_tokens, used_custom_terms, hijack_comments, hijack_fixes, token_count + + +def forward_old(self: sd_hijack_clip.FrozenCLIPEmbedderWithCustomWordsBase, texts): + batch_multipliers, remade_batch_tokens, used_custom_terms, hijack_comments, hijack_fixes, token_count = process_text_old(self, texts) + + self.hijack.comments += hijack_comments + + if len(used_custom_terms) > 0: + self.hijack.comments.append("Used embeddings: " + ", ".join([f'{word} [{checksum}]' for word, checksum in used_custom_terms])) + + self.hijack.fixes = hijack_fixes + return self.process_tokens(remade_batch_tokens, batch_multipliers) diff --git a/sd/stable-diffusion-webui/modules/sd_hijack_inpainting.py b/sd/stable-diffusion-webui/modules/sd_hijack_inpainting.py new file mode 100644 index 0000000000000000000000000000000000000000..55a2ce4d19200acafd79e6fce7e017c4abc50a73 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_hijack_inpainting.py @@ -0,0 +1,103 @@ +import os +import torch + +from einops import repeat +from omegaconf import ListConfig + +import ldm.models.diffusion.ddpm +import ldm.models.diffusion.ddim +import ldm.models.diffusion.plms + +from ldm.models.diffusion.ddpm import LatentDiffusion +from ldm.models.diffusion.plms import PLMSSampler +from ldm.models.diffusion.ddim import DDIMSampler, noise_like +from ldm.models.diffusion.sampling_util import norm_thresholding + + +@torch.no_grad() +def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False, + temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None, + unconditional_guidance_scale=1., unconditional_conditioning=None, old_eps=None, t_next=None, dynamic_threshold=None): + b, *_, device = *x.shape, x.device + + def get_model_output(x, t): + if unconditional_conditioning is None or unconditional_guidance_scale == 1.: + e_t = self.model.apply_model(x, t, c) + else: + x_in = torch.cat([x] * 2) + t_in = torch.cat([t] * 2) + + if isinstance(c, dict): + assert isinstance(unconditional_conditioning, dict) + c_in = dict() + for k in c: + if isinstance(c[k], list): + c_in[k] = [ + torch.cat([unconditional_conditioning[k][i], c[k][i]]) + for i in range(len(c[k])) + ] + else: + c_in[k] = torch.cat([unconditional_conditioning[k], c[k]]) + else: + c_in = torch.cat([unconditional_conditioning, c]) + + e_t_uncond, e_t = self.model.apply_model(x_in, t_in, c_in).chunk(2) + e_t = e_t_uncond + unconditional_guidance_scale * (e_t - e_t_uncond) + + if score_corrector is not None: + assert self.model.parameterization == "eps" + e_t = score_corrector.modify_score(self.model, e_t, x, t, c, **corrector_kwargs) + + return e_t + + alphas = self.model.alphas_cumprod if use_original_steps else self.ddim_alphas + alphas_prev = self.model.alphas_cumprod_prev if use_original_steps else self.ddim_alphas_prev + sqrt_one_minus_alphas = self.model.sqrt_one_minus_alphas_cumprod if use_original_steps else self.ddim_sqrt_one_minus_alphas + sigmas = self.model.ddim_sigmas_for_original_num_steps if use_original_steps else self.ddim_sigmas + + def get_x_prev_and_pred_x0(e_t, index): + # select parameters corresponding to the currently considered timestep + a_t = torch.full((b, 1, 1, 1), alphas[index], device=device) + a_prev = torch.full((b, 1, 1, 1), alphas_prev[index], device=device) + sigma_t = torch.full((b, 1, 1, 1), sigmas[index], device=device) + sqrt_one_minus_at = torch.full((b, 1, 1, 1), sqrt_one_minus_alphas[index],device=device) + + # current prediction for x_0 + pred_x0 = (x - sqrt_one_minus_at * e_t) / a_t.sqrt() + if quantize_denoised: + pred_x0, _, *_ = self.model.first_stage_model.quantize(pred_x0) + if dynamic_threshold is not None: + pred_x0 = norm_thresholding(pred_x0, dynamic_threshold) + # direction pointing to x_t + dir_xt = (1. - a_prev - sigma_t**2).sqrt() * e_t + noise = sigma_t * noise_like(x.shape, device, repeat_noise) * temperature + if noise_dropout > 0.: + noise = torch.nn.functional.dropout(noise, p=noise_dropout) + x_prev = a_prev.sqrt() * pred_x0 + dir_xt + noise + return x_prev, pred_x0 + + e_t = get_model_output(x, t) + if len(old_eps) == 0: + # Pseudo Improved Euler (2nd order) + x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t, index) + e_t_next = get_model_output(x_prev, t_next) + e_t_prime = (e_t + e_t_next) / 2 + elif len(old_eps) == 1: + # 2nd order Pseudo Linear Multistep (Adams-Bashforth) + e_t_prime = (3 * e_t - old_eps[-1]) / 2 + elif len(old_eps) == 2: + # 3nd order Pseudo Linear Multistep (Adams-Bashforth) + e_t_prime = (23 * e_t - 16 * old_eps[-1] + 5 * old_eps[-2]) / 12 + elif len(old_eps) >= 3: + # 4nd order Pseudo Linear Multistep (Adams-Bashforth) + e_t_prime = (55 * e_t - 59 * old_eps[-1] + 37 * old_eps[-2] - 9 * old_eps[-3]) / 24 + + x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t_prime, index) + + return x_prev, pred_x0, e_t + + +def do_inpainting_hijack(): + # p_sample_plms is needed because PLMS can't work with dicts as conditionings + + ldm.models.diffusion.plms.PLMSSampler.p_sample_plms = p_sample_plms diff --git a/sd/stable-diffusion-webui/modules/sd_hijack_ip2p.py b/sd/stable-diffusion-webui/modules/sd_hijack_ip2p.py new file mode 100644 index 0000000000000000000000000000000000000000..3c727d3b75332508629458d23f7fb86cc9ede44b --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_hijack_ip2p.py @@ -0,0 +1,13 @@ +import collections +import os.path +import sys +import gc +import time + +def should_hijack_ip2p(checkpoint_info): + from modules import sd_models_config + + ckpt_basename = os.path.basename(checkpoint_info.filename).lower() + cfg_basename = os.path.basename(sd_models_config.find_checkpoint_config_near_filename(checkpoint_info)).lower() + + return "pix2pix" in ckpt_basename and not "pix2pix" in cfg_basename diff --git a/sd/stable-diffusion-webui/modules/sd_hijack_open_clip.py b/sd/stable-diffusion-webui/modules/sd_hijack_open_clip.py new file mode 100644 index 0000000000000000000000000000000000000000..f13feb5c0373d10248d266e02dd28dd88b02c175 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_hijack_open_clip.py @@ -0,0 +1,37 @@ +import open_clip.tokenizer +import torch + +from modules import sd_hijack_clip, devices +from modules.shared import opts + +tokenizer = open_clip.tokenizer._tokenizer + + +class FrozenOpenCLIPEmbedderWithCustomWords(sd_hijack_clip.FrozenCLIPEmbedderWithCustomWordsBase): + def __init__(self, wrapped, hijack): + super().__init__(wrapped, hijack) + + self.comma_token = [v for k, v in tokenizer.encoder.items() if k == ','][0] + self.id_start = tokenizer.encoder[""] + self.id_end = tokenizer.encoder[""] + self.id_pad = 0 + + def tokenize(self, texts): + assert not opts.use_old_emphasis_implementation, 'Old emphasis implementation not supported for Open Clip' + + tokenized = [tokenizer.encode(text) for text in texts] + + return tokenized + + def encode_with_transformers(self, tokens): + # set self.wrapped.layer_idx here according to opts.CLIP_stop_at_last_layers + z = self.wrapped.encode_with_transformer(tokens) + + return z + + def encode_embedding_init_text(self, init_text, nvpt): + ids = tokenizer.encode(init_text) + ids = torch.asarray([ids], device=devices.device, dtype=torch.int) + embedded = self.wrapped.model.token_embedding.wrapped(ids).squeeze(0) + + return embedded diff --git a/sd/stable-diffusion-webui/modules/sd_hijack_optimizations.py b/sd/stable-diffusion-webui/modules/sd_hijack_optimizations.py new file mode 100644 index 0000000000000000000000000000000000000000..b2eca9247660911030770740055521117203f16a --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_hijack_optimizations.py @@ -0,0 +1,444 @@ +import math +import sys +import traceback +import psutil + +import torch +from torch import einsum + +from ldm.util import default +from einops import rearrange + +from modules import shared, errors, devices +from modules.hypernetworks import hypernetwork + +from .sub_quadratic_attention import efficient_dot_product_attention + + +if shared.cmd_opts.xformers or shared.cmd_opts.force_enable_xformers: + try: + import xformers.ops + shared.xformers_available = True + except Exception: + print("Cannot import xformers", file=sys.stderr) + print(traceback.format_exc(), file=sys.stderr) + + +def get_available_vram(): + if shared.device.type == 'cuda': + stats = torch.cuda.memory_stats(shared.device) + mem_active = stats['active_bytes.all.current'] + mem_reserved = stats['reserved_bytes.all.current'] + mem_free_cuda, _ = torch.cuda.mem_get_info(torch.cuda.current_device()) + mem_free_torch = mem_reserved - mem_active + mem_free_total = mem_free_cuda + mem_free_torch + return mem_free_total + else: + return psutil.virtual_memory().available + + +# see https://github.com/basujindal/stable-diffusion/pull/117 for discussion +def split_cross_attention_forward_v1(self, x, context=None, mask=None): + h = self.heads + + q_in = self.to_q(x) + context = default(context, x) + + context_k, context_v = hypernetwork.apply_hypernetworks(shared.loaded_hypernetworks, context) + k_in = self.to_k(context_k) + v_in = self.to_v(context_v) + del context, context_k, context_v, x + + q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h=h), (q_in, k_in, v_in)) + del q_in, k_in, v_in + + dtype = q.dtype + if shared.opts.upcast_attn: + q, k, v = q.float(), k.float(), v.float() + + with devices.without_autocast(disable=not shared.opts.upcast_attn): + r1 = torch.zeros(q.shape[0], q.shape[1], v.shape[2], device=q.device, dtype=q.dtype) + for i in range(0, q.shape[0], 2): + end = i + 2 + s1 = einsum('b i d, b j d -> b i j', q[i:end], k[i:end]) + s1 *= self.scale + + s2 = s1.softmax(dim=-1) + del s1 + + r1[i:end] = einsum('b i j, b j d -> b i d', s2, v[i:end]) + del s2 + del q, k, v + + r1 = r1.to(dtype) + + r2 = rearrange(r1, '(b h) n d -> b n (h d)', h=h) + del r1 + + return self.to_out(r2) + + +# taken from https://github.com/Doggettx/stable-diffusion and modified +def split_cross_attention_forward(self, x, context=None, mask=None): + h = self.heads + + q_in = self.to_q(x) + context = default(context, x) + + context_k, context_v = hypernetwork.apply_hypernetworks(shared.loaded_hypernetworks, context) + k_in = self.to_k(context_k) + v_in = self.to_v(context_v) + + dtype = q_in.dtype + if shared.opts.upcast_attn: + q_in, k_in, v_in = q_in.float(), k_in.float(), v_in if v_in.device.type == 'mps' else v_in.float() + + with devices.without_autocast(disable=not shared.opts.upcast_attn): + k_in = k_in * self.scale + + del context, x + + q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h=h), (q_in, k_in, v_in)) + del q_in, k_in, v_in + + r1 = torch.zeros(q.shape[0], q.shape[1], v.shape[2], device=q.device, dtype=q.dtype) + + mem_free_total = get_available_vram() + + gb = 1024 ** 3 + tensor_size = q.shape[0] * q.shape[1] * k.shape[1] * q.element_size() + modifier = 3 if q.element_size() == 2 else 2.5 + mem_required = tensor_size * modifier + steps = 1 + + if mem_required > mem_free_total: + steps = 2 ** (math.ceil(math.log(mem_required / mem_free_total, 2))) + # print(f"Expected tensor size:{tensor_size/gb:0.1f}GB, cuda free:{mem_free_cuda/gb:0.1f}GB " + # f"torch free:{mem_free_torch/gb:0.1f} total:{mem_free_total/gb:0.1f} steps:{steps}") + + if steps > 64: + max_res = math.floor(math.sqrt(math.sqrt(mem_free_total / 2.5)) / 8) * 64 + raise RuntimeError(f'Not enough memory, use lower resolution (max approx. {max_res}x{max_res}). ' + f'Need: {mem_required / 64 / gb:0.1f}GB free, Have:{mem_free_total / gb:0.1f}GB free') + + slice_size = q.shape[1] // steps if (q.shape[1] % steps) == 0 else q.shape[1] + for i in range(0, q.shape[1], slice_size): + end = i + slice_size + s1 = einsum('b i d, b j d -> b i j', q[:, i:end], k) + + s2 = s1.softmax(dim=-1, dtype=q.dtype) + del s1 + + r1[:, i:end] = einsum('b i j, b j d -> b i d', s2, v) + del s2 + + del q, k, v + + r1 = r1.to(dtype) + + r2 = rearrange(r1, '(b h) n d -> b n (h d)', h=h) + del r1 + + return self.to_out(r2) + + +# -- Taken from https://github.com/invoke-ai/InvokeAI and modified -- +mem_total_gb = psutil.virtual_memory().total // (1 << 30) + +def einsum_op_compvis(q, k, v): + s = einsum('b i d, b j d -> b i j', q, k) + s = s.softmax(dim=-1, dtype=s.dtype) + return einsum('b i j, b j d -> b i d', s, v) + +def einsum_op_slice_0(q, k, v, slice_size): + r = torch.zeros(q.shape[0], q.shape[1], v.shape[2], device=q.device, dtype=q.dtype) + for i in range(0, q.shape[0], slice_size): + end = i + slice_size + r[i:end] = einsum_op_compvis(q[i:end], k[i:end], v[i:end]) + return r + +def einsum_op_slice_1(q, k, v, slice_size): + r = torch.zeros(q.shape[0], q.shape[1], v.shape[2], device=q.device, dtype=q.dtype) + for i in range(0, q.shape[1], slice_size): + end = i + slice_size + r[:, i:end] = einsum_op_compvis(q[:, i:end], k, v) + return r + +def einsum_op_mps_v1(q, k, v): + if q.shape[0] * q.shape[1] <= 2**16: # (512x512) max q.shape[1]: 4096 + return einsum_op_compvis(q, k, v) + else: + slice_size = math.floor(2**30 / (q.shape[0] * q.shape[1])) + if slice_size % 4096 == 0: + slice_size -= 1 + return einsum_op_slice_1(q, k, v, slice_size) + +def einsum_op_mps_v2(q, k, v): + if mem_total_gb > 8 and q.shape[0] * q.shape[1] <= 2**16: + return einsum_op_compvis(q, k, v) + else: + return einsum_op_slice_0(q, k, v, 1) + +def einsum_op_tensor_mem(q, k, v, max_tensor_mb): + size_mb = q.shape[0] * q.shape[1] * k.shape[1] * q.element_size() // (1 << 20) + if size_mb <= max_tensor_mb: + return einsum_op_compvis(q, k, v) + div = 1 << int((size_mb - 1) / max_tensor_mb).bit_length() + if div <= q.shape[0]: + return einsum_op_slice_0(q, k, v, q.shape[0] // div) + return einsum_op_slice_1(q, k, v, max(q.shape[1] // div, 1)) + +def einsum_op_cuda(q, k, v): + stats = torch.cuda.memory_stats(q.device) + mem_active = stats['active_bytes.all.current'] + mem_reserved = stats['reserved_bytes.all.current'] + mem_free_cuda, _ = torch.cuda.mem_get_info(q.device) + mem_free_torch = mem_reserved - mem_active + mem_free_total = mem_free_cuda + mem_free_torch + # Divide factor of safety as there's copying and fragmentation + return einsum_op_tensor_mem(q, k, v, mem_free_total / 3.3 / (1 << 20)) + +def einsum_op(q, k, v): + if q.device.type == 'cuda': + return einsum_op_cuda(q, k, v) + + if q.device.type == 'mps': + if mem_total_gb >= 32 and q.shape[0] % 32 != 0 and q.shape[0] * q.shape[1] < 2**18: + return einsum_op_mps_v1(q, k, v) + return einsum_op_mps_v2(q, k, v) + + # Smaller slices are faster due to L2/L3/SLC caches. + # Tested on i7 with 8MB L3 cache. + return einsum_op_tensor_mem(q, k, v, 32) + +def split_cross_attention_forward_invokeAI(self, x, context=None, mask=None): + h = self.heads + + q = self.to_q(x) + context = default(context, x) + + context_k, context_v = hypernetwork.apply_hypernetworks(shared.loaded_hypernetworks, context) + k = self.to_k(context_k) + v = self.to_v(context_v) + del context, context_k, context_v, x + + dtype = q.dtype + if shared.opts.upcast_attn: + q, k, v = q.float(), k.float(), v if v.device.type == 'mps' else v.float() + + with devices.without_autocast(disable=not shared.opts.upcast_attn): + k = k * self.scale + + q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h=h), (q, k, v)) + r = einsum_op(q, k, v) + r = r.to(dtype) + return self.to_out(rearrange(r, '(b h) n d -> b n (h d)', h=h)) + +# -- End of code from https://github.com/invoke-ai/InvokeAI -- + + +# Based on Birch-san's modified implementation of sub-quadratic attention from https://github.com/Birch-san/diffusers/pull/1 +# The sub_quad_attention_forward function is under the MIT License listed under Memory Efficient Attention in the Licenses section of the web UI interface +def sub_quad_attention_forward(self, x, context=None, mask=None): + assert mask is None, "attention-mask not currently implemented for SubQuadraticCrossAttnProcessor." + + h = self.heads + + q = self.to_q(x) + context = default(context, x) + + context_k, context_v = hypernetwork.apply_hypernetworks(shared.loaded_hypernetworks, context) + k = self.to_k(context_k) + v = self.to_v(context_v) + del context, context_k, context_v, x + + q = q.unflatten(-1, (h, -1)).transpose(1,2).flatten(end_dim=1) + k = k.unflatten(-1, (h, -1)).transpose(1,2).flatten(end_dim=1) + v = v.unflatten(-1, (h, -1)).transpose(1,2).flatten(end_dim=1) + + dtype = q.dtype + if shared.opts.upcast_attn: + q, k = q.float(), k.float() + + x = sub_quad_attention(q, k, v, q_chunk_size=shared.cmd_opts.sub_quad_q_chunk_size, kv_chunk_size=shared.cmd_opts.sub_quad_kv_chunk_size, chunk_threshold=shared.cmd_opts.sub_quad_chunk_threshold, use_checkpoint=self.training) + + x = x.to(dtype) + + x = x.unflatten(0, (-1, h)).transpose(1,2).flatten(start_dim=2) + + out_proj, dropout = self.to_out + x = out_proj(x) + x = dropout(x) + + return x + +def sub_quad_attention(q, k, v, q_chunk_size=1024, kv_chunk_size=None, kv_chunk_size_min=None, chunk_threshold=None, use_checkpoint=True): + bytes_per_token = torch.finfo(q.dtype).bits//8 + batch_x_heads, q_tokens, _ = q.shape + _, k_tokens, _ = k.shape + qk_matmul_size_bytes = batch_x_heads * bytes_per_token * q_tokens * k_tokens + + if chunk_threshold is None: + chunk_threshold_bytes = int(get_available_vram() * 0.9) if q.device.type == 'mps' else int(get_available_vram() * 0.7) + elif chunk_threshold == 0: + chunk_threshold_bytes = None + else: + chunk_threshold_bytes = int(0.01 * chunk_threshold * get_available_vram()) + + if kv_chunk_size_min is None and chunk_threshold_bytes is not None: + kv_chunk_size_min = chunk_threshold_bytes // (batch_x_heads * bytes_per_token * (k.shape[2] + v.shape[2])) + elif kv_chunk_size_min == 0: + kv_chunk_size_min = None + + if chunk_threshold_bytes is not None and qk_matmul_size_bytes <= chunk_threshold_bytes: + # the big matmul fits into our memory limit; do everything in 1 chunk, + # i.e. send it down the unchunked fast-path + query_chunk_size = q_tokens + kv_chunk_size = k_tokens + + with devices.without_autocast(disable=q.dtype == v.dtype): + return efficient_dot_product_attention( + q, + k, + v, + query_chunk_size=q_chunk_size, + kv_chunk_size=kv_chunk_size, + kv_chunk_size_min = kv_chunk_size_min, + use_checkpoint=use_checkpoint, + ) + + +def get_xformers_flash_attention_op(q, k, v): + if not shared.cmd_opts.xformers_flash_attention: + return None + + try: + flash_attention_op = xformers.ops.MemoryEfficientAttentionFlashAttentionOp + fw, bw = flash_attention_op + if fw.supports(xformers.ops.fmha.Inputs(query=q, key=k, value=v, attn_bias=None)): + return flash_attention_op + except Exception as e: + errors.display_once(e, "enabling flash attention") + + return None + + +def xformers_attention_forward(self, x, context=None, mask=None): + h = self.heads + q_in = self.to_q(x) + context = default(context, x) + + context_k, context_v = hypernetwork.apply_hypernetworks(shared.loaded_hypernetworks, context) + k_in = self.to_k(context_k) + v_in = self.to_v(context_v) + + q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> b n h d', h=h), (q_in, k_in, v_in)) + del q_in, k_in, v_in + + dtype = q.dtype + if shared.opts.upcast_attn: + q, k = q.float(), k.float() + + out = xformers.ops.memory_efficient_attention(q, k, v, attn_bias=None, op=get_xformers_flash_attention_op(q, k, v)) + + out = out.to(dtype) + + out = rearrange(out, 'b n h d -> b n (h d)', h=h) + return self.to_out(out) + +def cross_attention_attnblock_forward(self, x): + h_ = x + h_ = self.norm(h_) + q1 = self.q(h_) + k1 = self.k(h_) + v = self.v(h_) + + # compute attention + b, c, h, w = q1.shape + + q2 = q1.reshape(b, c, h*w) + del q1 + + q = q2.permute(0, 2, 1) # b,hw,c + del q2 + + k = k1.reshape(b, c, h*w) # b,c,hw + del k1 + + h_ = torch.zeros_like(k, device=q.device) + + mem_free_total = get_available_vram() + + tensor_size = q.shape[0] * q.shape[1] * k.shape[2] * q.element_size() + mem_required = tensor_size * 2.5 + steps = 1 + + if mem_required > mem_free_total: + steps = 2**(math.ceil(math.log(mem_required / mem_free_total, 2))) + + slice_size = q.shape[1] // steps if (q.shape[1] % steps) == 0 else q.shape[1] + for i in range(0, q.shape[1], slice_size): + end = i + slice_size + + w1 = torch.bmm(q[:, i:end], k) # b,hw,hw w[b,i,j]=sum_c q[b,i,c]k[b,c,j] + w2 = w1 * (int(c)**(-0.5)) + del w1 + w3 = torch.nn.functional.softmax(w2, dim=2, dtype=q.dtype) + del w2 + + # attend to values + v1 = v.reshape(b, c, h*w) + w4 = w3.permute(0, 2, 1) # b,hw,hw (first hw of k, second of q) + del w3 + + h_[:, :, i:end] = torch.bmm(v1, w4) # b, c,hw (hw of q) h_[b,c,j] = sum_i v[b,c,i] w_[b,i,j] + del v1, w4 + + h2 = h_.reshape(b, c, h, w) + del h_ + + h3 = self.proj_out(h2) + del h2 + + h3 += x + + return h3 + +def xformers_attnblock_forward(self, x): + try: + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + b, c, h, w = q.shape + q, k, v = map(lambda t: rearrange(t, 'b c h w -> b (h w) c'), (q, k, v)) + dtype = q.dtype + if shared.opts.upcast_attn: + q, k = q.float(), k.float() + q = q.contiguous() + k = k.contiguous() + v = v.contiguous() + out = xformers.ops.memory_efficient_attention(q, k, v, op=get_xformers_flash_attention_op(q, k, v)) + out = out.to(dtype) + out = rearrange(out, 'b (h w) c -> b c h w', h=h) + out = self.proj_out(out) + return x + out + except NotImplementedError: + return cross_attention_attnblock_forward(self, x) + +def sub_quad_attnblock_forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + b, c, h, w = q.shape + q, k, v = map(lambda t: rearrange(t, 'b c h w -> b (h w) c'), (q, k, v)) + q = q.contiguous() + k = k.contiguous() + v = v.contiguous() + out = sub_quad_attention(q, k, v, q_chunk_size=shared.cmd_opts.sub_quad_q_chunk_size, kv_chunk_size=shared.cmd_opts.sub_quad_kv_chunk_size, chunk_threshold=shared.cmd_opts.sub_quad_chunk_threshold, use_checkpoint=self.training) + out = rearrange(out, 'b (h w) c -> b c h w', h=h) + out = self.proj_out(out) + return x + out diff --git a/sd/stable-diffusion-webui/modules/sd_hijack_unet.py b/sd/stable-diffusion-webui/modules/sd_hijack_unet.py new file mode 100644 index 0000000000000000000000000000000000000000..ac931c80df9b7c34b342d560bc969ff461b0afc7 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_hijack_unet.py @@ -0,0 +1,79 @@ +import torch +from packaging import version + +from modules import devices +from modules.sd_hijack_utils import CondFunc + + +class TorchHijackForUnet: + """ + This is torch, but with cat that resizes tensors to appropriate dimensions if they do not match; + this makes it possible to create pictures with dimensions that are multiples of 8 rather than 64 + """ + + def __getattr__(self, item): + if item == 'cat': + return self.cat + + if hasattr(torch, item): + return getattr(torch, item) + + raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, item)) + + def cat(self, tensors, *args, **kwargs): + if len(tensors) == 2: + a, b = tensors + if a.shape[-2:] != b.shape[-2:]: + a = torch.nn.functional.interpolate(a, b.shape[-2:], mode="nearest") + + tensors = (a, b) + + return torch.cat(tensors, *args, **kwargs) + + +th = TorchHijackForUnet() + + +# Below are monkey patches to enable upcasting a float16 UNet for float32 sampling +def apply_model(orig_func, self, x_noisy, t, cond, **kwargs): + + if isinstance(cond, dict): + for y in cond.keys(): + cond[y] = [x.to(devices.dtype_unet) if isinstance(x, torch.Tensor) else x for x in cond[y]] + + with devices.autocast(): + return orig_func(self, x_noisy.to(devices.dtype_unet), t.to(devices.dtype_unet), cond, **kwargs).float() + + +class GELUHijack(torch.nn.GELU, torch.nn.Module): + def __init__(self, *args, **kwargs): + torch.nn.GELU.__init__(self, *args, **kwargs) + def forward(self, x): + if devices.unet_needs_upcast: + return torch.nn.GELU.forward(self.float(), x.float()).to(devices.dtype_unet) + else: + return torch.nn.GELU.forward(self, x) + + +ddpm_edit_hijack = None +def hijack_ddpm_edit(): + global ddpm_edit_hijack + if not ddpm_edit_hijack: + CondFunc('modules.models.diffusion.ddpm_edit.LatentDiffusion.decode_first_stage', first_stage_sub, first_stage_cond) + CondFunc('modules.models.diffusion.ddpm_edit.LatentDiffusion.encode_first_stage', first_stage_sub, first_stage_cond) + ddpm_edit_hijack = CondFunc('modules.models.diffusion.ddpm_edit.LatentDiffusion.apply_model', apply_model, unet_needs_upcast) + + +unet_needs_upcast = lambda *args, **kwargs: devices.unet_needs_upcast +CondFunc('ldm.models.diffusion.ddpm.LatentDiffusion.apply_model', apply_model, unet_needs_upcast) +CondFunc('ldm.modules.diffusionmodules.openaimodel.timestep_embedding', lambda orig_func, timesteps, *args, **kwargs: orig_func(timesteps, *args, **kwargs).to(torch.float32 if timesteps.dtype == torch.int64 else devices.dtype_unet), unet_needs_upcast) +if version.parse(torch.__version__) <= version.parse("1.13.1"): + CondFunc('ldm.modules.diffusionmodules.util.GroupNorm32.forward', lambda orig_func, self, *args, **kwargs: orig_func(self.float(), *args, **kwargs), unet_needs_upcast) + CondFunc('ldm.modules.attention.GEGLU.forward', lambda orig_func, self, x: orig_func(self.float(), x.float()).to(devices.dtype_unet), unet_needs_upcast) + CondFunc('open_clip.transformer.ResidualAttentionBlock.__init__', lambda orig_func, *args, **kwargs: kwargs.update({'act_layer': GELUHijack}) and False or orig_func(*args, **kwargs), lambda _, *args, **kwargs: kwargs.get('act_layer') is None or kwargs['act_layer'] == torch.nn.GELU) + +first_stage_cond = lambda _, self, *args, **kwargs: devices.unet_needs_upcast and self.model.diffusion_model.dtype == torch.float16 +first_stage_sub = lambda orig_func, self, x, **kwargs: orig_func(self, x.to(devices.dtype_vae), **kwargs) +CondFunc('ldm.models.diffusion.ddpm.LatentDiffusion.decode_first_stage', first_stage_sub, first_stage_cond) +CondFunc('ldm.models.diffusion.ddpm.LatentDiffusion.encode_first_stage', first_stage_sub, first_stage_cond) +CondFunc('ldm.models.diffusion.ddpm.LatentDiffusion.get_first_stage_encoding', lambda orig_func, *args, **kwargs: orig_func(*args, **kwargs).float(), first_stage_cond) diff --git a/sd/stable-diffusion-webui/modules/sd_hijack_utils.py b/sd/stable-diffusion-webui/modules/sd_hijack_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..179ebc78e6a3d16e7a4318b8644fee690b447d12 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_hijack_utils.py @@ -0,0 +1,28 @@ +import importlib + +class CondFunc: + def __new__(cls, orig_func, sub_func, cond_func): + self = super(CondFunc, cls).__new__(cls) + if isinstance(orig_func, str): + func_path = orig_func.split('.') + for i in range(len(func_path)-1, -1, -1): + try: + resolved_obj = importlib.import_module('.'.join(func_path[:i])) + break + except ImportError: + pass + for attr_name in func_path[i:-1]: + resolved_obj = getattr(resolved_obj, attr_name) + orig_func = getattr(resolved_obj, func_path[-1]) + setattr(resolved_obj, func_path[-1], lambda *args, **kwargs: self(*args, **kwargs)) + self.__init__(orig_func, sub_func, cond_func) + return lambda *args, **kwargs: self(*args, **kwargs) + def __init__(self, orig_func, sub_func, cond_func): + self.__orig_func = orig_func + self.__sub_func = sub_func + self.__cond_func = cond_func + def __call__(self, *args, **kwargs): + if not self.__cond_func or self.__cond_func(self.__orig_func, *args, **kwargs): + return self.__sub_func(self.__orig_func, *args, **kwargs) + else: + return self.__orig_func(*args, **kwargs) diff --git a/sd/stable-diffusion-webui/modules/sd_hijack_xlmr.py b/sd/stable-diffusion-webui/modules/sd_hijack_xlmr.py new file mode 100644 index 0000000000000000000000000000000000000000..9e7e1803cbca8be1d8fd9e9e32f413016d02960d --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_hijack_xlmr.py @@ -0,0 +1,34 @@ +import open_clip.tokenizer +import torch + +from modules import sd_hijack_clip, devices +from modules.shared import opts + + +class FrozenXLMREmbedderWithCustomWords(sd_hijack_clip.FrozenCLIPEmbedderWithCustomWords): + def __init__(self, wrapped, hijack): + super().__init__(wrapped, hijack) + + self.id_start = wrapped.config.bos_token_id + self.id_end = wrapped.config.eos_token_id + self.id_pad = wrapped.config.pad_token_id + + self.comma_token = self.tokenizer.get_vocab().get(',', None) # alt diffusion doesn't have bits for comma + + def encode_with_transformers(self, tokens): + # there's no CLIP Skip here because all hidden layers have size of 1024 and the last one uses a + # trained layer to transform those 1024 into 768 for unet; so you can't choose which transformer + # layer to work with - you have to use the last + + attention_mask = (tokens != self.id_pad).to(device=tokens.device, dtype=torch.int64) + features = self.wrapped(input_ids=tokens, attention_mask=attention_mask) + z = features['projection_state'] + + return z + + def encode_embedding_init_text(self, init_text, nvpt): + embedding_layer = self.wrapped.roberta.embeddings + ids = self.wrapped.tokenizer(init_text, max_length=nvpt, return_tensors="pt", add_special_tokens=False)["input_ids"] + embedded = embedding_layer.token_embedding.wrapped(ids.to(devices.device)).squeeze(0) + + return embedded diff --git a/sd/stable-diffusion-webui/modules/sd_models.py b/sd/stable-diffusion-webui/modules/sd_models.py new file mode 100644 index 0000000000000000000000000000000000000000..e25a5495783c2768d50b63b35e105175c1b78bbf --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_models.py @@ -0,0 +1,495 @@ +import collections +import os.path +import sys +import gc +import torch +import re +import safetensors.torch +from omegaconf import OmegaConf +from os import mkdir +from urllib import request +import ldm.modules.midas as midas + +from ldm.util import instantiate_from_config + +from modules import paths, shared, modelloader, devices, script_callbacks, sd_vae, sd_disable_initialization, errors, hashes, sd_models_config +from modules.paths import models_path +from modules.sd_hijack_inpainting import do_inpainting_hijack +from modules.timer import Timer + +model_dir = "Stable-diffusion" +model_path = os.path.abspath(os.path.join(paths.models_path, model_dir)) + +checkpoints_list = {} +checkpoint_alisases = {} +checkpoints_loaded = collections.OrderedDict() + + +class CheckpointInfo: + def __init__(self, filename): + self.filename = filename + abspath = os.path.abspath(filename) + + if shared.cmd_opts.ckpt_dir is not None and abspath.startswith(shared.cmd_opts.ckpt_dir): + name = abspath.replace(shared.cmd_opts.ckpt_dir, '') + elif abspath.startswith(model_path): + name = abspath.replace(model_path, '') + else: + name = os.path.basename(filename) + + if name.startswith("\\") or name.startswith("/"): + name = name[1:] + + self.name = name + self.name_for_extra = os.path.splitext(os.path.basename(filename))[0] + self.model_name = os.path.splitext(name.replace("/", "_").replace("\\", "_"))[0] + self.hash = model_hash(filename) + + self.sha256 = hashes.sha256_from_cache(self.filename, "checkpoint/" + name) + self.shorthash = self.sha256[0:10] if self.sha256 else None + + self.title = name if self.shorthash is None else f'{name} [{self.shorthash}]' + + self.ids = [self.hash, self.model_name, self.title, name, f'{name} [{self.hash}]'] + ([self.shorthash, self.sha256, f'{self.name} [{self.shorthash}]'] if self.shorthash else []) + + def register(self): + checkpoints_list[self.title] = self + for id in self.ids: + checkpoint_alisases[id] = self + + def calculate_shorthash(self): + self.sha256 = hashes.sha256(self.filename, "checkpoint/" + self.name) + if self.sha256 is None: + return + + self.shorthash = self.sha256[0:10] + + if self.shorthash not in self.ids: + self.ids += [self.shorthash, self.sha256, f'{self.name} [{self.shorthash}]'] + + checkpoints_list.pop(self.title) + self.title = f'{self.name} [{self.shorthash}]' + self.register() + + return self.shorthash + + +try: + # this silences the annoying "Some weights of the model checkpoint were not used when initializing..." message at start. + + from transformers import logging, CLIPModel + + logging.set_verbosity_error() +except Exception: + pass + + +def setup_model(): + if not os.path.exists(model_path): + os.makedirs(model_path) + + list_models() + enable_midas_autodownload() + + +def checkpoint_tiles(): + def convert(name): + return int(name) if name.isdigit() else name.lower() + + def alphanumeric_key(key): + return [convert(c) for c in re.split('([0-9]+)', key)] + + return sorted([x.title for x in checkpoints_list.values()], key=alphanumeric_key) + + +def list_models(): + checkpoints_list.clear() + checkpoint_alisases.clear() + + cmd_ckpt = shared.cmd_opts.ckpt + if shared.cmd_opts.no_download_sd_model or cmd_ckpt != shared.sd_model_file or os.path.exists(cmd_ckpt): + model_url = None + else: + model_url = "https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors" + + model_list = modelloader.load_models(model_path=model_path, model_url=model_url, command_path=shared.cmd_opts.ckpt_dir, ext_filter=[".ckpt", ".safetensors"], download_name="v1-5-pruned-emaonly.safetensors", ext_blacklist=[".vae.ckpt", ".vae.safetensors"]) + + if os.path.exists(cmd_ckpt): + checkpoint_info = CheckpointInfo(cmd_ckpt) + checkpoint_info.register() + + shared.opts.data['sd_model_checkpoint'] = checkpoint_info.title + elif cmd_ckpt is not None and cmd_ckpt != shared.default_sd_model_file: + print(f"Checkpoint in --ckpt argument not found (Possible it was moved to {model_path}: {cmd_ckpt}", file=sys.stderr) + + for filename in model_list: + checkpoint_info = CheckpointInfo(filename) + checkpoint_info.register() + + +def get_closet_checkpoint_match(search_string): + checkpoint_info = checkpoint_alisases.get(search_string, None) + if checkpoint_info is not None: + return checkpoint_info + + found = sorted([info for info in checkpoints_list.values() if search_string in info.title], key=lambda x: len(x.title)) + if found: + return found[0] + + return None + + +def model_hash(filename): + """old hash that only looks at a small part of the file and is prone to collisions""" + + try: + with open(filename, "rb") as file: + import hashlib + m = hashlib.sha256() + + file.seek(0x100000) + m.update(file.read(0x10000)) + return m.hexdigest()[0:8] + except FileNotFoundError: + return 'NOFILE' + + +def select_checkpoint(): + model_checkpoint = shared.opts.sd_model_checkpoint + + checkpoint_info = checkpoint_alisases.get(model_checkpoint, None) + if checkpoint_info is not None: + return checkpoint_info + + if len(checkpoints_list) == 0: + print("No checkpoints found. When searching for checkpoints, looked at:", file=sys.stderr) + if shared.cmd_opts.ckpt is not None: + print(f" - file {os.path.abspath(shared.cmd_opts.ckpt)}", file=sys.stderr) + print(f" - directory {model_path}", file=sys.stderr) + if shared.cmd_opts.ckpt_dir is not None: + print(f" - directory {os.path.abspath(shared.cmd_opts.ckpt_dir)}", file=sys.stderr) + print("Can't run without a checkpoint. Find and place a .ckpt or .safetensors file into any of those locations. The program will exit.", file=sys.stderr) + exit(1) + + checkpoint_info = next(iter(checkpoints_list.values())) + if model_checkpoint is not None: + print(f"Checkpoint {model_checkpoint} not found; loading fallback {checkpoint_info.title}", file=sys.stderr) + + return checkpoint_info + + +chckpoint_dict_replacements = { + 'cond_stage_model.transformer.embeddings.': 'cond_stage_model.transformer.text_model.embeddings.', + 'cond_stage_model.transformer.encoder.': 'cond_stage_model.transformer.text_model.encoder.', + 'cond_stage_model.transformer.final_layer_norm.': 'cond_stage_model.transformer.text_model.final_layer_norm.', +} + + +def transform_checkpoint_dict_key(k): + for text, replacement in chckpoint_dict_replacements.items(): + if k.startswith(text): + k = replacement + k[len(text):] + + return k + + +def get_state_dict_from_checkpoint(pl_sd): + pl_sd = pl_sd.pop("state_dict", pl_sd) + pl_sd.pop("state_dict", None) + + sd = {} + for k, v in pl_sd.items(): + new_key = transform_checkpoint_dict_key(k) + + if new_key is not None: + sd[new_key] = v + + pl_sd.clear() + pl_sd.update(sd) + + return pl_sd + + +def read_state_dict(checkpoint_file, print_global_state=False, map_location=None): + _, extension = os.path.splitext(checkpoint_file) + if extension.lower() == ".safetensors": + device = map_location or shared.weight_load_location or devices.get_optimal_device_name() + pl_sd = safetensors.torch.load_file(checkpoint_file, device=device) + else: + pl_sd = torch.load(checkpoint_file, map_location=map_location or shared.weight_load_location) + + if print_global_state and "global_step" in pl_sd: + print(f"Global Step: {pl_sd['global_step']}") + + sd = get_state_dict_from_checkpoint(pl_sd) + return sd + + +def get_checkpoint_state_dict(checkpoint_info: CheckpointInfo, timer): + sd_model_hash = checkpoint_info.calculate_shorthash() + timer.record("calculate hash") + + if checkpoint_info in checkpoints_loaded: + # use checkpoint cache + print(f"Loading weights [{sd_model_hash}] from cache") + return checkpoints_loaded[checkpoint_info] + + print(f"Loading weights [{sd_model_hash}] from {checkpoint_info.filename}") + res = read_state_dict(checkpoint_info.filename) + timer.record("load weights from disk") + + return res + + +def load_model_weights(model, checkpoint_info: CheckpointInfo, state_dict, timer): + sd_model_hash = checkpoint_info.calculate_shorthash() + timer.record("calculate hash") + + shared.opts.data["sd_model_checkpoint"] = checkpoint_info.title + + if state_dict is None: + state_dict = get_checkpoint_state_dict(checkpoint_info, timer) + + model.load_state_dict(state_dict, strict=False) + del state_dict + timer.record("apply weights to model") + + if shared.opts.sd_checkpoint_cache > 0: + # cache newly loaded model + checkpoints_loaded[checkpoint_info] = model.state_dict().copy() + + if shared.cmd_opts.opt_channelslast: + model.to(memory_format=torch.channels_last) + timer.record("apply channels_last") + + if not shared.cmd_opts.no_half: + vae = model.first_stage_model + depth_model = getattr(model, 'depth_model', None) + + # with --no-half-vae, remove VAE from model when doing half() to prevent its weights from being converted to float16 + if shared.cmd_opts.no_half_vae: + model.first_stage_model = None + # with --upcast-sampling, don't convert the depth model weights to float16 + if shared.cmd_opts.upcast_sampling and depth_model: + model.depth_model = None + + model.half() + model.first_stage_model = vae + if depth_model: + model.depth_model = depth_model + + timer.record("apply half()") + + devices.dtype = torch.float32 if shared.cmd_opts.no_half else torch.float16 + devices.dtype_vae = torch.float32 if shared.cmd_opts.no_half or shared.cmd_opts.no_half_vae else torch.float16 + devices.dtype_unet = model.model.diffusion_model.dtype + devices.unet_needs_upcast = shared.cmd_opts.upcast_sampling and devices.dtype == torch.float16 and devices.dtype_unet == torch.float16 + + model.first_stage_model.to(devices.dtype_vae) + timer.record("apply dtype to VAE") + + # clean up cache if limit is reached + while len(checkpoints_loaded) > shared.opts.sd_checkpoint_cache: + checkpoints_loaded.popitem(last=False) + + model.sd_model_hash = sd_model_hash + model.sd_model_checkpoint = checkpoint_info.filename + model.sd_checkpoint_info = checkpoint_info + shared.opts.data["sd_checkpoint_hash"] = checkpoint_info.sha256 + + model.logvar = model.logvar.to(devices.device) # fix for training + + sd_vae.delete_base_vae() + sd_vae.clear_loaded_vae() + vae_file, vae_source = sd_vae.resolve_vae(checkpoint_info.filename) + sd_vae.load_vae(model, vae_file, vae_source) + timer.record("load VAE") + + +def enable_midas_autodownload(): + """ + Gives the ldm.modules.midas.api.load_model function automatic downloading. + + When the 512-depth-ema model, and other future models like it, is loaded, + it calls midas.api.load_model to load the associated midas depth model. + This function applies a wrapper to download the model to the correct + location automatically. + """ + + midas_path = os.path.join(paths.models_path, 'midas') + + # stable-diffusion-stability-ai hard-codes the midas model path to + # a location that differs from where other scripts using this model look. + # HACK: Overriding the path here. + for k, v in midas.api.ISL_PATHS.items(): + file_name = os.path.basename(v) + midas.api.ISL_PATHS[k] = os.path.join(midas_path, file_name) + + midas_urls = { + "dpt_large": "https://github.com/intel-isl/DPT/releases/download/1_0/dpt_large-midas-2f21e586.pt", + "dpt_hybrid": "https://github.com/intel-isl/DPT/releases/download/1_0/dpt_hybrid-midas-501f0c75.pt", + "midas_v21": "https://github.com/AlexeyAB/MiDaS/releases/download/midas_dpt/midas_v21-f6b98070.pt", + "midas_v21_small": "https://github.com/AlexeyAB/MiDaS/releases/download/midas_dpt/midas_v21_small-70d6b9c8.pt", + } + + midas.api.load_model_inner = midas.api.load_model + + def load_model_wrapper(model_type): + path = midas.api.ISL_PATHS[model_type] + if not os.path.exists(path): + if not os.path.exists(midas_path): + mkdir(midas_path) + + print(f"Downloading midas model weights for {model_type} to {path}") + request.urlretrieve(midas_urls[model_type], path) + print(f"{model_type} downloaded") + + return midas.api.load_model_inner(model_type) + + midas.api.load_model = load_model_wrapper + + +def repair_config(sd_config): + + if not hasattr(sd_config.model.params, "use_ema"): + sd_config.model.params.use_ema = False + + if shared.cmd_opts.no_half: + sd_config.model.params.unet_config.params.use_fp16 = False + elif shared.cmd_opts.upcast_sampling: + sd_config.model.params.unet_config.params.use_fp16 = True + + +sd1_clip_weight = 'cond_stage_model.transformer.text_model.embeddings.token_embedding.weight' +sd2_clip_weight = 'cond_stage_model.model.transformer.resblocks.0.attn.in_proj_weight' + +def load_model(checkpoint_info=None, already_loaded_state_dict=None, time_taken_to_load_state_dict=None): + from modules import lowvram, sd_hijack + checkpoint_info = checkpoint_info or select_checkpoint() + + if shared.sd_model: + sd_hijack.model_hijack.undo_hijack(shared.sd_model) + shared.sd_model = None + gc.collect() + devices.torch_gc() + + do_inpainting_hijack() + + timer = Timer() + + if already_loaded_state_dict is not None: + state_dict = already_loaded_state_dict + else: + state_dict = get_checkpoint_state_dict(checkpoint_info, timer) + + checkpoint_config = sd_models_config.find_checkpoint_config(state_dict, checkpoint_info) + clip_is_included_into_sd = sd1_clip_weight in state_dict or sd2_clip_weight in state_dict + + timer.record("find config") + + sd_config = OmegaConf.load(checkpoint_config) + repair_config(sd_config) + + timer.record("load config") + + print(f"Creating model from config: {checkpoint_config}") + + sd_model = None + try: + with sd_disable_initialization.DisableInitialization(disable_clip=clip_is_included_into_sd): + sd_model = instantiate_from_config(sd_config.model) + except Exception as e: + pass + + if sd_model is None: + print('Failed to create model quickly; will retry using slow method.', file=sys.stderr) + sd_model = instantiate_from_config(sd_config.model) + + sd_model.used_config = checkpoint_config + + timer.record("create model") + + load_model_weights(sd_model, checkpoint_info, state_dict, timer) + + if shared.cmd_opts.lowvram or shared.cmd_opts.medvram: + lowvram.setup_for_low_vram(sd_model, shared.cmd_opts.medvram) + else: + sd_model.to(shared.device) + + timer.record("move model to device") + + sd_hijack.model_hijack.hijack(sd_model) + + timer.record("hijack") + + sd_model.eval() + shared.sd_model = sd_model + + sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings(force_reload=True) # Reload embeddings after model load as they may or may not fit the model + + timer.record("load textual inversion embeddings") + + script_callbacks.model_loaded_callback(sd_model) + + timer.record("scripts callbacks") + + print(f"Model loaded in {timer.summary()}.") + + return sd_model + + +def reload_model_weights(sd_model=None, info=None): + from modules import lowvram, devices, sd_hijack + checkpoint_info = info or select_checkpoint() + + if not sd_model: + sd_model = shared.sd_model + + if sd_model is None: # previous model load failed + current_checkpoint_info = None + else: + current_checkpoint_info = sd_model.sd_checkpoint_info + if sd_model.sd_model_checkpoint == checkpoint_info.filename: + return + + if shared.cmd_opts.lowvram or shared.cmd_opts.medvram: + lowvram.send_everything_to_cpu() + else: + sd_model.to(devices.cpu) + + sd_hijack.model_hijack.undo_hijack(sd_model) + + timer = Timer() + + state_dict = get_checkpoint_state_dict(checkpoint_info, timer) + + checkpoint_config = sd_models_config.find_checkpoint_config(state_dict, checkpoint_info) + + timer.record("find config") + + if sd_model is None or checkpoint_config != sd_model.used_config: + del sd_model + checkpoints_loaded.clear() + load_model(checkpoint_info, already_loaded_state_dict=state_dict, time_taken_to_load_state_dict=timer.records["load weights from disk"]) + return shared.sd_model + + try: + load_model_weights(sd_model, checkpoint_info, state_dict, timer) + except Exception as e: + print("Failed to load checkpoint, restoring previous") + load_model_weights(sd_model, current_checkpoint_info, None, timer) + raise + finally: + sd_hijack.model_hijack.hijack(sd_model) + timer.record("hijack") + + script_callbacks.model_loaded_callback(sd_model) + timer.record("script callbacks") + + if not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram: + sd_model.to(devices.device) + timer.record("move model to device") + + print(f"Weights loaded in {timer.summary()}.") + + return sd_model diff --git a/sd/stable-diffusion-webui/modules/sd_models_config.py b/sd/stable-diffusion-webui/modules/sd_models_config.py new file mode 100644 index 0000000000000000000000000000000000000000..222793d451b3659f7954c208260af71840b475a2 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_models_config.py @@ -0,0 +1,112 @@ +import re +import os + +import torch + +from modules import shared, paths, sd_disable_initialization + +sd_configs_path = shared.sd_configs_path +sd_repo_configs_path = os.path.join(paths.paths['Stable Diffusion'], "configs", "stable-diffusion") + + +config_default = shared.sd_default_config +config_sd2 = os.path.join(sd_repo_configs_path, "v2-inference.yaml") +config_sd2v = os.path.join(sd_repo_configs_path, "v2-inference-v.yaml") +config_sd2_inpainting = os.path.join(sd_repo_configs_path, "v2-inpainting-inference.yaml") +config_depth_model = os.path.join(sd_repo_configs_path, "v2-midas-inference.yaml") +config_inpainting = os.path.join(sd_configs_path, "v1-inpainting-inference.yaml") +config_instruct_pix2pix = os.path.join(sd_configs_path, "instruct-pix2pix.yaml") +config_alt_diffusion = os.path.join(sd_configs_path, "alt-diffusion-inference.yaml") + + +def is_using_v_parameterization_for_sd2(state_dict): + """ + Detects whether unet in state_dict is using v-parameterization. Returns True if it is. You're welcome. + """ + + import ldm.modules.diffusionmodules.openaimodel + from modules import devices + + device = devices.cpu + + with sd_disable_initialization.DisableInitialization(): + unet = ldm.modules.diffusionmodules.openaimodel.UNetModel( + use_checkpoint=True, + use_fp16=False, + image_size=32, + in_channels=4, + out_channels=4, + model_channels=320, + attention_resolutions=[4, 2, 1], + num_res_blocks=2, + channel_mult=[1, 2, 4, 4], + num_head_channels=64, + use_spatial_transformer=True, + use_linear_in_transformer=True, + transformer_depth=1, + context_dim=1024, + legacy=False + ) + unet.eval() + + with torch.no_grad(): + unet_sd = {k.replace("model.diffusion_model.", ""): v for k, v in state_dict.items() if "model.diffusion_model." in k} + unet.load_state_dict(unet_sd, strict=True) + unet.to(device=device, dtype=torch.float) + + test_cond = torch.ones((1, 2, 1024), device=device) * 0.5 + x_test = torch.ones((1, 4, 8, 8), device=device) * 0.5 + + out = (unet(x_test, torch.asarray([999], device=device), context=test_cond) - x_test).mean().item() + + return out < -1 + + +def guess_model_config_from_state_dict(sd, filename): + sd2_cond_proj_weight = sd.get('cond_stage_model.model.transformer.resblocks.0.attn.in_proj_weight', None) + diffusion_model_input = sd.get('model.diffusion_model.input_blocks.0.0.weight', None) + + if sd.get('depth_model.model.pretrained.act_postprocess3.0.project.0.bias', None) is not None: + return config_depth_model + + if sd2_cond_proj_weight is not None and sd2_cond_proj_weight.shape[1] == 1024: + if diffusion_model_input.shape[1] == 9: + return config_sd2_inpainting + elif is_using_v_parameterization_for_sd2(sd): + return config_sd2v + else: + return config_sd2 + + if diffusion_model_input is not None: + if diffusion_model_input.shape[1] == 9: + return config_inpainting + if diffusion_model_input.shape[1] == 8: + return config_instruct_pix2pix + + if sd.get('cond_stage_model.roberta.embeddings.word_embeddings.weight', None) is not None: + return config_alt_diffusion + + return config_default + + +def find_checkpoint_config(state_dict, info): + if info is None: + return guess_model_config_from_state_dict(state_dict, "") + + config = find_checkpoint_config_near_filename(info) + if config is not None: + return config + + return guess_model_config_from_state_dict(state_dict, info.filename) + + +def find_checkpoint_config_near_filename(info): + if info is None: + return None + + config = os.path.splitext(info.filename)[0] + ".yaml" + if os.path.exists(config): + return config + + return None + diff --git a/sd/stable-diffusion-webui/modules/sd_samplers.py b/sd/stable-diffusion-webui/modules/sd_samplers.py new file mode 100644 index 0000000000000000000000000000000000000000..981702b85cbf5734bc42a3cbfeeedfc2db57b647 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_samplers.py @@ -0,0 +1,47 @@ +from modules import sd_samplers_compvis, sd_samplers_kdiffusion, shared + +# imports for functions that previously were here and are used by other modules +from modules.sd_samplers_common import samples_to_image_grid, sample_to_image + +all_samplers = [ + *sd_samplers_kdiffusion.samplers_data_k_diffusion, + *sd_samplers_compvis.samplers_data_compvis, +] +all_samplers_map = {x.name: x for x in all_samplers} + +samplers = [] +samplers_for_img2img = [] +samplers_map = {} + + +def create_sampler(name, model): + if name is not None: + config = all_samplers_map.get(name, None) + else: + config = all_samplers[0] + + assert config is not None, f'bad sampler name: {name}' + + sampler = config.constructor(model) + sampler.config = config + + return sampler + + +def set_samplers(): + global samplers, samplers_for_img2img + + hidden = set(shared.opts.hide_samplers) + hidden_img2img = set(shared.opts.hide_samplers + ['PLMS']) + + samplers = [x for x in all_samplers if x.name not in hidden] + samplers_for_img2img = [x for x in all_samplers if x.name not in hidden_img2img] + + samplers_map.clear() + for sampler in all_samplers: + samplers_map[sampler.name.lower()] = sampler.name + for alias in sampler.aliases: + samplers_map[alias.lower()] = sampler.name + + +set_samplers() diff --git a/sd/stable-diffusion-webui/modules/sd_samplers_common.py b/sd/stable-diffusion-webui/modules/sd_samplers_common.py new file mode 100644 index 0000000000000000000000000000000000000000..c0709f6260ba776c82232abbd7142608840cac88 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_samplers_common.py @@ -0,0 +1,62 @@ +from collections import namedtuple +import numpy as np +import torch +from PIL import Image +from modules import devices, processing, images, sd_vae_approx + +from modules.shared import opts, state +import modules.shared as shared + +SamplerData = namedtuple('SamplerData', ['name', 'constructor', 'aliases', 'options']) + + +def setup_img2img_steps(p, steps=None): + if opts.img2img_fix_steps or steps is not None: + requested_steps = (steps or p.steps) + steps = int(requested_steps / min(p.denoising_strength, 0.999)) if p.denoising_strength > 0 else 0 + t_enc = requested_steps - 1 + else: + steps = p.steps + t_enc = int(min(p.denoising_strength, 0.999) * steps) + + return steps, t_enc + + +approximation_indexes = {"Full": 0, "Approx NN": 1, "Approx cheap": 2} + + +def single_sample_to_image(sample, approximation=None): + if approximation is None: + approximation = approximation_indexes.get(opts.show_progress_type, 0) + + if approximation == 2: + x_sample = sd_vae_approx.cheap_approximation(sample) + elif approximation == 1: + x_sample = sd_vae_approx.model()(sample.to(devices.device, devices.dtype).unsqueeze(0))[0].detach() + else: + x_sample = processing.decode_first_stage(shared.sd_model, sample.unsqueeze(0))[0] + + x_sample = torch.clamp((x_sample + 1.0) / 2.0, min=0.0, max=1.0) + x_sample = 255. * np.moveaxis(x_sample.cpu().numpy(), 0, 2) + x_sample = x_sample.astype(np.uint8) + return Image.fromarray(x_sample) + + +def sample_to_image(samples, index=0, approximation=None): + return single_sample_to_image(samples[index], approximation) + + +def samples_to_image_grid(samples, approximation=None): + return images.image_grid([single_sample_to_image(sample, approximation) for sample in samples]) + + +def store_latent(decoded): + state.current_latent = decoded + + if opts.live_previews_enable and opts.show_progress_every_n_steps > 0 and shared.state.sampling_step % opts.show_progress_every_n_steps == 0: + if not shared.parallel_processing_allowed: + shared.state.assign_current_image(sample_to_image(decoded)) + + +class InterruptedException(BaseException): + pass diff --git a/sd/stable-diffusion-webui/modules/sd_samplers_compvis.py b/sd/stable-diffusion-webui/modules/sd_samplers_compvis.py new file mode 100644 index 0000000000000000000000000000000000000000..46a7054ebdedc5a3281d6d5928c3e4d6746fbc27 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_samplers_compvis.py @@ -0,0 +1,160 @@ +import math +import ldm.models.diffusion.ddim +import ldm.models.diffusion.plms + +import numpy as np +import torch + +from modules.shared import state +from modules import sd_samplers_common, prompt_parser, shared + + +samplers_data_compvis = [ + sd_samplers_common.SamplerData('DDIM', lambda model: VanillaStableDiffusionSampler(ldm.models.diffusion.ddim.DDIMSampler, model), [], {}), + sd_samplers_common.SamplerData('PLMS', lambda model: VanillaStableDiffusionSampler(ldm.models.diffusion.plms.PLMSSampler, model), [], {}), +] + + +class VanillaStableDiffusionSampler: + def __init__(self, constructor, sd_model): + self.sampler = constructor(sd_model) + self.is_plms = hasattr(self.sampler, 'p_sample_plms') + self.orig_p_sample_ddim = self.sampler.p_sample_plms if self.is_plms else self.sampler.p_sample_ddim + self.mask = None + self.nmask = None + self.init_latent = None + self.sampler_noises = None + self.step = 0 + self.stop_at = None + self.eta = None + self.config = None + self.last_latent = None + + self.conditioning_key = sd_model.model.conditioning_key + + def number_of_needed_noises(self, p): + return 0 + + def launch_sampling(self, steps, func): + state.sampling_steps = steps + state.sampling_step = 0 + + try: + return func() + except sd_samplers_common.InterruptedException: + return self.last_latent + + def p_sample_ddim_hook(self, x_dec, cond, ts, unconditional_conditioning, *args, **kwargs): + if state.interrupted or state.skipped: + raise sd_samplers_common.InterruptedException + + if self.stop_at is not None and self.step > self.stop_at: + raise sd_samplers_common.InterruptedException + + # Have to unwrap the inpainting conditioning here to perform pre-processing + image_conditioning = None + if isinstance(cond, dict): + image_conditioning = cond["c_concat"][0] + cond = cond["c_crossattn"][0] + unconditional_conditioning = unconditional_conditioning["c_crossattn"][0] + + conds_list, tensor = prompt_parser.reconstruct_multicond_batch(cond, self.step) + unconditional_conditioning = prompt_parser.reconstruct_cond_batch(unconditional_conditioning, self.step) + + assert all([len(conds) == 1 for conds in conds_list]), 'composition via AND is not supported for DDIM/PLMS samplers' + cond = tensor + + # for DDIM, shapes must match, we can't just process cond and uncond independently; + # filling unconditional_conditioning with repeats of the last vector to match length is + # not 100% correct but should work well enough + if unconditional_conditioning.shape[1] < cond.shape[1]: + last_vector = unconditional_conditioning[:, -1:] + last_vector_repeated = last_vector.repeat([1, cond.shape[1] - unconditional_conditioning.shape[1], 1]) + unconditional_conditioning = torch.hstack([unconditional_conditioning, last_vector_repeated]) + elif unconditional_conditioning.shape[1] > cond.shape[1]: + unconditional_conditioning = unconditional_conditioning[:, :cond.shape[1]] + + if self.mask is not None: + img_orig = self.sampler.model.q_sample(self.init_latent, ts) + x_dec = img_orig * self.mask + self.nmask * x_dec + + # Wrap the image conditioning back up since the DDIM code can accept the dict directly. + # Note that they need to be lists because it just concatenates them later. + if image_conditioning is not None: + cond = {"c_concat": [image_conditioning], "c_crossattn": [cond]} + unconditional_conditioning = {"c_concat": [image_conditioning], "c_crossattn": [unconditional_conditioning]} + + res = self.orig_p_sample_ddim(x_dec, cond, ts, unconditional_conditioning=unconditional_conditioning, *args, **kwargs) + + if self.mask is not None: + self.last_latent = self.init_latent * self.mask + self.nmask * res[1] + else: + self.last_latent = res[1] + + sd_samplers_common.store_latent(self.last_latent) + + self.step += 1 + state.sampling_step = self.step + shared.total_tqdm.update() + + return res + + def initialize(self, p): + self.eta = p.eta if p.eta is not None else shared.opts.eta_ddim + if self.eta != 0.0: + p.extra_generation_params["Eta DDIM"] = self.eta + + for fieldname in ['p_sample_ddim', 'p_sample_plms']: + if hasattr(self.sampler, fieldname): + setattr(self.sampler, fieldname, self.p_sample_ddim_hook) + + self.mask = p.mask if hasattr(p, 'mask') else None + self.nmask = p.nmask if hasattr(p, 'nmask') else None + + def adjust_steps_if_invalid(self, p, num_steps): + if (self.config.name == 'DDIM' and p.ddim_discretize == 'uniform') or (self.config.name == 'PLMS'): + valid_step = 999 / (1000 // num_steps) + if valid_step == math.floor(valid_step): + return int(valid_step) + 1 + + return num_steps + + def sample_img2img(self, p, x, noise, conditioning, unconditional_conditioning, steps=None, image_conditioning=None): + steps, t_enc = sd_samplers_common.setup_img2img_steps(p, steps) + steps = self.adjust_steps_if_invalid(p, steps) + self.initialize(p) + + self.sampler.make_schedule(ddim_num_steps=steps, ddim_eta=self.eta, ddim_discretize=p.ddim_discretize, verbose=False) + x1 = self.sampler.stochastic_encode(x, torch.tensor([t_enc] * int(x.shape[0])).to(shared.device), noise=noise) + + self.init_latent = x + self.last_latent = x + self.step = 0 + + # Wrap the conditioning models with additional image conditioning for inpainting model + if image_conditioning is not None: + conditioning = {"c_concat": [image_conditioning], "c_crossattn": [conditioning]} + unconditional_conditioning = {"c_concat": [image_conditioning], "c_crossattn": [unconditional_conditioning]} + + samples = self.launch_sampling(t_enc + 1, lambda: self.sampler.decode(x1, conditioning, t_enc, unconditional_guidance_scale=p.cfg_scale, unconditional_conditioning=unconditional_conditioning)) + + return samples + + def sample(self, p, x, conditioning, unconditional_conditioning, steps=None, image_conditioning=None): + self.initialize(p) + + self.init_latent = None + self.last_latent = x + self.step = 0 + + steps = self.adjust_steps_if_invalid(p, steps or p.steps) + + # Wrap the conditioning models with additional image conditioning for inpainting model + # dummy_for_plms is needed because PLMS code checks the first item in the dict to have the right shape + if image_conditioning is not None: + conditioning = {"dummy_for_plms": np.zeros((conditioning.shape[0],)), "c_crossattn": [conditioning], "c_concat": [image_conditioning]} + unconditional_conditioning = {"c_crossattn": [unconditional_conditioning], "c_concat": [image_conditioning]} + + samples_ddim = self.launch_sampling(steps, lambda: self.sampler.sample(S=steps, conditioning=conditioning, batch_size=int(x.shape[0]), shape=x[0].shape, verbose=False, unconditional_guidance_scale=p.cfg_scale, unconditional_conditioning=unconditional_conditioning, x_T=x, eta=self.eta)[0]) + + return samples_ddim diff --git a/sd/stable-diffusion-webui/modules/sd_samplers_kdiffusion.py b/sd/stable-diffusion-webui/modules/sd_samplers_kdiffusion.py new file mode 100644 index 0000000000000000000000000000000000000000..9d16fc11b8fc0678c36dadc9cca0de7122f47cee --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_samplers_kdiffusion.py @@ -0,0 +1,357 @@ +from collections import deque +import torch +import inspect +import einops +import k_diffusion.sampling +from modules import prompt_parser, devices, sd_samplers_common + +from modules.shared import opts, state +import modules.shared as shared +from modules.script_callbacks import CFGDenoiserParams, cfg_denoiser_callback +from modules.script_callbacks import CFGDenoisedParams, cfg_denoised_callback + +samplers_k_diffusion = [ + ('Euler a', 'sample_euler_ancestral', ['k_euler_a', 'k_euler_ancestral'], {}), + ('Euler', 'sample_euler', ['k_euler'], {}), + ('LMS', 'sample_lms', ['k_lms'], {}), + ('Heun', 'sample_heun', ['k_heun'], {}), + ('DPM2', 'sample_dpm_2', ['k_dpm_2'], {'discard_next_to_last_sigma': True}), + ('DPM2 a', 'sample_dpm_2_ancestral', ['k_dpm_2_a'], {'discard_next_to_last_sigma': True}), + ('DPM++ 2S a', 'sample_dpmpp_2s_ancestral', ['k_dpmpp_2s_a'], {}), + ('DPM++ 2M', 'sample_dpmpp_2m', ['k_dpmpp_2m'], {}), + ('DPM++ SDE', 'sample_dpmpp_sde', ['k_dpmpp_sde'], {}), + ('DPM fast', 'sample_dpm_fast', ['k_dpm_fast'], {}), + ('DPM adaptive', 'sample_dpm_adaptive', ['k_dpm_ad'], {}), + ('LMS Karras', 'sample_lms', ['k_lms_ka'], {'scheduler': 'karras'}), + ('DPM2 Karras', 'sample_dpm_2', ['k_dpm_2_ka'], {'scheduler': 'karras', 'discard_next_to_last_sigma': True}), + ('DPM2 a Karras', 'sample_dpm_2_ancestral', ['k_dpm_2_a_ka'], {'scheduler': 'karras', 'discard_next_to_last_sigma': True}), + ('DPM++ 2S a Karras', 'sample_dpmpp_2s_ancestral', ['k_dpmpp_2s_a_ka'], {'scheduler': 'karras'}), + ('DPM++ 2M Karras', 'sample_dpmpp_2m', ['k_dpmpp_2m_ka'], {'scheduler': 'karras'}), + ('DPM++ SDE Karras', 'sample_dpmpp_sde', ['k_dpmpp_sde_ka'], {'scheduler': 'karras'}), +] + +samplers_data_k_diffusion = [ + sd_samplers_common.SamplerData(label, lambda model, funcname=funcname: KDiffusionSampler(funcname, model), aliases, options) + for label, funcname, aliases, options in samplers_k_diffusion + if hasattr(k_diffusion.sampling, funcname) +] + +sampler_extra_params = { + 'sample_euler': ['s_churn', 's_tmin', 's_tmax', 's_noise'], + 'sample_heun': ['s_churn', 's_tmin', 's_tmax', 's_noise'], + 'sample_dpm_2': ['s_churn', 's_tmin', 's_tmax', 's_noise'], +} + + +class CFGDenoiser(torch.nn.Module): + """ + Classifier free guidance denoiser. A wrapper for stable diffusion model (specifically for unet) + that can take a noisy picture and produce a noise-free picture using two guidances (prompts) + instead of one. Originally, the second prompt is just an empty string, but we use non-empty + negative prompt. + """ + + def __init__(self, model): + super().__init__() + self.inner_model = model + self.mask = None + self.nmask = None + self.init_latent = None + self.step = 0 + self.image_cfg_scale = None + + def combine_denoised(self, x_out, conds_list, uncond, cond_scale): + denoised_uncond = x_out[-uncond.shape[0]:] + denoised = torch.clone(denoised_uncond) + + for i, conds in enumerate(conds_list): + for cond_index, weight in conds: + denoised[i] += (x_out[cond_index] - denoised_uncond[i]) * (weight * cond_scale) + + return denoised + + def combine_denoised_for_edit_model(self, x_out, cond_scale): + out_cond, out_img_cond, out_uncond = x_out.chunk(3) + denoised = out_uncond + cond_scale * (out_cond - out_img_cond) + self.image_cfg_scale * (out_img_cond - out_uncond) + + return denoised + + def forward(self, x, sigma, uncond, cond, cond_scale, image_cond): + if state.interrupted or state.skipped: + raise sd_samplers_common.InterruptedException + + # at self.image_cfg_scale == 1.0 produced results for edit model are the same as with normal sampling, + # so is_edit_model is set to False to support AND composition. + is_edit_model = shared.sd_model.cond_stage_key == "edit" and self.image_cfg_scale is not None and self.image_cfg_scale != 1.0 + + conds_list, tensor = prompt_parser.reconstruct_multicond_batch(cond, self.step) + uncond = prompt_parser.reconstruct_cond_batch(uncond, self.step) + + assert not is_edit_model or all([len(conds) == 1 for conds in conds_list]), "AND is not supported for InstructPix2Pix checkpoint (unless using Image CFG scale = 1.0)" + + batch_size = len(conds_list) + repeats = [len(conds_list[i]) for i in range(batch_size)] + + if not is_edit_model: + x_in = torch.cat([torch.stack([x[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [x]) + sigma_in = torch.cat([torch.stack([sigma[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [sigma]) + image_cond_in = torch.cat([torch.stack([image_cond[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [image_cond]) + else: + x_in = torch.cat([torch.stack([x[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [x] + [x]) + sigma_in = torch.cat([torch.stack([sigma[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [sigma] + [sigma]) + image_cond_in = torch.cat([torch.stack([image_cond[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [image_cond] + [torch.zeros_like(self.init_latent)]) + + denoiser_params = CFGDenoiserParams(x_in, image_cond_in, sigma_in, state.sampling_step, state.sampling_steps) + cfg_denoiser_callback(denoiser_params) + x_in = denoiser_params.x + image_cond_in = denoiser_params.image_cond + sigma_in = denoiser_params.sigma + + if tensor.shape[1] == uncond.shape[1]: + if not is_edit_model: + cond_in = torch.cat([tensor, uncond]) + else: + cond_in = torch.cat([tensor, uncond, uncond]) + + if shared.batch_cond_uncond: + x_out = self.inner_model(x_in, sigma_in, cond={"c_crossattn": [cond_in], "c_concat": [image_cond_in]}) + else: + x_out = torch.zeros_like(x_in) + for batch_offset in range(0, x_out.shape[0], batch_size): + a = batch_offset + b = a + batch_size + x_out[a:b] = self.inner_model(x_in[a:b], sigma_in[a:b], cond={"c_crossattn": [cond_in[a:b]], "c_concat": [image_cond_in[a:b]]}) + else: + x_out = torch.zeros_like(x_in) + batch_size = batch_size*2 if shared.batch_cond_uncond else batch_size + for batch_offset in range(0, tensor.shape[0], batch_size): + a = batch_offset + b = min(a + batch_size, tensor.shape[0]) + + if not is_edit_model: + c_crossattn = [tensor[a:b]] + else: + c_crossattn = torch.cat([tensor[a:b]], uncond) + + x_out[a:b] = self.inner_model(x_in[a:b], sigma_in[a:b], cond={"c_crossattn": c_crossattn, "c_concat": [image_cond_in[a:b]]}) + + x_out[-uncond.shape[0]:] = self.inner_model(x_in[-uncond.shape[0]:], sigma_in[-uncond.shape[0]:], cond={"c_crossattn": [uncond], "c_concat": [image_cond_in[-uncond.shape[0]:]]}) + + denoised_params = CFGDenoisedParams(x_out, state.sampling_step, state.sampling_steps) + cfg_denoised_callback(denoised_params) + + devices.test_for_nans(x_out, "unet") + + if opts.live_preview_content == "Prompt": + sd_samplers_common.store_latent(x_out[0:uncond.shape[0]]) + elif opts.live_preview_content == "Negative prompt": + sd_samplers_common.store_latent(x_out[-uncond.shape[0]:]) + + if not is_edit_model: + denoised = self.combine_denoised(x_out, conds_list, uncond, cond_scale) + else: + denoised = self.combine_denoised_for_edit_model(x_out, cond_scale) + + if self.mask is not None: + denoised = self.init_latent * self.mask + self.nmask * denoised + + self.step += 1 + + return denoised + + +class TorchHijack: + def __init__(self, sampler_noises): + # Using a deque to efficiently receive the sampler_noises in the same order as the previous index-based + # implementation. + self.sampler_noises = deque(sampler_noises) + + def __getattr__(self, item): + if item == 'randn_like': + return self.randn_like + + if hasattr(torch, item): + return getattr(torch, item) + + raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, item)) + + def randn_like(self, x): + if self.sampler_noises: + noise = self.sampler_noises.popleft() + if noise.shape == x.shape: + return noise + + if x.device.type == 'mps': + return torch.randn_like(x, device=devices.cpu).to(x.device) + else: + return torch.randn_like(x) + + +class KDiffusionSampler: + def __init__(self, funcname, sd_model): + denoiser = k_diffusion.external.CompVisVDenoiser if sd_model.parameterization == "v" else k_diffusion.external.CompVisDenoiser + + self.model_wrap = denoiser(sd_model, quantize=shared.opts.enable_quantization) + self.funcname = funcname + self.func = getattr(k_diffusion.sampling, self.funcname) + self.extra_params = sampler_extra_params.get(funcname, []) + self.model_wrap_cfg = CFGDenoiser(self.model_wrap) + self.sampler_noises = None + self.stop_at = None + self.eta = None + self.config = None + self.last_latent = None + + self.conditioning_key = sd_model.model.conditioning_key + + def callback_state(self, d): + step = d['i'] + latent = d["denoised"] + if opts.live_preview_content == "Combined": + sd_samplers_common.store_latent(latent) + self.last_latent = latent + + if self.stop_at is not None and step > self.stop_at: + raise sd_samplers_common.InterruptedException + + state.sampling_step = step + shared.total_tqdm.update() + + def launch_sampling(self, steps, func): + state.sampling_steps = steps + state.sampling_step = 0 + + try: + return func() + except sd_samplers_common.InterruptedException: + return self.last_latent + + def number_of_needed_noises(self, p): + return p.steps + + def initialize(self, p): + self.model_wrap_cfg.mask = p.mask if hasattr(p, 'mask') else None + self.model_wrap_cfg.nmask = p.nmask if hasattr(p, 'nmask') else None + self.model_wrap_cfg.step = 0 + self.model_wrap_cfg.image_cfg_scale = getattr(p, 'image_cfg_scale', None) + self.eta = p.eta if p.eta is not None else opts.eta_ancestral + + k_diffusion.sampling.torch = TorchHijack(self.sampler_noises if self.sampler_noises is not None else []) + + extra_params_kwargs = {} + for param_name in self.extra_params: + if hasattr(p, param_name) and param_name in inspect.signature(self.func).parameters: + extra_params_kwargs[param_name] = getattr(p, param_name) + + if 'eta' in inspect.signature(self.func).parameters: + if self.eta != 1.0: + p.extra_generation_params["Eta"] = self.eta + + extra_params_kwargs['eta'] = self.eta + + return extra_params_kwargs + + def get_sigmas(self, p, steps): + discard_next_to_last_sigma = self.config is not None and self.config.options.get('discard_next_to_last_sigma', False) + if opts.always_discard_next_to_last_sigma and not discard_next_to_last_sigma: + discard_next_to_last_sigma = True + p.extra_generation_params["Discard penultimate sigma"] = True + + steps += 1 if discard_next_to_last_sigma else 0 + + if p.sampler_noise_scheduler_override: + sigmas = p.sampler_noise_scheduler_override(steps) + elif self.config is not None and self.config.options.get('scheduler', None) == 'karras': + sigma_min, sigma_max = (0.1, 10) if opts.use_old_karras_scheduler_sigmas else (self.model_wrap.sigmas[0].item(), self.model_wrap.sigmas[-1].item()) + + sigmas = k_diffusion.sampling.get_sigmas_karras(n=steps, sigma_min=sigma_min, sigma_max=sigma_max, device=shared.device) + else: + sigmas = self.model_wrap.get_sigmas(steps) + + if discard_next_to_last_sigma: + sigmas = torch.cat([sigmas[:-2], sigmas[-1:]]) + + return sigmas + + def create_noise_sampler(self, x, sigmas, p): + """For DPM++ SDE: manually create noise sampler to enable deterministic results across different batch sizes""" + if shared.opts.no_dpmpp_sde_batch_determinism: + return None + + from k_diffusion.sampling import BrownianTreeNoiseSampler + sigma_min, sigma_max = sigmas[sigmas > 0].min(), sigmas.max() + current_iter_seeds = p.all_seeds[p.iteration * p.batch_size:(p.iteration + 1) * p.batch_size] + return BrownianTreeNoiseSampler(x, sigma_min, sigma_max, seed=current_iter_seeds) + + def sample_img2img(self, p, x, noise, conditioning, unconditional_conditioning, steps=None, image_conditioning=None): + steps, t_enc = sd_samplers_common.setup_img2img_steps(p, steps) + + sigmas = self.get_sigmas(p, steps) + + sigma_sched = sigmas[steps - t_enc - 1:] + xi = x + noise * sigma_sched[0] + + extra_params_kwargs = self.initialize(p) + parameters = inspect.signature(self.func).parameters + + if 'sigma_min' in parameters: + ## last sigma is zero which isn't allowed by DPM Fast & Adaptive so taking value before last + extra_params_kwargs['sigma_min'] = sigma_sched[-2] + if 'sigma_max' in parameters: + extra_params_kwargs['sigma_max'] = sigma_sched[0] + if 'n' in parameters: + extra_params_kwargs['n'] = len(sigma_sched) - 1 + if 'sigma_sched' in parameters: + extra_params_kwargs['sigma_sched'] = sigma_sched + if 'sigmas' in parameters: + extra_params_kwargs['sigmas'] = sigma_sched + + if self.funcname == 'sample_dpmpp_sde': + noise_sampler = self.create_noise_sampler(x, sigmas, p) + extra_params_kwargs['noise_sampler'] = noise_sampler + + self.model_wrap_cfg.init_latent = x + self.last_latent = x + extra_args={ + 'cond': conditioning, + 'image_cond': image_conditioning, + 'uncond': unconditional_conditioning, + 'cond_scale': p.cfg_scale, + } + + samples = self.launch_sampling(t_enc + 1, lambda: self.func(self.model_wrap_cfg, xi, extra_args=extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs)) + + return samples + + def sample(self, p, x, conditioning, unconditional_conditioning, steps=None, image_conditioning=None): + steps = steps or p.steps + + sigmas = self.get_sigmas(p, steps) + + x = x * sigmas[0] + + extra_params_kwargs = self.initialize(p) + parameters = inspect.signature(self.func).parameters + + if 'sigma_min' in parameters: + extra_params_kwargs['sigma_min'] = self.model_wrap.sigmas[0].item() + extra_params_kwargs['sigma_max'] = self.model_wrap.sigmas[-1].item() + if 'n' in parameters: + extra_params_kwargs['n'] = steps + else: + extra_params_kwargs['sigmas'] = sigmas + + if self.funcname == 'sample_dpmpp_sde': + noise_sampler = self.create_noise_sampler(x, sigmas, p) + extra_params_kwargs['noise_sampler'] = noise_sampler + + self.last_latent = x + samples = self.launch_sampling(steps, lambda: self.func(self.model_wrap_cfg, x, extra_args={ + 'cond': conditioning, + 'image_cond': image_conditioning, + 'uncond': unconditional_conditioning, + 'cond_scale': p.cfg_scale + }, disable=False, callback=self.callback_state, **extra_params_kwargs)) + + return samples + diff --git a/sd/stable-diffusion-webui/modules/sd_vae.py b/sd/stable-diffusion-webui/modules/sd_vae.py new file mode 100644 index 0000000000000000000000000000000000000000..9b00f76e9c62c794b3a27b36bae0f168ff4f5ab8 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_vae.py @@ -0,0 +1,216 @@ +import torch +import safetensors.torch +import os +import collections +from collections import namedtuple +from modules import paths, shared, devices, script_callbacks, sd_models +import glob +from copy import deepcopy + + +vae_path = os.path.abspath(os.path.join(paths.models_path, "VAE")) +vae_ignore_keys = {"model_ema.decay", "model_ema.num_updates"} +vae_dict = {} + + +base_vae = None +loaded_vae_file = None +checkpoint_info = None + +checkpoints_loaded = collections.OrderedDict() + +def get_base_vae(model): + if base_vae is not None and checkpoint_info == model.sd_checkpoint_info and model: + return base_vae + return None + + +def store_base_vae(model): + global base_vae, checkpoint_info + if checkpoint_info != model.sd_checkpoint_info: + assert not loaded_vae_file, "Trying to store non-base VAE!" + base_vae = deepcopy(model.first_stage_model.state_dict()) + checkpoint_info = model.sd_checkpoint_info + + +def delete_base_vae(): + global base_vae, checkpoint_info + base_vae = None + checkpoint_info = None + + +def restore_base_vae(model): + global loaded_vae_file + if base_vae is not None and checkpoint_info == model.sd_checkpoint_info: + print("Restoring base VAE") + _load_vae_dict(model, base_vae) + loaded_vae_file = None + delete_base_vae() + + +def get_filename(filepath): + return os.path.basename(filepath) + + +def refresh_vae_list(): + vae_dict.clear() + + paths = [ + os.path.join(sd_models.model_path, '**/*.vae.ckpt'), + os.path.join(sd_models.model_path, '**/*.vae.pt'), + os.path.join(sd_models.model_path, '**/*.vae.safetensors'), + os.path.join(vae_path, '**/*.ckpt'), + os.path.join(vae_path, '**/*.pt'), + os.path.join(vae_path, '**/*.safetensors'), + ] + + if shared.cmd_opts.ckpt_dir is not None and os.path.isdir(shared.cmd_opts.ckpt_dir): + paths += [ + os.path.join(shared.cmd_opts.ckpt_dir, '**/*.vae.ckpt'), + os.path.join(shared.cmd_opts.ckpt_dir, '**/*.vae.pt'), + os.path.join(shared.cmd_opts.ckpt_dir, '**/*.vae.safetensors'), + ] + + if shared.cmd_opts.vae_dir is not None and os.path.isdir(shared.cmd_opts.vae_dir): + paths += [ + os.path.join(shared.cmd_opts.vae_dir, '**/*.ckpt'), + os.path.join(shared.cmd_opts.vae_dir, '**/*.pt'), + os.path.join(shared.cmd_opts.vae_dir, '**/*.safetensors'), + ] + + candidates = [] + for path in paths: + candidates += glob.iglob(path, recursive=True) + + for filepath in candidates: + name = get_filename(filepath) + vae_dict[name] = filepath + + +def find_vae_near_checkpoint(checkpoint_file): + checkpoint_path = os.path.splitext(checkpoint_file)[0] + for vae_location in [checkpoint_path + ".vae.pt", checkpoint_path + ".vae.ckpt", checkpoint_path + ".vae.safetensors"]: + if os.path.isfile(vae_location): + return vae_location + + return None + + +def resolve_vae(checkpoint_file): + if shared.cmd_opts.vae_path is not None: + return shared.cmd_opts.vae_path, 'from commandline argument' + + is_automatic = shared.opts.sd_vae in {"Automatic", "auto"} # "auto" for people with old config + + vae_near_checkpoint = find_vae_near_checkpoint(checkpoint_file) + if vae_near_checkpoint is not None and (shared.opts.sd_vae_as_default or is_automatic): + return vae_near_checkpoint, 'found near the checkpoint' + + if shared.opts.sd_vae == "None": + return None, None + + vae_from_options = vae_dict.get(shared.opts.sd_vae, None) + if vae_from_options is not None: + return vae_from_options, 'specified in settings' + + if not is_automatic: + print(f"Couldn't find VAE named {shared.opts.sd_vae}; using None instead") + + return None, None + + +def load_vae_dict(filename, map_location): + vae_ckpt = sd_models.read_state_dict(filename, map_location=map_location) + vae_dict_1 = {k: v for k, v in vae_ckpt.items() if k[0:4] != "loss" and k not in vae_ignore_keys} + return vae_dict_1 + + +def load_vae(model, vae_file=None, vae_source="from unknown source"): + global vae_dict, loaded_vae_file + # save_settings = False + + cache_enabled = shared.opts.sd_vae_checkpoint_cache > 0 + + if vae_file: + if cache_enabled and vae_file in checkpoints_loaded: + # use vae checkpoint cache + print(f"Loading VAE weights {vae_source}: cached {get_filename(vae_file)}") + store_base_vae(model) + _load_vae_dict(model, checkpoints_loaded[vae_file]) + else: + assert os.path.isfile(vae_file), f"VAE {vae_source} doesn't exist: {vae_file}" + print(f"Loading VAE weights {vae_source}: {vae_file}") + store_base_vae(model) + + vae_dict_1 = load_vae_dict(vae_file, map_location=shared.weight_load_location) + _load_vae_dict(model, vae_dict_1) + + if cache_enabled: + # cache newly loaded vae + checkpoints_loaded[vae_file] = vae_dict_1.copy() + + # clean up cache if limit is reached + if cache_enabled: + while len(checkpoints_loaded) > shared.opts.sd_vae_checkpoint_cache + 1: # we need to count the current model + checkpoints_loaded.popitem(last=False) # LRU + + # If vae used is not in dict, update it + # It will be removed on refresh though + vae_opt = get_filename(vae_file) + if vae_opt not in vae_dict: + vae_dict[vae_opt] = vae_file + + elif loaded_vae_file: + restore_base_vae(model) + + loaded_vae_file = vae_file + + +# don't call this from outside +def _load_vae_dict(model, vae_dict_1): + model.first_stage_model.load_state_dict(vae_dict_1) + model.first_stage_model.to(devices.dtype_vae) + + +def clear_loaded_vae(): + global loaded_vae_file + loaded_vae_file = None + + +unspecified = object() + + +def reload_vae_weights(sd_model=None, vae_file=unspecified): + from modules import lowvram, devices, sd_hijack + + if not sd_model: + sd_model = shared.sd_model + + checkpoint_info = sd_model.sd_checkpoint_info + checkpoint_file = checkpoint_info.filename + + if vae_file == unspecified: + vae_file, vae_source = resolve_vae(checkpoint_file) + else: + vae_source = "from function argument" + + if loaded_vae_file == vae_file: + return + + if shared.cmd_opts.lowvram or shared.cmd_opts.medvram: + lowvram.send_everything_to_cpu() + else: + sd_model.to(devices.cpu) + + sd_hijack.model_hijack.undo_hijack(sd_model) + + load_vae(sd_model, vae_file, vae_source) + + sd_hijack.model_hijack.hijack(sd_model) + script_callbacks.model_loaded_callback(sd_model) + + if not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram: + sd_model.to(devices.device) + + print("VAE weights loaded.") + return sd_model diff --git a/sd/stable-diffusion-webui/modules/sd_vae_approx.py b/sd/stable-diffusion-webui/modules/sd_vae_approx.py new file mode 100644 index 0000000000000000000000000000000000000000..ea4c4a3a72941c31a654a29ce90cf8d9c82ce674 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sd_vae_approx.py @@ -0,0 +1,58 @@ +import os + +import torch +from torch import nn +from modules import devices, paths + +sd_vae_approx_model = None + + +class VAEApprox(nn.Module): + def __init__(self): + super(VAEApprox, self).__init__() + self.conv1 = nn.Conv2d(4, 8, (7, 7)) + self.conv2 = nn.Conv2d(8, 16, (5, 5)) + self.conv3 = nn.Conv2d(16, 32, (3, 3)) + self.conv4 = nn.Conv2d(32, 64, (3, 3)) + self.conv5 = nn.Conv2d(64, 32, (3, 3)) + self.conv6 = nn.Conv2d(32, 16, (3, 3)) + self.conv7 = nn.Conv2d(16, 8, (3, 3)) + self.conv8 = nn.Conv2d(8, 3, (3, 3)) + + def forward(self, x): + extra = 11 + x = nn.functional.interpolate(x, (x.shape[2] * 2, x.shape[3] * 2)) + x = nn.functional.pad(x, (extra, extra, extra, extra)) + + for layer in [self.conv1, self.conv2, self.conv3, self.conv4, self.conv5, self.conv6, self.conv7, self.conv8, ]: + x = layer(x) + x = nn.functional.leaky_relu(x, 0.1) + + return x + + +def model(): + global sd_vae_approx_model + + if sd_vae_approx_model is None: + sd_vae_approx_model = VAEApprox() + sd_vae_approx_model.load_state_dict(torch.load(os.path.join(paths.models_path, "VAE-approx", "model.pt"), map_location='cpu' if devices.device.type != 'cuda' else None)) + sd_vae_approx_model.eval() + sd_vae_approx_model.to(devices.device, devices.dtype) + + return sd_vae_approx_model + + +def cheap_approximation(sample): + # https://discuss.huggingface.co/t/decoding-latents-to-rgb-without-upscaling/23204/2 + + coefs = torch.tensor([ + [0.298, 0.207, 0.208], + [0.187, 0.286, 0.173], + [-0.158, 0.189, 0.264], + [-0.184, -0.271, -0.473], + ]).to(sample.device) + + x_sample = torch.einsum("lxy,lr -> rxy", sample, coefs) + + return x_sample diff --git a/sd/stable-diffusion-webui/modules/shared.py b/sd/stable-diffusion-webui/modules/shared.py new file mode 100644 index 0000000000000000000000000000000000000000..2a3037ac85a4ce46300b769c97b63d736315f848 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/shared.py @@ -0,0 +1,720 @@ +import argparse +import datetime +import json +import os +import sys +import time + +from PIL import Image +import gradio as gr +import tqdm + +import modules.interrogate +import modules.memmon +import modules.styles +import modules.devices as devices +from modules import localization, extensions, script_loading, errors, ui_components, shared_items +from modules.paths import models_path, script_path, data_path + + +demo = None + +sd_configs_path = os.path.join(script_path, "configs") +sd_default_config = os.path.join(sd_configs_path, "v1-inference.yaml") +sd_model_file = os.path.join(script_path, 'model.ckpt') +default_sd_model_file = sd_model_file + +parser = argparse.ArgumentParser() +parser.add_argument("--data-dir", type=str, default=os.path.dirname(os.path.dirname(os.path.realpath(__file__))), help="base path where all user data is stored",) +parser.add_argument("--config", type=str, default=sd_default_config, help="path to config which constructs model",) +parser.add_argument("--ckpt", type=str, default=sd_model_file, help="path to checkpoint of stable diffusion model; if specified, this checkpoint will be added to the list of checkpoints and loaded",) +parser.add_argument("--ckpt-dir", type=str, default=None, help="Path to directory with stable diffusion checkpoints") +parser.add_argument("--vae-dir", type=str, default=None, help="Path to directory with VAE files") +parser.add_argument("--gfpgan-dir", type=str, help="GFPGAN directory", default=('./src/gfpgan' if os.path.exists('./src/gfpgan') else './GFPGAN')) +parser.add_argument("--gfpgan-model", type=str, help="GFPGAN model file name", default=None) +parser.add_argument("--no-half", action='store_true', help="do not switch the model to 16-bit floats") +parser.add_argument("--no-half-vae", action='store_true', help="do not switch the VAE model to 16-bit floats") +parser.add_argument("--no-progressbar-hiding", action='store_true', help="do not hide progressbar in gradio UI (we hide it because it slows down ML if you have hardware acceleration in browser)") +parser.add_argument("--max-batch-count", type=int, default=16, help="maximum batch count value for the UI") +parser.add_argument("--embeddings-dir", type=str, default=os.path.join(data_path, 'embeddings'), help="embeddings directory for textual inversion (default: embeddings)") +parser.add_argument("--textual-inversion-templates-dir", type=str, default=os.path.join(script_path, 'textual_inversion_templates'), help="directory with textual inversion templates") +parser.add_argument("--hypernetwork-dir", type=str, default=os.path.join(models_path, 'hypernetworks'), help="hypernetwork directory") +parser.add_argument("--localizations-dir", type=str, default=os.path.join(script_path, 'localizations'), help="localizations directory") +parser.add_argument("--allow-code", action='store_true', help="allow custom script execution from webui") +parser.add_argument("--medvram", action='store_true', help="enable stable diffusion model optimizations for sacrificing a little speed for low VRM usage") +parser.add_argument("--lowvram", action='store_true', help="enable stable diffusion model optimizations for sacrificing a lot of speed for very low VRM usage") +parser.add_argument("--lowram", action='store_true', help="load stable diffusion checkpoint weights to VRAM instead of RAM") +parser.add_argument("--always-batch-cond-uncond", action='store_true', help="disables cond/uncond batching that is enabled to save memory with --medvram or --lowvram") +parser.add_argument("--unload-gfpgan", action='store_true', help="does not do anything.") +parser.add_argument("--precision", type=str, help="evaluate at this precision", choices=["full", "autocast"], default="autocast") +parser.add_argument("--upcast-sampling", action='store_true', help="upcast sampling. No effect with --no-half. Usually produces similar results to --no-half with better performance while using less memory.") +parser.add_argument("--share", action='store_true', help="use share=True for gradio and make the UI accessible through their site") +parser.add_argument("--ngrok", type=str, help="ngrok authtoken, alternative to gradio --share", default=None) +parser.add_argument("--ngrok-region", type=str, help="The region in which ngrok should start.", default="us") +parser.add_argument("--enable-insecure-extension-access", action='store_true', help="enable extensions tab regardless of other options") +parser.add_argument("--codeformer-models-path", type=str, help="Path to directory with codeformer model file(s).", default=os.path.join(models_path, 'Codeformer')) +parser.add_argument("--gfpgan-models-path", type=str, help="Path to directory with GFPGAN model file(s).", default=os.path.join(models_path, 'GFPGAN')) +parser.add_argument("--esrgan-models-path", type=str, help="Path to directory with ESRGAN model file(s).", default=os.path.join(models_path, 'ESRGAN')) +parser.add_argument("--bsrgan-models-path", type=str, help="Path to directory with BSRGAN model file(s).", default=os.path.join(models_path, 'BSRGAN')) +parser.add_argument("--realesrgan-models-path", type=str, help="Path to directory with RealESRGAN model file(s).", default=os.path.join(models_path, 'RealESRGAN')) +parser.add_argument("--clip-models-path", type=str, help="Path to directory with CLIP model file(s).", default=None) +parser.add_argument("--xformers", action='store_true', help="enable xformers for cross attention layers") +parser.add_argument("--force-enable-xformers", action='store_true', help="enable xformers for cross attention layers regardless of whether the checking code thinks you can run it; do not make bug reports if this fails to work") +parser.add_argument("--xformers-flash-attention", action='store_true', help="enable xformers with Flash Attention to improve reproducibility (supported for SD2.x or variant only)") +parser.add_argument("--deepdanbooru", action='store_true', help="does not do anything") +parser.add_argument("--opt-split-attention", action='store_true', help="force-enables Doggettx's cross-attention layer optimization. By default, it's on for torch cuda.") +parser.add_argument("--opt-sub-quad-attention", action='store_true', help="enable memory efficient sub-quadratic cross-attention layer optimization") +parser.add_argument("--sub-quad-q-chunk-size", type=int, help="query chunk size for the sub-quadratic cross-attention layer optimization to use", default=1024) +parser.add_argument("--sub-quad-kv-chunk-size", type=int, help="kv chunk size for the sub-quadratic cross-attention layer optimization to use", default=None) +parser.add_argument("--sub-quad-chunk-threshold", type=int, help="the percentage of VRAM threshold for the sub-quadratic cross-attention layer optimization to use chunking", default=None) +parser.add_argument("--opt-split-attention-invokeai", action='store_true', help="force-enables InvokeAI's cross-attention layer optimization. By default, it's on when cuda is unavailable.") +parser.add_argument("--opt-split-attention-v1", action='store_true', help="enable older version of split attention optimization that does not consume all the VRAM it can find") +parser.add_argument("--disable-opt-split-attention", action='store_true', help="force-disables cross-attention layer optimization") +parser.add_argument("--disable-nan-check", action='store_true', help="do not check if produced images/latent spaces have nans; useful for running without a checkpoint in CI") +parser.add_argument("--use-cpu", nargs='+', help="use CPU as torch device for specified modules", default=[], type=str.lower) +parser.add_argument("--listen", action='store_true', help="launch gradio with 0.0.0.0 as server name, allowing to respond to network requests") +parser.add_argument("--port", type=int, help="launch gradio with given server port, you need root/admin rights for ports < 1024, defaults to 7860 if available", default=None) +parser.add_argument("--show-negative-prompt", action='store_true', help="does not do anything", default=False) +parser.add_argument("--ui-config-file", type=str, help="filename to use for ui configuration", default=os.path.join(data_path, 'ui-config.json')) +parser.add_argument("--hide-ui-dir-config", action='store_true', help="hide directory configuration from webui", default=False) +parser.add_argument("--freeze-settings", action='store_true', help="disable editing settings", default=False) +parser.add_argument("--ui-settings-file", type=str, help="filename to use for ui settings", default=os.path.join(data_path, 'config.json')) +parser.add_argument("--gradio-debug", action='store_true', help="launch gradio with --debug option") +parser.add_argument("--gradio-auth", type=str, help='set gradio authentication like "username:password"; or comma-delimit multiple like "u1:p1,u2:p2,u3:p3"', default=None) +parser.add_argument("--gradio-auth-path", type=str, help='set gradio authentication file path ex. "/path/to/auth/file" same auth format as --gradio-auth', default=None) +parser.add_argument("--gradio-img2img-tool", type=str, help='does not do anything') +parser.add_argument("--gradio-inpaint-tool", type=str, help="does not do anything") +parser.add_argument("--opt-channelslast", action='store_true', help="change memory type for stable diffusion to channels last") +parser.add_argument("--styles-file", type=str, help="filename to use for styles", default=os.path.join(data_path, 'styles.csv')) +parser.add_argument("--autolaunch", action='store_true', help="open the webui URL in the system's default browser upon launch", default=False) +parser.add_argument("--theme", type=str, help="launches the UI with light or dark theme", default=None) +parser.add_argument("--use-textbox-seed", action='store_true', help="use textbox for seeds in UI (no up/down, but possible to input long seeds)", default=False) +parser.add_argument("--disable-console-progressbars", action='store_true', help="do not output progressbars to console", default=False) +parser.add_argument("--enable-console-prompts", action='store_true', help="print prompts to console when generating with txt2img and img2img", default=False) +parser.add_argument('--vae-path', type=str, help='Checkpoint to use as VAE; setting this argument disables all settings related to VAE', default=None) +parser.add_argument("--disable-safe-unpickle", action='store_true', help="disable checking pytorch models for malicious code", default=False) +parser.add_argument("--api", action='store_true', help="use api=True to launch the API together with the webui (use --nowebui instead for only the API)") +parser.add_argument("--api-auth", type=str, help='Set authentication for API like "username:password"; or comma-delimit multiple like "u1:p1,u2:p2,u3:p3"', default=None) +parser.add_argument("--api-log", action='store_true', help="use api-log=True to enable logging of all API requests") +parser.add_argument("--nowebui", action='store_true', help="use api=True to launch the API instead of the webui") +parser.add_argument("--ui-debug-mode", action='store_true', help="Don't load model to quickly launch UI") +parser.add_argument("--device-id", type=str, help="Select the default CUDA device to use (export CUDA_VISIBLE_DEVICES=0,1,etc might be needed before)", default=None) +parser.add_argument("--administrator", action='store_true', help="Administrator rights", default=False) +parser.add_argument("--cors-allow-origins", type=str, help="Allowed CORS origin(s) in the form of a comma-separated list (no spaces)", default=None) +parser.add_argument("--cors-allow-origins-regex", type=str, help="Allowed CORS origin(s) in the form of a single regular expression", default=None) +parser.add_argument("--tls-keyfile", type=str, help="Partially enables TLS, requires --tls-certfile to fully function", default=None) +parser.add_argument("--tls-certfile", type=str, help="Partially enables TLS, requires --tls-keyfile to fully function", default=None) +parser.add_argument("--server-name", type=str, help="Sets hostname of server", default=None) +parser.add_argument("--gradio-queue", action='store_true', help="Uses gradio queue; experimental option; breaks restart UI button") +parser.add_argument("--skip-version-check", action='store_true', help="Do not check versions of torch and xformers") +parser.add_argument("--no-hashing", action='store_true', help="disable sha256 hashing of checkpoints to help loading performance", default=False) +parser.add_argument("--no-download-sd-model", action='store_true', help="don't download SD1.5 model even if no model is found in --ckpt-dir", default=False) + + +script_loading.preload_extensions(extensions.extensions_dir, parser) +script_loading.preload_extensions(extensions.extensions_builtin_dir, parser) + +cmd_opts = parser.parse_args() + +restricted_opts = { + "samples_filename_pattern", + "directories_filename_pattern", + "outdir_samples", + "outdir_txt2img_samples", + "outdir_img2img_samples", + "outdir_extras_samples", + "outdir_grids", + "outdir_txt2img_grids", + "outdir_save", +} + +ui_reorder_categories = [ + "inpaint", + "sampler", + "checkboxes", + "hires_fix", + "dimensions", + "cfg", + "seed", + "batch", + "override_settings", + "scripts", +] + +cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen or cmd_opts.server_name) and not cmd_opts.enable_insecure_extension_access + +devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_esrgan, devices.device_codeformer = \ + (devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'esrgan', 'codeformer']) + +device = devices.device +weight_load_location = None if cmd_opts.lowram else "cpu" + +batch_cond_uncond = cmd_opts.always_batch_cond_uncond or not (cmd_opts.lowvram or cmd_opts.medvram) +parallel_processing_allowed = not cmd_opts.lowvram and not cmd_opts.medvram +xformers_available = False +config_filename = cmd_opts.ui_settings_file + +os.makedirs(cmd_opts.hypernetwork_dir, exist_ok=True) +hypernetworks = {} +loaded_hypernetworks = [] + + +def reload_hypernetworks(): + from modules.hypernetworks import hypernetwork + global hypernetworks + + hypernetworks = hypernetwork.list_hypernetworks(cmd_opts.hypernetwork_dir) + + +class State: + skipped = False + interrupted = False + job = "" + job_no = 0 + job_count = 0 + processing_has_refined_job_count = False + job_timestamp = '0' + sampling_step = 0 + sampling_steps = 0 + current_latent = None + current_image = None + current_image_sampling_step = 0 + id_live_preview = 0 + textinfo = None + time_start = None + need_restart = False + server_start = None + + def skip(self): + self.skipped = True + + def interrupt(self): + self.interrupted = True + + def nextjob(self): + if opts.live_previews_enable and opts.show_progress_every_n_steps == -1: + self.do_set_current_image() + + self.job_no += 1 + self.sampling_step = 0 + self.current_image_sampling_step = 0 + + def dict(self): + obj = { + "skipped": self.skipped, + "interrupted": self.interrupted, + "job": self.job, + "job_count": self.job_count, + "job_timestamp": self.job_timestamp, + "job_no": self.job_no, + "sampling_step": self.sampling_step, + "sampling_steps": self.sampling_steps, + } + + return obj + + def begin(self): + self.sampling_step = 0 + self.job_count = -1 + self.processing_has_refined_job_count = False + self.job_no = 0 + self.job_timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + self.current_latent = None + self.current_image = None + self.current_image_sampling_step = 0 + self.id_live_preview = 0 + self.skipped = False + self.interrupted = False + self.textinfo = None + self.time_start = time.time() + + devices.torch_gc() + + def end(self): + self.job = "" + self.job_count = 0 + + devices.torch_gc() + + def set_current_image(self): + """sets self.current_image from self.current_latent if enough sampling steps have been made after the last call to this""" + if not parallel_processing_allowed: + return + + if self.sampling_step - self.current_image_sampling_step >= opts.show_progress_every_n_steps and opts.live_previews_enable and opts.show_progress_every_n_steps != -1: + self.do_set_current_image() + + def do_set_current_image(self): + if self.current_latent is None: + return + + import modules.sd_samplers + if opts.show_progress_grid: + self.assign_current_image(modules.sd_samplers.samples_to_image_grid(self.current_latent)) + else: + self.assign_current_image(modules.sd_samplers.sample_to_image(self.current_latent)) + + self.current_image_sampling_step = self.sampling_step + + def assign_current_image(self, image): + self.current_image = image + self.id_live_preview += 1 + + +state = State() +state.server_start = time.time() + +styles_filename = cmd_opts.styles_file +prompt_styles = modules.styles.StyleDatabase(styles_filename) + +interrogator = modules.interrogate.InterrogateModels("interrogate") + +face_restorers = [] + +class OptionInfo: + def __init__(self, default=None, label="", component=None, component_args=None, onchange=None, section=None, refresh=None): + self.default = default + self.label = label + self.component = component + self.component_args = component_args + self.onchange = onchange + self.section = section + self.refresh = refresh + + +def options_section(section_identifier, options_dict): + for k, v in options_dict.items(): + v.section = section_identifier + + return options_dict + + +def list_checkpoint_tiles(): + import modules.sd_models + return modules.sd_models.checkpoint_tiles() + + +def refresh_checkpoints(): + import modules.sd_models + return modules.sd_models.list_models() + + +def list_samplers(): + import modules.sd_samplers + return modules.sd_samplers.all_samplers + + +hide_dirs = {"visible": not cmd_opts.hide_ui_dir_config} + +options_templates = {} + +options_templates.update(options_section(('saving-images', "Saving images/grids"), { + "samples_save": OptionInfo(True, "Always save all generated images"), + "samples_format": OptionInfo('png', 'File format for images'), + "samples_filename_pattern": OptionInfo("", "Images filename pattern", component_args=hide_dirs), + "save_images_add_number": OptionInfo(True, "Add number to filename when saving", component_args=hide_dirs), + + "grid_save": OptionInfo(True, "Always save all generated image grids"), + "grid_format": OptionInfo('png', 'File format for grids'), + "grid_extended_filename": OptionInfo(False, "Add extended info (seed, prompt) to filename when saving grid"), + "grid_only_if_multiple": OptionInfo(True, "Do not save grids consisting of one picture"), + "grid_prevent_empty_spots": OptionInfo(False, "Prevent empty spots in grid (when set to autodetect)"), + "n_rows": OptionInfo(-1, "Grid row count; use -1 for autodetect and 0 for it to be same as batch size", gr.Slider, {"minimum": -1, "maximum": 16, "step": 1}), + + "enable_pnginfo": OptionInfo(True, "Save text information about generation parameters as chunks to png files"), + "save_txt": OptionInfo(False, "Create a text file next to every image with generation parameters."), + "save_images_before_face_restoration": OptionInfo(False, "Save a copy of image before doing face restoration."), + "save_images_before_highres_fix": OptionInfo(False, "Save a copy of image before applying highres fix."), + "save_images_before_color_correction": OptionInfo(False, "Save a copy of image before applying color correction to img2img results"), + "jpeg_quality": OptionInfo(80, "Quality for saved jpeg images", gr.Slider, {"minimum": 1, "maximum": 100, "step": 1}), + "export_for_4chan": OptionInfo(True, "If the saved image file size is above the limit, or its either width or height are above the limit, save a downscaled copy as JPG"), + "img_downscale_threshold": OptionInfo(4.0, "File size limit for the above option, MB", gr.Number), + "target_side_length": OptionInfo(4000, "Width/height limit for the above option, in pixels", gr.Number), + + "use_original_name_batch": OptionInfo(True, "Use original name for output filename during batch process in extras tab"), + "use_upscaler_name_as_suffix": OptionInfo(False, "Use upscaler name as filename suffix in the extras tab"), + "save_selected_only": OptionInfo(True, "When using 'Save' button, only save a single selected image"), + "do_not_add_watermark": OptionInfo(False, "Do not add watermark to images"), + + "temp_dir": OptionInfo("", "Directory for temporary images; leave empty for default"), + "clean_temp_dir_at_start": OptionInfo(False, "Cleanup non-default temporary directory when starting webui"), + +})) + +options_templates.update(options_section(('saving-paths', "Paths for saving"), { + "outdir_samples": OptionInfo("", "Output directory for images; if empty, defaults to three directories below", component_args=hide_dirs), + "outdir_txt2img_samples": OptionInfo("outputs/txt2img-images", 'Output directory for txt2img images', component_args=hide_dirs), + "outdir_img2img_samples": OptionInfo("outputs/img2img-images", 'Output directory for img2img images', component_args=hide_dirs), + "outdir_extras_samples": OptionInfo("outputs/extras-images", 'Output directory for images from extras tab', component_args=hide_dirs), + "outdir_grids": OptionInfo("", "Output directory for grids; if empty, defaults to two directories below", component_args=hide_dirs), + "outdir_txt2img_grids": OptionInfo("outputs/txt2img-grids", 'Output directory for txt2img grids', component_args=hide_dirs), + "outdir_img2img_grids": OptionInfo("outputs/img2img-grids", 'Output directory for img2img grids', component_args=hide_dirs), + "outdir_save": OptionInfo("log/images", "Directory for saving images using the Save button", component_args=hide_dirs), +})) + +options_templates.update(options_section(('saving-to-dirs', "Saving to a directory"), { + "save_to_dirs": OptionInfo(True, "Save images to a subdirectory"), + "grid_save_to_dirs": OptionInfo(True, "Save grids to a subdirectory"), + "use_save_to_dirs_for_ui": OptionInfo(False, "When using \"Save\" button, save images to a subdirectory"), + "directories_filename_pattern": OptionInfo("[date]", "Directory name pattern", component_args=hide_dirs), + "directories_max_prompt_words": OptionInfo(8, "Max prompt words for [prompt_words] pattern", gr.Slider, {"minimum": 1, "maximum": 20, "step": 1, **hide_dirs}), +})) + +options_templates.update(options_section(('upscaling', "Upscaling"), { + "ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers. 0 = no tiling.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}), + "ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for ESRGAN upscalers. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}), + "realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Select which Real-ESRGAN models to show in the web UI. (Requires restart)", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}), + "upscaler_for_img2img": OptionInfo(None, "Upscaler for img2img", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}), +})) + +options_templates.update(options_section(('face-restoration', "Face restoration"), { + "face_restoration_model": OptionInfo("CodeFormer", "Face restoration model", gr.Radio, lambda: {"choices": [x.name() for x in face_restorers]}), + "code_former_weight": OptionInfo(0.5, "CodeFormer weight parameter; 0 = maximum effect; 1 = minimum effect", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}), + "face_restoration_unload": OptionInfo(False, "Move face restoration model from VRAM into RAM after processing"), +})) + +options_templates.update(options_section(('system', "System"), { + "show_warnings": OptionInfo(False, "Show warnings in console."), + "memmon_poll_rate": OptionInfo(8, "VRAM usage polls per second during generation. Set to 0 to disable.", gr.Slider, {"minimum": 0, "maximum": 40, "step": 1}), + "samples_log_stdout": OptionInfo(False, "Always print all generation info to standard output"), + "multiple_tqdm": OptionInfo(True, "Add a second progress bar to the console that shows progress for an entire job."), + "print_hypernet_extra": OptionInfo(False, "Print extra hypernetwork information to console."), +})) + +options_templates.update(options_section(('training', "Training"), { + "unload_models_when_training": OptionInfo(False, "Move VAE and CLIP to RAM when training if possible. Saves VRAM."), + "pin_memory": OptionInfo(False, "Turn on pin_memory for DataLoader. Makes training slightly faster but can increase memory usage."), + "save_optimizer_state": OptionInfo(False, "Saves Optimizer state as separate *.optim file. Training of embedding or HN can be resumed with the matching optim file."), + "save_training_settings_to_txt": OptionInfo(True, "Save textual inversion and hypernet settings to a text file whenever training starts."), + "dataset_filename_word_regex": OptionInfo("", "Filename word regex"), + "dataset_filename_join_string": OptionInfo(" ", "Filename join string"), + "training_image_repeats_per_epoch": OptionInfo(1, "Number of repeats for a single input image per epoch; used only for displaying epoch number", gr.Number, {"precision": 0}), + "training_write_csv_every": OptionInfo(500, "Save an csv containing the loss to log directory every N steps, 0 to disable"), + "training_xattention_optimizations": OptionInfo(False, "Use cross attention optimizations while training"), + "training_enable_tensorboard": OptionInfo(False, "Enable tensorboard logging."), + "training_tensorboard_save_images": OptionInfo(False, "Save generated images within tensorboard."), + "training_tensorboard_flush_every": OptionInfo(120, "How often, in seconds, to flush the pending tensorboard events and summaries to disk."), +})) + +options_templates.update(options_section(('sd', "Stable Diffusion"), { + "sd_model_checkpoint": OptionInfo(None, "Stable Diffusion checkpoint", gr.Dropdown, lambda: {"choices": list_checkpoint_tiles()}, refresh=refresh_checkpoints), + "sd_checkpoint_cache": OptionInfo(0, "Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), + "sd_vae_checkpoint_cache": OptionInfo(0, "VAE Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), + "sd_vae": OptionInfo("Automatic", "SD VAE", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list), + "sd_vae_as_default": OptionInfo(True, "Ignore selected VAE for stable diffusion checkpoints that have their own .vae.pt next to them"), + "inpainting_mask_weight": OptionInfo(1.0, "Inpainting conditioning mask strength", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + "initial_noise_multiplier": OptionInfo(1.0, "Noise multiplier for img2img", gr.Slider, {"minimum": 0.5, "maximum": 1.5, "step": 0.01}), + "img2img_color_correction": OptionInfo(False, "Apply color correction to img2img results to match original colors."), + "img2img_fix_steps": OptionInfo(False, "With img2img, do exactly the amount of steps the slider specifies (normally you'd do less with less denoising)."), + "img2img_background_color": OptionInfo("#ffffff", "With img2img, fill image's transparent parts with this color.", ui_components.FormColorPicker, {}), + "enable_quantization": OptionInfo(False, "Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds. Requires restart to apply."), + "enable_emphasis": OptionInfo(True, "Emphasis: use (text) to make model pay more attention to text and [text] to make it pay less attention"), + "enable_batch_seeds": OptionInfo(True, "Make K-diffusion samplers produce same images in a batch as when making a single image"), + "comma_padding_backtrack": OptionInfo(20, "Increase coherency by padding from the last comma within n tokens when using more than 75 tokens", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1 }), + "CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1}), + "upcast_attn": OptionInfo(False, "Upcast cross attention layer to float32"), +})) + +options_templates.update(options_section(('compatibility', "Compatibility"), { + "use_old_emphasis_implementation": OptionInfo(False, "Use old emphasis implementation. Can be useful to reproduce old seeds."), + "use_old_karras_scheduler_sigmas": OptionInfo(False, "Use old karras scheduler sigmas (0.1 to 10)."), + "no_dpmpp_sde_batch_determinism": OptionInfo(False, "Do not make DPM++ SDE deterministic across different batch sizes."), + "use_old_hires_fix_width_height": OptionInfo(False, "For hires fix, use width/height sliders to set final resolution rather than first pass (disables Upscale by, Resize width/height to)."), +})) + +options_templates.update(options_section(('interrogate', "Interrogate Options"), { + "interrogate_keep_models_in_memory": OptionInfo(False, "Interrogate: keep models in VRAM"), + "interrogate_return_ranks": OptionInfo(False, "Interrogate: include ranks of model tags matches in results (Has no effect on caption-based interrogators)."), + "interrogate_clip_num_beams": OptionInfo(1, "Interrogate: num_beams for BLIP", gr.Slider, {"minimum": 1, "maximum": 16, "step": 1}), + "interrogate_clip_min_length": OptionInfo(24, "Interrogate: minimum description length (excluding artists, etc..)", gr.Slider, {"minimum": 1, "maximum": 128, "step": 1}), + "interrogate_clip_max_length": OptionInfo(48, "Interrogate: maximum description length", gr.Slider, {"minimum": 1, "maximum": 256, "step": 1}), + "interrogate_clip_dict_limit": OptionInfo(1500, "CLIP: maximum number of lines in text file (0 = No limit)"), + "interrogate_clip_skip_categories": OptionInfo([], "CLIP: skip inquire categories", gr.CheckboxGroup, lambda: {"choices": modules.interrogate.category_types()}, refresh=modules.interrogate.category_types), + "interrogate_deepbooru_score_threshold": OptionInfo(0.5, "Interrogate: deepbooru score threshold", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}), + "deepbooru_sort_alpha": OptionInfo(True, "Interrogate: deepbooru sort alphabetically"), + "deepbooru_use_spaces": OptionInfo(False, "use spaces for tags in deepbooru"), + "deepbooru_escape": OptionInfo(True, "escape (\\) brackets in deepbooru (so they are used as literal brackets and not for emphasis)"), + "deepbooru_filter_tags": OptionInfo("", "filter out those tags from deepbooru output (separated by comma)"), +})) + +options_templates.update(options_section(('extra_networks', "Extra Networks"), { + "extra_networks_default_view": OptionInfo("cards", "Default view for Extra Networks", gr.Dropdown, {"choices": ["cards", "thumbs"]}), + "extra_networks_default_multiplier": OptionInfo(1.0, "Multiplier for extra networks", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + "sd_hypernetwork": OptionInfo("None", "Add hypernetwork to prompt", gr.Dropdown, lambda: {"choices": [""] + [x for x in hypernetworks.keys()]}, refresh=reload_hypernetworks), +})) + +options_templates.update(options_section(('ui', "User interface"), { + "return_grid": OptionInfo(True, "Show grid in results for web"), + "do_not_show_images": OptionInfo(False, "Do not show any images in results for web"), + "add_model_hash_to_info": OptionInfo(True, "Add model hash to generation information"), + "add_model_name_to_info": OptionInfo(True, "Add model name to generation information"), + "disable_weights_auto_swap": OptionInfo(True, "When reading generation parameters from text into UI (from PNG info or pasted text), do not change the selected model/checkpoint."), + "send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface"), + "send_size": OptionInfo(True, "Send size when sending prompt or image to another interface"), + "font": OptionInfo("", "Font for image grids that have text"), + "js_modal_lightbox": OptionInfo(True, "Enable full page image viewer"), + "js_modal_lightbox_initially_zoomed": OptionInfo(True, "Show images zoomed in by default in full page image viewer"), + "show_progress_in_title": OptionInfo(True, "Show generation progress in window title."), + "samplers_in_dropdown": OptionInfo(True, "Use dropdown for sampler selection instead of radio group"), + "dimensions_and_batch_together": OptionInfo(True, "Show Width/Height and Batch sliders in same row"), + "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), + "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), + "quicksettings": OptionInfo("sd_model_checkpoint", "Quicksettings list"), + "ui_reorder": OptionInfo(", ".join(ui_reorder_categories), "txt2img/img2img UI item order"), + "ui_extra_networks_tab_reorder": OptionInfo("", "Extra networks tab order"), + "localization": OptionInfo("None", "Localization (requires restart)", gr.Dropdown, lambda: {"choices": ["None"] + list(localization.localizations.keys())}, refresh=lambda: localization.list_localizations(cmd_opts.localizations_dir)), +})) + +options_templates.update(options_section(('ui', "Live previews"), { + "show_progressbar": OptionInfo(True, "Show progressbar"), + "live_previews_enable": OptionInfo(True, "Show live previews of the created image"), + "show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"), + "show_progress_every_n_steps": OptionInfo(10, "Show new live preview image every N sampling steps. Set to -1 to show after completion of batch.", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}), + "show_progress_type": OptionInfo("Approx NN", "Image creation progress preview mode", gr.Radio, {"choices": ["Full", "Approx NN", "Approx cheap"]}), + "live_preview_content": OptionInfo("Prompt", "Live preview subject", gr.Radio, {"choices": ["Combined", "Prompt", "Negative prompt"]}), + "live_preview_refresh_period": OptionInfo(1000, "Progressbar/preview update period, in milliseconds") +})) + +options_templates.update(options_section(('sampler-params', "Sampler parameters"), { + "hide_samplers": OptionInfo([], "Hide samplers in user interface (requires restart)", gr.CheckboxGroup, lambda: {"choices": [x.name for x in list_samplers()]}), + "eta_ddim": OptionInfo(0.0, "eta (noise multiplier) for DDIM", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + "eta_ancestral": OptionInfo(1.0, "eta (noise multiplier) for ancestral samplers", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + "ddim_discretize": OptionInfo('uniform', "img2img DDIM discretize", gr.Radio, {"choices": ['uniform', 'quad']}), + 's_churn': OptionInfo(0.0, "sigma churn", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + 's_tmin': OptionInfo(0.0, "sigma tmin", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + 's_noise': OptionInfo(1.0, "sigma noise", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + 'eta_noise_seed_delta': OptionInfo(0, "Eta noise seed delta", gr.Number, {"precision": 0}), + 'always_discard_next_to_last_sigma': OptionInfo(False, "Always discard next-to-last sigma"), +})) + +options_templates.update(options_section(('postprocessing', "Postprocessing"), { + 'postprocessing_enable_in_main_ui': OptionInfo([], "Enable postprocessing operations in txt2img and img2img tabs", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}), + 'postprocessing_operation_order': OptionInfo([], "Postprocessing operation order", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}), + 'upscaling_max_images_in_cache': OptionInfo(5, "Maximum number of images in upscaling cache", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), +})) + +options_templates.update(options_section((None, "Hidden options"), { + "disabled_extensions": OptionInfo([], "Disable those extensions"), + "sd_checkpoint_hash": OptionInfo("", "SHA256 hash of the current checkpoint"), +})) + +options_templates.update() + + +class Options: + data = None + data_labels = options_templates + typemap = {int: float} + + def __init__(self): + self.data = {k: v.default for k, v in self.data_labels.items()} + + def __setattr__(self, key, value): + if self.data is not None: + if key in self.data or key in self.data_labels: + assert not cmd_opts.freeze_settings, "changing settings is disabled" + + info = opts.data_labels.get(key, None) + comp_args = info.component_args if info else None + if isinstance(comp_args, dict) and comp_args.get('visible', True) is False: + raise RuntimeError(f"not possible to set {key} because it is restricted") + + if cmd_opts.hide_ui_dir_config and key in restricted_opts: + raise RuntimeError(f"not possible to set {key} because it is restricted") + + self.data[key] = value + return + + return super(Options, self).__setattr__(key, value) + + def __getattr__(self, item): + if self.data is not None: + if item in self.data: + return self.data[item] + + if item in self.data_labels: + return self.data_labels[item].default + + return super(Options, self).__getattribute__(item) + + def set(self, key, value): + """sets an option and calls its onchange callback, returning True if the option changed and False otherwise""" + + oldval = self.data.get(key, None) + if oldval == value: + return False + + try: + setattr(self, key, value) + except RuntimeError: + return False + + if self.data_labels[key].onchange is not None: + try: + self.data_labels[key].onchange() + except Exception as e: + errors.display(e, f"changing setting {key} to {value}") + setattr(self, key, oldval) + return False + + return True + + def save(self, filename): + assert not cmd_opts.freeze_settings, "saving settings is disabled" + + with open(filename, "w", encoding="utf8") as file: + json.dump(self.data, file, indent=4) + + def same_type(self, x, y): + if x is None or y is None: + return True + + type_x = self.typemap.get(type(x), type(x)) + type_y = self.typemap.get(type(y), type(y)) + + return type_x == type_y + + def load(self, filename): + with open(filename, "r", encoding="utf8") as file: + self.data = json.load(file) + + bad_settings = 0 + for k, v in self.data.items(): + info = self.data_labels.get(k, None) + if info is not None and not self.same_type(info.default, v): + print(f"Warning: bad setting value: {k}: {v} ({type(v).__name__}; expected {type(info.default).__name__})", file=sys.stderr) + bad_settings += 1 + + if bad_settings > 0: + print(f"The program is likely to not work with bad settings.\nSettings file: {filename}\nEither fix the file, or delete it and restart.", file=sys.stderr) + + def onchange(self, key, func, call=True): + item = self.data_labels.get(key) + item.onchange = func + + if call: + func() + + def dumpjson(self): + d = {k: self.data.get(k, self.data_labels.get(k).default) for k in self.data_labels.keys()} + return json.dumps(d) + + def add_option(self, key, info): + self.data_labels[key] = info + + def reorder(self): + """reorder settings so that all items related to section always go together""" + + section_ids = {} + settings_items = self.data_labels.items() + for k, item in settings_items: + if item.section not in section_ids: + section_ids[item.section] = len(section_ids) + + self.data_labels = {k: v for k, v in sorted(settings_items, key=lambda x: section_ids[x[1].section])} + + def cast_value(self, key, value): + """casts an arbitrary to the same type as this setting's value with key + Example: cast_value("eta_noise_seed_delta", "12") -> returns 12 (an int rather than str) + """ + + if value is None: + return None + + default_value = self.data_labels[key].default + if default_value is None: + default_value = getattr(self, key, None) + if default_value is None: + return None + + expected_type = type(default_value) + if expected_type == bool and value == "False": + value = False + else: + value = expected_type(value) + + return value + + + +opts = Options() +if os.path.exists(config_filename): + opts.load(config_filename) + +settings_components = None +"""assinged from ui.py, a mapping on setting anmes to gradio components repsponsible for those settings""" + +latent_upscale_default_mode = "Latent" +latent_upscale_modes = { + "Latent": {"mode": "bilinear", "antialias": False}, + "Latent (antialiased)": {"mode": "bilinear", "antialias": True}, + "Latent (bicubic)": {"mode": "bicubic", "antialias": False}, + "Latent (bicubic antialiased)": {"mode": "bicubic", "antialias": True}, + "Latent (nearest)": {"mode": "nearest", "antialias": False}, + "Latent (nearest-exact)": {"mode": "nearest-exact", "antialias": False}, +} + +sd_upscalers = [] + +sd_model = None + +clip_model = None + +progress_print_out = sys.stdout + + +class TotalTQDM: + def __init__(self): + self._tqdm = None + + def reset(self): + self._tqdm = tqdm.tqdm( + desc="Total progress", + total=state.job_count * state.sampling_steps, + position=1, + file=progress_print_out + ) + + def update(self): + if not opts.multiple_tqdm or cmd_opts.disable_console_progressbars: + return + if self._tqdm is None: + self.reset() + self._tqdm.update() + + def updateTotal(self, new_total): + if not opts.multiple_tqdm or cmd_opts.disable_console_progressbars: + return + if self._tqdm is None: + self.reset() + self._tqdm.total = new_total + + def clear(self): + if self._tqdm is not None: + self._tqdm.close() + self._tqdm = None + + +total_tqdm = TotalTQDM() + +mem_mon = modules.memmon.MemUsageMonitor("MemMon", device, opts) +mem_mon.start() + + +def listfiles(dirname): + filenames = [os.path.join(dirname, x) for x in sorted(os.listdir(dirname)) if not x.startswith(".")] + return [file for file in filenames if os.path.isfile(file)] + + +def html_path(filename): + return os.path.join(script_path, "html", filename) + + +def html(filename): + path = html_path(filename) + + if os.path.exists(path): + with open(path, encoding="utf8") as file: + return file.read() + + return "" diff --git a/sd/stable-diffusion-webui/modules/shared_items.py b/sd/stable-diffusion-webui/modules/shared_items.py new file mode 100644 index 0000000000000000000000000000000000000000..8dd832ed9b1e610b2ab1b4d5f911c58d63c00f80 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/shared_items.py @@ -0,0 +1,23 @@ + + +def realesrgan_models_names(): + import modules.realesrgan_model + return [x.name for x in modules.realesrgan_model.get_realesrgan_models(None)] + + +def postprocessing_scripts(): + import modules.scripts + + return modules.scripts.scripts_postproc.scripts + + +def sd_vae_items(): + import modules.sd_vae + + return ["Automatic", "None"] + list(modules.sd_vae.vae_dict) + + +def refresh_vae_list(): + import modules.sd_vae + + modules.sd_vae.refresh_vae_list() diff --git a/sd/stable-diffusion-webui/modules/styles.py b/sd/stable-diffusion-webui/modules/styles.py new file mode 100644 index 0000000000000000000000000000000000000000..d635c0109a1afd8867ef29b2d66ad864e1658113 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/styles.py @@ -0,0 +1,87 @@ +# We need this so Python doesn't complain about the unknown StableDiffusionProcessing-typehint at runtime +from __future__ import annotations + +import csv +import os +import os.path +import typing +import collections.abc as abc +import tempfile +import shutil + +if typing.TYPE_CHECKING: + # Only import this when code is being type-checked, it doesn't have any effect at runtime + from .processing import StableDiffusionProcessing + + +class PromptStyle(typing.NamedTuple): + name: str + prompt: str + negative_prompt: str + + +def merge_prompts(style_prompt: str, prompt: str) -> str: + if "{prompt}" in style_prompt: + res = style_prompt.replace("{prompt}", prompt) + else: + parts = filter(None, (prompt.strip(), style_prompt.strip())) + res = ", ".join(parts) + + return res + + +def apply_styles_to_prompt(prompt, styles): + for style in styles: + prompt = merge_prompts(style, prompt) + + return prompt + + +class StyleDatabase: + def __init__(self, path: str): + self.no_style = PromptStyle("None", "", "") + self.styles = {} + self.path = path + + self.reload() + + def reload(self): + self.styles.clear() + + if not os.path.exists(self.path): + return + + with open(self.path, "r", encoding="utf-8-sig", newline='') as file: + reader = csv.DictReader(file) + for row in reader: + # Support loading old CSV format with "name, text"-columns + prompt = row["prompt"] if "prompt" in row else row["text"] + negative_prompt = row.get("negative_prompt", "") + self.styles[row["name"]] = PromptStyle(row["name"], prompt, negative_prompt) + + def get_style_prompts(self, styles): + return [self.styles.get(x, self.no_style).prompt for x in styles] + + def get_negative_style_prompts(self, styles): + return [self.styles.get(x, self.no_style).negative_prompt for x in styles] + + def apply_styles_to_prompt(self, prompt, styles): + return apply_styles_to_prompt(prompt, [self.styles.get(x, self.no_style).prompt for x in styles]) + + def apply_negative_styles_to_prompt(self, prompt, styles): + return apply_styles_to_prompt(prompt, [self.styles.get(x, self.no_style).negative_prompt for x in styles]) + + def save_styles(self, path: str) -> None: + # Write to temporary file first, so we don't nuke the file if something goes wrong + fd, temp_path = tempfile.mkstemp(".csv") + with os.fdopen(fd, "w", encoding="utf-8-sig", newline='') as file: + # _fields is actually part of the public API: typing.NamedTuple is a replacement for collections.NamedTuple, + # and collections.NamedTuple has explicit documentation for accessing _fields. Same goes for _asdict() + writer = csv.DictWriter(file, fieldnames=PromptStyle._fields) + writer.writeheader() + writer.writerows(style._asdict() for k, style in self.styles.items()) + + # Always keep a backup file around + if os.path.exists(path): + shutil.move(path, path + ".bak") + shutil.move(temp_path, path) diff --git a/sd/stable-diffusion-webui/modules/sub_quadratic_attention.py b/sd/stable-diffusion-webui/modules/sub_quadratic_attention.py new file mode 100644 index 0000000000000000000000000000000000000000..055953236e5e1d1401feca93ca6a1cc342cb8595 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/sub_quadratic_attention.py @@ -0,0 +1,214 @@ +# original source: +# https://github.com/AminRezaei0x443/memory-efficient-attention/blob/1bc0d9e6ac5f82ea43a375135c4e1d3896ee1694/memory_efficient_attention/attention_torch.py +# license: +# MIT License (see Memory Efficient Attention under the Licenses section in the web UI interface for the full license) +# credit: +# Amin Rezaei (original author) +# Alex Birch (optimized algorithm for 3D tensors, at the expense of removing bias, masking and callbacks) +# brkirch (modified to use torch.narrow instead of dynamic_slice implementation) +# implementation of: +# Self-attention Does Not Need O(n2) Memory": +# https://arxiv.org/abs/2112.05682v2 + +from functools import partial +import torch +from torch import Tensor +from torch.utils.checkpoint import checkpoint +import math +from typing import Optional, NamedTuple, List + + +def narrow_trunc( + input: Tensor, + dim: int, + start: int, + length: int +) -> Tensor: + return torch.narrow(input, dim, start, length if input.shape[dim] >= start + length else input.shape[dim] - start) + + +class AttnChunk(NamedTuple): + exp_values: Tensor + exp_weights_sum: Tensor + max_score: Tensor + + +class SummarizeChunk: + @staticmethod + def __call__( + query: Tensor, + key: Tensor, + value: Tensor, + ) -> AttnChunk: ... + + +class ComputeQueryChunkAttn: + @staticmethod + def __call__( + query: Tensor, + key: Tensor, + value: Tensor, + ) -> Tensor: ... + + +def _summarize_chunk( + query: Tensor, + key: Tensor, + value: Tensor, + scale: float, +) -> AttnChunk: + attn_weights = torch.baddbmm( + torch.empty(1, 1, 1, device=query.device, dtype=query.dtype), + query, + key.transpose(1,2), + alpha=scale, + beta=0, + ) + max_score, _ = torch.max(attn_weights, -1, keepdim=True) + max_score = max_score.detach() + exp_weights = torch.exp(attn_weights - max_score) + exp_values = torch.bmm(exp_weights, value) if query.device.type == 'mps' else torch.bmm(exp_weights, value.to(exp_weights.dtype)).to(value.dtype) + max_score = max_score.squeeze(-1) + return AttnChunk(exp_values, exp_weights.sum(dim=-1), max_score) + + +def _query_chunk_attention( + query: Tensor, + key: Tensor, + value: Tensor, + summarize_chunk: SummarizeChunk, + kv_chunk_size: int, +) -> Tensor: + batch_x_heads, k_tokens, k_channels_per_head = key.shape + _, _, v_channels_per_head = value.shape + + def chunk_scanner(chunk_idx: int) -> AttnChunk: + key_chunk = narrow_trunc( + key, + 1, + chunk_idx, + kv_chunk_size + ) + value_chunk = narrow_trunc( + value, + 1, + chunk_idx, + kv_chunk_size + ) + return summarize_chunk(query, key_chunk, value_chunk) + + chunks: List[AttnChunk] = [ + chunk_scanner(chunk) for chunk in torch.arange(0, k_tokens, kv_chunk_size) + ] + acc_chunk = AttnChunk(*map(torch.stack, zip(*chunks))) + chunk_values, chunk_weights, chunk_max = acc_chunk + + global_max, _ = torch.max(chunk_max, 0, keepdim=True) + max_diffs = torch.exp(chunk_max - global_max) + chunk_values *= torch.unsqueeze(max_diffs, -1) + chunk_weights *= max_diffs + + all_values = chunk_values.sum(dim=0) + all_weights = torch.unsqueeze(chunk_weights, -1).sum(dim=0) + return all_values / all_weights + + +# TODO: refactor CrossAttention#get_attention_scores to share code with this +def _get_attention_scores_no_kv_chunking( + query: Tensor, + key: Tensor, + value: Tensor, + scale: float, +) -> Tensor: + attn_scores = torch.baddbmm( + torch.empty(1, 1, 1, device=query.device, dtype=query.dtype), + query, + key.transpose(1,2), + alpha=scale, + beta=0, + ) + attn_probs = attn_scores.softmax(dim=-1) + del attn_scores + hidden_states_slice = torch.bmm(attn_probs, value) if query.device.type == 'mps' else torch.bmm(attn_probs, value.to(attn_probs.dtype)).to(value.dtype) + return hidden_states_slice + + +class ScannedChunk(NamedTuple): + chunk_idx: int + attn_chunk: AttnChunk + + +def efficient_dot_product_attention( + query: Tensor, + key: Tensor, + value: Tensor, + query_chunk_size=1024, + kv_chunk_size: Optional[int] = None, + kv_chunk_size_min: Optional[int] = None, + use_checkpoint=True, +): + """Computes efficient dot-product attention given query, key, and value. + This is efficient version of attention presented in + https://arxiv.org/abs/2112.05682v2 which comes with O(sqrt(n)) memory requirements. + Args: + query: queries for calculating attention with shape of + `[batch * num_heads, tokens, channels_per_head]`. + key: keys for calculating attention with shape of + `[batch * num_heads, tokens, channels_per_head]`. + value: values to be used in attention with shape of + `[batch * num_heads, tokens, channels_per_head]`. + query_chunk_size: int: query chunks size + kv_chunk_size: Optional[int]: key/value chunks size. if None: defaults to sqrt(key_tokens) + kv_chunk_size_min: Optional[int]: key/value minimum chunk size. only considered when kv_chunk_size is None. changes `sqrt(key_tokens)` into `max(sqrt(key_tokens), kv_chunk_size_min)`, to ensure our chunk sizes don't get too small (smaller chunks = more chunks = less concurrent work done). + use_checkpoint: bool: whether to use checkpointing (recommended True for training, False for inference) + Returns: + Output of shape `[batch * num_heads, query_tokens, channels_per_head]`. + """ + batch_x_heads, q_tokens, q_channels_per_head = query.shape + _, k_tokens, _ = key.shape + scale = q_channels_per_head ** -0.5 + + kv_chunk_size = min(kv_chunk_size or int(math.sqrt(k_tokens)), k_tokens) + if kv_chunk_size_min is not None: + kv_chunk_size = max(kv_chunk_size, kv_chunk_size_min) + + def get_query_chunk(chunk_idx: int) -> Tensor: + return narrow_trunc( + query, + 1, + chunk_idx, + min(query_chunk_size, q_tokens) + ) + + summarize_chunk: SummarizeChunk = partial(_summarize_chunk, scale=scale) + summarize_chunk: SummarizeChunk = partial(checkpoint, summarize_chunk) if use_checkpoint else summarize_chunk + compute_query_chunk_attn: ComputeQueryChunkAttn = partial( + _get_attention_scores_no_kv_chunking, + scale=scale + ) if k_tokens <= kv_chunk_size else ( + # fast-path for when there's just 1 key-value chunk per query chunk (this is just sliced attention btw) + partial( + _query_chunk_attention, + kv_chunk_size=kv_chunk_size, + summarize_chunk=summarize_chunk, + ) + ) + + if q_tokens <= query_chunk_size: + # fast-path for when there's just 1 query chunk + return compute_query_chunk_attn( + query=query, + key=key, + value=value, + ) + + # TODO: maybe we should use torch.empty_like(query) to allocate storage in-advance, + # and pass slices to be mutated, instead of torch.cat()ing the returned slices + res = torch.cat([ + compute_query_chunk_attn( + query=get_query_chunk(i * query_chunk_size), + key=key, + value=value, + ) for i in range(math.ceil(q_tokens / query_chunk_size)) + ], dim=1) + return res diff --git a/sd/stable-diffusion-webui/modules/textual_inversion/autocrop.py b/sd/stable-diffusion-webui/modules/textual_inversion/autocrop.py new file mode 100644 index 0000000000000000000000000000000000000000..1486728775c35b15037f4fa8a3b41cd4806b7b28 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/textual_inversion/autocrop.py @@ -0,0 +1,341 @@ +import cv2 +import requests +import os +from collections import defaultdict +from math import log, sqrt +import numpy as np +from PIL import Image, ImageDraw + +GREEN = "#0F0" +BLUE = "#00F" +RED = "#F00" + + +def crop_image(im, settings): + """ Intelligently crop an image to the subject matter """ + + scale_by = 1 + if is_landscape(im.width, im.height): + scale_by = settings.crop_height / im.height + elif is_portrait(im.width, im.height): + scale_by = settings.crop_width / im.width + elif is_square(im.width, im.height): + if is_square(settings.crop_width, settings.crop_height): + scale_by = settings.crop_width / im.width + elif is_landscape(settings.crop_width, settings.crop_height): + scale_by = settings.crop_width / im.width + elif is_portrait(settings.crop_width, settings.crop_height): + scale_by = settings.crop_height / im.height + + im = im.resize((int(im.width * scale_by), int(im.height * scale_by))) + im_debug = im.copy() + + focus = focal_point(im_debug, settings) + + # take the focal point and turn it into crop coordinates that try to center over the focal + # point but then get adjusted back into the frame + y_half = int(settings.crop_height / 2) + x_half = int(settings.crop_width / 2) + + x1 = focus.x - x_half + if x1 < 0: + x1 = 0 + elif x1 + settings.crop_width > im.width: + x1 = im.width - settings.crop_width + + y1 = focus.y - y_half + if y1 < 0: + y1 = 0 + elif y1 + settings.crop_height > im.height: + y1 = im.height - settings.crop_height + + x2 = x1 + settings.crop_width + y2 = y1 + settings.crop_height + + crop = [x1, y1, x2, y2] + + results = [] + + results.append(im.crop(tuple(crop))) + + if settings.annotate_image: + d = ImageDraw.Draw(im_debug) + rect = list(crop) + rect[2] -= 1 + rect[3] -= 1 + d.rectangle(rect, outline=GREEN) + results.append(im_debug) + if settings.destop_view_image: + im_debug.show() + + return results + +def focal_point(im, settings): + corner_points = image_corner_points(im, settings) if settings.corner_points_weight > 0 else [] + entropy_points = image_entropy_points(im, settings) if settings.entropy_points_weight > 0 else [] + face_points = image_face_points(im, settings) if settings.face_points_weight > 0 else [] + + pois = [] + + weight_pref_total = 0 + if len(corner_points) > 0: + weight_pref_total += settings.corner_points_weight + if len(entropy_points) > 0: + weight_pref_total += settings.entropy_points_weight + if len(face_points) > 0: + weight_pref_total += settings.face_points_weight + + corner_centroid = None + if len(corner_points) > 0: + corner_centroid = centroid(corner_points) + corner_centroid.weight = settings.corner_points_weight / weight_pref_total + pois.append(corner_centroid) + + entropy_centroid = None + if len(entropy_points) > 0: + entropy_centroid = centroid(entropy_points) + entropy_centroid.weight = settings.entropy_points_weight / weight_pref_total + pois.append(entropy_centroid) + + face_centroid = None + if len(face_points) > 0: + face_centroid = centroid(face_points) + face_centroid.weight = settings.face_points_weight / weight_pref_total + pois.append(face_centroid) + + average_point = poi_average(pois, settings) + + if settings.annotate_image: + d = ImageDraw.Draw(im) + max_size = min(im.width, im.height) * 0.07 + if corner_centroid is not None: + color = BLUE + box = corner_centroid.bounding(max_size * corner_centroid.weight) + d.text((box[0], box[1]-15), "Edge: %.02f" % corner_centroid.weight, fill=color) + d.ellipse(box, outline=color) + if len(corner_points) > 1: + for f in corner_points: + d.rectangle(f.bounding(4), outline=color) + if entropy_centroid is not None: + color = "#ff0" + box = entropy_centroid.bounding(max_size * entropy_centroid.weight) + d.text((box[0], box[1]-15), "Entropy: %.02f" % entropy_centroid.weight, fill=color) + d.ellipse(box, outline=color) + if len(entropy_points) > 1: + for f in entropy_points: + d.rectangle(f.bounding(4), outline=color) + if face_centroid is not None: + color = RED + box = face_centroid.bounding(max_size * face_centroid.weight) + d.text((box[0], box[1]-15), "Face: %.02f" % face_centroid.weight, fill=color) + d.ellipse(box, outline=color) + if len(face_points) > 1: + for f in face_points: + d.rectangle(f.bounding(4), outline=color) + + d.ellipse(average_point.bounding(max_size), outline=GREEN) + + return average_point + + +def image_face_points(im, settings): + if settings.dnn_model_path is not None: + detector = cv2.FaceDetectorYN.create( + settings.dnn_model_path, + "", + (im.width, im.height), + 0.9, # score threshold + 0.3, # nms threshold + 5000 # keep top k before nms + ) + faces = detector.detect(np.array(im)) + results = [] + if faces[1] is not None: + for face in faces[1]: + x = face[0] + y = face[1] + w = face[2] + h = face[3] + results.append( + PointOfInterest( + int(x + (w * 0.5)), # face focus left/right is center + int(y + (h * 0.33)), # face focus up/down is close to the top of the head + size = w, + weight = 1/len(faces[1]) + ) + ) + return results + else: + np_im = np.array(im) + gray = cv2.cvtColor(np_im, cv2.COLOR_BGR2GRAY) + + tries = [ + [ f'{cv2.data.haarcascades}haarcascade_eye.xml', 0.01 ], + [ f'{cv2.data.haarcascades}haarcascade_frontalface_default.xml', 0.05 ], + [ f'{cv2.data.haarcascades}haarcascade_profileface.xml', 0.05 ], + [ f'{cv2.data.haarcascades}haarcascade_frontalface_alt.xml', 0.05 ], + [ f'{cv2.data.haarcascades}haarcascade_frontalface_alt2.xml', 0.05 ], + [ f'{cv2.data.haarcascades}haarcascade_frontalface_alt_tree.xml', 0.05 ], + [ f'{cv2.data.haarcascades}haarcascade_eye_tree_eyeglasses.xml', 0.05 ], + [ f'{cv2.data.haarcascades}haarcascade_upperbody.xml', 0.05 ] + ] + for t in tries: + classifier = cv2.CascadeClassifier(t[0]) + minsize = int(min(im.width, im.height) * t[1]) # at least N percent of the smallest side + try: + faces = classifier.detectMultiScale(gray, scaleFactor=1.1, + minNeighbors=7, minSize=(minsize, minsize), flags=cv2.CASCADE_SCALE_IMAGE) + except: + continue + + if len(faces) > 0: + rects = [[f[0], f[1], f[0] + f[2], f[1] + f[3]] for f in faces] + return [PointOfInterest((r[0] +r[2]) // 2, (r[1] + r[3]) // 2, size=abs(r[0]-r[2]), weight=1/len(rects)) for r in rects] + return [] + + +def image_corner_points(im, settings): + grayscale = im.convert("L") + + # naive attempt at preventing focal points from collecting at watermarks near the bottom + gd = ImageDraw.Draw(grayscale) + gd.rectangle([0, im.height*.9, im.width, im.height], fill="#999") + + np_im = np.array(grayscale) + + points = cv2.goodFeaturesToTrack( + np_im, + maxCorners=100, + qualityLevel=0.04, + minDistance=min(grayscale.width, grayscale.height)*0.06, + useHarrisDetector=False, + ) + + if points is None: + return [] + + focal_points = [] + for point in points: + x, y = point.ravel() + focal_points.append(PointOfInterest(x, y, size=4, weight=1/len(points))) + + return focal_points + + +def image_entropy_points(im, settings): + landscape = im.height < im.width + portrait = im.height > im.width + if landscape: + move_idx = [0, 2] + move_max = im.size[0] + elif portrait: + move_idx = [1, 3] + move_max = im.size[1] + else: + return [] + + e_max = 0 + crop_current = [0, 0, settings.crop_width, settings.crop_height] + crop_best = crop_current + while crop_current[move_idx[1]] < move_max: + crop = im.crop(tuple(crop_current)) + e = image_entropy(crop) + + if (e > e_max): + e_max = e + crop_best = list(crop_current) + + crop_current[move_idx[0]] += 4 + crop_current[move_idx[1]] += 4 + + x_mid = int(crop_best[0] + settings.crop_width/2) + y_mid = int(crop_best[1] + settings.crop_height/2) + + return [PointOfInterest(x_mid, y_mid, size=25, weight=1.0)] + + +def image_entropy(im): + # greyscale image entropy + # band = np.asarray(im.convert("L")) + band = np.asarray(im.convert("1"), dtype=np.uint8) + hist, _ = np.histogram(band, bins=range(0, 256)) + hist = hist[hist > 0] + return -np.log2(hist / hist.sum()).sum() + +def centroid(pois): + x = [poi.x for poi in pois] + y = [poi.y for poi in pois] + return PointOfInterest(sum(x)/len(pois), sum(y)/len(pois)) + + +def poi_average(pois, settings): + weight = 0.0 + x = 0.0 + y = 0.0 + for poi in pois: + weight += poi.weight + x += poi.x * poi.weight + y += poi.y * poi.weight + avg_x = round(weight and x / weight) + avg_y = round(weight and y / weight) + + return PointOfInterest(avg_x, avg_y) + + +def is_landscape(w, h): + return w > h + + +def is_portrait(w, h): + return h > w + + +def is_square(w, h): + return w == h + + +def download_and_cache_models(dirname): + download_url = 'https://github.com/opencv/opencv_zoo/blob/91fb0290f50896f38a0ab1e558b74b16bc009428/models/face_detection_yunet/face_detection_yunet_2022mar.onnx?raw=true' + model_file_name = 'face_detection_yunet.onnx' + + if not os.path.exists(dirname): + os.makedirs(dirname) + + cache_file = os.path.join(dirname, model_file_name) + if not os.path.exists(cache_file): + print(f"downloading face detection model from '{download_url}' to '{cache_file}'") + response = requests.get(download_url) + with open(cache_file, "wb") as f: + f.write(response.content) + + if os.path.exists(cache_file): + return cache_file + return None + + +class PointOfInterest: + def __init__(self, x, y, weight=1.0, size=10): + self.x = x + self.y = y + self.weight = weight + self.size = size + + def bounding(self, size): + return [ + self.x - size//2, + self.y - size//2, + self.x + size//2, + self.y + size//2 + ] + + +class Settings: + def __init__(self, crop_width=512, crop_height=512, corner_points_weight=0.5, entropy_points_weight=0.5, face_points_weight=0.5, annotate_image=False, dnn_model_path=None): + self.crop_width = crop_width + self.crop_height = crop_height + self.corner_points_weight = corner_points_weight + self.entropy_points_weight = entropy_points_weight + self.face_points_weight = face_points_weight + self.annotate_image = annotate_image + self.destop_view_image = False + self.dnn_model_path = dnn_model_path diff --git a/sd/stable-diffusion-webui/modules/textual_inversion/dataset.py b/sd/stable-diffusion-webui/modules/textual_inversion/dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..093efcd4fc70602a802a82bc50457d0a5d08b5e2 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/textual_inversion/dataset.py @@ -0,0 +1,246 @@ +import os +import numpy as np +import PIL +import torch +from PIL import Image +from torch.utils.data import Dataset, DataLoader, Sampler +from torchvision import transforms +from collections import defaultdict +from random import shuffle, choices + +import random +import tqdm +from modules import devices, shared +import re + +from ldm.modules.distributions.distributions import DiagonalGaussianDistribution + +re_numbers_at_start = re.compile(r"^[-\d]+\s*") + + +class DatasetEntry: + def __init__(self, filename=None, filename_text=None, latent_dist=None, latent_sample=None, cond=None, cond_text=None, pixel_values=None, weight=None): + self.filename = filename + self.filename_text = filename_text + self.weight = weight + self.latent_dist = latent_dist + self.latent_sample = latent_sample + self.cond = cond + self.cond_text = cond_text + self.pixel_values = pixel_values + + +class PersonalizedBase(Dataset): + def __init__(self, data_root, width, height, repeats, flip_p=0.5, placeholder_token="*", model=None, cond_model=None, device=None, template_file=None, include_cond=False, batch_size=1, gradient_step=1, shuffle_tags=False, tag_drop_out=0, latent_sampling_method='once', varsize=False, use_weight=False): + re_word = re.compile(shared.opts.dataset_filename_word_regex) if len(shared.opts.dataset_filename_word_regex) > 0 else None + + self.placeholder_token = placeholder_token + + self.flip = transforms.RandomHorizontalFlip(p=flip_p) + + self.dataset = [] + + with open(template_file, "r") as file: + lines = [x.strip() for x in file.readlines()] + + self.lines = lines + + assert data_root, 'dataset directory not specified' + assert os.path.isdir(data_root), "Dataset directory doesn't exist" + assert os.listdir(data_root), "Dataset directory is empty" + + self.image_paths = [os.path.join(data_root, file_path) for file_path in os.listdir(data_root)] + + self.shuffle_tags = shuffle_tags + self.tag_drop_out = tag_drop_out + groups = defaultdict(list) + + print("Preparing dataset...") + for path in tqdm.tqdm(self.image_paths): + alpha_channel = None + if shared.state.interrupted: + raise Exception("interrupted") + try: + image = Image.open(path) + #Currently does not work for single color transparency + #We would need to read image.info['transparency'] for that + if use_weight and 'A' in image.getbands(): + alpha_channel = image.getchannel('A') + image = image.convert('RGB') + if not varsize: + image = image.resize((width, height), PIL.Image.BICUBIC) + except Exception: + continue + + text_filename = os.path.splitext(path)[0] + ".txt" + filename = os.path.basename(path) + + if os.path.exists(text_filename): + with open(text_filename, "r", encoding="utf8") as file: + filename_text = file.read() + else: + filename_text = os.path.splitext(filename)[0] + filename_text = re.sub(re_numbers_at_start, '', filename_text) + if re_word: + tokens = re_word.findall(filename_text) + filename_text = (shared.opts.dataset_filename_join_string or "").join(tokens) + + npimage = np.array(image).astype(np.uint8) + npimage = (npimage / 127.5 - 1.0).astype(np.float32) + + torchdata = torch.from_numpy(npimage).permute(2, 0, 1).to(device=device, dtype=torch.float32) + latent_sample = None + + with devices.autocast(): + latent_dist = model.encode_first_stage(torchdata.unsqueeze(dim=0)) + + #Perform latent sampling, even for random sampling. + #We need the sample dimensions for the weights + if latent_sampling_method == "deterministic": + if isinstance(latent_dist, DiagonalGaussianDistribution): + # Works only for DiagonalGaussianDistribution + latent_dist.std = 0 + else: + latent_sampling_method = "once" + latent_sample = model.get_first_stage_encoding(latent_dist).squeeze().to(devices.cpu) + + if use_weight and alpha_channel is not None: + channels, *latent_size = latent_sample.shape + weight_img = alpha_channel.resize(latent_size) + npweight = np.array(weight_img).astype(np.float32) + #Repeat for every channel in the latent sample + weight = torch.tensor([npweight] * channels).reshape([channels] + latent_size) + #Normalize the weight to a minimum of 0 and a mean of 1, that way the loss will be comparable to default. + weight -= weight.min() + weight /= weight.mean() + elif use_weight: + #If an image does not have a alpha channel, add a ones weight map anyway so we can stack it later + weight = torch.ones(latent_sample.shape) + else: + weight = None + + if latent_sampling_method == "random": + entry = DatasetEntry(filename=path, filename_text=filename_text, latent_dist=latent_dist, weight=weight) + else: + entry = DatasetEntry(filename=path, filename_text=filename_text, latent_sample=latent_sample, weight=weight) + + if not (self.tag_drop_out != 0 or self.shuffle_tags): + entry.cond_text = self.create_text(filename_text) + + if include_cond and not (self.tag_drop_out != 0 or self.shuffle_tags): + with devices.autocast(): + entry.cond = cond_model([entry.cond_text]).to(devices.cpu).squeeze(0) + groups[image.size].append(len(self.dataset)) + self.dataset.append(entry) + del torchdata + del latent_dist + del latent_sample + del weight + + self.length = len(self.dataset) + self.groups = list(groups.values()) + assert self.length > 0, "No images have been found in the dataset." + self.batch_size = min(batch_size, self.length) + self.gradient_step = min(gradient_step, self.length // self.batch_size) + self.latent_sampling_method = latent_sampling_method + + if len(groups) > 1: + print("Buckets:") + for (w, h), ids in sorted(groups.items(), key=lambda x: x[0]): + print(f" {w}x{h}: {len(ids)}") + print() + + def create_text(self, filename_text): + text = random.choice(self.lines) + tags = filename_text.split(',') + if self.tag_drop_out != 0: + tags = [t for t in tags if random.random() > self.tag_drop_out] + if self.shuffle_tags: + random.shuffle(tags) + text = text.replace("[filewords]", ','.join(tags)) + text = text.replace("[name]", self.placeholder_token) + return text + + def __len__(self): + return self.length + + def __getitem__(self, i): + entry = self.dataset[i] + if self.tag_drop_out != 0 or self.shuffle_tags: + entry.cond_text = self.create_text(entry.filename_text) + if self.latent_sampling_method == "random": + entry.latent_sample = shared.sd_model.get_first_stage_encoding(entry.latent_dist).to(devices.cpu) + return entry + + +class GroupedBatchSampler(Sampler): + def __init__(self, data_source: PersonalizedBase, batch_size: int): + super().__init__(data_source) + + n = len(data_source) + self.groups = data_source.groups + self.len = n_batch = n // batch_size + expected = [len(g) / n * n_batch * batch_size for g in data_source.groups] + self.base = [int(e) // batch_size for e in expected] + self.n_rand_batches = nrb = n_batch - sum(self.base) + self.probs = [e%batch_size/nrb/batch_size if nrb>0 else 0 for e in expected] + self.batch_size = batch_size + + def __len__(self): + return self.len + + def __iter__(self): + b = self.batch_size + + for g in self.groups: + shuffle(g) + + batches = [] + for g in self.groups: + batches.extend(g[i*b:(i+1)*b] for i in range(len(g) // b)) + for _ in range(self.n_rand_batches): + rand_group = choices(self.groups, self.probs)[0] + batches.append(choices(rand_group, k=b)) + + shuffle(batches) + + yield from batches + + +class PersonalizedDataLoader(DataLoader): + def __init__(self, dataset, latent_sampling_method="once", batch_size=1, pin_memory=False): + super(PersonalizedDataLoader, self).__init__(dataset, batch_sampler=GroupedBatchSampler(dataset, batch_size), pin_memory=pin_memory) + if latent_sampling_method == "random": + self.collate_fn = collate_wrapper_random + else: + self.collate_fn = collate_wrapper + + +class BatchLoader: + def __init__(self, data): + self.cond_text = [entry.cond_text for entry in data] + self.cond = [entry.cond for entry in data] + self.latent_sample = torch.stack([entry.latent_sample for entry in data]).squeeze(1) + if all(entry.weight is not None for entry in data): + self.weight = torch.stack([entry.weight for entry in data]).squeeze(1) + else: + self.weight = None + #self.emb_index = [entry.emb_index for entry in data] + #print(self.latent_sample.device) + + def pin_memory(self): + self.latent_sample = self.latent_sample.pin_memory() + return self + +def collate_wrapper(batch): + return BatchLoader(batch) + +class BatchLoaderRandom(BatchLoader): + def __init__(self, data): + super().__init__(data) + + def pin_memory(self): + return self + +def collate_wrapper_random(batch): + return BatchLoaderRandom(batch) \ No newline at end of file diff --git a/sd/stable-diffusion-webui/modules/textual_inversion/image_embedding.py b/sd/stable-diffusion-webui/modules/textual_inversion/image_embedding.py new file mode 100644 index 0000000000000000000000000000000000000000..660ca62038594937f5fc3b29f50546a75a4af588 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/textual_inversion/image_embedding.py @@ -0,0 +1,220 @@ +import base64 +import json +import numpy as np +import zlib +from PIL import Image, PngImagePlugin, ImageDraw, ImageFont +from fonts.ttf import Roboto +import torch +from modules.shared import opts + + +class EmbeddingEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, torch.Tensor): + return {'TORCHTENSOR': obj.cpu().detach().numpy().tolist()} + return json.JSONEncoder.default(self, obj) + + +class EmbeddingDecoder(json.JSONDecoder): + def __init__(self, *args, **kwargs): + json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs) + + def object_hook(self, d): + if 'TORCHTENSOR' in d: + return torch.from_numpy(np.array(d['TORCHTENSOR'])) + return d + + +def embedding_to_b64(data): + d = json.dumps(data, cls=EmbeddingEncoder) + return base64.b64encode(d.encode()) + + +def embedding_from_b64(data): + d = base64.b64decode(data) + return json.loads(d, cls=EmbeddingDecoder) + + +def lcg(m=2**32, a=1664525, c=1013904223, seed=0): + while True: + seed = (a * seed + c) % m + yield seed % 255 + + +def xor_block(block): + g = lcg() + randblock = np.array([next(g) for _ in range(np.product(block.shape))]).astype(np.uint8).reshape(block.shape) + return np.bitwise_xor(block.astype(np.uint8), randblock & 0x0F) + + +def style_block(block, sequence): + im = Image.new('RGB', (block.shape[1], block.shape[0])) + draw = ImageDraw.Draw(im) + i = 0 + for x in range(-6, im.size[0], 8): + for yi, y in enumerate(range(-6, im.size[1], 8)): + offset = 0 + if yi % 2 == 0: + offset = 4 + shade = sequence[i % len(sequence)] + i += 1 + draw.ellipse((x+offset, y, x+6+offset, y+6), fill=(shade, shade, shade)) + + fg = np.array(im).astype(np.uint8) & 0xF0 + + return block ^ fg + + +def insert_image_data_embed(image, data): + d = 3 + data_compressed = zlib.compress(json.dumps(data, cls=EmbeddingEncoder).encode(), level=9) + data_np_ = np.frombuffer(data_compressed, np.uint8).copy() + data_np_high = data_np_ >> 4 + data_np_low = data_np_ & 0x0F + + h = image.size[1] + next_size = data_np_low.shape[0] + (h-(data_np_low.shape[0] % h)) + next_size = next_size + ((h*d)-(next_size % (h*d))) + + data_np_low = np.resize(data_np_low, next_size) + data_np_low = data_np_low.reshape((h, -1, d)) + + data_np_high = np.resize(data_np_high, next_size) + data_np_high = data_np_high.reshape((h, -1, d)) + + edge_style = list(data['string_to_param'].values())[0].cpu().detach().numpy().tolist()[0][:1024] + edge_style = (np.abs(edge_style)/np.max(np.abs(edge_style))*255).astype(np.uint8) + + data_np_low = style_block(data_np_low, sequence=edge_style) + data_np_low = xor_block(data_np_low) + data_np_high = style_block(data_np_high, sequence=edge_style[::-1]) + data_np_high = xor_block(data_np_high) + + im_low = Image.fromarray(data_np_low, mode='RGB') + im_high = Image.fromarray(data_np_high, mode='RGB') + + background = Image.new('RGB', (image.size[0]+im_low.size[0]+im_high.size[0]+2, image.size[1]), (0, 0, 0)) + background.paste(im_low, (0, 0)) + background.paste(image, (im_low.size[0]+1, 0)) + background.paste(im_high, (im_low.size[0]+1+image.size[0]+1, 0)) + + return background + + +def crop_black(img, tol=0): + mask = (img > tol).all(2) + mask0, mask1 = mask.any(0), mask.any(1) + col_start, col_end = mask0.argmax(), mask.shape[1]-mask0[::-1].argmax() + row_start, row_end = mask1.argmax(), mask.shape[0]-mask1[::-1].argmax() + return img[row_start:row_end, col_start:col_end] + + +def extract_image_data_embed(image): + d = 3 + outarr = crop_black(np.array(image.convert('RGB').getdata()).reshape(image.size[1], image.size[0], d).astype(np.uint8)) & 0x0F + black_cols = np.where(np.sum(outarr, axis=(0, 2)) == 0) + if black_cols[0].shape[0] < 2: + print('No Image data blocks found.') + return None + + data_block_lower = outarr[:, :black_cols[0].min(), :].astype(np.uint8) + data_block_upper = outarr[:, black_cols[0].max()+1:, :].astype(np.uint8) + + data_block_lower = xor_block(data_block_lower) + data_block_upper = xor_block(data_block_upper) + + data_block = (data_block_upper << 4) | (data_block_lower) + data_block = data_block.flatten().tobytes() + + data = zlib.decompress(data_block) + return json.loads(data, cls=EmbeddingDecoder) + + +def caption_image_overlay(srcimage, title, footerLeft, footerMid, footerRight, textfont=None): + from math import cos + + image = srcimage.copy() + fontsize = 32 + if textfont is None: + try: + textfont = ImageFont.truetype(opts.font or Roboto, fontsize) + textfont = opts.font or Roboto + except Exception: + textfont = Roboto + + factor = 1.5 + gradient = Image.new('RGBA', (1, image.size[1]), color=(0, 0, 0, 0)) + for y in range(image.size[1]): + mag = 1-cos(y/image.size[1]*factor) + mag = max(mag, 1-cos((image.size[1]-y)/image.size[1]*factor*1.1)) + gradient.putpixel((0, y), (0, 0, 0, int(mag*255))) + image = Image.alpha_composite(image.convert('RGBA'), gradient.resize(image.size)) + + draw = ImageDraw.Draw(image) + + font = ImageFont.truetype(textfont, fontsize) + padding = 10 + + _, _, w, h = draw.textbbox((0, 0), title, font=font) + fontsize = min(int(fontsize * (((image.size[0]*0.75)-(padding*4))/w)), 72) + font = ImageFont.truetype(textfont, fontsize) + _, _, w, h = draw.textbbox((0, 0), title, font=font) + draw.text((padding, padding), title, anchor='lt', font=font, fill=(255, 255, 255, 230)) + + _, _, w, h = draw.textbbox((0, 0), footerLeft, font=font) + fontsize_left = min(int(fontsize * (((image.size[0]/3)-(padding))/w)), 72) + _, _, w, h = draw.textbbox((0, 0), footerMid, font=font) + fontsize_mid = min(int(fontsize * (((image.size[0]/3)-(padding))/w)), 72) + _, _, w, h = draw.textbbox((0, 0), footerRight, font=font) + fontsize_right = min(int(fontsize * (((image.size[0]/3)-(padding))/w)), 72) + + font = ImageFont.truetype(textfont, min(fontsize_left, fontsize_mid, fontsize_right)) + + draw.text((padding, image.size[1]-padding), footerLeft, anchor='ls', font=font, fill=(255, 255, 255, 230)) + draw.text((image.size[0]/2, image.size[1]-padding), footerMid, anchor='ms', font=font, fill=(255, 255, 255, 230)) + draw.text((image.size[0]-padding, image.size[1]-padding), footerRight, anchor='rs', font=font, fill=(255, 255, 255, 230)) + + return image + + +if __name__ == '__main__': + + testEmbed = Image.open('test_embedding.png') + data = extract_image_data_embed(testEmbed) + assert data is not None + + data = embedding_from_b64(testEmbed.text['sd-ti-embedding']) + assert data is not None + + image = Image.new('RGBA', (512, 512), (255, 255, 200, 255)) + cap_image = caption_image_overlay(image, 'title', 'footerLeft', 'footerMid', 'footerRight') + + test_embed = {'string_to_param': {'*': torch.from_numpy(np.random.random((2, 4096)))}} + + embedded_image = insert_image_data_embed(cap_image, test_embed) + + retrived_embed = extract_image_data_embed(embedded_image) + + assert str(retrived_embed) == str(test_embed) + + embedded_image2 = insert_image_data_embed(cap_image, retrived_embed) + + assert embedded_image == embedded_image2 + + g = lcg() + shared_random = np.array([next(g) for _ in range(100)]).astype(np.uint8).tolist() + + reference_random = [253, 242, 127, 44, 157, 27, 239, 133, 38, 79, 167, 4, 177, + 95, 130, 79, 78, 14, 52, 215, 220, 194, 126, 28, 240, 179, + 160, 153, 149, 50, 105, 14, 21, 218, 199, 18, 54, 198, 193, + 38, 128, 19, 53, 195, 124, 75, 205, 12, 6, 145, 0, 28, + 30, 148, 8, 45, 218, 171, 55, 249, 97, 166, 12, 35, 0, + 41, 221, 122, 215, 170, 31, 113, 186, 97, 119, 31, 23, 185, + 66, 140, 30, 41, 37, 63, 137, 109, 216, 55, 159, 145, 82, + 204, 86, 73, 222, 44, 198, 118, 240, 97] + + assert shared_random == reference_random + + hunna_kay_random_sum = sum(np.array([next(g) for _ in range(100000)]).astype(np.uint8).tolist()) + + assert 12731374 == hunna_kay_random_sum diff --git a/sd/stable-diffusion-webui/modules/textual_inversion/learn_schedule.py b/sd/stable-diffusion-webui/modules/textual_inversion/learn_schedule.py new file mode 100644 index 0000000000000000000000000000000000000000..f5156dd53e549570f53964781e564b7b92a1045a --- /dev/null +++ b/sd/stable-diffusion-webui/modules/textual_inversion/learn_schedule.py @@ -0,0 +1,81 @@ +import tqdm + + +class LearnScheduleIterator: + def __init__(self, learn_rate, max_steps, cur_step=0): + """ + specify learn_rate as "0.001:100, 0.00001:1000, 1e-5:10000" to have lr of 0.001 until step 100, 0.00001 until 1000, and 1e-5 until 10000 + """ + + pairs = learn_rate.split(',') + self.rates = [] + self.it = 0 + self.maxit = 0 + try: + for i, pair in enumerate(pairs): + if not pair.strip(): + continue + tmp = pair.split(':') + if len(tmp) == 2: + step = int(tmp[1]) + if step > cur_step: + self.rates.append((float(tmp[0]), min(step, max_steps))) + self.maxit += 1 + if step > max_steps: + return + elif step == -1: + self.rates.append((float(tmp[0]), max_steps)) + self.maxit += 1 + return + else: + self.rates.append((float(tmp[0]), max_steps)) + self.maxit += 1 + return + assert self.rates + except (ValueError, AssertionError): + raise Exception('Invalid learning rate schedule. It should be a number or, for example, like "0.001:100, 0.00001:1000, 1e-5:10000" to have lr of 0.001 until step 100, 0.00001 until 1000, and 1e-5 until 10000.') + + + def __iter__(self): + return self + + def __next__(self): + if self.it < self.maxit: + self.it += 1 + return self.rates[self.it - 1] + else: + raise StopIteration + + +class LearnRateScheduler: + def __init__(self, learn_rate, max_steps, cur_step=0, verbose=True): + self.schedules = LearnScheduleIterator(learn_rate, max_steps, cur_step) + (self.learn_rate, self.end_step) = next(self.schedules) + self.verbose = verbose + + if self.verbose: + print(f'Training at rate of {self.learn_rate} until step {self.end_step}') + + self.finished = False + + def step(self, step_number): + if step_number < self.end_step: + return False + + try: + (self.learn_rate, self.end_step) = next(self.schedules) + except StopIteration: + self.finished = True + return False + return True + + def apply(self, optimizer, step_number): + if not self.step(step_number): + return + + if self.verbose: + tqdm.tqdm.write(f'Training at rate of {self.learn_rate} until step {self.end_step}') + + for pg in optimizer.param_groups: + pg['lr'] = self.learn_rate + diff --git a/sd/stable-diffusion-webui/modules/textual_inversion/logging.py b/sd/stable-diffusion-webui/modules/textual_inversion/logging.py new file mode 100644 index 0000000000000000000000000000000000000000..b2c01f0a4ef6666c0c2e1147dbee9d6850d277c0 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/textual_inversion/logging.py @@ -0,0 +1,24 @@ +import datetime +import json +import os + +saved_params_shared = {"model_name", "model_hash", "initial_step", "num_of_dataset_images", "learn_rate", "batch_size", "clip_grad_mode", "clip_grad_value", "gradient_step", "data_root", "log_directory", "training_width", "training_height", "steps", "create_image_every", "template_file", "gradient_step", "latent_sampling_method"} +saved_params_ti = {"embedding_name", "num_vectors_per_token", "save_embedding_every", "save_image_with_stored_embedding"} +saved_params_hypernet = {"hypernetwork_name", "layer_structure", "activation_func", "weight_init", "add_layer_norm", "use_dropout", "save_hypernetwork_every"} +saved_params_all = saved_params_shared | saved_params_ti | saved_params_hypernet +saved_params_previews = {"preview_prompt", "preview_negative_prompt", "preview_steps", "preview_sampler_index", "preview_cfg_scale", "preview_seed", "preview_width", "preview_height"} + + +def save_settings_to_file(log_directory, all_params): + now = datetime.datetime.now() + params = {"datetime": now.strftime("%Y-%m-%d %H:%M:%S")} + + keys = saved_params_all + if all_params.get('preview_from_txt2img'): + keys = keys | saved_params_previews + + params.update({k: v for k, v in all_params.items() if k in keys}) + + filename = f'settings-{now.strftime("%Y-%m-%d-%H-%M-%S")}.json' + with open(os.path.join(log_directory, filename), "w") as file: + json.dump(params, file, indent=4) diff --git a/sd/stable-diffusion-webui/modules/textual_inversion/preprocess.py b/sd/stable-diffusion-webui/modules/textual_inversion/preprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..e1902115c97a076ace06e07f3a2e94085cb707cf --- /dev/null +++ b/sd/stable-diffusion-webui/modules/textual_inversion/preprocess.py @@ -0,0 +1,230 @@ +import os +from PIL import Image, ImageOps +import math +import platform +import sys +import tqdm +import time + +from modules import paths, shared, images, deepbooru +from modules.shared import opts, cmd_opts +from modules.textual_inversion import autocrop + + +def preprocess(id_task, process_src, process_dst, process_width, process_height, preprocess_txt_action, process_flip, process_split, process_caption, process_caption_deepbooru=False, split_threshold=0.5, overlap_ratio=0.2, process_focal_crop=False, process_focal_crop_face_weight=0.9, process_focal_crop_entropy_weight=0.3, process_focal_crop_edges_weight=0.5, process_focal_crop_debug=False, process_multicrop=None, process_multicrop_mindim=None, process_multicrop_maxdim=None, process_multicrop_minarea=None, process_multicrop_maxarea=None, process_multicrop_objective=None, process_multicrop_threshold=None): + try: + if process_caption: + shared.interrogator.load() + + if process_caption_deepbooru: + deepbooru.model.start() + + preprocess_work(process_src, process_dst, process_width, process_height, preprocess_txt_action, process_flip, process_split, process_caption, process_caption_deepbooru, split_threshold, overlap_ratio, process_focal_crop, process_focal_crop_face_weight, process_focal_crop_entropy_weight, process_focal_crop_edges_weight, process_focal_crop_debug, process_multicrop, process_multicrop_mindim, process_multicrop_maxdim, process_multicrop_minarea, process_multicrop_maxarea, process_multicrop_objective, process_multicrop_threshold) + + finally: + + if process_caption: + shared.interrogator.send_blip_to_ram() + + if process_caption_deepbooru: + deepbooru.model.stop() + + +def listfiles(dirname): + return os.listdir(dirname) + + +class PreprocessParams: + src = None + dstdir = None + subindex = 0 + flip = False + process_caption = False + process_caption_deepbooru = False + preprocess_txt_action = None + + +def save_pic_with_caption(image, index, params: PreprocessParams, existing_caption=None): + caption = "" + + if params.process_caption: + caption += shared.interrogator.generate_caption(image) + + if params.process_caption_deepbooru: + if len(caption) > 0: + caption += ", " + caption += deepbooru.model.tag_multi(image) + + filename_part = params.src + filename_part = os.path.splitext(filename_part)[0] + filename_part = os.path.basename(filename_part) + + basename = f"{index:05}-{params.subindex}-{filename_part}" + image.save(os.path.join(params.dstdir, f"{basename}.png")) + + if params.preprocess_txt_action == 'prepend' and existing_caption: + caption = existing_caption + ' ' + caption + elif params.preprocess_txt_action == 'append' and existing_caption: + caption = caption + ' ' + existing_caption + elif params.preprocess_txt_action == 'copy' and existing_caption: + caption = existing_caption + + caption = caption.strip() + + if len(caption) > 0: + with open(os.path.join(params.dstdir, f"{basename}.txt"), "w", encoding="utf8") as file: + file.write(caption) + + params.subindex += 1 + + +def save_pic(image, index, params, existing_caption=None): + save_pic_with_caption(image, index, params, existing_caption=existing_caption) + + if params.flip: + save_pic_with_caption(ImageOps.mirror(image), index, params, existing_caption=existing_caption) + + +def split_pic(image, inverse_xy, width, height, overlap_ratio): + if inverse_xy: + from_w, from_h = image.height, image.width + to_w, to_h = height, width + else: + from_w, from_h = image.width, image.height + to_w, to_h = width, height + h = from_h * to_w // from_w + if inverse_xy: + image = image.resize((h, to_w)) + else: + image = image.resize((to_w, h)) + + split_count = math.ceil((h - to_h * overlap_ratio) / (to_h * (1.0 - overlap_ratio))) + y_step = (h - to_h) / (split_count - 1) + for i in range(split_count): + y = int(y_step * i) + if inverse_xy: + splitted = image.crop((y, 0, y + to_h, to_w)) + else: + splitted = image.crop((0, y, to_w, y + to_h)) + yield splitted + +# not using torchvision.transforms.CenterCrop because it doesn't allow float regions +def center_crop(image: Image, w: int, h: int): + iw, ih = image.size + if ih / h < iw / w: + sw = w * ih / h + box = (iw - sw) / 2, 0, iw - (iw - sw) / 2, ih + else: + sh = h * iw / w + box = 0, (ih - sh) / 2, iw, ih - (ih - sh) / 2 + return image.resize((w, h), Image.Resampling.LANCZOS, box) + + +def multicrop_pic(image: Image, mindim, maxdim, minarea, maxarea, objective, threshold): + iw, ih = image.size + err = lambda w, h: 1-(lambda x: x if x < 1 else 1/x)(iw/ih/(w/h)) + wh = max(((w, h) for w in range(mindim, maxdim+1, 64) for h in range(mindim, maxdim+1, 64) + if minarea <= w * h <= maxarea and err(w, h) <= threshold), + key= lambda wh: (wh[0]*wh[1], -err(*wh))[::1 if objective=='Maximize area' else -1], + default=None + ) + return wh and center_crop(image, *wh) + + +def preprocess_work(process_src, process_dst, process_width, process_height, preprocess_txt_action, process_flip, process_split, process_caption, process_caption_deepbooru=False, split_threshold=0.5, overlap_ratio=0.2, process_focal_crop=False, process_focal_crop_face_weight=0.9, process_focal_crop_entropy_weight=0.3, process_focal_crop_edges_weight=0.5, process_focal_crop_debug=False, process_multicrop=None, process_multicrop_mindim=None, process_multicrop_maxdim=None, process_multicrop_minarea=None, process_multicrop_maxarea=None, process_multicrop_objective=None, process_multicrop_threshold=None): + width = process_width + height = process_height + src = os.path.abspath(process_src) + dst = os.path.abspath(process_dst) + split_threshold = max(0.0, min(1.0, split_threshold)) + overlap_ratio = max(0.0, min(0.9, overlap_ratio)) + + assert src != dst, 'same directory specified as source and destination' + + os.makedirs(dst, exist_ok=True) + + files = listfiles(src) + + shared.state.job = "preprocess" + shared.state.textinfo = "Preprocessing..." + shared.state.job_count = len(files) + + params = PreprocessParams() + params.dstdir = dst + params.flip = process_flip + params.process_caption = process_caption + params.process_caption_deepbooru = process_caption_deepbooru + params.preprocess_txt_action = preprocess_txt_action + + pbar = tqdm.tqdm(files) + for index, imagefile in enumerate(pbar): + params.subindex = 0 + filename = os.path.join(src, imagefile) + try: + img = Image.open(filename).convert("RGB") + except Exception: + continue + + description = f"Preprocessing [Image {index}/{len(files)}]" + pbar.set_description(description) + shared.state.textinfo = description + + params.src = filename + + existing_caption = None + existing_caption_filename = os.path.splitext(filename)[0] + '.txt' + if os.path.exists(existing_caption_filename): + with open(existing_caption_filename, 'r', encoding="utf8") as file: + existing_caption = file.read() + + if shared.state.interrupted: + break + + if img.height > img.width: + ratio = (img.width * height) / (img.height * width) + inverse_xy = False + else: + ratio = (img.height * width) / (img.width * height) + inverse_xy = True + + process_default_resize = True + + if process_split and ratio < 1.0 and ratio <= split_threshold: + for splitted in split_pic(img, inverse_xy, width, height, overlap_ratio): + save_pic(splitted, index, params, existing_caption=existing_caption) + process_default_resize = False + + if process_focal_crop and img.height != img.width: + + dnn_model_path = None + try: + dnn_model_path = autocrop.download_and_cache_models(os.path.join(paths.models_path, "opencv")) + except Exception as e: + print("Unable to load face detection model for auto crop selection. Falling back to lower quality haar method.", e) + + autocrop_settings = autocrop.Settings( + crop_width = width, + crop_height = height, + face_points_weight = process_focal_crop_face_weight, + entropy_points_weight = process_focal_crop_entropy_weight, + corner_points_weight = process_focal_crop_edges_weight, + annotate_image = process_focal_crop_debug, + dnn_model_path = dnn_model_path, + ) + for focal in autocrop.crop_image(img, autocrop_settings): + save_pic(focal, index, params, existing_caption=existing_caption) + process_default_resize = False + + if process_multicrop: + cropped = multicrop_pic(img, process_multicrop_mindim, process_multicrop_maxdim, process_multicrop_minarea, process_multicrop_maxarea, process_multicrop_objective, process_multicrop_threshold) + if cropped is not None: + save_pic(cropped, index, params, existing_caption=existing_caption) + else: + print(f"skipped {img.width}x{img.height} image {filename} (can't find suitable size within error threshold)") + process_default_resize = False + + if process_default_resize: + img = images.resize_image(1, img, width, height) + save_pic(img, index, params, existing_caption=existing_caption) + + shared.state.nextjob() diff --git a/sd/stable-diffusion-webui/modules/textual_inversion/test_embedding.png b/sd/stable-diffusion-webui/modules/textual_inversion/test_embedding.png new file mode 100644 index 0000000000000000000000000000000000000000..07e2d9afaeaff3751b68a7c0f49d8b3466474282 Binary files /dev/null and b/sd/stable-diffusion-webui/modules/textual_inversion/test_embedding.png differ diff --git a/sd/stable-diffusion-webui/modules/textual_inversion/textual_inversion.py b/sd/stable-diffusion-webui/modules/textual_inversion/textual_inversion.py new file mode 100644 index 0000000000000000000000000000000000000000..62c44737f83bed6f42c3cc5155ba59eb0d63afcb --- /dev/null +++ b/sd/stable-diffusion-webui/modules/textual_inversion/textual_inversion.py @@ -0,0 +1,657 @@ +import os +import sys +import traceback +import inspect +from collections import namedtuple + +import torch +import tqdm +import html +import datetime +import csv +import safetensors.torch + +import numpy as np +from PIL import Image, PngImagePlugin +from torch.utils.tensorboard import SummaryWriter + +from modules import shared, devices, sd_hijack, processing, sd_models, images, sd_samplers, sd_hijack_checkpoint +import modules.textual_inversion.dataset +from modules.textual_inversion.learn_schedule import LearnRateScheduler + +from modules.textual_inversion.image_embedding import embedding_to_b64, embedding_from_b64, insert_image_data_embed, extract_image_data_embed, caption_image_overlay +from modules.textual_inversion.logging import save_settings_to_file + + +TextualInversionTemplate = namedtuple("TextualInversionTemplate", ["name", "path"]) +textual_inversion_templates = {} + + +def list_textual_inversion_templates(): + textual_inversion_templates.clear() + + for root, dirs, fns in os.walk(shared.cmd_opts.textual_inversion_templates_dir): + for fn in fns: + path = os.path.join(root, fn) + + textual_inversion_templates[fn] = TextualInversionTemplate(fn, path) + + return textual_inversion_templates + + +class Embedding: + def __init__(self, vec, name, step=None): + self.vec = vec + self.name = name + self.step = step + self.shape = None + self.vectors = 0 + self.cached_checksum = None + self.sd_checkpoint = None + self.sd_checkpoint_name = None + self.optimizer_state_dict = None + self.filename = None + + def save(self, filename): + embedding_data = { + "string_to_token": {"*": 265}, + "string_to_param": {"*": self.vec}, + "name": self.name, + "step": self.step, + "sd_checkpoint": self.sd_checkpoint, + "sd_checkpoint_name": self.sd_checkpoint_name, + } + + torch.save(embedding_data, filename) + + if shared.opts.save_optimizer_state and self.optimizer_state_dict is not None: + optimizer_saved_dict = { + 'hash': self.checksum(), + 'optimizer_state_dict': self.optimizer_state_dict, + } + torch.save(optimizer_saved_dict, filename + '.optim') + + def checksum(self): + if self.cached_checksum is not None: + return self.cached_checksum + + def const_hash(a): + r = 0 + for v in a: + r = (r * 281 ^ int(v) * 997) & 0xFFFFFFFF + return r + + self.cached_checksum = f'{const_hash(self.vec.reshape(-1) * 100) & 0xffff:04x}' + return self.cached_checksum + + +class DirWithTextualInversionEmbeddings: + def __init__(self, path): + self.path = path + self.mtime = None + + def has_changed(self): + if not os.path.isdir(self.path): + return False + + mt = os.path.getmtime(self.path) + if self.mtime is None or mt > self.mtime: + return True + + def update(self): + if not os.path.isdir(self.path): + return + + self.mtime = os.path.getmtime(self.path) + + +class EmbeddingDatabase: + def __init__(self): + self.ids_lookup = {} + self.word_embeddings = {} + self.skipped_embeddings = {} + self.expected_shape = -1 + self.embedding_dirs = {} + self.previously_displayed_embeddings = () + + def add_embedding_dir(self, path): + self.embedding_dirs[path] = DirWithTextualInversionEmbeddings(path) + + def clear_embedding_dirs(self): + self.embedding_dirs.clear() + + def register_embedding(self, embedding, model): + self.word_embeddings[embedding.name] = embedding + + ids = model.cond_stage_model.tokenize([embedding.name])[0] + + first_id = ids[0] + if first_id not in self.ids_lookup: + self.ids_lookup[first_id] = [] + + self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: len(x[0]), reverse=True) + + return embedding + + def get_expected_shape(self): + vec = shared.sd_model.cond_stage_model.encode_embedding_init_text(",", 1) + return vec.shape[1] + + def load_from_file(self, path, filename): + name, ext = os.path.splitext(filename) + ext = ext.upper() + + if ext in ['.PNG', '.WEBP', '.JXL', '.AVIF']: + _, second_ext = os.path.splitext(name) + if second_ext.upper() == '.PREVIEW': + return + + embed_image = Image.open(path) + if hasattr(embed_image, 'text') and 'sd-ti-embedding' in embed_image.text: + data = embedding_from_b64(embed_image.text['sd-ti-embedding']) + name = data.get('name', name) + else: + data = extract_image_data_embed(embed_image) + name = data.get('name', name) + elif ext in ['.BIN', '.PT']: + data = torch.load(path, map_location="cpu") + elif ext in ['.SAFETENSORS']: + data = safetensors.torch.load_file(path, device="cpu") + else: + return + + # textual inversion embeddings + if 'string_to_param' in data: + param_dict = data['string_to_param'] + if hasattr(param_dict, '_parameters'): + param_dict = getattr(param_dict, '_parameters') # fix for torch 1.12.1 loading saved file from torch 1.11 + assert len(param_dict) == 1, 'embedding file has multiple terms in it' + emb = next(iter(param_dict.items()))[1] + # diffuser concepts + elif type(data) == dict and type(next(iter(data.values()))) == torch.Tensor: + assert len(data.keys()) == 1, 'embedding file has multiple terms in it' + + emb = next(iter(data.values())) + if len(emb.shape) == 1: + emb = emb.unsqueeze(0) + else: + raise Exception(f"Couldn't identify {filename} as neither textual inversion embedding nor diffuser concept.") + + vec = emb.detach().to(devices.device, dtype=torch.float32) + embedding = Embedding(vec, name) + embedding.step = data.get('step', None) + embedding.sd_checkpoint = data.get('sd_checkpoint', None) + embedding.sd_checkpoint_name = data.get('sd_checkpoint_name', None) + embedding.vectors = vec.shape[0] + embedding.shape = vec.shape[-1] + embedding.filename = path + + if self.expected_shape == -1 or self.expected_shape == embedding.shape: + self.register_embedding(embedding, shared.sd_model) + else: + self.skipped_embeddings[name] = embedding + + def load_from_dir(self, embdir): + if not os.path.isdir(embdir.path): + return + + for root, dirs, fns in os.walk(embdir.path, followlinks=True): + for fn in fns: + try: + fullfn = os.path.join(root, fn) + + if os.stat(fullfn).st_size == 0: + continue + + self.load_from_file(fullfn, fn) + except Exception: + print(f"Error loading embedding {fn}:", file=sys.stderr) + print(traceback.format_exc(), file=sys.stderr) + continue + + def load_textual_inversion_embeddings(self, force_reload=False): + if not force_reload: + need_reload = False + for path, embdir in self.embedding_dirs.items(): + if embdir.has_changed(): + need_reload = True + break + + if not need_reload: + return + + self.ids_lookup.clear() + self.word_embeddings.clear() + self.skipped_embeddings.clear() + self.expected_shape = self.get_expected_shape() + + for path, embdir in self.embedding_dirs.items(): + self.load_from_dir(embdir) + embdir.update() + + displayed_embeddings = (tuple(self.word_embeddings.keys()), tuple(self.skipped_embeddings.keys())) + if self.previously_displayed_embeddings != displayed_embeddings: + self.previously_displayed_embeddings = displayed_embeddings + print(f"Textual inversion embeddings loaded({len(self.word_embeddings)}): {', '.join(self.word_embeddings.keys())}") + if len(self.skipped_embeddings) > 0: + print(f"Textual inversion embeddings skipped({len(self.skipped_embeddings)}): {', '.join(self.skipped_embeddings.keys())}") + + def find_embedding_at_position(self, tokens, offset): + token = tokens[offset] + possible_matches = self.ids_lookup.get(token, None) + + if possible_matches is None: + return None, None + + for ids, embedding in possible_matches: + if tokens[offset:offset + len(ids)] == ids: + return embedding, len(ids) + + return None, None + + +def create_embedding(name, num_vectors_per_token, overwrite_old, init_text='*'): + cond_model = shared.sd_model.cond_stage_model + + with devices.autocast(): + cond_model([""]) # will send cond model to GPU if lowvram/medvram is active + + #cond_model expects at least some text, so we provide '*' as backup. + embedded = cond_model.encode_embedding_init_text(init_text or '*', num_vectors_per_token) + vec = torch.zeros((num_vectors_per_token, embedded.shape[1]), device=devices.device) + + #Only copy if we provided an init_text, otherwise keep vectors as zeros + if init_text: + for i in range(num_vectors_per_token): + vec[i] = embedded[i * int(embedded.shape[0]) // num_vectors_per_token] + + # Remove illegal characters from name. + name = "".join( x for x in name if (x.isalnum() or x in "._- ")) + fn = os.path.join(shared.cmd_opts.embeddings_dir, f"{name}.pt") + if not overwrite_old: + assert not os.path.exists(fn), f"file {fn} already exists" + + embedding = Embedding(vec, name) + embedding.step = 0 + embedding.save(fn) + + return fn + + +def write_loss(log_directory, filename, step, epoch_len, values): + if shared.opts.training_write_csv_every == 0: + return + + if step % shared.opts.training_write_csv_every != 0: + return + write_csv_header = False if os.path.exists(os.path.join(log_directory, filename)) else True + + with open(os.path.join(log_directory, filename), "a+", newline='') as fout: + csv_writer = csv.DictWriter(fout, fieldnames=["step", "epoch", "epoch_step", *(values.keys())]) + + if write_csv_header: + csv_writer.writeheader() + + epoch = (step - 1) // epoch_len + epoch_step = (step - 1) % epoch_len + + csv_writer.writerow({ + "step": step, + "epoch": epoch, + "epoch_step": epoch_step, + **values, + }) + +def tensorboard_setup(log_directory): + os.makedirs(os.path.join(log_directory, "tensorboard"), exist_ok=True) + return SummaryWriter( + log_dir=os.path.join(log_directory, "tensorboard"), + flush_secs=shared.opts.training_tensorboard_flush_every) + +def tensorboard_add(tensorboard_writer, loss, global_step, step, learn_rate, epoch_num): + tensorboard_add_scaler(tensorboard_writer, "Loss/train", loss, global_step) + tensorboard_add_scaler(tensorboard_writer, f"Loss/train/epoch-{epoch_num}", loss, step) + tensorboard_add_scaler(tensorboard_writer, "Learn rate/train", learn_rate, global_step) + tensorboard_add_scaler(tensorboard_writer, f"Learn rate/train/epoch-{epoch_num}", learn_rate, step) + +def tensorboard_add_scaler(tensorboard_writer, tag, value, step): + tensorboard_writer.add_scalar(tag=tag, + scalar_value=value, global_step=step) + +def tensorboard_add_image(tensorboard_writer, tag, pil_image, step): + # Convert a pil image to a torch tensor + img_tensor = torch.as_tensor(np.array(pil_image, copy=True)) + img_tensor = img_tensor.view(pil_image.size[1], pil_image.size[0], + len(pil_image.getbands())) + img_tensor = img_tensor.permute((2, 0, 1)) + + tensorboard_writer.add_image(tag, img_tensor, global_step=step) + +def validate_train_inputs(model_name, learn_rate, batch_size, gradient_step, data_root, template_file, template_filename, steps, save_model_every, create_image_every, log_directory, name="embedding"): + assert model_name, f"{name} not selected" + assert learn_rate, "Learning rate is empty or 0" + assert isinstance(batch_size, int), "Batch size must be integer" + assert batch_size > 0, "Batch size must be positive" + assert isinstance(gradient_step, int), "Gradient accumulation step must be integer" + assert gradient_step > 0, "Gradient accumulation step must be positive" + assert data_root, "Dataset directory is empty" + assert os.path.isdir(data_root), "Dataset directory doesn't exist" + assert os.listdir(data_root), "Dataset directory is empty" + assert template_filename, "Prompt template file not selected" + assert template_file, f"Prompt template file {template_filename} not found" + assert os.path.isfile(template_file.path), f"Prompt template file {template_filename} doesn't exist" + assert steps, "Max steps is empty or 0" + assert isinstance(steps, int), "Max steps must be integer" + assert steps > 0, "Max steps must be positive" + assert isinstance(save_model_every, int), "Save {name} must be integer" + assert save_model_every >= 0, "Save {name} must be positive or 0" + assert isinstance(create_image_every, int), "Create image must be integer" + assert create_image_every >= 0, "Create image must be positive or 0" + if save_model_every or create_image_every: + assert log_directory, "Log directory is empty" + + +def train_embedding(id_task, embedding_name, learn_rate, batch_size, gradient_step, data_root, log_directory, training_width, training_height, varsize, steps, clip_grad_mode, clip_grad_value, shuffle_tags, tag_drop_out, latent_sampling_method, use_weight, create_image_every, save_embedding_every, template_filename, save_image_with_stored_embedding, preview_from_txt2img, preview_prompt, preview_negative_prompt, preview_steps, preview_sampler_index, preview_cfg_scale, preview_seed, preview_width, preview_height): + save_embedding_every = save_embedding_every or 0 + create_image_every = create_image_every or 0 + template_file = textual_inversion_templates.get(template_filename, None) + validate_train_inputs(embedding_name, learn_rate, batch_size, gradient_step, data_root, template_file, template_filename, steps, save_embedding_every, create_image_every, log_directory, name="embedding") + template_file = template_file.path + + shared.state.job = "train-embedding" + shared.state.textinfo = "Initializing textual inversion training..." + shared.state.job_count = steps + + filename = os.path.join(shared.cmd_opts.embeddings_dir, f'{embedding_name}.pt') + + log_directory = os.path.join(log_directory, datetime.datetime.now().strftime("%Y-%m-%d"), embedding_name) + unload = shared.opts.unload_models_when_training + + if save_embedding_every > 0: + embedding_dir = os.path.join(log_directory, "embeddings") + os.makedirs(embedding_dir, exist_ok=True) + else: + embedding_dir = None + + if create_image_every > 0: + images_dir = os.path.join(log_directory, "images") + os.makedirs(images_dir, exist_ok=True) + else: + images_dir = None + + if create_image_every > 0 and save_image_with_stored_embedding: + images_embeds_dir = os.path.join(log_directory, "image_embeddings") + os.makedirs(images_embeds_dir, exist_ok=True) + else: + images_embeds_dir = None + + hijack = sd_hijack.model_hijack + + embedding = hijack.embedding_db.word_embeddings[embedding_name] + checkpoint = sd_models.select_checkpoint() + + initial_step = embedding.step or 0 + if initial_step >= steps: + shared.state.textinfo = "Model has already been trained beyond specified max steps" + return embedding, filename + + scheduler = LearnRateScheduler(learn_rate, steps, initial_step) + clip_grad = torch.nn.utils.clip_grad_value_ if clip_grad_mode == "value" else \ + torch.nn.utils.clip_grad_norm_ if clip_grad_mode == "norm" else \ + None + if clip_grad: + clip_grad_sched = LearnRateScheduler(clip_grad_value, steps, initial_step, verbose=False) + # dataset loading may take a while, so input validations and early returns should be done before this + shared.state.textinfo = f"Preparing dataset from {html.escape(data_root)}..." + old_parallel_processing_allowed = shared.parallel_processing_allowed + + if shared.opts.training_enable_tensorboard: + tensorboard_writer = tensorboard_setup(log_directory) + + pin_memory = shared.opts.pin_memory + + ds = modules.textual_inversion.dataset.PersonalizedBase(data_root=data_root, width=training_width, height=training_height, repeats=shared.opts.training_image_repeats_per_epoch, placeholder_token=embedding_name, model=shared.sd_model, cond_model=shared.sd_model.cond_stage_model, device=devices.device, template_file=template_file, batch_size=batch_size, gradient_step=gradient_step, shuffle_tags=shuffle_tags, tag_drop_out=tag_drop_out, latent_sampling_method=latent_sampling_method, varsize=varsize, use_weight=use_weight) + + if shared.opts.save_training_settings_to_txt: + save_settings_to_file(log_directory, {**dict(model_name=checkpoint.model_name, model_hash=checkpoint.shorthash, num_of_dataset_images=len(ds), num_vectors_per_token=len(embedding.vec)), **locals()}) + + latent_sampling_method = ds.latent_sampling_method + + dl = modules.textual_inversion.dataset.PersonalizedDataLoader(ds, latent_sampling_method=latent_sampling_method, batch_size=ds.batch_size, pin_memory=pin_memory) + + if unload: + shared.parallel_processing_allowed = False + shared.sd_model.first_stage_model.to(devices.cpu) + + embedding.vec.requires_grad = True + optimizer = torch.optim.AdamW([embedding.vec], lr=scheduler.learn_rate, weight_decay=0.0) + if shared.opts.save_optimizer_state: + optimizer_state_dict = None + if os.path.exists(filename + '.optim'): + optimizer_saved_dict = torch.load(filename + '.optim', map_location='cpu') + if embedding.checksum() == optimizer_saved_dict.get('hash', None): + optimizer_state_dict = optimizer_saved_dict.get('optimizer_state_dict', None) + + if optimizer_state_dict is not None: + optimizer.load_state_dict(optimizer_state_dict) + print("Loaded existing optimizer from checkpoint") + else: + print("No saved optimizer exists in checkpoint") + + scaler = torch.cuda.amp.GradScaler() + + batch_size = ds.batch_size + gradient_step = ds.gradient_step + # n steps = batch_size * gradient_step * n image processed + steps_per_epoch = len(ds) // batch_size // gradient_step + max_steps_per_epoch = len(ds) // batch_size - (len(ds) // batch_size) % gradient_step + loss_step = 0 + _loss_step = 0 #internal + + last_saved_file = "" + last_saved_image = "" + forced_filename = "" + embedding_yet_to_be_embedded = False + + is_training_inpainting_model = shared.sd_model.model.conditioning_key in {'hybrid', 'concat'} + img_c = None + + pbar = tqdm.tqdm(total=steps - initial_step) + try: + sd_hijack_checkpoint.add() + + for i in range((steps-initial_step) * gradient_step): + if scheduler.finished: + break + if shared.state.interrupted: + break + for j, batch in enumerate(dl): + # works as a drop_last=True for gradient accumulation + if j == max_steps_per_epoch: + break + scheduler.apply(optimizer, embedding.step) + if scheduler.finished: + break + if shared.state.interrupted: + break + + if clip_grad: + clip_grad_sched.step(embedding.step) + + with devices.autocast(): + x = batch.latent_sample.to(devices.device, non_blocking=pin_memory) + if use_weight: + w = batch.weight.to(devices.device, non_blocking=pin_memory) + c = shared.sd_model.cond_stage_model(batch.cond_text) + + if is_training_inpainting_model: + if img_c is None: + img_c = processing.txt2img_image_conditioning(shared.sd_model, c, training_width, training_height) + + cond = {"c_concat": [img_c], "c_crossattn": [c]} + else: + cond = c + + if use_weight: + loss = shared.sd_model.weighted_forward(x, cond, w)[0] / gradient_step + del w + else: + loss = shared.sd_model.forward(x, cond)[0] / gradient_step + del x + + _loss_step += loss.item() + scaler.scale(loss).backward() + + # go back until we reach gradient accumulation steps + if (j + 1) % gradient_step != 0: + continue + + if clip_grad: + clip_grad(embedding.vec, clip_grad_sched.learn_rate) + + scaler.step(optimizer) + scaler.update() + embedding.step += 1 + pbar.update() + optimizer.zero_grad(set_to_none=True) + loss_step = _loss_step + _loss_step = 0 + + steps_done = embedding.step + 1 + + epoch_num = embedding.step // steps_per_epoch + epoch_step = embedding.step % steps_per_epoch + + description = f"Training textual inversion [Epoch {epoch_num}: {epoch_step+1}/{steps_per_epoch}] loss: {loss_step:.7f}" + pbar.set_description(description) + if embedding_dir is not None and steps_done % save_embedding_every == 0: + # Before saving, change name to match current checkpoint. + embedding_name_every = f'{embedding_name}-{steps_done}' + last_saved_file = os.path.join(embedding_dir, f'{embedding_name_every}.pt') + save_embedding(embedding, optimizer, checkpoint, embedding_name_every, last_saved_file, remove_cached_checksum=True) + embedding_yet_to_be_embedded = True + + write_loss(log_directory, "textual_inversion_loss.csv", embedding.step, steps_per_epoch, { + "loss": f"{loss_step:.7f}", + "learn_rate": scheduler.learn_rate + }) + + if images_dir is not None and steps_done % create_image_every == 0: + forced_filename = f'{embedding_name}-{steps_done}' + last_saved_image = os.path.join(images_dir, forced_filename) + + shared.sd_model.first_stage_model.to(devices.device) + + p = processing.StableDiffusionProcessingTxt2Img( + sd_model=shared.sd_model, + do_not_save_grid=True, + do_not_save_samples=True, + do_not_reload_embeddings=True, + ) + + if preview_from_txt2img: + p.prompt = preview_prompt + p.negative_prompt = preview_negative_prompt + p.steps = preview_steps + p.sampler_name = sd_samplers.samplers[preview_sampler_index].name + p.cfg_scale = preview_cfg_scale + p.seed = preview_seed + p.width = preview_width + p.height = preview_height + else: + p.prompt = batch.cond_text[0] + p.steps = 20 + p.width = training_width + p.height = training_height + + preview_text = p.prompt + + processed = processing.process_images(p) + image = processed.images[0] if len(processed.images) > 0 else None + + if unload: + shared.sd_model.first_stage_model.to(devices.cpu) + + if image is not None: + shared.state.assign_current_image(image) + + last_saved_image, last_text_info = images.save_image(image, images_dir, "", p.seed, p.prompt, shared.opts.samples_format, processed.infotexts[0], p=p, forced_filename=forced_filename, save_to_dirs=False) + last_saved_image += f", prompt: {preview_text}" + + if shared.opts.training_enable_tensorboard and shared.opts.training_tensorboard_save_images: + tensorboard_add_image(tensorboard_writer, f"Validation at epoch {epoch_num}", image, embedding.step) + + if save_image_with_stored_embedding and os.path.exists(last_saved_file) and embedding_yet_to_be_embedded: + + last_saved_image_chunks = os.path.join(images_embeds_dir, f'{embedding_name}-{steps_done}.png') + + info = PngImagePlugin.PngInfo() + data = torch.load(last_saved_file) + info.add_text("sd-ti-embedding", embedding_to_b64(data)) + + title = "<{}>".format(data.get('name', '???')) + + try: + vectorSize = list(data['string_to_param'].values())[0].shape[0] + except Exception as e: + vectorSize = '?' + + checkpoint = sd_models.select_checkpoint() + footer_left = checkpoint.model_name + footer_mid = '[{}]'.format(checkpoint.shorthash) + footer_right = '{}v {}s'.format(vectorSize, steps_done) + + captioned_image = caption_image_overlay(image, title, footer_left, footer_mid, footer_right) + captioned_image = insert_image_data_embed(captioned_image, data) + + captioned_image.save(last_saved_image_chunks, "PNG", pnginfo=info) + embedding_yet_to_be_embedded = False + + last_saved_image, last_text_info = images.save_image(image, images_dir, "", p.seed, p.prompt, shared.opts.samples_format, processed.infotexts[0], p=p, forced_filename=forced_filename, save_to_dirs=False) + last_saved_image += f", prompt: {preview_text}" + + shared.state.job_no = embedding.step + + shared.state.textinfo = f""" +

+Loss: {loss_step:.7f}
+Step: {steps_done}
+Last prompt: {html.escape(batch.cond_text[0])}
+Last saved embedding: {html.escape(last_saved_file)}
+Last saved image: {html.escape(last_saved_image)}
+

+""" + filename = os.path.join(shared.cmd_opts.embeddings_dir, f'{embedding_name}.pt') + save_embedding(embedding, optimizer, checkpoint, embedding_name, filename, remove_cached_checksum=True) + except Exception: + print(traceback.format_exc(), file=sys.stderr) + pass + finally: + pbar.leave = False + pbar.close() + shared.sd_model.first_stage_model.to(devices.device) + shared.parallel_processing_allowed = old_parallel_processing_allowed + sd_hijack_checkpoint.remove() + + return embedding, filename + + +def save_embedding(embedding, optimizer, checkpoint, embedding_name, filename, remove_cached_checksum=True): + old_embedding_name = embedding.name + old_sd_checkpoint = embedding.sd_checkpoint if hasattr(embedding, "sd_checkpoint") else None + old_sd_checkpoint_name = embedding.sd_checkpoint_name if hasattr(embedding, "sd_checkpoint_name") else None + old_cached_checksum = embedding.cached_checksum if hasattr(embedding, "cached_checksum") else None + try: + embedding.sd_checkpoint = checkpoint.shorthash + embedding.sd_checkpoint_name = checkpoint.model_name + if remove_cached_checksum: + embedding.cached_checksum = None + embedding.name = embedding_name + embedding.optimizer_state_dict = optimizer.state_dict() + embedding.save(filename) + except: + embedding.sd_checkpoint = old_sd_checkpoint + embedding.sd_checkpoint_name = old_sd_checkpoint_name + embedding.name = old_embedding_name + embedding.cached_checksum = old_cached_checksum + raise diff --git a/sd/stable-diffusion-webui/modules/textual_inversion/ui.py b/sd/stable-diffusion-webui/modules/textual_inversion/ui.py new file mode 100644 index 0000000000000000000000000000000000000000..5b75f799e745fa693cda06763af80069324a964f --- /dev/null +++ b/sd/stable-diffusion-webui/modules/textual_inversion/ui.py @@ -0,0 +1,45 @@ +import html + +import gradio as gr + +import modules.textual_inversion.textual_inversion +import modules.textual_inversion.preprocess +from modules import sd_hijack, shared + + +def create_embedding(name, initialization_text, nvpt, overwrite_old): + filename = modules.textual_inversion.textual_inversion.create_embedding(name, nvpt, overwrite_old, init_text=initialization_text) + + sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings() + + return gr.Dropdown.update(choices=sorted(sd_hijack.model_hijack.embedding_db.word_embeddings.keys())), f"Created: {filename}", "" + + +def preprocess(*args): + modules.textual_inversion.preprocess.preprocess(*args) + + return f"Preprocessing {'interrupted' if shared.state.interrupted else 'finished'}.", "" + + +def train_embedding(*args): + + assert not shared.cmd_opts.lowvram, 'Training models with lowvram not possible' + + apply_optimizations = shared.opts.training_xattention_optimizations + try: + if not apply_optimizations: + sd_hijack.undo_optimizations() + + embedding, filename = modules.textual_inversion.textual_inversion.train_embedding(*args) + + res = f""" +Training {'interrupted' if shared.state.interrupted else 'finished'} at {embedding.step} steps. +Embedding saved to {html.escape(filename)} +""" + return res, "" + except Exception: + raise + finally: + if not apply_optimizations: + sd_hijack.apply_optimizations() + diff --git a/sd/stable-diffusion-webui/modules/timer.py b/sd/stable-diffusion-webui/modules/timer.py new file mode 100644 index 0000000000000000000000000000000000000000..8187c28edea3d7ce30d1d8c086a6191eb49d960c --- /dev/null +++ b/sd/stable-diffusion-webui/modules/timer.py @@ -0,0 +1,35 @@ +import time + + +class Timer: + def __init__(self): + self.start = time.time() + self.records = {} + self.total = 0 + + def elapsed(self): + end = time.time() + res = end - self.start + self.start = end + return res + + def record(self, category, extra_time=0): + e = self.elapsed() + if category not in self.records: + self.records[category] = 0 + + self.records[category] += e + extra_time + self.total += e + extra_time + + def summary(self): + res = f"{self.total:.1f}s" + + additions = [x for x in self.records.items() if x[1] >= 0.1] + if not additions: + return res + + res += " (" + res += ", ".join([f"{category}: {time_taken:.1f}s" for category, time_taken in additions]) + res += ")" + + return res diff --git a/sd/stable-diffusion-webui/modules/txt2img.py b/sd/stable-diffusion-webui/modules/txt2img.py new file mode 100644 index 0000000000000000000000000000000000000000..3927d8538f06c1ed270c9a6cfd55d4bb15705ee5 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/txt2img.py @@ -0,0 +1,69 @@ +import modules.scripts +from modules import sd_samplers +from modules.generation_parameters_copypaste import create_override_settings_dict +from modules.processing import StableDiffusionProcessing, Processed, StableDiffusionProcessingTxt2Img, \ + StableDiffusionProcessingImg2Img, process_images +from modules.shared import opts, cmd_opts +import modules.shared as shared +import modules.processing as processing +from modules.ui import plaintext_to_html + + +def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, steps: int, sampler_index: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, height: int, width: int, enable_hr: bool, denoising_strength: float, hr_scale: float, hr_upscaler: str, hr_second_pass_steps: int, hr_resize_x: int, hr_resize_y: int, override_settings_texts, *args): + override_settings = create_override_settings_dict(override_settings_texts) + + p = StableDiffusionProcessingTxt2Img( + sd_model=shared.sd_model, + outpath_samples=opts.outdir_samples or opts.outdir_txt2img_samples, + outpath_grids=opts.outdir_grids or opts.outdir_txt2img_grids, + prompt=prompt, + styles=prompt_styles, + negative_prompt=negative_prompt, + seed=seed, + subseed=subseed, + subseed_strength=subseed_strength, + seed_resize_from_h=seed_resize_from_h, + seed_resize_from_w=seed_resize_from_w, + seed_enable_extras=seed_enable_extras, + sampler_name=sd_samplers.samplers[sampler_index].name, + batch_size=batch_size, + n_iter=n_iter, + steps=steps, + cfg_scale=cfg_scale, + width=width, + height=height, + restore_faces=restore_faces, + tiling=tiling, + enable_hr=enable_hr, + denoising_strength=denoising_strength if enable_hr else None, + hr_scale=hr_scale, + hr_upscaler=hr_upscaler, + hr_second_pass_steps=hr_second_pass_steps, + hr_resize_x=hr_resize_x, + hr_resize_y=hr_resize_y, + override_settings=override_settings, + ) + + p.scripts = modules.scripts.scripts_txt2img + p.script_args = args + + if cmd_opts.enable_console_prompts: + print(f"\ntxt2img: {prompt}", file=shared.progress_print_out) + + processed = modules.scripts.scripts_txt2img.run(p, *args) + + if processed is None: + processed = process_images(p) + + p.close() + + shared.total_tqdm.clear() + + generation_info_js = processed.js() + if opts.samples_log_stdout: + print(generation_info_js) + + if opts.do_not_show_images: + processed.images = [] + + return processed.images, generation_info_js, plaintext_to_html(processed.info), plaintext_to_html(processed.comments) diff --git a/sd/stable-diffusion-webui/modules/ui.py b/sd/stable-diffusion-webui/modules/ui.py new file mode 100644 index 0000000000000000000000000000000000000000..badf4975128985ad55bb69d6ee028adc0a61f97c --- /dev/null +++ b/sd/stable-diffusion-webui/modules/ui.py @@ -0,0 +1,1798 @@ +import html +import json +import math +import mimetypes +import os +import platform +import random +import sys +import tempfile +import time +import traceback +from functools import partial, reduce +import warnings + +import gradio as gr +import gradio.routes +import gradio.utils +import numpy as np +from PIL import Image, PngImagePlugin +from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call, wrap_gradio_call + +from modules import sd_hijack, sd_models, localization, script_callbacks, ui_extensions, deepbooru, sd_vae, extra_networks, postprocessing, ui_components, ui_common, ui_postprocessing +from modules.ui_components import FormRow, FormGroup, ToolButton, FormHTML +from modules.paths import script_path, data_path + +from modules.shared import opts, cmd_opts, restricted_opts + +import modules.codeformer_model +import modules.generation_parameters_copypaste as parameters_copypaste +import modules.gfpgan_model +import modules.hypernetworks.ui +import modules.scripts +import modules.shared as shared +import modules.styles +import modules.textual_inversion.ui +from modules import prompt_parser +from modules.images import save_image +from modules.sd_hijack import model_hijack +from modules.sd_samplers import samplers, samplers_for_img2img +from modules.textual_inversion import textual_inversion +import modules.hypernetworks.ui +from modules.generation_parameters_copypaste import image_from_url_text +import modules.extras + +warnings.filterwarnings("default" if opts.show_warnings else "ignore", category=UserWarning) + +# this is a fix for Windows users. Without it, javascript files will be served with text/html content-type and the browser will not show any UI +mimetypes.init() +mimetypes.add_type('application/javascript', '.js') + +if not cmd_opts.share and not cmd_opts.listen: + # fix gradio phoning home + gradio.utils.version_check = lambda: None + gradio.utils.get_local_ip_address = lambda: '127.0.0.1' + +if cmd_opts.ngrok is not None: + import modules.ngrok as ngrok + print('ngrok authtoken detected, trying to connect...') + ngrok.connect( + cmd_opts.ngrok, + cmd_opts.port if cmd_opts.port is not None else 7860, + cmd_opts.ngrok_region + ) + + +def gr_show(visible=True): + return {"visible": visible, "__type__": "update"} + + +sample_img2img = "assets/stable-samples/img2img/sketch-mountains-input.jpg" +sample_img2img = sample_img2img if os.path.exists(sample_img2img) else None + +css_hide_progressbar = """ +.wrap .m-12 svg { display:none!important; } +.wrap .m-12::before { content:"Loading..." } +.wrap .z-20 svg { display:none!important; } +.wrap .z-20::before { content:"Loading..." } +.wrap.cover-bg .z-20::before { content:"" } +.progress-bar { display:none!important; } +.meta-text { display:none!important; } +.meta-text-center { display:none!important; } +""" + +# Using constants for these since the variation selector isn't visible. +# Important that they exactly match script.js for tooltip to work. +random_symbol = '\U0001f3b2\ufe0f' # 🎲️ +reuse_symbol = '\u267b\ufe0f' # ♻️ +paste_symbol = '\u2199\ufe0f' # ↙ +refresh_symbol = '\U0001f504' # 🔄 +save_style_symbol = '\U0001f4be' # 💾 +apply_style_symbol = '\U0001f4cb' # 📋 +clear_prompt_symbol = '\U0001F5D1' # 🗑️ +extra_networks_symbol = '\U0001F3B4' # 🎴 +switch_values_symbol = '\U000021C5' # ⇅ + + +def plaintext_to_html(text): + return ui_common.plaintext_to_html(text) + + +def send_gradio_gallery_to_image(x): + if len(x) == 0: + return None + return image_from_url_text(x[0]) + +def visit(x, func, path=""): + if hasattr(x, 'children'): + for c in x.children: + visit(c, func, path) + elif x.label is not None: + func(path + "/" + str(x.label), x) + + +def add_style(name: str, prompt: str, negative_prompt: str): + if name is None: + return [gr_show() for x in range(4)] + + style = modules.styles.PromptStyle(name, prompt, negative_prompt) + shared.prompt_styles.styles[style.name] = style + # Save all loaded prompt styles: this allows us to update the storage format in the future more easily, because we + # reserialize all styles every time we save them + shared.prompt_styles.save_styles(shared.styles_filename) + + return [gr.Dropdown.update(visible=True, choices=list(shared.prompt_styles.styles)) for _ in range(2)] + + +def calc_resolution_hires(enable, width, height, hr_scale, hr_resize_x, hr_resize_y): + from modules import processing, devices + + if not enable: + return "" + + p = processing.StableDiffusionProcessingTxt2Img(width=width, height=height, enable_hr=True, hr_scale=hr_scale, hr_resize_x=hr_resize_x, hr_resize_y=hr_resize_y) + + with devices.autocast(): + p.init([""], [0], [0]) + + return f"resize: from {p.width}x{p.height} to {p.hr_resize_x or p.hr_upscale_to_x}x{p.hr_resize_y or p.hr_upscale_to_y}" + + +def apply_styles(prompt, prompt_neg, styles): + prompt = shared.prompt_styles.apply_styles_to_prompt(prompt, styles) + prompt_neg = shared.prompt_styles.apply_negative_styles_to_prompt(prompt_neg, styles) + + return [gr.Textbox.update(value=prompt), gr.Textbox.update(value=prompt_neg), gr.Dropdown.update(value=[])] + + +def process_interrogate(interrogation_function, mode, ii_input_dir, ii_output_dir, *ii_singles): + if mode in {0, 1, 3, 4}: + return [interrogation_function(ii_singles[mode]), None] + elif mode == 2: + return [interrogation_function(ii_singles[mode]["image"]), None] + elif mode == 5: + assert not shared.cmd_opts.hide_ui_dir_config, "Launched with --hide-ui-dir-config, batch img2img disabled" + images = shared.listfiles(ii_input_dir) + print(f"Will process {len(images)} images.") + if ii_output_dir != "": + os.makedirs(ii_output_dir, exist_ok=True) + else: + ii_output_dir = ii_input_dir + + for image in images: + img = Image.open(image) + filename = os.path.basename(image) + left, _ = os.path.splitext(filename) + print(interrogation_function(img), file=open(os.path.join(ii_output_dir, left + ".txt"), 'a')) + + return [gr.update(), None] + + +def interrogate(image): + prompt = shared.interrogator.interrogate(image.convert("RGB")) + return gr.update() if prompt is None else prompt + + +def interrogate_deepbooru(image): + prompt = deepbooru.model.tag(image) + return gr.update() if prompt is None else prompt + + +def create_seed_inputs(target_interface): + with FormRow(elem_id=target_interface + '_seed_row'): + seed = (gr.Textbox if cmd_opts.use_textbox_seed else gr.Number)(label='Seed', value=-1, elem_id=target_interface + '_seed') + seed.style(container=False) + random_seed = gr.Button(random_symbol, elem_id=target_interface + '_random_seed') + reuse_seed = gr.Button(reuse_symbol, elem_id=target_interface + '_reuse_seed') + + with gr.Group(elem_id=target_interface + '_subseed_show_box'): + seed_checkbox = gr.Checkbox(label='Extra', elem_id=target_interface + '_subseed_show', value=False) + + # Components to show/hide based on the 'Extra' checkbox + seed_extras = [] + + with FormRow(visible=False, elem_id=target_interface + '_subseed_row') as seed_extra_row_1: + seed_extras.append(seed_extra_row_1) + subseed = gr.Number(label='Variation seed', value=-1, elem_id=target_interface + '_subseed') + subseed.style(container=False) + random_subseed = gr.Button(random_symbol, elem_id=target_interface + '_random_subseed') + reuse_subseed = gr.Button(reuse_symbol, elem_id=target_interface + '_reuse_subseed') + subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01, elem_id=target_interface + '_subseed_strength') + + with FormRow(visible=False) as seed_extra_row_2: + seed_extras.append(seed_extra_row_2) + seed_resize_from_w = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from width", value=0, elem_id=target_interface + '_seed_resize_from_w') + seed_resize_from_h = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from height", value=0, elem_id=target_interface + '_seed_resize_from_h') + + random_seed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[seed]) + random_subseed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[subseed]) + + def change_visibility(show): + return {comp: gr_show(show) for comp in seed_extras} + + seed_checkbox.change(change_visibility, show_progress=False, inputs=[seed_checkbox], outputs=seed_extras) + + return seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox + + + +def connect_clear_prompt(button): + """Given clear button, prompt, and token_counter objects, setup clear prompt button click event""" + button.click( + _js="clear_prompt", + fn=None, + inputs=[], + outputs=[], + ) + + +def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, dummy_component, is_subseed): + """ Connects a 'reuse (sub)seed' button's click event so that it copies last used + (sub)seed value from generation info the to the seed field. If copying subseed and subseed strength + was 0, i.e. no variation seed was used, it copies the normal seed value instead.""" + def copy_seed(gen_info_string: str, index): + res = -1 + + try: + gen_info = json.loads(gen_info_string) + index -= gen_info.get('index_of_first_image', 0) + + if is_subseed and gen_info.get('subseed_strength', 0) > 0: + all_subseeds = gen_info.get('all_subseeds', [-1]) + res = all_subseeds[index if 0 <= index < len(all_subseeds) else 0] + else: + all_seeds = gen_info.get('all_seeds', [-1]) + res = all_seeds[index if 0 <= index < len(all_seeds) else 0] + + except json.decoder.JSONDecodeError as e: + if gen_info_string != '': + print("Error parsing JSON generation info:", file=sys.stderr) + print(gen_info_string, file=sys.stderr) + + return [res, gr_show(False)] + + reuse_seed.click( + fn=copy_seed, + _js="(x, y) => [x, selected_gallery_index()]", + show_progress=False, + inputs=[generation_info, dummy_component], + outputs=[seed, dummy_component] + ) + + +def update_token_counter(text, steps): + try: + text, _ = extra_networks.parse_prompt(text) + + _, prompt_flat_list, _ = prompt_parser.get_multicond_prompt_list([text]) + prompt_schedules = prompt_parser.get_learned_conditioning_prompt_schedules(prompt_flat_list, steps) + + except Exception: + # a parsing error can happen here during typing, and we don't want to bother the user with + # messages related to it in console + prompt_schedules = [[[steps, text]]] + + flat_prompts = reduce(lambda list1, list2: list1+list2, prompt_schedules) + prompts = [prompt_text for step, prompt_text in flat_prompts] + token_count, max_length = max([model_hijack.get_prompt_lengths(prompt) for prompt in prompts], key=lambda args: args[0]) + return f"{token_count}/{max_length}" + + +def create_toprow(is_img2img): + id_part = "img2img" if is_img2img else "txt2img" + + with gr.Row(elem_id=f"{id_part}_toprow", variant="compact"): + with gr.Column(elem_id=f"{id_part}_prompt_container", scale=6): + with gr.Row(): + with gr.Column(scale=80): + with gr.Row(): + prompt = gr.Textbox(label="Prompt", elem_id=f"{id_part}_prompt", show_label=False, lines=3, placeholder="Prompt (press Ctrl+Enter or Alt+Enter to generate)") + + with gr.Row(): + with gr.Column(scale=80): + with gr.Row(): + negative_prompt = gr.Textbox(label="Negative prompt", elem_id=f"{id_part}_neg_prompt", show_label=False, lines=2, placeholder="Negative prompt (press Ctrl+Enter or Alt+Enter to generate)") + + button_interrogate = None + button_deepbooru = None + if is_img2img: + with gr.Column(scale=1, elem_id="interrogate_col"): + button_interrogate = gr.Button('Interrogate\nCLIP', elem_id="interrogate") + button_deepbooru = gr.Button('Interrogate\nDeepBooru', elem_id="deepbooru") + + with gr.Column(scale=1, elem_id=f"{id_part}_actions_column"): + with gr.Row(elem_id=f"{id_part}_generate_box"): + interrupt = gr.Button('Interrupt', elem_id=f"{id_part}_interrupt") + skip = gr.Button('Skip', elem_id=f"{id_part}_skip") + submit = gr.Button('Generate', elem_id=f"{id_part}_generate", variant='primary') + + skip.click( + fn=lambda: shared.state.skip(), + inputs=[], + outputs=[], + ) + + interrupt.click( + fn=lambda: shared.state.interrupt(), + inputs=[], + outputs=[], + ) + + with gr.Row(elem_id=f"{id_part}_tools"): + paste = ToolButton(value=paste_symbol, elem_id="paste") + clear_prompt_button = ToolButton(value=clear_prompt_symbol, elem_id=f"{id_part}_clear_prompt") + extra_networks_button = ToolButton(value=extra_networks_symbol, elem_id=f"{id_part}_extra_networks") + prompt_style_apply = ToolButton(value=apply_style_symbol, elem_id=f"{id_part}_style_apply") + save_style = ToolButton(value=save_style_symbol, elem_id=f"{id_part}_style_create") + + token_counter = gr.HTML(value="", elem_id=f"{id_part}_token_counter") + token_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button") + negative_token_counter = gr.HTML(value="", elem_id=f"{id_part}_negative_token_counter") + negative_token_button = gr.Button(visible=False, elem_id=f"{id_part}_negative_token_button") + + clear_prompt_button.click( + fn=lambda *x: x, + _js="confirm_clear_prompt", + inputs=[prompt, negative_prompt], + outputs=[prompt, negative_prompt], + ) + + with gr.Row(elem_id=f"{id_part}_styles_row"): + prompt_styles = gr.Dropdown(label="Styles", elem_id=f"{id_part}_styles", choices=[k for k, v in shared.prompt_styles.styles.items()], value=[], multiselect=True) + create_refresh_button(prompt_styles, shared.prompt_styles.reload, lambda: {"choices": [k for k, v in shared.prompt_styles.styles.items()]}, f"refresh_{id_part}_styles") + + return prompt, prompt_styles, negative_prompt, submit, button_interrogate, button_deepbooru, prompt_style_apply, save_style, paste, extra_networks_button, token_counter, token_button, negative_token_counter, negative_token_button + + +def setup_progressbar(*args, **kwargs): + pass + + +def apply_setting(key, value): + if value is None: + return gr.update() + + if shared.cmd_opts.freeze_settings: + return gr.update() + + # dont allow model to be swapped when model hash exists in prompt + if key == "sd_model_checkpoint" and opts.disable_weights_auto_swap: + return gr.update() + + if key == "sd_model_checkpoint": + ckpt_info = sd_models.get_closet_checkpoint_match(value) + + if ckpt_info is not None: + value = ckpt_info.title + else: + return gr.update() + + comp_args = opts.data_labels[key].component_args + if comp_args and isinstance(comp_args, dict) and comp_args.get('visible') is False: + return + + valtype = type(opts.data_labels[key].default) + oldval = opts.data.get(key, None) + opts.data[key] = valtype(value) if valtype != type(None) else value + if oldval != value and opts.data_labels[key].onchange is not None: + opts.data_labels[key].onchange() + + opts.save(shared.config_filename) + return getattr(opts, key) + + +def create_refresh_button(refresh_component, refresh_method, refreshed_args, elem_id): + def refresh(): + refresh_method() + args = refreshed_args() if callable(refreshed_args) else refreshed_args + + for k, v in args.items(): + setattr(refresh_component, k, v) + + return gr.update(**(args or {})) + + refresh_button = ToolButton(value=refresh_symbol, elem_id=elem_id) + refresh_button.click( + fn=refresh, + inputs=[], + outputs=[refresh_component] + ) + return refresh_button + + +def create_output_panel(tabname, outdir): + return ui_common.create_output_panel(tabname, outdir) + + +def create_sampler_and_steps_selection(choices, tabname): + if opts.samplers_in_dropdown: + with FormRow(elem_id=f"sampler_selection_{tabname}"): + sampler_index = gr.Dropdown(label='Sampling method', elem_id=f"{tabname}_sampling", choices=[x.name for x in choices], value=choices[0].name, type="index") + steps = gr.Slider(minimum=1, maximum=150, step=1, elem_id=f"{tabname}_steps", label="Sampling steps", value=20) + else: + with FormGroup(elem_id=f"sampler_selection_{tabname}"): + steps = gr.Slider(minimum=1, maximum=150, step=1, elem_id=f"{tabname}_steps", label="Sampling steps", value=20) + sampler_index = gr.Radio(label='Sampling method', elem_id=f"{tabname}_sampling", choices=[x.name for x in choices], value=choices[0].name, type="index") + + return steps, sampler_index + + +def ordered_ui_categories(): + user_order = {x.strip(): i * 2 + 1 for i, x in enumerate(shared.opts.ui_reorder.split(","))} + + for i, category in sorted(enumerate(shared.ui_reorder_categories), key=lambda x: user_order.get(x[1], x[0] * 2 + 0)): + yield category + + +def get_value_for_setting(key): + value = getattr(opts, key) + + info = opts.data_labels[key] + args = info.component_args() if callable(info.component_args) else info.component_args or {} + args = {k: v for k, v in args.items() if k not in {'precision'}} + + return gr.update(value=value, **args) + + +def create_override_settings_dropdown(tabname, row): + dropdown = gr.Dropdown([], label="Override settings", visible=False, elem_id=f"{tabname}_override_settings", multiselect=True) + + dropdown.change( + fn=lambda x: gr.Dropdown.update(visible=len(x) > 0), + inputs=[dropdown], + outputs=[dropdown], + ) + + return dropdown + + +def create_ui(): + import modules.img2img + import modules.txt2img + + reload_javascript() + + parameters_copypaste.reset() + + modules.scripts.scripts_current = modules.scripts.scripts_txt2img + modules.scripts.scripts_txt2img.initialize_scripts(is_img2img=False) + + with gr.Blocks(analytics_enabled=False) as txt2img_interface: + txt2img_prompt, txt2img_prompt_styles, txt2img_negative_prompt, submit, _, _, txt2img_prompt_style_apply, txt2img_save_style, txt2img_paste, extra_networks_button, token_counter, token_button, negative_token_counter, negative_token_button = create_toprow(is_img2img=False) + + dummy_component = gr.Label(visible=False) + txt_prompt_img = gr.File(label="", elem_id="txt2img_prompt_image", file_count="single", type="binary", visible=False) + + with FormRow(variant='compact', elem_id="txt2img_extra_networks", visible=False) as extra_networks: + from modules import ui_extra_networks + extra_networks_ui = ui_extra_networks.create_ui(extra_networks, extra_networks_button, 'txt2img') + + with gr.Row().style(equal_height=False): + with gr.Column(variant='compact', elem_id="txt2img_settings"): + for category in ordered_ui_categories(): + if category == "sampler": + steps, sampler_index = create_sampler_and_steps_selection(samplers, "txt2img") + + elif category == "dimensions": + with FormRow(): + with gr.Column(elem_id="txt2img_column_size", scale=4): + width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="txt2img_width") + height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="txt2img_height") + + res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="txt2img_res_switch_btn") + if opts.dimensions_and_batch_together: + with gr.Column(elem_id="txt2img_column_batch"): + batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="txt2img_batch_count") + batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1, elem_id="txt2img_batch_size") + + elif category == "cfg": + cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0, elem_id="txt2img_cfg_scale") + + elif category == "seed": + seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox = create_seed_inputs('txt2img') + + elif category == "checkboxes": + with FormRow(elem_id="txt2img_checkboxes", variant="compact"): + restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(shared.face_restorers) > 1, elem_id="txt2img_restore_faces") + tiling = gr.Checkbox(label='Tiling', value=False, elem_id="txt2img_tiling") + enable_hr = gr.Checkbox(label='Hires. fix', value=False, elem_id="txt2img_enable_hr") + hr_final_resolution = FormHTML(value="", elem_id="txtimg_hr_finalres", label="Upscaled resolution", interactive=False) + + elif category == "hires_fix": + with FormGroup(visible=False, elem_id="txt2img_hires_fix") as hr_options: + with FormRow(elem_id="txt2img_hires_fix_row1", variant="compact"): + hr_upscaler = gr.Dropdown(label="Upscaler", elem_id="txt2img_hr_upscaler", choices=[*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]], value=shared.latent_upscale_default_mode) + hr_second_pass_steps = gr.Slider(minimum=0, maximum=150, step=1, label='Hires steps', value=0, elem_id="txt2img_hires_steps") + denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.7, elem_id="txt2img_denoising_strength") + + with FormRow(elem_id="txt2img_hires_fix_row2", variant="compact"): + hr_scale = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Upscale by", value=2.0, elem_id="txt2img_hr_scale") + hr_resize_x = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize width to", value=0, elem_id="txt2img_hr_resize_x") + hr_resize_y = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize height to", value=0, elem_id="txt2img_hr_resize_y") + + elif category == "batch": + if not opts.dimensions_and_batch_together: + with FormRow(elem_id="txt2img_column_batch"): + batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="txt2img_batch_count") + batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1, elem_id="txt2img_batch_size") + + elif category == "override_settings": + with FormRow(elem_id="txt2img_override_settings_row") as row: + override_settings = create_override_settings_dropdown('txt2img', row) + + elif category == "scripts": + with FormGroup(elem_id="txt2img_script_container"): + custom_inputs = modules.scripts.scripts_txt2img.setup_ui() + + hr_resolution_preview_inputs = [enable_hr, width, height, hr_scale, hr_resize_x, hr_resize_y] + for input in hr_resolution_preview_inputs: + input.change( + fn=calc_resolution_hires, + inputs=hr_resolution_preview_inputs, + outputs=[hr_final_resolution], + show_progress=False, + ) + input.change( + None, + _js="onCalcResolutionHires", + inputs=hr_resolution_preview_inputs, + outputs=[], + show_progress=False, + ) + + txt2img_gallery, generation_info, html_info, html_log = create_output_panel("txt2img", opts.outdir_txt2img_samples) + + connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False) + connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True) + + txt2img_args = dict( + fn=wrap_gradio_gpu_call(modules.txt2img.txt2img, extra_outputs=[None, '', '']), + _js="submit", + inputs=[ + dummy_component, + txt2img_prompt, + txt2img_negative_prompt, + txt2img_prompt_styles, + steps, + sampler_index, + restore_faces, + tiling, + batch_count, + batch_size, + cfg_scale, + seed, + subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox, + height, + width, + enable_hr, + denoising_strength, + hr_scale, + hr_upscaler, + hr_second_pass_steps, + hr_resize_x, + hr_resize_y, + override_settings, + ] + custom_inputs, + + outputs=[ + txt2img_gallery, + generation_info, + html_info, + html_log, + ], + show_progress=False, + ) + + txt2img_prompt.submit(**txt2img_args) + submit.click(**txt2img_args) + + res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height]) + + txt_prompt_img.change( + fn=modules.images.image_data, + inputs=[ + txt_prompt_img + ], + outputs=[ + txt2img_prompt, + txt_prompt_img + ] + ) + + enable_hr.change( + fn=lambda x: gr_show(x), + inputs=[enable_hr], + outputs=[hr_options], + show_progress = False, + ) + + txt2img_paste_fields = [ + (txt2img_prompt, "Prompt"), + (txt2img_negative_prompt, "Negative prompt"), + (steps, "Steps"), + (sampler_index, "Sampler"), + (restore_faces, "Face restoration"), + (cfg_scale, "CFG scale"), + (seed, "Seed"), + (width, "Size-1"), + (height, "Size-2"), + (batch_size, "Batch size"), + (subseed, "Variation seed"), + (subseed_strength, "Variation seed strength"), + (seed_resize_from_w, "Seed resize from-1"), + (seed_resize_from_h, "Seed resize from-2"), + (denoising_strength, "Denoising strength"), + (enable_hr, lambda d: "Denoising strength" in d), + (hr_options, lambda d: gr.Row.update(visible="Denoising strength" in d)), + (hr_scale, "Hires upscale"), + (hr_upscaler, "Hires upscaler"), + (hr_second_pass_steps, "Hires steps"), + (hr_resize_x, "Hires resize-1"), + (hr_resize_y, "Hires resize-2"), + *modules.scripts.scripts_txt2img.infotext_fields + ] + parameters_copypaste.add_paste_fields("txt2img", None, txt2img_paste_fields, override_settings) + parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding( + paste_button=txt2img_paste, tabname="txt2img", source_text_component=txt2img_prompt, source_image_component=None, + )) + + txt2img_preview_params = [ + txt2img_prompt, + txt2img_negative_prompt, + steps, + sampler_index, + cfg_scale, + seed, + width, + height, + ] + + token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[txt2img_prompt, steps], outputs=[token_counter]) + negative_token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[txt2img_negative_prompt, steps], outputs=[negative_token_counter]) + + ui_extra_networks.setup_ui(extra_networks_ui, txt2img_gallery) + + modules.scripts.scripts_current = modules.scripts.scripts_img2img + modules.scripts.scripts_img2img.initialize_scripts(is_img2img=True) + + with gr.Blocks(analytics_enabled=False) as img2img_interface: + img2img_prompt, img2img_prompt_styles, img2img_negative_prompt, submit, img2img_interrogate, img2img_deepbooru, img2img_prompt_style_apply, img2img_save_style, img2img_paste, extra_networks_button, token_counter, token_button, negative_token_counter, negative_token_button = create_toprow(is_img2img=True) + + img2img_prompt_img = gr.File(label="", elem_id="img2img_prompt_image", file_count="single", type="binary", visible=False) + + with FormRow(variant='compact', elem_id="img2img_extra_networks", visible=False) as extra_networks: + from modules import ui_extra_networks + extra_networks_ui_img2img = ui_extra_networks.create_ui(extra_networks, extra_networks_button, 'img2img') + + with FormRow().style(equal_height=False): + with gr.Column(variant='compact', elem_id="img2img_settings"): + copy_image_buttons = [] + copy_image_destinations = {} + + def add_copy_image_controls(tab_name, elem): + with gr.Row(variant="compact", elem_id=f"img2img_copy_to_{tab_name}"): + gr.HTML("Copy image to: ", elem_id=f"img2img_label_copy_to_{tab_name}") + + for title, name in zip(['img2img', 'sketch', 'inpaint', 'inpaint sketch'], ['img2img', 'sketch', 'inpaint', 'inpaint_sketch']): + if name == tab_name: + gr.Button(title, interactive=False) + copy_image_destinations[name] = elem + continue + + button = gr.Button(title) + copy_image_buttons.append((button, name, elem)) + + with gr.Tabs(elem_id="mode_img2img"): + with gr.TabItem('img2img', id='img2img', elem_id="img2img_img2img_tab") as tab_img2img: + init_img = gr.Image(label="Image for img2img", elem_id="img2img_image", show_label=False, source="upload", interactive=True, type="pil", tool="editor", image_mode="RGBA").style(height=480) + add_copy_image_controls('img2img', init_img) + + with gr.TabItem('Sketch', id='img2img_sketch', elem_id="img2img_img2img_sketch_tab") as tab_sketch: + sketch = gr.Image(label="Image for img2img", elem_id="img2img_sketch", show_label=False, source="upload", interactive=True, type="pil", tool="color-sketch", image_mode="RGBA").style(height=480) + add_copy_image_controls('sketch', sketch) + + with gr.TabItem('Inpaint', id='inpaint', elem_id="img2img_inpaint_tab") as tab_inpaint: + init_img_with_mask = gr.Image(label="Image for inpainting with mask", show_label=False, elem_id="img2maskimg", source="upload", interactive=True, type="pil", tool="sketch", image_mode="RGBA").style(height=480) + add_copy_image_controls('inpaint', init_img_with_mask) + + with gr.TabItem('Inpaint sketch', id='inpaint_sketch', elem_id="img2img_inpaint_sketch_tab") as tab_inpaint_color: + inpaint_color_sketch = gr.Image(label="Color sketch inpainting", show_label=False, elem_id="inpaint_sketch", source="upload", interactive=True, type="pil", tool="color-sketch", image_mode="RGBA").style(height=480) + inpaint_color_sketch_orig = gr.State(None) + add_copy_image_controls('inpaint_sketch', inpaint_color_sketch) + + def update_orig(image, state): + if image is not None: + same_size = state is not None and state.size == image.size + has_exact_match = np.any(np.all(np.array(image) == np.array(state), axis=-1)) + edited = same_size and has_exact_match + return image if not edited or state is None else state + + inpaint_color_sketch.change(update_orig, [inpaint_color_sketch, inpaint_color_sketch_orig], inpaint_color_sketch_orig) + + with gr.TabItem('Inpaint upload', id='inpaint_upload', elem_id="img2img_inpaint_upload_tab") as tab_inpaint_upload: + init_img_inpaint = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil", elem_id="img_inpaint_base") + init_mask_inpaint = gr.Image(label="Mask", source="upload", interactive=True, type="pil", elem_id="img_inpaint_mask") + + with gr.TabItem('Batch', id='batch', elem_id="img2img_batch_tab") as tab_batch: + hidden = '
Disabled when launched with --hide-ui-dir-config.' if shared.cmd_opts.hide_ui_dir_config else '' + gr.HTML( + f"

Process images in a directory on the same machine where the server is running." + + f"
Use an empty output directory to save pictures normally instead of writing to the output directory." + + f"
Add inpaint batch mask directory to enable inpaint batch processing." + f"{hidden}

" + ) + img2img_batch_input_dir = gr.Textbox(label="Input directory", **shared.hide_dirs, elem_id="img2img_batch_input_dir") + img2img_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs, elem_id="img2img_batch_output_dir") + img2img_batch_inpaint_mask_dir = gr.Textbox(label="Inpaint batch mask directory (required for inpaint batch processing only)", **shared.hide_dirs, elem_id="img2img_batch_inpaint_mask_dir") + + def copy_image(img): + if isinstance(img, dict) and 'image' in img: + return img['image'] + + return img + + for button, name, elem in copy_image_buttons: + button.click( + fn=copy_image, + inputs=[elem], + outputs=[copy_image_destinations[name]], + ) + button.click( + fn=lambda: None, + _js="switch_to_"+name.replace(" ", "_"), + inputs=[], + outputs=[], + ) + + with FormRow(): + resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", choices=["Just resize", "Crop and resize", "Resize and fill", "Just resize (latent upscale)"], type="index", value="Just resize") + + for category in ordered_ui_categories(): + if category == "sampler": + steps, sampler_index = create_sampler_and_steps_selection(samplers_for_img2img, "img2img") + + elif category == "dimensions": + with FormRow(): + with gr.Column(elem_id="img2img_column_size", scale=4): + width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="img2img_width") + height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="img2img_height") + + res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="img2img_res_switch_btn") + if opts.dimensions_and_batch_together: + with gr.Column(elem_id="img2img_column_batch"): + batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="img2img_batch_count") + batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1, elem_id="img2img_batch_size") + + elif category == "cfg": + with FormGroup(): + with FormRow(): + cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0, elem_id="img2img_cfg_scale") + image_cfg_scale = gr.Slider(minimum=0, maximum=3.0, step=0.05, label='Image CFG Scale', value=1.5, elem_id="img2img_image_cfg_scale", visible=shared.sd_model and shared.sd_model.cond_stage_key == "edit") + denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength") + + elif category == "seed": + seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox = create_seed_inputs('img2img') + + elif category == "checkboxes": + with FormRow(elem_id="img2img_checkboxes", variant="compact"): + restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(shared.face_restorers) > 1, elem_id="img2img_restore_faces") + tiling = gr.Checkbox(label='Tiling', value=False, elem_id="img2img_tiling") + + elif category == "batch": + if not opts.dimensions_and_batch_together: + with FormRow(elem_id="img2img_column_batch"): + batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="img2img_batch_count") + batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1, elem_id="img2img_batch_size") + + elif category == "override_settings": + with FormRow(elem_id="img2img_override_settings_row") as row: + override_settings = create_override_settings_dropdown('img2img', row) + + elif category == "scripts": + with FormGroup(elem_id="img2img_script_container"): + custom_inputs = modules.scripts.scripts_img2img.setup_ui() + + elif category == "inpaint": + with FormGroup(elem_id="inpaint_controls", visible=False) as inpaint_controls: + with FormRow(): + mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4, elem_id="img2img_mask_blur") + mask_alpha = gr.Slider(label="Mask transparency", visible=False, elem_id="img2img_mask_alpha") + + with FormRow(): + inpainting_mask_invert = gr.Radio(label='Mask mode', choices=['Inpaint masked', 'Inpaint not masked'], value='Inpaint masked', type="index", elem_id="img2img_mask_mode") + + with FormRow(): + inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='original', type="index", elem_id="img2img_inpainting_fill") + + with FormRow(): + with gr.Column(): + inpaint_full_res = gr.Radio(label="Inpaint area", choices=["Whole picture", "Only masked"], type="index", value="Whole picture", elem_id="img2img_inpaint_full_res") + + with gr.Column(scale=4): + inpaint_full_res_padding = gr.Slider(label='Only masked padding, pixels', minimum=0, maximum=256, step=4, value=32, elem_id="img2img_inpaint_full_res_padding") + + def select_img2img_tab(tab): + return gr.update(visible=tab in [2, 3, 4]), gr.update(visible=tab == 3), + + for i, elem in enumerate([tab_img2img, tab_sketch, tab_inpaint, tab_inpaint_color, tab_inpaint_upload, tab_batch]): + elem.select( + fn=lambda tab=i: select_img2img_tab(tab), + inputs=[], + outputs=[inpaint_controls, mask_alpha], + ) + + img2img_gallery, generation_info, html_info, html_log = create_output_panel("img2img", opts.outdir_img2img_samples) + + connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False) + connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True) + + img2img_prompt_img.change( + fn=modules.images.image_data, + inputs=[ + img2img_prompt_img + ], + outputs=[ + img2img_prompt, + img2img_prompt_img + ] + ) + + img2img_args = dict( + fn=wrap_gradio_gpu_call(modules.img2img.img2img, extra_outputs=[None, '', '']), + _js="submit_img2img", + inputs=[ + dummy_component, + dummy_component, + img2img_prompt, + img2img_negative_prompt, + img2img_prompt_styles, + init_img, + sketch, + init_img_with_mask, + inpaint_color_sketch, + inpaint_color_sketch_orig, + init_img_inpaint, + init_mask_inpaint, + steps, + sampler_index, + mask_blur, + mask_alpha, + inpainting_fill, + restore_faces, + tiling, + batch_count, + batch_size, + cfg_scale, + image_cfg_scale, + denoising_strength, + seed, + subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox, + height, + width, + resize_mode, + inpaint_full_res, + inpaint_full_res_padding, + inpainting_mask_invert, + img2img_batch_input_dir, + img2img_batch_output_dir, + img2img_batch_inpaint_mask_dir, + override_settings, + ] + custom_inputs, + outputs=[ + img2img_gallery, + generation_info, + html_info, + html_log, + ], + show_progress=False, + ) + + interrogate_args = dict( + _js="get_img2img_tab_index", + inputs=[ + dummy_component, + img2img_batch_input_dir, + img2img_batch_output_dir, + init_img, + sketch, + init_img_with_mask, + inpaint_color_sketch, + init_img_inpaint, + ], + outputs=[img2img_prompt, dummy_component], + ) + + img2img_prompt.submit(**img2img_args) + submit.click(**img2img_args) + res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height]) + + img2img_interrogate.click( + fn=lambda *args: process_interrogate(interrogate, *args), + **interrogate_args, + ) + + img2img_deepbooru.click( + fn=lambda *args: process_interrogate(interrogate_deepbooru, *args), + **interrogate_args, + ) + + prompts = [(txt2img_prompt, txt2img_negative_prompt), (img2img_prompt, img2img_negative_prompt)] + style_dropdowns = [txt2img_prompt_styles, img2img_prompt_styles] + style_js_funcs = ["update_txt2img_tokens", "update_img2img_tokens"] + + for button, (prompt, negative_prompt) in zip([txt2img_save_style, img2img_save_style], prompts): + button.click( + fn=add_style, + _js="ask_for_style_name", + # Have to pass empty dummy component here, because the JavaScript and Python function have to accept + # the same number of parameters, but we only know the style-name after the JavaScript prompt + inputs=[dummy_component, prompt, negative_prompt], + outputs=[txt2img_prompt_styles, img2img_prompt_styles], + ) + + for button, (prompt, negative_prompt), styles, js_func in zip([txt2img_prompt_style_apply, img2img_prompt_style_apply], prompts, style_dropdowns, style_js_funcs): + button.click( + fn=apply_styles, + _js=js_func, + inputs=[prompt, negative_prompt, styles], + outputs=[prompt, negative_prompt, styles], + ) + + token_button.click(fn=update_token_counter, inputs=[img2img_prompt, steps], outputs=[token_counter]) + negative_token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[txt2img_negative_prompt, steps], outputs=[negative_token_counter]) + + ui_extra_networks.setup_ui(extra_networks_ui_img2img, img2img_gallery) + + img2img_paste_fields = [ + (img2img_prompt, "Prompt"), + (img2img_negative_prompt, "Negative prompt"), + (steps, "Steps"), + (sampler_index, "Sampler"), + (restore_faces, "Face restoration"), + (cfg_scale, "CFG scale"), + (image_cfg_scale, "Image CFG scale"), + (seed, "Seed"), + (width, "Size-1"), + (height, "Size-2"), + (batch_size, "Batch size"), + (subseed, "Variation seed"), + (subseed_strength, "Variation seed strength"), + (seed_resize_from_w, "Seed resize from-1"), + (seed_resize_from_h, "Seed resize from-2"), + (denoising_strength, "Denoising strength"), + (mask_blur, "Mask blur"), + *modules.scripts.scripts_img2img.infotext_fields + ] + parameters_copypaste.add_paste_fields("img2img", init_img, img2img_paste_fields, override_settings) + parameters_copypaste.add_paste_fields("inpaint", init_img_with_mask, img2img_paste_fields, override_settings) + parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding( + paste_button=img2img_paste, tabname="img2img", source_text_component=img2img_prompt, source_image_component=None, + )) + + modules.scripts.scripts_current = None + + with gr.Blocks(analytics_enabled=False) as extras_interface: + ui_postprocessing.create_ui() + + with gr.Blocks(analytics_enabled=False) as pnginfo_interface: + with gr.Row().style(equal_height=False): + with gr.Column(variant='panel'): + image = gr.Image(elem_id="pnginfo_image", label="Source", source="upload", interactive=True, type="pil") + + with gr.Column(variant='panel'): + html = gr.HTML() + generation_info = gr.Textbox(visible=False, elem_id="pnginfo_generation_info") + html2 = gr.HTML() + with gr.Row(): + buttons = parameters_copypaste.create_buttons(["txt2img", "img2img", "inpaint", "extras"]) + + for tabname, button in buttons.items(): + parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding( + paste_button=button, tabname=tabname, source_text_component=generation_info, source_image_component=image, + )) + + image.change( + fn=wrap_gradio_call(modules.extras.run_pnginfo), + inputs=[image], + outputs=[html, generation_info, html2], + ) + + def update_interp_description(value): + interp_description_css = "

{}

" + interp_descriptions = { + "No interpolation": interp_description_css.format("No interpolation will be used. Requires one model; A. Allows for format conversion and VAE baking."), + "Weighted sum": interp_description_css.format("A weighted sum will be used for interpolation. Requires two models; A and B. The result is calculated as A * (1 - M) + B * M"), + "Add difference": interp_description_css.format("The difference between the last two models will be added to the first. Requires three models; A, B and C. The result is calculated as A + (B - C) * M") + } + return interp_descriptions[value] + + with gr.Blocks(analytics_enabled=False) as modelmerger_interface: + with gr.Row().style(equal_height=False): + with gr.Column(variant='compact'): + interp_description = gr.HTML(value=update_interp_description("Weighted sum"), elem_id="modelmerger_interp_description") + + with FormRow(elem_id="modelmerger_models"): + primary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_primary_model_name", label="Primary model (A)") + create_refresh_button(primary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_A") + + secondary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_secondary_model_name", label="Secondary model (B)") + create_refresh_button(secondary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_B") + + tertiary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_tertiary_model_name", label="Tertiary model (C)") + create_refresh_button(tertiary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_C") + + custom_name = gr.Textbox(label="Custom Name (Optional)", elem_id="modelmerger_custom_name") + interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Multiplier (M) - set to 0 to get model A', value=0.3, elem_id="modelmerger_interp_amount") + interp_method = gr.Radio(choices=["No interpolation", "Weighted sum", "Add difference"], value="Weighted sum", label="Interpolation Method", elem_id="modelmerger_interp_method") + interp_method.change(fn=update_interp_description, inputs=[interp_method], outputs=[interp_description]) + + with FormRow(): + checkpoint_format = gr.Radio(choices=["ckpt", "safetensors"], value="ckpt", label="Checkpoint format", elem_id="modelmerger_checkpoint_format") + save_as_half = gr.Checkbox(value=False, label="Save as float16", elem_id="modelmerger_save_as_half") + + with FormRow(): + with gr.Column(): + config_source = gr.Radio(choices=["A, B or C", "B", "C", "Don't"], value="A, B or C", label="Copy config from", type="index", elem_id="modelmerger_config_method") + + with gr.Column(): + with FormRow(): + bake_in_vae = gr.Dropdown(choices=["None"] + list(sd_vae.vae_dict), value="None", label="Bake in VAE", elem_id="modelmerger_bake_in_vae") + create_refresh_button(bake_in_vae, sd_vae.refresh_vae_list, lambda: {"choices": ["None"] + list(sd_vae.vae_dict)}, "modelmerger_refresh_bake_in_vae") + + with FormRow(): + discard_weights = gr.Textbox(value="", label="Discard weights with matching name", elem_id="modelmerger_discard_weights") + + with gr.Row(): + modelmerger_merge = gr.Button(elem_id="modelmerger_merge", value="Merge", variant='primary') + + with gr.Column(variant='compact', elem_id="modelmerger_results_container"): + with gr.Group(elem_id="modelmerger_results_panel"): + modelmerger_result = gr.HTML(elem_id="modelmerger_result", show_label=False) + + with gr.Blocks(analytics_enabled=False) as train_interface: + with gr.Row().style(equal_height=False): + gr.HTML(value="

See wiki for detailed explanation.

") + + with gr.Row(variant="compact").style(equal_height=False): + with gr.Tabs(elem_id="train_tabs"): + + with gr.Tab(label="Create embedding"): + new_embedding_name = gr.Textbox(label="Name", elem_id="train_new_embedding_name") + initialization_text = gr.Textbox(label="Initialization text", value="*", elem_id="train_initialization_text") + nvpt = gr.Slider(label="Number of vectors per token", minimum=1, maximum=75, step=1, value=1, elem_id="train_nvpt") + overwrite_old_embedding = gr.Checkbox(value=False, label="Overwrite Old Embedding", elem_id="train_overwrite_old_embedding") + + with gr.Row(): + with gr.Column(scale=3): + gr.HTML(value="") + + with gr.Column(): + create_embedding = gr.Button(value="Create embedding", variant='primary', elem_id="train_create_embedding") + + with gr.Tab(label="Create hypernetwork"): + new_hypernetwork_name = gr.Textbox(label="Name", elem_id="train_new_hypernetwork_name") + new_hypernetwork_sizes = gr.CheckboxGroup(label="Modules", value=["768", "320", "640", "1280"], choices=["768", "1024", "320", "640", "1280"], elem_id="train_new_hypernetwork_sizes") + new_hypernetwork_layer_structure = gr.Textbox("1, 2, 1", label="Enter hypernetwork layer structure", placeholder="1st and last digit must be 1. ex:'1, 2, 1'", elem_id="train_new_hypernetwork_layer_structure") + new_hypernetwork_activation_func = gr.Dropdown(value="linear", label="Select activation function of hypernetwork. Recommended : Swish / Linear(none)", choices=modules.hypernetworks.ui.keys, elem_id="train_new_hypernetwork_activation_func") + new_hypernetwork_initialization_option = gr.Dropdown(value = "Normal", label="Select Layer weights initialization. Recommended: Kaiming for relu-like, Xavier for sigmoid-like, Normal otherwise", choices=["Normal", "KaimingUniform", "KaimingNormal", "XavierUniform", "XavierNormal"], elem_id="train_new_hypernetwork_initialization_option") + new_hypernetwork_add_layer_norm = gr.Checkbox(label="Add layer normalization", elem_id="train_new_hypernetwork_add_layer_norm") + new_hypernetwork_use_dropout = gr.Checkbox(label="Use dropout", elem_id="train_new_hypernetwork_use_dropout") + new_hypernetwork_dropout_structure = gr.Textbox("0, 0, 0", label="Enter hypernetwork Dropout structure (or empty). Recommended : 0~0.35 incrementing sequence: 0, 0.05, 0.15", placeholder="1st and last digit must be 0 and values should be between 0 and 1. ex:'0, 0.01, 0'") + overwrite_old_hypernetwork = gr.Checkbox(value=False, label="Overwrite Old Hypernetwork", elem_id="train_overwrite_old_hypernetwork") + + with gr.Row(): + with gr.Column(scale=3): + gr.HTML(value="") + + with gr.Column(): + create_hypernetwork = gr.Button(value="Create hypernetwork", variant='primary', elem_id="train_create_hypernetwork") + + with gr.Tab(label="Preprocess images"): + process_src = gr.Textbox(label='Source directory', elem_id="train_process_src") + process_dst = gr.Textbox(label='Destination directory', elem_id="train_process_dst") + process_width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="train_process_width") + process_height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="train_process_height") + preprocess_txt_action = gr.Dropdown(label='Existing Caption txt Action', value="ignore", choices=["ignore", "copy", "prepend", "append"], elem_id="train_preprocess_txt_action") + + with gr.Row(): + process_flip = gr.Checkbox(label='Create flipped copies', elem_id="train_process_flip") + process_split = gr.Checkbox(label='Split oversized images', elem_id="train_process_split") + process_focal_crop = gr.Checkbox(label='Auto focal point crop', elem_id="train_process_focal_crop") + process_multicrop = gr.Checkbox(label='Auto-sized crop', elem_id="train_process_multicrop") + process_caption = gr.Checkbox(label='Use BLIP for caption', elem_id="train_process_caption") + process_caption_deepbooru = gr.Checkbox(label='Use deepbooru for caption', visible=True, elem_id="train_process_caption_deepbooru") + + with gr.Row(visible=False) as process_split_extra_row: + process_split_threshold = gr.Slider(label='Split image threshold', value=0.5, minimum=0.0, maximum=1.0, step=0.05, elem_id="train_process_split_threshold") + process_overlap_ratio = gr.Slider(label='Split image overlap ratio', value=0.2, minimum=0.0, maximum=0.9, step=0.05, elem_id="train_process_overlap_ratio") + + with gr.Row(visible=False) as process_focal_crop_row: + process_focal_crop_face_weight = gr.Slider(label='Focal point face weight', value=0.9, minimum=0.0, maximum=1.0, step=0.05, elem_id="train_process_focal_crop_face_weight") + process_focal_crop_entropy_weight = gr.Slider(label='Focal point entropy weight', value=0.15, minimum=0.0, maximum=1.0, step=0.05, elem_id="train_process_focal_crop_entropy_weight") + process_focal_crop_edges_weight = gr.Slider(label='Focal point edges weight', value=0.5, minimum=0.0, maximum=1.0, step=0.05, elem_id="train_process_focal_crop_edges_weight") + process_focal_crop_debug = gr.Checkbox(label='Create debug image', elem_id="train_process_focal_crop_debug") + + with gr.Column(visible=False) as process_multicrop_col: + gr.Markdown('Each image is center-cropped with an automatically chosen width and height.') + with gr.Row(): + process_multicrop_mindim = gr.Slider(minimum=64, maximum=2048, step=8, label="Dimension lower bound", value=384, elem_id="train_process_multicrop_mindim") + process_multicrop_maxdim = gr.Slider(minimum=64, maximum=2048, step=8, label="Dimension upper bound", value=768, elem_id="train_process_multicrop_maxdim") + with gr.Row(): + process_multicrop_minarea = gr.Slider(minimum=64*64, maximum=2048*2048, step=1, label="Area lower bound", value=64*64, elem_id="train_process_multicrop_minarea") + process_multicrop_maxarea = gr.Slider(minimum=64*64, maximum=2048*2048, step=1, label="Area upper bound", value=640*640, elem_id="train_process_multicrop_maxarea") + with gr.Row(): + process_multicrop_objective = gr.Radio(["Maximize area", "Minimize error"], value="Maximize area", label="Resizing objective", elem_id="train_process_multicrop_objective") + process_multicrop_threshold = gr.Slider(minimum=0, maximum=1, step=0.01, label="Error threshold", value=0.1, elem_id="train_process_multicrop_threshold") + + with gr.Row(): + with gr.Column(scale=3): + gr.HTML(value="") + + with gr.Column(): + with gr.Row(): + interrupt_preprocessing = gr.Button("Interrupt", elem_id="train_interrupt_preprocessing") + run_preprocess = gr.Button(value="Preprocess", variant='primary', elem_id="train_run_preprocess") + + process_split.change( + fn=lambda show: gr_show(show), + inputs=[process_split], + outputs=[process_split_extra_row], + ) + + process_focal_crop.change( + fn=lambda show: gr_show(show), + inputs=[process_focal_crop], + outputs=[process_focal_crop_row], + ) + + process_multicrop.change( + fn=lambda show: gr_show(show), + inputs=[process_multicrop], + outputs=[process_multicrop_col], + ) + + def get_textual_inversion_template_names(): + return sorted([x for x in textual_inversion.textual_inversion_templates]) + + with gr.Tab(label="Train"): + gr.HTML(value="

Train an embedding or Hypernetwork; you must specify a directory with a set of 1:1 ratio images [wiki]

") + with FormRow(): + train_embedding_name = gr.Dropdown(label='Embedding', elem_id="train_embedding", choices=sorted(sd_hijack.model_hijack.embedding_db.word_embeddings.keys())) + create_refresh_button(train_embedding_name, sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings, lambda: {"choices": sorted(sd_hijack.model_hijack.embedding_db.word_embeddings.keys())}, "refresh_train_embedding_name") + + train_hypernetwork_name = gr.Dropdown(label='Hypernetwork', elem_id="train_hypernetwork", choices=[x for x in shared.hypernetworks.keys()]) + create_refresh_button(train_hypernetwork_name, shared.reload_hypernetworks, lambda: {"choices": sorted([x for x in shared.hypernetworks.keys()])}, "refresh_train_hypernetwork_name") + + with FormRow(): + embedding_learn_rate = gr.Textbox(label='Embedding Learning rate', placeholder="Embedding Learning rate", value="0.005", elem_id="train_embedding_learn_rate") + hypernetwork_learn_rate = gr.Textbox(label='Hypernetwork Learning rate', placeholder="Hypernetwork Learning rate", value="0.00001", elem_id="train_hypernetwork_learn_rate") + + with FormRow(): + clip_grad_mode = gr.Dropdown(value="disabled", label="Gradient Clipping", choices=["disabled", "value", "norm"]) + clip_grad_value = gr.Textbox(placeholder="Gradient clip value", value="0.1", show_label=False) + + with FormRow(): + batch_size = gr.Number(label='Batch size', value=1, precision=0, elem_id="train_batch_size") + gradient_step = gr.Number(label='Gradient accumulation steps', value=1, precision=0, elem_id="train_gradient_step") + + dataset_directory = gr.Textbox(label='Dataset directory', placeholder="Path to directory with input images", elem_id="train_dataset_directory") + log_directory = gr.Textbox(label='Log directory', placeholder="Path to directory where to write outputs", value="textual_inversion", elem_id="train_log_directory") + + with FormRow(): + template_file = gr.Dropdown(label='Prompt template', value="style_filewords.txt", elem_id="train_template_file", choices=get_textual_inversion_template_names()) + create_refresh_button(template_file, textual_inversion.list_textual_inversion_templates, lambda: {"choices": get_textual_inversion_template_names()}, "refrsh_train_template_file") + + training_width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="train_training_width") + training_height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="train_training_height") + varsize = gr.Checkbox(label="Do not resize images", value=False, elem_id="train_varsize") + steps = gr.Number(label='Max steps', value=100000, precision=0, elem_id="train_steps") + + with FormRow(): + create_image_every = gr.Number(label='Save an image to log directory every N steps, 0 to disable', value=500, precision=0, elem_id="train_create_image_every") + save_embedding_every = gr.Number(label='Save a copy of embedding to log directory every N steps, 0 to disable', value=500, precision=0, elem_id="train_save_embedding_every") + + use_weight = gr.Checkbox(label="Use PNG alpha channel as loss weight", value=False, elem_id="use_weight") + + save_image_with_stored_embedding = gr.Checkbox(label='Save images with embedding in PNG chunks', value=True, elem_id="train_save_image_with_stored_embedding") + preview_from_txt2img = gr.Checkbox(label='Read parameters (prompt, etc...) from txt2img tab when making previews', value=False, elem_id="train_preview_from_txt2img") + + shuffle_tags = gr.Checkbox(label="Shuffle tags by ',' when creating prompts.", value=False, elem_id="train_shuffle_tags") + tag_drop_out = gr.Slider(minimum=0, maximum=1, step=0.1, label="Drop out tags when creating prompts.", value=0, elem_id="train_tag_drop_out") + + latent_sampling_method = gr.Radio(label='Choose latent sampling method', value="once", choices=['once', 'deterministic', 'random'], elem_id="train_latent_sampling_method") + + with gr.Row(): + train_embedding = gr.Button(value="Train Embedding", variant='primary', elem_id="train_train_embedding") + interrupt_training = gr.Button(value="Interrupt", elem_id="train_interrupt_training") + train_hypernetwork = gr.Button(value="Train Hypernetwork", variant='primary', elem_id="train_train_hypernetwork") + + params = script_callbacks.UiTrainTabParams(txt2img_preview_params) + + script_callbacks.ui_train_tabs_callback(params) + + with gr.Column(elem_id='ti_gallery_container'): + ti_output = gr.Text(elem_id="ti_output", value="", show_label=False) + ti_gallery = gr.Gallery(label='Output', show_label=False, elem_id='ti_gallery').style(grid=4) + ti_progress = gr.HTML(elem_id="ti_progress", value="") + ti_outcome = gr.HTML(elem_id="ti_error", value="") + + create_embedding.click( + fn=modules.textual_inversion.ui.create_embedding, + inputs=[ + new_embedding_name, + initialization_text, + nvpt, + overwrite_old_embedding, + ], + outputs=[ + train_embedding_name, + ti_output, + ti_outcome, + ] + ) + + create_hypernetwork.click( + fn=modules.hypernetworks.ui.create_hypernetwork, + inputs=[ + new_hypernetwork_name, + new_hypernetwork_sizes, + overwrite_old_hypernetwork, + new_hypernetwork_layer_structure, + new_hypernetwork_activation_func, + new_hypernetwork_initialization_option, + new_hypernetwork_add_layer_norm, + new_hypernetwork_use_dropout, + new_hypernetwork_dropout_structure + ], + outputs=[ + train_hypernetwork_name, + ti_output, + ti_outcome, + ] + ) + + run_preprocess.click( + fn=wrap_gradio_gpu_call(modules.textual_inversion.ui.preprocess, extra_outputs=[gr.update()]), + _js="start_training_textual_inversion", + inputs=[ + dummy_component, + process_src, + process_dst, + process_width, + process_height, + preprocess_txt_action, + process_flip, + process_split, + process_caption, + process_caption_deepbooru, + process_split_threshold, + process_overlap_ratio, + process_focal_crop, + process_focal_crop_face_weight, + process_focal_crop_entropy_weight, + process_focal_crop_edges_weight, + process_focal_crop_debug, + process_multicrop, + process_multicrop_mindim, + process_multicrop_maxdim, + process_multicrop_minarea, + process_multicrop_maxarea, + process_multicrop_objective, + process_multicrop_threshold, + ], + outputs=[ + ti_output, + ti_outcome, + ], + ) + + train_embedding.click( + fn=wrap_gradio_gpu_call(modules.textual_inversion.ui.train_embedding, extra_outputs=[gr.update()]), + _js="start_training_textual_inversion", + inputs=[ + dummy_component, + train_embedding_name, + embedding_learn_rate, + batch_size, + gradient_step, + dataset_directory, + log_directory, + training_width, + training_height, + varsize, + steps, + clip_grad_mode, + clip_grad_value, + shuffle_tags, + tag_drop_out, + latent_sampling_method, + use_weight, + create_image_every, + save_embedding_every, + template_file, + save_image_with_stored_embedding, + preview_from_txt2img, + *txt2img_preview_params, + ], + outputs=[ + ti_output, + ti_outcome, + ] + ) + + train_hypernetwork.click( + fn=wrap_gradio_gpu_call(modules.hypernetworks.ui.train_hypernetwork, extra_outputs=[gr.update()]), + _js="start_training_textual_inversion", + inputs=[ + dummy_component, + train_hypernetwork_name, + hypernetwork_learn_rate, + batch_size, + gradient_step, + dataset_directory, + log_directory, + training_width, + training_height, + varsize, + steps, + clip_grad_mode, + clip_grad_value, + shuffle_tags, + tag_drop_out, + latent_sampling_method, + use_weight, + create_image_every, + save_embedding_every, + template_file, + preview_from_txt2img, + *txt2img_preview_params, + ], + outputs=[ + ti_output, + ti_outcome, + ] + ) + + interrupt_training.click( + fn=lambda: shared.state.interrupt(), + inputs=[], + outputs=[], + ) + + interrupt_preprocessing.click( + fn=lambda: shared.state.interrupt(), + inputs=[], + outputs=[], + ) + + def create_setting_component(key, is_quicksettings=False): + def fun(): + return opts.data[key] if key in opts.data else opts.data_labels[key].default + + info = opts.data_labels[key] + t = type(info.default) + + args = info.component_args() if callable(info.component_args) else info.component_args + + if info.component is not None: + comp = info.component + elif t == str: + comp = gr.Textbox + elif t == int: + comp = gr.Number + elif t == bool: + comp = gr.Checkbox + else: + raise Exception(f'bad options item type: {str(t)} for key {key}') + + elem_id = "setting_"+key + + if info.refresh is not None: + if is_quicksettings: + res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {})) + create_refresh_button(res, info.refresh, info.component_args, "refresh_" + key) + else: + with FormRow(): + res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {})) + create_refresh_button(res, info.refresh, info.component_args, "refresh_" + key) + else: + res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {})) + + return res + + components = [] + component_dict = {} + shared.settings_components = component_dict + + script_callbacks.ui_settings_callback() + opts.reorder() + + def run_settings(*args): + changed = [] + + for key, value, comp in zip(opts.data_labels.keys(), args, components): + assert comp == dummy_component or opts.same_type(value, opts.data_labels[key].default), f"Bad value for setting {key}: {value}; expecting {type(opts.data_labels[key].default).__name__}" + + for key, value, comp in zip(opts.data_labels.keys(), args, components): + if comp == dummy_component: + continue + + if opts.set(key, value): + changed.append(key) + + try: + opts.save(shared.config_filename) + except RuntimeError: + return opts.dumpjson(), f'{len(changed)} settings changed without save: {", ".join(changed)}.' + return opts.dumpjson(), f'{len(changed)} settings changed{": " if len(changed) > 0 else ""}{", ".join(changed)}.' + + def run_settings_single(value, key): + if not opts.same_type(value, opts.data_labels[key].default): + return gr.update(visible=True), opts.dumpjson() + + if not opts.set(key, value): + return gr.update(value=getattr(opts, key)), opts.dumpjson() + + opts.save(shared.config_filename) + + return get_value_for_setting(key), opts.dumpjson() + + with gr.Blocks(analytics_enabled=False) as settings_interface: + with gr.Row(): + with gr.Column(scale=6): + settings_submit = gr.Button(value="Apply settings", variant='primary', elem_id="settings_submit") + with gr.Column(): + restart_gradio = gr.Button(value='Reload UI', variant='primary', elem_id="settings_restart_gradio") + + result = gr.HTML(elem_id="settings_result") + + quicksettings_names = [x.strip() for x in opts.quicksettings.split(",")] + quicksettings_names = {x: i for i, x in enumerate(quicksettings_names) if x != 'quicksettings'} + + quicksettings_list = [] + + previous_section = None + current_tab = None + current_row = None + with gr.Tabs(elem_id="settings"): + for i, (k, item) in enumerate(opts.data_labels.items()): + section_must_be_skipped = item.section[0] is None + + if previous_section != item.section and not section_must_be_skipped: + elem_id, text = item.section + + if current_tab is not None: + current_row.__exit__() + current_tab.__exit__() + + gr.Group() + current_tab = gr.TabItem(elem_id="settings_{}".format(elem_id), label=text) + current_tab.__enter__() + current_row = gr.Column(variant='compact') + current_row.__enter__() + + previous_section = item.section + + if k in quicksettings_names and not shared.cmd_opts.freeze_settings: + quicksettings_list.append((i, k, item)) + components.append(dummy_component) + elif section_must_be_skipped: + components.append(dummy_component) + else: + component = create_setting_component(k) + component_dict[k] = component + components.append(component) + + if current_tab is not None: + current_row.__exit__() + current_tab.__exit__() + + with gr.TabItem("Actions"): + request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications") + download_localization = gr.Button(value='Download localization template', elem_id="download_localization") + reload_script_bodies = gr.Button(value='Reload custom script bodies (No ui updates, No restart)', variant='secondary', elem_id="settings_reload_script_bodies") + + with gr.TabItem("Licenses"): + gr.HTML(shared.html("licenses.html"), elem_id="licenses") + + gr.Button(value="Show all pages", elem_id="settings_show_all_pages") + + request_notifications.click( + fn=lambda: None, + inputs=[], + outputs=[], + _js='function(){}' + ) + + download_localization.click( + fn=lambda: None, + inputs=[], + outputs=[], + _js='download_localization' + ) + + def reload_scripts(): + modules.scripts.reload_script_body_only() + reload_javascript() # need to refresh the html page + + reload_script_bodies.click( + fn=reload_scripts, + inputs=[], + outputs=[] + ) + + def request_restart(): + shared.state.interrupt() + shared.state.need_restart = True + + restart_gradio.click( + fn=request_restart, + _js='restart_reload', + inputs=[], + outputs=[], + ) + + interfaces = [ + (txt2img_interface, "txt2img", "txt2img"), + (img2img_interface, "img2img", "img2img"), + (extras_interface, "Extras", "extras"), + (pnginfo_interface, "PNG Info", "pnginfo"), + (modelmerger_interface, "Checkpoint Merger", "modelmerger"), + (train_interface, "Train", "ti"), + ] + + css = "" + + for cssfile in modules.scripts.list_files_with_name("style.css"): + if not os.path.isfile(cssfile): + continue + + with open(cssfile, "r", encoding="utf8") as file: + css += file.read() + "\n" + + if os.path.exists(os.path.join(data_path, "user.css")): + with open(os.path.join(data_path, "user.css"), "r", encoding="utf8") as file: + css += file.read() + "\n" + + if not cmd_opts.no_progressbar_hiding: + css += css_hide_progressbar + + interfaces += script_callbacks.ui_tabs_callback() + interfaces += [(settings_interface, "Settings", "settings")] + + extensions_interface = ui_extensions.create_ui() + interfaces += [(extensions_interface, "Extensions", "extensions")] + + with gr.Blocks(css=css, analytics_enabled=False, title="Stable Diffusion") as demo: + with gr.Row(elem_id="quicksettings", variant="compact"): + for i, k, item in sorted(quicksettings_list, key=lambda x: quicksettings_names.get(x[1], x[0])): + component = create_setting_component(k, is_quicksettings=True) + component_dict[k] = component + + parameters_copypaste.connect_paste_params_buttons() + + with gr.Tabs(elem_id="tabs") as tabs: + for interface, label, ifid in interfaces: + with gr.TabItem(label, id=ifid, elem_id='tab_' + ifid): + interface.render() + + if os.path.exists(os.path.join(script_path, "notification.mp3")): + audio_notification = gr.Audio(interactive=False, value=os.path.join(script_path, "notification.mp3"), elem_id="audio_notification", visible=False) + + footer = shared.html("footer.html") + footer = footer.format(versions=versions_html()) + gr.HTML(footer, elem_id="footer") + + text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False) + settings_submit.click( + fn=wrap_gradio_call(run_settings, extra_outputs=[gr.update()]), + inputs=components, + outputs=[text_settings, result], + ) + + for i, k, item in quicksettings_list: + component = component_dict[k] + + component.change( + fn=lambda value, k=k: run_settings_single(value, key=k), + inputs=[component], + outputs=[component, text_settings], + ) + + text_settings.change( + fn=lambda: gr.update(visible=shared.sd_model and shared.sd_model.cond_stage_key == "edit"), + inputs=[], + outputs=[image_cfg_scale], + ) + + button_set_checkpoint = gr.Button('Change checkpoint', elem_id='change_checkpoint', visible=False) + button_set_checkpoint.click( + fn=lambda value, _: run_settings_single(value, key='sd_model_checkpoint'), + _js="function(v){ var res = desiredCheckpointName; desiredCheckpointName = ''; return [res || v, null]; }", + inputs=[component_dict['sd_model_checkpoint'], dummy_component], + outputs=[component_dict['sd_model_checkpoint'], text_settings], + ) + + component_keys = [k for k in opts.data_labels.keys() if k in component_dict] + + def get_settings_values(): + return [get_value_for_setting(key) for key in component_keys] + + demo.load( + fn=get_settings_values, + inputs=[], + outputs=[component_dict[k] for k in component_keys], + ) + + def modelmerger(*args): + try: + results = modules.extras.run_modelmerger(*args) + except Exception as e: + print("Error loading/saving model file:", file=sys.stderr) + print(traceback.format_exc(), file=sys.stderr) + modules.sd_models.list_models() # to remove the potentially missing models from the list + return [*[gr.Dropdown.update(choices=modules.sd_models.checkpoint_tiles()) for _ in range(4)], f"Error merging checkpoints: {e}"] + return results + + modelmerger_merge.click(fn=lambda: '', inputs=[], outputs=[modelmerger_result]) + modelmerger_merge.click( + fn=wrap_gradio_gpu_call(modelmerger, extra_outputs=lambda: [gr.update() for _ in range(4)]), + _js='modelmerger', + inputs=[ + dummy_component, + primary_model_name, + secondary_model_name, + tertiary_model_name, + interp_method, + interp_amount, + save_as_half, + custom_name, + checkpoint_format, + config_source, + bake_in_vae, + discard_weights, + ], + outputs=[ + primary_model_name, + secondary_model_name, + tertiary_model_name, + component_dict['sd_model_checkpoint'], + modelmerger_result, + ] + ) + + ui_config_file = cmd_opts.ui_config_file + ui_settings = {} + settings_count = len(ui_settings) + error_loading = False + + try: + if os.path.exists(ui_config_file): + with open(ui_config_file, "r", encoding="utf8") as file: + ui_settings = json.load(file) + except Exception: + error_loading = True + print("Error loading settings:", file=sys.stderr) + print(traceback.format_exc(), file=sys.stderr) + + def loadsave(path, x): + def apply_field(obj, field, condition=None, init_field=None): + key = path + "/" + field + + if getattr(obj, 'custom_script_source', None) is not None: + key = 'customscript/' + obj.custom_script_source + '/' + key + + if getattr(obj, 'do_not_save_to_config', False): + return + + saved_value = ui_settings.get(key, None) + if saved_value is None: + ui_settings[key] = getattr(obj, field) + elif condition and not condition(saved_value): + pass + + # this warning is generally not useful; + # print(f'Warning: Bad ui setting value: {key}: {saved_value}; Default value "{getattr(obj, field)}" will be used instead.') + else: + setattr(obj, field, saved_value) + if init_field is not None: + init_field(saved_value) + + if type(x) in [gr.Slider, gr.Radio, gr.Checkbox, gr.Textbox, gr.Number, gr.Dropdown] and x.visible: + apply_field(x, 'visible') + + if type(x) == gr.Slider: + apply_field(x, 'value') + apply_field(x, 'minimum') + apply_field(x, 'maximum') + apply_field(x, 'step') + + if type(x) == gr.Radio: + apply_field(x, 'value', lambda val: val in x.choices) + + if type(x) == gr.Checkbox: + apply_field(x, 'value') + + if type(x) == gr.Textbox: + apply_field(x, 'value') + + if type(x) == gr.Number: + apply_field(x, 'value') + + if type(x) == gr.Dropdown: + def check_dropdown(val): + if getattr(x, 'multiselect', False): + return all([value in x.choices for value in val]) + else: + return val in x.choices + + apply_field(x, 'value', check_dropdown, getattr(x, 'init_field', None)) + + visit(txt2img_interface, loadsave, "txt2img") + visit(img2img_interface, loadsave, "img2img") + visit(extras_interface, loadsave, "extras") + visit(modelmerger_interface, loadsave, "modelmerger") + visit(train_interface, loadsave, "train") + + if not error_loading and (not os.path.exists(ui_config_file) or settings_count != len(ui_settings)): + with open(ui_config_file, "w", encoding="utf8") as file: + json.dump(ui_settings, file, indent=4) + + # Required as a workaround for change() event not triggering when loading values from ui-config.json + interp_description.value = update_interp_description(interp_method.value) + + return demo + + +def reload_javascript(): + head = f'\n' + + inline = f"{localization.localization_js(shared.opts.localization)};" + if cmd_opts.theme is not None: + inline += f"set_theme('{cmd_opts.theme}');" + + for script in modules.scripts.list_scripts("javascript", ".js"): + head += f'\n' + + head += f'\n' + + def template_response(*args, **kwargs): + res = shared.GradioTemplateResponseOriginal(*args, **kwargs) + res.body = res.body.replace(b'', f'{head}'.encode("utf8")) + res.init_headers() + return res + + gradio.routes.templates.TemplateResponse = template_response + + +if not hasattr(shared, 'GradioTemplateResponseOriginal'): + shared.GradioTemplateResponseOriginal = gradio.routes.templates.TemplateResponse + + +def versions_html(): + import torch + import launch + + python_version = ".".join([str(x) for x in sys.version_info[0:3]]) + commit = launch.commit_hash() + short_commit = commit[0:8] + + if shared.xformers_available: + import xformers + xformers_version = xformers.__version__ + else: + xformers_version = "N/A" + + return f""" +python: {python_version} + •  +torch: {getattr(torch, '__long_version__',torch.__version__)} + •  +xformers: {xformers_version} + •  +gradio: {gr.__version__} + •  +commit: {short_commit} + •  +checkpoint: N/A +""" diff --git a/sd/stable-diffusion-webui/modules/ui_common.py b/sd/stable-diffusion-webui/modules/ui_common.py new file mode 100644 index 0000000000000000000000000000000000000000..21ebb0955eec9604d6d41c22eeb1541f70a82580 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/ui_common.py @@ -0,0 +1,206 @@ +import json +import html +import os +import platform +import sys + +import gradio as gr +import subprocess as sp + +from modules import call_queue, shared +from modules.generation_parameters_copypaste import image_from_url_text +import modules.images + +folder_symbol = '\U0001f4c2' # 📂 + + +def update_generation_info(generation_info, html_info, img_index): + try: + generation_info = json.loads(generation_info) + if img_index < 0 or img_index >= len(generation_info["infotexts"]): + return html_info, gr.update() + return plaintext_to_html(generation_info["infotexts"][img_index]), gr.update() + except Exception: + pass + # if the json parse or anything else fails, just return the old html_info + return html_info, gr.update() + + +def plaintext_to_html(text): + text = "

" + "
\n".join([f"{html.escape(x)}" for x in text.split('\n')]) + "

" + return text + + +def save_files(js_data, images, do_make_zip, index): + import csv + filenames = [] + fullfns = [] + + #quick dictionary to class object conversion. Its necessary due apply_filename_pattern requiring it + class MyObject: + def __init__(self, d=None): + if d is not None: + for key, value in d.items(): + setattr(self, key, value) + + data = json.loads(js_data) + + p = MyObject(data) + path = shared.opts.outdir_save + save_to_dirs = shared.opts.use_save_to_dirs_for_ui + extension: str = shared.opts.samples_format + start_index = 0 + + if index > -1 and shared.opts.save_selected_only and (index >= data["index_of_first_image"]): # ensures we are looking at a specific non-grid picture, and we have save_selected_only + + images = [images[index]] + start_index = index + + os.makedirs(shared.opts.outdir_save, exist_ok=True) + + with open(os.path.join(shared.opts.outdir_save, "log.csv"), "a", encoding="utf8", newline='') as file: + at_start = file.tell() == 0 + writer = csv.writer(file) + if at_start: + writer.writerow(["prompt", "seed", "width", "height", "sampler", "cfgs", "steps", "filename", "negative_prompt"]) + + for image_index, filedata in enumerate(images, start_index): + image = image_from_url_text(filedata) + + is_grid = image_index < p.index_of_first_image + i = 0 if is_grid else (image_index - p.index_of_first_image) + + fullfn, txt_fullfn = modules.images.save_image(image, path, "", seed=p.all_seeds[i], prompt=p.all_prompts[i], extension=extension, info=p.infotexts[image_index], grid=is_grid, p=p, save_to_dirs=save_to_dirs) + + filename = os.path.relpath(fullfn, path) + filenames.append(filename) + fullfns.append(fullfn) + if txt_fullfn: + filenames.append(os.path.basename(txt_fullfn)) + fullfns.append(txt_fullfn) + + writer.writerow([data["prompt"], data["seed"], data["width"], data["height"], data["sampler_name"], data["cfg_scale"], data["steps"], filenames[0], data["negative_prompt"]]) + + # Make Zip + if do_make_zip: + zip_filepath = os.path.join(path, "images.zip") + + from zipfile import ZipFile + with ZipFile(zip_filepath, "w") as zip_file: + for i in range(len(fullfns)): + with open(fullfns[i], mode="rb") as f: + zip_file.writestr(filenames[i], f.read()) + fullfns.insert(0, zip_filepath) + + return gr.File.update(value=fullfns, visible=True), plaintext_to_html(f"Saved: {filenames[0]}") + + +def create_output_panel(tabname, outdir): + from modules import shared + import modules.generation_parameters_copypaste as parameters_copypaste + + def open_folder(f): + if not os.path.exists(f): + print(f'Folder "{f}" does not exist. After you create an image, the folder will be created.') + return + elif not os.path.isdir(f): + print(f""" +WARNING +An open_folder request was made with an argument that is not a folder. +This could be an error or a malicious attempt to run code on your computer. +Requested path was: {f} +""", file=sys.stderr) + return + + if not shared.cmd_opts.hide_ui_dir_config: + path = os.path.normpath(f) + if platform.system() == "Windows": + os.startfile(path) + elif platform.system() == "Darwin": + sp.Popen(["open", path]) + elif "microsoft-standard-WSL2" in platform.uname().release: + sp.Popen(["wsl-open", path]) + else: + sp.Popen(["xdg-open", path]) + + with gr.Column(variant='panel', elem_id=f"{tabname}_results"): + with gr.Group(elem_id=f"{tabname}_gallery_container"): + result_gallery = gr.Gallery(label='Output', show_label=False, elem_id=f"{tabname}_gallery").style(grid=4) + + generation_info = None + with gr.Column(): + with gr.Row(elem_id=f"image_buttons_{tabname}"): + open_folder_button = gr.Button(folder_symbol, elem_id="hidden_element" if shared.cmd_opts.hide_ui_dir_config else f'open_folder_{tabname}') + + if tabname != "extras": + save = gr.Button('Save', elem_id=f'save_{tabname}') + save_zip = gr.Button('Zip', elem_id=f'save_zip_{tabname}') + + buttons = parameters_copypaste.create_buttons(["img2img", "inpaint", "extras"]) + + open_folder_button.click( + fn=lambda: open_folder(shared.opts.outdir_samples or outdir), + inputs=[], + outputs=[], + ) + + if tabname != "extras": + with gr.Row(): + download_files = gr.File(None, file_count="multiple", interactive=False, show_label=False, visible=False, elem_id=f'download_files_{tabname}') + + with gr.Group(): + html_info = gr.HTML(elem_id=f'html_info_{tabname}') + html_log = gr.HTML(elem_id=f'html_log_{tabname}') + + generation_info = gr.Textbox(visible=False, elem_id=f'generation_info_{tabname}') + if tabname == 'txt2img' or tabname == 'img2img': + generation_info_button = gr.Button(visible=False, elem_id=f"{tabname}_generation_info_button") + generation_info_button.click( + fn=update_generation_info, + _js="function(x, y, z){ return [x, y, selected_gallery_index()] }", + inputs=[generation_info, html_info, html_info], + outputs=[html_info, html_info], + ) + + save.click( + fn=call_queue.wrap_gradio_call(save_files), + _js="(x, y, z, w) => [x, y, false, selected_gallery_index()]", + inputs=[ + generation_info, + result_gallery, + html_info, + html_info, + ], + outputs=[ + download_files, + html_log, + ], + show_progress=False, + ) + + save_zip.click( + fn=call_queue.wrap_gradio_call(save_files), + _js="(x, y, z, w) => [x, y, true, selected_gallery_index()]", + inputs=[ + generation_info, + result_gallery, + html_info, + html_info, + ], + outputs=[ + download_files, + html_log, + ] + ) + + else: + html_info_x = gr.HTML(elem_id=f'html_info_x_{tabname}') + html_info = gr.HTML(elem_id=f'html_info_{tabname}') + html_log = gr.HTML(elem_id=f'html_log_{tabname}') + + for paste_tabname, paste_button in buttons.items(): + parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding( + paste_button=paste_button, tabname=paste_tabname, source_tabname="txt2img" if tabname == "txt2img" else None, source_image_component=result_gallery + )) + + return result_gallery, generation_info if tabname != "extras" else html_info_x, html_info, html_log diff --git a/sd/stable-diffusion-webui/modules/ui_components.py b/sd/stable-diffusion-webui/modules/ui_components.py new file mode 100644 index 0000000000000000000000000000000000000000..d239d3f70938942f625f5f49e9398fcde10016bf --- /dev/null +++ b/sd/stable-diffusion-webui/modules/ui_components.py @@ -0,0 +1,58 @@ +import gradio as gr + + +class ToolButton(gr.Button, gr.components.FormComponent): + """Small button with single emoji as text, fits inside gradio forms""" + + def __init__(self, **kwargs): + super().__init__(variant="tool", **kwargs) + + def get_block_name(self): + return "button" + + +class ToolButtonTop(gr.Button, gr.components.FormComponent): + """Small button with single emoji as text, with extra margin at top, fits inside gradio forms""" + + def __init__(self, **kwargs): + super().__init__(variant="tool-top", **kwargs) + + def get_block_name(self): + return "button" + + +class FormRow(gr.Row, gr.components.FormComponent): + """Same as gr.Row but fits inside gradio forms""" + + def get_block_name(self): + return "row" + + +class FormGroup(gr.Group, gr.components.FormComponent): + """Same as gr.Row but fits inside gradio forms""" + + def get_block_name(self): + return "group" + + +class FormHTML(gr.HTML, gr.components.FormComponent): + """Same as gr.HTML but fits inside gradio forms""" + + def get_block_name(self): + return "html" + + +class FormColorPicker(gr.ColorPicker, gr.components.FormComponent): + """Same as gr.ColorPicker but fits inside gradio forms""" + + def get_block_name(self): + return "colorpicker" + + +class DropdownMulti(gr.Dropdown): + """Same as gr.Dropdown but always multiselect""" + def __init__(self, **kwargs): + super().__init__(multiselect=True, **kwargs) + + def get_block_name(self): + return "dropdown" diff --git a/sd/stable-diffusion-webui/modules/ui_extensions.py b/sd/stable-diffusion-webui/modules/ui_extensions.py new file mode 100644 index 0000000000000000000000000000000000000000..12f395cef3a6e1e0ad28d1577c0208794b897335 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/ui_extensions.py @@ -0,0 +1,354 @@ +import json +import os.path +import shutil +import sys +import time +import traceback + +import git + +import gradio as gr +import html +import shutil +import errno + +from modules import extensions, shared, paths +from modules.call_queue import wrap_gradio_gpu_call + +available_extensions = {"extensions": []} + + +def check_access(): + assert not shared.cmd_opts.disable_extension_access, "extension access disabled because of command line flags" + + +def apply_and_restart(disable_list, update_list): + check_access() + + disabled = json.loads(disable_list) + assert type(disabled) == list, f"wrong disable_list data for apply_and_restart: {disable_list}" + + update = json.loads(update_list) + assert type(update) == list, f"wrong update_list data for apply_and_restart: {update_list}" + + update = set(update) + + for ext in extensions.extensions: + if ext.name not in update: + continue + + try: + ext.fetch_and_reset_hard() + except Exception: + print(f"Error getting updates for {ext.name}:", file=sys.stderr) + print(traceback.format_exc(), file=sys.stderr) + + shared.opts.disabled_extensions = disabled + shared.opts.save(shared.config_filename) + + shared.state.interrupt() + shared.state.need_restart = True + + +def check_updates(id_task, disable_list): + check_access() + + disabled = json.loads(disable_list) + assert type(disabled) == list, f"wrong disable_list data for apply_and_restart: {disable_list}" + + exts = [ext for ext in extensions.extensions if ext.remote is not None and ext.name not in disabled] + shared.state.job_count = len(exts) + + for ext in exts: + shared.state.textinfo = ext.name + + try: + ext.check_updates() + except Exception: + print(f"Error checking updates for {ext.name}:", file=sys.stderr) + print(traceback.format_exc(), file=sys.stderr) + + shared.state.nextjob() + + return extension_table(), "" + + +def extension_table(): + code = f""" + + + + + + + + + + + """ + + for ext in extensions.extensions: + remote = f"""{html.escape("built-in" if ext.is_builtin else ext.remote or '')}""" + + if ext.can_update: + ext_status = f"""""" + else: + ext_status = ext.status + + code += f""" + + + + + {ext_status} + + """ + + code += """ + +
ExtensionURLVersionUpdate
{remote}{ext.version}
+ """ + + return code + + +def normalize_git_url(url): + if url is None: + return "" + + url = url.replace(".git", "") + return url + + +def install_extension_from_url(dirname, url): + check_access() + + assert url, 'No URL specified' + + if dirname is None or dirname == "": + *parts, last_part = url.split('/') + last_part = normalize_git_url(last_part) + + dirname = last_part + + target_dir = os.path.join(extensions.extensions_dir, dirname) + assert not os.path.exists(target_dir), f'Extension directory already exists: {target_dir}' + + normalized_url = normalize_git_url(url) + assert len([x for x in extensions.extensions if normalize_git_url(x.remote) == normalized_url]) == 0, 'Extension with this URL is already installed' + + tmpdir = os.path.join(paths.data_path, "tmp", dirname) + + try: + shutil.rmtree(tmpdir, True) + + repo = git.Repo.clone_from(url, tmpdir) + repo.remote().fetch() + + try: + os.rename(tmpdir, target_dir) + except OSError as err: + # TODO what does this do on windows? I think it'll be a different error code but I don't have a system to check it + # Shouldn't cause any new issues at least but we probably want to handle it there too. + if err.errno == errno.EXDEV: + # Cross device link, typical in docker or when tmp/ and extensions/ are on different file systems + # Since we can't use a rename, do the slower but more versitile shutil.move() + shutil.move(tmpdir, target_dir) + else: + # Something else, not enough free space, permissions, etc. rethrow it so that it gets handled. + raise(err) + + import launch + launch.run_extension_installer(target_dir) + + extensions.list_extensions() + return [extension_table(), html.escape(f"Installed into {target_dir}. Use Installed tab to restart.")] + finally: + shutil.rmtree(tmpdir, True) + + +def install_extension_from_index(url, hide_tags, sort_column): + ext_table, message = install_extension_from_url(None, url) + + code, _ = refresh_available_extensions_from_data(hide_tags, sort_column) + + return code, ext_table, message + + +def refresh_available_extensions(url, hide_tags, sort_column): + global available_extensions + + import urllib.request + with urllib.request.urlopen(url) as response: + text = response.read() + + available_extensions = json.loads(text) + + code, tags = refresh_available_extensions_from_data(hide_tags, sort_column) + + return url, code, gr.CheckboxGroup.update(choices=tags), '' + + +def refresh_available_extensions_for_tags(hide_tags, sort_column): + code, _ = refresh_available_extensions_from_data(hide_tags, sort_column) + + return code, '' + + +sort_ordering = [ + # (reverse, order_by_function) + (True, lambda x: x.get('added', 'z')), + (False, lambda x: x.get('added', 'z')), + (False, lambda x: x.get('name', 'z')), + (True, lambda x: x.get('name', 'z')), + (False, lambda x: 'z'), +] + + +def refresh_available_extensions_from_data(hide_tags, sort_column): + extlist = available_extensions["extensions"] + installed_extension_urls = {normalize_git_url(extension.remote): extension.name for extension in extensions.extensions} + + tags = available_extensions.get("tags", {}) + tags_to_hide = set(hide_tags) + hidden = 0 + + code = f""" + + + + + + + + + + """ + + sort_reverse, sort_function = sort_ordering[sort_column if 0 <= sort_column < len(sort_ordering) else 0] + + for ext in sorted(extlist, key=sort_function, reverse=sort_reverse): + name = ext.get("name", "noname") + added = ext.get('added', 'unknown') + url = ext.get("url", None) + description = ext.get("description", "") + extension_tags = ext.get("tags", []) + + if url is None: + continue + + existing = installed_extension_urls.get(normalize_git_url(url), None) + extension_tags = extension_tags + ["installed"] if existing else extension_tags + + if len([x for x in extension_tags if x in tags_to_hide]) > 0: + hidden += 1 + continue + + install_code = f"""""" + + tags_text = ", ".join([f"{x}" for x in extension_tags]) + + code += f""" + + + + + + + """ + + for tag in [x for x in extension_tags if x not in tags]: + tags[tag] = tag + + code += """ + +
ExtensionDescriptionAction
{html.escape(name)}
{tags_text}
{html.escape(description)}

Added: {html.escape(added)}

{install_code}
+ """ + + if hidden > 0: + code += f"

Extension hidden: {hidden}

" + + return code, list(tags) + + +def create_ui(): + import modules.ui + + with gr.Blocks(analytics_enabled=False) as ui: + with gr.Tabs(elem_id="tabs_extensions") as tabs: + with gr.TabItem("Installed"): + + with gr.Row(elem_id="extensions_installed_top"): + apply = gr.Button(value="Apply and restart UI", variant="primary") + check = gr.Button(value="Check for updates") + extensions_disabled_list = gr.Text(elem_id="extensions_disabled_list", visible=False).style(container=False) + extensions_update_list = gr.Text(elem_id="extensions_update_list", visible=False).style(container=False) + + info = gr.HTML() + extensions_table = gr.HTML(lambda: extension_table()) + + apply.click( + fn=apply_and_restart, + _js="extensions_apply", + inputs=[extensions_disabled_list, extensions_update_list], + outputs=[], + ) + + check.click( + fn=wrap_gradio_gpu_call(check_updates, extra_outputs=[gr.update()]), + _js="extensions_check", + inputs=[info, extensions_disabled_list], + outputs=[extensions_table, info], + ) + + with gr.TabItem("Available"): + with gr.Row(): + refresh_available_extensions_button = gr.Button(value="Load from:", variant="primary") + available_extensions_index = gr.Text(value="https://raw.githubusercontent.com/wiki/AUTOMATIC1111/stable-diffusion-webui/Extensions-index.md", label="Extension index URL").style(container=False) + extension_to_install = gr.Text(elem_id="extension_to_install", visible=False) + install_extension_button = gr.Button(elem_id="install_extension_button", visible=False) + + with gr.Row(): + hide_tags = gr.CheckboxGroup(value=["ads", "localization", "installed"], label="Hide extensions with tags", choices=["script", "ads", "localization", "installed"]) + sort_column = gr.Radio(value="newest first", label="Order", choices=["newest first", "oldest first", "a-z", "z-a", "internal order", ], type="index") + + install_result = gr.HTML() + available_extensions_table = gr.HTML() + + refresh_available_extensions_button.click( + fn=modules.ui.wrap_gradio_call(refresh_available_extensions, extra_outputs=[gr.update(), gr.update(), gr.update()]), + inputs=[available_extensions_index, hide_tags, sort_column], + outputs=[available_extensions_index, available_extensions_table, hide_tags, install_result], + ) + + install_extension_button.click( + fn=modules.ui.wrap_gradio_call(install_extension_from_index, extra_outputs=[gr.update(), gr.update()]), + inputs=[extension_to_install, hide_tags, sort_column], + outputs=[available_extensions_table, extensions_table, install_result], + ) + + hide_tags.change( + fn=modules.ui.wrap_gradio_call(refresh_available_extensions_for_tags, extra_outputs=[gr.update()]), + inputs=[hide_tags, sort_column], + outputs=[available_extensions_table, install_result] + ) + + sort_column.change( + fn=modules.ui.wrap_gradio_call(refresh_available_extensions_for_tags, extra_outputs=[gr.update()]), + inputs=[hide_tags, sort_column], + outputs=[available_extensions_table, install_result] + ) + + with gr.TabItem("Install from URL"): + install_url = gr.Text(label="URL for extension's git repository") + install_dirname = gr.Text(label="Local directory name", placeholder="Leave empty for auto") + install_button = gr.Button(value="Install", variant="primary") + install_result = gr.HTML(elem_id="extension_install_result") + + install_button.click( + fn=modules.ui.wrap_gradio_call(install_extension_from_url, extra_outputs=[gr.update()]), + inputs=[install_dirname, install_url], + outputs=[extensions_table, install_result], + ) + + return ui diff --git a/sd/stable-diffusion-webui/modules/ui_extra_networks.py b/sd/stable-diffusion-webui/modules/ui_extra_networks.py new file mode 100644 index 0000000000000000000000000000000000000000..e788319dad4aba4484437af166b7c0fccd651798 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/ui_extra_networks.py @@ -0,0 +1,251 @@ +import glob +import os.path +import urllib.parse +from pathlib import Path + +from modules import shared +import gradio as gr +import json +import html + +from modules.generation_parameters_copypaste import image_from_url_text + +extra_pages = [] +allowed_dirs = set() + + +def register_page(page): + """registers extra networks page for the UI; recommend doing it in on_before_ui() callback for extensions""" + + extra_pages.append(page) + allowed_dirs.clear() + allowed_dirs.update(set(sum([x.allowed_directories_for_previews() for x in extra_pages], []))) + + +def add_pages_to_demo(app): + def fetch_file(filename: str = ""): + from starlette.responses import FileResponse + + if not any([Path(x).absolute() in Path(filename).absolute().parents for x in allowed_dirs]): + raise ValueError(f"File cannot be fetched: {filename}. Must be in one of directories registered by extra pages.") + + ext = os.path.splitext(filename)[1].lower() + if ext not in (".png", ".jpg"): + raise ValueError(f"File cannot be fetched: {filename}. Only png and jpg.") + + # would profit from returning 304 + return FileResponse(filename, headers={"Accept-Ranges": "bytes"}) + + app.add_api_route("/sd_extra_networks/thumb", fetch_file, methods=["GET"]) + + +class ExtraNetworksPage: + def __init__(self, title): + self.title = title + self.name = title.lower() + self.card_page = shared.html("extra-networks-card.html") + self.allow_negative_prompt = False + + def refresh(self): + pass + + def link_preview(self, filename): + return "./sd_extra_networks/thumb?filename=" + urllib.parse.quote(filename.replace('\\', '/')) + "&mtime=" + str(os.path.getmtime(filename)) + + def search_terms_from_path(self, filename, possible_directories=None): + abspath = os.path.abspath(filename) + + for parentdir in (possible_directories if possible_directories is not None else self.allowed_directories_for_previews()): + parentdir = os.path.abspath(parentdir) + if abspath.startswith(parentdir): + return abspath[len(parentdir):].replace('\\', '/') + + return "" + + def create_html(self, tabname): + view = shared.opts.extra_networks_default_view + items_html = '' + + subdirs = {} + for parentdir in [os.path.abspath(x) for x in self.allowed_directories_for_previews()]: + for x in glob.glob(os.path.join(parentdir, '**/*'), recursive=True): + if not os.path.isdir(x): + continue + + subdir = os.path.abspath(x)[len(parentdir):].replace("\\", "/") + while subdir.startswith("/"): + subdir = subdir[1:] + + is_empty = len(os.listdir(x)) == 0 + if not is_empty and not subdir.endswith("/"): + subdir = subdir + "/" + + subdirs[subdir] = 1 + + if subdirs: + subdirs = {"": 1, **subdirs} + + subdirs_html = "".join([f""" + +""" for subdir in subdirs]) + + for item in self.list_items(): + items_html += self.create_html_for_item(item, tabname) + + if items_html == '': + dirs = "".join([f"
  • {x}
  • " for x in self.allowed_directories_for_previews()]) + items_html = shared.html("extra-networks-no-cards.html").format(dirs=dirs) + + self_name_id = self.name.replace(" ", "_") + + res = f""" +
    +{subdirs_html} +
    +
    +{items_html} +
    +""" + + return res + + def list_items(self): + raise NotImplementedError() + + def allowed_directories_for_previews(self): + return [] + + def create_html_for_item(self, item, tabname): + preview = item.get("preview", None) + + onclick = item.get("onclick", None) + if onclick is None: + onclick = '"' + html.escape(f"""return cardClicked({json.dumps(tabname)}, {item["prompt"]}, {"true" if self.allow_negative_prompt else "false"})""") + '"' + + args = { + "preview_html": "style='background-image: url(\"" + html.escape(preview) + "\")'" if preview else '', + "prompt": item.get("prompt", None), + "tabname": json.dumps(tabname), + "local_preview": json.dumps(item["local_preview"]), + "name": item["name"], + "card_clicked": onclick, + "save_card_preview": '"' + html.escape(f"""return saveCardPreview(event, {json.dumps(tabname)}, {json.dumps(item["local_preview"])})""") + '"', + "search_term": item.get("search_term", ""), + } + + return self.card_page.format(**args) + + +def intialize(): + extra_pages.clear() + + +class ExtraNetworksUi: + def __init__(self): + self.pages = None + self.stored_extra_pages = None + + self.button_save_preview = None + self.preview_target_filename = None + + self.tabname = None + + +def pages_in_preferred_order(pages): + tab_order = [x.lower().strip() for x in shared.opts.ui_extra_networks_tab_reorder.split(",")] + + def tab_name_score(name): + name = name.lower() + for i, possible_match in enumerate(tab_order): + if possible_match in name: + return i + + return len(pages) + + tab_scores = {page.name: (tab_name_score(page.name), original_index) for original_index, page in enumerate(pages)} + + return sorted(pages, key=lambda x: tab_scores[x.name]) + + +def create_ui(container, button, tabname): + ui = ExtraNetworksUi() + ui.pages = [] + ui.stored_extra_pages = pages_in_preferred_order(extra_pages.copy()) + ui.tabname = tabname + + with gr.Tabs(elem_id=tabname+"_extra_tabs") as tabs: + for page in ui.stored_extra_pages: + with gr.Tab(page.title): + page_elem = gr.HTML(page.create_html(ui.tabname)) + ui.pages.append(page_elem) + + filter = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", placeholder="Search...", visible=False) + button_refresh = gr.Button('Refresh', elem_id=tabname+"_extra_refresh") + button_close = gr.Button('Close', elem_id=tabname+"_extra_close") + + ui.button_save_preview = gr.Button('Save preview', elem_id=tabname+"_save_preview", visible=False) + ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=tabname+"_preview_filename", visible=False) + + def toggle_visibility(is_visible): + is_visible = not is_visible + return is_visible, gr.update(visible=is_visible) + + state_visible = gr.State(value=False) + button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container]) + button_close.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container]) + + def refresh(): + res = [] + + for pg in ui.stored_extra_pages: + pg.refresh() + res.append(pg.create_html(ui.tabname)) + + return res + + button_refresh.click(fn=refresh, inputs=[], outputs=ui.pages) + + return ui + + +def path_is_parent(parent_path, child_path): + parent_path = os.path.abspath(parent_path) + child_path = os.path.abspath(child_path) + + return child_path.startswith(parent_path) + + +def setup_ui(ui, gallery): + def save_preview(index, images, filename): + if len(images) == 0: + print("There is no image in gallery to save as a preview.") + return [page.create_html(ui.tabname) for page in ui.stored_extra_pages] + + index = int(index) + index = 0 if index < 0 else index + index = len(images) - 1 if index >= len(images) else index + + img_info = images[index if index >= 0 else 0] + image = image_from_url_text(img_info) + + is_allowed = False + for extra_page in ui.stored_extra_pages: + if any([path_is_parent(x, filename) for x in extra_page.allowed_directories_for_previews()]): + is_allowed = True + break + + assert is_allowed, f'writing to {filename} is not allowed' + + image.save(filename) + + return [page.create_html(ui.tabname) for page in ui.stored_extra_pages] + + ui.button_save_preview.click( + fn=save_preview, + _js="function(x, y, z){return [selected_gallery_index(), y, z]}", + inputs=[ui.preview_target_filename, gallery, ui.preview_target_filename], + outputs=[*ui.pages] + ) + diff --git a/sd/stable-diffusion-webui/modules/ui_extra_networks_checkpoints.py b/sd/stable-diffusion-webui/modules/ui_extra_networks_checkpoints.py new file mode 100644 index 0000000000000000000000000000000000000000..766e2c2499f0a1866e88424a319b99a9df973fc4 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/ui_extra_networks_checkpoints.py @@ -0,0 +1,39 @@ +import html +import json +import os +import urllib.parse + +from modules import shared, ui_extra_networks, sd_models + + +class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): + def __init__(self): + super().__init__('Checkpoints') + + def refresh(self): + shared.refresh_checkpoints() + + def list_items(self): + checkpoint: sd_models.CheckpointInfo + for name, checkpoint in sd_models.checkpoints_list.items(): + path, ext = os.path.splitext(checkpoint.filename) + previews = [path + ".png", path + ".preview.png"] + + preview = None + for file in previews: + if os.path.isfile(file): + preview = self.link_preview(file) + break + + yield { + "name": checkpoint.name_for_extra, + "filename": path, + "preview": preview, + "search_term": self.search_terms_from_path(checkpoint.filename) + " " + (checkpoint.sha256 or ""), + "onclick": '"' + html.escape(f"""return selectCheckpoint({json.dumps(name)})""") + '"', + "local_preview": path + ".png", + } + + def allowed_directories_for_previews(self): + return [v for v in [shared.cmd_opts.ckpt_dir, sd_models.model_path] if v is not None] + diff --git a/sd/stable-diffusion-webui/modules/ui_extra_networks_hypernets.py b/sd/stable-diffusion-webui/modules/ui_extra_networks_hypernets.py new file mode 100644 index 0000000000000000000000000000000000000000..5fe6516a71e7ca5203fdacec3f750494d5650efd --- /dev/null +++ b/sd/stable-diffusion-webui/modules/ui_extra_networks_hypernets.py @@ -0,0 +1,36 @@ +import json +import os + +from modules import shared, ui_extra_networks + + +class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage): + def __init__(self): + super().__init__('Hypernetworks') + + def refresh(self): + shared.reload_hypernetworks() + + def list_items(self): + for name, path in shared.hypernetworks.items(): + path, ext = os.path.splitext(path) + previews = [path + ".png", path + ".preview.png"] + + preview = None + for file in previews: + if os.path.isfile(file): + preview = self.link_preview(file) + break + + yield { + "name": name, + "filename": path, + "preview": preview, + "search_term": self.search_terms_from_path(path), + "prompt": json.dumps(f""), + "local_preview": path + ".png", + } + + def allowed_directories_for_previews(self): + return [shared.cmd_opts.hypernetwork_dir] + diff --git a/sd/stable-diffusion-webui/modules/ui_extra_networks_textual_inversion.py b/sd/stable-diffusion-webui/modules/ui_extra_networks_textual_inversion.py new file mode 100644 index 0000000000000000000000000000000000000000..47491b2f09ccc960e2e237097f9d9e78075d25c0 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/ui_extra_networks_textual_inversion.py @@ -0,0 +1,34 @@ +import json +import os + +from modules import ui_extra_networks, sd_hijack + + +class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage): + def __init__(self): + super().__init__('Textual Inversion') + self.allow_negative_prompt = True + + def refresh(self): + sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings(force_reload=True) + + def list_items(self): + for embedding in sd_hijack.model_hijack.embedding_db.word_embeddings.values(): + path, ext = os.path.splitext(embedding.filename) + preview_file = path + ".preview.png" + + preview = None + if os.path.isfile(preview_file): + preview = self.link_preview(preview_file) + + yield { + "name": embedding.name, + "filename": embedding.filename, + "preview": preview, + "search_term": self.search_terms_from_path(embedding.filename), + "prompt": json.dumps(embedding.name), + "local_preview": path + ".preview.png", + } + + def allowed_directories_for_previews(self): + return list(sd_hijack.model_hijack.embedding_db.embedding_dirs) diff --git a/sd/stable-diffusion-webui/modules/ui_postprocessing.py b/sd/stable-diffusion-webui/modules/ui_postprocessing.py new file mode 100644 index 0000000000000000000000000000000000000000..7789347028ecb309607038d0bc79eff934f45711 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/ui_postprocessing.py @@ -0,0 +1,57 @@ +import gradio as gr +from modules import scripts_postprocessing, scripts, shared, gfpgan_model, codeformer_model, ui_common, postprocessing, call_queue +import modules.generation_parameters_copypaste as parameters_copypaste + + +def create_ui(): + tab_index = gr.State(value=0) + + with gr.Row().style(equal_height=False, variant='compact'): + with gr.Column(variant='compact'): + with gr.Tabs(elem_id="mode_extras"): + with gr.TabItem('Single Image', elem_id="extras_single_tab") as tab_single: + extras_image = gr.Image(label="Source", source="upload", interactive=True, type="pil", elem_id="extras_image") + + with gr.TabItem('Batch Process', elem_id="extras_batch_process_tab") as tab_batch: + image_batch = gr.File(label="Batch Process", file_count="multiple", interactive=True, type="file", elem_id="extras_image_batch") + + with gr.TabItem('Batch from Directory', elem_id="extras_batch_directory_tab") as tab_batch_dir: + extras_batch_input_dir = gr.Textbox(label="Input directory", **shared.hide_dirs, placeholder="A directory on the same machine where the server is running.", elem_id="extras_batch_input_dir") + extras_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs, placeholder="Leave blank to save images to the default path.", elem_id="extras_batch_output_dir") + show_extras_results = gr.Checkbox(label='Show result images', value=True, elem_id="extras_show_extras_results") + + submit = gr.Button('Generate', elem_id="extras_generate", variant='primary') + + script_inputs = scripts.scripts_postproc.setup_ui() + + with gr.Column(): + result_images, html_info_x, html_info, html_log = ui_common.create_output_panel("extras", shared.opts.outdir_extras_samples) + + tab_single.select(fn=lambda: 0, inputs=[], outputs=[tab_index]) + tab_batch.select(fn=lambda: 1, inputs=[], outputs=[tab_index]) + tab_batch_dir.select(fn=lambda: 2, inputs=[], outputs=[tab_index]) + + submit.click( + fn=call_queue.wrap_gradio_gpu_call(postprocessing.run_postprocessing, extra_outputs=[None, '']), + inputs=[ + tab_index, + extras_image, + image_batch, + extras_batch_input_dir, + extras_batch_output_dir, + show_extras_results, + *script_inputs + ], + outputs=[ + result_images, + html_info_x, + html_info, + ] + ) + + parameters_copypaste.add_paste_fields("extras", extras_image, None) + + extras_image.change( + fn=scripts.scripts_postproc.image_changed, + inputs=[], outputs=[] + ) diff --git a/sd/stable-diffusion-webui/modules/ui_tempdir.py b/sd/stable-diffusion-webui/modules/ui_tempdir.py new file mode 100644 index 0000000000000000000000000000000000000000..126f73a21d71070887fd094beaf0fe6d7e12df9c --- /dev/null +++ b/sd/stable-diffusion-webui/modules/ui_tempdir.py @@ -0,0 +1,82 @@ +import os +import tempfile +from collections import namedtuple +from pathlib import Path + +import gradio as gr + +from PIL import PngImagePlugin + +from modules import shared + + +Savedfile = namedtuple("Savedfile", ["name"]) + + +def register_tmp_file(gradio, filename): + if hasattr(gradio, 'temp_file_sets'): # gradio 3.15 + gradio.temp_file_sets[0] = gradio.temp_file_sets[0] | {os.path.abspath(filename)} + + if hasattr(gradio, 'temp_dirs'): # gradio 3.9 + gradio.temp_dirs = gradio.temp_dirs | {os.path.abspath(os.path.dirname(filename))} + + +def check_tmp_file(gradio, filename): + if hasattr(gradio, 'temp_file_sets'): + return any([filename in fileset for fileset in gradio.temp_file_sets]) + + if hasattr(gradio, 'temp_dirs'): + return any(Path(temp_dir).resolve() in Path(filename).resolve().parents for temp_dir in gradio.temp_dirs) + + return False + + +def save_pil_to_file(pil_image, dir=None): + already_saved_as = getattr(pil_image, 'already_saved_as', None) + if already_saved_as and os.path.isfile(already_saved_as): + register_tmp_file(shared.demo, already_saved_as) + + file_obj = Savedfile(already_saved_as) + return file_obj + + if shared.opts.temp_dir != "": + dir = shared.opts.temp_dir + + use_metadata = False + metadata = PngImagePlugin.PngInfo() + for key, value in pil_image.info.items(): + if isinstance(key, str) and isinstance(value, str): + metadata.add_text(key, value) + use_metadata = True + + file_obj = tempfile.NamedTemporaryFile(delete=False, suffix=".png", dir=dir) + pil_image.save(file_obj, pnginfo=(metadata if use_metadata else None)) + return file_obj + + +# override save to file function so that it also writes PNG info +gr.processing_utils.save_pil_to_file = save_pil_to_file + + +def on_tmpdir_changed(): + if shared.opts.temp_dir == "" or shared.demo is None: + return + + os.makedirs(shared.opts.temp_dir, exist_ok=True) + + register_tmp_file(shared.demo, os.path.join(shared.opts.temp_dir, "x")) + + +def cleanup_tmpdr(): + temp_dir = shared.opts.temp_dir + if temp_dir == "" or not os.path.isdir(temp_dir): + return + + for root, dirs, files in os.walk(temp_dir, topdown=False): + for name in files: + _, extension = os.path.splitext(name) + if extension != ".png": + continue + + filename = os.path.join(root, name) + os.remove(filename) diff --git a/sd/stable-diffusion-webui/modules/upscaler.py b/sd/stable-diffusion-webui/modules/upscaler.py new file mode 100644 index 0000000000000000000000000000000000000000..e2eaa7308af0091b6e8f407e889b2e446679e149 --- /dev/null +++ b/sd/stable-diffusion-webui/modules/upscaler.py @@ -0,0 +1,145 @@ +import os +from abc import abstractmethod + +import PIL +import numpy as np +import torch +from PIL import Image + +import modules.shared +from modules import modelloader, shared + +LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS) +NEAREST = (Image.Resampling.NEAREST if hasattr(Image, 'Resampling') else Image.NEAREST) + + +class Upscaler: + name = None + model_path = None + model_name = None + model_url = None + enable = True + filter = None + model = None + user_path = None + scalers: [] + tile = True + + def __init__(self, create_dirs=False): + self.mod_pad_h = None + self.tile_size = modules.shared.opts.ESRGAN_tile + self.tile_pad = modules.shared.opts.ESRGAN_tile_overlap + self.device = modules.shared.device + self.img = None + self.output = None + self.scale = 1 + self.half = not modules.shared.cmd_opts.no_half + self.pre_pad = 0 + self.mod_scale = None + + if self.model_path is None and self.name: + self.model_path = os.path.join(shared.models_path, self.name) + if self.model_path and create_dirs: + os.makedirs(self.model_path, exist_ok=True) + + try: + import cv2 + self.can_tile = True + except: + pass + + @abstractmethod + def do_upscale(self, img: PIL.Image, selected_model: str): + return img + + def upscale(self, img: PIL.Image, scale, selected_model: str = None): + self.scale = scale + dest_w = int(img.width * scale) + dest_h = int(img.height * scale) + + for i in range(3): + shape = (img.width, img.height) + + img = self.do_upscale(img, selected_model) + + if shape == (img.width, img.height): + break + + if img.width >= dest_w and img.height >= dest_h: + break + + if img.width != dest_w or img.height != dest_h: + img = img.resize((int(dest_w), int(dest_h)), resample=LANCZOS) + + return img + + @abstractmethod + def load_model(self, path: str): + pass + + def find_models(self, ext_filter=None) -> list: + return modelloader.load_models(model_path=self.model_path, model_url=self.model_url, command_path=self.user_path) + + def update_status(self, prompt): + print(f"\nextras: {prompt}", file=shared.progress_print_out) + + +class UpscalerData: + name = None + data_path = None + scale: int = 4 + scaler: Upscaler = None + model: None + + def __init__(self, name: str, path: str, upscaler: Upscaler = None, scale: int = 4, model=None): + self.name = name + self.data_path = path + self.local_data_path = path + self.scaler = upscaler + self.scale = scale + self.model = model + + +class UpscalerNone(Upscaler): + name = "None" + scalers = [] + + def load_model(self, path): + pass + + def do_upscale(self, img, selected_model=None): + return img + + def __init__(self, dirname=None): + super().__init__(False) + self.scalers = [UpscalerData("None", None, self)] + + +class UpscalerLanczos(Upscaler): + scalers = [] + + def do_upscale(self, img, selected_model=None): + return img.resize((int(img.width * self.scale), int(img.height * self.scale)), resample=LANCZOS) + + def load_model(self, _): + pass + + def __init__(self, dirname=None): + super().__init__(False) + self.name = "Lanczos" + self.scalers = [UpscalerData("Lanczos", None, self)] + + +class UpscalerNearest(Upscaler): + scalers = [] + + def do_upscale(self, img, selected_model=None): + return img.resize((int(img.width * self.scale), int(img.height * self.scale)), resample=NEAREST) + + def load_model(self, _): + pass + + def __init__(self, dirname=None): + super().__init__(False) + self.name = "Nearest" + self.scalers = [UpscalerData("Nearest", None, self)] diff --git a/sd/stable-diffusion-webui/modules/xlmr.py b/sd/stable-diffusion-webui/modules/xlmr.py new file mode 100644 index 0000000000000000000000000000000000000000..beab3fdf55e7bcffd96f3b36679e7a90c0f390dc --- /dev/null +++ b/sd/stable-diffusion-webui/modules/xlmr.py @@ -0,0 +1,137 @@ +from transformers import BertPreTrainedModel,BertModel,BertConfig +import torch.nn as nn +import torch +from transformers.models.xlm_roberta.configuration_xlm_roberta import XLMRobertaConfig +from transformers import XLMRobertaModel,XLMRobertaTokenizer +from typing import Optional + +class BertSeriesConfig(BertConfig): + def __init__(self, vocab_size=30522, hidden_size=768, num_hidden_layers=12, num_attention_heads=12, intermediate_size=3072, hidden_act="gelu", hidden_dropout_prob=0.1, attention_probs_dropout_prob=0.1, max_position_embeddings=512, type_vocab_size=2, initializer_range=0.02, layer_norm_eps=1e-12, pad_token_id=0, position_embedding_type="absolute", use_cache=True, classifier_dropout=None,project_dim=512, pooler_fn="average",learn_encoder=False,model_type='bert',**kwargs): + + super().__init__(vocab_size, hidden_size, num_hidden_layers, num_attention_heads, intermediate_size, hidden_act, hidden_dropout_prob, attention_probs_dropout_prob, max_position_embeddings, type_vocab_size, initializer_range, layer_norm_eps, pad_token_id, position_embedding_type, use_cache, classifier_dropout, **kwargs) + self.project_dim = project_dim + self.pooler_fn = pooler_fn + self.learn_encoder = learn_encoder + +class RobertaSeriesConfig(XLMRobertaConfig): + def __init__(self, pad_token_id=1, bos_token_id=0, eos_token_id=2,project_dim=512,pooler_fn='cls',learn_encoder=False, **kwargs): + super().__init__(pad_token_id=pad_token_id, bos_token_id=bos_token_id, eos_token_id=eos_token_id, **kwargs) + self.project_dim = project_dim + self.pooler_fn = pooler_fn + self.learn_encoder = learn_encoder + + +class BertSeriesModelWithTransformation(BertPreTrainedModel): + + _keys_to_ignore_on_load_unexpected = [r"pooler"] + _keys_to_ignore_on_load_missing = [r"position_ids", r"predictions.decoder.bias"] + config_class = BertSeriesConfig + + def __init__(self, config=None, **kargs): + # modify initialization for autoloading + if config is None: + config = XLMRobertaConfig() + config.attention_probs_dropout_prob= 0.1 + config.bos_token_id=0 + config.eos_token_id=2 + config.hidden_act='gelu' + config.hidden_dropout_prob=0.1 + config.hidden_size=1024 + config.initializer_range=0.02 + config.intermediate_size=4096 + config.layer_norm_eps=1e-05 + config.max_position_embeddings=514 + + config.num_attention_heads=16 + config.num_hidden_layers=24 + config.output_past=True + config.pad_token_id=1 + config.position_embedding_type= "absolute" + + config.type_vocab_size= 1 + config.use_cache=True + config.vocab_size= 250002 + config.project_dim = 768 + config.learn_encoder = False + super().__init__(config) + self.roberta = XLMRobertaModel(config) + self.transformation = nn.Linear(config.hidden_size,config.project_dim) + self.pre_LN=nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.tokenizer = XLMRobertaTokenizer.from_pretrained('xlm-roberta-large') + self.pooler = lambda x: x[:,0] + self.post_init() + + def encode(self,c): + device = next(self.parameters()).device + text = self.tokenizer(c, + truncation=True, + max_length=77, + return_length=False, + return_overflowing_tokens=False, + padding="max_length", + return_tensors="pt") + text["input_ids"] = torch.tensor(text["input_ids"]).to(device) + text["attention_mask"] = torch.tensor( + text['attention_mask']).to(device) + features = self(**text) + return features['projection_state'] + + def forward( + self, + input_ids: Optional[torch.Tensor] = None, + attention_mask: Optional[torch.Tensor] = None, + token_type_ids: Optional[torch.Tensor] = None, + position_ids: Optional[torch.Tensor] = None, + head_mask: Optional[torch.Tensor] = None, + inputs_embeds: Optional[torch.Tensor] = None, + encoder_hidden_states: Optional[torch.Tensor] = None, + encoder_attention_mask: Optional[torch.Tensor] = None, + output_attentions: Optional[bool] = None, + return_dict: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + ) : + r""" + """ + + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + + outputs = self.roberta( + input_ids=input_ids, + attention_mask=attention_mask, + token_type_ids=token_type_ids, + position_ids=position_ids, + head_mask=head_mask, + inputs_embeds=inputs_embeds, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + output_attentions=output_attentions, + output_hidden_states=True, + return_dict=return_dict, + ) + + # last module outputs + sequence_output = outputs[0] + + + # project every module + sequence_output_ln = self.pre_LN(sequence_output) + + # pooler + pooler_output = self.pooler(sequence_output_ln) + pooler_output = self.transformation(pooler_output) + projection_state = self.transformation(outputs.last_hidden_state) + + return { + 'pooler_output':pooler_output, + 'last_hidden_state':outputs.last_hidden_state, + 'hidden_states':outputs.hidden_states, + 'attentions':outputs.attentions, + 'projection_state':projection_state, + 'sequence_out': sequence_output + } + + +class RobertaSeriesModelWithTransformation(BertSeriesModelWithTransformation): + base_model_prefix = 'roberta' + config_class= RobertaSeriesConfig \ No newline at end of file diff --git a/sd/stable-diffusion-webui/requirements_versions.txt b/sd/stable-diffusion-webui/requirements_versions.txt new file mode 100644 index 0000000000000000000000000000000000000000..7e2e15b32f6801fa9bb9db46ff604670ad55ab6c --- /dev/null +++ b/sd/stable-diffusion-webui/requirements_versions.txt @@ -0,0 +1,30 @@ +blendmodes==2022 +transformers==4.25.1 +accelerate==0.12.0 +basicsr==1.4.2 +gfpgan==1.3.8 +gradio==3.16.2 +numpy==1.23.3 +Pillow==9.4.0 +realesrgan==0.3.0 +torch +omegaconf==2.2.3 +pytorch_lightning==1.7.6 +scikit-image==0.19.2 +fonts +font-roboto +timm==0.6.7 +piexif==1.1.3 +einops==0.4.1 +jsonmerge==1.8.0 +clean-fid==0.1.29 +resize-right==0.0.2 +torchdiffeq==0.2.3 +kornia==0.6.7 +lark==1.1.2 +inflection==0.5.1 +GitPython==3.1.27 +torchsde==0.2.5 +safetensors==0.2.7 +httpcore<=0.15 +fastapi==0.90.1 diff --git a/sd/stable-diffusion-webui/script.js b/sd/stable-diffusion-webui/script.js new file mode 100644 index 0000000000000000000000000000000000000000..97e0bfcf9fa3cc6b5823d86b0949ab9c947c6418 --- /dev/null +++ b/sd/stable-diffusion-webui/script.js @@ -0,0 +1,102 @@ +function gradioApp() { + const elems = document.getElementsByTagName('gradio-app') + const gradioShadowRoot = elems.length == 0 ? null : elems[0].shadowRoot + return !!gradioShadowRoot ? gradioShadowRoot : document; +} + +function get_uiCurrentTab() { + return gradioApp().querySelector('#tabs button:not(.border-transparent)') +} + +function get_uiCurrentTabContent() { + return gradioApp().querySelector('.tabitem[id^=tab_]:not([style*="display: none"])') +} + +uiUpdateCallbacks = [] +uiLoadedCallbacks = [] +uiTabChangeCallbacks = [] +optionsChangedCallbacks = [] +let uiCurrentTab = null + +function onUiUpdate(callback){ + uiUpdateCallbacks.push(callback) +} +function onUiLoaded(callback){ + uiLoadedCallbacks.push(callback) +} +function onUiTabChange(callback){ + uiTabChangeCallbacks.push(callback) +} +function onOptionsChanged(callback){ + optionsChangedCallbacks.push(callback) +} + +function runCallback(x, m){ + try { + x(m) + } catch (e) { + (console.error || console.log).call(console, e.message, e); + } +} +function executeCallbacks(queue, m) { + queue.forEach(function(x){runCallback(x, m)}) +} + +var executedOnLoaded = false; + +document.addEventListener("DOMContentLoaded", function() { + var mutationObserver = new MutationObserver(function(m){ + if(!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')){ + executedOnLoaded = true; + executeCallbacks(uiLoadedCallbacks); + } + + executeCallbacks(uiUpdateCallbacks, m); + const newTab = get_uiCurrentTab(); + if ( newTab && ( newTab !== uiCurrentTab ) ) { + uiCurrentTab = newTab; + executeCallbacks(uiTabChangeCallbacks); + } + }); + mutationObserver.observe( gradioApp(), { childList:true, subtree:true }) +}); + +/** + * Add a ctrl+enter as a shortcut to start a generation + */ +document.addEventListener('keydown', function(e) { + var handled = false; + if (e.key !== undefined) { + if((e.key == "Enter" && (e.metaKey || e.ctrlKey || e.altKey))) handled = true; + } else if (e.keyCode !== undefined) { + if((e.keyCode == 13 && (e.metaKey || e.ctrlKey || e.altKey))) handled = true; + } + if (handled) { + button = get_uiCurrentTabContent().querySelector('button[id$=_generate]'); + if (button) { + button.click(); + } + e.preventDefault(); + } +}) + +/** + * checks that a UI element is not in another hidden element or tab content + */ +function uiElementIsVisible(el) { + let isVisible = !el.closest('.\\!hidden'); + if ( ! isVisible ) { + return false; + } + + while( isVisible = el.closest('.tabitem')?.style.display !== 'none' ) { + if ( ! isVisible ) { + return false; + } else if ( el.parentElement ) { + el = el.parentElement + } else { + break; + } + } + return isVisible; +} diff --git a/sd/stable-diffusion-webui/scripts/custom_code.py b/sd/stable-diffusion-webui/scripts/custom_code.py new file mode 100644 index 0000000000000000000000000000000000000000..935c544e3e8b9a9a282108563d4e00074502829a --- /dev/null +++ b/sd/stable-diffusion-webui/scripts/custom_code.py @@ -0,0 +1,41 @@ +import modules.scripts as scripts +import gradio as gr + +from modules.processing import Processed +from modules.shared import opts, cmd_opts, state + +class Script(scripts.Script): + + def title(self): + return "Custom code" + + def show(self, is_img2img): + return cmd_opts.allow_code + + def ui(self, is_img2img): + code = gr.Textbox(label="Python code", lines=1, elem_id=self.elem_id("code")) + + return [code] + + + def run(self, p, code): + assert cmd_opts.allow_code, '--allow-code option must be enabled' + + display_result_data = [[], -1, ""] + + def display(imgs, s=display_result_data[1], i=display_result_data[2]): + display_result_data[0] = imgs + display_result_data[1] = s + display_result_data[2] = i + + from types import ModuleType + compiled = compile(code, '', 'exec') + module = ModuleType("testmodule") + module.__dict__.update(globals()) + module.p = p + module.display = display + exec(compiled, module.__dict__) + + return Processed(p, *display_result_data) + + \ No newline at end of file diff --git a/sd/stable-diffusion-webui/scripts/img2imgalt.py b/sd/stable-diffusion-webui/scripts/img2imgalt.py new file mode 100644 index 0000000000000000000000000000000000000000..65b61533929a018f0cb97a89266154bf569cd40e --- /dev/null +++ b/sd/stable-diffusion-webui/scripts/img2imgalt.py @@ -0,0 +1,216 @@ +from collections import namedtuple + +import numpy as np +from tqdm import trange + +import modules.scripts as scripts +import gradio as gr + +from modules import processing, shared, sd_samplers, prompt_parser, sd_samplers_common +from modules.processing import Processed +from modules.shared import opts, cmd_opts, state + +import torch +import k_diffusion as K + +from PIL import Image +from torch import autocast +from einops import rearrange, repeat + + +def find_noise_for_image(p, cond, uncond, cfg_scale, steps): + x = p.init_latent + + s_in = x.new_ones([x.shape[0]]) + dnw = K.external.CompVisDenoiser(shared.sd_model) + sigmas = dnw.get_sigmas(steps).flip(0) + + shared.state.sampling_steps = steps + + for i in trange(1, len(sigmas)): + shared.state.sampling_step += 1 + + x_in = torch.cat([x] * 2) + sigma_in = torch.cat([sigmas[i] * s_in] * 2) + cond_in = torch.cat([uncond, cond]) + + image_conditioning = torch.cat([p.image_conditioning] * 2) + cond_in = {"c_concat": [image_conditioning], "c_crossattn": [cond_in]} + + c_out, c_in = [K.utils.append_dims(k, x_in.ndim) for k in dnw.get_scalings(sigma_in)] + t = dnw.sigma_to_t(sigma_in) + + eps = shared.sd_model.apply_model(x_in * c_in, t, cond=cond_in) + denoised_uncond, denoised_cond = (x_in + eps * c_out).chunk(2) + + denoised = denoised_uncond + (denoised_cond - denoised_uncond) * cfg_scale + + d = (x - denoised) / sigmas[i] + dt = sigmas[i] - sigmas[i - 1] + + x = x + d * dt + + sd_samplers_common.store_latent(x) + + # This shouldn't be necessary, but solved some VRAM issues + del x_in, sigma_in, cond_in, c_out, c_in, t, + del eps, denoised_uncond, denoised_cond, denoised, d, dt + + shared.state.nextjob() + + return x / x.std() + + +Cached = namedtuple("Cached", ["noise", "cfg_scale", "steps", "latent", "original_prompt", "original_negative_prompt", "sigma_adjustment"]) + + +# Based on changes suggested by briansemrau in https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/736 +def find_noise_for_image_sigma_adjustment(p, cond, uncond, cfg_scale, steps): + x = p.init_latent + + s_in = x.new_ones([x.shape[0]]) + dnw = K.external.CompVisDenoiser(shared.sd_model) + sigmas = dnw.get_sigmas(steps).flip(0) + + shared.state.sampling_steps = steps + + for i in trange(1, len(sigmas)): + shared.state.sampling_step += 1 + + x_in = torch.cat([x] * 2) + sigma_in = torch.cat([sigmas[i - 1] * s_in] * 2) + cond_in = torch.cat([uncond, cond]) + + image_conditioning = torch.cat([p.image_conditioning] * 2) + cond_in = {"c_concat": [image_conditioning], "c_crossattn": [cond_in]} + + c_out, c_in = [K.utils.append_dims(k, x_in.ndim) for k in dnw.get_scalings(sigma_in)] + + if i == 1: + t = dnw.sigma_to_t(torch.cat([sigmas[i] * s_in] * 2)) + else: + t = dnw.sigma_to_t(sigma_in) + + eps = shared.sd_model.apply_model(x_in * c_in, t, cond=cond_in) + denoised_uncond, denoised_cond = (x_in + eps * c_out).chunk(2) + + denoised = denoised_uncond + (denoised_cond - denoised_uncond) * cfg_scale + + if i == 1: + d = (x - denoised) / (2 * sigmas[i]) + else: + d = (x - denoised) / sigmas[i - 1] + + dt = sigmas[i] - sigmas[i - 1] + x = x + d * dt + + sd_samplers_common.store_latent(x) + + # This shouldn't be necessary, but solved some VRAM issues + del x_in, sigma_in, cond_in, c_out, c_in, t, + del eps, denoised_uncond, denoised_cond, denoised, d, dt + + shared.state.nextjob() + + return x / sigmas[-1] + + +class Script(scripts.Script): + def __init__(self): + self.cache = None + + def title(self): + return "img2img alternative test" + + def show(self, is_img2img): + return is_img2img + + def ui(self, is_img2img): + info = gr.Markdown(''' + * `CFG Scale` should be 2 or lower. + ''') + + override_sampler = gr.Checkbox(label="Override `Sampling method` to Euler?(this method is built for it)", value=True, elem_id=self.elem_id("override_sampler")) + + override_prompt = gr.Checkbox(label="Override `prompt` to the same value as `original prompt`?(and `negative prompt`)", value=True, elem_id=self.elem_id("override_prompt")) + original_prompt = gr.Textbox(label="Original prompt", lines=1, elem_id=self.elem_id("original_prompt")) + original_negative_prompt = gr.Textbox(label="Original negative prompt", lines=1, elem_id=self.elem_id("original_negative_prompt")) + + override_steps = gr.Checkbox(label="Override `Sampling Steps` to the same value as `Decode steps`?", value=True, elem_id=self.elem_id("override_steps")) + st = gr.Slider(label="Decode steps", minimum=1, maximum=150, step=1, value=50, elem_id=self.elem_id("st")) + + override_strength = gr.Checkbox(label="Override `Denoising strength` to 1?", value=True, elem_id=self.elem_id("override_strength")) + + cfg = gr.Slider(label="Decode CFG scale", minimum=0.0, maximum=15.0, step=0.1, value=1.0, elem_id=self.elem_id("cfg")) + randomness = gr.Slider(label="Randomness", minimum=0.0, maximum=1.0, step=0.01, value=0.0, elem_id=self.elem_id("randomness")) + sigma_adjustment = gr.Checkbox(label="Sigma adjustment for finding noise for image", value=False, elem_id=self.elem_id("sigma_adjustment")) + + return [ + info, + override_sampler, + override_prompt, original_prompt, original_negative_prompt, + override_steps, st, + override_strength, + cfg, randomness, sigma_adjustment, + ] + + def run(self, p, _, override_sampler, override_prompt, original_prompt, original_negative_prompt, override_steps, st, override_strength, cfg, randomness, sigma_adjustment): + # Override + if override_sampler: + p.sampler_name = "Euler" + if override_prompt: + p.prompt = original_prompt + p.negative_prompt = original_negative_prompt + if override_steps: + p.steps = st + if override_strength: + p.denoising_strength = 1.0 + + def sample_extra(conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts): + lat = (p.init_latent.cpu().numpy() * 10).astype(int) + + same_params = self.cache is not None and self.cache.cfg_scale == cfg and self.cache.steps == st \ + and self.cache.original_prompt == original_prompt \ + and self.cache.original_negative_prompt == original_negative_prompt \ + and self.cache.sigma_adjustment == sigma_adjustment + same_everything = same_params and self.cache.latent.shape == lat.shape and np.abs(self.cache.latent-lat).sum() < 100 + + if same_everything: + rec_noise = self.cache.noise + else: + shared.state.job_count += 1 + cond = p.sd_model.get_learned_conditioning(p.batch_size * [original_prompt]) + uncond = p.sd_model.get_learned_conditioning(p.batch_size * [original_negative_prompt]) + if sigma_adjustment: + rec_noise = find_noise_for_image_sigma_adjustment(p, cond, uncond, cfg, st) + else: + rec_noise = find_noise_for_image(p, cond, uncond, cfg, st) + self.cache = Cached(rec_noise, cfg, st, lat, original_prompt, original_negative_prompt, sigma_adjustment) + + rand_noise = processing.create_random_tensors(p.init_latent.shape[1:], seeds=seeds, subseeds=subseeds, subseed_strength=p.subseed_strength, seed_resize_from_h=p.seed_resize_from_h, seed_resize_from_w=p.seed_resize_from_w, p=p) + + combined_noise = ((1 - randomness) * rec_noise + randomness * rand_noise) / ((randomness**2 + (1-randomness)**2) ** 0.5) + + sampler = sd_samplers.create_sampler(p.sampler_name, p.sd_model) + + sigmas = sampler.model_wrap.get_sigmas(p.steps) + + noise_dt = combined_noise - (p.init_latent / sigmas[0]) + + p.seed = p.seed + 1 + + return sampler.sample_img2img(p, p.init_latent, noise_dt, conditioning, unconditional_conditioning, image_conditioning=p.image_conditioning) + + p.sample = sample_extra + + p.extra_generation_params["Decode prompt"] = original_prompt + p.extra_generation_params["Decode negative prompt"] = original_negative_prompt + p.extra_generation_params["Decode CFG scale"] = cfg + p.extra_generation_params["Decode steps"] = st + p.extra_generation_params["Randomness"] = randomness + p.extra_generation_params["Sigma Adjustment"] = sigma_adjustment + + processed = processing.process_images(p) + + return processed + diff --git a/sd/stable-diffusion-webui/scripts/loopback.py b/sd/stable-diffusion-webui/scripts/loopback.py new file mode 100644 index 0000000000000000000000000000000000000000..e3ac98240ea022b028e056c21626b453ae19543d --- /dev/null +++ b/sd/stable-diffusion-webui/scripts/loopback.py @@ -0,0 +1,98 @@ +import numpy as np +from tqdm import trange + +import modules.scripts as scripts +import gradio as gr + +from modules import processing, shared, sd_samplers, images +from modules.processing import Processed +from modules.sd_samplers import samplers +from modules.shared import opts, cmd_opts, state +from modules import deepbooru + + +class Script(scripts.Script): + def title(self): + return "Loopback" + + def show(self, is_img2img): + return is_img2img + + def ui(self, is_img2img): + loops = gr.Slider(minimum=1, maximum=32, step=1, label='Loops', value=4, elem_id=self.elem_id("loops")) + denoising_strength_change_factor = gr.Slider(minimum=0.9, maximum=1.1, step=0.01, label='Denoising strength change factor', value=1, elem_id=self.elem_id("denoising_strength_change_factor")) + append_interrogation = gr.Dropdown(label="Append interrogated prompt at each iteration", choices=["None", "CLIP", "DeepBooru"], value="None") + + return [loops, denoising_strength_change_factor, append_interrogation] + + def run(self, p, loops, denoising_strength_change_factor, append_interrogation): + processing.fix_seed(p) + batch_count = p.n_iter + p.extra_generation_params = { + "Denoising strength change factor": denoising_strength_change_factor, + } + + p.batch_size = 1 + p.n_iter = 1 + + output_images, info = None, None + initial_seed = None + initial_info = None + + grids = [] + all_images = [] + original_init_image = p.init_images + original_prompt = p.prompt + state.job_count = loops * batch_count + + initial_color_corrections = [processing.setup_color_correction(p.init_images[0])] + + for n in range(batch_count): + history = [] + + # Reset to original init image at the start of each batch + p.init_images = original_init_image + + for i in range(loops): + p.n_iter = 1 + p.batch_size = 1 + p.do_not_save_grid = True + + if opts.img2img_color_correction: + p.color_corrections = initial_color_corrections + + if append_interrogation != "None": + p.prompt = original_prompt + ", " if original_prompt != "" else "" + if append_interrogation == "CLIP": + p.prompt += shared.interrogator.interrogate(p.init_images[0]) + elif append_interrogation == "DeepBooru": + p.prompt += deepbooru.model.tag(p.init_images[0]) + + state.job = f"Iteration {i + 1}/{loops}, batch {n + 1}/{batch_count}" + + processed = processing.process_images(p) + + if initial_seed is None: + initial_seed = processed.seed + initial_info = processed.info + + init_img = processed.images[0] + + p.init_images = [init_img] + p.seed = processed.seed + 1 + p.denoising_strength = min(max(p.denoising_strength * denoising_strength_change_factor, 0.1), 1) + history.append(processed.images[0]) + + grid = images.image_grid(history, rows=1) + if opts.grid_save: + images.save_image(grid, p.outpath_grids, "grid", initial_seed, p.prompt, opts.grid_format, info=info, short_filename=not opts.grid_extended_filename, grid=True, p=p) + + grids.append(grid) + all_images += history + + if opts.return_grid: + all_images = grids + all_images + + processed = Processed(p, all_images, initial_seed, initial_info) + + return processed diff --git a/sd/stable-diffusion-webui/scripts/outpainting_mk_2.py b/sd/stable-diffusion-webui/scripts/outpainting_mk_2.py new file mode 100644 index 0000000000000000000000000000000000000000..5d80b46cd3263ef0905514a761bb473441d8a1e7 --- /dev/null +++ b/sd/stable-diffusion-webui/scripts/outpainting_mk_2.py @@ -0,0 +1,283 @@ +import math + +import numpy as np +import skimage + +import modules.scripts as scripts +import gradio as gr +from PIL import Image, ImageDraw + +from modules import images, processing, devices +from modules.processing import Processed, process_images +from modules.shared import opts, cmd_opts, state + + +# this function is taken from https://github.com/parlance-zz/g-diffuser-bot +def get_matched_noise(_np_src_image, np_mask_rgb, noise_q=1, color_variation=0.05): + # helper fft routines that keep ortho normalization and auto-shift before and after fft + def _fft2(data): + if data.ndim > 2: # has channels + out_fft = np.zeros((data.shape[0], data.shape[1], data.shape[2]), dtype=np.complex128) + for c in range(data.shape[2]): + c_data = data[:, :, c] + out_fft[:, :, c] = np.fft.fft2(np.fft.fftshift(c_data), norm="ortho") + out_fft[:, :, c] = np.fft.ifftshift(out_fft[:, :, c]) + else: # one channel + out_fft = np.zeros((data.shape[0], data.shape[1]), dtype=np.complex128) + out_fft[:, :] = np.fft.fft2(np.fft.fftshift(data), norm="ortho") + out_fft[:, :] = np.fft.ifftshift(out_fft[:, :]) + + return out_fft + + def _ifft2(data): + if data.ndim > 2: # has channels + out_ifft = np.zeros((data.shape[0], data.shape[1], data.shape[2]), dtype=np.complex128) + for c in range(data.shape[2]): + c_data = data[:, :, c] + out_ifft[:, :, c] = np.fft.ifft2(np.fft.fftshift(c_data), norm="ortho") + out_ifft[:, :, c] = np.fft.ifftshift(out_ifft[:, :, c]) + else: # one channel + out_ifft = np.zeros((data.shape[0], data.shape[1]), dtype=np.complex128) + out_ifft[:, :] = np.fft.ifft2(np.fft.fftshift(data), norm="ortho") + out_ifft[:, :] = np.fft.ifftshift(out_ifft[:, :]) + + return out_ifft + + def _get_gaussian_window(width, height, std=3.14, mode=0): + window_scale_x = float(width / min(width, height)) + window_scale_y = float(height / min(width, height)) + + window = np.zeros((width, height)) + x = (np.arange(width) / width * 2. - 1.) * window_scale_x + for y in range(height): + fy = (y / height * 2. - 1.) * window_scale_y + if mode == 0: + window[:, y] = np.exp(-(x ** 2 + fy ** 2) * std) + else: + window[:, y] = (1 / ((x ** 2 + 1.) * (fy ** 2 + 1.))) ** (std / 3.14) # hey wait a minute that's not gaussian + + return window + + def _get_masked_window_rgb(np_mask_grey, hardness=1.): + np_mask_rgb = np.zeros((np_mask_grey.shape[0], np_mask_grey.shape[1], 3)) + if hardness != 1.: + hardened = np_mask_grey[:] ** hardness + else: + hardened = np_mask_grey[:] + for c in range(3): + np_mask_rgb[:, :, c] = hardened[:] + return np_mask_rgb + + width = _np_src_image.shape[0] + height = _np_src_image.shape[1] + num_channels = _np_src_image.shape[2] + + np_src_image = _np_src_image[:] * (1. - np_mask_rgb) + np_mask_grey = (np.sum(np_mask_rgb, axis=2) / 3.) + img_mask = np_mask_grey > 1e-6 + ref_mask = np_mask_grey < 1e-3 + + windowed_image = _np_src_image * (1. - _get_masked_window_rgb(np_mask_grey)) + windowed_image /= np.max(windowed_image) + windowed_image += np.average(_np_src_image) * np_mask_rgb # / (1.-np.average(np_mask_rgb)) # rather than leave the masked area black, we get better results from fft by filling the average unmasked color + + src_fft = _fft2(windowed_image) # get feature statistics from masked src img + src_dist = np.absolute(src_fft) + src_phase = src_fft / src_dist + + # create a generator with a static seed to make outpainting deterministic / only follow global seed + rng = np.random.default_rng(0) + + noise_window = _get_gaussian_window(width, height, mode=1) # start with simple gaussian noise + noise_rgb = rng.random((width, height, num_channels)) + noise_grey = (np.sum(noise_rgb, axis=2) / 3.) + noise_rgb *= color_variation # the colorfulness of the starting noise is blended to greyscale with a parameter + for c in range(num_channels): + noise_rgb[:, :, c] += (1. - color_variation) * noise_grey + + noise_fft = _fft2(noise_rgb) + for c in range(num_channels): + noise_fft[:, :, c] *= noise_window + noise_rgb = np.real(_ifft2(noise_fft)) + shaped_noise_fft = _fft2(noise_rgb) + shaped_noise_fft[:, :, :] = np.absolute(shaped_noise_fft[:, :, :]) ** 2 * (src_dist ** noise_q) * src_phase # perform the actual shaping + + brightness_variation = 0. # color_variation # todo: temporarily tieing brightness variation to color variation for now + contrast_adjusted_np_src = _np_src_image[:] * (brightness_variation + 1.) - brightness_variation * 2. + + # scikit-image is used for histogram matching, very convenient! + shaped_noise = np.real(_ifft2(shaped_noise_fft)) + shaped_noise -= np.min(shaped_noise) + shaped_noise /= np.max(shaped_noise) + shaped_noise[img_mask, :] = skimage.exposure.match_histograms(shaped_noise[img_mask, :] ** 1., contrast_adjusted_np_src[ref_mask, :], channel_axis=1) + shaped_noise = _np_src_image[:] * (1. - np_mask_rgb) + shaped_noise * np_mask_rgb + + matched_noise = shaped_noise[:] + + return np.clip(matched_noise, 0., 1.) + + + +class Script(scripts.Script): + def title(self): + return "Outpainting mk2" + + def show(self, is_img2img): + return is_img2img + + def ui(self, is_img2img): + if not is_img2img: + return None + + info = gr.HTML("

    Recommended settings: Sampling Steps: 80-100, Sampler: Euler a, Denoising strength: 0.8

    ") + + pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128, elem_id=self.elem_id("pixels")) + mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=8, elem_id=self.elem_id("mask_blur")) + direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down'], elem_id=self.elem_id("direction")) + noise_q = gr.Slider(label="Fall-off exponent (lower=higher detail)", minimum=0.0, maximum=4.0, step=0.01, value=1.0, elem_id=self.elem_id("noise_q")) + color_variation = gr.Slider(label="Color variation", minimum=0.0, maximum=1.0, step=0.01, value=0.05, elem_id=self.elem_id("color_variation")) + + return [info, pixels, mask_blur, direction, noise_q, color_variation] + + def run(self, p, _, pixels, mask_blur, direction, noise_q, color_variation): + initial_seed_and_info = [None, None] + + process_width = p.width + process_height = p.height + + p.mask_blur = mask_blur*4 + p.inpaint_full_res = False + p.inpainting_fill = 1 + p.do_not_save_samples = True + p.do_not_save_grid = True + + left = pixels if "left" in direction else 0 + right = pixels if "right" in direction else 0 + up = pixels if "up" in direction else 0 + down = pixels if "down" in direction else 0 + + init_img = p.init_images[0] + target_w = math.ceil((init_img.width + left + right) / 64) * 64 + target_h = math.ceil((init_img.height + up + down) / 64) * 64 + + if left > 0: + left = left * (target_w - init_img.width) // (left + right) + + if right > 0: + right = target_w - init_img.width - left + + if up > 0: + up = up * (target_h - init_img.height) // (up + down) + + if down > 0: + down = target_h - init_img.height - up + + def expand(init, count, expand_pixels, is_left=False, is_right=False, is_top=False, is_bottom=False): + is_horiz = is_left or is_right + is_vert = is_top or is_bottom + pixels_horiz = expand_pixels if is_horiz else 0 + pixels_vert = expand_pixels if is_vert else 0 + + images_to_process = [] + output_images = [] + for n in range(count): + res_w = init[n].width + pixels_horiz + res_h = init[n].height + pixels_vert + process_res_w = math.ceil(res_w / 64) * 64 + process_res_h = math.ceil(res_h / 64) * 64 + + img = Image.new("RGB", (process_res_w, process_res_h)) + img.paste(init[n], (pixels_horiz if is_left else 0, pixels_vert if is_top else 0)) + mask = Image.new("RGB", (process_res_w, process_res_h), "white") + draw = ImageDraw.Draw(mask) + draw.rectangle(( + expand_pixels + mask_blur if is_left else 0, + expand_pixels + mask_blur if is_top else 0, + mask.width - expand_pixels - mask_blur if is_right else res_w, + mask.height - expand_pixels - mask_blur if is_bottom else res_h, + ), fill="black") + + np_image = (np.asarray(img) / 255.0).astype(np.float64) + np_mask = (np.asarray(mask) / 255.0).astype(np.float64) + noised = get_matched_noise(np_image, np_mask, noise_q, color_variation) + output_images.append(Image.fromarray(np.clip(noised * 255., 0., 255.).astype(np.uint8), mode="RGB")) + + target_width = min(process_width, init[n].width + pixels_horiz) if is_horiz else img.width + target_height = min(process_height, init[n].height + pixels_vert) if is_vert else img.height + p.width = target_width if is_horiz else img.width + p.height = target_height if is_vert else img.height + + crop_region = ( + 0 if is_left else output_images[n].width - target_width, + 0 if is_top else output_images[n].height - target_height, + target_width if is_left else output_images[n].width, + target_height if is_top else output_images[n].height, + ) + mask = mask.crop(crop_region) + p.image_mask = mask + + image_to_process = output_images[n].crop(crop_region) + images_to_process.append(image_to_process) + + p.init_images = images_to_process + + latent_mask = Image.new("RGB", (p.width, p.height), "white") + draw = ImageDraw.Draw(latent_mask) + draw.rectangle(( + expand_pixels + mask_blur * 2 if is_left else 0, + expand_pixels + mask_blur * 2 if is_top else 0, + mask.width - expand_pixels - mask_blur * 2 if is_right else res_w, + mask.height - expand_pixels - mask_blur * 2 if is_bottom else res_h, + ), fill="black") + p.latent_mask = latent_mask + + proc = process_images(p) + + if initial_seed_and_info[0] is None: + initial_seed_and_info[0] = proc.seed + initial_seed_and_info[1] = proc.info + + for n in range(count): + output_images[n].paste(proc.images[n], (0 if is_left else output_images[n].width - proc.images[n].width, 0 if is_top else output_images[n].height - proc.images[n].height)) + output_images[n] = output_images[n].crop((0, 0, res_w, res_h)) + + return output_images + + batch_count = p.n_iter + batch_size = p.batch_size + p.n_iter = 1 + state.job_count = batch_count * ((1 if left > 0 else 0) + (1 if right > 0 else 0) + (1 if up > 0 else 0) + (1 if down > 0 else 0)) + all_processed_images = [] + + for i in range(batch_count): + imgs = [init_img] * batch_size + state.job = f"Batch {i + 1} out of {batch_count}" + + if left > 0: + imgs = expand(imgs, batch_size, left, is_left=True) + if right > 0: + imgs = expand(imgs, batch_size, right, is_right=True) + if up > 0: + imgs = expand(imgs, batch_size, up, is_top=True) + if down > 0: + imgs = expand(imgs, batch_size, down, is_bottom=True) + + all_processed_images += imgs + + all_images = all_processed_images + + combined_grid_image = images.image_grid(all_processed_images) + unwanted_grid_because_of_img_count = len(all_processed_images) < 2 and opts.grid_only_if_multiple + if opts.return_grid and not unwanted_grid_because_of_img_count: + all_images = [combined_grid_image] + all_processed_images + + res = Processed(p, all_images, initial_seed_and_info[0], initial_seed_and_info[1]) + + if opts.samples_save: + for img in all_processed_images: + images.save_image(img, p.outpath_samples, "", res.seed, p.prompt, opts.grid_format, info=res.info, p=p) + + if opts.grid_save and not unwanted_grid_because_of_img_count: + images.save_image(combined_grid_image, p.outpath_grids, "grid", res.seed, p.prompt, opts.grid_format, info=res.info, short_filename=not opts.grid_extended_filename, grid=True, p=p) + + return res diff --git a/sd/stable-diffusion-webui/scripts/poor_mans_outpainting.py b/sd/stable-diffusion-webui/scripts/poor_mans_outpainting.py new file mode 100644 index 0000000000000000000000000000000000000000..d39f61c1073376eae210d955ac1e9eba836402da --- /dev/null +++ b/sd/stable-diffusion-webui/scripts/poor_mans_outpainting.py @@ -0,0 +1,146 @@ +import math + +import modules.scripts as scripts +import gradio as gr +from PIL import Image, ImageDraw + +from modules import images, processing, devices +from modules.processing import Processed, process_images +from modules.shared import opts, cmd_opts, state + + +class Script(scripts.Script): + def title(self): + return "Poor man's outpainting" + + def show(self, is_img2img): + return is_img2img + + def ui(self, is_img2img): + if not is_img2img: + return None + + pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128, elem_id=self.elem_id("pixels")) + mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4, elem_id=self.elem_id("mask_blur")) + inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index", elem_id=self.elem_id("inpainting_fill")) + direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down'], elem_id=self.elem_id("direction")) + + return [pixels, mask_blur, inpainting_fill, direction] + + def run(self, p, pixels, mask_blur, inpainting_fill, direction): + initial_seed = None + initial_info = None + + p.mask_blur = mask_blur * 2 + p.inpainting_fill = inpainting_fill + p.inpaint_full_res = False + + left = pixels if "left" in direction else 0 + right = pixels if "right" in direction else 0 + up = pixels if "up" in direction else 0 + down = pixels if "down" in direction else 0 + + init_img = p.init_images[0] + target_w = math.ceil((init_img.width + left + right) / 64) * 64 + target_h = math.ceil((init_img.height + up + down) / 64) * 64 + + if left > 0: + left = left * (target_w - init_img.width) // (left + right) + if right > 0: + right = target_w - init_img.width - left + + if up > 0: + up = up * (target_h - init_img.height) // (up + down) + + if down > 0: + down = target_h - init_img.height - up + + img = Image.new("RGB", (target_w, target_h)) + img.paste(init_img, (left, up)) + + mask = Image.new("L", (img.width, img.height), "white") + draw = ImageDraw.Draw(mask) + draw.rectangle(( + left + (mask_blur * 2 if left > 0 else 0), + up + (mask_blur * 2 if up > 0 else 0), + mask.width - right - (mask_blur * 2 if right > 0 else 0), + mask.height - down - (mask_blur * 2 if down > 0 else 0) + ), fill="black") + + latent_mask = Image.new("L", (img.width, img.height), "white") + latent_draw = ImageDraw.Draw(latent_mask) + latent_draw.rectangle(( + left + (mask_blur//2 if left > 0 else 0), + up + (mask_blur//2 if up > 0 else 0), + mask.width - right - (mask_blur//2 if right > 0 else 0), + mask.height - down - (mask_blur//2 if down > 0 else 0) + ), fill="black") + + devices.torch_gc() + + grid = images.split_grid(img, tile_w=p.width, tile_h=p.height, overlap=pixels) + grid_mask = images.split_grid(mask, tile_w=p.width, tile_h=p.height, overlap=pixels) + grid_latent_mask = images.split_grid(latent_mask, tile_w=p.width, tile_h=p.height, overlap=pixels) + + p.n_iter = 1 + p.batch_size = 1 + p.do_not_save_grid = True + p.do_not_save_samples = True + + work = [] + work_mask = [] + work_latent_mask = [] + work_results = [] + + for (y, h, row), (_, _, row_mask), (_, _, row_latent_mask) in zip(grid.tiles, grid_mask.tiles, grid_latent_mask.tiles): + for tiledata, tiledata_mask, tiledata_latent_mask in zip(row, row_mask, row_latent_mask): + x, w = tiledata[0:2] + + if x >= left and x+w <= img.width - right and y >= up and y+h <= img.height - down: + continue + + work.append(tiledata[2]) + work_mask.append(tiledata_mask[2]) + work_latent_mask.append(tiledata_latent_mask[2]) + + batch_count = len(work) + print(f"Poor man's outpainting will process a total of {len(work)} images tiled as {len(grid.tiles[0][2])}x{len(grid.tiles)}.") + + state.job_count = batch_count + + for i in range(batch_count): + p.init_images = [work[i]] + p.image_mask = work_mask[i] + p.latent_mask = work_latent_mask[i] + + state.job = f"Batch {i + 1} out of {batch_count}" + processed = process_images(p) + + if initial_seed is None: + initial_seed = processed.seed + initial_info = processed.info + + p.seed = processed.seed + 1 + work_results += processed.images + + + image_index = 0 + for y, h, row in grid.tiles: + for tiledata in row: + x, w = tiledata[0:2] + + if x >= left and x+w <= img.width - right and y >= up and y+h <= img.height - down: + continue + + tiledata[2] = work_results[image_index] if image_index < len(work_results) else Image.new("RGB", (p.width, p.height)) + image_index += 1 + + combined_image = images.combine_grid(grid) + + if opts.samples_save: + images.save_image(combined_image, p.outpath_samples, "", initial_seed, p.prompt, opts.grid_format, info=initial_info, p=p) + + processed = Processed(p, [combined_image], initial_seed, initial_info) + + return processed + diff --git a/sd/stable-diffusion-webui/scripts/postprocessing_codeformer.py b/sd/stable-diffusion-webui/scripts/postprocessing_codeformer.py new file mode 100644 index 0000000000000000000000000000000000000000..7e337ec41ffffe11fd88fced0ff4f6338d959571 --- /dev/null +++ b/sd/stable-diffusion-webui/scripts/postprocessing_codeformer.py @@ -0,0 +1,36 @@ +from PIL import Image +import numpy as np + +from modules import scripts_postprocessing, codeformer_model +import gradio as gr + +from modules.ui_components import FormRow + + +class ScriptPostprocessingCodeFormer(scripts_postprocessing.ScriptPostprocessing): + name = "CodeFormer" + order = 3000 + + def ui(self): + with FormRow(): + codeformer_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="CodeFormer visibility", value=0, elem_id="extras_codeformer_visibility") + codeformer_weight = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="CodeFormer weight (0 = maximum effect, 1 = minimum effect)", value=0, elem_id="extras_codeformer_weight") + + return { + "codeformer_visibility": codeformer_visibility, + "codeformer_weight": codeformer_weight, + } + + def process(self, pp: scripts_postprocessing.PostprocessedImage, codeformer_visibility, codeformer_weight): + if codeformer_visibility == 0: + return + + restored_img = codeformer_model.codeformer.restore(np.array(pp.image, dtype=np.uint8), w=codeformer_weight) + res = Image.fromarray(restored_img) + + if codeformer_visibility < 1.0: + res = Image.blend(pp.image, res, codeformer_visibility) + + pp.image = res + pp.info["CodeFormer visibility"] = round(codeformer_visibility, 3) + pp.info["CodeFormer weight"] = round(codeformer_weight, 3) diff --git a/sd/stable-diffusion-webui/scripts/postprocessing_gfpgan.py b/sd/stable-diffusion-webui/scripts/postprocessing_gfpgan.py new file mode 100644 index 0000000000000000000000000000000000000000..9f7c2baaa28333958818d332324b34bcb8bce3ca --- /dev/null +++ b/sd/stable-diffusion-webui/scripts/postprocessing_gfpgan.py @@ -0,0 +1,33 @@ +from PIL import Image +import numpy as np + +from modules import scripts_postprocessing, gfpgan_model +import gradio as gr + +from modules.ui_components import FormRow + + +class ScriptPostprocessingGfpGan(scripts_postprocessing.ScriptPostprocessing): + name = "GFPGAN" + order = 2000 + + def ui(self): + with FormRow(): + gfpgan_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="GFPGAN visibility", value=0, elem_id="extras_gfpgan_visibility") + + return { + "gfpgan_visibility": gfpgan_visibility, + } + + def process(self, pp: scripts_postprocessing.PostprocessedImage, gfpgan_visibility): + if gfpgan_visibility == 0: + return + + restored_img = gfpgan_model.gfpgan_fix_faces(np.array(pp.image, dtype=np.uint8)) + res = Image.fromarray(restored_img) + + if gfpgan_visibility < 1.0: + res = Image.blend(pp.image, res, gfpgan_visibility) + + pp.image = res + pp.info["GFPGAN visibility"] = round(gfpgan_visibility, 3) diff --git a/sd/stable-diffusion-webui/scripts/postprocessing_upscale.py b/sd/stable-diffusion-webui/scripts/postprocessing_upscale.py new file mode 100644 index 0000000000000000000000000000000000000000..ccec72fcbc72eeffbe24a659bf53ecba71162391 --- /dev/null +++ b/sd/stable-diffusion-webui/scripts/postprocessing_upscale.py @@ -0,0 +1,131 @@ +from PIL import Image +import numpy as np + +from modules import scripts_postprocessing, shared +import gradio as gr + +from modules.ui_components import FormRow + + +upscale_cache = {} + + +class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): + name = "Upscale" + order = 1000 + + def ui(self): + selected_tab = gr.State(value=0) + + with gr.Tabs(elem_id="extras_resize_mode"): + with gr.TabItem('Scale by', elem_id="extras_scale_by_tab") as tab_scale_by: + upscaling_resize = gr.Slider(minimum=1.0, maximum=8.0, step=0.05, label="Resize", value=4, elem_id="extras_upscaling_resize") + + with gr.TabItem('Scale to', elem_id="extras_scale_to_tab") as tab_scale_to: + with FormRow(): + upscaling_resize_w = gr.Number(label="Width", value=512, precision=0, elem_id="extras_upscaling_resize_w") + upscaling_resize_h = gr.Number(label="Height", value=512, precision=0, elem_id="extras_upscaling_resize_h") + upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") + + with FormRow(): + extras_upscaler_1 = gr.Dropdown(label='Upscaler 1', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) + + with FormRow(): + extras_upscaler_2 = gr.Dropdown(label='Upscaler 2', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) + extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility") + + tab_scale_by.select(fn=lambda: 0, inputs=[], outputs=[selected_tab]) + tab_scale_to.select(fn=lambda: 1, inputs=[], outputs=[selected_tab]) + + return { + "upscale_mode": selected_tab, + "upscale_by": upscaling_resize, + "upscale_to_width": upscaling_resize_w, + "upscale_to_height": upscaling_resize_h, + "upscale_crop": upscaling_crop, + "upscaler_1_name": extras_upscaler_1, + "upscaler_2_name": extras_upscaler_2, + "upscaler_2_visibility": extras_upscaler_2_visibility, + } + + def upscale(self, image, info, upscaler, upscale_mode, upscale_by, upscale_to_width, upscale_to_height, upscale_crop): + if upscale_mode == 1: + upscale_by = max(upscale_to_width/image.width, upscale_to_height/image.height) + info["Postprocess upscale to"] = f"{upscale_to_width}x{upscale_to_height}" + else: + info["Postprocess upscale by"] = upscale_by + + cache_key = (hash(np.array(image.getdata()).tobytes()), upscaler.name, upscale_mode, upscale_by, upscale_to_width, upscale_to_height, upscale_crop) + cached_image = upscale_cache.pop(cache_key, None) + + if cached_image is not None: + image = cached_image + else: + image = upscaler.scaler.upscale(image, upscale_by, upscaler.data_path) + + upscale_cache[cache_key] = image + if len(upscale_cache) > shared.opts.upscaling_max_images_in_cache: + upscale_cache.pop(next(iter(upscale_cache), None), None) + + if upscale_mode == 1 and upscale_crop: + cropped = Image.new("RGB", (upscale_to_width, upscale_to_height)) + cropped.paste(image, box=(upscale_to_width // 2 - image.width // 2, upscale_to_height // 2 - image.height // 2)) + image = cropped + info["Postprocess crop to"] = f"{image.width}x{image.height}" + + return image + + def process(self, pp: scripts_postprocessing.PostprocessedImage, upscale_mode=1, upscale_by=2.0, upscale_to_width=None, upscale_to_height=None, upscale_crop=False, upscaler_1_name=None, upscaler_2_name=None, upscaler_2_visibility=0.0): + if upscaler_1_name == "None": + upscaler_1_name = None + + upscaler1 = next(iter([x for x in shared.sd_upscalers if x.name == upscaler_1_name]), None) + assert upscaler1 or (upscaler_1_name is None), f'could not find upscaler named {upscaler_1_name}' + + if not upscaler1: + return + + if upscaler_2_name == "None": + upscaler_2_name = None + + upscaler2 = next(iter([x for x in shared.sd_upscalers if x.name == upscaler_2_name and x.name != "None"]), None) + assert upscaler2 or (upscaler_2_name is None), f'could not find upscaler named {upscaler_2_name}' + + upscaled_image = self.upscale(pp.image, pp.info, upscaler1, upscale_mode, upscale_by, upscale_to_width, upscale_to_height, upscale_crop) + pp.info[f"Postprocess upscaler"] = upscaler1.name + + if upscaler2 and upscaler_2_visibility > 0: + second_upscale = self.upscale(pp.image, pp.info, upscaler2, upscale_mode, upscale_by, upscale_to_width, upscale_to_height, upscale_crop) + upscaled_image = Image.blend(upscaled_image, second_upscale, upscaler_2_visibility) + + pp.info[f"Postprocess upscaler 2"] = upscaler2.name + + pp.image = upscaled_image + + def image_changed(self): + upscale_cache.clear() + + +class ScriptPostprocessingUpscaleSimple(ScriptPostprocessingUpscale): + name = "Simple Upscale" + order = 900 + + def ui(self): + with FormRow(): + upscaler_name = gr.Dropdown(label='Upscaler', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) + upscale_by = gr.Slider(minimum=0.05, maximum=8.0, step=0.05, label="Upscale by", value=2) + + return { + "upscale_by": upscale_by, + "upscaler_name": upscaler_name, + } + + def process(self, pp: scripts_postprocessing.PostprocessedImage, upscale_by=2.0, upscaler_name=None): + if upscaler_name is None or upscaler_name == "None": + return + + upscaler1 = next(iter([x for x in shared.sd_upscalers if x.name == upscaler_name]), None) + assert upscaler1, f'could not find upscaler named {upscaler_name}' + + pp.image = self.upscale(pp.image, pp.info, upscaler1, 0, upscale_by, 0, 0, False) + pp.info[f"Postprocess upscaler"] = upscaler1.name diff --git a/sd/stable-diffusion-webui/scripts/prompt_matrix.py b/sd/stable-diffusion-webui/scripts/prompt_matrix.py new file mode 100644 index 0000000000000000000000000000000000000000..51c70998866d4b0853a46e4de73d86c3d9ec9b93 --- /dev/null +++ b/sd/stable-diffusion-webui/scripts/prompt_matrix.py @@ -0,0 +1,111 @@ +import math +from collections import namedtuple +from copy import copy +import random + +import modules.scripts as scripts +import gradio as gr + +from modules import images +from modules.processing import process_images, Processed +from modules.shared import opts, cmd_opts, state +import modules.sd_samplers + + +def draw_xy_grid(xs, ys, x_label, y_label, cell): + res = [] + + ver_texts = [[images.GridAnnotation(y_label(y))] for y in ys] + hor_texts = [[images.GridAnnotation(x_label(x))] for x in xs] + + first_processed = None + + state.job_count = len(xs) * len(ys) + + for iy, y in enumerate(ys): + for ix, x in enumerate(xs): + state.job = f"{ix + iy * len(xs) + 1} out of {len(xs) * len(ys)}" + + processed = cell(x, y) + if first_processed is None: + first_processed = processed + + res.append(processed.images[0]) + + grid = images.image_grid(res, rows=len(ys)) + grid = images.draw_grid_annotations(grid, res[0].width, res[0].height, hor_texts, ver_texts) + + first_processed.images = [grid] + + return first_processed + + +class Script(scripts.Script): + def title(self): + return "Prompt matrix" + + def ui(self, is_img2img): + gr.HTML('
    ') + with gr.Row(): + with gr.Column(): + put_at_start = gr.Checkbox(label='Put variable parts at start of prompt', value=False, elem_id=self.elem_id("put_at_start")) + different_seeds = gr.Checkbox(label='Use different seed for each picture', value=False, elem_id=self.elem_id("different_seeds")) + with gr.Column(): + prompt_type = gr.Radio(["positive", "negative"], label="Select prompt", elem_id=self.elem_id("prompt_type"), value="positive") + variations_delimiter = gr.Radio(["comma", "space"], label="Select joining char", elem_id=self.elem_id("variations_delimiter"), value="comma") + with gr.Column(): + margin_size = gr.Slider(label="Grid margins (px)", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size")) + + return [put_at_start, different_seeds, prompt_type, variations_delimiter, margin_size] + + def run(self, p, put_at_start, different_seeds, prompt_type, variations_delimiter, margin_size): + modules.processing.fix_seed(p) + # Raise error if promp type is not positive or negative + if prompt_type not in ["positive", "negative"]: + raise ValueError(f"Unknown prompt type {prompt_type}") + # Raise error if variations delimiter is not comma or space + if variations_delimiter not in ["comma", "space"]: + raise ValueError(f"Unknown variations delimiter {variations_delimiter}") + + prompt = p.prompt if prompt_type == "positive" else p.negative_prompt + original_prompt = prompt[0] if type(prompt) == list else prompt + positive_prompt = p.prompt[0] if type(p.prompt) == list else p.prompt + + delimiter = ", " if variations_delimiter == "comma" else " " + + all_prompts = [] + prompt_matrix_parts = original_prompt.split("|") + combination_count = 2 ** (len(prompt_matrix_parts) - 1) + for combination_num in range(combination_count): + selected_prompts = [text.strip().strip(',') for n, text in enumerate(prompt_matrix_parts[1:]) if combination_num & (1 << n)] + + if put_at_start: + selected_prompts = selected_prompts + [prompt_matrix_parts[0]] + else: + selected_prompts = [prompt_matrix_parts[0]] + selected_prompts + + all_prompts.append(delimiter.join(selected_prompts)) + + p.n_iter = math.ceil(len(all_prompts) / p.batch_size) + p.do_not_save_grid = True + + print(f"Prompt matrix will create {len(all_prompts)} images using a total of {p.n_iter} batches.") + + if prompt_type == "positive": + p.prompt = all_prompts + else: + p.negative_prompt = all_prompts + p.seed = [p.seed + (i if different_seeds else 0) for i in range(len(all_prompts))] + p.prompt_for_display = positive_prompt + processed = process_images(p) + + grid = images.image_grid(processed.images, p.batch_size, rows=1 << ((len(prompt_matrix_parts) - 1) // 2)) + grid = images.draw_prompt_matrix(grid, processed.images[0].width, processed.images[1].height, prompt_matrix_parts, margin_size) + processed.images.insert(0, grid) + processed.index_of_first_image = 1 + processed.infotexts.insert(0, processed.infotexts[0]) + + if opts.grid_save: + images.save_image(processed.images[0], p.outpath_grids, "prompt_matrix", extension=opts.grid_format, prompt=original_prompt, seed=processed.seed, grid=True, p=p) + + return processed diff --git a/sd/stable-diffusion-webui/scripts/prompts_from_file.py b/sd/stable-diffusion-webui/scripts/prompts_from_file.py new file mode 100644 index 0000000000000000000000000000000000000000..17c9c967ffeeb3f538c6f95d93ae79c32ca17828 --- /dev/null +++ b/sd/stable-diffusion-webui/scripts/prompts_from_file.py @@ -0,0 +1,177 @@ +import copy +import math +import os +import random +import sys +import traceback +import shlex + +import modules.scripts as scripts +import gradio as gr + +from modules import sd_samplers +from modules.processing import Processed, process_images +from PIL import Image +from modules.shared import opts, cmd_opts, state + + +def process_string_tag(tag): + return tag + + +def process_int_tag(tag): + return int(tag) + + +def process_float_tag(tag): + return float(tag) + + +def process_boolean_tag(tag): + return True if (tag == "true") else False + + +prompt_tags = { + "sd_model": None, + "outpath_samples": process_string_tag, + "outpath_grids": process_string_tag, + "prompt_for_display": process_string_tag, + "prompt": process_string_tag, + "negative_prompt": process_string_tag, + "styles": process_string_tag, + "seed": process_int_tag, + "subseed_strength": process_float_tag, + "subseed": process_int_tag, + "seed_resize_from_h": process_int_tag, + "seed_resize_from_w": process_int_tag, + "sampler_index": process_int_tag, + "sampler_name": process_string_tag, + "batch_size": process_int_tag, + "n_iter": process_int_tag, + "steps": process_int_tag, + "cfg_scale": process_float_tag, + "width": process_int_tag, + "height": process_int_tag, + "restore_faces": process_boolean_tag, + "tiling": process_boolean_tag, + "do_not_save_samples": process_boolean_tag, + "do_not_save_grid": process_boolean_tag +} + + +def cmdargs(line): + args = shlex.split(line) + pos = 0 + res = {} + + while pos < len(args): + arg = args[pos] + + assert arg.startswith("--"), f'must start with "--": {arg}' + assert pos+1 < len(args), f'missing argument for command line option {arg}' + + tag = arg[2:] + + if tag == "prompt" or tag == "negative_prompt": + pos += 1 + prompt = args[pos] + pos += 1 + while pos < len(args) and not args[pos].startswith("--"): + prompt += " " + prompt += args[pos] + pos += 1 + res[tag] = prompt + continue + + + func = prompt_tags.get(tag, None) + assert func, f'unknown commandline option: {arg}' + + val = args[pos+1] + if tag == "sampler_name": + val = sd_samplers.samplers_map.get(val.lower(), None) + + res[tag] = func(val) + + pos += 2 + + return res + + +def load_prompt_file(file): + if file is None: + lines = [] + else: + lines = [x.strip() for x in file.decode('utf8', errors='ignore').split("\n")] + + return None, "\n".join(lines), gr.update(lines=7) + + +class Script(scripts.Script): + def title(self): + return "Prompts from file or textbox" + + def ui(self, is_img2img): + checkbox_iterate = gr.Checkbox(label="Iterate seed every line", value=False, elem_id=self.elem_id("checkbox_iterate")) + checkbox_iterate_batch = gr.Checkbox(label="Use same random seed for all lines", value=False, elem_id=self.elem_id("checkbox_iterate_batch")) + + prompt_txt = gr.Textbox(label="List of prompt inputs", lines=1, elem_id=self.elem_id("prompt_txt")) + file = gr.File(label="Upload prompt inputs", type='binary', elem_id=self.elem_id("file")) + + file.change(fn=load_prompt_file, inputs=[file], outputs=[file, prompt_txt, prompt_txt]) + + # We start at one line. When the text changes, we jump to seven lines, or two lines if no \n. + # We don't shrink back to 1, because that causes the control to ignore [enter], and it may + # be unclear to the user that shift-enter is needed. + prompt_txt.change(lambda tb: gr.update(lines=7) if ("\n" in tb) else gr.update(lines=2), inputs=[prompt_txt], outputs=[prompt_txt]) + return [checkbox_iterate, checkbox_iterate_batch, prompt_txt] + + def run(self, p, checkbox_iterate, checkbox_iterate_batch, prompt_txt: str): + lines = [x.strip() for x in prompt_txt.splitlines()] + lines = [x for x in lines if len(x) > 0] + + p.do_not_save_grid = True + + job_count = 0 + jobs = [] + + for line in lines: + if "--" in line: + try: + args = cmdargs(line) + except Exception: + print(f"Error parsing line {line} as commandline:", file=sys.stderr) + print(traceback.format_exc(), file=sys.stderr) + args = {"prompt": line} + else: + args = {"prompt": line} + + job_count += args.get("n_iter", p.n_iter) + + jobs.append(args) + + print(f"Will process {len(lines)} lines in {job_count} jobs.") + if (checkbox_iterate or checkbox_iterate_batch) and p.seed == -1: + p.seed = int(random.randrange(4294967294)) + + state.job_count = job_count + + images = [] + all_prompts = [] + infotexts = [] + for n, args in enumerate(jobs): + state.job = f"{state.job_no + 1} out of {state.job_count}" + + copy_p = copy.copy(p) + for k, v in args.items(): + setattr(copy_p, k, v) + + proc = process_images(copy_p) + images += proc.images + + if checkbox_iterate: + p.seed = p.seed + (p.batch_size * p.n_iter) + all_prompts += proc.all_prompts + infotexts += proc.infotexts + + return Processed(p, images, p.seed, "", all_prompts=all_prompts, infotexts=infotexts) diff --git a/sd/stable-diffusion-webui/scripts/sd_upscale.py b/sd/stable-diffusion-webui/scripts/sd_upscale.py new file mode 100644 index 0000000000000000000000000000000000000000..dd64d7d385b34f960eebd7aec3233a84ea90609a --- /dev/null +++ b/sd/stable-diffusion-webui/scripts/sd_upscale.py @@ -0,0 +1,101 @@ +import math + +import modules.scripts as scripts +import gradio as gr +from PIL import Image + +from modules import processing, shared, sd_samplers, images, devices +from modules.processing import Processed +from modules.shared import opts, cmd_opts, state + + +class Script(scripts.Script): + def title(self): + return "SD upscale" + + def show(self, is_img2img): + return is_img2img + + def ui(self, is_img2img): + info = gr.HTML("

    Will upscale the image by the selected scale factor; use width and height sliders to set tile size

    ") + overlap = gr.Slider(minimum=0, maximum=256, step=16, label='Tile overlap', value=64, elem_id=self.elem_id("overlap")) + scale_factor = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label='Scale Factor', value=2.0, elem_id=self.elem_id("scale_factor")) + upscaler_index = gr.Radio(label='Upscaler', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, type="index", elem_id=self.elem_id("upscaler_index")) + + return [info, overlap, upscaler_index, scale_factor] + + def run(self, p, _, overlap, upscaler_index, scale_factor): + if isinstance(upscaler_index, str): + upscaler_index = [x.name.lower() for x in shared.sd_upscalers].index(upscaler_index.lower()) + processing.fix_seed(p) + upscaler = shared.sd_upscalers[upscaler_index] + + p.extra_generation_params["SD upscale overlap"] = overlap + p.extra_generation_params["SD upscale upscaler"] = upscaler.name + + initial_info = None + seed = p.seed + + init_img = p.init_images[0] + init_img = images.flatten(init_img, opts.img2img_background_color) + + if upscaler.name != "None": + img = upscaler.scaler.upscale(init_img, scale_factor, upscaler.data_path) + else: + img = init_img + + devices.torch_gc() + + grid = images.split_grid(img, tile_w=p.width, tile_h=p.height, overlap=overlap) + + batch_size = p.batch_size + upscale_count = p.n_iter + p.n_iter = 1 + p.do_not_save_grid = True + p.do_not_save_samples = True + + work = [] + + for y, h, row in grid.tiles: + for tiledata in row: + work.append(tiledata[2]) + + batch_count = math.ceil(len(work) / batch_size) + state.job_count = batch_count * upscale_count + + print(f"SD upscaling will process a total of {len(work)} images tiled as {len(grid.tiles[0][2])}x{len(grid.tiles)} per upscale in a total of {state.job_count} batches.") + + result_images = [] + for n in range(upscale_count): + start_seed = seed + n + p.seed = start_seed + + work_results = [] + for i in range(batch_count): + p.batch_size = batch_size + p.init_images = work[i * batch_size:(i + 1) * batch_size] + + state.job = f"Batch {i + 1 + n * batch_count} out of {state.job_count}" + processed = processing.process_images(p) + + if initial_info is None: + initial_info = processed.info + + p.seed = processed.seed + 1 + work_results += processed.images + + image_index = 0 + for y, h, row in grid.tiles: + for tiledata in row: + tiledata[2] = work_results[image_index] if image_index < len(work_results) else Image.new("RGB", (p.width, p.height)) + image_index += 1 + + combined_image = images.combine_grid(grid) + result_images.append(combined_image) + + if opts.samples_save: + images.save_image(combined_image, p.outpath_samples, "", start_seed, p.prompt, opts.samples_format, info=initial_info, p=p) + + processed = Processed(p, result_images, seed, initial_info) + + return processed diff --git a/sd/stable-diffusion-webui/scripts/xyz_grid.py b/sd/stable-diffusion-webui/scripts/xyz_grid.py new file mode 100644 index 0000000000000000000000000000000000000000..e457d53de2ade37a53cc200d6902323d3d6f25ce --- /dev/null +++ b/sd/stable-diffusion-webui/scripts/xyz_grid.py @@ -0,0 +1,620 @@ +from collections import namedtuple +from copy import copy +from itertools import permutations, chain +import random +import csv +from io import StringIO +from PIL import Image +import numpy as np + +import modules.scripts as scripts +import gradio as gr + +from modules import images, paths, sd_samplers, processing, sd_models, sd_vae +from modules.processing import process_images, Processed, StableDiffusionProcessingTxt2Img +from modules.shared import opts, cmd_opts, state +import modules.shared as shared +import modules.sd_samplers +import modules.sd_models +import modules.sd_vae +import glob +import os +import re + +from modules.ui_components import ToolButton + +fill_values_symbol = "\U0001f4d2" # 📒 + +AxisInfo = namedtuple('AxisInfo', ['axis', 'values']) + + +def apply_field(field): + def fun(p, x, xs): + setattr(p, field, x) + + return fun + + +def apply_prompt(p, x, xs): + if xs[0] not in p.prompt and xs[0] not in p.negative_prompt: + raise RuntimeError(f"Prompt S/R did not find {xs[0]} in prompt or negative prompt.") + + p.prompt = p.prompt.replace(xs[0], x) + p.negative_prompt = p.negative_prompt.replace(xs[0], x) + + +def apply_order(p, x, xs): + token_order = [] + + # Initally grab the tokens from the prompt, so they can be replaced in order of earliest seen + for token in x: + token_order.append((p.prompt.find(token), token)) + + token_order.sort(key=lambda t: t[0]) + + prompt_parts = [] + + # Split the prompt up, taking out the tokens + for _, token in token_order: + n = p.prompt.find(token) + prompt_parts.append(p.prompt[0:n]) + p.prompt = p.prompt[n + len(token):] + + # Rebuild the prompt with the tokens in the order we want + prompt_tmp = "" + for idx, part in enumerate(prompt_parts): + prompt_tmp += part + prompt_tmp += x[idx] + p.prompt = prompt_tmp + p.prompt + + +def apply_sampler(p, x, xs): + sampler_name = sd_samplers.samplers_map.get(x.lower(), None) + if sampler_name is None: + raise RuntimeError(f"Unknown sampler: {x}") + + p.sampler_name = sampler_name + + +def confirm_samplers(p, xs): + for x in xs: + if x.lower() not in sd_samplers.samplers_map: + raise RuntimeError(f"Unknown sampler: {x}") + + +def apply_checkpoint(p, x, xs): + info = modules.sd_models.get_closet_checkpoint_match(x) + if info is None: + raise RuntimeError(f"Unknown checkpoint: {x}") + modules.sd_models.reload_model_weights(shared.sd_model, info) + + +def confirm_checkpoints(p, xs): + for x in xs: + if modules.sd_models.get_closet_checkpoint_match(x) is None: + raise RuntimeError(f"Unknown checkpoint: {x}") + + +def apply_clip_skip(p, x, xs): + opts.data["CLIP_stop_at_last_layers"] = x + + +def apply_upscale_latent_space(p, x, xs): + if x.lower().strip() != '0': + opts.data["use_scale_latent_for_hires_fix"] = True + else: + opts.data["use_scale_latent_for_hires_fix"] = False + + +def find_vae(name: str): + if name.lower() in ['auto', 'automatic']: + return modules.sd_vae.unspecified + if name.lower() == 'none': + return None + else: + choices = [x for x in sorted(modules.sd_vae.vae_dict, key=lambda x: len(x)) if name.lower().strip() in x.lower()] + if len(choices) == 0: + print(f"No VAE found for {name}; using automatic") + return modules.sd_vae.unspecified + else: + return modules.sd_vae.vae_dict[choices[0]] + + +def apply_vae(p, x, xs): + modules.sd_vae.reload_vae_weights(shared.sd_model, vae_file=find_vae(x)) + + +def apply_styles(p: StableDiffusionProcessingTxt2Img, x: str, _): + p.styles.extend(x.split(',')) + + +def format_value_add_label(p, opt, x): + if type(x) == float: + x = round(x, 8) + + return f"{opt.label}: {x}" + + +def format_value(p, opt, x): + if type(x) == float: + x = round(x, 8) + return x + + +def format_value_join_list(p, opt, x): + return ", ".join(x) + + +def do_nothing(p, x, xs): + pass + + +def format_nothing(p, opt, x): + return "" + + +def str_permutations(x): + """dummy function for specifying it in AxisOption's type when you want to get a list of permutations""" + return x + + +class AxisOption: + def __init__(self, label, type, apply, format_value=format_value_add_label, confirm=None, cost=0.0, choices=None): + self.label = label + self.type = type + self.apply = apply + self.format_value = format_value + self.confirm = confirm + self.cost = cost + self.choices = choices + + +class AxisOptionImg2Img(AxisOption): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.is_img2img = True + +class AxisOptionTxt2Img(AxisOption): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.is_img2img = False + + +axis_options = [ + AxisOption("Nothing", str, do_nothing, format_value=format_nothing), + AxisOption("Seed", int, apply_field("seed")), + AxisOption("Var. seed", int, apply_field("subseed")), + AxisOption("Var. strength", float, apply_field("subseed_strength")), + AxisOption("Steps", int, apply_field("steps")), + AxisOptionTxt2Img("Hires steps", int, apply_field("hr_second_pass_steps")), + AxisOption("CFG Scale", float, apply_field("cfg_scale")), + AxisOptionImg2Img("Image CFG Scale", float, apply_field("image_cfg_scale")), + AxisOption("Prompt S/R", str, apply_prompt, format_value=format_value), + AxisOption("Prompt order", str_permutations, apply_order, format_value=format_value_join_list), + AxisOptionTxt2Img("Sampler", str, apply_sampler, format_value=format_value, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers]), + AxisOptionImg2Img("Sampler", str, apply_sampler, format_value=format_value, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers_for_img2img]), + AxisOption("Checkpoint name", str, apply_checkpoint, format_value=format_value, confirm=confirm_checkpoints, cost=1.0, choices=lambda: list(sd_models.checkpoints_list)), + AxisOption("Sigma Churn", float, apply_field("s_churn")), + AxisOption("Sigma min", float, apply_field("s_tmin")), + AxisOption("Sigma max", float, apply_field("s_tmax")), + AxisOption("Sigma noise", float, apply_field("s_noise")), + AxisOption("Eta", float, apply_field("eta")), + AxisOption("Clip skip", int, apply_clip_skip), + AxisOption("Denoising", float, apply_field("denoising_strength")), + AxisOptionTxt2Img("Hires upscaler", str, apply_field("hr_upscaler"), choices=lambda: [*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]]), + AxisOptionImg2Img("Cond. Image Mask Weight", float, apply_field("inpainting_mask_weight")), + AxisOption("VAE", str, apply_vae, cost=0.7, choices=lambda: list(sd_vae.vae_dict)), + AxisOption("Styles", str, apply_styles, choices=lambda: list(shared.prompt_styles.styles)), +] + + +def draw_xyz_grid(p, xs, ys, zs, x_labels, y_labels, z_labels, cell, draw_legend, include_lone_images, include_sub_grids, first_axes_processed, second_axes_processed, margin_size): + hor_texts = [[images.GridAnnotation(x)] for x in x_labels] + ver_texts = [[images.GridAnnotation(y)] for y in y_labels] + title_texts = [[images.GridAnnotation(z)] for z in z_labels] + + # Temporary list of all the images that are generated to be populated into the grid. + # Will be filled with empty images for any individual step that fails to process properly + image_cache = [None] * (len(xs) * len(ys) * len(zs)) + + processed_result = None + cell_mode = "P" + cell_size = (1, 1) + + state.job_count = len(xs) * len(ys) * len(zs) * p.n_iter + + def process_cell(x, y, z, ix, iy, iz): + nonlocal image_cache, processed_result, cell_mode, cell_size + + def index(ix, iy, iz): + return ix + iy * len(xs) + iz * len(xs) * len(ys) + + state.job = f"{index(ix, iy, iz) + 1} out of {len(xs) * len(ys) * len(zs)}" + + processed: Processed = cell(x, y, z) + + try: + # this dereference will throw an exception if the image was not processed + # (this happens in cases such as if the user stops the process from the UI) + processed_image = processed.images[0] + + if processed_result is None: + # Use our first valid processed result as a template container to hold our full results + processed_result = copy(processed) + cell_mode = processed_image.mode + cell_size = processed_image.size + processed_result.images = [Image.new(cell_mode, cell_size)] + processed_result.all_prompts = [processed.prompt] + processed_result.all_seeds = [processed.seed] + processed_result.infotexts = [processed.infotexts[0]] + + image_cache[index(ix, iy, iz)] = processed_image + if include_lone_images: + processed_result.images.append(processed_image) + processed_result.all_prompts.append(processed.prompt) + processed_result.all_seeds.append(processed.seed) + processed_result.infotexts.append(processed.infotexts[0]) + except: + image_cache[index(ix, iy, iz)] = Image.new(cell_mode, cell_size) + + if first_axes_processed == 'x': + for ix, x in enumerate(xs): + if second_axes_processed == 'y': + for iy, y in enumerate(ys): + for iz, z in enumerate(zs): + process_cell(x, y, z, ix, iy, iz) + else: + for iz, z in enumerate(zs): + for iy, y in enumerate(ys): + process_cell(x, y, z, ix, iy, iz) + elif first_axes_processed == 'y': + for iy, y in enumerate(ys): + if second_axes_processed == 'x': + for ix, x in enumerate(xs): + for iz, z in enumerate(zs): + process_cell(x, y, z, ix, iy, iz) + else: + for iz, z in enumerate(zs): + for ix, x in enumerate(xs): + process_cell(x, y, z, ix, iy, iz) + elif first_axes_processed == 'z': + for iz, z in enumerate(zs): + if second_axes_processed == 'x': + for ix, x in enumerate(xs): + for iy, y in enumerate(ys): + process_cell(x, y, z, ix, iy, iz) + else: + for iy, y in enumerate(ys): + for ix, x in enumerate(xs): + process_cell(x, y, z, ix, iy, iz) + + if not processed_result: + print("Unexpected error: draw_xyz_grid failed to return even a single processed image") + return Processed(p, []) + + sub_grids = [None] * len(zs) + for i in range(len(zs)): + start_index = i * len(xs) * len(ys) + end_index = start_index + len(xs) * len(ys) + grid = images.image_grid(image_cache[start_index:end_index], rows=len(ys)) + if draw_legend: + grid = images.draw_grid_annotations(grid, cell_size[0], cell_size[1], hor_texts, ver_texts, margin_size) + sub_grids[i] = grid + if include_sub_grids and len(zs) > 1: + processed_result.images.insert(i+1, grid) + + sub_grid_size = sub_grids[0].size + z_grid = images.image_grid(sub_grids, rows=1) + if draw_legend: + z_grid = images.draw_grid_annotations(z_grid, sub_grid_size[0], sub_grid_size[1], title_texts, [[images.GridAnnotation()]]) + processed_result.images[0] = z_grid + + return processed_result, sub_grids + + +class SharedSettingsStackHelper(object): + def __enter__(self): + self.CLIP_stop_at_last_layers = opts.CLIP_stop_at_last_layers + self.vae = opts.sd_vae + + def __exit__(self, exc_type, exc_value, tb): + opts.data["sd_vae"] = self.vae + modules.sd_models.reload_model_weights() + modules.sd_vae.reload_vae_weights() + + opts.data["CLIP_stop_at_last_layers"] = self.CLIP_stop_at_last_layers + + +re_range = re.compile(r"\s*([+-]?\s*\d+)\s*-\s*([+-]?\s*\d+)(?:\s*\(([+-]\d+)\s*\))?\s*") +re_range_float = re.compile(r"\s*([+-]?\s*\d+(?:.\d*)?)\s*-\s*([+-]?\s*\d+(?:.\d*)?)(?:\s*\(([+-]\d+(?:.\d*)?)\s*\))?\s*") + +re_range_count = re.compile(r"\s*([+-]?\s*\d+)\s*-\s*([+-]?\s*\d+)(?:\s*\[(\d+)\s*\])?\s*") +re_range_count_float = re.compile(r"\s*([+-]?\s*\d+(?:.\d*)?)\s*-\s*([+-]?\s*\d+(?:.\d*)?)(?:\s*\[(\d+(?:.\d*)?)\s*\])?\s*") + + +class Script(scripts.Script): + def title(self): + return "X/Y/Z plot" + + def ui(self, is_img2img): + self.current_axis_options = [x for x in axis_options if type(x) == AxisOption or x.is_img2img == is_img2img] + + with gr.Row(): + with gr.Column(scale=19): + with gr.Row(): + x_type = gr.Dropdown(label="X type", choices=[x.label for x in self.current_axis_options], value=self.current_axis_options[1].label, type="index", elem_id=self.elem_id("x_type")) + x_values = gr.Textbox(label="X values", lines=1, elem_id=self.elem_id("x_values")) + fill_x_button = ToolButton(value=fill_values_symbol, elem_id="xyz_grid_fill_x_tool_button", visible=False) + + with gr.Row(): + y_type = gr.Dropdown(label="Y type", choices=[x.label for x in self.current_axis_options], value=self.current_axis_options[0].label, type="index", elem_id=self.elem_id("y_type")) + y_values = gr.Textbox(label="Y values", lines=1, elem_id=self.elem_id("y_values")) + fill_y_button = ToolButton(value=fill_values_symbol, elem_id="xyz_grid_fill_y_tool_button", visible=False) + + with gr.Row(): + z_type = gr.Dropdown(label="Z type", choices=[x.label for x in self.current_axis_options], value=self.current_axis_options[0].label, type="index", elem_id=self.elem_id("z_type")) + z_values = gr.Textbox(label="Z values", lines=1, elem_id=self.elem_id("z_values")) + fill_z_button = ToolButton(value=fill_values_symbol, elem_id="xyz_grid_fill_z_tool_button", visible=False) + + with gr.Row(variant="compact", elem_id="axis_options"): + with gr.Column(): + draw_legend = gr.Checkbox(label='Draw legend', value=True, elem_id=self.elem_id("draw_legend")) + no_fixed_seeds = gr.Checkbox(label='Keep -1 for seeds', value=False, elem_id=self.elem_id("no_fixed_seeds")) + with gr.Column(): + include_lone_images = gr.Checkbox(label='Include Sub Images', value=False, elem_id=self.elem_id("include_lone_images")) + include_sub_grids = gr.Checkbox(label='Include Sub Grids', value=False, elem_id=self.elem_id("include_sub_grids")) + with gr.Column(): + margin_size = gr.Slider(label="Grid margins (px)", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size")) + + with gr.Row(variant="compact", elem_id="swap_axes"): + swap_xy_axes_button = gr.Button(value="Swap X/Y axes", elem_id="xy_grid_swap_axes_button") + swap_yz_axes_button = gr.Button(value="Swap Y/Z axes", elem_id="yz_grid_swap_axes_button") + swap_xz_axes_button = gr.Button(value="Swap X/Z axes", elem_id="xz_grid_swap_axes_button") + + def swap_axes(axis1_type, axis1_values, axis2_type, axis2_values): + return self.current_axis_options[axis2_type].label, axis2_values, self.current_axis_options[axis1_type].label, axis1_values + + xy_swap_args = [x_type, x_values, y_type, y_values] + swap_xy_axes_button.click(swap_axes, inputs=xy_swap_args, outputs=xy_swap_args) + yz_swap_args = [y_type, y_values, z_type, z_values] + swap_yz_axes_button.click(swap_axes, inputs=yz_swap_args, outputs=yz_swap_args) + xz_swap_args = [x_type, x_values, z_type, z_values] + swap_xz_axes_button.click(swap_axes, inputs=xz_swap_args, outputs=xz_swap_args) + + def fill(x_type): + axis = self.current_axis_options[x_type] + return ", ".join(axis.choices()) if axis.choices else gr.update() + + fill_x_button.click(fn=fill, inputs=[x_type], outputs=[x_values]) + fill_y_button.click(fn=fill, inputs=[y_type], outputs=[y_values]) + fill_z_button.click(fn=fill, inputs=[z_type], outputs=[z_values]) + + def select_axis(x_type): + return gr.Button.update(visible=self.current_axis_options[x_type].choices is not None) + + x_type.change(fn=select_axis, inputs=[x_type], outputs=[fill_x_button]) + y_type.change(fn=select_axis, inputs=[y_type], outputs=[fill_y_button]) + z_type.change(fn=select_axis, inputs=[z_type], outputs=[fill_z_button]) + + self.infotext_fields = ( + (x_type, "X Type"), + (x_values, "X Values"), + (y_type, "Y Type"), + (y_values, "Y Values"), + (z_type, "Z Type"), + (z_values, "Z Values"), + ) + + return [x_type, x_values, y_type, y_values, z_type, z_values, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, margin_size] + + def run(self, p, x_type, x_values, y_type, y_values, z_type, z_values, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, margin_size): + if not no_fixed_seeds: + modules.processing.fix_seed(p) + + if not opts.return_grid: + p.batch_size = 1 + + def process_axis(opt, vals): + if opt.label == 'Nothing': + return [0] + + valslist = [x.strip() for x in chain.from_iterable(csv.reader(StringIO(vals)))] + + if opt.type == int: + valslist_ext = [] + + for val in valslist: + m = re_range.fullmatch(val) + mc = re_range_count.fullmatch(val) + if m is not None: + start = int(m.group(1)) + end = int(m.group(2))+1 + step = int(m.group(3)) if m.group(3) is not None else 1 + + valslist_ext += list(range(start, end, step)) + elif mc is not None: + start = int(mc.group(1)) + end = int(mc.group(2)) + num = int(mc.group(3)) if mc.group(3) is not None else 1 + + valslist_ext += [int(x) for x in np.linspace(start=start, stop=end, num=num).tolist()] + else: + valslist_ext.append(val) + + valslist = valslist_ext + elif opt.type == float: + valslist_ext = [] + + for val in valslist: + m = re_range_float.fullmatch(val) + mc = re_range_count_float.fullmatch(val) + if m is not None: + start = float(m.group(1)) + end = float(m.group(2)) + step = float(m.group(3)) if m.group(3) is not None else 1 + + valslist_ext += np.arange(start, end + step, step).tolist() + elif mc is not None: + start = float(mc.group(1)) + end = float(mc.group(2)) + num = int(mc.group(3)) if mc.group(3) is not None else 1 + + valslist_ext += np.linspace(start=start, stop=end, num=num).tolist() + else: + valslist_ext.append(val) + + valslist = valslist_ext + elif opt.type == str_permutations: + valslist = list(permutations(valslist)) + + valslist = [opt.type(x) for x in valslist] + + # Confirm options are valid before starting + if opt.confirm: + opt.confirm(p, valslist) + + return valslist + + x_opt = self.current_axis_options[x_type] + xs = process_axis(x_opt, x_values) + + y_opt = self.current_axis_options[y_type] + ys = process_axis(y_opt, y_values) + + z_opt = self.current_axis_options[z_type] + zs = process_axis(z_opt, z_values) + + def fix_axis_seeds(axis_opt, axis_list): + if axis_opt.label in ['Seed', 'Var. seed']: + return [int(random.randrange(4294967294)) if val is None or val == '' or val == -1 else val for val in axis_list] + else: + return axis_list + + if not no_fixed_seeds: + xs = fix_axis_seeds(x_opt, xs) + ys = fix_axis_seeds(y_opt, ys) + zs = fix_axis_seeds(z_opt, zs) + + if x_opt.label == 'Steps': + total_steps = sum(xs) * len(ys) * len(zs) + elif y_opt.label == 'Steps': + total_steps = sum(ys) * len(xs) * len(zs) + elif z_opt.label == 'Steps': + total_steps = sum(zs) * len(xs) * len(ys) + else: + total_steps = p.steps * len(xs) * len(ys) * len(zs) + + if isinstance(p, StableDiffusionProcessingTxt2Img) and p.enable_hr: + if x_opt.label == "Hires steps": + total_steps += sum(xs) * len(ys) * len(zs) + elif y_opt.label == "Hires steps": + total_steps += sum(ys) * len(xs) * len(zs) + elif z_opt.label == "Hires steps": + total_steps += sum(zs) * len(xs) * len(ys) + elif p.hr_second_pass_steps: + total_steps += p.hr_second_pass_steps * len(xs) * len(ys) * len(zs) + else: + total_steps *= 2 + + total_steps *= p.n_iter + + image_cell_count = p.n_iter * p.batch_size + cell_console_text = f"; {image_cell_count} images per cell" if image_cell_count > 1 else "" + plural_s = 's' if len(zs) > 1 else '' + print(f"X/Y/Z plot will create {len(xs) * len(ys) * len(zs) * image_cell_count} images on {len(zs)} {len(xs)}x{len(ys)} grid{plural_s}{cell_console_text}. (Total steps to process: {total_steps})") + shared.total_tqdm.updateTotal(total_steps) + + grid_infotext = [None] + + state.xyz_plot_x = AxisInfo(x_opt, xs) + state.xyz_plot_y = AxisInfo(y_opt, ys) + state.xyz_plot_z = AxisInfo(z_opt, zs) + + # If one of the axes is very slow to change between (like SD model + # checkpoint), then make sure it is in the outer iteration of the nested + # `for` loop. + first_axes_processed = 'x' + second_axes_processed = 'y' + if x_opt.cost > y_opt.cost and x_opt.cost > z_opt.cost: + first_axes_processed = 'x' + if y_opt.cost > z_opt.cost: + second_axes_processed = 'y' + else: + second_axes_processed = 'z' + elif y_opt.cost > x_opt.cost and y_opt.cost > z_opt.cost: + first_axes_processed = 'y' + if x_opt.cost > z_opt.cost: + second_axes_processed = 'x' + else: + second_axes_processed = 'z' + elif z_opt.cost > x_opt.cost and z_opt.cost > y_opt.cost: + first_axes_processed = 'z' + if x_opt.cost > y_opt.cost: + second_axes_processed = 'x' + else: + second_axes_processed = 'y' + + def cell(x, y, z): + if shared.state.interrupted: + return Processed(p, [], p.seed, "") + + pc = copy(p) + pc.styles = pc.styles[:] + x_opt.apply(pc, x, xs) + y_opt.apply(pc, y, ys) + z_opt.apply(pc, z, zs) + + res = process_images(pc) + + if grid_infotext[0] is None: + pc.extra_generation_params = copy(pc.extra_generation_params) + pc.extra_generation_params['Script'] = self.title() + + if x_opt.label != 'Nothing': + pc.extra_generation_params["X Type"] = x_opt.label + pc.extra_generation_params["X Values"] = x_values + if x_opt.label in ["Seed", "Var. seed"] and not no_fixed_seeds: + pc.extra_generation_params["Fixed X Values"] = ", ".join([str(x) for x in xs]) + + if y_opt.label != 'Nothing': + pc.extra_generation_params["Y Type"] = y_opt.label + pc.extra_generation_params["Y Values"] = y_values + if y_opt.label in ["Seed", "Var. seed"] and not no_fixed_seeds: + pc.extra_generation_params["Fixed Y Values"] = ", ".join([str(y) for y in ys]) + + if z_opt.label != 'Nothing': + pc.extra_generation_params["Z Type"] = z_opt.label + pc.extra_generation_params["Z Values"] = z_values + if z_opt.label in ["Seed", "Var. seed"] and not no_fixed_seeds: + pc.extra_generation_params["Fixed Z Values"] = ", ".join([str(z) for z in zs]) + + grid_infotext[0] = processing.create_infotext(pc, pc.all_prompts, pc.all_seeds, pc.all_subseeds) + + return res + + with SharedSettingsStackHelper(): + processed, sub_grids = draw_xyz_grid( + p, + xs=xs, + ys=ys, + zs=zs, + x_labels=[x_opt.format_value(p, x_opt, x) for x in xs], + y_labels=[y_opt.format_value(p, y_opt, y) for y in ys], + z_labels=[z_opt.format_value(p, z_opt, z) for z in zs], + cell=cell, + draw_legend=draw_legend, + include_lone_images=include_lone_images, + include_sub_grids=include_sub_grids, + first_axes_processed=first_axes_processed, + second_axes_processed=second_axes_processed, + margin_size=margin_size + ) + + if opts.grid_save and len(sub_grids) > 1: + for sub_grid in sub_grids: + images.save_image(sub_grid, p.outpath_grids, "xyz_grid", info=grid_infotext[0], extension=opts.grid_format, prompt=p.prompt, seed=processed.seed, grid=True, p=p) + + if opts.grid_save: + images.save_image(processed.images[0], p.outpath_grids, "xyz_grid", info=grid_infotext[0], extension=opts.grid_format, prompt=p.prompt, seed=processed.seed, grid=True, p=p) + + return processed diff --git a/sd/stable-diffusion-webui/style.css b/sd/stable-diffusion-webui/style.css new file mode 100644 index 0000000000000000000000000000000000000000..8f58bc9d0cba1f6ec371f211f531530245bf367e --- /dev/null +++ b/sd/stable-diffusion-webui/style.css @@ -0,0 +1,961 @@ +.container { + max-width: 100%; +} + +.token-counter{ + position: absolute; + display: inline-block; + right: 2em; + min-width: 0 !important; + width: auto; + z-index: 100; +} + +.token-counter.error span{ + box-shadow: 0 0 0.0 0.3em rgba(255,0,0,0.15), inset 0 0 0.6em rgba(255,0,0,0.075); + border: 2px solid rgba(255,0,0,0.4) !important; +} + +.token-counter div{ + display: inline; +} + +.token-counter span{ + padding: 0.1em 0.75em; +} + +#sh{ + min-width: 2em; + min-height: 2em; + max-width: 2em; + max-height: 2em; + flex-grow: 0; + padding-left: 0.25em; + padding-right: 0.25em; + margin: 0.1em 0; + opacity: 0%; + cursor: default; +} + +.output-html p {margin: 0 0.5em;} + +.row > *, +.row > .gr-form > * { + min-width: min(120px, 100%); + flex: 1 1 0%; +} + +.performance { + font-size: 0.85em; + color: #444; +} + +.performance p{ + display: inline-block; +} + +.performance .time { + margin-right: 0; +} + +.performance .vram { +} + +#txt2img_generate, #img2img_generate { + min-height: 4.5em; +} + +@media screen and (min-width: 2500px) { + #txt2img_gallery, #img2img_gallery { + min-height: 768px; + } +} + +#txt2img_gallery img, #img2img_gallery img{ + object-fit: scale-down; +} +#txt2img_actions_column, #img2img_actions_column { + margin: 0.35rem 0.75rem 0.35rem 0; +} +#script_list { + padding: .625rem .75rem 0 .625rem; +} +.justify-center.overflow-x-scroll { + justify-content: left; +} + +.justify-center.overflow-x-scroll button:first-of-type { + margin-left: auto; +} + +.justify-center.overflow-x-scroll button:last-of-type { + margin-right: auto; +} + +[id$=_random_seed], [id$=_random_subseed], [id$=_reuse_seed], [id$=_reuse_subseed], #open_folder{ + min-width: 2.3em; + height: 2.5em; + flex-grow: 0; + padding-left: 0.25em; + padding-right: 0.25em; +} + +#hidden_element{ + display: none; +} + +[id$=_seed_row], [id$=_subseed_row]{ + gap: 0.5rem; + padding: 0.6em; +} + +[id$=_subseed_show_box]{ + min-width: auto; + flex-grow: 0; +} + +[id$=_subseed_show_box] > div{ + border: 0; + height: 100%; +} + +[id$=_subseed_show]{ + min-width: auto; + flex-grow: 0; + padding: 0; +} + +[id$=_subseed_show] label{ + height: 100%; +} + +#txt2img_actions_column, #img2img_actions_column{ + gap: 0; + margin-right: .75rem; +} + +#txt2img_tools, #img2img_tools{ + gap: 0.4em; +} + +#interrogate_col{ + min-width: 0 !important; + max-width: 8em !important; + margin-right: 1em; + gap: 0; +} +#interrogate, #deepbooru{ + margin: 0em 0.25em 0.5em 0.25em; + min-width: 8em; + max-width: 8em; +} + +#style_pos_col, #style_neg_col{ + min-width: 8em !important; +} + +#txt2img_styles_row, #img2img_styles_row{ + gap: 0.25em; + margin-top: 0.3em; +} + +#txt2img_styles_row > button, #img2img_styles_row > button{ + margin: 0; +} + +#txt2img_styles, #img2img_styles{ + padding: 0; +} + +#txt2img_styles > label > div, #img2img_styles > label > div{ + min-height: 3.2em; +} + +ul.list-none{ + max-height: 35em; + z-index: 2000; +} + +.gr-form{ + background: transparent; +} + +.my-4{ + margin-top: 0; + margin-bottom: 0; +} + +#resize_mode{ + flex: 1.5; +} + +button{ + align-self: stretch !important; +} + +.overflow-hidden, .gr-panel{ + overflow: visible !important; +} + +#x_type, #y_type{ + max-width: 10em; +} + +#txt2img_preview, #img2img_preview, #ti_preview{ + position: absolute; + width: 320px; + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; + margin-top: 34px; + z-index: 100; + border: none; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +@media screen and (min-width: 768px) { + #txt2img_preview, #img2img_preview, #ti_preview { + position: absolute; + } +} + +@media screen and (max-width: 767px) { + #txt2img_preview, #img2img_preview, #ti_preview { + position: relative; + } +} + +#txt2img_preview div.left-0.top-0, #img2img_preview div.left-0.top-0, #ti_preview div.left-0.top-0{ + display: none; +} + +fieldset span.text-gray-500, .gr-block.gr-box span.text-gray-500, label.block span{ + position: absolute; + top: -0.7em; + line-height: 1.2em; + padding: 0; + margin: 0 0.5em; + + background-color: white; + box-shadow: 6px 0 6px 0px white, -6px 0 6px 0px white; + + z-index: 300; +} + +.dark fieldset span.text-gray-500, .dark .gr-block.gr-box span.text-gray-500, .dark label.block span{ + background-color: rgb(31, 41, 55); + box-shadow: none; + border: 1px solid rgba(128, 128, 128, 0.1); + border-radius: 6px; + padding: 0.1em 0.5em; +} + +#txt2img_column_batch, #img2img_column_batch{ + min-width: min(13.5em, 100%) !important; +} + +#settings fieldset span.text-gray-500, #settings .gr-block.gr-box span.text-gray-500, #settings label.block span{ + position: relative; + border: none; + margin-right: 8em; +} + +#settings .gr-panel div.flex-col div.justify-between div{ + position: relative; + z-index: 200; +} + +#settings{ + display: block; +} + +#settings > div{ + border: none; + margin-left: 10em; +} + +#settings > div.flex-wrap{ + float: left; + display: block; + margin-left: 0; + width: 10em; +} + +#settings > div.flex-wrap button{ + display: block; + border: none; + text-align: left; +} + +#settings_result{ + height: 1.4em; + margin: 0 1.2em; +} + +input[type="range"]{ + margin: 0.5em 0 -0.3em 0; +} + +#mask_bug_info { + text-align: center; + display: block; + margin-top: -0.75em; + margin-bottom: -0.75em; +} + +#txt2img_negative_prompt, #img2img_negative_prompt{ +} + +/* gradio 3.8 adds opacity to progressbar which makes it blink; disable it here */ +.transition.opacity-20 { + opacity: 1 !important; +} + +/* more gradio's garbage cleanup */ +.min-h-\[4rem\] { min-height: unset !important; } +.min-h-\[6rem\] { min-height: unset !important; } + +.progressDiv{ + position: relative; + height: 20px; + background: #b4c0cc; + border-radius: 3px !important; + margin-bottom: -3px; +} + +.dark .progressDiv{ + background: #424c5b; +} + +.progressDiv .progress{ + width: 0%; + height: 20px; + background: #0060df; + color: white; + font-weight: bold; + line-height: 20px; + padding: 0 8px 0 0; + text-align: right; + border-radius: 3px; + overflow: visible; + white-space: nowrap; + padding: 0 0.5em; +} + +.livePreview{ + position: absolute; + z-index: 300; + background-color: white; + margin: -4px; +} + +.dark .livePreview{ + background-color: rgb(17 24 39 / var(--tw-bg-opacity)); +} + +.livePreview img{ + position: absolute; + object-fit: contain; + width: 100%; + height: 100%; +} + +#lightboxModal{ + display: none; + position: fixed; + z-index: 1001; + padding-top: 100px; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(20, 20, 20, 0.95); + user-select: none; + -webkit-user-select: none; +} + +.modalControls { + display: grid; + grid-template-columns: 32px 32px 32px 1fr 32px; + grid-template-areas: "zoom tile save space close"; + position: absolute; + top: 0; + left: 0; + right: 0; + padding: 16px; + gap: 16px; + background-color: rgba(0,0,0,0.2); +} + +.modalClose { + grid-area: close; +} + +.modalZoom { + grid-area: zoom; +} + +.modalSave { + grid-area: save; +} + +.modalTileImage { + grid-area: tile; +} + +.modalClose, +.modalZoom, +.modalTileImage { + color: white; + font-size: 35px; + font-weight: bold; + cursor: pointer; +} + +.modalSave { + color: white; + font-size: 28px; + margin-top: 8px; + font-weight: bold; + cursor: pointer; +} + +.modalClose:hover, +.modalClose:focus, +.modalSave:hover, +.modalSave:focus, +.modalZoom:hover, +.modalZoom:focus { + color: #999; + text-decoration: none; + cursor: pointer; +} + +#modalImage { + display: block; + margin-left: auto; + margin-right: auto; + margin-top: auto; + width: auto; +} + +.modalImageFullscreen { + object-fit: contain; + height: 90%; +} + +.modalPrev, +.modalNext { + cursor: pointer; + position: absolute; + top: 50%; + width: auto; + padding: 16px; + margin-top: -50px; + color: white; + font-weight: bold; + font-size: 20px; + transition: 0.6s ease; + border-radius: 0 3px 3px 0; + user-select: none; + -webkit-user-select: none; +} + +.modalNext { + right: 0; + border-radius: 3px 0 0 3px; +} + +.modalPrev:hover, +.modalNext:hover { + background-color: rgba(0, 0, 0, 0.8); +} + +#imageARPreview{ + position:absolute; + top:0px; + left:0px; + border:2px solid red; + background:rgba(255, 0, 0, 0.3); + z-index: 900; + pointer-events:none; + display:none +} + +#txt2img_generate_box, #img2img_generate_box{ + position: relative; +} + +#txt2img_interrupt, #img2img_interrupt, #txt2img_skip, #img2img_skip{ + position: absolute; + width: 50%; + height: 100%; + background: #b4c0cc; + display: none; +} + +#txt2img_interrupt, #img2img_interrupt{ + left: 0; + border-radius: 0.5rem 0 0 0.5rem; +} +#txt2img_skip, #img2img_skip{ + right: 0; + border-radius: 0 0.5rem 0.5rem 0; +} + +.red { + color: red; +} + +.gallery-item { + --tw-bg-opacity: 0 !important; +} + +#context-menu{ + z-index:9999; + position:absolute; + display:block; + padding:0px 0; + border:2px solid #a55000; + border-radius:8px; + box-shadow:1px 1px 2px #CE6400; + width: 200px; +} + +.context-menu-items{ + list-style: none; + margin: 0; + padding: 0; +} + +.context-menu-items a{ + display:block; + padding:5px; + cursor:pointer; +} + +.context-menu-items a:hover{ + background: #a55000; +} + +#quicksettings { + width: fit-content; +} + +#quicksettings > div, #quicksettings > fieldset{ + max-width: 24em; + min-width: 24em; + padding: 0; + border: none; + box-shadow: none; + background: none; + margin-right: 10px; +} + +#quicksettings > div > div > div > label > span { + position: relative; + margin-right: 9em; + margin-bottom: -1em; +} + +canvas[key="mask"] { + z-index: 12 !important; + filter: invert(); + mix-blend-mode: multiply; + pointer-events: none; +} + + +/* gradio 3.4.1 stuff for editable scrollbar values */ +.gr-box > div > div > input.gr-text-input{ + position: absolute; + right: 0.5em; + top: -0.6em; + z-index: 400; + width: 6em; +} +#quicksettings .gr-box > div > div > input.gr-text-input { + top: -1.12em; +} + +.row.gr-compact{ + overflow: visible; +} + +#img2img_image, #img2img_image > .h-60, #img2img_image > .h-60 > div, #img2img_image > .h-60 > div > img, +#img2img_sketch, #img2img_sketch > .h-60, #img2img_sketch > .h-60 > div, #img2img_sketch > .h-60 > div > img, +#img2maskimg, #img2maskimg > .h-60, #img2maskimg > .h-60 > div, #img2maskimg > .h-60 > div > img, +#inpaint_sketch, #inpaint_sketch > .h-60, #inpaint_sketch > .h-60 > div, #inpaint_sketch > .h-60 > div > img +{ + height: 480px !important; + max-height: 480px !important; + min-height: 480px !important; +} + +/* Extensions */ + +#tab_extensions table{ + border-collapse: collapse; +} + +#tab_extensions table td, #tab_extensions table th{ + border: 1px solid #ccc; + padding: 0.25em 0.5em; +} + +#tab_extensions table input[type="checkbox"]{ + margin-right: 0.5em; +} + +#tab_extensions button{ + max-width: 16em; +} + +#tab_extensions input[disabled="disabled"]{ + opacity: 0.5; +} + +.extension-tag{ + font-weight: bold; + font-size: 95%; +} + +#available_extensions .info{ + margin: 0; +} + +#available_extensions .date_added{ + opacity: 0.85; + font-size: 90%; +} + +#image_buttons_txt2img button, #image_buttons_img2img button, #image_buttons_extras button{ + min-width: auto; + padding-left: 0.5em; + padding-right: 0.5em; +} + +.gr-form{ + background-color: white; +} + +.dark .gr-form{ + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); +} + +.gr-button-tool, .gr-button-tool-top{ + max-width: 2.5em; + min-width: 2.5em !important; + height: 2.4em; +} + +.gr-button-tool{ + margin: 0.6em 0em 0.55em 0; +} + +.gr-button-tool-top, #settings .gr-button-tool{ + margin: 1.6em 0.7em 0.55em 0; +} + + +#modelmerger_results_container{ + margin-top: 1em; + overflow: visible; +} + +#modelmerger_models{ + gap: 0; +} + + +#quicksettings .gr-button-tool{ + margin: 0; + border-color: unset; + background-color: unset; +} + +#modelmerger_interp_description>p { + margin: 0!important; + text-align: center; +} +#modelmerger_interp_description { + margin: 0.35rem 0.75rem 1.23rem; +} +#img2img_settings > div.gr-form, #txt2img_settings > div.gr-form { + padding-top: 0.9em; + padding-bottom: 0.9em; +} +#txt2img_settings { + padding-top: 1.16em; + padding-bottom: 0.9em; +} +#img2img_settings { + padding-bottom: 0.9em; +} + +#img2img_settings div.gr-form .gr-form, #txt2img_settings div.gr-form .gr-form, #train_tabs div.gr-form .gr-form{ + border: none; + padding-bottom: 0.5em; +} + +footer { + display: none !important; +} + +#footer{ + text-align: center; +} + +#footer div{ + display: inline-block; +} + +#footer .versions{ + font-size: 85%; + opacity: 0.85; +} + +#txtimg_hr_finalres{ + min-height: 0 !important; + padding: .625rem .75rem; + margin-left: -0.75em + +} + +#txtimg_hr_finalres .resolution{ + font-weight: bold; +} + +#txt2img_checkboxes, #img2img_checkboxes{ + margin-bottom: 0.5em; + margin-left: 0em; +} +#txt2img_checkboxes > div, #img2img_checkboxes > div{ + flex: 0; + white-space: nowrap; + min-width: auto; +} + +#img2img_copy_to_img2img, #img2img_copy_to_sketch, #img2img_copy_to_inpaint, #img2img_copy_to_inpaint_sketch{ + margin-left: 0em; +} + +#axis_options { + margin-left: 0em; +} + +.inactive{ + opacity: 0.5; +} + +[id*='_prompt_container']{ + gap: 0; +} + +[id*='_prompt_container'] > div{ + margin: -0.4em 0 0 0; +} + +.gr-compact { + border: none; +} + +.dark .gr-compact{ + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); + margin-left: 0; +} + +.gr-compact{ + overflow: visible; +} + +.gr-compact > *{ +} + +.gr-compact .gr-block, .gr-compact .gr-form{ + border: none; + box-shadow: none; +} + +.gr-compact .gr-box{ + border-radius: .5rem !important; + border-width: 1px !important; +} + +#mode_img2img > div > div{ + gap: 0 !important; +} + +[id*='img2img_copy_to_'] { + border: none; +} + +[id*='img2img_copy_to_'] > button { +} + +[id*='img2img_label_copy_to_'] { + font-size: 1.0em; + font-weight: bold; + text-align: center; + line-height: 2.4em; +} + +.extra-networks > div > [id *= '_extra_']{ + margin: 0.3em; +} + +.extra-network-subdirs{ + padding: 0.2em 0.35em; +} + +.extra-network-subdirs button{ + margin: 0 0.15em; +} + +#txt2img_extra_networks .search, #img2img_extra_networks .search{ + display: inline-block; + max-width: 16em; + margin: 0.3em; + align-self: center; +} + +#txt2img_extra_view, #img2img_extra_view { + width: auto; +} + +.extra-network-cards .nocards, .extra-network-thumbs .nocards{ + margin: 1.25em 0.5em 0.5em 0.5em; +} + +.extra-network-cards .nocards h1, .extra-network-thumbs .nocards h1{ + font-size: 1.5em; + margin-bottom: 1em; +} + +.extra-network-cards .nocards li, .extra-network-thumbs .nocards li{ + margin-left: 0.5em; +} + +.extra-network-thumbs { + display: flex; + flex-flow: row wrap; + gap: 10px; +} + +.extra-network-thumbs .card { + height: 6em; + width: 6em; + cursor: pointer; + background-image: url('./file=html/card-no-preview.png'); + background-size: cover; + background-position: center center; + position: relative; +} + +.extra-network-thumbs .card:hover .additional a { + display: block; +} + +.extra-network-thumbs .actions .additional a { + background-image: url('./file=html/image-update.svg'); + background-repeat: no-repeat; + background-size: cover; + background-position: center center; + position: absolute; + top: 0; + left: 0; + width: 24px; + height: 24px; + display: none; + font-size: 0; + text-align: -9999; +} + +.extra-network-thumbs .actions .name { + position: absolute; + bottom: 0; + font-size: 10px; + padding: 3px; + width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + background: rgba(0,0,0,.5); + color: white; +} + +.extra-network-thumbs .card:hover .actions .name { + white-space: normal; + word-break: break-all; +} + +.extra-network-cards .card{ + display: inline-block; + margin: 0.5em; + width: 16em; + height: 24em; + box-shadow: 0 0 5px rgba(128, 128, 128, 0.5); + border-radius: 0.2em; + position: relative; + + background-size: auto 100%; + background-position: center; + overflow: hidden; + cursor: pointer; + + background-image: url('./file=html/card-no-preview.png') +} + +.extra-network-cards .card:hover{ + box-shadow: 0 0 2px 0.3em rgba(0, 128, 255, 0.35); +} + +.extra-network-cards .card .actions .additional{ + display: none; +} + +.extra-network-cards .card .actions{ + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding: 0.5em; + color: white; + background: rgba(0,0,0,0.5); + box-shadow: 0 0 0.25em 0.25em rgba(0,0,0,0.5); + text-shadow: 0 0 0.2em black; +} + +.extra-network-cards .card .actions:hover{ + box-shadow: 0 0 0.75em 0.75em rgba(0,0,0,0.5) !important; +} + +.extra-network-cards .card .actions .name{ + font-size: 1.7em; + font-weight: bold; + line-break: anywhere; +} + +.extra-network-cards .card .actions:hover .additional{ + display: block; +} + +.extra-network-cards .card ul{ + margin: 0.25em 0 0.75em 0.25em; + cursor: unset; +} + +.extra-network-cards .card ul a{ + cursor: pointer; +} + +.extra-network-cards .card ul a:hover{ + color: red; +} + +[id*='_prompt_container'] > div { + margin: 0!important; +} diff --git a/sd/stable-diffusion-webui/test/__init__.py b/sd/stable-diffusion-webui/test/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stable-diffusion-webui/test/basic_features/__init__.py b/sd/stable-diffusion-webui/test/basic_features/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stable-diffusion-webui/test/basic_features/extras_test.py b/sd/stable-diffusion-webui/test/basic_features/extras_test.py new file mode 100644 index 0000000000000000000000000000000000000000..0170c511fe54cc6bcf49ec7f75ca7c747de41db5 --- /dev/null +++ b/sd/stable-diffusion-webui/test/basic_features/extras_test.py @@ -0,0 +1,54 @@ +import unittest +import requests +from gradio.processing_utils import encode_pil_to_base64 +from PIL import Image + +class TestExtrasWorking(unittest.TestCase): + def setUp(self): + self.url_extras_single = "http://localhost:7860/sdapi/v1/extra-single-image" + self.extras_single = { + "resize_mode": 0, + "show_extras_results": True, + "gfpgan_visibility": 0, + "codeformer_visibility": 0, + "codeformer_weight": 0, + "upscaling_resize": 2, + "upscaling_resize_w": 128, + "upscaling_resize_h": 128, + "upscaling_crop": True, + "upscaler_1": "None", + "upscaler_2": "None", + "extras_upscaler_2_visibility": 0, + "image": encode_pil_to_base64(Image.open(r"test/test_files/img2img_basic.png")) + } + + def test_simple_upscaling_performed(self): + self.extras_single["upscaler_1"] = "Lanczos" + self.assertEqual(requests.post(self.url_extras_single, json=self.extras_single).status_code, 200) + + +class TestPngInfoWorking(unittest.TestCase): + def setUp(self): + self.url_png_info = "http://localhost:7860/sdapi/v1/extra-single-image" + self.png_info = { + "image": encode_pil_to_base64(Image.open(r"test/test_files/img2img_basic.png")) + } + + def test_png_info_performed(self): + self.assertEqual(requests.post(self.url_png_info, json=self.png_info).status_code, 200) + + +class TestInterrogateWorking(unittest.TestCase): + def setUp(self): + self.url_interrogate = "http://localhost:7860/sdapi/v1/extra-single-image" + self.interrogate = { + "image": encode_pil_to_base64(Image.open(r"test/test_files/img2img_basic.png")), + "model": "clip" + } + + def test_interrogate_performed(self): + self.assertEqual(requests.post(self.url_interrogate, json=self.interrogate).status_code, 200) + + +if __name__ == "__main__": + unittest.main() diff --git a/sd/stable-diffusion-webui/test/basic_features/img2img_test.py b/sd/stable-diffusion-webui/test/basic_features/img2img_test.py new file mode 100644 index 0000000000000000000000000000000000000000..08c5c903e8382ef4b969b01da87bc69fb06ff2b4 --- /dev/null +++ b/sd/stable-diffusion-webui/test/basic_features/img2img_test.py @@ -0,0 +1,66 @@ +import unittest +import requests +from gradio.processing_utils import encode_pil_to_base64 +from PIL import Image + + +class TestImg2ImgWorking(unittest.TestCase): + def setUp(self): + self.url_img2img = "http://localhost:7860/sdapi/v1/img2img" + self.simple_img2img = { + "init_images": [encode_pil_to_base64(Image.open(r"test/test_files/img2img_basic.png"))], + "resize_mode": 0, + "denoising_strength": 0.75, + "mask": None, + "mask_blur": 4, + "inpainting_fill": 0, + "inpaint_full_res": False, + "inpaint_full_res_padding": 0, + "inpainting_mask_invert": False, + "prompt": "example prompt", + "styles": [], + "seed": -1, + "subseed": -1, + "subseed_strength": 0, + "seed_resize_from_h": -1, + "seed_resize_from_w": -1, + "batch_size": 1, + "n_iter": 1, + "steps": 3, + "cfg_scale": 7, + "width": 64, + "height": 64, + "restore_faces": False, + "tiling": False, + "negative_prompt": "", + "eta": 0, + "s_churn": 0, + "s_tmax": 0, + "s_tmin": 0, + "s_noise": 1, + "override_settings": {}, + "sampler_index": "Euler a", + "include_init_images": False + } + + def test_img2img_simple_performed(self): + self.assertEqual(requests.post(self.url_img2img, json=self.simple_img2img).status_code, 200) + + def test_inpainting_masked_performed(self): + self.simple_img2img["mask"] = encode_pil_to_base64(Image.open(r"test/test_files/mask_basic.png")) + self.assertEqual(requests.post(self.url_img2img, json=self.simple_img2img).status_code, 200) + + def test_inpainting_with_inverted_masked_performed(self): + self.simple_img2img["mask"] = encode_pil_to_base64(Image.open(r"test/test_files/mask_basic.png")) + self.simple_img2img["inpainting_mask_invert"] = True + self.assertEqual(requests.post(self.url_img2img, json=self.simple_img2img).status_code, 200) + + def test_img2img_sd_upscale_performed(self): + self.simple_img2img["script_name"] = "sd upscale" + self.simple_img2img["script_args"] = ["", 8, "Lanczos", 2.0] + + self.assertEqual(requests.post(self.url_img2img, json=self.simple_img2img).status_code, 200) + + +if __name__ == "__main__": + unittest.main() diff --git a/sd/stable-diffusion-webui/test/basic_features/txt2img_test.py b/sd/stable-diffusion-webui/test/basic_features/txt2img_test.py new file mode 100644 index 0000000000000000000000000000000000000000..5aa43a44a3e3818d7220a98acf5a6a504cdca3e3 --- /dev/null +++ b/sd/stable-diffusion-webui/test/basic_features/txt2img_test.py @@ -0,0 +1,80 @@ +import unittest +import requests + + +class TestTxt2ImgWorking(unittest.TestCase): + def setUp(self): + self.url_txt2img = "http://localhost:7860/sdapi/v1/txt2img" + self.simple_txt2img = { + "enable_hr": False, + "denoising_strength": 0, + "firstphase_width": 0, + "firstphase_height": 0, + "prompt": "example prompt", + "styles": [], + "seed": -1, + "subseed": -1, + "subseed_strength": 0, + "seed_resize_from_h": -1, + "seed_resize_from_w": -1, + "batch_size": 1, + "n_iter": 1, + "steps": 3, + "cfg_scale": 7, + "width": 64, + "height": 64, + "restore_faces": False, + "tiling": False, + "negative_prompt": "", + "eta": 0, + "s_churn": 0, + "s_tmax": 0, + "s_tmin": 0, + "s_noise": 1, + "sampler_index": "Euler a" + } + + def test_txt2img_simple_performed(self): + self.assertEqual(requests.post(self.url_txt2img, json=self.simple_txt2img).status_code, 200) + + def test_txt2img_with_negative_prompt_performed(self): + self.simple_txt2img["negative_prompt"] = "example negative prompt" + self.assertEqual(requests.post(self.url_txt2img, json=self.simple_txt2img).status_code, 200) + + def test_txt2img_with_complex_prompt_performed(self): + self.simple_txt2img["prompt"] = "((emphasis)), (emphasis1:1.1), [to:1], [from::2], [from:to:0.3], [alt|alt1]" + self.assertEqual(requests.post(self.url_txt2img, json=self.simple_txt2img).status_code, 200) + + def test_txt2img_not_square_image_performed(self): + self.simple_txt2img["height"] = 128 + self.assertEqual(requests.post(self.url_txt2img, json=self.simple_txt2img).status_code, 200) + + def test_txt2img_with_hrfix_performed(self): + self.simple_txt2img["enable_hr"] = True + self.assertEqual(requests.post(self.url_txt2img, json=self.simple_txt2img).status_code, 200) + + def test_txt2img_with_tiling_performed(self): + self.simple_txt2img["tiling"] = True + self.assertEqual(requests.post(self.url_txt2img, json=self.simple_txt2img).status_code, 200) + + def test_txt2img_with_restore_faces_performed(self): + self.simple_txt2img["restore_faces"] = True + self.assertEqual(requests.post(self.url_txt2img, json=self.simple_txt2img).status_code, 200) + + def test_txt2img_with_vanilla_sampler_performed(self): + self.simple_txt2img["sampler_index"] = "PLMS" + self.assertEqual(requests.post(self.url_txt2img, json=self.simple_txt2img).status_code, 200) + self.simple_txt2img["sampler_index"] = "DDIM" + self.assertEqual(requests.post(self.url_txt2img, json=self.simple_txt2img).status_code, 200) + + def test_txt2img_multiple_batches_performed(self): + self.simple_txt2img["n_iter"] = 2 + self.assertEqual(requests.post(self.url_txt2img, json=self.simple_txt2img).status_code, 200) + + def test_txt2img_batch_performed(self): + self.simple_txt2img["batch_size"] = 2 + self.assertEqual(requests.post(self.url_txt2img, json=self.simple_txt2img).status_code, 200) + + +if __name__ == "__main__": + unittest.main() diff --git a/sd/stable-diffusion-webui/test/basic_features/utils_test.py b/sd/stable-diffusion-webui/test/basic_features/utils_test.py new file mode 100644 index 0000000000000000000000000000000000000000..0bfc28a0d30c070c292ff8154e9b93a74abecb85 --- /dev/null +++ b/sd/stable-diffusion-webui/test/basic_features/utils_test.py @@ -0,0 +1,62 @@ +import unittest +import requests + +class UtilsTests(unittest.TestCase): + def setUp(self): + self.url_options = "http://localhost:7860/sdapi/v1/options" + self.url_cmd_flags = "http://localhost:7860/sdapi/v1/cmd-flags" + self.url_samplers = "http://localhost:7860/sdapi/v1/samplers" + self.url_upscalers = "http://localhost:7860/sdapi/v1/upscalers" + self.url_sd_models = "http://localhost:7860/sdapi/v1/sd-models" + self.url_hypernetworks = "http://localhost:7860/sdapi/v1/hypernetworks" + self.url_face_restorers = "http://localhost:7860/sdapi/v1/face-restorers" + self.url_realesrgan_models = "http://localhost:7860/sdapi/v1/realesrgan-models" + self.url_prompt_styles = "http://localhost:7860/sdapi/v1/prompt-styles" + self.url_embeddings = "http://localhost:7860/sdapi/v1/embeddings" + + def test_options_get(self): + self.assertEqual(requests.get(self.url_options).status_code, 200) + + def test_options_write(self): + response = requests.get(self.url_options) + self.assertEqual(response.status_code, 200) + + pre_value = response.json()["send_seed"] + + self.assertEqual(requests.post(self.url_options, json={"send_seed":not pre_value}).status_code, 200) + + response = requests.get(self.url_options) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()["send_seed"], not pre_value) + + requests.post(self.url_options, json={"send_seed": pre_value}) + + def test_cmd_flags(self): + self.assertEqual(requests.get(self.url_cmd_flags).status_code, 200) + + def test_samplers(self): + self.assertEqual(requests.get(self.url_samplers).status_code, 200) + + def test_upscalers(self): + self.assertEqual(requests.get(self.url_upscalers).status_code, 200) + + def test_sd_models(self): + self.assertEqual(requests.get(self.url_sd_models).status_code, 200) + + def test_hypernetworks(self): + self.assertEqual(requests.get(self.url_hypernetworks).status_code, 200) + + def test_face_restorers(self): + self.assertEqual(requests.get(self.url_face_restorers).status_code, 200) + + def test_realesrgan_models(self): + self.assertEqual(requests.get(self.url_realesrgan_models).status_code, 200) + + def test_prompt_styles(self): + self.assertEqual(requests.get(self.url_prompt_styles).status_code, 200) + + def test_embeddings(self): + self.assertEqual(requests.get(self.url_embeddings).status_code, 200) + +if __name__ == "__main__": + unittest.main() diff --git a/sd/stable-diffusion-webui/test/server_poll.py b/sd/stable-diffusion-webui/test/server_poll.py new file mode 100644 index 0000000000000000000000000000000000000000..42d56a4caacfc40d686dc99668d72238392448cd --- /dev/null +++ b/sd/stable-diffusion-webui/test/server_poll.py @@ -0,0 +1,24 @@ +import unittest +import requests +import time + + +def run_tests(proc, test_dir): + timeout_threshold = 240 + start_time = time.time() + while time.time()-start_time < timeout_threshold: + try: + requests.head("http://localhost:7860/") + break + except requests.exceptions.ConnectionError: + if proc.poll() is not None: + break + if proc.poll() is None: + if test_dir is None: + test_dir = "test" + suite = unittest.TestLoader().discover(test_dir, pattern="*_test.py", top_level_dir="test") + result = unittest.TextTestRunner(verbosity=2).run(suite) + return len(result.failures) + len(result.errors) + else: + print("Launch unsuccessful") + return 1 diff --git a/sd/stable-diffusion-webui/test/test_files/empty.pt b/sd/stable-diffusion-webui/test/test_files/empty.pt new file mode 100644 index 0000000000000000000000000000000000000000..02d735ce54e25e46a75d9122e1d9f2ad5d396fb0 --- /dev/null +++ b/sd/stable-diffusion-webui/test/test_files/empty.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99153ed9b545f0f5af916841c8510b36c9a0a84e88f412678bced8aba994b482 +size 128 diff --git a/sd/stable-diffusion-webui/test/test_files/img2img_basic.png b/sd/stable-diffusion-webui/test/test_files/img2img_basic.png new file mode 100644 index 0000000000000000000000000000000000000000..49a420482d0a70b9f5986d776a66cb3ea39d1a97 Binary files /dev/null and b/sd/stable-diffusion-webui/test/test_files/img2img_basic.png differ diff --git a/sd/stable-diffusion-webui/test/test_files/mask_basic.png b/sd/stable-diffusion-webui/test/test_files/mask_basic.png new file mode 100644 index 0000000000000000000000000000000000000000..0c2e9a6899e5c0381ce7c7364b31d684464ab423 Binary files /dev/null and b/sd/stable-diffusion-webui/test/test_files/mask_basic.png differ diff --git a/sd/stable-diffusion-webui/textual_inversion_templates/hypernetwork.txt b/sd/stable-diffusion-webui/textual_inversion_templates/hypernetwork.txt new file mode 100644 index 0000000000000000000000000000000000000000..91e06890571c7e4974d5a76c30fab62e8587c7d2 --- /dev/null +++ b/sd/stable-diffusion-webui/textual_inversion_templates/hypernetwork.txt @@ -0,0 +1,27 @@ +a photo of a [filewords] +a rendering of a [filewords] +a cropped photo of the [filewords] +the photo of a [filewords] +a photo of a clean [filewords] +a photo of a dirty [filewords] +a dark photo of the [filewords] +a photo of my [filewords] +a photo of the cool [filewords] +a close-up photo of a [filewords] +a bright photo of the [filewords] +a cropped photo of a [filewords] +a photo of the [filewords] +a good photo of the [filewords] +a photo of one [filewords] +a close-up photo of the [filewords] +a rendition of the [filewords] +a photo of the clean [filewords] +a rendition of a [filewords] +a photo of a nice [filewords] +a good photo of a [filewords] +a photo of the nice [filewords] +a photo of the small [filewords] +a photo of the weird [filewords] +a photo of the large [filewords] +a photo of a cool [filewords] +a photo of a small [filewords] diff --git a/sd/stable-diffusion-webui/textual_inversion_templates/none.txt b/sd/stable-diffusion-webui/textual_inversion_templates/none.txt new file mode 100644 index 0000000000000000000000000000000000000000..f77af4612b289a56b718c3bee62c66a6151f75be --- /dev/null +++ b/sd/stable-diffusion-webui/textual_inversion_templates/none.txt @@ -0,0 +1 @@ +picture diff --git a/sd/stable-diffusion-webui/textual_inversion_templates/style.txt b/sd/stable-diffusion-webui/textual_inversion_templates/style.txt new file mode 100644 index 0000000000000000000000000000000000000000..15af2d6b85f259d0bf41fbe0c8ca7a3340e1b259 --- /dev/null +++ b/sd/stable-diffusion-webui/textual_inversion_templates/style.txt @@ -0,0 +1,19 @@ +a painting, art by [name] +a rendering, art by [name] +a cropped painting, art by [name] +the painting, art by [name] +a clean painting, art by [name] +a dirty painting, art by [name] +a dark painting, art by [name] +a picture, art by [name] +a cool painting, art by [name] +a close-up painting, art by [name] +a bright painting, art by [name] +a cropped painting, art by [name] +a good painting, art by [name] +a close-up painting, art by [name] +a rendition, art by [name] +a nice painting, art by [name] +a small painting, art by [name] +a weird painting, art by [name] +a large painting, art by [name] diff --git a/sd/stable-diffusion-webui/textual_inversion_templates/style_filewords.txt b/sd/stable-diffusion-webui/textual_inversion_templates/style_filewords.txt new file mode 100644 index 0000000000000000000000000000000000000000..b3a8159a869a7890bdd42470664fadf015e0658d --- /dev/null +++ b/sd/stable-diffusion-webui/textual_inversion_templates/style_filewords.txt @@ -0,0 +1,19 @@ +a painting of [filewords], art by [name] +a rendering of [filewords], art by [name] +a cropped painting of [filewords], art by [name] +the painting of [filewords], art by [name] +a clean painting of [filewords], art by [name] +a dirty painting of [filewords], art by [name] +a dark painting of [filewords], art by [name] +a picture of [filewords], art by [name] +a cool painting of [filewords], art by [name] +a close-up painting of [filewords], art by [name] +a bright painting of [filewords], art by [name] +a cropped painting of [filewords], art by [name] +a good painting of [filewords], art by [name] +a close-up painting of [filewords], art by [name] +a rendition of [filewords], art by [name] +a nice painting of [filewords], art by [name] +a small painting of [filewords], art by [name] +a weird painting of [filewords], art by [name] +a large painting of [filewords], art by [name] diff --git a/sd/stable-diffusion-webui/textual_inversion_templates/subject.txt b/sd/stable-diffusion-webui/textual_inversion_templates/subject.txt new file mode 100644 index 0000000000000000000000000000000000000000..79f36aa0543fc2151b7f7e28725309c0c9a4912a --- /dev/null +++ b/sd/stable-diffusion-webui/textual_inversion_templates/subject.txt @@ -0,0 +1,27 @@ +a photo of a [name] +a rendering of a [name] +a cropped photo of the [name] +the photo of a [name] +a photo of a clean [name] +a photo of a dirty [name] +a dark photo of the [name] +a photo of my [name] +a photo of the cool [name] +a close-up photo of a [name] +a bright photo of the [name] +a cropped photo of a [name] +a photo of the [name] +a good photo of the [name] +a photo of one [name] +a close-up photo of the [name] +a rendition of the [name] +a photo of the clean [name] +a rendition of a [name] +a photo of a nice [name] +a good photo of a [name] +a photo of the nice [name] +a photo of the small [name] +a photo of the weird [name] +a photo of the large [name] +a photo of a cool [name] +a photo of a small [name] diff --git a/sd/stable-diffusion-webui/textual_inversion_templates/subject_filewords.txt b/sd/stable-diffusion-webui/textual_inversion_templates/subject_filewords.txt new file mode 100644 index 0000000000000000000000000000000000000000..008652a6bf4277f12a1759f5f3c815ae754dcfcf --- /dev/null +++ b/sd/stable-diffusion-webui/textual_inversion_templates/subject_filewords.txt @@ -0,0 +1,27 @@ +a photo of a [name], [filewords] +a rendering of a [name], [filewords] +a cropped photo of the [name], [filewords] +the photo of a [name], [filewords] +a photo of a clean [name], [filewords] +a photo of a dirty [name], [filewords] +a dark photo of the [name], [filewords] +a photo of my [name], [filewords] +a photo of the cool [name], [filewords] +a close-up photo of a [name], [filewords] +a bright photo of the [name], [filewords] +a cropped photo of a [name], [filewords] +a photo of the [name], [filewords] +a good photo of the [name], [filewords] +a photo of one [name], [filewords] +a close-up photo of the [name], [filewords] +a rendition of the [name], [filewords] +a photo of the clean [name], [filewords] +a rendition of a [name], [filewords] +a photo of a nice [name], [filewords] +a good photo of a [name], [filewords] +a photo of the nice [name], [filewords] +a photo of the small [name], [filewords] +a photo of the weird [name], [filewords] +a photo of the large [name], [filewords] +a photo of a cool [name], [filewords] +a photo of a small [name], [filewords] diff --git a/sd/stable-diffusion-webui/webui-user.sh b/sd/stable-diffusion-webui/webui-user.sh new file mode 100644 index 0000000000000000000000000000000000000000..bfa53cb7c67083ec0a01bfa420269af4d85c6c94 --- /dev/null +++ b/sd/stable-diffusion-webui/webui-user.sh @@ -0,0 +1,46 @@ +#!/bin/bash +######################################################### +# Uncomment and change the variables below to your need:# +######################################################### + +# Install directory without trailing slash +#install_dir="/home/$(whoami)" + +# Name of the subdirectory +#clone_dir="stable-diffusion-webui" + +# Commandline arguments for webui.py, for example: export COMMANDLINE_ARGS="--medvram --opt-split-attention" +#export COMMANDLINE_ARGS="" + +# python3 executable +#python_cmd="python3" + +# git executable +#export GIT="git" + +# python3 venv without trailing slash (defaults to ${install_dir}/${clone_dir}/venv) +#venv_dir="venv" + +# script to launch to start the app +#export LAUNCH_SCRIPT="launch.py" + +# install command for torch +#export TORCH_COMMAND="pip install torch==1.12.1+cu113 --extra-index-url https://download.pytorch.org/whl/cu113" + +# Requirements file to use for stable-diffusion-webui +#export REQS_FILE="requirements_versions.txt" + +# Fixed git repos +#export K_DIFFUSION_PACKAGE="" +#export GFPGAN_PACKAGE="" + +# Fixed git commits +#export STABLE_DIFFUSION_COMMIT_HASH="" +#export TAMING_TRANSFORMERS_COMMIT_HASH="" +#export CODEFORMER_COMMIT_HASH="" +#export BLIP_COMMIT_HASH="" + +# Uncomment to enable accelerated launch +#export ACCELERATE="True" + +########################################### diff --git a/sd/stable-diffusion-webui/webui.py b/sd/stable-diffusion-webui/webui.py new file mode 100644 index 0000000000000000000000000000000000000000..4c053a3aceeb84d64f63880b87bf1b6afcbaa937 --- /dev/null +++ b/sd/stable-diffusion-webui/webui.py @@ -0,0 +1,286 @@ +import os +import sys +import time +import importlib +import signal +import re +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from fastapi.middleware.gzip import GZipMiddleware +from packaging import version + +import logging +logging.getLogger("xformers").addFilter(lambda record: 'A matching Triton is not available' not in record.getMessage()) + +from modules import import_hook, errors, extra_networks, ui_extra_networks_checkpoints +from modules import extra_networks_hypernet, ui_extra_networks_hypernets, ui_extra_networks_textual_inversion +from modules.call_queue import wrap_queued_call, queue_lock, wrap_gradio_gpu_call + +import torch + +# Truncate version number of nightly/local build of PyTorch to not cause exceptions with CodeFormer or Safetensors +if ".dev" in torch.__version__ or "+git" in torch.__version__: + torch.__long_version__ = torch.__version__ + torch.__version__ = re.search(r'[\d.]+[\d]', torch.__version__).group(0) + +from modules import shared, devices, sd_samplers, upscaler, extensions, localization, ui_tempdir, ui_extra_networks +import modules.codeformer_model as codeformer +import modules.face_restoration +import modules.gfpgan_model as gfpgan +import modules.img2img + +import modules.lowvram +import modules.paths +import modules.scripts +import modules.sd_hijack +import modules.sd_models +import modules.sd_vae +import modules.txt2img +import modules.script_callbacks +import modules.textual_inversion.textual_inversion +import modules.progress + +import modules.ui +from modules import modelloader +from modules.shared import cmd_opts +import modules.hypernetworks.hypernetwork + + +if cmd_opts.server_name: + server_name = cmd_opts.server_name +else: + server_name = "0.0.0.0" if cmd_opts.listen else None + + +def check_versions(): + if shared.cmd_opts.skip_version_check: + return + + expected_torch_version = "1.13.1" + + if version.parse(torch.__version__) < version.parse(expected_torch_version): + errors.print_error_explanation(f""" +You are running torch {torch.__version__}. +The program is tested to work with torch {expected_torch_version}. +To reinstall the desired version, run with commandline flag --reinstall-torch. +Beware that this will cause a lot of large files to be downloaded, as well as +there are reports of issues with training tab on the latest version. + +Use --skip-version-check commandline argument to disable this check. + """.strip()) + + expected_xformers_version = "0.0.16rc425" + if shared.xformers_available: + import xformers + + if version.parse(xformers.__version__) < version.parse(expected_xformers_version): + errors.print_error_explanation(f""" +You are running xformers {xformers.__version__}. +The program is tested to work with xformers {expected_xformers_version}. +To reinstall the desired version, run with commandline flag --reinstall-xformers. + +Use --skip-version-check commandline argument to disable this check. + """.strip()) + + +def initialize(): + check_versions() + + extensions.list_extensions() + localization.list_localizations(cmd_opts.localizations_dir) + + if cmd_opts.ui_debug_mode: + shared.sd_upscalers = upscaler.UpscalerLanczos().scalers + modules.scripts.load_scripts() + return + + modelloader.cleanup_models() + modules.sd_models.setup_model() + codeformer.setup_model(cmd_opts.codeformer_models_path) + gfpgan.setup_model(cmd_opts.gfpgan_models_path) + + modelloader.list_builtin_upscalers() + modules.scripts.load_scripts() + modelloader.load_upscalers() + + modules.sd_vae.refresh_vae_list() + + modules.textual_inversion.textual_inversion.list_textual_inversion_templates() + + try: + modules.sd_models.load_model() + except Exception as e: + errors.display(e, "loading stable diffusion model") + print("", file=sys.stderr) + print("Stable diffusion model failed to load, exiting", file=sys.stderr) + exit(1) + + shared.opts.data["sd_model_checkpoint"] = shared.sd_model.sd_checkpoint_info.title + + shared.opts.onchange("sd_model_checkpoint", wrap_queued_call(lambda: modules.sd_models.reload_model_weights())) + shared.opts.onchange("sd_vae", wrap_queued_call(lambda: modules.sd_vae.reload_vae_weights()), call=False) + shared.opts.onchange("sd_vae_as_default", wrap_queued_call(lambda: modules.sd_vae.reload_vae_weights()), call=False) + shared.opts.onchange("temp_dir", ui_tempdir.on_tmpdir_changed) + + shared.reload_hypernetworks() + + ui_extra_networks.intialize() + ui_extra_networks.register_page(ui_extra_networks_textual_inversion.ExtraNetworksPageTextualInversion()) + ui_extra_networks.register_page(ui_extra_networks_hypernets.ExtraNetworksPageHypernetworks()) + ui_extra_networks.register_page(ui_extra_networks_checkpoints.ExtraNetworksPageCheckpoints()) + + extra_networks.initialize() + extra_networks.register_extra_network(extra_networks_hypernet.ExtraNetworkHypernet()) + + if cmd_opts.tls_keyfile is not None and cmd_opts.tls_keyfile is not None: + + try: + if not os.path.exists(cmd_opts.tls_keyfile): + print("Invalid path to TLS keyfile given") + if not os.path.exists(cmd_opts.tls_certfile): + print(f"Invalid path to TLS certfile: '{cmd_opts.tls_certfile}'") + except TypeError: + cmd_opts.tls_keyfile = cmd_opts.tls_certfile = None + print("TLS setup invalid, running webui without TLS") + else: + print("Running with TLS") + + # make the program just exit at ctrl+c without waiting for anything + def sigint_handler(sig, frame): + print(f'Interrupted with signal {sig} in {frame}') + os._exit(0) + + signal.signal(signal.SIGINT, sigint_handler) + + +def setup_cors(app): + if cmd_opts.cors_allow_origins and cmd_opts.cors_allow_origins_regex: + app.add_middleware(CORSMiddleware, allow_origins=cmd_opts.cors_allow_origins.split(','), allow_origin_regex=cmd_opts.cors_allow_origins_regex, allow_methods=['*'], allow_credentials=True, allow_headers=['*']) + elif cmd_opts.cors_allow_origins: + app.add_middleware(CORSMiddleware, allow_origins=cmd_opts.cors_allow_origins.split(','), allow_methods=['*'], allow_credentials=True, allow_headers=['*']) + elif cmd_opts.cors_allow_origins_regex: + app.add_middleware(CORSMiddleware, allow_origin_regex=cmd_opts.cors_allow_origins_regex, allow_methods=['*'], allow_credentials=True, allow_headers=['*']) + + +def create_api(app): + from modules.api.api import Api + api = Api(app, queue_lock) + return api + + +def wait_on_server(demo=None): + while 1: + time.sleep(0.5) + if shared.state.need_restart: + shared.state.need_restart = False + time.sleep(0.5) + demo.close() + time.sleep(0.5) + break + + +def api_only(): + initialize() + + app = FastAPI() + setup_cors(app) + app.add_middleware(GZipMiddleware, minimum_size=1000) + api = create_api(app) + + modules.script_callbacks.app_started_callback(None, app) + + api.launch(server_name="0.0.0.0" if cmd_opts.listen else "127.0.0.1", port=cmd_opts.port if cmd_opts.port else 7861) + + +def webui(): + launch_api = cmd_opts.api + initialize() + + while 1: + if shared.opts.clean_temp_dir_at_start: + ui_tempdir.cleanup_tmpdr() + + modules.script_callbacks.before_ui_callback() + + shared.demo = modules.ui.create_ui() + + if cmd_opts.gradio_queue: + shared.demo.queue(64) + + gradio_auth_creds = [] + if cmd_opts.gradio_auth: + gradio_auth_creds += cmd_opts.gradio_auth.strip('"').replace('\n', '').split(',') + if cmd_opts.gradio_auth_path: + with open(cmd_opts.gradio_auth_path, 'r', encoding="utf8") as file: + for line in file.readlines(): + gradio_auth_creds += [x.strip() for x in line.split(',')] + + app, local_url, share_url = shared.demo.launch( + share=cmd_opts.share, + server_name=server_name, + server_port=cmd_opts.port, + ssl_keyfile=cmd_opts.tls_keyfile, + ssl_certfile=cmd_opts.tls_certfile, + debug=cmd_opts.gradio_debug, + auth=[tuple(cred.split(':')) for cred in gradio_auth_creds] if gradio_auth_creds else None, + inbrowser=cmd_opts.autolaunch, + prevent_thread_lock=True + ) + # after initial launch, disable --autolaunch for subsequent restarts + cmd_opts.autolaunch = False + + # gradio uses a very open CORS policy via app.user_middleware, which makes it possible for + # an attacker to trick the user into opening a malicious HTML page, which makes a request to the + # running web ui and do whatever the attacker wants, including installing an extension and + # running its code. We disable this here. Suggested by RyotaK. + app.user_middleware = [x for x in app.user_middleware if x.cls.__name__ != 'CORSMiddleware'] + + setup_cors(app) + + app.add_middleware(GZipMiddleware, minimum_size=1000) + + modules.progress.setup_progress_api(app) + + if launch_api: + create_api(app) + + ui_extra_networks.add_pages_to_demo(app) + + modules.script_callbacks.app_started_callback(shared.demo, app) + + wait_on_server(shared.demo) + print('Restarting UI...') + + sd_samplers.set_samplers() + + modules.script_callbacks.script_unloaded_callback() + extensions.list_extensions() + + localization.list_localizations(cmd_opts.localizations_dir) + + modelloader.forbid_loaded_nonbuiltin_upscalers() + modules.scripts.reload_scripts() + modules.script_callbacks.model_loaded_callback(shared.sd_model) + modelloader.load_upscalers() + + for module in [module for name, module in sys.modules.items() if name.startswith("modules.ui")]: + importlib.reload(module) + + modules.sd_models.list_models() + + shared.reload_hypernetworks() + + ui_extra_networks.intialize() + ui_extra_networks.register_page(ui_extra_networks_textual_inversion.ExtraNetworksPageTextualInversion()) + ui_extra_networks.register_page(ui_extra_networks_hypernets.ExtraNetworksPageHypernetworks()) + ui_extra_networks.register_page(ui_extra_networks_checkpoints.ExtraNetworksPageCheckpoints()) + + extra_networks.initialize() + extra_networks.register_extra_network(extra_networks_hypernet.ExtraNetworkHypernet()) + + +if __name__ == "__main__": + if cmd_opts.nowebui: + api_only() + else: + webui() diff --git a/sd/stable-diffusion-webui/webui.sh b/sd/stable-diffusion-webui/webui.sh new file mode 100644 index 0000000000000000000000000000000000000000..8cdad22d310fed20f229b09d7a3160aeb1731a85 --- /dev/null +++ b/sd/stable-diffusion-webui/webui.sh @@ -0,0 +1,186 @@ +#!/usr/bin/env bash +################################################# +# Please do not make any changes to this file, # +# change the variables in webui-user.sh instead # +################################################# + +# If run from macOS, load defaults from webui-macos-env.sh +if [[ "$OSTYPE" == "darwin"* ]]; then + if [[ -f webui-macos-env.sh ]] + then + source ./webui-macos-env.sh + fi +fi + +# Read variables from webui-user.sh +# shellcheck source=/dev/null +if [[ -f webui-user.sh ]] +then + source ./webui-user.sh +fi + +# Set defaults +# Install directory without trailing slash +if [[ -z "${install_dir}" ]] +then + install_dir="/home/$(whoami)" +fi + +# Name of the subdirectory (defaults to stable-diffusion-webui) +if [[ -z "${clone_dir}" ]] +then + clone_dir="stable-diffusion-webui" +fi + +# python3 executable +if [[ -z "${python_cmd}" ]] +then + python_cmd="python3" +fi + +# git executable +if [[ -z "${GIT}" ]] +then + export GIT="git" +fi + +# python3 venv without trailing slash (defaults to ${install_dir}/${clone_dir}/venv) +if [[ -z "${venv_dir}" ]] +then + venv_dir="venv" +fi + +if [[ -z "${LAUNCH_SCRIPT}" ]] +then + LAUNCH_SCRIPT="launch.py" +fi + +# this script cannot be run as root by default +can_run_as_root=0 + +# read any command line flags to the webui.sh script +while getopts "f" flag > /dev/null 2>&1 +do + case ${flag} in + f) can_run_as_root=1;; + *) break;; + esac +done + +# Disable sentry logging +export ERROR_REPORTING=FALSE + +# Do not reinstall existing pip packages on Debian/Ubuntu +export PIP_IGNORE_INSTALLED=0 + +# Pretty print +delimiter="################################################################" + +printf "\n%s\n" "${delimiter}" +printf "\e[1m\e[32mInstall script for stable-diffusion + Web UI\n" +printf "\e[1m\e[34mTested on Debian 11 (Bullseye)\e[0m" +printf "\n%s\n" "${delimiter}" + +# Do not run as root +if [[ $(id -u) -eq 0 && can_run_as_root -eq 0 ]] +then + printf "\n%s\n" "${delimiter}" + printf "\e[1m\e[31mERROR: This script must not be launched as root, aborting...\e[0m" + printf "\n%s\n" "${delimiter}" + exit 1 +else + printf "\n%s\n" "${delimiter}" + printf "Running on \e[1m\e[32m%s\e[0m user" "$(whoami)" + printf "\n%s\n" "${delimiter}" +fi + +if [[ -d .git ]] +then + printf "\n%s\n" "${delimiter}" + printf "Repo already cloned, using it as install directory" + printf "\n%s\n" "${delimiter}" + install_dir="${PWD}/../" + clone_dir="${PWD##*/}" +fi + +# Check prerequisites +gpu_info=$(lspci 2>/dev/null | grep VGA) +case "$gpu_info" in + *"Navi 1"*|*"Navi 2"*) export HSA_OVERRIDE_GFX_VERSION=10.3.0 + ;; + *"Renoir"*) export HSA_OVERRIDE_GFX_VERSION=9.0.0 + printf "\n%s\n" "${delimiter}" + printf "Experimental support for Renoir: make sure to have at least 4GB of VRAM and 10GB of RAM or enable cpu mode: --use-cpu all --no-half" + printf "\n%s\n" "${delimiter}" + ;; + *) + ;; +esac +if echo "$gpu_info" | grep -q "AMD" && [[ -z "${TORCH_COMMAND}" ]] +then + export TORCH_COMMAND="pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/rocm5.2" +fi + +for preq in "${GIT}" "${python_cmd}" +do + if ! hash "${preq}" &>/dev/null + then + printf "\n%s\n" "${delimiter}" + printf "\e[1m\e[31mERROR: %s is not installed, aborting...\e[0m" "${preq}" + printf "\n%s\n" "${delimiter}" + exit 1 + fi +done + +if ! "${python_cmd}" -c "import venv" &>/dev/null +then + printf "\n%s\n" "${delimiter}" + printf "\e[1m\e[31mERROR: python3-venv is not installed, aborting...\e[0m" + printf "\n%s\n" "${delimiter}" + exit 1 +fi + +cd "${install_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/, aborting...\e[0m" "${install_dir}"; exit 1; } +if [[ -d "${clone_dir}" ]] +then + cd "${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; } +else + printf "\n%s\n" "${delimiter}" + printf "Clone stable-diffusion-webui" + printf "\n%s\n" "${delimiter}" + "${GIT}" clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git "${clone_dir}" + cd "${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; } +fi + +printf "\n%s\n" "${delimiter}" +printf "Create and activate python venv" +printf "\n%s\n" "${delimiter}" +cd "${install_dir}"/"${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; } +if [[ ! -d "${venv_dir}" ]] +then + "${python_cmd}" -m venv "${venv_dir}" + first_launch=1 +fi +# shellcheck source=/dev/null +if [[ -f "${venv_dir}"/bin/activate ]] +then + source "${venv_dir}"/bin/activate +else + printf "\n%s\n" "${delimiter}" + printf "\e[1m\e[31mERROR: Cannot activate python venv, aborting...\e[0m" + printf "\n%s\n" "${delimiter}" + exit 1 +fi + +if [[ ! -z "${ACCELERATE}" ]] && [ ${ACCELERATE}="True" ] && [ -x "$(command -v accelerate)" ] +then + printf "\n%s\n" "${delimiter}" + printf "Accelerating launch.py..." + printf "\n%s\n" "${delimiter}" + exec accelerate launch --num_cpu_threads_per_process=6 "${LAUNCH_SCRIPT}" "$@" +else + printf "\n%s\n" "${delimiter}" + printf "Launching launch.py..." + printf "\n%s\n" "${delimiter}" + exec "${python_cmd}" "${LAUNCH_SCRIPT}" "$@" +fi diff --git a/sd/stablediffusion/.gitignore b/sd/stablediffusion/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3b33f062894825a067995b8b9e8224ba4d9ff708 --- /dev/null +++ b/sd/stablediffusion/.gitignore @@ -0,0 +1,165 @@ +# Generated by project +outputs/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# General MacOS +.DS_Store +.AppleDouble +.LSOverride + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# IDEs +.idea/ +.vscode/ diff --git a/sd/stablediffusion/LICENSE b/sd/stablediffusion/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..58a49c99b2b9151af5e1fee0dbd20307671f47ab --- /dev/null +++ b/sd/stablediffusion/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Stability AI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sd/stablediffusion/LICENSE-MODEL b/sd/stablediffusion/LICENSE-MODEL new file mode 100644 index 0000000000000000000000000000000000000000..9684533d88e7d853a55cabf6caa2f1e4a3e6fdc4 --- /dev/null +++ b/sd/stablediffusion/LICENSE-MODEL @@ -0,0 +1,84 @@ +Copyright (c) 2022 Stability AI and contributors + +CreativeML Open RAIL++-M License +dated November 24, 2022 + +Section I: PREAMBLE + +Multimodal generative models are being widely adopted and used, and have the potential to transform the way artists, among other individuals, conceive and benefit from AI or ML technologies as a tool for content creation. + +Notwithstanding the current and potential benefits that these artifacts can bring to society at large, there are also concerns about potential misuses of them, either due to their technical limitations or ethical considerations. + +In short, this license strives for both the open and responsible downstream use of the accompanying model. When it comes to the open character, we took inspiration from open source permissive licenses regarding the grant of IP rights. Referring to the downstream responsible use, we added use-based restrictions not permitting the use of the Model in very specific scenarios, in order for the licensor to be able to enforce the license in case potential misuses of the Model may occur. At the same time, we strive to promote open and responsible research on generative models for art and content generation. + +Even though downstream derivative versions of the model could be released under different licensing terms, the latter will always have to include - at minimum - the same use-based restrictions as the ones in the original license (this license). We believe in the intersection between open and responsible AI development; thus, this License aims to strike a balance between both in order to enable responsible open-science in the field of AI. + +This License governs the use of the model (and its derivatives) and is informed by the model card associated with the model. + +NOW THEREFORE, You and Licensor agree as follows: + +1. Definitions + +- "License" means the terms and conditions for use, reproduction, and Distribution as defined in this document. +- "Data" means a collection of information and/or content extracted from the dataset used with the Model, including to train, pretrain, or otherwise evaluate the Model. The Data is not licensed under this License. +- "Output" means the results of operating a Model as embodied in informational content resulting therefrom. +- "Model" means any accompanying machine-learning based assemblies (including checkpoints), consisting of learnt weights, parameters (including optimizer states), corresponding to the model architecture as embodied in the Complementary Material, that have been trained or tuned, in whole or in part on the Data, using the Complementary Material. +- "Derivatives of the Model" means all modifications to the Model, works based on the Model, or any other model which is created or initialized by transfer of patterns of the weights, parameters, activations or output of the Model, to the other model, in order to cause the other model to perform similarly to the Model, including - but not limited to - distillation methods entailing the use of intermediate data representations or methods based on the generation of synthetic data by the Model for training the other model. +- "Complementary Material" means the accompanying source code and scripts used to define, run, load, benchmark or evaluate the Model, and used to prepare data for training or evaluation, if any. This includes any accompanying documentation, tutorials, examples, etc, if any. +- "Distribution" means any transmission, reproduction, publication or other sharing of the Model or Derivatives of the Model to a third party, including providing the Model as a hosted service made available by electronic or other remote means - e.g. API-based or web access. +- "Licensor" means the copyright owner or entity authorized by the copyright owner that is granting the License, including the persons or entities that may have rights in the Model and/or distributing the Model. +- "You" (or "Your") means an individual or Legal Entity exercising permissions granted by this License and/or making use of the Model for whichever purpose and in any field of use, including usage of the Model in an end-use application - e.g. chatbot, translator, image generator. +- "Third Parties" means individuals or legal entities that are not under common control with Licensor or You. +- "Contribution" means any work of authorship, including the original version of the Model and any modifications or additions to that Model or Derivatives of the Model thereof, that is intentionally submitted to Licensor for inclusion in the Model by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Model, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." +- "Contributor" means Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Model. + +Section II: INTELLECTUAL PROPERTY RIGHTS + +Both copyright and patent grants apply to the Model, Derivatives of the Model and Complementary Material. The Model and Derivatives of the Model are subject to additional terms as described in Section III. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare, publicly display, publicly perform, sublicense, and distribute the Complementary Material, the Model, and Derivatives of the Model. +3. Grant of Patent License. Subject to the terms and conditions of this License and where and as applicable, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this paragraph) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Model and the Complementary Material, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Model to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Model and/or Complementary Material or a Contribution incorporated within the Model and/or Complementary Material constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for the Model and/or Work shall terminate as of the date such litigation is asserted or filed. + +Section III: CONDITIONS OF USAGE, DISTRIBUTION AND REDISTRIBUTION + +4. Distribution and Redistribution. You may host for Third Party remote access purposes (e.g. software-as-a-service), reproduce and distribute copies of the Model or Derivatives of the Model thereof in any medium, with or without modifications, provided that You meet the following conditions: +Use-based restrictions as referenced in paragraph 5 MUST be included as an enforceable provision by You in any type of legal agreement (e.g. a license) governing the use and/or distribution of the Model or Derivatives of the Model, and You shall give notice to subsequent users You Distribute to, that the Model or Derivatives of the Model are subject to paragraph 5. This provision does not apply to the use of Complementary Material. +You must give any Third Party recipients of the Model or Derivatives of the Model a copy of this License; +You must cause any modified files to carry prominent notices stating that You changed the files; +You must retain all copyright, patent, trademark, and attribution notices excluding those notices that do not pertain to any part of the Model, Derivatives of the Model. +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions - respecting paragraph 4.a. - for use, reproduction, or Distribution of Your modifications, or for any such Derivatives of the Model as a whole, provided Your use, reproduction, and Distribution of the Model otherwise complies with the conditions stated in this License. +5. Use-based restrictions. The restrictions set forth in Attachment A are considered Use-based restrictions. Therefore You cannot use the Model and the Derivatives of the Model for the specified restricted uses. You may use the Model subject to this License, including only for lawful purposes and in accordance with the License. Use may include creating any content with, finetuning, updating, running, training, evaluating and/or reparametrizing the Model. You shall require all of Your users who use the Model or a Derivative of the Model to comply with the terms of this paragraph (paragraph 5). +6. The Output You Generate. Except as set forth herein, Licensor claims no rights in the Output You generate using the Model. You are accountable for the Output you generate and its subsequent uses. No use of the output can contravene any provision as stated in the License. + +Section IV: OTHER PROVISIONS + +7. Updates and Runtime Restrictions. To the maximum extent permitted by law, Licensor reserves the right to restrict (remotely or otherwise) usage of the Model in violation of this License. +8. Trademarks and related. Nothing in this License permits You to make use of Licensors’ trademarks, trade names, logos or to otherwise suggest endorsement or misrepresent the relationship between the parties; and any rights not expressly granted herein are reserved by the Licensors. +9. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Model and the Complementary Material (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Model, Derivatives of the Model, and the Complementary Material and assume any risks associated with Your exercise of permissions under this License. +10. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Model and the Complementary Material (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +11. Accepting Warranty or Additional Liability. While redistributing the Model, Derivatives of the Model and the Complementary Material thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. +12. If any provision of this License is held to be invalid, illegal or unenforceable, the remaining provisions shall be unaffected thereby and remain valid as if such provision had not been set forth herein. + +END OF TERMS AND CONDITIONS + + + + +Attachment A + +Use Restrictions + +You agree not to use the Model or Derivatives of the Model: + +- In any way that violates any applicable national, federal, state, local or international law or regulation; +- For the purpose of exploiting, harming or attempting to exploit or harm minors in any way; +- To generate or disseminate verifiably false information and/or content with the purpose of harming others; +- To generate or disseminate personal identifiable information that can be used to harm an individual; +- To defame, disparage or otherwise harass others; +- For fully automated decision making that adversely impacts an individual’s legal rights or otherwise creates or modifies a binding, enforceable obligation; +- For any use intended to or which has the effect of discriminating against or harming individuals or groups based on online or offline social behavior or known or predicted personal or personality characteristics; +- To exploit any of the vulnerabilities of a specific group of persons based on their age, social, physical or mental characteristics, in order to materially distort the behavior of a person pertaining to that group in a manner that causes or is likely to cause that person or another person physical or psychological harm; +- For any use intended to or which has the effect of discriminating against individuals or groups based on legally protected characteristics or categories; +- To provide medical advice and medical results interpretation; +- To generate or disseminate information for the purpose to be used for administration of justice, law enforcement, immigration or asylum processes, such as predicting an individual will commit fraud/crime commitment (e.g. by text profiling, drawing causal relationships between assertions made in documents, indiscriminate and arbitrarily-targeted use). + diff --git a/sd/stablediffusion/README.md b/sd/stablediffusion/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f41306887b2b06c62b8ae5cff0212a78c0f6e0f5 --- /dev/null +++ b/sd/stablediffusion/README.md @@ -0,0 +1,257 @@ +# Stable Diffusion Version 2 +![t2i](assets/stable-samples/txt2img/768/merged-0006.png) +![t2i](assets/stable-samples/txt2img/768/merged-0002.png) +![t2i](assets/stable-samples/txt2img/768/merged-0005.png) + +This repository contains [Stable Diffusion](https://github.com/CompVis/stable-diffusion) models trained from scratch and will be continuously updated with +new checkpoints. The following list provides an overview of all currently available models. More coming soon. + +## News + +**December 7, 2022** + +*Version 2.1* + +- New stable diffusion model (_Stable Diffusion 2.1-v_, [HuggingFace](https://huggingface.co/stabilityai/stable-diffusion-2-1)) at 768x768 resolution and (_Stable Diffusion 2.1-base_, [HuggingFace](https://huggingface.co/stabilityai/stable-diffusion-2-1-base)) at 512x512 resolution, both based on the same number of parameters and architecture as 2.0 and fine-tuned on 2.0, on a less restrictive NSFW filtering of the [LAION-5B](https://laion.ai/blog/laion-5b/) dataset. +Per default, the attention operation of the model is evaluated at full precision when `xformers` is not installed. To enable fp16 (which can cause numerical instabilities with the vanilla attention module on the v2.1 model) , run your script with `ATTN_PRECISION=fp16 python ` + +**November 24, 2022** + +*Version 2.0* + +- New stable diffusion model (_Stable Diffusion 2.0-v_) at 768x768 resolution. Same number of parameters in the U-Net as 1.5, but uses [OpenCLIP-ViT/H](https://github.com/mlfoundations/open_clip) as the text encoder and is trained from scratch. _SD 2.0-v_ is a so-called [v-prediction](https://arxiv.org/abs/2202.00512) model. +- The above model is finetuned from _SD 2.0-base_, which was trained as a standard noise-prediction model on 512x512 images and is also made available. +- Added a [x4 upscaling latent text-guided diffusion model](#image-upscaling-with-stable-diffusion). +- New [depth-guided stable diffusion model](#depth-conditional-stable-diffusion), finetuned from _SD 2.0-base_. The model is conditioned on monocular depth estimates inferred via [MiDaS](https://github.com/isl-org/MiDaS) and can be used for structure-preserving img2img and shape-conditional synthesis. + + ![d2i](assets/stable-samples/depth2img/depth2img01.png) +- A [text-guided inpainting model](#image-inpainting-with-stable-diffusion), finetuned from SD _2.0-base_. + +We follow the [original repository](https://github.com/CompVis/stable-diffusion) and provide basic inference scripts to sample from the models. + +________________ +*The original Stable Diffusion model was created in a collaboration with [CompVis](https://arxiv.org/abs/2202.00512) and [RunwayML](https://runwayml.com/) and builds upon the work:* + +[**High-Resolution Image Synthesis with Latent Diffusion Models**](https://ommer-lab.com/research/latent-diffusion-models/)
    +[Robin Rombach](https://github.com/rromb)\*, +[Andreas Blattmann](https://github.com/ablattmann)\*, +[Dominik Lorenz](https://github.com/qp-qp)\, +[Patrick Esser](https://github.com/pesser), +[Björn Ommer](https://hci.iwr.uni-heidelberg.de/Staff/bommer)
    +_[CVPR '22 Oral](https://openaccess.thecvf.com/content/CVPR2022/html/Rombach_High-Resolution_Image_Synthesis_With_Latent_Diffusion_Models_CVPR_2022_paper.html) | +[GitHub](https://github.com/CompVis/latent-diffusion) | [arXiv](https://arxiv.org/abs/2112.10752) | [Project page](https://ommer-lab.com/research/latent-diffusion-models/)_ + +and [many others](#shout-outs). + +Stable Diffusion is a latent text-to-image diffusion model. +________________________________ + +## Requirements + +You can update an existing [latent diffusion](https://github.com/CompVis/latent-diffusion) environment by running + +``` +conda install pytorch==1.12.1 torchvision==0.13.1 -c pytorch +pip install transformers==4.19.2 diffusers invisible-watermark +pip install -e . +``` +#### xformers efficient attention +For more efficiency and speed on GPUs, +we highly recommended installing the [xformers](https://github.com/facebookresearch/xformers) +library. + +Tested on A100 with CUDA 11.4. +Installation needs a somewhat recent version of nvcc and gcc/g++, obtain those, e.g., via +```commandline +export CUDA_HOME=/usr/local/cuda-11.4 +conda install -c nvidia/label/cuda-11.4.0 cuda-nvcc +conda install -c conda-forge gcc +conda install -c conda-forge gxx_linux-64==9.5.0 +``` + +Then, run the following (compiling takes up to 30 min). + +```commandline +cd .. +git clone https://github.com/facebookresearch/xformers.git +cd xformers +git submodule update --init --recursive +pip install -r requirements.txt +pip install -e . +cd ../stablediffusion +``` +Upon successful installation, the code will automatically default to [memory efficient attention](https://github.com/facebookresearch/xformers) +for the self- and cross-attention layers in the U-Net and autoencoder. + +## General Disclaimer +Stable Diffusion models are general text-to-image diffusion models and therefore mirror biases and (mis-)conceptions that are present +in their training data. Although efforts were made to reduce the inclusion of explicit pornographic material, **we do not recommend using the provided weights for services or products without additional safety mechanisms and considerations. +The weights are research artifacts and should be treated as such.** +Details on the training procedure and data, as well as the intended use of the model can be found in the corresponding [model card](https://huggingface.co/stabilityai/stable-diffusion-2). +The weights are available via [the StabilityAI organization at Hugging Face](https://huggingface.co/StabilityAI) under the [CreativeML Open RAIL++-M License](LICENSE-MODEL). + + + +## Stable Diffusion v2 + +Stable Diffusion v2 refers to a specific configuration of the model +architecture that uses a downsampling-factor 8 autoencoder with an 865M UNet +and OpenCLIP ViT-H/14 text encoder for the diffusion model. The _SD 2-v_ model produces 768x768 px outputs. + +Evaluations with different classifier-free guidance scales (1.5, 2.0, 3.0, 4.0, +5.0, 6.0, 7.0, 8.0) and 50 DDIM sampling steps show the relative improvements of the checkpoints: + +![sd evaluation results](assets/model-variants.jpg) + + + +### Text-to-Image +![txt2img-stable2](assets/stable-samples/txt2img/merged-0003.png) +![txt2img-stable2](assets/stable-samples/txt2img/merged-0001.png) + +Stable Diffusion 2 is a latent diffusion model conditioned on the penultimate text embeddings of a CLIP ViT-H/14 text encoder. +We provide a [reference script for sampling](#reference-sampling-script). +#### Reference Sampling Script + +This script incorporates an [invisible watermarking](https://github.com/ShieldMnt/invisible-watermark) of the outputs, to help viewers [identify the images as machine-generated](scripts/tests/test_watermark.py). +We provide the configs for the _SD2-v_ (768px) and _SD2-base_ (512px) model. + +First, download the weights for [_SD2.1-v_](https://huggingface.co/stabilityai/stable-diffusion-2-1) and [_SD2.1-base_](https://huggingface.co/stabilityai/stable-diffusion-2-1-base). + +To sample from the _SD2.1-v_ model, run the following: + +``` +python scripts/txt2img.py --prompt "a professional photograph of an astronaut riding a horse" --ckpt --config configs/stable-diffusion/v2-inference-v.yaml --H 768 --W 768 +``` +or try out the Web Demo: [![Hugging Face Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)](https://huggingface.co/spaces/stabilityai/stable-diffusion). + +To sample from the base model, use +``` +python scripts/txt2img.py --prompt "a professional photograph of an astronaut riding a horse" --ckpt --config +``` + +By default, this uses the [DDIM sampler](https://arxiv.org/abs/2010.02502), and renders images of size 768x768 (which it was trained on) in 50 steps. +Empirically, the v-models can be sampled with higher guidance scales. + +Note: The inference config for all model versions is designed to be used with EMA-only checkpoints. +For this reason `use_ema=False` is set in the configuration, otherwise the code will try to switch from +non-EMA to EMA weights. + +### Image Modification with Stable Diffusion + +![depth2img-stable2](assets/stable-samples/depth2img/merged-0000.png) +#### Depth-Conditional Stable Diffusion + +To augment the well-established [img2img](https://github.com/CompVis/stable-diffusion#image-modification-with-stable-diffusion) functionality of Stable Diffusion, we provide a _shape-preserving_ stable diffusion model. + + +Note that the original method for image modification introduces significant semantic changes w.r.t. the initial image. +If that is not desired, download our [depth-conditional stable diffusion](https://huggingface.co/stabilityai/stable-diffusion-2-depth) model and the `dpt_hybrid` MiDaS [model weights](https://github.com/intel-isl/DPT/releases/download/1_0/dpt_hybrid-midas-501f0c75.pt), place the latter in a folder `midas_models` and sample via +``` +python scripts/gradio/depth2img.py configs/stable-diffusion/v2-midas-inference.yaml +``` + +or + +``` +streamlit run scripts/streamlit/depth2img.py configs/stable-diffusion/v2-midas-inference.yaml +``` + +This method can be used on the samples of the base model itself. +For example, take [this sample](assets/stable-samples/depth2img/old_man.png) generated by an anonymous discord user. +Using the [gradio](https://gradio.app) or [streamlit](https://streamlit.io/) script `depth2img.py`, the MiDaS model first infers a monocular depth estimate given this input, +and the diffusion model is then conditioned on the (relative) depth output. + +

    + depth2image
    + +

    + +This model is particularly useful for a photorealistic style; see the [examples](assets/stable-samples/depth2img). +For a maximum strength of 1.0, the model removes all pixel-based information and only relies on the text prompt and the inferred monocular depth estimate. + +![depth2img-stable3](assets/stable-samples/depth2img/merged-0005.png) + +#### Classic Img2Img + +For running the "classic" img2img, use +``` +python scripts/img2img.py --prompt "A fantasy landscape, trending on artstation" --init-img --strength 0.8 --ckpt +``` +and adapt the checkpoint and config paths accordingly. + +### Image Upscaling with Stable Diffusion +![upscaling-x4](assets/stable-samples/upscaling/merged-dog.png) +After [downloading the weights](https://huggingface.co/stabilityai/stable-diffusion-x4-upscaler), run +``` +python scripts/gradio/superresolution.py configs/stable-diffusion/x4-upscaling.yaml +``` + +or + +``` +streamlit run scripts/streamlit/superresolution.py -- configs/stable-diffusion/x4-upscaling.yaml +``` + +for a Gradio or Streamlit demo of the text-guided x4 superresolution model. +This model can be used both on real inputs and on synthesized examples. For the latter, we recommend setting a higher +`noise_level`, e.g. `noise_level=100`. + +### Image Inpainting with Stable Diffusion + +![inpainting-stable2](assets/stable-inpainting/merged-leopards.png) + +[Download the SD 2.0-inpainting checkpoint](https://huggingface.co/stabilityai/stable-diffusion-2-inpainting) and run + +``` +python scripts/gradio/inpainting.py configs/stable-diffusion/v2-inpainting-inference.yaml +``` + +or + +``` +streamlit run scripts/streamlit/inpainting.py -- configs/stable-diffusion/v2-inpainting-inference.yaml +``` + +for a Gradio or Streamlit demo of the inpainting model. +This scripts adds invisible watermarking to the demo in the [RunwayML](https://github.com/runwayml/stable-diffusion/blob/main/scripts/inpaint_st.py) repository, but both should work interchangeably with the checkpoints/configs. + + + +## Shout-Outs +- Thanks to [Hugging Face](https://huggingface.co/) and in particular [Apolinário](https://github.com/apolinario) for support with our model releases! +- Stable Diffusion would not be possible without [LAION](https://laion.ai/) and their efforts to create open, large-scale datasets. +- The [DeepFloyd team](https://twitter.com/deepfloydai) at Stability AI, for creating the subset of [LAION-5B](https://laion.ai/blog/laion-5b/) dataset used to train the model. +- Stable Diffusion 2.0 uses [OpenCLIP](https://laion.ai/blog/large-openclip/), trained by [Romain Beaumont](https://github.com/rom1504). +- Our codebase for the diffusion models builds heavily on [OpenAI's ADM codebase](https://github.com/openai/guided-diffusion) +and [https://github.com/lucidrains/denoising-diffusion-pytorch](https://github.com/lucidrains/denoising-diffusion-pytorch). +Thanks for open-sourcing! +- [CompVis](https://github.com/CompVis/stable-diffusion) initial stable diffusion release +- [Patrick](https://github.com/pesser)'s [implementation](https://github.com/runwayml/stable-diffusion/blob/main/scripts/inpaint_st.py) of the streamlit demo for inpainting. +- `img2img` is an application of [SDEdit](https://arxiv.org/abs/2108.01073) by [Chenlin Meng](https://cs.stanford.edu/~chenlin/) from the [Stanford AI Lab](https://cs.stanford.edu/~ermon/website/). +- [Kat's implementation]((https://github.com/CompVis/latent-diffusion/pull/51)) of the [PLMS](https://arxiv.org/abs/2202.09778) sampler, and [more](https://github.com/crowsonkb/k-diffusion). +- [DPMSolver](https://arxiv.org/abs/2206.00927) [integration](https://github.com/CompVis/stable-diffusion/pull/440) by [Cheng Lu](https://github.com/LuChengTHU). +- Facebook's [xformers](https://github.com/facebookresearch/xformers) for efficient attention computation. +- [MiDaS](https://github.com/isl-org/MiDaS) for monocular depth estimation. + + +## License + +The code in this repository is released under the MIT License. + +The weights are available via [the StabilityAI organization at Hugging Face](https://huggingface.co/StabilityAI), and released under the [CreativeML Open RAIL++-M License](LICENSE-MODEL) License. + +## BibTeX + +``` +@misc{rombach2021highresolution, + title={High-Resolution Image Synthesis with Latent Diffusion Models}, + author={Robin Rombach and Andreas Blattmann and Dominik Lorenz and Patrick Esser and Björn Ommer}, + year={2021}, + eprint={2112.10752}, + archivePrefix={arXiv}, + primaryClass={cs.CV} +} +``` + + diff --git a/sd/stablediffusion/assets/model-variants.jpg b/sd/stablediffusion/assets/model-variants.jpg new file mode 100644 index 0000000000000000000000000000000000000000..de5bb3fc8af5d60d0c0fac8c0c866e9f8d857ba3 Binary files /dev/null and b/sd/stablediffusion/assets/model-variants.jpg differ diff --git a/sd/stablediffusion/assets/modelfigure.png b/sd/stablediffusion/assets/modelfigure.png new file mode 100644 index 0000000000000000000000000000000000000000..6b1d3e6b9d59fd8d38468e7bce47c903a4e1c932 Binary files /dev/null and b/sd/stablediffusion/assets/modelfigure.png differ diff --git a/sd/stablediffusion/assets/rick.jpeg b/sd/stablediffusion/assets/rick.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..995486061ba50bd0ae2e213c72de87a27326632f Binary files /dev/null and b/sd/stablediffusion/assets/rick.jpeg differ diff --git a/sd/stablediffusion/assets/stable-inpainting/inpainting.gif b/sd/stablediffusion/assets/stable-inpainting/inpainting.gif new file mode 100644 index 0000000000000000000000000000000000000000..4f511347bc057f858db95015725488560ec1f676 Binary files /dev/null and b/sd/stablediffusion/assets/stable-inpainting/inpainting.gif differ diff --git a/sd/stablediffusion/assets/stable-inpainting/merged-leopards.png b/sd/stablediffusion/assets/stable-inpainting/merged-leopards.png new file mode 100644 index 0000000000000000000000000000000000000000..b877158fff2a15d4016135ce2307e19ece877b59 --- /dev/null +++ b/sd/stablediffusion/assets/stable-inpainting/merged-leopards.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94a05d717a340d7b240283e72e91984e82093750ba066aa05ab0759188467e69 +size 4958974 diff --git a/sd/stablediffusion/assets/stable-samples/depth2img/d2i.gif b/sd/stablediffusion/assets/stable-samples/depth2img/d2i.gif new file mode 100644 index 0000000000000000000000000000000000000000..265c1d1ae56feb60c5aabbf88526a6ed47fda383 --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/depth2img/d2i.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7236fc7f4349740c537ef9c8730590c15d198aaf42925a46755ded26bc436bc4 +size 1140400 diff --git a/sd/stablediffusion/assets/stable-samples/depth2img/depth2fantasy.jpeg b/sd/stablediffusion/assets/stable-samples/depth2img/depth2fantasy.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..8c0c94ee7354cc19e4c86134900397c1ed13eb01 Binary files /dev/null and b/sd/stablediffusion/assets/stable-samples/depth2img/depth2fantasy.jpeg differ diff --git a/sd/stablediffusion/assets/stable-samples/depth2img/depth2img01.png b/sd/stablediffusion/assets/stable-samples/depth2img/depth2img01.png new file mode 100644 index 0000000000000000000000000000000000000000..f062b45a32bf71ce830b4ab2401475d421417d03 --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/depth2img/depth2img01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60cb68c273602eae8e2fb769a2848e55844d812196260ada112a9aecc604f735 +size 3324111 diff --git a/sd/stablediffusion/assets/stable-samples/depth2img/depth2img02.png b/sd/stablediffusion/assets/stable-samples/depth2img/depth2img02.png new file mode 100644 index 0000000000000000000000000000000000000000..f91cad5b401955221d8875529578504762ca6bfc --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/depth2img/depth2img02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:adabff92594b17a2554408257d2ab61eb9b89270d5917eafd44a9b75740aab04 +size 1775470 diff --git a/sd/stablediffusion/assets/stable-samples/depth2img/merged-0000.png b/sd/stablediffusion/assets/stable-samples/depth2img/merged-0000.png new file mode 100644 index 0000000000000000000000000000000000000000..fce6c7a0b76bd34a2028a20efa5e5a497953cc7e --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/depth2img/merged-0000.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b19da6052f01a3b115ac3315ef5db1b7dcdb58091879c0dfe3895a7765a491ac +size 2129264 diff --git a/sd/stablediffusion/assets/stable-samples/depth2img/merged-0004.png b/sd/stablediffusion/assets/stable-samples/depth2img/merged-0004.png new file mode 100644 index 0000000000000000000000000000000000000000..41641d3e2d07575fb5fa0e87609770ab1bae8126 --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/depth2img/merged-0004.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d3ae25218f696375aa953e55d12d034da3bd7abce68616a3af916bdae01cc86 +size 1448945 diff --git a/sd/stablediffusion/assets/stable-samples/depth2img/merged-0005.png b/sd/stablediffusion/assets/stable-samples/depth2img/merged-0005.png new file mode 100644 index 0000000000000000000000000000000000000000..46634965fdf77e2134e2ac574fe02c12cd34d96e --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/depth2img/merged-0005.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:935252e202a3a6cafa476443f3a0ae3ac95cf85c37c0133f4f32af2aafb8f9ab +size 4546641 diff --git a/sd/stablediffusion/assets/stable-samples/depth2img/midas.jpeg b/sd/stablediffusion/assets/stable-samples/depth2img/midas.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..4890ab7c6ca378f27dcf1eef6012848798713c94 Binary files /dev/null and b/sd/stablediffusion/assets/stable-samples/depth2img/midas.jpeg differ diff --git a/sd/stablediffusion/assets/stable-samples/depth2img/old_man.png b/sd/stablediffusion/assets/stable-samples/depth2img/old_man.png new file mode 100644 index 0000000000000000000000000000000000000000..44bbc0051b5f12743b1c7ef92f23ed5758eed4e2 Binary files /dev/null and b/sd/stablediffusion/assets/stable-samples/depth2img/old_man.png differ diff --git a/sd/stablediffusion/assets/stable-samples/img2img/mountains-1.png b/sd/stablediffusion/assets/stable-samples/img2img/mountains-1.png new file mode 100644 index 0000000000000000000000000000000000000000..d01b8350743e3bd4fdf653d1563ee7d5c2153323 Binary files /dev/null and b/sd/stablediffusion/assets/stable-samples/img2img/mountains-1.png differ diff --git a/sd/stablediffusion/assets/stable-samples/img2img/mountains-2.png b/sd/stablediffusion/assets/stable-samples/img2img/mountains-2.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f4e708535f0e5b53372a3a39e6aa31dce383fd Binary files /dev/null and b/sd/stablediffusion/assets/stable-samples/img2img/mountains-2.png differ diff --git a/sd/stablediffusion/assets/stable-samples/img2img/mountains-3.png b/sd/stablediffusion/assets/stable-samples/img2img/mountains-3.png new file mode 100644 index 0000000000000000000000000000000000000000..017de3012c2f03e4f87cce21b4d3342713b9ae95 Binary files /dev/null and b/sd/stablediffusion/assets/stable-samples/img2img/mountains-3.png differ diff --git a/sd/stablediffusion/assets/stable-samples/img2img/sketch-mountains-input.jpg b/sd/stablediffusion/assets/stable-samples/img2img/sketch-mountains-input.jpg new file mode 100644 index 0000000000000000000000000000000000000000..79d652b8003bbcd1d0c0ba2d984dbbe299ac5916 Binary files /dev/null and b/sd/stablediffusion/assets/stable-samples/img2img/sketch-mountains-input.jpg differ diff --git a/sd/stablediffusion/assets/stable-samples/img2img/upscaling-in.png b/sd/stablediffusion/assets/stable-samples/img2img/upscaling-in.png new file mode 100644 index 0000000000000000000000000000000000000000..6a16bf53a95850a4eb7730105cea61af7c435c5e --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/img2img/upscaling-in.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16e043b62bdfcc5be7d0eca5c747878b78e4e6ffaeb3cd1257568cbc2b5e6f7a +size 1167237 diff --git a/sd/stablediffusion/assets/stable-samples/img2img/upscaling-out.png b/sd/stablediffusion/assets/stable-samples/img2img/upscaling-out.png new file mode 100644 index 0000000000000000000000000000000000000000..b7926bc81099736f7c8df32cadc4481c07eddbd6 --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/img2img/upscaling-out.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c171218814d141f87884672cb00ae07c3ed0e14ce7f7023f2041678e01d93f59 +size 1317941 diff --git a/sd/stablediffusion/assets/stable-samples/txt2img/000002025.png b/sd/stablediffusion/assets/stable-samples/txt2img/000002025.png new file mode 100644 index 0000000000000000000000000000000000000000..66891c142e9cfe3e0b0193d16b5a45d7b72a7d8a Binary files /dev/null and b/sd/stablediffusion/assets/stable-samples/txt2img/000002025.png differ diff --git a/sd/stablediffusion/assets/stable-samples/txt2img/000002035.png b/sd/stablediffusion/assets/stable-samples/txt2img/000002035.png new file mode 100644 index 0000000000000000000000000000000000000000..c707c130932a13fae76584324368fedc33faeba4 Binary files /dev/null and b/sd/stablediffusion/assets/stable-samples/txt2img/000002035.png differ diff --git a/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0001.png b/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0001.png new file mode 100644 index 0000000000000000000000000000000000000000..a3260bf98645b5f78e7eaf6d08b417a53c310fc2 --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0001.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed10e1df0f4c0f83794310e59a77098b4836d96a2b12cc809ddf39e77b1b6c94 +size 4627059 diff --git a/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0002.png b/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0002.png new file mode 100644 index 0000000000000000000000000000000000000000..297dbfd110876b8e9783e72bb6cf650cf1c760fd --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0002.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4a009d112633ac788fbbe7a7176d4002e95407b64672afa8104755534bb4641 +size 3458875 diff --git a/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0004.png b/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0004.png new file mode 100644 index 0000000000000000000000000000000000000000..f70da62c63f24b9d87347f8c8613dd1cf2993886 --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0004.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ac938c03b4b554e1c475a4b3c5df50b72a890eec21542fa7911a8ff01bf13f4 +size 4097005 diff --git a/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0005.png b/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0005.png new file mode 100644 index 0000000000000000000000000000000000000000..067259de9ef2422adac2793d3417245fe2671b24 --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0005.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:849b877a80752b4578afc2ece4ea0726768809298ee9a38284ba4e159d0a817c +size 2166197 diff --git a/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0006.png b/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0006.png new file mode 100644 index 0000000000000000000000000000000000000000..0002baa798eaae545f6f2fc205bf6cb965a2b97c --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/txt2img/768/merged-0006.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d31dbbf76633677be3b8eba933e9eec82825925535ef9c557a3003daf16ad42 +size 4371148 diff --git a/sd/stablediffusion/assets/stable-samples/txt2img/merged-0001.png b/sd/stablediffusion/assets/stable-samples/txt2img/merged-0001.png new file mode 100644 index 0000000000000000000000000000000000000000..c76fd405d99ba9a83ea93c974d6fa12bd9555a8f --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/txt2img/merged-0001.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71ca5f77befffa10a2ef6d4b69f8bb721e7ebd7ea03538e2c359dc44f526b0e8 +size 2409358 diff --git a/sd/stablediffusion/assets/stable-samples/txt2img/merged-0003.png b/sd/stablediffusion/assets/stable-samples/txt2img/merged-0003.png new file mode 100644 index 0000000000000000000000000000000000000000..cdc0e618cf42d225d417901b64b1203732eeb879 --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/txt2img/merged-0003.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9fde5a40c512d61e2390e70d9f14b0d33f0af84cbde2dcd9d86e1f9b38072266 +size 2273528 diff --git a/sd/stablediffusion/assets/stable-samples/txt2img/merged-0005.png b/sd/stablediffusion/assets/stable-samples/txt2img/merged-0005.png new file mode 100644 index 0000000000000000000000000000000000000000..88fcd04df9f576070d97f5655733b7a185352676 --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/txt2img/merged-0005.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a417aadc1d91b91531ca6bbf89840a36f432d8e9382aaa953610bedce22ff76f +size 2576264 diff --git a/sd/stablediffusion/assets/stable-samples/txt2img/merged-0006.png b/sd/stablediffusion/assets/stable-samples/txt2img/merged-0006.png new file mode 100644 index 0000000000000000000000000000000000000000..831c2096d8a99cb50323e48e17cc3f4dbba60f8c --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/txt2img/merged-0006.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d55ba7d103da275b4612976e93f405fcb593f7e6a6fda31f2e180b41c8e4f59 +size 2638534 diff --git a/sd/stablediffusion/assets/stable-samples/txt2img/merged-0007.png b/sd/stablediffusion/assets/stable-samples/txt2img/merged-0007.png new file mode 100644 index 0000000000000000000000000000000000000000..05e0f3fd7c8b6db82f15026053687558906e9e30 --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/txt2img/merged-0007.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:920ccf908b7fa5073a7c5cd3f4e109b5e66f7e29517ef5462ca55e931d0b5689 +size 2406799 diff --git a/sd/stablediffusion/assets/stable-samples/upscaling/merged-dog.png b/sd/stablediffusion/assets/stable-samples/upscaling/merged-dog.png new file mode 100644 index 0000000000000000000000000000000000000000..5af3eff5d6e42150b29a2c5a6cad5b3b025212d6 --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/upscaling/merged-dog.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d85d15bd51b3fa162f2b020ccac5a64b10ced728c6f22dcba183dc65ab6e8b5a +size 1823141 diff --git a/sd/stablediffusion/assets/stable-samples/upscaling/sampled-bear-x4.png b/sd/stablediffusion/assets/stable-samples/upscaling/sampled-bear-x4.png new file mode 100644 index 0000000000000000000000000000000000000000..00dc7730d2504b40de6958d41411252852a246cd --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/upscaling/sampled-bear-x4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d4f2aaa8eb3054cda0a6e8577170d09c1494809cceca21973497602e17a22f1e +size 3155724 diff --git a/sd/stablediffusion/assets/stable-samples/upscaling/snow-leopard-x4.png b/sd/stablediffusion/assets/stable-samples/upscaling/snow-leopard-x4.png new file mode 100644 index 0000000000000000000000000000000000000000..30a30a0929d898a5ea71b0b5e6403f54cb992da8 --- /dev/null +++ b/sd/stablediffusion/assets/stable-samples/upscaling/snow-leopard-x4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe8231dddcf77ada4b46f6949b4ea7757ff2d006253e1807ba6e1168077aad19 +size 3887284 diff --git a/sd/stablediffusion/configs/stable-diffusion/v2-inference-v.yaml b/sd/stablediffusion/configs/stable-diffusion/v2-inference-v.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8ec8dfbfefe94ae8522c93017668fea78d580acf --- /dev/null +++ b/sd/stablediffusion/configs/stable-diffusion/v2-inference-v.yaml @@ -0,0 +1,68 @@ +model: + base_learning_rate: 1.0e-4 + target: ldm.models.diffusion.ddpm.LatentDiffusion + params: + parameterization: "v" + linear_start: 0.00085 + linear_end: 0.0120 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + image_size: 64 + channels: 4 + cond_stage_trainable: false + conditioning_key: crossattn + monitor: val/loss_simple_ema + scale_factor: 0.18215 + use_ema: False # we set this to false because this is an inference only config + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + use_checkpoint: True + use_fp16: True + image_size: 32 # unused + in_channels: 4 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_head_channels: 64 # need to fix for flash-attn + use_spatial_transformer: True + use_linear_in_transformer: True + transformer_depth: 1 + context_dim: 1024 + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + #attn_type: "vanilla-xformers" + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder + params: + freeze: True + layer: "penultimate" diff --git a/sd/stablediffusion/configs/stable-diffusion/v2-inference.yaml b/sd/stablediffusion/configs/stable-diffusion/v2-inference.yaml new file mode 100644 index 0000000000000000000000000000000000000000..152c4f3c2b36c3b246a9cb10eb8166134b0d2e1c --- /dev/null +++ b/sd/stablediffusion/configs/stable-diffusion/v2-inference.yaml @@ -0,0 +1,67 @@ +model: + base_learning_rate: 1.0e-4 + target: ldm.models.diffusion.ddpm.LatentDiffusion + params: + linear_start: 0.00085 + linear_end: 0.0120 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + image_size: 64 + channels: 4 + cond_stage_trainable: false + conditioning_key: crossattn + monitor: val/loss_simple_ema + scale_factor: 0.18215 + use_ema: False # we set this to false because this is an inference only config + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + use_checkpoint: True + use_fp16: True + image_size: 32 # unused + in_channels: 4 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_head_channels: 64 # need to fix for flash-attn + use_spatial_transformer: True + use_linear_in_transformer: True + transformer_depth: 1 + context_dim: 1024 + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + #attn_type: "vanilla-xformers" + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder + params: + freeze: True + layer: "penultimate" diff --git a/sd/stablediffusion/configs/stable-diffusion/v2-inpainting-inference.yaml b/sd/stablediffusion/configs/stable-diffusion/v2-inpainting-inference.yaml new file mode 100644 index 0000000000000000000000000000000000000000..32a9471d71b828c51bcbbabfe34c5f6c8282c803 --- /dev/null +++ b/sd/stablediffusion/configs/stable-diffusion/v2-inpainting-inference.yaml @@ -0,0 +1,158 @@ +model: + base_learning_rate: 5.0e-05 + target: ldm.models.diffusion.ddpm.LatentInpaintDiffusion + params: + linear_start: 0.00085 + linear_end: 0.0120 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + image_size: 64 + channels: 4 + cond_stage_trainable: false + conditioning_key: hybrid + scale_factor: 0.18215 + monitor: val/loss_simple_ema + finetune_keys: null + use_ema: False + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + use_checkpoint: True + image_size: 32 # unused + in_channels: 9 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_head_channels: 64 # need to fix for flash-attn + use_spatial_transformer: True + use_linear_in_transformer: True + transformer_depth: 1 + context_dim: 1024 + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + #attn_type: "vanilla-xformers" + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [ ] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder + params: + freeze: True + layer: "penultimate" + + +data: + target: ldm.data.laion.WebDataModuleFromConfig + params: + tar_base: null # for concat as in LAION-A + p_unsafe_threshold: 0.1 + filter_word_list: "data/filters.yaml" + max_pwatermark: 0.45 + batch_size: 8 + num_workers: 6 + multinode: True + min_size: 512 + train: + shards: + - "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-0/{00000..18699}.tar -" + - "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-1/{00000..18699}.tar -" + - "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-2/{00000..18699}.tar -" + - "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-3/{00000..18699}.tar -" + - "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-4/{00000..18699}.tar -" #{00000-94333}.tar" + shuffle: 10000 + image_key: jpg + image_transforms: + - target: torchvision.transforms.Resize + params: + size: 512 + interpolation: 3 + - target: torchvision.transforms.RandomCrop + params: + size: 512 + postprocess: + target: ldm.data.laion.AddMask + params: + mode: "512train-large" + p_drop: 0.25 + # NOTE use enough shards to avoid empty validation loops in workers + validation: + shards: + - "pipe:aws s3 cp s3://deep-floyd-s3/datasets/laion_cleaned-part5/{93001..94333}.tar - " + shuffle: 0 + image_key: jpg + image_transforms: + - target: torchvision.transforms.Resize + params: + size: 512 + interpolation: 3 + - target: torchvision.transforms.CenterCrop + params: + size: 512 + postprocess: + target: ldm.data.laion.AddMask + params: + mode: "512train-large" + p_drop: 0.25 + +lightning: + find_unused_parameters: True + modelcheckpoint: + params: + every_n_train_steps: 5000 + + callbacks: + metrics_over_trainsteps_checkpoint: + params: + every_n_train_steps: 10000 + + image_logger: + target: main.ImageLogger + params: + enable_autocast: False + disabled: False + batch_frequency: 1000 + max_images: 4 + increase_log_steps: False + log_first_step: False + log_images_kwargs: + use_ema_scope: False + inpaint: False + plot_progressive_rows: False + plot_diffusion_rows: False + N: 4 + unconditional_guidance_scale: 5.0 + unconditional_guidance_label: [""] + ddim_steps: 50 # todo check these out for depth2img, + ddim_eta: 0.0 # todo check these out for depth2img, + + trainer: + benchmark: True + val_check_interval: 5000000 + num_sanity_val_steps: 0 + accumulate_grad_batches: 1 diff --git a/sd/stablediffusion/configs/stable-diffusion/v2-midas-inference.yaml b/sd/stablediffusion/configs/stable-diffusion/v2-midas-inference.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f20c30f618b81091e31c2c4cf15325fa38638af4 --- /dev/null +++ b/sd/stablediffusion/configs/stable-diffusion/v2-midas-inference.yaml @@ -0,0 +1,74 @@ +model: + base_learning_rate: 5.0e-07 + target: ldm.models.diffusion.ddpm.LatentDepth2ImageDiffusion + params: + linear_start: 0.00085 + linear_end: 0.0120 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + image_size: 64 + channels: 4 + cond_stage_trainable: false + conditioning_key: hybrid + scale_factor: 0.18215 + monitor: val/loss_simple_ema + finetune_keys: null + use_ema: False + + depth_stage_config: + target: ldm.modules.midas.api.MiDaSInference + params: + model_type: "dpt_hybrid" + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + use_checkpoint: True + image_size: 32 # unused + in_channels: 5 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_head_channels: 64 # need to fix for flash-attn + use_spatial_transformer: True + use_linear_in_transformer: True + transformer_depth: 1 + context_dim: 1024 + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + #attn_type: "vanilla-xformers" + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [ ] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder + params: + freeze: True + layer: "penultimate" + + diff --git a/sd/stablediffusion/configs/stable-diffusion/x4-upscaling.yaml b/sd/stablediffusion/configs/stable-diffusion/x4-upscaling.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2db0964af699f86d1891c761710a2d53f59b842c --- /dev/null +++ b/sd/stablediffusion/configs/stable-diffusion/x4-upscaling.yaml @@ -0,0 +1,76 @@ +model: + base_learning_rate: 1.0e-04 + target: ldm.models.diffusion.ddpm.LatentUpscaleDiffusion + params: + parameterization: "v" + low_scale_key: "lr" + linear_start: 0.0001 + linear_end: 0.02 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + image_size: 128 + channels: 4 + cond_stage_trainable: false + conditioning_key: "hybrid-adm" + monitor: val/loss_simple_ema + scale_factor: 0.08333 + use_ema: False + + low_scale_config: + target: ldm.modules.diffusionmodules.upscaling.ImageConcatWithNoiseAugmentation + params: + noise_schedule_config: # image space + linear_start: 0.0001 + linear_end: 0.02 + max_noise_level: 350 + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + use_checkpoint: True + num_classes: 1000 # timesteps for noise conditioning (here constant, just need one) + image_size: 128 + in_channels: 7 + out_channels: 4 + model_channels: 256 + attention_resolutions: [ 2,4,8] + num_res_blocks: 2 + channel_mult: [ 1, 2, 2, 4] + disable_self_attentions: [True, True, True, False] + disable_middle_self_attn: False + num_heads: 8 + use_spatial_transformer: True + transformer_depth: 1 + context_dim: 1024 + legacy: False + use_linear_in_transformer: True + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + ddconfig: + # attn_type: "vanilla-xformers" this model needs efficient attention to be feasible on HR data, also the decoder seems to break in half precision (UNet is fine though) + double_z: True + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: [ 1,2,4 ] # num_down = len(ch_mult)-1 + num_res_blocks: 2 + attn_resolutions: [ ] + dropout: 0.0 + + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder + params: + freeze: True + layer: "penultimate" + diff --git a/sd/stablediffusion/environment.yaml b/sd/stablediffusion/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4687b309b60ae2d6040fcb3f90a380cf6fb11b21 --- /dev/null +++ b/sd/stablediffusion/environment.yaml @@ -0,0 +1,29 @@ +name: ldm +channels: + - pytorch + - defaults +dependencies: + - python=3.8.5 + - pip=20.3 + - cudatoolkit=11.3 + - pytorch=1.12.1 + - torchvision=0.13.1 + - numpy=1.23.1 + - pip: + - albumentations==1.3.0 + - opencv-python==4.6.0.66 + - imageio==2.9.0 + - imageio-ffmpeg==0.4.2 + - pytorch-lightning==1.4.2 + - omegaconf==2.1.1 + - test-tube>=0.7.5 + - streamlit==1.12.1 + - einops==0.3.0 + - transformers==4.19.2 + - webdataset==0.2.5 + - kornia==0.6 + - open_clip_torch==2.0.2 + - invisible-watermark>=0.1.5 + - streamlit-drawable-canvas==0.8.0 + - torchmetrics==0.6.0 + - -e . diff --git a/sd/stablediffusion/ldm/__pycache__/util.cpython-39.pyc b/sd/stablediffusion/ldm/__pycache__/util.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..216be49b4dddf1f19d4f27578653a7bb31b7d88a Binary files /dev/null and b/sd/stablediffusion/ldm/__pycache__/util.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/data/__init__.py b/sd/stablediffusion/ldm/data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/ldm/data/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/ldm/data/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..385f7f1f758ace4e086ebf744a47fa1d0a7569ee Binary files /dev/null and b/sd/stablediffusion/ldm/data/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/data/__pycache__/util.cpython-39.pyc b/sd/stablediffusion/ldm/data/__pycache__/util.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..18f411deda254ba7b10b6a13cdbd15f9034cc125 Binary files /dev/null and b/sd/stablediffusion/ldm/data/__pycache__/util.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/data/util.py b/sd/stablediffusion/ldm/data/util.py new file mode 100644 index 0000000000000000000000000000000000000000..5b60ceb2349e3bd7900ff325740e2022d2903b1c --- /dev/null +++ b/sd/stablediffusion/ldm/data/util.py @@ -0,0 +1,24 @@ +import torch + +from ldm.modules.midas.api import load_midas_transform + + +class AddMiDaS(object): + def __init__(self, model_type): + super().__init__() + self.transform = load_midas_transform(model_type) + + def pt2np(self, x): + x = ((x + 1.0) * .5).detach().cpu().numpy() + return x + + def np2pt(self, x): + x = torch.from_numpy(x) * 2 - 1. + return x + + def __call__(self, sample): + # sample['jpg'] is tensor hwc in [-1, 1] at this point + x = self.pt2np(sample['jpg']) + x = self.transform({"image": x})["image"] + sample['midas_in'] = x + return sample \ No newline at end of file diff --git a/sd/stablediffusion/ldm/models/__pycache__/autoencoder.cpython-39.pyc b/sd/stablediffusion/ldm/models/__pycache__/autoencoder.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cddd9b1a834d2f6fa4b5f72f245e34ae98287ebe Binary files /dev/null and b/sd/stablediffusion/ldm/models/__pycache__/autoencoder.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/models/autoencoder.py b/sd/stablediffusion/ldm/models/autoencoder.py new file mode 100644 index 0000000000000000000000000000000000000000..d122549995ce2cd64092c81a58419ed4a15a02fd --- /dev/null +++ b/sd/stablediffusion/ldm/models/autoencoder.py @@ -0,0 +1,219 @@ +import torch +import pytorch_lightning as pl +import torch.nn.functional as F +from contextlib import contextmanager + +from ldm.modules.diffusionmodules.model import Encoder, Decoder +from ldm.modules.distributions.distributions import DiagonalGaussianDistribution + +from ldm.util import instantiate_from_config +from ldm.modules.ema import LitEma + + +class AutoencoderKL(pl.LightningModule): + def __init__(self, + ddconfig, + lossconfig, + embed_dim, + ckpt_path=None, + ignore_keys=[], + image_key="image", + colorize_nlabels=None, + monitor=None, + ema_decay=None, + learn_logvar=False + ): + super().__init__() + self.learn_logvar = learn_logvar + self.image_key = image_key + self.encoder = Encoder(**ddconfig) + self.decoder = Decoder(**ddconfig) + self.loss = instantiate_from_config(lossconfig) + assert ddconfig["double_z"] + self.quant_conv = torch.nn.Conv2d(2*ddconfig["z_channels"], 2*embed_dim, 1) + self.post_quant_conv = torch.nn.Conv2d(embed_dim, ddconfig["z_channels"], 1) + self.embed_dim = embed_dim + if colorize_nlabels is not None: + assert type(colorize_nlabels)==int + self.register_buffer("colorize", torch.randn(3, colorize_nlabels, 1, 1)) + if monitor is not None: + self.monitor = monitor + + self.use_ema = ema_decay is not None + if self.use_ema: + self.ema_decay = ema_decay + assert 0. < ema_decay < 1. + self.model_ema = LitEma(self, decay=ema_decay) + print(f"Keeping EMAs of {len(list(self.model_ema.buffers()))}.") + + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys) + + def init_from_ckpt(self, path, ignore_keys=list()): + sd = torch.load(path, map_location="cpu")["state_dict"] + keys = list(sd.keys()) + for k in keys: + for ik in ignore_keys: + if k.startswith(ik): + print("Deleting key {} from state_dict.".format(k)) + del sd[k] + self.load_state_dict(sd, strict=False) + print(f"Restored from {path}") + + @contextmanager + def ema_scope(self, context=None): + if self.use_ema: + self.model_ema.store(self.parameters()) + self.model_ema.copy_to(self) + if context is not None: + print(f"{context}: Switched to EMA weights") + try: + yield None + finally: + if self.use_ema: + self.model_ema.restore(self.parameters()) + if context is not None: + print(f"{context}: Restored training weights") + + def on_train_batch_end(self, *args, **kwargs): + if self.use_ema: + self.model_ema(self) + + def encode(self, x): + h = self.encoder(x) + moments = self.quant_conv(h) + posterior = DiagonalGaussianDistribution(moments) + return posterior + + def decode(self, z): + z = self.post_quant_conv(z) + dec = self.decoder(z) + return dec + + def forward(self, input, sample_posterior=True): + posterior = self.encode(input) + if sample_posterior: + z = posterior.sample() + else: + z = posterior.mode() + dec = self.decode(z) + return dec, posterior + + def get_input(self, batch, k): + x = batch[k] + if len(x.shape) == 3: + x = x[..., None] + x = x.permute(0, 3, 1, 2).to(memory_format=torch.contiguous_format).float() + return x + + def training_step(self, batch, batch_idx, optimizer_idx): + inputs = self.get_input(batch, self.image_key) + reconstructions, posterior = self(inputs) + + if optimizer_idx == 0: + # train encoder+decoder+logvar + aeloss, log_dict_ae = self.loss(inputs, reconstructions, posterior, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + self.log("aeloss", aeloss, prog_bar=True, logger=True, on_step=True, on_epoch=True) + self.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=False) + return aeloss + + if optimizer_idx == 1: + # train the discriminator + discloss, log_dict_disc = self.loss(inputs, reconstructions, posterior, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + + self.log("discloss", discloss, prog_bar=True, logger=True, on_step=True, on_epoch=True) + self.log_dict(log_dict_disc, prog_bar=False, logger=True, on_step=True, on_epoch=False) + return discloss + + def validation_step(self, batch, batch_idx): + log_dict = self._validation_step(batch, batch_idx) + with self.ema_scope(): + log_dict_ema = self._validation_step(batch, batch_idx, postfix="_ema") + return log_dict + + def _validation_step(self, batch, batch_idx, postfix=""): + inputs = self.get_input(batch, self.image_key) + reconstructions, posterior = self(inputs) + aeloss, log_dict_ae = self.loss(inputs, reconstructions, posterior, 0, self.global_step, + last_layer=self.get_last_layer(), split="val"+postfix) + + discloss, log_dict_disc = self.loss(inputs, reconstructions, posterior, 1, self.global_step, + last_layer=self.get_last_layer(), split="val"+postfix) + + self.log(f"val{postfix}/rec_loss", log_dict_ae[f"val{postfix}/rec_loss"]) + self.log_dict(log_dict_ae) + self.log_dict(log_dict_disc) + return self.log_dict + + def configure_optimizers(self): + lr = self.learning_rate + ae_params_list = list(self.encoder.parameters()) + list(self.decoder.parameters()) + list( + self.quant_conv.parameters()) + list(self.post_quant_conv.parameters()) + if self.learn_logvar: + print(f"{self.__class__.__name__}: Learning logvar") + ae_params_list.append(self.loss.logvar) + opt_ae = torch.optim.Adam(ae_params_list, + lr=lr, betas=(0.5, 0.9)) + opt_disc = torch.optim.Adam(self.loss.discriminator.parameters(), + lr=lr, betas=(0.5, 0.9)) + return [opt_ae, opt_disc], [] + + def get_last_layer(self): + return self.decoder.conv_out.weight + + @torch.no_grad() + def log_images(self, batch, only_inputs=False, log_ema=False, **kwargs): + log = dict() + x = self.get_input(batch, self.image_key) + x = x.to(self.device) + if not only_inputs: + xrec, posterior = self(x) + if x.shape[1] > 3: + # colorize with random projection + assert xrec.shape[1] > 3 + x = self.to_rgb(x) + xrec = self.to_rgb(xrec) + log["samples"] = self.decode(torch.randn_like(posterior.sample())) + log["reconstructions"] = xrec + if log_ema or self.use_ema: + with self.ema_scope(): + xrec_ema, posterior_ema = self(x) + if x.shape[1] > 3: + # colorize with random projection + assert xrec_ema.shape[1] > 3 + xrec_ema = self.to_rgb(xrec_ema) + log["samples_ema"] = self.decode(torch.randn_like(posterior_ema.sample())) + log["reconstructions_ema"] = xrec_ema + log["inputs"] = x + return log + + def to_rgb(self, x): + assert self.image_key == "segmentation" + if not hasattr(self, "colorize"): + self.register_buffer("colorize", torch.randn(3, x.shape[1], 1, 1).to(x)) + x = F.conv2d(x, weight=self.colorize) + x = 2.*(x-x.min())/(x.max()-x.min()) - 1. + return x + + +class IdentityFirstStage(torch.nn.Module): + def __init__(self, *args, vq_interface=False, **kwargs): + self.vq_interface = vq_interface + super().__init__() + + def encode(self, x, *args, **kwargs): + return x + + def decode(self, x, *args, **kwargs): + return x + + def quantize(self, x, *args, **kwargs): + if self.vq_interface: + return x, None, [None, None, None] + return x + + def forward(self, x, *args, **kwargs): + return x + diff --git a/sd/stablediffusion/ldm/models/diffusion/__init__.py b/sd/stablediffusion/ldm/models/diffusion/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/ldm/models/diffusion/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/ldm/models/diffusion/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..358a268544cafae095efa27f20603c4f6a1d8062 Binary files /dev/null and b/sd/stablediffusion/ldm/models/diffusion/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/models/diffusion/__pycache__/ddim.cpython-39.pyc b/sd/stablediffusion/ldm/models/diffusion/__pycache__/ddim.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..298346c30024d18d6041918321246ce1353b3ab3 Binary files /dev/null and b/sd/stablediffusion/ldm/models/diffusion/__pycache__/ddim.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/models/diffusion/__pycache__/ddpm.cpython-39.pyc b/sd/stablediffusion/ldm/models/diffusion/__pycache__/ddpm.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f1dd1a2b16815d91536a47bd79c408e0159d56b Binary files /dev/null and b/sd/stablediffusion/ldm/models/diffusion/__pycache__/ddpm.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/models/diffusion/__pycache__/plms.cpython-39.pyc b/sd/stablediffusion/ldm/models/diffusion/__pycache__/plms.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ffdce2bb73709c68c081f5f217c5fc4df66651f8 Binary files /dev/null and b/sd/stablediffusion/ldm/models/diffusion/__pycache__/plms.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/models/diffusion/__pycache__/sampling_util.cpython-39.pyc b/sd/stablediffusion/ldm/models/diffusion/__pycache__/sampling_util.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a4986bba3ebb6dd1838e63c2bcd3d6534eb808cc Binary files /dev/null and b/sd/stablediffusion/ldm/models/diffusion/__pycache__/sampling_util.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/models/diffusion/ddim.py b/sd/stablediffusion/ldm/models/diffusion/ddim.py new file mode 100644 index 0000000000000000000000000000000000000000..27ead0ea914c64c747b64e690662899fb3801144 --- /dev/null +++ b/sd/stablediffusion/ldm/models/diffusion/ddim.py @@ -0,0 +1,336 @@ +"""SAMPLING ONLY.""" + +import torch +import numpy as np +from tqdm import tqdm + +from ldm.modules.diffusionmodules.util import make_ddim_sampling_parameters, make_ddim_timesteps, noise_like, extract_into_tensor + + +class DDIMSampler(object): + def __init__(self, model, schedule="linear", **kwargs): + super().__init__() + self.model = model + self.ddpm_num_timesteps = model.num_timesteps + self.schedule = schedule + + def register_buffer(self, name, attr): + if type(attr) == torch.Tensor: + if attr.device != torch.device("cuda"): + attr = attr.to(torch.device("cuda")) + setattr(self, name, attr) + + def make_schedule(self, ddim_num_steps, ddim_discretize="uniform", ddim_eta=0., verbose=True): + self.ddim_timesteps = make_ddim_timesteps(ddim_discr_method=ddim_discretize, num_ddim_timesteps=ddim_num_steps, + num_ddpm_timesteps=self.ddpm_num_timesteps,verbose=verbose) + alphas_cumprod = self.model.alphas_cumprod + assert alphas_cumprod.shape[0] == self.ddpm_num_timesteps, 'alphas have to be defined for each timestep' + to_torch = lambda x: x.clone().detach().to(torch.float32).to(self.model.device) + + self.register_buffer('betas', to_torch(self.model.betas)) + self.register_buffer('alphas_cumprod', to_torch(alphas_cumprod)) + self.register_buffer('alphas_cumprod_prev', to_torch(self.model.alphas_cumprod_prev)) + + # calculations for diffusion q(x_t | x_{t-1}) and others + self.register_buffer('sqrt_alphas_cumprod', to_torch(np.sqrt(alphas_cumprod.cpu()))) + self.register_buffer('sqrt_one_minus_alphas_cumprod', to_torch(np.sqrt(1. - alphas_cumprod.cpu()))) + self.register_buffer('log_one_minus_alphas_cumprod', to_torch(np.log(1. - alphas_cumprod.cpu()))) + self.register_buffer('sqrt_recip_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod.cpu()))) + self.register_buffer('sqrt_recipm1_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod.cpu() - 1))) + + # ddim sampling parameters + ddim_sigmas, ddim_alphas, ddim_alphas_prev = make_ddim_sampling_parameters(alphacums=alphas_cumprod.cpu(), + ddim_timesteps=self.ddim_timesteps, + eta=ddim_eta,verbose=verbose) + self.register_buffer('ddim_sigmas', ddim_sigmas) + self.register_buffer('ddim_alphas', ddim_alphas) + self.register_buffer('ddim_alphas_prev', ddim_alphas_prev) + self.register_buffer('ddim_sqrt_one_minus_alphas', np.sqrt(1. - ddim_alphas)) + sigmas_for_original_sampling_steps = ddim_eta * torch.sqrt( + (1 - self.alphas_cumprod_prev) / (1 - self.alphas_cumprod) * ( + 1 - self.alphas_cumprod / self.alphas_cumprod_prev)) + self.register_buffer('ddim_sigmas_for_original_num_steps', sigmas_for_original_sampling_steps) + + @torch.no_grad() + def sample(self, + S, + batch_size, + shape, + conditioning=None, + callback=None, + normals_sequence=None, + img_callback=None, + quantize_x0=False, + eta=0., + mask=None, + x0=None, + temperature=1., + noise_dropout=0., + score_corrector=None, + corrector_kwargs=None, + verbose=True, + x_T=None, + log_every_t=100, + unconditional_guidance_scale=1., + unconditional_conditioning=None, # this has to come in the same format as the conditioning, # e.g. as encoded tokens, ... + dynamic_threshold=None, + ucg_schedule=None, + **kwargs + ): + if conditioning is not None: + if isinstance(conditioning, dict): + ctmp = conditioning[list(conditioning.keys())[0]] + while isinstance(ctmp, list): ctmp = ctmp[0] + cbs = ctmp.shape[0] + if cbs != batch_size: + print(f"Warning: Got {cbs} conditionings but batch-size is {batch_size}") + + elif isinstance(conditioning, list): + for ctmp in conditioning: + if ctmp.shape[0] != batch_size: + print(f"Warning: Got {cbs} conditionings but batch-size is {batch_size}") + + else: + if conditioning.shape[0] != batch_size: + print(f"Warning: Got {conditioning.shape[0]} conditionings but batch-size is {batch_size}") + + self.make_schedule(ddim_num_steps=S, ddim_eta=eta, verbose=verbose) + # sampling + C, H, W = shape + size = (batch_size, C, H, W) + print(f'Data shape for DDIM sampling is {size}, eta {eta}') + + samples, intermediates = self.ddim_sampling(conditioning, size, + callback=callback, + img_callback=img_callback, + quantize_denoised=quantize_x0, + mask=mask, x0=x0, + ddim_use_original_steps=False, + noise_dropout=noise_dropout, + temperature=temperature, + score_corrector=score_corrector, + corrector_kwargs=corrector_kwargs, + x_T=x_T, + log_every_t=log_every_t, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=unconditional_conditioning, + dynamic_threshold=dynamic_threshold, + ucg_schedule=ucg_schedule + ) + return samples, intermediates + + @torch.no_grad() + def ddim_sampling(self, cond, shape, + x_T=None, ddim_use_original_steps=False, + callback=None, timesteps=None, quantize_denoised=False, + mask=None, x0=None, img_callback=None, log_every_t=100, + temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None, + unconditional_guidance_scale=1., unconditional_conditioning=None, dynamic_threshold=None, + ucg_schedule=None): + device = self.model.betas.device + b = shape[0] + if x_T is None: + img = torch.randn(shape, device=device) + else: + img = x_T + + if timesteps is None: + timesteps = self.ddpm_num_timesteps if ddim_use_original_steps else self.ddim_timesteps + elif timesteps is not None and not ddim_use_original_steps: + subset_end = int(min(timesteps / self.ddim_timesteps.shape[0], 1) * self.ddim_timesteps.shape[0]) - 1 + timesteps = self.ddim_timesteps[:subset_end] + + intermediates = {'x_inter': [img], 'pred_x0': [img]} + time_range = reversed(range(0,timesteps)) if ddim_use_original_steps else np.flip(timesteps) + total_steps = timesteps if ddim_use_original_steps else timesteps.shape[0] + print(f"Running DDIM Sampling with {total_steps} timesteps") + + iterator = tqdm(time_range, desc='DDIM Sampler', total=total_steps) + + for i, step in enumerate(iterator): + index = total_steps - i - 1 + ts = torch.full((b,), step, device=device, dtype=torch.long) + + if mask is not None: + assert x0 is not None + img_orig = self.model.q_sample(x0, ts) # TODO: deterministic forward pass? + img = img_orig * mask + (1. - mask) * img + + if ucg_schedule is not None: + assert len(ucg_schedule) == len(time_range) + unconditional_guidance_scale = ucg_schedule[i] + + outs = self.p_sample_ddim(img, cond, ts, index=index, use_original_steps=ddim_use_original_steps, + quantize_denoised=quantize_denoised, temperature=temperature, + noise_dropout=noise_dropout, score_corrector=score_corrector, + corrector_kwargs=corrector_kwargs, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=unconditional_conditioning, + dynamic_threshold=dynamic_threshold) + img, pred_x0 = outs + if callback: callback(i) + if img_callback: img_callback(pred_x0, i) + + if index % log_every_t == 0 or index == total_steps - 1: + intermediates['x_inter'].append(img) + intermediates['pred_x0'].append(pred_x0) + + return img, intermediates + + @torch.no_grad() + def p_sample_ddim(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False, + temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None, + unconditional_guidance_scale=1., unconditional_conditioning=None, + dynamic_threshold=None): + b, *_, device = *x.shape, x.device + + if unconditional_conditioning is None or unconditional_guidance_scale == 1.: + model_output = self.model.apply_model(x, t, c) + else: + x_in = torch.cat([x] * 2) + t_in = torch.cat([t] * 2) + if isinstance(c, dict): + assert isinstance(unconditional_conditioning, dict) + c_in = dict() + for k in c: + if isinstance(c[k], list): + c_in[k] = [torch.cat([ + unconditional_conditioning[k][i], + c[k][i]]) for i in range(len(c[k]))] + else: + c_in[k] = torch.cat([ + unconditional_conditioning[k], + c[k]]) + elif isinstance(c, list): + c_in = list() + assert isinstance(unconditional_conditioning, list) + for i in range(len(c)): + c_in.append(torch.cat([unconditional_conditioning[i], c[i]])) + else: + c_in = torch.cat([unconditional_conditioning, c]) + model_uncond, model_t = self.model.apply_model(x_in, t_in, c_in).chunk(2) + model_output = model_uncond + unconditional_guidance_scale * (model_t - model_uncond) + + if self.model.parameterization == "v": + e_t = self.model.predict_eps_from_z_and_v(x, t, model_output) + else: + e_t = model_output + + if score_corrector is not None: + assert self.model.parameterization == "eps", 'not implemented' + e_t = score_corrector.modify_score(self.model, e_t, x, t, c, **corrector_kwargs) + + alphas = self.model.alphas_cumprod if use_original_steps else self.ddim_alphas + alphas_prev = self.model.alphas_cumprod_prev if use_original_steps else self.ddim_alphas_prev + sqrt_one_minus_alphas = self.model.sqrt_one_minus_alphas_cumprod if use_original_steps else self.ddim_sqrt_one_minus_alphas + sigmas = self.model.ddim_sigmas_for_original_num_steps if use_original_steps else self.ddim_sigmas + # select parameters corresponding to the currently considered timestep + a_t = torch.full((b, 1, 1, 1), alphas[index], device=device) + a_prev = torch.full((b, 1, 1, 1), alphas_prev[index], device=device) + sigma_t = torch.full((b, 1, 1, 1), sigmas[index], device=device) + sqrt_one_minus_at = torch.full((b, 1, 1, 1), sqrt_one_minus_alphas[index],device=device) + + # current prediction for x_0 + if self.model.parameterization != "v": + pred_x0 = (x - sqrt_one_minus_at * e_t) / a_t.sqrt() + else: + pred_x0 = self.model.predict_start_from_z_and_v(x, t, model_output) + + if quantize_denoised: + pred_x0, _, *_ = self.model.first_stage_model.quantize(pred_x0) + + if dynamic_threshold is not None: + raise NotImplementedError() + + # direction pointing to x_t + dir_xt = (1. - a_prev - sigma_t**2).sqrt() * e_t + noise = sigma_t * noise_like(x.shape, device, repeat_noise) * temperature + if noise_dropout > 0.: + noise = torch.nn.functional.dropout(noise, p=noise_dropout) + x_prev = a_prev.sqrt() * pred_x0 + dir_xt + noise + return x_prev, pred_x0 + + @torch.no_grad() + def encode(self, x0, c, t_enc, use_original_steps=False, return_intermediates=None, + unconditional_guidance_scale=1.0, unconditional_conditioning=None, callback=None): + num_reference_steps = self.ddpm_num_timesteps if use_original_steps else self.ddim_timesteps.shape[0] + + assert t_enc <= num_reference_steps + num_steps = t_enc + + if use_original_steps: + alphas_next = self.alphas_cumprod[:num_steps] + alphas = self.alphas_cumprod_prev[:num_steps] + else: + alphas_next = self.ddim_alphas[:num_steps] + alphas = torch.tensor(self.ddim_alphas_prev[:num_steps]) + + x_next = x0 + intermediates = [] + inter_steps = [] + for i in tqdm(range(num_steps), desc='Encoding Image'): + t = torch.full((x0.shape[0],), i, device=self.model.device, dtype=torch.long) + if unconditional_guidance_scale == 1.: + noise_pred = self.model.apply_model(x_next, t, c) + else: + assert unconditional_conditioning is not None + e_t_uncond, noise_pred = torch.chunk( + self.model.apply_model(torch.cat((x_next, x_next)), torch.cat((t, t)), + torch.cat((unconditional_conditioning, c))), 2) + noise_pred = e_t_uncond + unconditional_guidance_scale * (noise_pred - e_t_uncond) + + xt_weighted = (alphas_next[i] / alphas[i]).sqrt() * x_next + weighted_noise_pred = alphas_next[i].sqrt() * ( + (1 / alphas_next[i] - 1).sqrt() - (1 / alphas[i] - 1).sqrt()) * noise_pred + x_next = xt_weighted + weighted_noise_pred + if return_intermediates and i % ( + num_steps // return_intermediates) == 0 and i < num_steps - 1: + intermediates.append(x_next) + inter_steps.append(i) + elif return_intermediates and i >= num_steps - 2: + intermediates.append(x_next) + inter_steps.append(i) + if callback: callback(i) + + out = {'x_encoded': x_next, 'intermediate_steps': inter_steps} + if return_intermediates: + out.update({'intermediates': intermediates}) + return x_next, out + + @torch.no_grad() + def stochastic_encode(self, x0, t, use_original_steps=False, noise=None): + # fast, but does not allow for exact reconstruction + # t serves as an index to gather the correct alphas + if use_original_steps: + sqrt_alphas_cumprod = self.sqrt_alphas_cumprod + sqrt_one_minus_alphas_cumprod = self.sqrt_one_minus_alphas_cumprod + else: + sqrt_alphas_cumprod = torch.sqrt(self.ddim_alphas) + sqrt_one_minus_alphas_cumprod = self.ddim_sqrt_one_minus_alphas + + if noise is None: + noise = torch.randn_like(x0) + return (extract_into_tensor(sqrt_alphas_cumprod, t, x0.shape) * x0 + + extract_into_tensor(sqrt_one_minus_alphas_cumprod, t, x0.shape) * noise) + + @torch.no_grad() + def decode(self, x_latent, cond, t_start, unconditional_guidance_scale=1.0, unconditional_conditioning=None, + use_original_steps=False, callback=None): + + timesteps = np.arange(self.ddpm_num_timesteps) if use_original_steps else self.ddim_timesteps + timesteps = timesteps[:t_start] + + time_range = np.flip(timesteps) + total_steps = timesteps.shape[0] + print(f"Running DDIM Sampling with {total_steps} timesteps") + + iterator = tqdm(time_range, desc='Decoding image', total=total_steps) + x_dec = x_latent + for i, step in enumerate(iterator): + index = total_steps - i - 1 + ts = torch.full((x_latent.shape[0],), step, device=x_latent.device, dtype=torch.long) + x_dec, _ = self.p_sample_ddim(x_dec, cond, ts, index=index, use_original_steps=use_original_steps, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=unconditional_conditioning) + if callback: callback(i) + return x_dec \ No newline at end of file diff --git a/sd/stablediffusion/ldm/models/diffusion/ddpm.py b/sd/stablediffusion/ldm/models/diffusion/ddpm.py new file mode 100644 index 0000000000000000000000000000000000000000..60902123c16e781141ccf6727a032d8c788b62f4 --- /dev/null +++ b/sd/stablediffusion/ldm/models/diffusion/ddpm.py @@ -0,0 +1,1795 @@ +""" +wild mixture of +https://github.com/lucidrains/denoising-diffusion-pytorch/blob/7706bdfc6f527f58d33f84b7b522e61e6e3164b3/denoising_diffusion_pytorch/denoising_diffusion_pytorch.py +https://github.com/openai/improved-diffusion/blob/e94489283bb876ac1477d5dd7709bbbd2d9902ce/improved_diffusion/gaussian_diffusion.py +https://github.com/CompVis/taming-transformers +-- merci +""" + +import torch +import torch.nn as nn +import numpy as np +import pytorch_lightning as pl +from torch.optim.lr_scheduler import LambdaLR +from einops import rearrange, repeat +from contextlib import contextmanager, nullcontext +from functools import partial +import itertools +from tqdm import tqdm +from torchvision.utils import make_grid +from pytorch_lightning.utilities.distributed import rank_zero_only +from omegaconf import ListConfig + +from ldm.util import log_txt_as_img, exists, default, ismap, isimage, mean_flat, count_params, instantiate_from_config +from ldm.modules.ema import LitEma +from ldm.modules.distributions.distributions import normal_kl, DiagonalGaussianDistribution +from ldm.models.autoencoder import IdentityFirstStage, AutoencoderKL +from ldm.modules.diffusionmodules.util import make_beta_schedule, extract_into_tensor, noise_like +from ldm.models.diffusion.ddim import DDIMSampler + + +__conditioning_keys__ = {'concat': 'c_concat', + 'crossattn': 'c_crossattn', + 'adm': 'y'} + + +def disabled_train(self, mode=True): + """Overwrite model.train with this function to make sure train/eval mode + does not change anymore.""" + return self + + +def uniform_on_device(r1, r2, shape, device): + return (r1 - r2) * torch.rand(*shape, device=device) + r2 + + +class DDPM(pl.LightningModule): + # classic DDPM with Gaussian diffusion, in image space + def __init__(self, + unet_config, + timesteps=1000, + beta_schedule="linear", + loss_type="l2", + ckpt_path=None, + ignore_keys=[], + load_only_unet=False, + monitor="val/loss", + use_ema=True, + first_stage_key="image", + image_size=256, + channels=3, + log_every_t=100, + clip_denoised=True, + linear_start=1e-4, + linear_end=2e-2, + cosine_s=8e-3, + given_betas=None, + original_elbo_weight=0., + v_posterior=0., # weight for choosing posterior variance as sigma = (1-v) * beta_tilde + v * beta + l_simple_weight=1., + conditioning_key=None, + parameterization="eps", # all assuming fixed variance schedules + scheduler_config=None, + use_positional_encodings=False, + learn_logvar=False, + logvar_init=0., + make_it_fit=False, + ucg_training=None, + reset_ema=False, + reset_num_ema_updates=False, + ): + super().__init__() + assert parameterization in ["eps", "x0", "v"], 'currently only supporting "eps" and "x0" and "v"' + self.parameterization = parameterization + print(f"{self.__class__.__name__}: Running in {self.parameterization}-prediction mode") + self.cond_stage_model = None + self.clip_denoised = clip_denoised + self.log_every_t = log_every_t + self.first_stage_key = first_stage_key + self.image_size = image_size # try conv? + self.channels = channels + self.use_positional_encodings = use_positional_encodings + self.model = DiffusionWrapper(unet_config, conditioning_key) + count_params(self.model, verbose=True) + self.use_ema = use_ema + if self.use_ema: + self.model_ema = LitEma(self.model) + print(f"Keeping EMAs of {len(list(self.model_ema.buffers()))}.") + + self.use_scheduler = scheduler_config is not None + if self.use_scheduler: + self.scheduler_config = scheduler_config + + self.v_posterior = v_posterior + self.original_elbo_weight = original_elbo_weight + self.l_simple_weight = l_simple_weight + + if monitor is not None: + self.monitor = monitor + self.make_it_fit = make_it_fit + if reset_ema: assert exists(ckpt_path) + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys, only_model=load_only_unet) + if reset_ema: + assert self.use_ema + print(f"Resetting ema to pure model weights. This is useful when restoring from an ema-only checkpoint.") + self.model_ema = LitEma(self.model) + if reset_num_ema_updates: + print(" +++++++++++ WARNING: RESETTING NUM_EMA UPDATES TO ZERO +++++++++++ ") + assert self.use_ema + self.model_ema.reset_num_updates() + + self.register_schedule(given_betas=given_betas, beta_schedule=beta_schedule, timesteps=timesteps, + linear_start=linear_start, linear_end=linear_end, cosine_s=cosine_s) + + self.loss_type = loss_type + + self.learn_logvar = learn_logvar + self.logvar = torch.full(fill_value=logvar_init, size=(self.num_timesteps,)) + if self.learn_logvar: + self.logvar = nn.Parameter(self.logvar, requires_grad=True) + + self.ucg_training = ucg_training or dict() + if self.ucg_training: + self.ucg_prng = np.random.RandomState() + + def register_schedule(self, given_betas=None, beta_schedule="linear", timesteps=1000, + linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + if exists(given_betas): + betas = given_betas + else: + betas = make_beta_schedule(beta_schedule, timesteps, linear_start=linear_start, linear_end=linear_end, + cosine_s=cosine_s) + alphas = 1. - betas + alphas_cumprod = np.cumprod(alphas, axis=0) + alphas_cumprod_prev = np.append(1., alphas_cumprod[:-1]) + + timesteps, = betas.shape + self.num_timesteps = int(timesteps) + self.linear_start = linear_start + self.linear_end = linear_end + assert alphas_cumprod.shape[0] == self.num_timesteps, 'alphas have to be defined for each timestep' + + to_torch = partial(torch.tensor, dtype=torch.float32) + + self.register_buffer('betas', to_torch(betas)) + self.register_buffer('alphas_cumprod', to_torch(alphas_cumprod)) + self.register_buffer('alphas_cumprod_prev', to_torch(alphas_cumprod_prev)) + + # calculations for diffusion q(x_t | x_{t-1}) and others + self.register_buffer('sqrt_alphas_cumprod', to_torch(np.sqrt(alphas_cumprod))) + self.register_buffer('sqrt_one_minus_alphas_cumprod', to_torch(np.sqrt(1. - alphas_cumprod))) + self.register_buffer('log_one_minus_alphas_cumprod', to_torch(np.log(1. - alphas_cumprod))) + self.register_buffer('sqrt_recip_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod))) + self.register_buffer('sqrt_recipm1_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod - 1))) + + # calculations for posterior q(x_{t-1} | x_t, x_0) + posterior_variance = (1 - self.v_posterior) * betas * (1. - alphas_cumprod_prev) / ( + 1. - alphas_cumprod) + self.v_posterior * betas + # above: equal to 1. / (1. / (1. - alpha_cumprod_tm1) + alpha_t / beta_t) + self.register_buffer('posterior_variance', to_torch(posterior_variance)) + # below: log calculation clipped because the posterior variance is 0 at the beginning of the diffusion chain + self.register_buffer('posterior_log_variance_clipped', to_torch(np.log(np.maximum(posterior_variance, 1e-20)))) + self.register_buffer('posterior_mean_coef1', to_torch( + betas * np.sqrt(alphas_cumprod_prev) / (1. - alphas_cumprod))) + self.register_buffer('posterior_mean_coef2', to_torch( + (1. - alphas_cumprod_prev) * np.sqrt(alphas) / (1. - alphas_cumprod))) + + if self.parameterization == "eps": + lvlb_weights = self.betas ** 2 / ( + 2 * self.posterior_variance * to_torch(alphas) * (1 - self.alphas_cumprod)) + elif self.parameterization == "x0": + lvlb_weights = 0.5 * np.sqrt(torch.Tensor(alphas_cumprod)) / (2. * 1 - torch.Tensor(alphas_cumprod)) + elif self.parameterization == "v": + lvlb_weights = torch.ones_like(self.betas ** 2 / ( + 2 * self.posterior_variance * to_torch(alphas) * (1 - self.alphas_cumprod))) + else: + raise NotImplementedError("mu not supported") + lvlb_weights[0] = lvlb_weights[1] + self.register_buffer('lvlb_weights', lvlb_weights, persistent=False) + assert not torch.isnan(self.lvlb_weights).all() + + @contextmanager + def ema_scope(self, context=None): + if self.use_ema: + self.model_ema.store(self.model.parameters()) + self.model_ema.copy_to(self.model) + if context is not None: + print(f"{context}: Switched to EMA weights") + try: + yield None + finally: + if self.use_ema: + self.model_ema.restore(self.model.parameters()) + if context is not None: + print(f"{context}: Restored training weights") + + @torch.no_grad() + def init_from_ckpt(self, path, ignore_keys=list(), only_model=False): + sd = torch.load(path, map_location="cpu") + if "state_dict" in list(sd.keys()): + sd = sd["state_dict"] + keys = list(sd.keys()) + for k in keys: + for ik in ignore_keys: + if k.startswith(ik): + print("Deleting key {} from state_dict.".format(k)) + del sd[k] + if self.make_it_fit: + n_params = len([name for name, _ in + itertools.chain(self.named_parameters(), + self.named_buffers())]) + for name, param in tqdm( + itertools.chain(self.named_parameters(), + self.named_buffers()), + desc="Fitting old weights to new weights", + total=n_params + ): + if not name in sd: + continue + old_shape = sd[name].shape + new_shape = param.shape + assert len(old_shape) == len(new_shape) + if len(new_shape) > 2: + # we only modify first two axes + assert new_shape[2:] == old_shape[2:] + # assumes first axis corresponds to output dim + if not new_shape == old_shape: + new_param = param.clone() + old_param = sd[name] + if len(new_shape) == 1: + for i in range(new_param.shape[0]): + new_param[i] = old_param[i % old_shape[0]] + elif len(new_shape) >= 2: + for i in range(new_param.shape[0]): + for j in range(new_param.shape[1]): + new_param[i, j] = old_param[i % old_shape[0], j % old_shape[1]] + + n_used_old = torch.ones(old_shape[1]) + for j in range(new_param.shape[1]): + n_used_old[j % old_shape[1]] += 1 + n_used_new = torch.zeros(new_shape[1]) + for j in range(new_param.shape[1]): + n_used_new[j] = n_used_old[j % old_shape[1]] + + n_used_new = n_used_new[None, :] + while len(n_used_new.shape) < len(new_shape): + n_used_new = n_used_new.unsqueeze(-1) + new_param /= n_used_new + + sd[name] = new_param + + missing, unexpected = self.load_state_dict(sd, strict=False) if not only_model else self.model.load_state_dict( + sd, strict=False) + print(f"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys") + if len(missing) > 0: + print(f"Missing Keys:\n {missing}") + if len(unexpected) > 0: + print(f"\nUnexpected Keys:\n {unexpected}") + + def q_mean_variance(self, x_start, t): + """ + Get the distribution q(x_t | x_0). + :param x_start: the [N x C x ...] tensor of noiseless inputs. + :param t: the number of diffusion steps (minus 1). Here, 0 means one step. + :return: A tuple (mean, variance, log_variance), all of x_start's shape. + """ + mean = (extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start) + variance = extract_into_tensor(1.0 - self.alphas_cumprod, t, x_start.shape) + log_variance = extract_into_tensor(self.log_one_minus_alphas_cumprod, t, x_start.shape) + return mean, variance, log_variance + + def predict_start_from_noise(self, x_t, t, noise): + return ( + extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - + extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) * noise + ) + + def predict_start_from_z_and_v(self, x_t, t, v): + # self.register_buffer('sqrt_alphas_cumprod', to_torch(np.sqrt(alphas_cumprod))) + # self.register_buffer('sqrt_one_minus_alphas_cumprod', to_torch(np.sqrt(1. - alphas_cumprod))) + return ( + extract_into_tensor(self.sqrt_alphas_cumprod, t, x_t.shape) * x_t - + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_t.shape) * v + ) + + def predict_eps_from_z_and_v(self, x_t, t, v): + return ( + extract_into_tensor(self.sqrt_alphas_cumprod, t, x_t.shape) * v + + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_t.shape) * x_t + ) + + def q_posterior(self, x_start, x_t, t): + posterior_mean = ( + extract_into_tensor(self.posterior_mean_coef1, t, x_t.shape) * x_start + + extract_into_tensor(self.posterior_mean_coef2, t, x_t.shape) * x_t + ) + posterior_variance = extract_into_tensor(self.posterior_variance, t, x_t.shape) + posterior_log_variance_clipped = extract_into_tensor(self.posterior_log_variance_clipped, t, x_t.shape) + return posterior_mean, posterior_variance, posterior_log_variance_clipped + + def p_mean_variance(self, x, t, clip_denoised: bool): + model_out = self.model(x, t) + if self.parameterization == "eps": + x_recon = self.predict_start_from_noise(x, t=t, noise=model_out) + elif self.parameterization == "x0": + x_recon = model_out + if clip_denoised: + x_recon.clamp_(-1., 1.) + + model_mean, posterior_variance, posterior_log_variance = self.q_posterior(x_start=x_recon, x_t=x, t=t) + return model_mean, posterior_variance, posterior_log_variance + + @torch.no_grad() + def p_sample(self, x, t, clip_denoised=True, repeat_noise=False): + b, *_, device = *x.shape, x.device + model_mean, _, model_log_variance = self.p_mean_variance(x=x, t=t, clip_denoised=clip_denoised) + noise = noise_like(x.shape, device, repeat_noise) + # no noise when t == 0 + nonzero_mask = (1 - (t == 0).float()).reshape(b, *((1,) * (len(x.shape) - 1))) + return model_mean + nonzero_mask * (0.5 * model_log_variance).exp() * noise + + @torch.no_grad() + def p_sample_loop(self, shape, return_intermediates=False): + device = self.betas.device + b = shape[0] + img = torch.randn(shape, device=device) + intermediates = [img] + for i in tqdm(reversed(range(0, self.num_timesteps)), desc='Sampling t', total=self.num_timesteps): + img = self.p_sample(img, torch.full((b,), i, device=device, dtype=torch.long), + clip_denoised=self.clip_denoised) + if i % self.log_every_t == 0 or i == self.num_timesteps - 1: + intermediates.append(img) + if return_intermediates: + return img, intermediates + return img + + @torch.no_grad() + def sample(self, batch_size=16, return_intermediates=False): + image_size = self.image_size + channels = self.channels + return self.p_sample_loop((batch_size, channels, image_size, image_size), + return_intermediates=return_intermediates) + + def q_sample(self, x_start, t, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + return (extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start + + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape) * noise) + + def get_v(self, x, noise, t): + return ( + extract_into_tensor(self.sqrt_alphas_cumprod, t, x.shape) * noise - + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x.shape) * x + ) + + def get_loss(self, pred, target, mean=True): + if self.loss_type == 'l1': + loss = (target - pred).abs() + if mean: + loss = loss.mean() + elif self.loss_type == 'l2': + if mean: + loss = torch.nn.functional.mse_loss(target, pred) + else: + loss = torch.nn.functional.mse_loss(target, pred, reduction='none') + else: + raise NotImplementedError("unknown loss type '{loss_type}'") + + return loss + + def p_losses(self, x_start, t, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + x_noisy = self.q_sample(x_start=x_start, t=t, noise=noise) + model_out = self.model(x_noisy, t) + + loss_dict = {} + if self.parameterization == "eps": + target = noise + elif self.parameterization == "x0": + target = x_start + elif self.parameterization == "v": + target = self.get_v(x_start, noise, t) + else: + raise NotImplementedError(f"Parameterization {self.parameterization} not yet supported") + + loss = self.get_loss(model_out, target, mean=False).mean(dim=[1, 2, 3]) + + log_prefix = 'train' if self.training else 'val' + + loss_dict.update({f'{log_prefix}/loss_simple': loss.mean()}) + loss_simple = loss.mean() * self.l_simple_weight + + loss_vlb = (self.lvlb_weights[t] * loss).mean() + loss_dict.update({f'{log_prefix}/loss_vlb': loss_vlb}) + + loss = loss_simple + self.original_elbo_weight * loss_vlb + + loss_dict.update({f'{log_prefix}/loss': loss}) + + return loss, loss_dict + + def forward(self, x, *args, **kwargs): + # b, c, h, w, device, img_size, = *x.shape, x.device, self.image_size + # assert h == img_size and w == img_size, f'height and width of image must be {img_size}' + t = torch.randint(0, self.num_timesteps, (x.shape[0],), device=self.device).long() + return self.p_losses(x, t, *args, **kwargs) + + def get_input(self, batch, k): + x = batch[k] + if len(x.shape) == 3: + x = x[..., None] + x = rearrange(x, 'b h w c -> b c h w') + x = x.to(memory_format=torch.contiguous_format).float() + return x + + def shared_step(self, batch): + x = self.get_input(batch, self.first_stage_key) + loss, loss_dict = self(x) + return loss, loss_dict + + def training_step(self, batch, batch_idx): + for k in self.ucg_training: + p = self.ucg_training[k]["p"] + val = self.ucg_training[k]["val"] + if val is None: + val = "" + for i in range(len(batch[k])): + if self.ucg_prng.choice(2, p=[1 - p, p]): + batch[k][i] = val + + loss, loss_dict = self.shared_step(batch) + + self.log_dict(loss_dict, prog_bar=True, + logger=True, on_step=True, on_epoch=True) + + self.log("global_step", self.global_step, + prog_bar=True, logger=True, on_step=True, on_epoch=False) + + if self.use_scheduler: + lr = self.optimizers().param_groups[0]['lr'] + self.log('lr_abs', lr, prog_bar=True, logger=True, on_step=True, on_epoch=False) + + return loss + + @torch.no_grad() + def validation_step(self, batch, batch_idx): + _, loss_dict_no_ema = self.shared_step(batch) + with self.ema_scope(): + _, loss_dict_ema = self.shared_step(batch) + loss_dict_ema = {key + '_ema': loss_dict_ema[key] for key in loss_dict_ema} + self.log_dict(loss_dict_no_ema, prog_bar=False, logger=True, on_step=False, on_epoch=True) + self.log_dict(loss_dict_ema, prog_bar=False, logger=True, on_step=False, on_epoch=True) + + def on_train_batch_end(self, *args, **kwargs): + if self.use_ema: + self.model_ema(self.model) + + def _get_rows_from_list(self, samples): + n_imgs_per_row = len(samples) + denoise_grid = rearrange(samples, 'n b c h w -> b n c h w') + denoise_grid = rearrange(denoise_grid, 'b n c h w -> (b n) c h w') + denoise_grid = make_grid(denoise_grid, nrow=n_imgs_per_row) + return denoise_grid + + @torch.no_grad() + def log_images(self, batch, N=8, n_row=2, sample=True, return_keys=None, **kwargs): + log = dict() + x = self.get_input(batch, self.first_stage_key) + N = min(x.shape[0], N) + n_row = min(x.shape[0], n_row) + x = x.to(self.device)[:N] + log["inputs"] = x + + # get diffusion row + diffusion_row = list() + x_start = x[:n_row] + + for t in range(self.num_timesteps): + if t % self.log_every_t == 0 or t == self.num_timesteps - 1: + t = repeat(torch.tensor([t]), '1 -> b', b=n_row) + t = t.to(self.device).long() + noise = torch.randn_like(x_start) + x_noisy = self.q_sample(x_start=x_start, t=t, noise=noise) + diffusion_row.append(x_noisy) + + log["diffusion_row"] = self._get_rows_from_list(diffusion_row) + + if sample: + # get denoise row + with self.ema_scope("Plotting"): + samples, denoise_row = self.sample(batch_size=N, return_intermediates=True) + + log["samples"] = samples + log["denoise_row"] = self._get_rows_from_list(denoise_row) + + if return_keys: + if np.intersect1d(list(log.keys()), return_keys).shape[0] == 0: + return log + else: + return {key: log[key] for key in return_keys} + return log + + def configure_optimizers(self): + lr = self.learning_rate + params = list(self.model.parameters()) + if self.learn_logvar: + params = params + [self.logvar] + opt = torch.optim.AdamW(params, lr=lr) + return opt + + +class LatentDiffusion(DDPM): + """main class""" + + def __init__(self, + first_stage_config, + cond_stage_config, + num_timesteps_cond=None, + cond_stage_key="image", + cond_stage_trainable=False, + concat_mode=True, + cond_stage_forward=None, + conditioning_key=None, + scale_factor=1.0, + scale_by_std=False, + force_null_conditioning=False, + *args, **kwargs): + self.force_null_conditioning = force_null_conditioning + self.num_timesteps_cond = default(num_timesteps_cond, 1) + self.scale_by_std = scale_by_std + assert self.num_timesteps_cond <= kwargs['timesteps'] + # for backwards compatibility after implementation of DiffusionWrapper + if conditioning_key is None: + conditioning_key = 'concat' if concat_mode else 'crossattn' + if cond_stage_config == '__is_unconditional__' and not self.force_null_conditioning: + conditioning_key = None + ckpt_path = kwargs.pop("ckpt_path", None) + reset_ema = kwargs.pop("reset_ema", False) + reset_num_ema_updates = kwargs.pop("reset_num_ema_updates", False) + ignore_keys = kwargs.pop("ignore_keys", []) + super().__init__(conditioning_key=conditioning_key, *args, **kwargs) + self.concat_mode = concat_mode + self.cond_stage_trainable = cond_stage_trainable + self.cond_stage_key = cond_stage_key + try: + self.num_downs = len(first_stage_config.params.ddconfig.ch_mult) - 1 + except: + self.num_downs = 0 + if not scale_by_std: + self.scale_factor = scale_factor + else: + self.register_buffer('scale_factor', torch.tensor(scale_factor)) + self.instantiate_first_stage(first_stage_config) + self.instantiate_cond_stage(cond_stage_config) + self.cond_stage_forward = cond_stage_forward + self.clip_denoised = False + self.bbox_tokenizer = None + + self.restarted_from_ckpt = False + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys) + self.restarted_from_ckpt = True + if reset_ema: + assert self.use_ema + print( + f"Resetting ema to pure model weights. This is useful when restoring from an ema-only checkpoint.") + self.model_ema = LitEma(self.model) + if reset_num_ema_updates: + print(" +++++++++++ WARNING: RESETTING NUM_EMA UPDATES TO ZERO +++++++++++ ") + assert self.use_ema + self.model_ema.reset_num_updates() + + def make_cond_schedule(self, ): + self.cond_ids = torch.full(size=(self.num_timesteps,), fill_value=self.num_timesteps - 1, dtype=torch.long) + ids = torch.round(torch.linspace(0, self.num_timesteps - 1, self.num_timesteps_cond)).long() + self.cond_ids[:self.num_timesteps_cond] = ids + + @rank_zero_only + @torch.no_grad() + def on_train_batch_start(self, batch, batch_idx, dataloader_idx): + # only for very first batch + if self.scale_by_std and self.current_epoch == 0 and self.global_step == 0 and batch_idx == 0 and not self.restarted_from_ckpt: + assert self.scale_factor == 1., 'rather not use custom rescaling and std-rescaling simultaneously' + # set rescale weight to 1./std of encodings + print("### USING STD-RESCALING ###") + x = super().get_input(batch, self.first_stage_key) + x = x.to(self.device) + encoder_posterior = self.encode_first_stage(x) + z = self.get_first_stage_encoding(encoder_posterior).detach() + del self.scale_factor + self.register_buffer('scale_factor', 1. / z.flatten().std()) + print(f"setting self.scale_factor to {self.scale_factor}") + print("### USING STD-RESCALING ###") + + def register_schedule(self, + given_betas=None, beta_schedule="linear", timesteps=1000, + linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + super().register_schedule(given_betas, beta_schedule, timesteps, linear_start, linear_end, cosine_s) + + self.shorten_cond_schedule = self.num_timesteps_cond > 1 + if self.shorten_cond_schedule: + self.make_cond_schedule() + + def instantiate_first_stage(self, config): + model = instantiate_from_config(config) + self.first_stage_model = model.eval() + self.first_stage_model.train = disabled_train + for param in self.first_stage_model.parameters(): + param.requires_grad = False + + def instantiate_cond_stage(self, config): + if not self.cond_stage_trainable: + if config == "__is_first_stage__": + print("Using first stage also as cond stage.") + self.cond_stage_model = self.first_stage_model + elif config == "__is_unconditional__": + print(f"Training {self.__class__.__name__} as an unconditional model.") + self.cond_stage_model = None + # self.be_unconditional = True + else: + model = instantiate_from_config(config) + self.cond_stage_model = model.eval() + self.cond_stage_model.train = disabled_train + for param in self.cond_stage_model.parameters(): + param.requires_grad = False + else: + assert config != '__is_first_stage__' + assert config != '__is_unconditional__' + model = instantiate_from_config(config) + self.cond_stage_model = model + + def _get_denoise_row_from_list(self, samples, desc='', force_no_decoder_quantization=False): + denoise_row = [] + for zd in tqdm(samples, desc=desc): + denoise_row.append(self.decode_first_stage(zd.to(self.device), + force_not_quantize=force_no_decoder_quantization)) + n_imgs_per_row = len(denoise_row) + denoise_row = torch.stack(denoise_row) # n_log_step, n_row, C, H, W + denoise_grid = rearrange(denoise_row, 'n b c h w -> b n c h w') + denoise_grid = rearrange(denoise_grid, 'b n c h w -> (b n) c h w') + denoise_grid = make_grid(denoise_grid, nrow=n_imgs_per_row) + return denoise_grid + + def get_first_stage_encoding(self, encoder_posterior): + if isinstance(encoder_posterior, DiagonalGaussianDistribution): + z = encoder_posterior.sample() + elif isinstance(encoder_posterior, torch.Tensor): + z = encoder_posterior + else: + raise NotImplementedError(f"encoder_posterior of type '{type(encoder_posterior)}' not yet implemented") + return self.scale_factor * z + + def get_learned_conditioning(self, c): + if self.cond_stage_forward is None: + if hasattr(self.cond_stage_model, 'encode') and callable(self.cond_stage_model.encode): + c = self.cond_stage_model.encode(c) + if isinstance(c, DiagonalGaussianDistribution): + c = c.mode() + else: + c = self.cond_stage_model(c) + else: + assert hasattr(self.cond_stage_model, self.cond_stage_forward) + c = getattr(self.cond_stage_model, self.cond_stage_forward)(c) + return c + + def meshgrid(self, h, w): + y = torch.arange(0, h).view(h, 1, 1).repeat(1, w, 1) + x = torch.arange(0, w).view(1, w, 1).repeat(h, 1, 1) + + arr = torch.cat([y, x], dim=-1) + return arr + + def delta_border(self, h, w): + """ + :param h: height + :param w: width + :return: normalized distance to image border, + wtith min distance = 0 at border and max dist = 0.5 at image center + """ + lower_right_corner = torch.tensor([h - 1, w - 1]).view(1, 1, 2) + arr = self.meshgrid(h, w) / lower_right_corner + dist_left_up = torch.min(arr, dim=-1, keepdims=True)[0] + dist_right_down = torch.min(1 - arr, dim=-1, keepdims=True)[0] + edge_dist = torch.min(torch.cat([dist_left_up, dist_right_down], dim=-1), dim=-1)[0] + return edge_dist + + def get_weighting(self, h, w, Ly, Lx, device): + weighting = self.delta_border(h, w) + weighting = torch.clip(weighting, self.split_input_params["clip_min_weight"], + self.split_input_params["clip_max_weight"], ) + weighting = weighting.view(1, h * w, 1).repeat(1, 1, Ly * Lx).to(device) + + if self.split_input_params["tie_braker"]: + L_weighting = self.delta_border(Ly, Lx) + L_weighting = torch.clip(L_weighting, + self.split_input_params["clip_min_tie_weight"], + self.split_input_params["clip_max_tie_weight"]) + + L_weighting = L_weighting.view(1, 1, Ly * Lx).to(device) + weighting = weighting * L_weighting + return weighting + + def get_fold_unfold(self, x, kernel_size, stride, uf=1, df=1): # todo load once not every time, shorten code + """ + :param x: img of size (bs, c, h, w) + :return: n img crops of size (n, bs, c, kernel_size[0], kernel_size[1]) + """ + bs, nc, h, w = x.shape + + # number of crops in image + Ly = (h - kernel_size[0]) // stride[0] + 1 + Lx = (w - kernel_size[1]) // stride[1] + 1 + + if uf == 1 and df == 1: + fold_params = dict(kernel_size=kernel_size, dilation=1, padding=0, stride=stride) + unfold = torch.nn.Unfold(**fold_params) + + fold = torch.nn.Fold(output_size=x.shape[2:], **fold_params) + + weighting = self.get_weighting(kernel_size[0], kernel_size[1], Ly, Lx, x.device).to(x.dtype) + normalization = fold(weighting).view(1, 1, h, w) # normalizes the overlap + weighting = weighting.view((1, 1, kernel_size[0], kernel_size[1], Ly * Lx)) + + elif uf > 1 and df == 1: + fold_params = dict(kernel_size=kernel_size, dilation=1, padding=0, stride=stride) + unfold = torch.nn.Unfold(**fold_params) + + fold_params2 = dict(kernel_size=(kernel_size[0] * uf, kernel_size[0] * uf), + dilation=1, padding=0, + stride=(stride[0] * uf, stride[1] * uf)) + fold = torch.nn.Fold(output_size=(x.shape[2] * uf, x.shape[3] * uf), **fold_params2) + + weighting = self.get_weighting(kernel_size[0] * uf, kernel_size[1] * uf, Ly, Lx, x.device).to(x.dtype) + normalization = fold(weighting).view(1, 1, h * uf, w * uf) # normalizes the overlap + weighting = weighting.view((1, 1, kernel_size[0] * uf, kernel_size[1] * uf, Ly * Lx)) + + elif df > 1 and uf == 1: + fold_params = dict(kernel_size=kernel_size, dilation=1, padding=0, stride=stride) + unfold = torch.nn.Unfold(**fold_params) + + fold_params2 = dict(kernel_size=(kernel_size[0] // df, kernel_size[0] // df), + dilation=1, padding=0, + stride=(stride[0] // df, stride[1] // df)) + fold = torch.nn.Fold(output_size=(x.shape[2] // df, x.shape[3] // df), **fold_params2) + + weighting = self.get_weighting(kernel_size[0] // df, kernel_size[1] // df, Ly, Lx, x.device).to(x.dtype) + normalization = fold(weighting).view(1, 1, h // df, w // df) # normalizes the overlap + weighting = weighting.view((1, 1, kernel_size[0] // df, kernel_size[1] // df, Ly * Lx)) + + else: + raise NotImplementedError + + return fold, unfold, normalization, weighting + + @torch.no_grad() + def get_input(self, batch, k, return_first_stage_outputs=False, force_c_encode=False, + cond_key=None, return_original_cond=False, bs=None, return_x=False): + x = super().get_input(batch, k) + if bs is not None: + x = x[:bs] + x = x.to(self.device) + encoder_posterior = self.encode_first_stage(x) + z = self.get_first_stage_encoding(encoder_posterior).detach() + + if self.model.conditioning_key is not None and not self.force_null_conditioning: + if cond_key is None: + cond_key = self.cond_stage_key + if cond_key != self.first_stage_key: + if cond_key in ['caption', 'coordinates_bbox', "txt"]: + xc = batch[cond_key] + elif cond_key in ['class_label', 'cls']: + xc = batch + else: + xc = super().get_input(batch, cond_key).to(self.device) + else: + xc = x + if not self.cond_stage_trainable or force_c_encode: + if isinstance(xc, dict) or isinstance(xc, list): + c = self.get_learned_conditioning(xc) + else: + c = self.get_learned_conditioning(xc.to(self.device)) + else: + c = xc + if bs is not None: + c = c[:bs] + + if self.use_positional_encodings: + pos_x, pos_y = self.compute_latent_shifts(batch) + ckey = __conditioning_keys__[self.model.conditioning_key] + c = {ckey: c, 'pos_x': pos_x, 'pos_y': pos_y} + + else: + c = None + xc = None + if self.use_positional_encodings: + pos_x, pos_y = self.compute_latent_shifts(batch) + c = {'pos_x': pos_x, 'pos_y': pos_y} + out = [z, c] + if return_first_stage_outputs: + xrec = self.decode_first_stage(z) + out.extend([x, xrec]) + if return_x: + out.extend([x]) + if return_original_cond: + out.append(xc) + return out + + @torch.no_grad() + def decode_first_stage(self, z, predict_cids=False, force_not_quantize=False): + if predict_cids: + if z.dim() == 4: + z = torch.argmax(z.exp(), dim=1).long() + z = self.first_stage_model.quantize.get_codebook_entry(z, shape=None) + z = rearrange(z, 'b h w c -> b c h w').contiguous() + + z = 1. / self.scale_factor * z + return self.first_stage_model.decode(z) + + @torch.no_grad() + def encode_first_stage(self, x): + return self.first_stage_model.encode(x) + + def shared_step(self, batch, **kwargs): + x, c = self.get_input(batch, self.first_stage_key) + loss = self(x, c) + return loss + + def forward(self, x, c, *args, **kwargs): + t = torch.randint(0, self.num_timesteps, (x.shape[0],), device=self.device).long() + if self.model.conditioning_key is not None: + assert c is not None + if self.cond_stage_trainable: + c = self.get_learned_conditioning(c) + if self.shorten_cond_schedule: # TODO: drop this option + tc = self.cond_ids[t].to(self.device) + c = self.q_sample(x_start=c, t=tc, noise=torch.randn_like(c.float())) + return self.p_losses(x, c, t, *args, **kwargs) + + def apply_model(self, x_noisy, t, cond, return_ids=False): + if isinstance(cond, dict): + # hybrid case, cond is expected to be a dict + pass + else: + if not isinstance(cond, list): + cond = [cond] + key = 'c_concat' if self.model.conditioning_key == 'concat' else 'c_crossattn' + cond = {key: cond} + + x_recon = self.model(x_noisy, t, **cond) + + if isinstance(x_recon, tuple) and not return_ids: + return x_recon[0] + else: + return x_recon + + def _predict_eps_from_xstart(self, x_t, t, pred_xstart): + return (extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - pred_xstart) / \ + extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) + + def _prior_bpd(self, x_start): + """ + Get the prior KL term for the variational lower-bound, measured in + bits-per-dim. + This term can't be optimized, as it only depends on the encoder. + :param x_start: the [N x C x ...] tensor of inputs. + :return: a batch of [N] KL values (in bits), one per batch element. + """ + batch_size = x_start.shape[0] + t = torch.tensor([self.num_timesteps - 1] * batch_size, device=x_start.device) + qt_mean, _, qt_log_variance = self.q_mean_variance(x_start, t) + kl_prior = normal_kl(mean1=qt_mean, logvar1=qt_log_variance, mean2=0.0, logvar2=0.0) + return mean_flat(kl_prior) / np.log(2.0) + + def p_losses(self, x_start, cond, t, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + x_noisy = self.q_sample(x_start=x_start, t=t, noise=noise) + model_output = self.apply_model(x_noisy, t, cond) + + loss_dict = {} + prefix = 'train' if self.training else 'val' + + if self.parameterization == "x0": + target = x_start + elif self.parameterization == "eps": + target = noise + elif self.parameterization == "v": + target = self.get_v(x_start, noise, t) + else: + raise NotImplementedError() + + loss_simple = self.get_loss(model_output, target, mean=False).mean([1, 2, 3]) + loss_dict.update({f'{prefix}/loss_simple': loss_simple.mean()}) + + logvar_t = self.logvar[t].to(self.device) + loss = loss_simple / torch.exp(logvar_t) + logvar_t + # loss = loss_simple / torch.exp(self.logvar) + self.logvar + if self.learn_logvar: + loss_dict.update({f'{prefix}/loss_gamma': loss.mean()}) + loss_dict.update({'logvar': self.logvar.data.mean()}) + + loss = self.l_simple_weight * loss.mean() + + loss_vlb = self.get_loss(model_output, target, mean=False).mean(dim=(1, 2, 3)) + loss_vlb = (self.lvlb_weights[t] * loss_vlb).mean() + loss_dict.update({f'{prefix}/loss_vlb': loss_vlb}) + loss += (self.original_elbo_weight * loss_vlb) + loss_dict.update({f'{prefix}/loss': loss}) + + return loss, loss_dict + + def p_mean_variance(self, x, c, t, clip_denoised: bool, return_codebook_ids=False, quantize_denoised=False, + return_x0=False, score_corrector=None, corrector_kwargs=None): + t_in = t + model_out = self.apply_model(x, t_in, c, return_ids=return_codebook_ids) + + if score_corrector is not None: + assert self.parameterization == "eps" + model_out = score_corrector.modify_score(self, model_out, x, t, c, **corrector_kwargs) + + if return_codebook_ids: + model_out, logits = model_out + + if self.parameterization == "eps": + x_recon = self.predict_start_from_noise(x, t=t, noise=model_out) + elif self.parameterization == "x0": + x_recon = model_out + else: + raise NotImplementedError() + + if clip_denoised: + x_recon.clamp_(-1., 1.) + if quantize_denoised: + x_recon, _, [_, _, indices] = self.first_stage_model.quantize(x_recon) + model_mean, posterior_variance, posterior_log_variance = self.q_posterior(x_start=x_recon, x_t=x, t=t) + if return_codebook_ids: + return model_mean, posterior_variance, posterior_log_variance, logits + elif return_x0: + return model_mean, posterior_variance, posterior_log_variance, x_recon + else: + return model_mean, posterior_variance, posterior_log_variance + + @torch.no_grad() + def p_sample(self, x, c, t, clip_denoised=False, repeat_noise=False, + return_codebook_ids=False, quantize_denoised=False, return_x0=False, + temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None): + b, *_, device = *x.shape, x.device + outputs = self.p_mean_variance(x=x, c=c, t=t, clip_denoised=clip_denoised, + return_codebook_ids=return_codebook_ids, + quantize_denoised=quantize_denoised, + return_x0=return_x0, + score_corrector=score_corrector, corrector_kwargs=corrector_kwargs) + if return_codebook_ids: + raise DeprecationWarning("Support dropped.") + model_mean, _, model_log_variance, logits = outputs + elif return_x0: + model_mean, _, model_log_variance, x0 = outputs + else: + model_mean, _, model_log_variance = outputs + + noise = noise_like(x.shape, device, repeat_noise) * temperature + if noise_dropout > 0.: + noise = torch.nn.functional.dropout(noise, p=noise_dropout) + # no noise when t == 0 + nonzero_mask = (1 - (t == 0).float()).reshape(b, *((1,) * (len(x.shape) - 1))) + + if return_codebook_ids: + return model_mean + nonzero_mask * (0.5 * model_log_variance).exp() * noise, logits.argmax(dim=1) + if return_x0: + return model_mean + nonzero_mask * (0.5 * model_log_variance).exp() * noise, x0 + else: + return model_mean + nonzero_mask * (0.5 * model_log_variance).exp() * noise + + @torch.no_grad() + def progressive_denoising(self, cond, shape, verbose=True, callback=None, quantize_denoised=False, + img_callback=None, mask=None, x0=None, temperature=1., noise_dropout=0., + score_corrector=None, corrector_kwargs=None, batch_size=None, x_T=None, start_T=None, + log_every_t=None): + if not log_every_t: + log_every_t = self.log_every_t + timesteps = self.num_timesteps + if batch_size is not None: + b = batch_size if batch_size is not None else shape[0] + shape = [batch_size] + list(shape) + else: + b = batch_size = shape[0] + if x_T is None: + img = torch.randn(shape, device=self.device) + else: + img = x_T + intermediates = [] + if cond is not None: + if isinstance(cond, dict): + cond = {key: cond[key][:batch_size] if not isinstance(cond[key], list) else + list(map(lambda x: x[:batch_size], cond[key])) for key in cond} + else: + cond = [c[:batch_size] for c in cond] if isinstance(cond, list) else cond[:batch_size] + + if start_T is not None: + timesteps = min(timesteps, start_T) + iterator = tqdm(reversed(range(0, timesteps)), desc='Progressive Generation', + total=timesteps) if verbose else reversed( + range(0, timesteps)) + if type(temperature) == float: + temperature = [temperature] * timesteps + + for i in iterator: + ts = torch.full((b,), i, device=self.device, dtype=torch.long) + if self.shorten_cond_schedule: + assert self.model.conditioning_key != 'hybrid' + tc = self.cond_ids[ts].to(cond.device) + cond = self.q_sample(x_start=cond, t=tc, noise=torch.randn_like(cond)) + + img, x0_partial = self.p_sample(img, cond, ts, + clip_denoised=self.clip_denoised, + quantize_denoised=quantize_denoised, return_x0=True, + temperature=temperature[i], noise_dropout=noise_dropout, + score_corrector=score_corrector, corrector_kwargs=corrector_kwargs) + if mask is not None: + assert x0 is not None + img_orig = self.q_sample(x0, ts) + img = img_orig * mask + (1. - mask) * img + + if i % log_every_t == 0 or i == timesteps - 1: + intermediates.append(x0_partial) + if callback: callback(i) + if img_callback: img_callback(img, i) + return img, intermediates + + @torch.no_grad() + def p_sample_loop(self, cond, shape, return_intermediates=False, + x_T=None, verbose=True, callback=None, timesteps=None, quantize_denoised=False, + mask=None, x0=None, img_callback=None, start_T=None, + log_every_t=None): + + if not log_every_t: + log_every_t = self.log_every_t + device = self.betas.device + b = shape[0] + if x_T is None: + img = torch.randn(shape, device=device) + else: + img = x_T + + intermediates = [img] + if timesteps is None: + timesteps = self.num_timesteps + + if start_T is not None: + timesteps = min(timesteps, start_T) + iterator = tqdm(reversed(range(0, timesteps)), desc='Sampling t', total=timesteps) if verbose else reversed( + range(0, timesteps)) + + if mask is not None: + assert x0 is not None + assert x0.shape[2:3] == mask.shape[2:3] # spatial size has to match + + for i in iterator: + ts = torch.full((b,), i, device=device, dtype=torch.long) + if self.shorten_cond_schedule: + assert self.model.conditioning_key != 'hybrid' + tc = self.cond_ids[ts].to(cond.device) + cond = self.q_sample(x_start=cond, t=tc, noise=torch.randn_like(cond)) + + img = self.p_sample(img, cond, ts, + clip_denoised=self.clip_denoised, + quantize_denoised=quantize_denoised) + if mask is not None: + img_orig = self.q_sample(x0, ts) + img = img_orig * mask + (1. - mask) * img + + if i % log_every_t == 0 or i == timesteps - 1: + intermediates.append(img) + if callback: callback(i) + if img_callback: img_callback(img, i) + + if return_intermediates: + return img, intermediates + return img + + @torch.no_grad() + def sample(self, cond, batch_size=16, return_intermediates=False, x_T=None, + verbose=True, timesteps=None, quantize_denoised=False, + mask=None, x0=None, shape=None, **kwargs): + if shape is None: + shape = (batch_size, self.channels, self.image_size, self.image_size) + if cond is not None: + if isinstance(cond, dict): + cond = {key: cond[key][:batch_size] if not isinstance(cond[key], list) else + list(map(lambda x: x[:batch_size], cond[key])) for key in cond} + else: + cond = [c[:batch_size] for c in cond] if isinstance(cond, list) else cond[:batch_size] + return self.p_sample_loop(cond, + shape, + return_intermediates=return_intermediates, x_T=x_T, + verbose=verbose, timesteps=timesteps, quantize_denoised=quantize_denoised, + mask=mask, x0=x0) + + @torch.no_grad() + def sample_log(self, cond, batch_size, ddim, ddim_steps, **kwargs): + if ddim: + ddim_sampler = DDIMSampler(self) + shape = (self.channels, self.image_size, self.image_size) + samples, intermediates = ddim_sampler.sample(ddim_steps, batch_size, + shape, cond, verbose=False, **kwargs) + + else: + samples, intermediates = self.sample(cond=cond, batch_size=batch_size, + return_intermediates=True, **kwargs) + + return samples, intermediates + + @torch.no_grad() + def get_unconditional_conditioning(self, batch_size, null_label=None): + if null_label is not None: + xc = null_label + if isinstance(xc, ListConfig): + xc = list(xc) + if isinstance(xc, dict) or isinstance(xc, list): + c = self.get_learned_conditioning(xc) + else: + if hasattr(xc, "to"): + xc = xc.to(self.device) + c = self.get_learned_conditioning(xc) + else: + if self.cond_stage_key in ["class_label", "cls"]: + xc = self.cond_stage_model.get_unconditional_conditioning(batch_size, device=self.device) + return self.get_learned_conditioning(xc) + else: + raise NotImplementedError("todo") + if isinstance(c, list): # in case the encoder gives us a list + for i in range(len(c)): + c[i] = repeat(c[i], '1 ... -> b ...', b=batch_size).to(self.device) + else: + c = repeat(c, '1 ... -> b ...', b=batch_size).to(self.device) + return c + + @torch.no_grad() + def log_images(self, batch, N=8, n_row=4, sample=True, ddim_steps=50, ddim_eta=0., return_keys=None, + quantize_denoised=True, inpaint=True, plot_denoise_rows=False, plot_progressive_rows=True, + plot_diffusion_rows=True, unconditional_guidance_scale=1., unconditional_guidance_label=None, + use_ema_scope=True, + **kwargs): + ema_scope = self.ema_scope if use_ema_scope else nullcontext + use_ddim = ddim_steps is not None + + log = dict() + z, c, x, xrec, xc = self.get_input(batch, self.first_stage_key, + return_first_stage_outputs=True, + force_c_encode=True, + return_original_cond=True, + bs=N) + N = min(x.shape[0], N) + n_row = min(x.shape[0], n_row) + log["inputs"] = x + log["reconstruction"] = xrec + if self.model.conditioning_key is not None: + if hasattr(self.cond_stage_model, "decode"): + xc = self.cond_stage_model.decode(c) + log["conditioning"] = xc + elif self.cond_stage_key in ["caption", "txt"]: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch[self.cond_stage_key], size=x.shape[2] // 25) + log["conditioning"] = xc + elif self.cond_stage_key in ['class_label', "cls"]: + try: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch["human_label"], size=x.shape[2] // 25) + log['conditioning'] = xc + except KeyError: + # probably no "human_label" in batch + pass + elif isimage(xc): + log["conditioning"] = xc + if ismap(xc): + log["original_conditioning"] = self.to_rgb(xc) + + if plot_diffusion_rows: + # get diffusion row + diffusion_row = list() + z_start = z[:n_row] + for t in range(self.num_timesteps): + if t % self.log_every_t == 0 or t == self.num_timesteps - 1: + t = repeat(torch.tensor([t]), '1 -> b', b=n_row) + t = t.to(self.device).long() + noise = torch.randn_like(z_start) + z_noisy = self.q_sample(x_start=z_start, t=t, noise=noise) + diffusion_row.append(self.decode_first_stage(z_noisy)) + + diffusion_row = torch.stack(diffusion_row) # n_log_step, n_row, C, H, W + diffusion_grid = rearrange(diffusion_row, 'n b c h w -> b n c h w') + diffusion_grid = rearrange(diffusion_grid, 'b n c h w -> (b n) c h w') + diffusion_grid = make_grid(diffusion_grid, nrow=diffusion_row.shape[0]) + log["diffusion_row"] = diffusion_grid + + if sample: + # get denoise row + with ema_scope("Sampling"): + samples, z_denoise_row = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta) + # samples, z_denoise_row = self.sample(cond=c, batch_size=N, return_intermediates=True) + x_samples = self.decode_first_stage(samples) + log["samples"] = x_samples + if plot_denoise_rows: + denoise_grid = self._get_denoise_row_from_list(z_denoise_row) + log["denoise_row"] = denoise_grid + + if quantize_denoised and not isinstance(self.first_stage_model, AutoencoderKL) and not isinstance( + self.first_stage_model, IdentityFirstStage): + # also display when quantizing x0 while sampling + with ema_scope("Plotting Quantized Denoised"): + samples, z_denoise_row = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta, + quantize_denoised=True) + # samples, z_denoise_row = self.sample(cond=c, batch_size=N, return_intermediates=True, + # quantize_denoised=True) + x_samples = self.decode_first_stage(samples.to(self.device)) + log["samples_x0_quantized"] = x_samples + + if unconditional_guidance_scale > 1.0: + uc = self.get_unconditional_conditioning(N, unconditional_guidance_label) + if self.model.conditioning_key == "crossattn-adm": + uc = {"c_crossattn": [uc], "c_adm": c["c_adm"]} + with ema_scope("Sampling with classifier-free guidance"): + samples_cfg, _ = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=uc, + ) + x_samples_cfg = self.decode_first_stage(samples_cfg) + log[f"samples_cfg_scale_{unconditional_guidance_scale:.2f}"] = x_samples_cfg + + if inpaint: + # make a simple center square + b, h, w = z.shape[0], z.shape[2], z.shape[3] + mask = torch.ones(N, h, w).to(self.device) + # zeros will be filled in + mask[:, h // 4:3 * h // 4, w // 4:3 * w // 4] = 0. + mask = mask[:, None, ...] + with ema_scope("Plotting Inpaint"): + samples, _ = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, eta=ddim_eta, + ddim_steps=ddim_steps, x0=z[:N], mask=mask) + x_samples = self.decode_first_stage(samples.to(self.device)) + log["samples_inpainting"] = x_samples + log["mask"] = mask + + # outpaint + mask = 1. - mask + with ema_scope("Plotting Outpaint"): + samples, _ = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, eta=ddim_eta, + ddim_steps=ddim_steps, x0=z[:N], mask=mask) + x_samples = self.decode_first_stage(samples.to(self.device)) + log["samples_outpainting"] = x_samples + + if plot_progressive_rows: + with ema_scope("Plotting Progressives"): + img, progressives = self.progressive_denoising(c, + shape=(self.channels, self.image_size, self.image_size), + batch_size=N) + prog_row = self._get_denoise_row_from_list(progressives, desc="Progressive Generation") + log["progressive_row"] = prog_row + + if return_keys: + if np.intersect1d(list(log.keys()), return_keys).shape[0] == 0: + return log + else: + return {key: log[key] for key in return_keys} + return log + + def configure_optimizers(self): + lr = self.learning_rate + params = list(self.model.parameters()) + if self.cond_stage_trainable: + print(f"{self.__class__.__name__}: Also optimizing conditioner params!") + params = params + list(self.cond_stage_model.parameters()) + if self.learn_logvar: + print('Diffusion model optimizing logvar') + params.append(self.logvar) + opt = torch.optim.AdamW(params, lr=lr) + if self.use_scheduler: + assert 'target' in self.scheduler_config + scheduler = instantiate_from_config(self.scheduler_config) + + print("Setting up LambdaLR scheduler...") + scheduler = [ + { + 'scheduler': LambdaLR(opt, lr_lambda=scheduler.schedule), + 'interval': 'step', + 'frequency': 1 + }] + return [opt], scheduler + return opt + + @torch.no_grad() + def to_rgb(self, x): + x = x.float() + if not hasattr(self, "colorize"): + self.colorize = torch.randn(3, x.shape[1], 1, 1).to(x) + x = nn.functional.conv2d(x, weight=self.colorize) + x = 2. * (x - x.min()) / (x.max() - x.min()) - 1. + return x + + +class DiffusionWrapper(pl.LightningModule): + def __init__(self, diff_model_config, conditioning_key): + super().__init__() + self.sequential_cross_attn = diff_model_config.pop("sequential_crossattn", False) + self.diffusion_model = instantiate_from_config(diff_model_config) + self.conditioning_key = conditioning_key + assert self.conditioning_key in [None, 'concat', 'crossattn', 'hybrid', 'adm', 'hybrid-adm', 'crossattn-adm'] + + def forward(self, x, t, c_concat: list = None, c_crossattn: list = None, c_adm=None): + if self.conditioning_key is None: + out = self.diffusion_model(x, t) + elif self.conditioning_key == 'concat': + xc = torch.cat([x] + c_concat, dim=1) + out = self.diffusion_model(xc, t) + elif self.conditioning_key == 'crossattn': + if not self.sequential_cross_attn: + cc = torch.cat(c_crossattn, 1) + else: + cc = c_crossattn + out = self.diffusion_model(x, t, context=cc) + elif self.conditioning_key == 'hybrid': + xc = torch.cat([x] + c_concat, dim=1) + cc = torch.cat(c_crossattn, 1) + out = self.diffusion_model(xc, t, context=cc) + elif self.conditioning_key == 'hybrid-adm': + assert c_adm is not None + xc = torch.cat([x] + c_concat, dim=1) + cc = torch.cat(c_crossattn, 1) + out = self.diffusion_model(xc, t, context=cc, y=c_adm) + elif self.conditioning_key == 'crossattn-adm': + assert c_adm is not None + cc = torch.cat(c_crossattn, 1) + out = self.diffusion_model(x, t, context=cc, y=c_adm) + elif self.conditioning_key == 'adm': + cc = c_crossattn[0] + out = self.diffusion_model(x, t, y=cc) + else: + raise NotImplementedError() + + return out + + +class LatentUpscaleDiffusion(LatentDiffusion): + def __init__(self, *args, low_scale_config, low_scale_key="LR", noise_level_key=None, **kwargs): + super().__init__(*args, **kwargs) + # assumes that neither the cond_stage nor the low_scale_model contain trainable params + assert not self.cond_stage_trainable + self.instantiate_low_stage(low_scale_config) + self.low_scale_key = low_scale_key + self.noise_level_key = noise_level_key + + def instantiate_low_stage(self, config): + model = instantiate_from_config(config) + self.low_scale_model = model.eval() + self.low_scale_model.train = disabled_train + for param in self.low_scale_model.parameters(): + param.requires_grad = False + + @torch.no_grad() + def get_input(self, batch, k, cond_key=None, bs=None, log_mode=False): + if not log_mode: + z, c = super().get_input(batch, k, force_c_encode=True, bs=bs) + else: + z, c, x, xrec, xc = super().get_input(batch, self.first_stage_key, return_first_stage_outputs=True, + force_c_encode=True, return_original_cond=True, bs=bs) + x_low = batch[self.low_scale_key][:bs] + x_low = rearrange(x_low, 'b h w c -> b c h w') + x_low = x_low.to(memory_format=torch.contiguous_format).float() + zx, noise_level = self.low_scale_model(x_low) + if self.noise_level_key is not None: + # get noise level from batch instead, e.g. when extracting a custom noise level for bsr + raise NotImplementedError('TODO') + + all_conds = {"c_concat": [zx], "c_crossattn": [c], "c_adm": noise_level} + if log_mode: + # TODO: maybe disable if too expensive + x_low_rec = self.low_scale_model.decode(zx) + return z, all_conds, x, xrec, xc, x_low, x_low_rec, noise_level + return z, all_conds + + @torch.no_grad() + def log_images(self, batch, N=8, n_row=4, sample=True, ddim_steps=200, ddim_eta=1., return_keys=None, + plot_denoise_rows=False, plot_progressive_rows=True, plot_diffusion_rows=True, + unconditional_guidance_scale=1., unconditional_guidance_label=None, use_ema_scope=True, + **kwargs): + ema_scope = self.ema_scope if use_ema_scope else nullcontext + use_ddim = ddim_steps is not None + + log = dict() + z, c, x, xrec, xc, x_low, x_low_rec, noise_level = self.get_input(batch, self.first_stage_key, bs=N, + log_mode=True) + N = min(x.shape[0], N) + n_row = min(x.shape[0], n_row) + log["inputs"] = x + log["reconstruction"] = xrec + log["x_lr"] = x_low + log[f"x_lr_rec_@noise_levels{'-'.join(map(lambda x: str(x), list(noise_level.cpu().numpy())))}"] = x_low_rec + if self.model.conditioning_key is not None: + if hasattr(self.cond_stage_model, "decode"): + xc = self.cond_stage_model.decode(c) + log["conditioning"] = xc + elif self.cond_stage_key in ["caption", "txt"]: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch[self.cond_stage_key], size=x.shape[2] // 25) + log["conditioning"] = xc + elif self.cond_stage_key in ['class_label', 'cls']: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch["human_label"], size=x.shape[2] // 25) + log['conditioning'] = xc + elif isimage(xc): + log["conditioning"] = xc + if ismap(xc): + log["original_conditioning"] = self.to_rgb(xc) + + if plot_diffusion_rows: + # get diffusion row + diffusion_row = list() + z_start = z[:n_row] + for t in range(self.num_timesteps): + if t % self.log_every_t == 0 or t == self.num_timesteps - 1: + t = repeat(torch.tensor([t]), '1 -> b', b=n_row) + t = t.to(self.device).long() + noise = torch.randn_like(z_start) + z_noisy = self.q_sample(x_start=z_start, t=t, noise=noise) + diffusion_row.append(self.decode_first_stage(z_noisy)) + + diffusion_row = torch.stack(diffusion_row) # n_log_step, n_row, C, H, W + diffusion_grid = rearrange(diffusion_row, 'n b c h w -> b n c h w') + diffusion_grid = rearrange(diffusion_grid, 'b n c h w -> (b n) c h w') + diffusion_grid = make_grid(diffusion_grid, nrow=diffusion_row.shape[0]) + log["diffusion_row"] = diffusion_grid + + if sample: + # get denoise row + with ema_scope("Sampling"): + samples, z_denoise_row = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta) + # samples, z_denoise_row = self.sample(cond=c, batch_size=N, return_intermediates=True) + x_samples = self.decode_first_stage(samples) + log["samples"] = x_samples + if plot_denoise_rows: + denoise_grid = self._get_denoise_row_from_list(z_denoise_row) + log["denoise_row"] = denoise_grid + + if unconditional_guidance_scale > 1.0: + uc_tmp = self.get_unconditional_conditioning(N, unconditional_guidance_label) + # TODO explore better "unconditional" choices for the other keys + # maybe guide away from empty text label and highest noise level and maximally degraded zx? + uc = dict() + for k in c: + if k == "c_crossattn": + assert isinstance(c[k], list) and len(c[k]) == 1 + uc[k] = [uc_tmp] + elif k == "c_adm": # todo: only run with text-based guidance? + assert isinstance(c[k], torch.Tensor) + #uc[k] = torch.ones_like(c[k]) * self.low_scale_model.max_noise_level + uc[k] = c[k] + elif isinstance(c[k], list): + uc[k] = [c[k][i] for i in range(len(c[k]))] + else: + uc[k] = c[k] + + with ema_scope("Sampling with classifier-free guidance"): + samples_cfg, _ = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=uc, + ) + x_samples_cfg = self.decode_first_stage(samples_cfg) + log[f"samples_cfg_scale_{unconditional_guidance_scale:.2f}"] = x_samples_cfg + + if plot_progressive_rows: + with ema_scope("Plotting Progressives"): + img, progressives = self.progressive_denoising(c, + shape=(self.channels, self.image_size, self.image_size), + batch_size=N) + prog_row = self._get_denoise_row_from_list(progressives, desc="Progressive Generation") + log["progressive_row"] = prog_row + + return log + + +class LatentFinetuneDiffusion(LatentDiffusion): + """ + Basis for different finetunas, such as inpainting or depth2image + To disable finetuning mode, set finetune_keys to None + """ + + def __init__(self, + concat_keys: tuple, + finetune_keys=("model.diffusion_model.input_blocks.0.0.weight", + "model_ema.diffusion_modelinput_blocks00weight" + ), + keep_finetune_dims=4, + # if model was trained without concat mode before and we would like to keep these channels + c_concat_log_start=None, # to log reconstruction of c_concat codes + c_concat_log_end=None, + *args, **kwargs + ): + ckpt_path = kwargs.pop("ckpt_path", None) + ignore_keys = kwargs.pop("ignore_keys", list()) + super().__init__(*args, **kwargs) + self.finetune_keys = finetune_keys + self.concat_keys = concat_keys + self.keep_dims = keep_finetune_dims + self.c_concat_log_start = c_concat_log_start + self.c_concat_log_end = c_concat_log_end + if exists(self.finetune_keys): assert exists(ckpt_path), 'can only finetune from a given checkpoint' + if exists(ckpt_path): + self.init_from_ckpt(ckpt_path, ignore_keys) + + def init_from_ckpt(self, path, ignore_keys=list(), only_model=False): + sd = torch.load(path, map_location="cpu") + if "state_dict" in list(sd.keys()): + sd = sd["state_dict"] + keys = list(sd.keys()) + for k in keys: + for ik in ignore_keys: + if k.startswith(ik): + print("Deleting key {} from state_dict.".format(k)) + del sd[k] + + # make it explicit, finetune by including extra input channels + if exists(self.finetune_keys) and k in self.finetune_keys: + new_entry = None + for name, param in self.named_parameters(): + if name in self.finetune_keys: + print( + f"modifying key '{name}' and keeping its original {self.keep_dims} (channels) dimensions only") + new_entry = torch.zeros_like(param) # zero init + assert exists(new_entry), 'did not find matching parameter to modify' + new_entry[:, :self.keep_dims, ...] = sd[k] + sd[k] = new_entry + + missing, unexpected = self.load_state_dict(sd, strict=False) if not only_model else self.model.load_state_dict( + sd, strict=False) + print(f"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys") + if len(missing) > 0: + print(f"Missing Keys: {missing}") + if len(unexpected) > 0: + print(f"Unexpected Keys: {unexpected}") + + @torch.no_grad() + def log_images(self, batch, N=8, n_row=4, sample=True, ddim_steps=200, ddim_eta=1., return_keys=None, + quantize_denoised=True, inpaint=True, plot_denoise_rows=False, plot_progressive_rows=True, + plot_diffusion_rows=True, unconditional_guidance_scale=1., unconditional_guidance_label=None, + use_ema_scope=True, + **kwargs): + ema_scope = self.ema_scope if use_ema_scope else nullcontext + use_ddim = ddim_steps is not None + + log = dict() + z, c, x, xrec, xc = self.get_input(batch, self.first_stage_key, bs=N, return_first_stage_outputs=True) + c_cat, c = c["c_concat"][0], c["c_crossattn"][0] + N = min(x.shape[0], N) + n_row = min(x.shape[0], n_row) + log["inputs"] = x + log["reconstruction"] = xrec + if self.model.conditioning_key is not None: + if hasattr(self.cond_stage_model, "decode"): + xc = self.cond_stage_model.decode(c) + log["conditioning"] = xc + elif self.cond_stage_key in ["caption", "txt"]: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch[self.cond_stage_key], size=x.shape[2] // 25) + log["conditioning"] = xc + elif self.cond_stage_key in ['class_label', 'cls']: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch["human_label"], size=x.shape[2] // 25) + log['conditioning'] = xc + elif isimage(xc): + log["conditioning"] = xc + if ismap(xc): + log["original_conditioning"] = self.to_rgb(xc) + + if not (self.c_concat_log_start is None and self.c_concat_log_end is None): + log["c_concat_decoded"] = self.decode_first_stage(c_cat[:, self.c_concat_log_start:self.c_concat_log_end]) + + if plot_diffusion_rows: + # get diffusion row + diffusion_row = list() + z_start = z[:n_row] + for t in range(self.num_timesteps): + if t % self.log_every_t == 0 or t == self.num_timesteps - 1: + t = repeat(torch.tensor([t]), '1 -> b', b=n_row) + t = t.to(self.device).long() + noise = torch.randn_like(z_start) + z_noisy = self.q_sample(x_start=z_start, t=t, noise=noise) + diffusion_row.append(self.decode_first_stage(z_noisy)) + + diffusion_row = torch.stack(diffusion_row) # n_log_step, n_row, C, H, W + diffusion_grid = rearrange(diffusion_row, 'n b c h w -> b n c h w') + diffusion_grid = rearrange(diffusion_grid, 'b n c h w -> (b n) c h w') + diffusion_grid = make_grid(diffusion_grid, nrow=diffusion_row.shape[0]) + log["diffusion_row"] = diffusion_grid + + if sample: + # get denoise row + with ema_scope("Sampling"): + samples, z_denoise_row = self.sample_log(cond={"c_concat": [c_cat], "c_crossattn": [c]}, + batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta) + # samples, z_denoise_row = self.sample(cond=c, batch_size=N, return_intermediates=True) + x_samples = self.decode_first_stage(samples) + log["samples"] = x_samples + if plot_denoise_rows: + denoise_grid = self._get_denoise_row_from_list(z_denoise_row) + log["denoise_row"] = denoise_grid + + if unconditional_guidance_scale > 1.0: + uc_cross = self.get_unconditional_conditioning(N, unconditional_guidance_label) + uc_cat = c_cat + uc_full = {"c_concat": [uc_cat], "c_crossattn": [uc_cross]} + with ema_scope("Sampling with classifier-free guidance"): + samples_cfg, _ = self.sample_log(cond={"c_concat": [c_cat], "c_crossattn": [c]}, + batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=uc_full, + ) + x_samples_cfg = self.decode_first_stage(samples_cfg) + log[f"samples_cfg_scale_{unconditional_guidance_scale:.2f}"] = x_samples_cfg + + return log + + +class LatentInpaintDiffusion(LatentFinetuneDiffusion): + """ + can either run as pure inpainting model (only concat mode) or with mixed conditionings, + e.g. mask as concat and text via cross-attn. + To disable finetuning mode, set finetune_keys to None + """ + + def __init__(self, + concat_keys=("mask", "masked_image"), + masked_image_key="masked_image", + *args, **kwargs + ): + super().__init__(concat_keys, *args, **kwargs) + self.masked_image_key = masked_image_key + assert self.masked_image_key in concat_keys + + @torch.no_grad() + def get_input(self, batch, k, cond_key=None, bs=None, return_first_stage_outputs=False): + # note: restricted to non-trainable encoders currently + assert not self.cond_stage_trainable, 'trainable cond stages not yet supported for inpainting' + z, c, x, xrec, xc = super().get_input(batch, self.first_stage_key, return_first_stage_outputs=True, + force_c_encode=True, return_original_cond=True, bs=bs) + + assert exists(self.concat_keys) + c_cat = list() + for ck in self.concat_keys: + cc = rearrange(batch[ck], 'b h w c -> b c h w').to(memory_format=torch.contiguous_format).float() + if bs is not None: + cc = cc[:bs] + cc = cc.to(self.device) + bchw = z.shape + if ck != self.masked_image_key: + cc = torch.nn.functional.interpolate(cc, size=bchw[-2:]) + else: + cc = self.get_first_stage_encoding(self.encode_first_stage(cc)) + c_cat.append(cc) + c_cat = torch.cat(c_cat, dim=1) + all_conds = {"c_concat": [c_cat], "c_crossattn": [c]} + if return_first_stage_outputs: + return z, all_conds, x, xrec, xc + return z, all_conds + + @torch.no_grad() + def log_images(self, *args, **kwargs): + log = super(LatentInpaintDiffusion, self).log_images(*args, **kwargs) + log["masked_image"] = rearrange(args[0]["masked_image"], + 'b h w c -> b c h w').to(memory_format=torch.contiguous_format).float() + return log + + +class LatentDepth2ImageDiffusion(LatentFinetuneDiffusion): + """ + condition on monocular depth estimation + """ + + def __init__(self, depth_stage_config, concat_keys=("midas_in",), *args, **kwargs): + super().__init__(concat_keys=concat_keys, *args, **kwargs) + self.depth_model = instantiate_from_config(depth_stage_config) + self.depth_stage_key = concat_keys[0] + + @torch.no_grad() + def get_input(self, batch, k, cond_key=None, bs=None, return_first_stage_outputs=False): + # note: restricted to non-trainable encoders currently + assert not self.cond_stage_trainable, 'trainable cond stages not yet supported for depth2img' + z, c, x, xrec, xc = super().get_input(batch, self.first_stage_key, return_first_stage_outputs=True, + force_c_encode=True, return_original_cond=True, bs=bs) + + assert exists(self.concat_keys) + assert len(self.concat_keys) == 1 + c_cat = list() + for ck in self.concat_keys: + cc = batch[ck] + if bs is not None: + cc = cc[:bs] + cc = cc.to(self.device) + cc = self.depth_model(cc) + cc = torch.nn.functional.interpolate( + cc, + size=z.shape[2:], + mode="bicubic", + align_corners=False, + ) + + depth_min, depth_max = torch.amin(cc, dim=[1, 2, 3], keepdim=True), torch.amax(cc, dim=[1, 2, 3], + keepdim=True) + cc = 2. * (cc - depth_min) / (depth_max - depth_min + 0.001) - 1. + c_cat.append(cc) + c_cat = torch.cat(c_cat, dim=1) + all_conds = {"c_concat": [c_cat], "c_crossattn": [c]} + if return_first_stage_outputs: + return z, all_conds, x, xrec, xc + return z, all_conds + + @torch.no_grad() + def log_images(self, *args, **kwargs): + log = super().log_images(*args, **kwargs) + depth = self.depth_model(args[0][self.depth_stage_key]) + depth_min, depth_max = torch.amin(depth, dim=[1, 2, 3], keepdim=True), \ + torch.amax(depth, dim=[1, 2, 3], keepdim=True) + log["depth"] = 2. * (depth - depth_min) / (depth_max - depth_min) - 1. + return log + + +class LatentUpscaleFinetuneDiffusion(LatentFinetuneDiffusion): + """ + condition on low-res image (and optionally on some spatial noise augmentation) + """ + def __init__(self, concat_keys=("lr",), reshuffle_patch_size=None, + low_scale_config=None, low_scale_key=None, *args, **kwargs): + super().__init__(concat_keys=concat_keys, *args, **kwargs) + self.reshuffle_patch_size = reshuffle_patch_size + self.low_scale_model = None + if low_scale_config is not None: + print("Initializing a low-scale model") + assert exists(low_scale_key) + self.instantiate_low_stage(low_scale_config) + self.low_scale_key = low_scale_key + + def instantiate_low_stage(self, config): + model = instantiate_from_config(config) + self.low_scale_model = model.eval() + self.low_scale_model.train = disabled_train + for param in self.low_scale_model.parameters(): + param.requires_grad = False + + @torch.no_grad() + def get_input(self, batch, k, cond_key=None, bs=None, return_first_stage_outputs=False): + # note: restricted to non-trainable encoders currently + assert not self.cond_stage_trainable, 'trainable cond stages not yet supported for upscaling-ft' + z, c, x, xrec, xc = super().get_input(batch, self.first_stage_key, return_first_stage_outputs=True, + force_c_encode=True, return_original_cond=True, bs=bs) + + assert exists(self.concat_keys) + assert len(self.concat_keys) == 1 + # optionally make spatial noise_level here + c_cat = list() + noise_level = None + for ck in self.concat_keys: + cc = batch[ck] + cc = rearrange(cc, 'b h w c -> b c h w') + if exists(self.reshuffle_patch_size): + assert isinstance(self.reshuffle_patch_size, int) + cc = rearrange(cc, 'b c (p1 h) (p2 w) -> b (p1 p2 c) h w', + p1=self.reshuffle_patch_size, p2=self.reshuffle_patch_size) + if bs is not None: + cc = cc[:bs] + cc = cc.to(self.device) + if exists(self.low_scale_model) and ck == self.low_scale_key: + cc, noise_level = self.low_scale_model(cc) + c_cat.append(cc) + c_cat = torch.cat(c_cat, dim=1) + if exists(noise_level): + all_conds = {"c_concat": [c_cat], "c_crossattn": [c], "c_adm": noise_level} + else: + all_conds = {"c_concat": [c_cat], "c_crossattn": [c]} + if return_first_stage_outputs: + return z, all_conds, x, xrec, xc + return z, all_conds + + @torch.no_grad() + def log_images(self, *args, **kwargs): + log = super().log_images(*args, **kwargs) + log["lr"] = rearrange(args[0]["lr"], 'b h w c -> b c h w') + return log diff --git a/sd/stablediffusion/ldm/models/diffusion/dpm_solver/__init__.py b/sd/stablediffusion/ldm/models/diffusion/dpm_solver/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7427f38c07530afbab79154ea8aaf88c4bf70a08 --- /dev/null +++ b/sd/stablediffusion/ldm/models/diffusion/dpm_solver/__init__.py @@ -0,0 +1 @@ +from .sampler import DPMSolverSampler \ No newline at end of file diff --git a/sd/stablediffusion/ldm/models/diffusion/dpm_solver/dpm_solver.py b/sd/stablediffusion/ldm/models/diffusion/dpm_solver/dpm_solver.py new file mode 100644 index 0000000000000000000000000000000000000000..095e5ba3ce0b1aa7f4b3f1e2e5d8fff7cfe6dc8c --- /dev/null +++ b/sd/stablediffusion/ldm/models/diffusion/dpm_solver/dpm_solver.py @@ -0,0 +1,1154 @@ +import torch +import torch.nn.functional as F +import math +from tqdm import tqdm + + +class NoiseScheduleVP: + def __init__( + self, + schedule='discrete', + betas=None, + alphas_cumprod=None, + continuous_beta_0=0.1, + continuous_beta_1=20., + ): + """Create a wrapper class for the forward SDE (VP type). + *** + Update: We support discrete-time diffusion models by implementing a picewise linear interpolation for log_alpha_t. + We recommend to use schedule='discrete' for the discrete-time diffusion models, especially for high-resolution images. + *** + The forward SDE ensures that the condition distribution q_{t|0}(x_t | x_0) = N ( alpha_t * x_0, sigma_t^2 * I ). + We further define lambda_t = log(alpha_t) - log(sigma_t), which is the half-logSNR (described in the DPM-Solver paper). + Therefore, we implement the functions for computing alpha_t, sigma_t and lambda_t. For t in [0, T], we have: + log_alpha_t = self.marginal_log_mean_coeff(t) + sigma_t = self.marginal_std(t) + lambda_t = self.marginal_lambda(t) + Moreover, as lambda(t) is an invertible function, we also support its inverse function: + t = self.inverse_lambda(lambda_t) + =============================================================== + We support both discrete-time DPMs (trained on n = 0, 1, ..., N-1) and continuous-time DPMs (trained on t in [t_0, T]). + 1. For discrete-time DPMs: + For discrete-time DPMs trained on n = 0, 1, ..., N-1, we convert the discrete steps to continuous time steps by: + t_i = (i + 1) / N + e.g. for N = 1000, we have t_0 = 1e-3 and T = t_{N-1} = 1. + We solve the corresponding diffusion ODE from time T = 1 to time t_0 = 1e-3. + Args: + betas: A `torch.Tensor`. The beta array for the discrete-time DPM. (See the original DDPM paper for details) + alphas_cumprod: A `torch.Tensor`. The cumprod alphas for the discrete-time DPM. (See the original DDPM paper for details) + Note that we always have alphas_cumprod = cumprod(betas). Therefore, we only need to set one of `betas` and `alphas_cumprod`. + **Important**: Please pay special attention for the args for `alphas_cumprod`: + The `alphas_cumprod` is the \hat{alpha_n} arrays in the notations of DDPM. Specifically, DDPMs assume that + q_{t_n | 0}(x_{t_n} | x_0) = N ( \sqrt{\hat{alpha_n}} * x_0, (1 - \hat{alpha_n}) * I ). + Therefore, the notation \hat{alpha_n} is different from the notation alpha_t in DPM-Solver. In fact, we have + alpha_{t_n} = \sqrt{\hat{alpha_n}}, + and + log(alpha_{t_n}) = 0.5 * log(\hat{alpha_n}). + 2. For continuous-time DPMs: + We support two types of VPSDEs: linear (DDPM) and cosine (improved-DDPM). The hyperparameters for the noise + schedule are the default settings in DDPM and improved-DDPM: + Args: + beta_min: A `float` number. The smallest beta for the linear schedule. + beta_max: A `float` number. The largest beta for the linear schedule. + cosine_s: A `float` number. The hyperparameter in the cosine schedule. + cosine_beta_max: A `float` number. The hyperparameter in the cosine schedule. + T: A `float` number. The ending time of the forward process. + =============================================================== + Args: + schedule: A `str`. The noise schedule of the forward SDE. 'discrete' for discrete-time DPMs, + 'linear' or 'cosine' for continuous-time DPMs. + Returns: + A wrapper object of the forward SDE (VP type). + + =============================================================== + Example: + # For discrete-time DPMs, given betas (the beta array for n = 0, 1, ..., N - 1): + >>> ns = NoiseScheduleVP('discrete', betas=betas) + # For discrete-time DPMs, given alphas_cumprod (the \hat{alpha_n} array for n = 0, 1, ..., N - 1): + >>> ns = NoiseScheduleVP('discrete', alphas_cumprod=alphas_cumprod) + # For continuous-time DPMs (VPSDE), linear schedule: + >>> ns = NoiseScheduleVP('linear', continuous_beta_0=0.1, continuous_beta_1=20.) + """ + + if schedule not in ['discrete', 'linear', 'cosine']: + raise ValueError( + "Unsupported noise schedule {}. The schedule needs to be 'discrete' or 'linear' or 'cosine'".format( + schedule)) + + self.schedule = schedule + if schedule == 'discrete': + if betas is not None: + log_alphas = 0.5 * torch.log(1 - betas).cumsum(dim=0) + else: + assert alphas_cumprod is not None + log_alphas = 0.5 * torch.log(alphas_cumprod) + self.total_N = len(log_alphas) + self.T = 1. + self.t_array = torch.linspace(0., 1., self.total_N + 1)[1:].reshape((1, -1)) + self.log_alpha_array = log_alphas.reshape((1, -1,)) + else: + self.total_N = 1000 + self.beta_0 = continuous_beta_0 + self.beta_1 = continuous_beta_1 + self.cosine_s = 0.008 + self.cosine_beta_max = 999. + self.cosine_t_max = math.atan(self.cosine_beta_max * (1. + self.cosine_s) / math.pi) * 2. * ( + 1. + self.cosine_s) / math.pi - self.cosine_s + self.cosine_log_alpha_0 = math.log(math.cos(self.cosine_s / (1. + self.cosine_s) * math.pi / 2.)) + self.schedule = schedule + if schedule == 'cosine': + # For the cosine schedule, T = 1 will have numerical issues. So we manually set the ending time T. + # Note that T = 0.9946 may be not the optimal setting. However, we find it works well. + self.T = 0.9946 + else: + self.T = 1. + + def marginal_log_mean_coeff(self, t): + """ + Compute log(alpha_t) of a given continuous-time label t in [0, T]. + """ + if self.schedule == 'discrete': + return interpolate_fn(t.reshape((-1, 1)), self.t_array.to(t.device), + self.log_alpha_array.to(t.device)).reshape((-1)) + elif self.schedule == 'linear': + return -0.25 * t ** 2 * (self.beta_1 - self.beta_0) - 0.5 * t * self.beta_0 + elif self.schedule == 'cosine': + log_alpha_fn = lambda s: torch.log(torch.cos((s + self.cosine_s) / (1. + self.cosine_s) * math.pi / 2.)) + log_alpha_t = log_alpha_fn(t) - self.cosine_log_alpha_0 + return log_alpha_t + + def marginal_alpha(self, t): + """ + Compute alpha_t of a given continuous-time label t in [0, T]. + """ + return torch.exp(self.marginal_log_mean_coeff(t)) + + def marginal_std(self, t): + """ + Compute sigma_t of a given continuous-time label t in [0, T]. + """ + return torch.sqrt(1. - torch.exp(2. * self.marginal_log_mean_coeff(t))) + + def marginal_lambda(self, t): + """ + Compute lambda_t = log(alpha_t) - log(sigma_t) of a given continuous-time label t in [0, T]. + """ + log_mean_coeff = self.marginal_log_mean_coeff(t) + log_std = 0.5 * torch.log(1. - torch.exp(2. * log_mean_coeff)) + return log_mean_coeff - log_std + + def inverse_lambda(self, lamb): + """ + Compute the continuous-time label t in [0, T] of a given half-logSNR lambda_t. + """ + if self.schedule == 'linear': + tmp = 2. * (self.beta_1 - self.beta_0) * torch.logaddexp(-2. * lamb, torch.zeros((1,)).to(lamb)) + Delta = self.beta_0 ** 2 + tmp + return tmp / (torch.sqrt(Delta) + self.beta_0) / (self.beta_1 - self.beta_0) + elif self.schedule == 'discrete': + log_alpha = -0.5 * torch.logaddexp(torch.zeros((1,)).to(lamb.device), -2. * lamb) + t = interpolate_fn(log_alpha.reshape((-1, 1)), torch.flip(self.log_alpha_array.to(lamb.device), [1]), + torch.flip(self.t_array.to(lamb.device), [1])) + return t.reshape((-1,)) + else: + log_alpha = -0.5 * torch.logaddexp(-2. * lamb, torch.zeros((1,)).to(lamb)) + t_fn = lambda log_alpha_t: torch.arccos(torch.exp(log_alpha_t + self.cosine_log_alpha_0)) * 2. * ( + 1. + self.cosine_s) / math.pi - self.cosine_s + t = t_fn(log_alpha) + return t + + +def model_wrapper( + model, + noise_schedule, + model_type="noise", + model_kwargs={}, + guidance_type="uncond", + condition=None, + unconditional_condition=None, + guidance_scale=1., + classifier_fn=None, + classifier_kwargs={}, +): + """Create a wrapper function for the noise prediction model. + DPM-Solver needs to solve the continuous-time diffusion ODEs. For DPMs trained on discrete-time labels, we need to + firstly wrap the model function to a noise prediction model that accepts the continuous time as the input. + We support four types of the diffusion model by setting `model_type`: + 1. "noise": noise prediction model. (Trained by predicting noise). + 2. "x_start": data prediction model. (Trained by predicting the data x_0 at time 0). + 3. "v": velocity prediction model. (Trained by predicting the velocity). + The "v" prediction is derivation detailed in Appendix D of [1], and is used in Imagen-Video [2]. + [1] Salimans, Tim, and Jonathan Ho. "Progressive distillation for fast sampling of diffusion models." + arXiv preprint arXiv:2202.00512 (2022). + [2] Ho, Jonathan, et al. "Imagen Video: High Definition Video Generation with Diffusion Models." + arXiv preprint arXiv:2210.02303 (2022). + + 4. "score": marginal score function. (Trained by denoising score matching). + Note that the score function and the noise prediction model follows a simple relationship: + ``` + noise(x_t, t) = -sigma_t * score(x_t, t) + ``` + We support three types of guided sampling by DPMs by setting `guidance_type`: + 1. "uncond": unconditional sampling by DPMs. + The input `model` has the following format: + `` + model(x, t_input, **model_kwargs) -> noise | x_start | v | score + `` + 2. "classifier": classifier guidance sampling [3] by DPMs and another classifier. + The input `model` has the following format: + `` + model(x, t_input, **model_kwargs) -> noise | x_start | v | score + `` + The input `classifier_fn` has the following format: + `` + classifier_fn(x, t_input, cond, **classifier_kwargs) -> logits(x, t_input, cond) + `` + [3] P. Dhariwal and A. Q. Nichol, "Diffusion models beat GANs on image synthesis," + in Advances in Neural Information Processing Systems, vol. 34, 2021, pp. 8780-8794. + 3. "classifier-free": classifier-free guidance sampling by conditional DPMs. + The input `model` has the following format: + `` + model(x, t_input, cond, **model_kwargs) -> noise | x_start | v | score + `` + And if cond == `unconditional_condition`, the model output is the unconditional DPM output. + [4] Ho, Jonathan, and Tim Salimans. "Classifier-free diffusion guidance." + arXiv preprint arXiv:2207.12598 (2022). + + The `t_input` is the time label of the model, which may be discrete-time labels (i.e. 0 to 999) + or continuous-time labels (i.e. epsilon to T). + We wrap the model function to accept only `x` and `t_continuous` as inputs, and outputs the predicted noise: + `` + def model_fn(x, t_continuous) -> noise: + t_input = get_model_input_time(t_continuous) + return noise_pred(model, x, t_input, **model_kwargs) + `` + where `t_continuous` is the continuous time labels (i.e. epsilon to T). And we use `model_fn` for DPM-Solver. + =============================================================== + Args: + model: A diffusion model with the corresponding format described above. + noise_schedule: A noise schedule object, such as NoiseScheduleVP. + model_type: A `str`. The parameterization type of the diffusion model. + "noise" or "x_start" or "v" or "score". + model_kwargs: A `dict`. A dict for the other inputs of the model function. + guidance_type: A `str`. The type of the guidance for sampling. + "uncond" or "classifier" or "classifier-free". + condition: A pytorch tensor. The condition for the guided sampling. + Only used for "classifier" or "classifier-free" guidance type. + unconditional_condition: A pytorch tensor. The condition for the unconditional sampling. + Only used for "classifier-free" guidance type. + guidance_scale: A `float`. The scale for the guided sampling. + classifier_fn: A classifier function. Only used for the classifier guidance. + classifier_kwargs: A `dict`. A dict for the other inputs of the classifier function. + Returns: + A noise prediction model that accepts the noised data and the continuous time as the inputs. + """ + + def get_model_input_time(t_continuous): + """ + Convert the continuous-time `t_continuous` (in [epsilon, T]) to the model input time. + For discrete-time DPMs, we convert `t_continuous` in [1 / N, 1] to `t_input` in [0, 1000 * (N - 1) / N]. + For continuous-time DPMs, we just use `t_continuous`. + """ + if noise_schedule.schedule == 'discrete': + return (t_continuous - 1. / noise_schedule.total_N) * 1000. + else: + return t_continuous + + def noise_pred_fn(x, t_continuous, cond=None): + if t_continuous.reshape((-1,)).shape[0] == 1: + t_continuous = t_continuous.expand((x.shape[0])) + t_input = get_model_input_time(t_continuous) + if cond is None: + output = model(x, t_input, **model_kwargs) + else: + output = model(x, t_input, cond, **model_kwargs) + if model_type == "noise": + return output + elif model_type == "x_start": + alpha_t, sigma_t = noise_schedule.marginal_alpha(t_continuous), noise_schedule.marginal_std(t_continuous) + dims = x.dim() + return (x - expand_dims(alpha_t, dims) * output) / expand_dims(sigma_t, dims) + elif model_type == "v": + alpha_t, sigma_t = noise_schedule.marginal_alpha(t_continuous), noise_schedule.marginal_std(t_continuous) + dims = x.dim() + return expand_dims(alpha_t, dims) * output + expand_dims(sigma_t, dims) * x + elif model_type == "score": + sigma_t = noise_schedule.marginal_std(t_continuous) + dims = x.dim() + return -expand_dims(sigma_t, dims) * output + + def cond_grad_fn(x, t_input): + """ + Compute the gradient of the classifier, i.e. nabla_{x} log p_t(cond | x_t). + """ + with torch.enable_grad(): + x_in = x.detach().requires_grad_(True) + log_prob = classifier_fn(x_in, t_input, condition, **classifier_kwargs) + return torch.autograd.grad(log_prob.sum(), x_in)[0] + + def model_fn(x, t_continuous): + """ + The noise predicition model function that is used for DPM-Solver. + """ + if t_continuous.reshape((-1,)).shape[0] == 1: + t_continuous = t_continuous.expand((x.shape[0])) + if guidance_type == "uncond": + return noise_pred_fn(x, t_continuous) + elif guidance_type == "classifier": + assert classifier_fn is not None + t_input = get_model_input_time(t_continuous) + cond_grad = cond_grad_fn(x, t_input) + sigma_t = noise_schedule.marginal_std(t_continuous) + noise = noise_pred_fn(x, t_continuous) + return noise - guidance_scale * expand_dims(sigma_t, dims=cond_grad.dim()) * cond_grad + elif guidance_type == "classifier-free": + if guidance_scale == 1. or unconditional_condition is None: + return noise_pred_fn(x, t_continuous, cond=condition) + else: + x_in = torch.cat([x] * 2) + t_in = torch.cat([t_continuous] * 2) + c_in = torch.cat([unconditional_condition, condition]) + noise_uncond, noise = noise_pred_fn(x_in, t_in, cond=c_in).chunk(2) + return noise_uncond + guidance_scale * (noise - noise_uncond) + + assert model_type in ["noise", "x_start", "v"] + assert guidance_type in ["uncond", "classifier", "classifier-free"] + return model_fn + + +class DPM_Solver: + def __init__(self, model_fn, noise_schedule, predict_x0=False, thresholding=False, max_val=1.): + """Construct a DPM-Solver. + We support both the noise prediction model ("predicting epsilon") and the data prediction model ("predicting x0"). + If `predict_x0` is False, we use the solver for the noise prediction model (DPM-Solver). + If `predict_x0` is True, we use the solver for the data prediction model (DPM-Solver++). + In such case, we further support the "dynamic thresholding" in [1] when `thresholding` is True. + The "dynamic thresholding" can greatly improve the sample quality for pixel-space DPMs with large guidance scales. + Args: + model_fn: A noise prediction model function which accepts the continuous-time input (t in [epsilon, T]): + `` + def model_fn(x, t_continuous): + return noise + `` + noise_schedule: A noise schedule object, such as NoiseScheduleVP. + predict_x0: A `bool`. If true, use the data prediction model; else, use the noise prediction model. + thresholding: A `bool`. Valid when `predict_x0` is True. Whether to use the "dynamic thresholding" in [1]. + max_val: A `float`. Valid when both `predict_x0` and `thresholding` are True. The max value for thresholding. + + [1] Chitwan Saharia, William Chan, Saurabh Saxena, Lala Li, Jay Whang, Emily Denton, Seyed Kamyar Seyed Ghasemipour, Burcu Karagol Ayan, S Sara Mahdavi, Rapha Gontijo Lopes, et al. Photorealistic text-to-image diffusion models with deep language understanding. arXiv preprint arXiv:2205.11487, 2022b. + """ + self.model = model_fn + self.noise_schedule = noise_schedule + self.predict_x0 = predict_x0 + self.thresholding = thresholding + self.max_val = max_val + + def noise_prediction_fn(self, x, t): + """ + Return the noise prediction model. + """ + return self.model(x, t) + + def data_prediction_fn(self, x, t): + """ + Return the data prediction model (with thresholding). + """ + noise = self.noise_prediction_fn(x, t) + dims = x.dim() + alpha_t, sigma_t = self.noise_schedule.marginal_alpha(t), self.noise_schedule.marginal_std(t) + x0 = (x - expand_dims(sigma_t, dims) * noise) / expand_dims(alpha_t, dims) + if self.thresholding: + p = 0.995 # A hyperparameter in the paper of "Imagen" [1]. + s = torch.quantile(torch.abs(x0).reshape((x0.shape[0], -1)), p, dim=1) + s = expand_dims(torch.maximum(s, self.max_val * torch.ones_like(s).to(s.device)), dims) + x0 = torch.clamp(x0, -s, s) / s + return x0 + + def model_fn(self, x, t): + """ + Convert the model to the noise prediction model or the data prediction model. + """ + if self.predict_x0: + return self.data_prediction_fn(x, t) + else: + return self.noise_prediction_fn(x, t) + + def get_time_steps(self, skip_type, t_T, t_0, N, device): + """Compute the intermediate time steps for sampling. + Args: + skip_type: A `str`. The type for the spacing of the time steps. We support three types: + - 'logSNR': uniform logSNR for the time steps. + - 'time_uniform': uniform time for the time steps. (**Recommended for high-resolutional data**.) + - 'time_quadratic': quadratic time for the time steps. (Used in DDIM for low-resolutional data.) + t_T: A `float`. The starting time of the sampling (default is T). + t_0: A `float`. The ending time of the sampling (default is epsilon). + N: A `int`. The total number of the spacing of the time steps. + device: A torch device. + Returns: + A pytorch tensor of the time steps, with the shape (N + 1,). + """ + if skip_type == 'logSNR': + lambda_T = self.noise_schedule.marginal_lambda(torch.tensor(t_T).to(device)) + lambda_0 = self.noise_schedule.marginal_lambda(torch.tensor(t_0).to(device)) + logSNR_steps = torch.linspace(lambda_T.cpu().item(), lambda_0.cpu().item(), N + 1).to(device) + return self.noise_schedule.inverse_lambda(logSNR_steps) + elif skip_type == 'time_uniform': + return torch.linspace(t_T, t_0, N + 1).to(device) + elif skip_type == 'time_quadratic': + t_order = 2 + t = torch.linspace(t_T ** (1. / t_order), t_0 ** (1. / t_order), N + 1).pow(t_order).to(device) + return t + else: + raise ValueError( + "Unsupported skip_type {}, need to be 'logSNR' or 'time_uniform' or 'time_quadratic'".format(skip_type)) + + def get_orders_and_timesteps_for_singlestep_solver(self, steps, order, skip_type, t_T, t_0, device): + """ + Get the order of each step for sampling by the singlestep DPM-Solver. + We combine both DPM-Solver-1,2,3 to use all the function evaluations, which is named as "DPM-Solver-fast". + Given a fixed number of function evaluations by `steps`, the sampling procedure by DPM-Solver-fast is: + - If order == 1: + We take `steps` of DPM-Solver-1 (i.e. DDIM). + - If order == 2: + - Denote K = (steps // 2). We take K or (K + 1) intermediate time steps for sampling. + - If steps % 2 == 0, we use K steps of DPM-Solver-2. + - If steps % 2 == 1, we use K steps of DPM-Solver-2 and 1 step of DPM-Solver-1. + - If order == 3: + - Denote K = (steps // 3 + 1). We take K intermediate time steps for sampling. + - If steps % 3 == 0, we use (K - 2) steps of DPM-Solver-3, and 1 step of DPM-Solver-2 and 1 step of DPM-Solver-1. + - If steps % 3 == 1, we use (K - 1) steps of DPM-Solver-3 and 1 step of DPM-Solver-1. + - If steps % 3 == 2, we use (K - 1) steps of DPM-Solver-3 and 1 step of DPM-Solver-2. + ============================================ + Args: + order: A `int`. The max order for the solver (2 or 3). + steps: A `int`. The total number of function evaluations (NFE). + skip_type: A `str`. The type for the spacing of the time steps. We support three types: + - 'logSNR': uniform logSNR for the time steps. + - 'time_uniform': uniform time for the time steps. (**Recommended for high-resolutional data**.) + - 'time_quadratic': quadratic time for the time steps. (Used in DDIM for low-resolutional data.) + t_T: A `float`. The starting time of the sampling (default is T). + t_0: A `float`. The ending time of the sampling (default is epsilon). + device: A torch device. + Returns: + orders: A list of the solver order of each step. + """ + if order == 3: + K = steps // 3 + 1 + if steps % 3 == 0: + orders = [3, ] * (K - 2) + [2, 1] + elif steps % 3 == 1: + orders = [3, ] * (K - 1) + [1] + else: + orders = [3, ] * (K - 1) + [2] + elif order == 2: + if steps % 2 == 0: + K = steps // 2 + orders = [2, ] * K + else: + K = steps // 2 + 1 + orders = [2, ] * (K - 1) + [1] + elif order == 1: + K = 1 + orders = [1, ] * steps + else: + raise ValueError("'order' must be '1' or '2' or '3'.") + if skip_type == 'logSNR': + # To reproduce the results in DPM-Solver paper + timesteps_outer = self.get_time_steps(skip_type, t_T, t_0, K, device) + else: + timesteps_outer = self.get_time_steps(skip_type, t_T, t_0, steps, device)[ + torch.cumsum(torch.tensor([0, ] + orders)).to(device)] + return timesteps_outer, orders + + def denoise_to_zero_fn(self, x, s): + """ + Denoise at the final step, which is equivalent to solve the ODE from lambda_s to infty by first-order discretization. + """ + return self.data_prediction_fn(x, s) + + def dpm_solver_first_update(self, x, s, t, model_s=None, return_intermediate=False): + """ + DPM-Solver-1 (equivalent to DDIM) from time `s` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + s: A pytorch tensor. The starting time, with the shape (x.shape[0],). + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + model_s: A pytorch tensor. The model function evaluated at time `s`. + If `model_s` is None, we evaluate the model by `x` and `s`; otherwise we directly use it. + return_intermediate: A `bool`. If true, also return the model value at time `s`. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + ns = self.noise_schedule + dims = x.dim() + lambda_s, lambda_t = ns.marginal_lambda(s), ns.marginal_lambda(t) + h = lambda_t - lambda_s + log_alpha_s, log_alpha_t = ns.marginal_log_mean_coeff(s), ns.marginal_log_mean_coeff(t) + sigma_s, sigma_t = ns.marginal_std(s), ns.marginal_std(t) + alpha_t = torch.exp(log_alpha_t) + + if self.predict_x0: + phi_1 = torch.expm1(-h) + if model_s is None: + model_s = self.model_fn(x, s) + x_t = ( + expand_dims(sigma_t / sigma_s, dims) * x + - expand_dims(alpha_t * phi_1, dims) * model_s + ) + if return_intermediate: + return x_t, {'model_s': model_s} + else: + return x_t + else: + phi_1 = torch.expm1(h) + if model_s is None: + model_s = self.model_fn(x, s) + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_s), dims) * x + - expand_dims(sigma_t * phi_1, dims) * model_s + ) + if return_intermediate: + return x_t, {'model_s': model_s} + else: + return x_t + + def singlestep_dpm_solver_second_update(self, x, s, t, r1=0.5, model_s=None, return_intermediate=False, + solver_type='dpm_solver'): + """ + Singlestep solver DPM-Solver-2 from time `s` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + s: A pytorch tensor. The starting time, with the shape (x.shape[0],). + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + r1: A `float`. The hyperparameter of the second-order solver. + model_s: A pytorch tensor. The model function evaluated at time `s`. + If `model_s` is None, we evaluate the model by `x` and `s`; otherwise we directly use it. + return_intermediate: A `bool`. If true, also return the model value at time `s` and `s1` (the intermediate time). + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + if solver_type not in ['dpm_solver', 'taylor']: + raise ValueError("'solver_type' must be either 'dpm_solver' or 'taylor', got {}".format(solver_type)) + if r1 is None: + r1 = 0.5 + ns = self.noise_schedule + dims = x.dim() + lambda_s, lambda_t = ns.marginal_lambda(s), ns.marginal_lambda(t) + h = lambda_t - lambda_s + lambda_s1 = lambda_s + r1 * h + s1 = ns.inverse_lambda(lambda_s1) + log_alpha_s, log_alpha_s1, log_alpha_t = ns.marginal_log_mean_coeff(s), ns.marginal_log_mean_coeff( + s1), ns.marginal_log_mean_coeff(t) + sigma_s, sigma_s1, sigma_t = ns.marginal_std(s), ns.marginal_std(s1), ns.marginal_std(t) + alpha_s1, alpha_t = torch.exp(log_alpha_s1), torch.exp(log_alpha_t) + + if self.predict_x0: + phi_11 = torch.expm1(-r1 * h) + phi_1 = torch.expm1(-h) + + if model_s is None: + model_s = self.model_fn(x, s) + x_s1 = ( + expand_dims(sigma_s1 / sigma_s, dims) * x + - expand_dims(alpha_s1 * phi_11, dims) * model_s + ) + model_s1 = self.model_fn(x_s1, s1) + if solver_type == 'dpm_solver': + x_t = ( + expand_dims(sigma_t / sigma_s, dims) * x + - expand_dims(alpha_t * phi_1, dims) * model_s + - (0.5 / r1) * expand_dims(alpha_t * phi_1, dims) * (model_s1 - model_s) + ) + elif solver_type == 'taylor': + x_t = ( + expand_dims(sigma_t / sigma_s, dims) * x + - expand_dims(alpha_t * phi_1, dims) * model_s + + (1. / r1) * expand_dims(alpha_t * ((torch.exp(-h) - 1.) / h + 1.), dims) * ( + model_s1 - model_s) + ) + else: + phi_11 = torch.expm1(r1 * h) + phi_1 = torch.expm1(h) + + if model_s is None: + model_s = self.model_fn(x, s) + x_s1 = ( + expand_dims(torch.exp(log_alpha_s1 - log_alpha_s), dims) * x + - expand_dims(sigma_s1 * phi_11, dims) * model_s + ) + model_s1 = self.model_fn(x_s1, s1) + if solver_type == 'dpm_solver': + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_s), dims) * x + - expand_dims(sigma_t * phi_1, dims) * model_s + - (0.5 / r1) * expand_dims(sigma_t * phi_1, dims) * (model_s1 - model_s) + ) + elif solver_type == 'taylor': + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_s), dims) * x + - expand_dims(sigma_t * phi_1, dims) * model_s + - (1. / r1) * expand_dims(sigma_t * ((torch.exp(h) - 1.) / h - 1.), dims) * (model_s1 - model_s) + ) + if return_intermediate: + return x_t, {'model_s': model_s, 'model_s1': model_s1} + else: + return x_t + + def singlestep_dpm_solver_third_update(self, x, s, t, r1=1. / 3., r2=2. / 3., model_s=None, model_s1=None, + return_intermediate=False, solver_type='dpm_solver'): + """ + Singlestep solver DPM-Solver-3 from time `s` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + s: A pytorch tensor. The starting time, with the shape (x.shape[0],). + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + r1: A `float`. The hyperparameter of the third-order solver. + r2: A `float`. The hyperparameter of the third-order solver. + model_s: A pytorch tensor. The model function evaluated at time `s`. + If `model_s` is None, we evaluate the model by `x` and `s`; otherwise we directly use it. + model_s1: A pytorch tensor. The model function evaluated at time `s1` (the intermediate time given by `r1`). + If `model_s1` is None, we evaluate the model at `s1`; otherwise we directly use it. + return_intermediate: A `bool`. If true, also return the model value at time `s`, `s1` and `s2` (the intermediate times). + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + if solver_type not in ['dpm_solver', 'taylor']: + raise ValueError("'solver_type' must be either 'dpm_solver' or 'taylor', got {}".format(solver_type)) + if r1 is None: + r1 = 1. / 3. + if r2 is None: + r2 = 2. / 3. + ns = self.noise_schedule + dims = x.dim() + lambda_s, lambda_t = ns.marginal_lambda(s), ns.marginal_lambda(t) + h = lambda_t - lambda_s + lambda_s1 = lambda_s + r1 * h + lambda_s2 = lambda_s + r2 * h + s1 = ns.inverse_lambda(lambda_s1) + s2 = ns.inverse_lambda(lambda_s2) + log_alpha_s, log_alpha_s1, log_alpha_s2, log_alpha_t = ns.marginal_log_mean_coeff( + s), ns.marginal_log_mean_coeff(s1), ns.marginal_log_mean_coeff(s2), ns.marginal_log_mean_coeff(t) + sigma_s, sigma_s1, sigma_s2, sigma_t = ns.marginal_std(s), ns.marginal_std(s1), ns.marginal_std( + s2), ns.marginal_std(t) + alpha_s1, alpha_s2, alpha_t = torch.exp(log_alpha_s1), torch.exp(log_alpha_s2), torch.exp(log_alpha_t) + + if self.predict_x0: + phi_11 = torch.expm1(-r1 * h) + phi_12 = torch.expm1(-r2 * h) + phi_1 = torch.expm1(-h) + phi_22 = torch.expm1(-r2 * h) / (r2 * h) + 1. + phi_2 = phi_1 / h + 1. + phi_3 = phi_2 / h - 0.5 + + if model_s is None: + model_s = self.model_fn(x, s) + if model_s1 is None: + x_s1 = ( + expand_dims(sigma_s1 / sigma_s, dims) * x + - expand_dims(alpha_s1 * phi_11, dims) * model_s + ) + model_s1 = self.model_fn(x_s1, s1) + x_s2 = ( + expand_dims(sigma_s2 / sigma_s, dims) * x + - expand_dims(alpha_s2 * phi_12, dims) * model_s + + r2 / r1 * expand_dims(alpha_s2 * phi_22, dims) * (model_s1 - model_s) + ) + model_s2 = self.model_fn(x_s2, s2) + if solver_type == 'dpm_solver': + x_t = ( + expand_dims(sigma_t / sigma_s, dims) * x + - expand_dims(alpha_t * phi_1, dims) * model_s + + (1. / r2) * expand_dims(alpha_t * phi_2, dims) * (model_s2 - model_s) + ) + elif solver_type == 'taylor': + D1_0 = (1. / r1) * (model_s1 - model_s) + D1_1 = (1. / r2) * (model_s2 - model_s) + D1 = (r2 * D1_0 - r1 * D1_1) / (r2 - r1) + D2 = 2. * (D1_1 - D1_0) / (r2 - r1) + x_t = ( + expand_dims(sigma_t / sigma_s, dims) * x + - expand_dims(alpha_t * phi_1, dims) * model_s + + expand_dims(alpha_t * phi_2, dims) * D1 + - expand_dims(alpha_t * phi_3, dims) * D2 + ) + else: + phi_11 = torch.expm1(r1 * h) + phi_12 = torch.expm1(r2 * h) + phi_1 = torch.expm1(h) + phi_22 = torch.expm1(r2 * h) / (r2 * h) - 1. + phi_2 = phi_1 / h - 1. + phi_3 = phi_2 / h - 0.5 + + if model_s is None: + model_s = self.model_fn(x, s) + if model_s1 is None: + x_s1 = ( + expand_dims(torch.exp(log_alpha_s1 - log_alpha_s), dims) * x + - expand_dims(sigma_s1 * phi_11, dims) * model_s + ) + model_s1 = self.model_fn(x_s1, s1) + x_s2 = ( + expand_dims(torch.exp(log_alpha_s2 - log_alpha_s), dims) * x + - expand_dims(sigma_s2 * phi_12, dims) * model_s + - r2 / r1 * expand_dims(sigma_s2 * phi_22, dims) * (model_s1 - model_s) + ) + model_s2 = self.model_fn(x_s2, s2) + if solver_type == 'dpm_solver': + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_s), dims) * x + - expand_dims(sigma_t * phi_1, dims) * model_s + - (1. / r2) * expand_dims(sigma_t * phi_2, dims) * (model_s2 - model_s) + ) + elif solver_type == 'taylor': + D1_0 = (1. / r1) * (model_s1 - model_s) + D1_1 = (1. / r2) * (model_s2 - model_s) + D1 = (r2 * D1_0 - r1 * D1_1) / (r2 - r1) + D2 = 2. * (D1_1 - D1_0) / (r2 - r1) + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_s), dims) * x + - expand_dims(sigma_t * phi_1, dims) * model_s + - expand_dims(sigma_t * phi_2, dims) * D1 + - expand_dims(sigma_t * phi_3, dims) * D2 + ) + + if return_intermediate: + return x_t, {'model_s': model_s, 'model_s1': model_s1, 'model_s2': model_s2} + else: + return x_t + + def multistep_dpm_solver_second_update(self, x, model_prev_list, t_prev_list, t, solver_type="dpm_solver"): + """ + Multistep solver DPM-Solver-2 from time `t_prev_list[-1]` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + model_prev_list: A list of pytorch tensor. The previous computed model values. + t_prev_list: A list of pytorch tensor. The previous times, each time has the shape (x.shape[0],) + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + if solver_type not in ['dpm_solver', 'taylor']: + raise ValueError("'solver_type' must be either 'dpm_solver' or 'taylor', got {}".format(solver_type)) + ns = self.noise_schedule + dims = x.dim() + model_prev_1, model_prev_0 = model_prev_list + t_prev_1, t_prev_0 = t_prev_list + lambda_prev_1, lambda_prev_0, lambda_t = ns.marginal_lambda(t_prev_1), ns.marginal_lambda( + t_prev_0), ns.marginal_lambda(t) + log_alpha_prev_0, log_alpha_t = ns.marginal_log_mean_coeff(t_prev_0), ns.marginal_log_mean_coeff(t) + sigma_prev_0, sigma_t = ns.marginal_std(t_prev_0), ns.marginal_std(t) + alpha_t = torch.exp(log_alpha_t) + + h_0 = lambda_prev_0 - lambda_prev_1 + h = lambda_t - lambda_prev_0 + r0 = h_0 / h + D1_0 = expand_dims(1. / r0, dims) * (model_prev_0 - model_prev_1) + if self.predict_x0: + if solver_type == 'dpm_solver': + x_t = ( + expand_dims(sigma_t / sigma_prev_0, dims) * x + - expand_dims(alpha_t * (torch.exp(-h) - 1.), dims) * model_prev_0 + - 0.5 * expand_dims(alpha_t * (torch.exp(-h) - 1.), dims) * D1_0 + ) + elif solver_type == 'taylor': + x_t = ( + expand_dims(sigma_t / sigma_prev_0, dims) * x + - expand_dims(alpha_t * (torch.exp(-h) - 1.), dims) * model_prev_0 + + expand_dims(alpha_t * ((torch.exp(-h) - 1.) / h + 1.), dims) * D1_0 + ) + else: + if solver_type == 'dpm_solver': + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_prev_0), dims) * x + - expand_dims(sigma_t * (torch.exp(h) - 1.), dims) * model_prev_0 + - 0.5 * expand_dims(sigma_t * (torch.exp(h) - 1.), dims) * D1_0 + ) + elif solver_type == 'taylor': + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_prev_0), dims) * x + - expand_dims(sigma_t * (torch.exp(h) - 1.), dims) * model_prev_0 + - expand_dims(sigma_t * ((torch.exp(h) - 1.) / h - 1.), dims) * D1_0 + ) + return x_t + + def multistep_dpm_solver_third_update(self, x, model_prev_list, t_prev_list, t, solver_type='dpm_solver'): + """ + Multistep solver DPM-Solver-3 from time `t_prev_list[-1]` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + model_prev_list: A list of pytorch tensor. The previous computed model values. + t_prev_list: A list of pytorch tensor. The previous times, each time has the shape (x.shape[0],) + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + ns = self.noise_schedule + dims = x.dim() + model_prev_2, model_prev_1, model_prev_0 = model_prev_list + t_prev_2, t_prev_1, t_prev_0 = t_prev_list + lambda_prev_2, lambda_prev_1, lambda_prev_0, lambda_t = ns.marginal_lambda(t_prev_2), ns.marginal_lambda( + t_prev_1), ns.marginal_lambda(t_prev_0), ns.marginal_lambda(t) + log_alpha_prev_0, log_alpha_t = ns.marginal_log_mean_coeff(t_prev_0), ns.marginal_log_mean_coeff(t) + sigma_prev_0, sigma_t = ns.marginal_std(t_prev_0), ns.marginal_std(t) + alpha_t = torch.exp(log_alpha_t) + + h_1 = lambda_prev_1 - lambda_prev_2 + h_0 = lambda_prev_0 - lambda_prev_1 + h = lambda_t - lambda_prev_0 + r0, r1 = h_0 / h, h_1 / h + D1_0 = expand_dims(1. / r0, dims) * (model_prev_0 - model_prev_1) + D1_1 = expand_dims(1. / r1, dims) * (model_prev_1 - model_prev_2) + D1 = D1_0 + expand_dims(r0 / (r0 + r1), dims) * (D1_0 - D1_1) + D2 = expand_dims(1. / (r0 + r1), dims) * (D1_0 - D1_1) + if self.predict_x0: + x_t = ( + expand_dims(sigma_t / sigma_prev_0, dims) * x + - expand_dims(alpha_t * (torch.exp(-h) - 1.), dims) * model_prev_0 + + expand_dims(alpha_t * ((torch.exp(-h) - 1.) / h + 1.), dims) * D1 + - expand_dims(alpha_t * ((torch.exp(-h) - 1. + h) / h ** 2 - 0.5), dims) * D2 + ) + else: + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_prev_0), dims) * x + - expand_dims(sigma_t * (torch.exp(h) - 1.), dims) * model_prev_0 + - expand_dims(sigma_t * ((torch.exp(h) - 1.) / h - 1.), dims) * D1 + - expand_dims(sigma_t * ((torch.exp(h) - 1. - h) / h ** 2 - 0.5), dims) * D2 + ) + return x_t + + def singlestep_dpm_solver_update(self, x, s, t, order, return_intermediate=False, solver_type='dpm_solver', r1=None, + r2=None): + """ + Singlestep DPM-Solver with the order `order` from time `s` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + s: A pytorch tensor. The starting time, with the shape (x.shape[0],). + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + order: A `int`. The order of DPM-Solver. We only support order == 1 or 2 or 3. + return_intermediate: A `bool`. If true, also return the model value at time `s`, `s1` and `s2` (the intermediate times). + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + r1: A `float`. The hyperparameter of the second-order or third-order solver. + r2: A `float`. The hyperparameter of the third-order solver. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + if order == 1: + return self.dpm_solver_first_update(x, s, t, return_intermediate=return_intermediate) + elif order == 2: + return self.singlestep_dpm_solver_second_update(x, s, t, return_intermediate=return_intermediate, + solver_type=solver_type, r1=r1) + elif order == 3: + return self.singlestep_dpm_solver_third_update(x, s, t, return_intermediate=return_intermediate, + solver_type=solver_type, r1=r1, r2=r2) + else: + raise ValueError("Solver order must be 1 or 2 or 3, got {}".format(order)) + + def multistep_dpm_solver_update(self, x, model_prev_list, t_prev_list, t, order, solver_type='dpm_solver'): + """ + Multistep DPM-Solver with the order `order` from time `t_prev_list[-1]` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + model_prev_list: A list of pytorch tensor. The previous computed model values. + t_prev_list: A list of pytorch tensor. The previous times, each time has the shape (x.shape[0],) + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + order: A `int`. The order of DPM-Solver. We only support order == 1 or 2 or 3. + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + if order == 1: + return self.dpm_solver_first_update(x, t_prev_list[-1], t, model_s=model_prev_list[-1]) + elif order == 2: + return self.multistep_dpm_solver_second_update(x, model_prev_list, t_prev_list, t, solver_type=solver_type) + elif order == 3: + return self.multistep_dpm_solver_third_update(x, model_prev_list, t_prev_list, t, solver_type=solver_type) + else: + raise ValueError("Solver order must be 1 or 2 or 3, got {}".format(order)) + + def dpm_solver_adaptive(self, x, order, t_T, t_0, h_init=0.05, atol=0.0078, rtol=0.05, theta=0.9, t_err=1e-5, + solver_type='dpm_solver'): + """ + The adaptive step size solver based on singlestep DPM-Solver. + Args: + x: A pytorch tensor. The initial value at time `t_T`. + order: A `int`. The (higher) order of the solver. We only support order == 2 or 3. + t_T: A `float`. The starting time of the sampling (default is T). + t_0: A `float`. The ending time of the sampling (default is epsilon). + h_init: A `float`. The initial step size (for logSNR). + atol: A `float`. The absolute tolerance of the solver. For image data, the default setting is 0.0078, followed [1]. + rtol: A `float`. The relative tolerance of the solver. The default setting is 0.05. + theta: A `float`. The safety hyperparameter for adapting the step size. The default setting is 0.9, followed [1]. + t_err: A `float`. The tolerance for the time. We solve the diffusion ODE until the absolute error between the + current time and `t_0` is less than `t_err`. The default setting is 1e-5. + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + Returns: + x_0: A pytorch tensor. The approximated solution at time `t_0`. + [1] A. Jolicoeur-Martineau, K. Li, R. Piché-Taillefer, T. Kachman, and I. Mitliagkas, "Gotta go fast when generating data with score-based models," arXiv preprint arXiv:2105.14080, 2021. + """ + ns = self.noise_schedule + s = t_T * torch.ones((x.shape[0],)).to(x) + lambda_s = ns.marginal_lambda(s) + lambda_0 = ns.marginal_lambda(t_0 * torch.ones_like(s).to(x)) + h = h_init * torch.ones_like(s).to(x) + x_prev = x + nfe = 0 + if order == 2: + r1 = 0.5 + lower_update = lambda x, s, t: self.dpm_solver_first_update(x, s, t, return_intermediate=True) + higher_update = lambda x, s, t, **kwargs: self.singlestep_dpm_solver_second_update(x, s, t, r1=r1, + solver_type=solver_type, + **kwargs) + elif order == 3: + r1, r2 = 1. / 3., 2. / 3. + lower_update = lambda x, s, t: self.singlestep_dpm_solver_second_update(x, s, t, r1=r1, + return_intermediate=True, + solver_type=solver_type) + higher_update = lambda x, s, t, **kwargs: self.singlestep_dpm_solver_third_update(x, s, t, r1=r1, r2=r2, + solver_type=solver_type, + **kwargs) + else: + raise ValueError("For adaptive step size solver, order must be 2 or 3, got {}".format(order)) + while torch.abs((s - t_0)).mean() > t_err: + t = ns.inverse_lambda(lambda_s + h) + x_lower, lower_noise_kwargs = lower_update(x, s, t) + x_higher = higher_update(x, s, t, **lower_noise_kwargs) + delta = torch.max(torch.ones_like(x).to(x) * atol, rtol * torch.max(torch.abs(x_lower), torch.abs(x_prev))) + norm_fn = lambda v: torch.sqrt(torch.square(v.reshape((v.shape[0], -1))).mean(dim=-1, keepdim=True)) + E = norm_fn((x_higher - x_lower) / delta).max() + if torch.all(E <= 1.): + x = x_higher + s = t + x_prev = x_lower + lambda_s = ns.marginal_lambda(s) + h = torch.min(theta * h * torch.float_power(E, -1. / order).float(), lambda_0 - lambda_s) + nfe += order + print('adaptive solver nfe', nfe) + return x + + def sample(self, x, steps=20, t_start=None, t_end=None, order=3, skip_type='time_uniform', + method='singlestep', lower_order_final=True, denoise_to_zero=False, solver_type='dpm_solver', + atol=0.0078, rtol=0.05, + ): + """ + Compute the sample at time `t_end` by DPM-Solver, given the initial `x` at time `t_start`. + ===================================================== + We support the following algorithms for both noise prediction model and data prediction model: + - 'singlestep': + Singlestep DPM-Solver (i.e. "DPM-Solver-fast" in the paper), which combines different orders of singlestep DPM-Solver. + We combine all the singlestep solvers with order <= `order` to use up all the function evaluations (steps). + The total number of function evaluations (NFE) == `steps`. + Given a fixed NFE == `steps`, the sampling procedure is: + - If `order` == 1: + - Denote K = steps. We use K steps of DPM-Solver-1 (i.e. DDIM). + - If `order` == 2: + - Denote K = (steps // 2) + (steps % 2). We take K intermediate time steps for sampling. + - If steps % 2 == 0, we use K steps of singlestep DPM-Solver-2. + - If steps % 2 == 1, we use (K - 1) steps of singlestep DPM-Solver-2 and 1 step of DPM-Solver-1. + - If `order` == 3: + - Denote K = (steps // 3 + 1). We take K intermediate time steps for sampling. + - If steps % 3 == 0, we use (K - 2) steps of singlestep DPM-Solver-3, and 1 step of singlestep DPM-Solver-2 and 1 step of DPM-Solver-1. + - If steps % 3 == 1, we use (K - 1) steps of singlestep DPM-Solver-3 and 1 step of DPM-Solver-1. + - If steps % 3 == 2, we use (K - 1) steps of singlestep DPM-Solver-3 and 1 step of singlestep DPM-Solver-2. + - 'multistep': + Multistep DPM-Solver with the order of `order`. The total number of function evaluations (NFE) == `steps`. + We initialize the first `order` values by lower order multistep solvers. + Given a fixed NFE == `steps`, the sampling procedure is: + Denote K = steps. + - If `order` == 1: + - We use K steps of DPM-Solver-1 (i.e. DDIM). + - If `order` == 2: + - We firstly use 1 step of DPM-Solver-1, then use (K - 1) step of multistep DPM-Solver-2. + - If `order` == 3: + - We firstly use 1 step of DPM-Solver-1, then 1 step of multistep DPM-Solver-2, then (K - 2) step of multistep DPM-Solver-3. + - 'singlestep_fixed': + Fixed order singlestep DPM-Solver (i.e. DPM-Solver-1 or singlestep DPM-Solver-2 or singlestep DPM-Solver-3). + We use singlestep DPM-Solver-`order` for `order`=1 or 2 or 3, with total [`steps` // `order`] * `order` NFE. + - 'adaptive': + Adaptive step size DPM-Solver (i.e. "DPM-Solver-12" and "DPM-Solver-23" in the paper). + We ignore `steps` and use adaptive step size DPM-Solver with a higher order of `order`. + You can adjust the absolute tolerance `atol` and the relative tolerance `rtol` to balance the computatation costs + (NFE) and the sample quality. + - If `order` == 2, we use DPM-Solver-12 which combines DPM-Solver-1 and singlestep DPM-Solver-2. + - If `order` == 3, we use DPM-Solver-23 which combines singlestep DPM-Solver-2 and singlestep DPM-Solver-3. + ===================================================== + Some advices for choosing the algorithm: + - For **unconditional sampling** or **guided sampling with small guidance scale** by DPMs: + Use singlestep DPM-Solver ("DPM-Solver-fast" in the paper) with `order = 3`. + e.g. + >>> dpm_solver = DPM_Solver(model_fn, noise_schedule, predict_x0=False) + >>> x_sample = dpm_solver.sample(x, steps=steps, t_start=t_start, t_end=t_end, order=3, + skip_type='time_uniform', method='singlestep') + - For **guided sampling with large guidance scale** by DPMs: + Use multistep DPM-Solver with `predict_x0 = True` and `order = 2`. + e.g. + >>> dpm_solver = DPM_Solver(model_fn, noise_schedule, predict_x0=True) + >>> x_sample = dpm_solver.sample(x, steps=steps, t_start=t_start, t_end=t_end, order=2, + skip_type='time_uniform', method='multistep') + We support three types of `skip_type`: + - 'logSNR': uniform logSNR for the time steps. **Recommended for low-resolutional images** + - 'time_uniform': uniform time for the time steps. **Recommended for high-resolutional images**. + - 'time_quadratic': quadratic time for the time steps. + ===================================================== + Args: + x: A pytorch tensor. The initial value at time `t_start` + e.g. if `t_start` == T, then `x` is a sample from the standard normal distribution. + steps: A `int`. The total number of function evaluations (NFE). + t_start: A `float`. The starting time of the sampling. + If `T` is None, we use self.noise_schedule.T (default is 1.0). + t_end: A `float`. The ending time of the sampling. + If `t_end` is None, we use 1. / self.noise_schedule.total_N. + e.g. if total_N == 1000, we have `t_end` == 1e-3. + For discrete-time DPMs: + - We recommend `t_end` == 1. / self.noise_schedule.total_N. + For continuous-time DPMs: + - We recommend `t_end` == 1e-3 when `steps` <= 15; and `t_end` == 1e-4 when `steps` > 15. + order: A `int`. The order of DPM-Solver. + skip_type: A `str`. The type for the spacing of the time steps. 'time_uniform' or 'logSNR' or 'time_quadratic'. + method: A `str`. The method for sampling. 'singlestep' or 'multistep' or 'singlestep_fixed' or 'adaptive'. + denoise_to_zero: A `bool`. Whether to denoise to time 0 at the final step. + Default is `False`. If `denoise_to_zero` is `True`, the total NFE is (`steps` + 1). + This trick is firstly proposed by DDPM (https://arxiv.org/abs/2006.11239) and + score_sde (https://arxiv.org/abs/2011.13456). Such trick can improve the FID + for diffusion models sampling by diffusion SDEs for low-resolutional images + (such as CIFAR-10). However, we observed that such trick does not matter for + high-resolutional images. As it needs an additional NFE, we do not recommend + it for high-resolutional images. + lower_order_final: A `bool`. Whether to use lower order solvers at the final steps. + Only valid for `method=multistep` and `steps < 15`. We empirically find that + this trick is a key to stabilizing the sampling by DPM-Solver with very few steps + (especially for steps <= 10). So we recommend to set it to be `True`. + solver_type: A `str`. The taylor expansion type for the solver. `dpm_solver` or `taylor`. We recommend `dpm_solver`. + atol: A `float`. The absolute tolerance of the adaptive step size solver. Valid when `method` == 'adaptive'. + rtol: A `float`. The relative tolerance of the adaptive step size solver. Valid when `method` == 'adaptive'. + Returns: + x_end: A pytorch tensor. The approximated solution at time `t_end`. + """ + t_0 = 1. / self.noise_schedule.total_N if t_end is None else t_end + t_T = self.noise_schedule.T if t_start is None else t_start + device = x.device + if method == 'adaptive': + with torch.no_grad(): + x = self.dpm_solver_adaptive(x, order=order, t_T=t_T, t_0=t_0, atol=atol, rtol=rtol, + solver_type=solver_type) + elif method == 'multistep': + assert steps >= order + timesteps = self.get_time_steps(skip_type=skip_type, t_T=t_T, t_0=t_0, N=steps, device=device) + assert timesteps.shape[0] - 1 == steps + with torch.no_grad(): + vec_t = timesteps[0].expand((x.shape[0])) + model_prev_list = [self.model_fn(x, vec_t)] + t_prev_list = [vec_t] + # Init the first `order` values by lower order multistep DPM-Solver. + for init_order in tqdm(range(1, order), desc="DPM init order"): + vec_t = timesteps[init_order].expand(x.shape[0]) + x = self.multistep_dpm_solver_update(x, model_prev_list, t_prev_list, vec_t, init_order, + solver_type=solver_type) + model_prev_list.append(self.model_fn(x, vec_t)) + t_prev_list.append(vec_t) + # Compute the remaining values by `order`-th order multistep DPM-Solver. + for step in tqdm(range(order, steps + 1), desc="DPM multistep"): + vec_t = timesteps[step].expand(x.shape[0]) + if lower_order_final and steps < 15: + step_order = min(order, steps + 1 - step) + else: + step_order = order + x = self.multistep_dpm_solver_update(x, model_prev_list, t_prev_list, vec_t, step_order, + solver_type=solver_type) + for i in range(order - 1): + t_prev_list[i] = t_prev_list[i + 1] + model_prev_list[i] = model_prev_list[i + 1] + t_prev_list[-1] = vec_t + # We do not need to evaluate the final model value. + if step < steps: + model_prev_list[-1] = self.model_fn(x, vec_t) + elif method in ['singlestep', 'singlestep_fixed']: + if method == 'singlestep': + timesteps_outer, orders = self.get_orders_and_timesteps_for_singlestep_solver(steps=steps, order=order, + skip_type=skip_type, + t_T=t_T, t_0=t_0, + device=device) + elif method == 'singlestep_fixed': + K = steps // order + orders = [order, ] * K + timesteps_outer = self.get_time_steps(skip_type=skip_type, t_T=t_T, t_0=t_0, N=K, device=device) + for i, order in enumerate(orders): + t_T_inner, t_0_inner = timesteps_outer[i], timesteps_outer[i + 1] + timesteps_inner = self.get_time_steps(skip_type=skip_type, t_T=t_T_inner.item(), t_0=t_0_inner.item(), + N=order, device=device) + lambda_inner = self.noise_schedule.marginal_lambda(timesteps_inner) + vec_s, vec_t = t_T_inner.tile(x.shape[0]), t_0_inner.tile(x.shape[0]) + h = lambda_inner[-1] - lambda_inner[0] + r1 = None if order <= 1 else (lambda_inner[1] - lambda_inner[0]) / h + r2 = None if order <= 2 else (lambda_inner[2] - lambda_inner[0]) / h + x = self.singlestep_dpm_solver_update(x, vec_s, vec_t, order, solver_type=solver_type, r1=r1, r2=r2) + if denoise_to_zero: + x = self.denoise_to_zero_fn(x, torch.ones((x.shape[0],)).to(device) * t_0) + return x + + +############################################################# +# other utility functions +############################################################# + +def interpolate_fn(x, xp, yp): + """ + A piecewise linear function y = f(x), using xp and yp as keypoints. + We implement f(x) in a differentiable way (i.e. applicable for autograd). + The function f(x) is well-defined for all x-axis. (For x beyond the bounds of xp, we use the outmost points of xp to define the linear function.) + Args: + x: PyTorch tensor with shape [N, C], where N is the batch size, C is the number of channels (we use C = 1 for DPM-Solver). + xp: PyTorch tensor with shape [C, K], where K is the number of keypoints. + yp: PyTorch tensor with shape [C, K]. + Returns: + The function values f(x), with shape [N, C]. + """ + N, K = x.shape[0], xp.shape[1] + all_x = torch.cat([x.unsqueeze(2), xp.unsqueeze(0).repeat((N, 1, 1))], dim=2) + sorted_all_x, x_indices = torch.sort(all_x, dim=2) + x_idx = torch.argmin(x_indices, dim=2) + cand_start_idx = x_idx - 1 + start_idx = torch.where( + torch.eq(x_idx, 0), + torch.tensor(1, device=x.device), + torch.where( + torch.eq(x_idx, K), torch.tensor(K - 2, device=x.device), cand_start_idx, + ), + ) + end_idx = torch.where(torch.eq(start_idx, cand_start_idx), start_idx + 2, start_idx + 1) + start_x = torch.gather(sorted_all_x, dim=2, index=start_idx.unsqueeze(2)).squeeze(2) + end_x = torch.gather(sorted_all_x, dim=2, index=end_idx.unsqueeze(2)).squeeze(2) + start_idx2 = torch.where( + torch.eq(x_idx, 0), + torch.tensor(0, device=x.device), + torch.where( + torch.eq(x_idx, K), torch.tensor(K - 2, device=x.device), cand_start_idx, + ), + ) + y_positions_expanded = yp.unsqueeze(0).expand(N, -1, -1) + start_y = torch.gather(y_positions_expanded, dim=2, index=start_idx2.unsqueeze(2)).squeeze(2) + end_y = torch.gather(y_positions_expanded, dim=2, index=(start_idx2 + 1).unsqueeze(2)).squeeze(2) + cand = start_y + (x - start_x) * (end_y - start_y) / (end_x - start_x) + return cand + + +def expand_dims(v, dims): + """ + Expand the tensor `v` to the dim `dims`. + Args: + `v`: a PyTorch tensor with shape [N]. + `dim`: a `int`. + Returns: + a PyTorch tensor with shape [N, 1, 1, ..., 1] and the total dimension is `dims`. + """ + return v[(...,) + (None,) * (dims - 1)] \ No newline at end of file diff --git a/sd/stablediffusion/ldm/models/diffusion/dpm_solver/sampler.py b/sd/stablediffusion/ldm/models/diffusion/dpm_solver/sampler.py new file mode 100644 index 0000000000000000000000000000000000000000..7d137b8cf36718c1c58faa09f9dd919e5fb2977b --- /dev/null +++ b/sd/stablediffusion/ldm/models/diffusion/dpm_solver/sampler.py @@ -0,0 +1,87 @@ +"""SAMPLING ONLY.""" +import torch + +from .dpm_solver import NoiseScheduleVP, model_wrapper, DPM_Solver + + +MODEL_TYPES = { + "eps": "noise", + "v": "v" +} + + +class DPMSolverSampler(object): + def __init__(self, model, **kwargs): + super().__init__() + self.model = model + to_torch = lambda x: x.clone().detach().to(torch.float32).to(model.device) + self.register_buffer('alphas_cumprod', to_torch(model.alphas_cumprod)) + + def register_buffer(self, name, attr): + if type(attr) == torch.Tensor: + if attr.device != torch.device("cuda"): + attr = attr.to(torch.device("cuda")) + setattr(self, name, attr) + + @torch.no_grad() + def sample(self, + S, + batch_size, + shape, + conditioning=None, + callback=None, + normals_sequence=None, + img_callback=None, + quantize_x0=False, + eta=0., + mask=None, + x0=None, + temperature=1., + noise_dropout=0., + score_corrector=None, + corrector_kwargs=None, + verbose=True, + x_T=None, + log_every_t=100, + unconditional_guidance_scale=1., + unconditional_conditioning=None, + # this has to come in the same format as the conditioning, # e.g. as encoded tokens, ... + **kwargs + ): + if conditioning is not None: + if isinstance(conditioning, dict): + cbs = conditioning[list(conditioning.keys())[0]].shape[0] + if cbs != batch_size: + print(f"Warning: Got {cbs} conditionings but batch-size is {batch_size}") + else: + if conditioning.shape[0] != batch_size: + print(f"Warning: Got {conditioning.shape[0]} conditionings but batch-size is {batch_size}") + + # sampling + C, H, W = shape + size = (batch_size, C, H, W) + + print(f'Data shape for DPM-Solver sampling is {size}, sampling steps {S}') + + device = self.model.betas.device + if x_T is None: + img = torch.randn(size, device=device) + else: + img = x_T + + ns = NoiseScheduleVP('discrete', alphas_cumprod=self.alphas_cumprod) + + model_fn = model_wrapper( + lambda x, t, c: self.model.apply_model(x, t, c), + ns, + model_type=MODEL_TYPES[self.model.parameterization], + guidance_type="classifier-free", + condition=conditioning, + unconditional_condition=unconditional_conditioning, + guidance_scale=unconditional_guidance_scale, + ) + + dpm_solver = DPM_Solver(model_fn, ns, predict_x0=True, thresholding=False) + x = dpm_solver.sample(img, steps=S, skip_type="time_uniform", method="multistep", order=2, lower_order_final=True) + + return x.to(device), None \ No newline at end of file diff --git a/sd/stablediffusion/ldm/models/diffusion/plms.py b/sd/stablediffusion/ldm/models/diffusion/plms.py new file mode 100644 index 0000000000000000000000000000000000000000..7002a365d27168ced0a04e9a4d83e088f8284eae --- /dev/null +++ b/sd/stablediffusion/ldm/models/diffusion/plms.py @@ -0,0 +1,244 @@ +"""SAMPLING ONLY.""" + +import torch +import numpy as np +from tqdm import tqdm +from functools import partial + +from ldm.modules.diffusionmodules.util import make_ddim_sampling_parameters, make_ddim_timesteps, noise_like +from ldm.models.diffusion.sampling_util import norm_thresholding + + +class PLMSSampler(object): + def __init__(self, model, schedule="linear", **kwargs): + super().__init__() + self.model = model + self.ddpm_num_timesteps = model.num_timesteps + self.schedule = schedule + + def register_buffer(self, name, attr): + if type(attr) == torch.Tensor: + if attr.device != torch.device("cuda"): + attr = attr.to(torch.device("cuda")) + setattr(self, name, attr) + + def make_schedule(self, ddim_num_steps, ddim_discretize="uniform", ddim_eta=0., verbose=True): + if ddim_eta != 0: + raise ValueError('ddim_eta must be 0 for PLMS') + self.ddim_timesteps = make_ddim_timesteps(ddim_discr_method=ddim_discretize, num_ddim_timesteps=ddim_num_steps, + num_ddpm_timesteps=self.ddpm_num_timesteps,verbose=verbose) + alphas_cumprod = self.model.alphas_cumprod + assert alphas_cumprod.shape[0] == self.ddpm_num_timesteps, 'alphas have to be defined for each timestep' + to_torch = lambda x: x.clone().detach().to(torch.float32).to(self.model.device) + + self.register_buffer('betas', to_torch(self.model.betas)) + self.register_buffer('alphas_cumprod', to_torch(alphas_cumprod)) + self.register_buffer('alphas_cumprod_prev', to_torch(self.model.alphas_cumprod_prev)) + + # calculations for diffusion q(x_t | x_{t-1}) and others + self.register_buffer('sqrt_alphas_cumprod', to_torch(np.sqrt(alphas_cumprod.cpu()))) + self.register_buffer('sqrt_one_minus_alphas_cumprod', to_torch(np.sqrt(1. - alphas_cumprod.cpu()))) + self.register_buffer('log_one_minus_alphas_cumprod', to_torch(np.log(1. - alphas_cumprod.cpu()))) + self.register_buffer('sqrt_recip_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod.cpu()))) + self.register_buffer('sqrt_recipm1_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod.cpu() - 1))) + + # ddim sampling parameters + ddim_sigmas, ddim_alphas, ddim_alphas_prev = make_ddim_sampling_parameters(alphacums=alphas_cumprod.cpu(), + ddim_timesteps=self.ddim_timesteps, + eta=ddim_eta,verbose=verbose) + self.register_buffer('ddim_sigmas', ddim_sigmas) + self.register_buffer('ddim_alphas', ddim_alphas) + self.register_buffer('ddim_alphas_prev', ddim_alphas_prev) + self.register_buffer('ddim_sqrt_one_minus_alphas', np.sqrt(1. - ddim_alphas)) + sigmas_for_original_sampling_steps = ddim_eta * torch.sqrt( + (1 - self.alphas_cumprod_prev) / (1 - self.alphas_cumprod) * ( + 1 - self.alphas_cumprod / self.alphas_cumprod_prev)) + self.register_buffer('ddim_sigmas_for_original_num_steps', sigmas_for_original_sampling_steps) + + @torch.no_grad() + def sample(self, + S, + batch_size, + shape, + conditioning=None, + callback=None, + normals_sequence=None, + img_callback=None, + quantize_x0=False, + eta=0., + mask=None, + x0=None, + temperature=1., + noise_dropout=0., + score_corrector=None, + corrector_kwargs=None, + verbose=True, + x_T=None, + log_every_t=100, + unconditional_guidance_scale=1., + unconditional_conditioning=None, + # this has to come in the same format as the conditioning, # e.g. as encoded tokens, ... + dynamic_threshold=None, + **kwargs + ): + if conditioning is not None: + if isinstance(conditioning, dict): + cbs = conditioning[list(conditioning.keys())[0]].shape[0] + if cbs != batch_size: + print(f"Warning: Got {cbs} conditionings but batch-size is {batch_size}") + else: + if conditioning.shape[0] != batch_size: + print(f"Warning: Got {conditioning.shape[0]} conditionings but batch-size is {batch_size}") + + self.make_schedule(ddim_num_steps=S, ddim_eta=eta, verbose=verbose) + # sampling + C, H, W = shape + size = (batch_size, C, H, W) + print(f'Data shape for PLMS sampling is {size}') + + samples, intermediates = self.plms_sampling(conditioning, size, + callback=callback, + img_callback=img_callback, + quantize_denoised=quantize_x0, + mask=mask, x0=x0, + ddim_use_original_steps=False, + noise_dropout=noise_dropout, + temperature=temperature, + score_corrector=score_corrector, + corrector_kwargs=corrector_kwargs, + x_T=x_T, + log_every_t=log_every_t, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=unconditional_conditioning, + dynamic_threshold=dynamic_threshold, + ) + return samples, intermediates + + @torch.no_grad() + def plms_sampling(self, cond, shape, + x_T=None, ddim_use_original_steps=False, + callback=None, timesteps=None, quantize_denoised=False, + mask=None, x0=None, img_callback=None, log_every_t=100, + temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None, + unconditional_guidance_scale=1., unconditional_conditioning=None, + dynamic_threshold=None): + device = self.model.betas.device + b = shape[0] + if x_T is None: + img = torch.randn(shape, device=device) + else: + img = x_T + + if timesteps is None: + timesteps = self.ddpm_num_timesteps if ddim_use_original_steps else self.ddim_timesteps + elif timesteps is not None and not ddim_use_original_steps: + subset_end = int(min(timesteps / self.ddim_timesteps.shape[0], 1) * self.ddim_timesteps.shape[0]) - 1 + timesteps = self.ddim_timesteps[:subset_end] + + intermediates = {'x_inter': [img], 'pred_x0': [img]} + time_range = list(reversed(range(0,timesteps))) if ddim_use_original_steps else np.flip(timesteps) + total_steps = timesteps if ddim_use_original_steps else timesteps.shape[0] + print(f"Running PLMS Sampling with {total_steps} timesteps") + + iterator = tqdm(time_range, desc='PLMS Sampler', total=total_steps) + old_eps = [] + + for i, step in enumerate(iterator): + index = total_steps - i - 1 + ts = torch.full((b,), step, device=device, dtype=torch.long) + ts_next = torch.full((b,), time_range[min(i + 1, len(time_range) - 1)], device=device, dtype=torch.long) + + if mask is not None: + assert x0 is not None + img_orig = self.model.q_sample(x0, ts) # TODO: deterministic forward pass? + img = img_orig * mask + (1. - mask) * img + + outs = self.p_sample_plms(img, cond, ts, index=index, use_original_steps=ddim_use_original_steps, + quantize_denoised=quantize_denoised, temperature=temperature, + noise_dropout=noise_dropout, score_corrector=score_corrector, + corrector_kwargs=corrector_kwargs, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=unconditional_conditioning, + old_eps=old_eps, t_next=ts_next, + dynamic_threshold=dynamic_threshold) + img, pred_x0, e_t = outs + old_eps.append(e_t) + if len(old_eps) >= 4: + old_eps.pop(0) + if callback: callback(i) + if img_callback: img_callback(pred_x0, i) + + if index % log_every_t == 0 or index == total_steps - 1: + intermediates['x_inter'].append(img) + intermediates['pred_x0'].append(pred_x0) + + return img, intermediates + + @torch.no_grad() + def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False, + temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None, + unconditional_guidance_scale=1., unconditional_conditioning=None, old_eps=None, t_next=None, + dynamic_threshold=None): + b, *_, device = *x.shape, x.device + + def get_model_output(x, t): + if unconditional_conditioning is None or unconditional_guidance_scale == 1.: + e_t = self.model.apply_model(x, t, c) + else: + x_in = torch.cat([x] * 2) + t_in = torch.cat([t] * 2) + c_in = torch.cat([unconditional_conditioning, c]) + e_t_uncond, e_t = self.model.apply_model(x_in, t_in, c_in).chunk(2) + e_t = e_t_uncond + unconditional_guidance_scale * (e_t - e_t_uncond) + + if score_corrector is not None: + assert self.model.parameterization == "eps" + e_t = score_corrector.modify_score(self.model, e_t, x, t, c, **corrector_kwargs) + + return e_t + + alphas = self.model.alphas_cumprod if use_original_steps else self.ddim_alphas + alphas_prev = self.model.alphas_cumprod_prev if use_original_steps else self.ddim_alphas_prev + sqrt_one_minus_alphas = self.model.sqrt_one_minus_alphas_cumprod if use_original_steps else self.ddim_sqrt_one_minus_alphas + sigmas = self.model.ddim_sigmas_for_original_num_steps if use_original_steps else self.ddim_sigmas + + def get_x_prev_and_pred_x0(e_t, index): + # select parameters corresponding to the currently considered timestep + a_t = torch.full((b, 1, 1, 1), alphas[index], device=device) + a_prev = torch.full((b, 1, 1, 1), alphas_prev[index], device=device) + sigma_t = torch.full((b, 1, 1, 1), sigmas[index], device=device) + sqrt_one_minus_at = torch.full((b, 1, 1, 1), sqrt_one_minus_alphas[index],device=device) + + # current prediction for x_0 + pred_x0 = (x - sqrt_one_minus_at * e_t) / a_t.sqrt() + if quantize_denoised: + pred_x0, _, *_ = self.model.first_stage_model.quantize(pred_x0) + if dynamic_threshold is not None: + pred_x0 = norm_thresholding(pred_x0, dynamic_threshold) + # direction pointing to x_t + dir_xt = (1. - a_prev - sigma_t**2).sqrt() * e_t + noise = sigma_t * noise_like(x.shape, device, repeat_noise) * temperature + if noise_dropout > 0.: + noise = torch.nn.functional.dropout(noise, p=noise_dropout) + x_prev = a_prev.sqrt() * pred_x0 + dir_xt + noise + return x_prev, pred_x0 + + e_t = get_model_output(x, t) + if len(old_eps) == 0: + # Pseudo Improved Euler (2nd order) + x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t, index) + e_t_next = get_model_output(x_prev, t_next) + e_t_prime = (e_t + e_t_next) / 2 + elif len(old_eps) == 1: + # 2nd order Pseudo Linear Multistep (Adams-Bashforth) + e_t_prime = (3 * e_t - old_eps[-1]) / 2 + elif len(old_eps) == 2: + # 3nd order Pseudo Linear Multistep (Adams-Bashforth) + e_t_prime = (23 * e_t - 16 * old_eps[-1] + 5 * old_eps[-2]) / 12 + elif len(old_eps) >= 3: + # 4nd order Pseudo Linear Multistep (Adams-Bashforth) + e_t_prime = (55 * e_t - 59 * old_eps[-1] + 37 * old_eps[-2] - 9 * old_eps[-3]) / 24 + + x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t_prime, index) + + return x_prev, pred_x0, e_t diff --git a/sd/stablediffusion/ldm/models/diffusion/sampling_util.py b/sd/stablediffusion/ldm/models/diffusion/sampling_util.py new file mode 100644 index 0000000000000000000000000000000000000000..7eff02be6d7c54d43ee6680636ac0698dd3b3f33 --- /dev/null +++ b/sd/stablediffusion/ldm/models/diffusion/sampling_util.py @@ -0,0 +1,22 @@ +import torch +import numpy as np + + +def append_dims(x, target_dims): + """Appends dimensions to the end of a tensor until it has target_dims dimensions. + From https://github.com/crowsonkb/k-diffusion/blob/master/k_diffusion/utils.py""" + dims_to_append = target_dims - x.ndim + if dims_to_append < 0: + raise ValueError(f'input has {x.ndim} dims but target_dims is {target_dims}, which is less') + return x[(...,) + (None,) * dims_to_append] + + +def norm_thresholding(x0, value): + s = append_dims(x0.pow(2).flatten(1).mean(1).sqrt().clamp(min=value), x0.ndim) + return x0 * (value / s) + + +def spatial_norm_thresholding(x0, value): + # b c h w + s = x0.pow(2).mean(1, keepdim=True).sqrt().clamp(min=value) + return x0 * (value / s) \ No newline at end of file diff --git a/sd/stablediffusion/ldm/modules/__pycache__/attention.cpython-39.pyc b/sd/stablediffusion/ldm/modules/__pycache__/attention.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..628842378fb1309fbc090612a84030b46e780c2c Binary files /dev/null and b/sd/stablediffusion/ldm/modules/__pycache__/attention.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/__pycache__/ema.cpython-39.pyc b/sd/stablediffusion/ldm/modules/__pycache__/ema.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..40b2da3fcbce647eda57ad370d0fe244f7960dfb Binary files /dev/null and b/sd/stablediffusion/ldm/modules/__pycache__/ema.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/attention.py b/sd/stablediffusion/ldm/modules/attention.py new file mode 100644 index 0000000000000000000000000000000000000000..509cd873768f0dd75a75ab3fcdd652822b12b59f --- /dev/null +++ b/sd/stablediffusion/ldm/modules/attention.py @@ -0,0 +1,341 @@ +from inspect import isfunction +import math +import torch +import torch.nn.functional as F +from torch import nn, einsum +from einops import rearrange, repeat +from typing import Optional, Any + +from ldm.modules.diffusionmodules.util import checkpoint + + +try: + import xformers + import xformers.ops + XFORMERS_IS_AVAILBLE = True +except: + XFORMERS_IS_AVAILBLE = False + +# CrossAttn precision handling +import os +_ATTN_PRECISION = os.environ.get("ATTN_PRECISION", "fp32") + +def exists(val): + return val is not None + + +def uniq(arr): + return{el: True for el in arr}.keys() + + +def default(val, d): + if exists(val): + return val + return d() if isfunction(d) else d + + +def max_neg_value(t): + return -torch.finfo(t.dtype).max + + +def init_(tensor): + dim = tensor.shape[-1] + std = 1 / math.sqrt(dim) + tensor.uniform_(-std, std) + return tensor + + +# feedforward +class GEGLU(nn.Module): + def __init__(self, dim_in, dim_out): + super().__init__() + self.proj = nn.Linear(dim_in, dim_out * 2) + + def forward(self, x): + x, gate = self.proj(x).chunk(2, dim=-1) + return x * F.gelu(gate) + + +class FeedForward(nn.Module): + def __init__(self, dim, dim_out=None, mult=4, glu=False, dropout=0.): + super().__init__() + inner_dim = int(dim * mult) + dim_out = default(dim_out, dim) + project_in = nn.Sequential( + nn.Linear(dim, inner_dim), + nn.GELU() + ) if not glu else GEGLU(dim, inner_dim) + + self.net = nn.Sequential( + project_in, + nn.Dropout(dropout), + nn.Linear(inner_dim, dim_out) + ) + + def forward(self, x): + return self.net(x) + + +def zero_module(module): + """ + Zero out the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().zero_() + return module + + +def Normalize(in_channels): + return torch.nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True) + + +class SpatialSelfAttention(nn.Module): + def __init__(self, in_channels): + super().__init__() + self.in_channels = in_channels + + self.norm = Normalize(in_channels) + self.q = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.k = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.v = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.proj_out = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + + def forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + # compute attention + b,c,h,w = q.shape + q = rearrange(q, 'b c h w -> b (h w) c') + k = rearrange(k, 'b c h w -> b c (h w)') + w_ = torch.einsum('bij,bjk->bik', q, k) + + w_ = w_ * (int(c)**(-0.5)) + w_ = torch.nn.functional.softmax(w_, dim=2) + + # attend to values + v = rearrange(v, 'b c h w -> b c (h w)') + w_ = rearrange(w_, 'b i j -> b j i') + h_ = torch.einsum('bij,bjk->bik', v, w_) + h_ = rearrange(h_, 'b c (h w) -> b c h w', h=h) + h_ = self.proj_out(h_) + + return x+h_ + + +class CrossAttention(nn.Module): + def __init__(self, query_dim, context_dim=None, heads=8, dim_head=64, dropout=0.): + super().__init__() + inner_dim = dim_head * heads + context_dim = default(context_dim, query_dim) + + self.scale = dim_head ** -0.5 + self.heads = heads + + self.to_q = nn.Linear(query_dim, inner_dim, bias=False) + self.to_k = nn.Linear(context_dim, inner_dim, bias=False) + self.to_v = nn.Linear(context_dim, inner_dim, bias=False) + + self.to_out = nn.Sequential( + nn.Linear(inner_dim, query_dim), + nn.Dropout(dropout) + ) + + def forward(self, x, context=None, mask=None): + h = self.heads + + q = self.to_q(x) + context = default(context, x) + k = self.to_k(context) + v = self.to_v(context) + + q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h=h), (q, k, v)) + + # force cast to fp32 to avoid overflowing + if _ATTN_PRECISION =="fp32": + with torch.autocast(enabled=False, device_type = 'cuda'): + q, k = q.float(), k.float() + sim = einsum('b i d, b j d -> b i j', q, k) * self.scale + else: + sim = einsum('b i d, b j d -> b i j', q, k) * self.scale + + del q, k + + if exists(mask): + mask = rearrange(mask, 'b ... -> b (...)') + max_neg_value = -torch.finfo(sim.dtype).max + mask = repeat(mask, 'b j -> (b h) () j', h=h) + sim.masked_fill_(~mask, max_neg_value) + + # attention, what we cannot get enough of + sim = sim.softmax(dim=-1) + + out = einsum('b i j, b j d -> b i d', sim, v) + out = rearrange(out, '(b h) n d -> b n (h d)', h=h) + return self.to_out(out) + + +class MemoryEfficientCrossAttention(nn.Module): + # https://github.com/MatthieuTPHR/diffusers/blob/d80b531ff8060ec1ea982b65a1b8df70f73aa67c/src/diffusers/models/attention.py#L223 + def __init__(self, query_dim, context_dim=None, heads=8, dim_head=64, dropout=0.0): + super().__init__() + print(f"Setting up {self.__class__.__name__}. Query dim is {query_dim}, context_dim is {context_dim} and using " + f"{heads} heads.") + inner_dim = dim_head * heads + context_dim = default(context_dim, query_dim) + + self.heads = heads + self.dim_head = dim_head + + self.to_q = nn.Linear(query_dim, inner_dim, bias=False) + self.to_k = nn.Linear(context_dim, inner_dim, bias=False) + self.to_v = nn.Linear(context_dim, inner_dim, bias=False) + + self.to_out = nn.Sequential(nn.Linear(inner_dim, query_dim), nn.Dropout(dropout)) + self.attention_op: Optional[Any] = None + + def forward(self, x, context=None, mask=None): + q = self.to_q(x) + context = default(context, x) + k = self.to_k(context) + v = self.to_v(context) + + b, _, _ = q.shape + q, k, v = map( + lambda t: t.unsqueeze(3) + .reshape(b, t.shape[1], self.heads, self.dim_head) + .permute(0, 2, 1, 3) + .reshape(b * self.heads, t.shape[1], self.dim_head) + .contiguous(), + (q, k, v), + ) + + # actually compute the attention, what we cannot get enough of + out = xformers.ops.memory_efficient_attention(q, k, v, attn_bias=None, op=self.attention_op) + + if exists(mask): + raise NotImplementedError + out = ( + out.unsqueeze(0) + .reshape(b, self.heads, out.shape[1], self.dim_head) + .permute(0, 2, 1, 3) + .reshape(b, out.shape[1], self.heads * self.dim_head) + ) + return self.to_out(out) + + +class BasicTransformerBlock(nn.Module): + ATTENTION_MODES = { + "softmax": CrossAttention, # vanilla attention + "softmax-xformers": MemoryEfficientCrossAttention + } + def __init__(self, dim, n_heads, d_head, dropout=0., context_dim=None, gated_ff=True, checkpoint=True, + disable_self_attn=False): + super().__init__() + attn_mode = "softmax-xformers" if XFORMERS_IS_AVAILBLE else "softmax" + assert attn_mode in self.ATTENTION_MODES + attn_cls = self.ATTENTION_MODES[attn_mode] + self.disable_self_attn = disable_self_attn + self.attn1 = attn_cls(query_dim=dim, heads=n_heads, dim_head=d_head, dropout=dropout, + context_dim=context_dim if self.disable_self_attn else None) # is a self-attention if not self.disable_self_attn + self.ff = FeedForward(dim, dropout=dropout, glu=gated_ff) + self.attn2 = attn_cls(query_dim=dim, context_dim=context_dim, + heads=n_heads, dim_head=d_head, dropout=dropout) # is self-attn if context is none + self.norm1 = nn.LayerNorm(dim) + self.norm2 = nn.LayerNorm(dim) + self.norm3 = nn.LayerNorm(dim) + self.checkpoint = checkpoint + + def forward(self, x, context=None): + return checkpoint(self._forward, (x, context), self.parameters(), self.checkpoint) + + def _forward(self, x, context=None): + x = self.attn1(self.norm1(x), context=context if self.disable_self_attn else None) + x + x = self.attn2(self.norm2(x), context=context) + x + x = self.ff(self.norm3(x)) + x + return x + + +class SpatialTransformer(nn.Module): + """ + Transformer block for image-like data. + First, project the input (aka embedding) + and reshape to b, t, d. + Then apply standard transformer action. + Finally, reshape to image + NEW: use_linear for more efficiency instead of the 1x1 convs + """ + def __init__(self, in_channels, n_heads, d_head, + depth=1, dropout=0., context_dim=None, + disable_self_attn=False, use_linear=False, + use_checkpoint=True): + super().__init__() + if exists(context_dim) and not isinstance(context_dim, list): + context_dim = [context_dim] + self.in_channels = in_channels + inner_dim = n_heads * d_head + self.norm = Normalize(in_channels) + if not use_linear: + self.proj_in = nn.Conv2d(in_channels, + inner_dim, + kernel_size=1, + stride=1, + padding=0) + else: + self.proj_in = nn.Linear(in_channels, inner_dim) + + self.transformer_blocks = nn.ModuleList( + [BasicTransformerBlock(inner_dim, n_heads, d_head, dropout=dropout, context_dim=context_dim[d], + disable_self_attn=disable_self_attn, checkpoint=use_checkpoint) + for d in range(depth)] + ) + if not use_linear: + self.proj_out = zero_module(nn.Conv2d(inner_dim, + in_channels, + kernel_size=1, + stride=1, + padding=0)) + else: + self.proj_out = zero_module(nn.Linear(in_channels, inner_dim)) + self.use_linear = use_linear + + def forward(self, x, context=None): + # note: if no context is given, cross-attention defaults to self-attention + if not isinstance(context, list): + context = [context] + b, c, h, w = x.shape + x_in = x + x = self.norm(x) + if not self.use_linear: + x = self.proj_in(x) + x = rearrange(x, 'b c h w -> b (h w) c').contiguous() + if self.use_linear: + x = self.proj_in(x) + for i, block in enumerate(self.transformer_blocks): + x = block(x, context=context[i]) + if self.use_linear: + x = self.proj_out(x) + x = rearrange(x, 'b (h w) c -> b c h w', h=h, w=w).contiguous() + if not self.use_linear: + x = self.proj_out(x) + return x + x_in + diff --git a/sd/stablediffusion/ldm/modules/diffusionmodules/__init__.py b/sd/stablediffusion/ldm/modules/diffusionmodules/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/ldm/modules/diffusionmodules/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/ldm/modules/diffusionmodules/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..88141577efb334d3426e4541f4295daafcdd9b31 Binary files /dev/null and b/sd/stablediffusion/ldm/modules/diffusionmodules/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/diffusionmodules/__pycache__/model.cpython-39.pyc b/sd/stablediffusion/ldm/modules/diffusionmodules/__pycache__/model.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07bc0fcc17da7a8f21e9d78a83eb14fbd8bc216d Binary files /dev/null and b/sd/stablediffusion/ldm/modules/diffusionmodules/__pycache__/model.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/diffusionmodules/__pycache__/openaimodel.cpython-39.pyc b/sd/stablediffusion/ldm/modules/diffusionmodules/__pycache__/openaimodel.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b42af95cd749d6760b9af78bfa67720b3580d6d Binary files /dev/null and b/sd/stablediffusion/ldm/modules/diffusionmodules/__pycache__/openaimodel.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/diffusionmodules/__pycache__/util.cpython-39.pyc b/sd/stablediffusion/ldm/modules/diffusionmodules/__pycache__/util.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..153dd89f45890ca2f12812f88a8a0bf3acecea41 Binary files /dev/null and b/sd/stablediffusion/ldm/modules/diffusionmodules/__pycache__/util.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/diffusionmodules/model.py b/sd/stablediffusion/ldm/modules/diffusionmodules/model.py new file mode 100644 index 0000000000000000000000000000000000000000..b089eebbe1676d8249005bb9def002ff5180715b --- /dev/null +++ b/sd/stablediffusion/ldm/modules/diffusionmodules/model.py @@ -0,0 +1,852 @@ +# pytorch_diffusion + derived encoder decoder +import math +import torch +import torch.nn as nn +import numpy as np +from einops import rearrange +from typing import Optional, Any + +from ldm.modules.attention import MemoryEfficientCrossAttention + +try: + import xformers + import xformers.ops + XFORMERS_IS_AVAILBLE = True +except: + XFORMERS_IS_AVAILBLE = False + print("No module 'xformers'. Proceeding without it.") + + +def get_timestep_embedding(timesteps, embedding_dim): + """ + This matches the implementation in Denoising Diffusion Probabilistic Models: + From Fairseq. + Build sinusoidal embeddings. + This matches the implementation in tensor2tensor, but differs slightly + from the description in Section 3.5 of "Attention Is All You Need". + """ + assert len(timesteps.shape) == 1 + + half_dim = embedding_dim // 2 + emb = math.log(10000) / (half_dim - 1) + emb = torch.exp(torch.arange(half_dim, dtype=torch.float32) * -emb) + emb = emb.to(device=timesteps.device) + emb = timesteps.float()[:, None] * emb[None, :] + emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=1) + if embedding_dim % 2 == 1: # zero pad + emb = torch.nn.functional.pad(emb, (0,1,0,0)) + return emb + + +def nonlinearity(x): + # swish + return x*torch.sigmoid(x) + + +def Normalize(in_channels, num_groups=32): + return torch.nn.GroupNorm(num_groups=num_groups, num_channels=in_channels, eps=1e-6, affine=True) + + +class Upsample(nn.Module): + def __init__(self, in_channels, with_conv): + super().__init__() + self.with_conv = with_conv + if self.with_conv: + self.conv = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + x = torch.nn.functional.interpolate(x, scale_factor=2.0, mode="nearest") + if self.with_conv: + x = self.conv(x) + return x + + +class Downsample(nn.Module): + def __init__(self, in_channels, with_conv): + super().__init__() + self.with_conv = with_conv + if self.with_conv: + # no asymmetric padding in torch conv, must do it ourselves + self.conv = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=3, + stride=2, + padding=0) + + def forward(self, x): + if self.with_conv: + pad = (0,1,0,1) + x = torch.nn.functional.pad(x, pad, mode="constant", value=0) + x = self.conv(x) + else: + x = torch.nn.functional.avg_pool2d(x, kernel_size=2, stride=2) + return x + + +class ResnetBlock(nn.Module): + def __init__(self, *, in_channels, out_channels=None, conv_shortcut=False, + dropout, temb_channels=512): + super().__init__() + self.in_channels = in_channels + out_channels = in_channels if out_channels is None else out_channels + self.out_channels = out_channels + self.use_conv_shortcut = conv_shortcut + + self.norm1 = Normalize(in_channels) + self.conv1 = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + if temb_channels > 0: + self.temb_proj = torch.nn.Linear(temb_channels, + out_channels) + self.norm2 = Normalize(out_channels) + self.dropout = torch.nn.Dropout(dropout) + self.conv2 = torch.nn.Conv2d(out_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + if self.in_channels != self.out_channels: + if self.use_conv_shortcut: + self.conv_shortcut = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + else: + self.nin_shortcut = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=1, + stride=1, + padding=0) + + def forward(self, x, temb): + h = x + h = self.norm1(h) + h = nonlinearity(h) + h = self.conv1(h) + + if temb is not None: + h = h + self.temb_proj(nonlinearity(temb))[:,:,None,None] + + h = self.norm2(h) + h = nonlinearity(h) + h = self.dropout(h) + h = self.conv2(h) + + if self.in_channels != self.out_channels: + if self.use_conv_shortcut: + x = self.conv_shortcut(x) + else: + x = self.nin_shortcut(x) + + return x+h + + +class AttnBlock(nn.Module): + def __init__(self, in_channels): + super().__init__() + self.in_channels = in_channels + + self.norm = Normalize(in_channels) + self.q = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.k = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.v = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.proj_out = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + + def forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + # compute attention + b,c,h,w = q.shape + q = q.reshape(b,c,h*w) + q = q.permute(0,2,1) # b,hw,c + k = k.reshape(b,c,h*w) # b,c,hw + w_ = torch.bmm(q,k) # b,hw,hw w[b,i,j]=sum_c q[b,i,c]k[b,c,j] + w_ = w_ * (int(c)**(-0.5)) + w_ = torch.nn.functional.softmax(w_, dim=2) + + # attend to values + v = v.reshape(b,c,h*w) + w_ = w_.permute(0,2,1) # b,hw,hw (first hw of k, second of q) + h_ = torch.bmm(v,w_) # b, c,hw (hw of q) h_[b,c,j] = sum_i v[b,c,i] w_[b,i,j] + h_ = h_.reshape(b,c,h,w) + + h_ = self.proj_out(h_) + + return x+h_ + +class MemoryEfficientAttnBlock(nn.Module): + """ + Uses xformers efficient implementation, + see https://github.com/MatthieuTPHR/diffusers/blob/d80b531ff8060ec1ea982b65a1b8df70f73aa67c/src/diffusers/models/attention.py#L223 + Note: this is a single-head self-attention operation + """ + # + def __init__(self, in_channels): + super().__init__() + self.in_channels = in_channels + + self.norm = Normalize(in_channels) + self.q = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.k = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.v = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.proj_out = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.attention_op: Optional[Any] = None + + def forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + # compute attention + B, C, H, W = q.shape + q, k, v = map(lambda x: rearrange(x, 'b c h w -> b (h w) c'), (q, k, v)) + + q, k, v = map( + lambda t: t.unsqueeze(3) + .reshape(B, t.shape[1], 1, C) + .permute(0, 2, 1, 3) + .reshape(B * 1, t.shape[1], C) + .contiguous(), + (q, k, v), + ) + out = xformers.ops.memory_efficient_attention(q, k, v, attn_bias=None, op=self.attention_op) + + out = ( + out.unsqueeze(0) + .reshape(B, 1, out.shape[1], C) + .permute(0, 2, 1, 3) + .reshape(B, out.shape[1], C) + ) + out = rearrange(out, 'b (h w) c -> b c h w', b=B, h=H, w=W, c=C) + out = self.proj_out(out) + return x+out + + +class MemoryEfficientCrossAttentionWrapper(MemoryEfficientCrossAttention): + def forward(self, x, context=None, mask=None): + b, c, h, w = x.shape + x = rearrange(x, 'b c h w -> b (h w) c') + out = super().forward(x, context=context, mask=mask) + out = rearrange(out, 'b (h w) c -> b c h w', h=h, w=w, c=c) + return x + out + + +def make_attn(in_channels, attn_type="vanilla", attn_kwargs=None): + assert attn_type in ["vanilla", "vanilla-xformers", "memory-efficient-cross-attn", "linear", "none"], f'attn_type {attn_type} unknown' + if XFORMERS_IS_AVAILBLE and attn_type == "vanilla": + attn_type = "vanilla-xformers" + print(f"making attention of type '{attn_type}' with {in_channels} in_channels") + if attn_type == "vanilla": + assert attn_kwargs is None + return AttnBlock(in_channels) + elif attn_type == "vanilla-xformers": + print(f"building MemoryEfficientAttnBlock with {in_channels} in_channels...") + return MemoryEfficientAttnBlock(in_channels) + elif type == "memory-efficient-cross-attn": + attn_kwargs["query_dim"] = in_channels + return MemoryEfficientCrossAttentionWrapper(**attn_kwargs) + elif attn_type == "none": + return nn.Identity(in_channels) + else: + raise NotImplementedError() + + +class Model(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, use_timestep=True, use_linear_attn=False, attn_type="vanilla"): + super().__init__() + if use_linear_attn: attn_type = "linear" + self.ch = ch + self.temb_ch = self.ch*4 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + + self.use_timestep = use_timestep + if self.use_timestep: + # timestep embedding + self.temb = nn.Module() + self.temb.dense = nn.ModuleList([ + torch.nn.Linear(self.ch, + self.temb_ch), + torch.nn.Linear(self.temb_ch, + self.temb_ch), + ]) + + # downsampling + self.conv_in = torch.nn.Conv2d(in_channels, + self.ch, + kernel_size=3, + stride=1, + padding=1) + + curr_res = resolution + in_ch_mult = (1,)+tuple(ch_mult) + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = ch*in_ch_mult[i_level] + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(make_attn(block_in, attn_type=attn_type)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions-1: + down.downsample = Downsample(block_in, resamp_with_conv) + curr_res = curr_res // 2 + self.down.append(down) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = make_attn(block_in, attn_type=attn_type) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = ch*ch_mult[i_level] + skip_in = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks+1): + if i_block == self.num_res_blocks: + skip_in = ch*in_ch_mult[i_level] + block.append(ResnetBlock(in_channels=block_in+skip_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(make_attn(block_in, attn_type=attn_type)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + up.upsample = Upsample(block_in, resamp_with_conv) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_ch, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x, t=None, context=None): + #assert x.shape[2] == x.shape[3] == self.resolution + if context is not None: + # assume aligned context, cat along channel axis + x = torch.cat((x, context), dim=1) + if self.use_timestep: + # timestep embedding + assert t is not None + temb = get_timestep_embedding(t, self.ch) + temb = self.temb.dense[0](temb) + temb = nonlinearity(temb) + temb = self.temb.dense[1](temb) + else: + temb = None + + # downsampling + hs = [self.conv_in(x)] + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](hs[-1], temb) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + hs.append(h) + if i_level != self.num_resolutions-1: + hs.append(self.down[i_level].downsample(hs[-1])) + + # middle + h = hs[-1] + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # upsampling + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks+1): + h = self.up[i_level].block[i_block]( + torch.cat([h, hs.pop()], dim=1), temb) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + def get_last_layer(self): + return self.conv_out.weight + + +class Encoder(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, z_channels, double_z=True, use_linear_attn=False, attn_type="vanilla", + **ignore_kwargs): + super().__init__() + if use_linear_attn: attn_type = "linear" + self.ch = ch + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + + # downsampling + self.conv_in = torch.nn.Conv2d(in_channels, + self.ch, + kernel_size=3, + stride=1, + padding=1) + + curr_res = resolution + in_ch_mult = (1,)+tuple(ch_mult) + self.in_ch_mult = in_ch_mult + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = ch*in_ch_mult[i_level] + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(make_attn(block_in, attn_type=attn_type)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions-1: + down.downsample = Downsample(block_in, resamp_with_conv) + curr_res = curr_res // 2 + self.down.append(down) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = make_attn(block_in, attn_type=attn_type) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + 2*z_channels if double_z else z_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + # timestep embedding + temb = None + + # downsampling + hs = [self.conv_in(x)] + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](hs[-1], temb) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + hs.append(h) + if i_level != self.num_resolutions-1: + hs.append(self.down[i_level].downsample(hs[-1])) + + # middle + h = hs[-1] + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class Decoder(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, z_channels, give_pre_end=False, tanh_out=False, use_linear_attn=False, + attn_type="vanilla", **ignorekwargs): + super().__init__() + if use_linear_attn: attn_type = "linear" + self.ch = ch + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + self.give_pre_end = give_pre_end + self.tanh_out = tanh_out + + # compute in_ch_mult, block_in and curr_res at lowest res + in_ch_mult = (1,)+tuple(ch_mult) + block_in = ch*ch_mult[self.num_resolutions-1] + curr_res = resolution // 2**(self.num_resolutions-1) + self.z_shape = (1,z_channels,curr_res,curr_res) + print("Working with z of shape {} = {} dimensions.".format( + self.z_shape, np.prod(self.z_shape))) + + # z to block_in + self.conv_in = torch.nn.Conv2d(z_channels, + block_in, + kernel_size=3, + stride=1, + padding=1) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = make_attn(block_in, attn_type=attn_type) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks+1): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(make_attn(block_in, attn_type=attn_type)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + up.upsample = Upsample(block_in, resamp_with_conv) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_ch, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, z): + #assert z.shape[1:] == self.z_shape[1:] + self.last_z_shape = z.shape + + # timestep embedding + temb = None + + # z to block_in + h = self.conv_in(z) + + # middle + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # upsampling + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks+1): + h = self.up[i_level].block[i_block](h, temb) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + + # end + if self.give_pre_end: + return h + + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + if self.tanh_out: + h = torch.tanh(h) + return h + + +class SimpleDecoder(nn.Module): + def __init__(self, in_channels, out_channels, *args, **kwargs): + super().__init__() + self.model = nn.ModuleList([nn.Conv2d(in_channels, in_channels, 1), + ResnetBlock(in_channels=in_channels, + out_channels=2 * in_channels, + temb_channels=0, dropout=0.0), + ResnetBlock(in_channels=2 * in_channels, + out_channels=4 * in_channels, + temb_channels=0, dropout=0.0), + ResnetBlock(in_channels=4 * in_channels, + out_channels=2 * in_channels, + temb_channels=0, dropout=0.0), + nn.Conv2d(2*in_channels, in_channels, 1), + Upsample(in_channels, with_conv=True)]) + # end + self.norm_out = Normalize(in_channels) + self.conv_out = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + for i, layer in enumerate(self.model): + if i in [1,2,3]: + x = layer(x, None) + else: + x = layer(x) + + h = self.norm_out(x) + h = nonlinearity(h) + x = self.conv_out(h) + return x + + +class UpsampleDecoder(nn.Module): + def __init__(self, in_channels, out_channels, ch, num_res_blocks, resolution, + ch_mult=(2,2), dropout=0.0): + super().__init__() + # upsampling + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + block_in = in_channels + curr_res = resolution // 2 ** (self.num_resolutions - 1) + self.res_blocks = nn.ModuleList() + self.upsample_blocks = nn.ModuleList() + for i_level in range(self.num_resolutions): + res_block = [] + block_out = ch * ch_mult[i_level] + for i_block in range(self.num_res_blocks + 1): + res_block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + self.res_blocks.append(nn.ModuleList(res_block)) + if i_level != self.num_resolutions - 1: + self.upsample_blocks.append(Upsample(block_in, True)) + curr_res = curr_res * 2 + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + # upsampling + h = x + for k, i_level in enumerate(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks + 1): + h = self.res_blocks[i_level][i_block](h, None) + if i_level != self.num_resolutions - 1: + h = self.upsample_blocks[k](h) + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class LatentRescaler(nn.Module): + def __init__(self, factor, in_channels, mid_channels, out_channels, depth=2): + super().__init__() + # residual block, interpolate, residual block + self.factor = factor + self.conv_in = nn.Conv2d(in_channels, + mid_channels, + kernel_size=3, + stride=1, + padding=1) + self.res_block1 = nn.ModuleList([ResnetBlock(in_channels=mid_channels, + out_channels=mid_channels, + temb_channels=0, + dropout=0.0) for _ in range(depth)]) + self.attn = AttnBlock(mid_channels) + self.res_block2 = nn.ModuleList([ResnetBlock(in_channels=mid_channels, + out_channels=mid_channels, + temb_channels=0, + dropout=0.0) for _ in range(depth)]) + + self.conv_out = nn.Conv2d(mid_channels, + out_channels, + kernel_size=1, + ) + + def forward(self, x): + x = self.conv_in(x) + for block in self.res_block1: + x = block(x, None) + x = torch.nn.functional.interpolate(x, size=(int(round(x.shape[2]*self.factor)), int(round(x.shape[3]*self.factor)))) + x = self.attn(x) + for block in self.res_block2: + x = block(x, None) + x = self.conv_out(x) + return x + + +class MergedRescaleEncoder(nn.Module): + def __init__(self, in_channels, ch, resolution, out_ch, num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, + ch_mult=(1,2,4,8), rescale_factor=1.0, rescale_module_depth=1): + super().__init__() + intermediate_chn = ch * ch_mult[-1] + self.encoder = Encoder(in_channels=in_channels, num_res_blocks=num_res_blocks, ch=ch, ch_mult=ch_mult, + z_channels=intermediate_chn, double_z=False, resolution=resolution, + attn_resolutions=attn_resolutions, dropout=dropout, resamp_with_conv=resamp_with_conv, + out_ch=None) + self.rescaler = LatentRescaler(factor=rescale_factor, in_channels=intermediate_chn, + mid_channels=intermediate_chn, out_channels=out_ch, depth=rescale_module_depth) + + def forward(self, x): + x = self.encoder(x) + x = self.rescaler(x) + return x + + +class MergedRescaleDecoder(nn.Module): + def __init__(self, z_channels, out_ch, resolution, num_res_blocks, attn_resolutions, ch, ch_mult=(1,2,4,8), + dropout=0.0, resamp_with_conv=True, rescale_factor=1.0, rescale_module_depth=1): + super().__init__() + tmp_chn = z_channels*ch_mult[-1] + self.decoder = Decoder(out_ch=out_ch, z_channels=tmp_chn, attn_resolutions=attn_resolutions, dropout=dropout, + resamp_with_conv=resamp_with_conv, in_channels=None, num_res_blocks=num_res_blocks, + ch_mult=ch_mult, resolution=resolution, ch=ch) + self.rescaler = LatentRescaler(factor=rescale_factor, in_channels=z_channels, mid_channels=tmp_chn, + out_channels=tmp_chn, depth=rescale_module_depth) + + def forward(self, x): + x = self.rescaler(x) + x = self.decoder(x) + return x + + +class Upsampler(nn.Module): + def __init__(self, in_size, out_size, in_channels, out_channels, ch_mult=2): + super().__init__() + assert out_size >= in_size + num_blocks = int(np.log2(out_size//in_size))+1 + factor_up = 1.+ (out_size % in_size) + print(f"Building {self.__class__.__name__} with in_size: {in_size} --> out_size {out_size} and factor {factor_up}") + self.rescaler = LatentRescaler(factor=factor_up, in_channels=in_channels, mid_channels=2*in_channels, + out_channels=in_channels) + self.decoder = Decoder(out_ch=out_channels, resolution=out_size, z_channels=in_channels, num_res_blocks=2, + attn_resolutions=[], in_channels=None, ch=in_channels, + ch_mult=[ch_mult for _ in range(num_blocks)]) + + def forward(self, x): + x = self.rescaler(x) + x = self.decoder(x) + return x + + +class Resize(nn.Module): + def __init__(self, in_channels=None, learned=False, mode="bilinear"): + super().__init__() + self.with_conv = learned + self.mode = mode + if self.with_conv: + print(f"Note: {self.__class__.__name} uses learned downsampling and will ignore the fixed {mode} mode") + raise NotImplementedError() + assert in_channels is not None + # no asymmetric padding in torch conv, must do it ourselves + self.conv = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=4, + stride=2, + padding=1) + + def forward(self, x, scale_factor=1.0): + if scale_factor==1.0: + return x + else: + x = torch.nn.functional.interpolate(x, mode=self.mode, align_corners=False, scale_factor=scale_factor) + return x diff --git a/sd/stablediffusion/ldm/modules/diffusionmodules/openaimodel.py b/sd/stablediffusion/ldm/modules/diffusionmodules/openaimodel.py new file mode 100644 index 0000000000000000000000000000000000000000..7df6b5abfe8eff07f0c8e8703ba8aee90d45984b --- /dev/null +++ b/sd/stablediffusion/ldm/modules/diffusionmodules/openaimodel.py @@ -0,0 +1,786 @@ +from abc import abstractmethod +import math + +import numpy as np +import torch as th +import torch.nn as nn +import torch.nn.functional as F + +from ldm.modules.diffusionmodules.util import ( + checkpoint, + conv_nd, + linear, + avg_pool_nd, + zero_module, + normalization, + timestep_embedding, +) +from ldm.modules.attention import SpatialTransformer +from ldm.util import exists + + +# dummy replace +def convert_module_to_f16(x): + pass + +def convert_module_to_f32(x): + pass + + +## go +class AttentionPool2d(nn.Module): + """ + Adapted from CLIP: https://github.com/openai/CLIP/blob/main/clip/model.py + """ + + def __init__( + self, + spacial_dim: int, + embed_dim: int, + num_heads_channels: int, + output_dim: int = None, + ): + super().__init__() + self.positional_embedding = nn.Parameter(th.randn(embed_dim, spacial_dim ** 2 + 1) / embed_dim ** 0.5) + self.qkv_proj = conv_nd(1, embed_dim, 3 * embed_dim, 1) + self.c_proj = conv_nd(1, embed_dim, output_dim or embed_dim, 1) + self.num_heads = embed_dim // num_heads_channels + self.attention = QKVAttention(self.num_heads) + + def forward(self, x): + b, c, *_spatial = x.shape + x = x.reshape(b, c, -1) # NC(HW) + x = th.cat([x.mean(dim=-1, keepdim=True), x], dim=-1) # NC(HW+1) + x = x + self.positional_embedding[None, :, :].to(x.dtype) # NC(HW+1) + x = self.qkv_proj(x) + x = self.attention(x) + x = self.c_proj(x) + return x[:, :, 0] + + +class TimestepBlock(nn.Module): + """ + Any module where forward() takes timestep embeddings as a second argument. + """ + + @abstractmethod + def forward(self, x, emb): + """ + Apply the module to `x` given `emb` timestep embeddings. + """ + + +class TimestepEmbedSequential(nn.Sequential, TimestepBlock): + """ + A sequential module that passes timestep embeddings to the children that + support it as an extra input. + """ + + def forward(self, x, emb, context=None): + for layer in self: + if isinstance(layer, TimestepBlock): + x = layer(x, emb) + elif isinstance(layer, SpatialTransformer): + x = layer(x, context) + else: + x = layer(x) + return x + + +class Upsample(nn.Module): + """ + An upsampling layer with an optional convolution. + :param channels: channels in the inputs and outputs. + :param use_conv: a bool determining if a convolution is applied. + :param dims: determines if the signal is 1D, 2D, or 3D. If 3D, then + upsampling occurs in the inner-two dimensions. + """ + + def __init__(self, channels, use_conv, dims=2, out_channels=None, padding=1): + super().__init__() + self.channels = channels + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.dims = dims + if use_conv: + self.conv = conv_nd(dims, self.channels, self.out_channels, 3, padding=padding) + + def forward(self, x): + assert x.shape[1] == self.channels + if self.dims == 3: + x = F.interpolate( + x, (x.shape[2], x.shape[3] * 2, x.shape[4] * 2), mode="nearest" + ) + else: + x = F.interpolate(x, scale_factor=2, mode="nearest") + if self.use_conv: + x = self.conv(x) + return x + +class TransposedUpsample(nn.Module): + 'Learned 2x upsampling without padding' + def __init__(self, channels, out_channels=None, ks=5): + super().__init__() + self.channels = channels + self.out_channels = out_channels or channels + + self.up = nn.ConvTranspose2d(self.channels,self.out_channels,kernel_size=ks,stride=2) + + def forward(self,x): + return self.up(x) + + +class Downsample(nn.Module): + """ + A downsampling layer with an optional convolution. + :param channels: channels in the inputs and outputs. + :param use_conv: a bool determining if a convolution is applied. + :param dims: determines if the signal is 1D, 2D, or 3D. If 3D, then + downsampling occurs in the inner-two dimensions. + """ + + def __init__(self, channels, use_conv, dims=2, out_channels=None,padding=1): + super().__init__() + self.channels = channels + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.dims = dims + stride = 2 if dims != 3 else (1, 2, 2) + if use_conv: + self.op = conv_nd( + dims, self.channels, self.out_channels, 3, stride=stride, padding=padding + ) + else: + assert self.channels == self.out_channels + self.op = avg_pool_nd(dims, kernel_size=stride, stride=stride) + + def forward(self, x): + assert x.shape[1] == self.channels + return self.op(x) + + +class ResBlock(TimestepBlock): + """ + A residual block that can optionally change the number of channels. + :param channels: the number of input channels. + :param emb_channels: the number of timestep embedding channels. + :param dropout: the rate of dropout. + :param out_channels: if specified, the number of out channels. + :param use_conv: if True and out_channels is specified, use a spatial + convolution instead of a smaller 1x1 convolution to change the + channels in the skip connection. + :param dims: determines if the signal is 1D, 2D, or 3D. + :param use_checkpoint: if True, use gradient checkpointing on this module. + :param up: if True, use this block for upsampling. + :param down: if True, use this block for downsampling. + """ + + def __init__( + self, + channels, + emb_channels, + dropout, + out_channels=None, + use_conv=False, + use_scale_shift_norm=False, + dims=2, + use_checkpoint=False, + up=False, + down=False, + ): + super().__init__() + self.channels = channels + self.emb_channels = emb_channels + self.dropout = dropout + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.use_checkpoint = use_checkpoint + self.use_scale_shift_norm = use_scale_shift_norm + + self.in_layers = nn.Sequential( + normalization(channels), + nn.SiLU(), + conv_nd(dims, channels, self.out_channels, 3, padding=1), + ) + + self.updown = up or down + + if up: + self.h_upd = Upsample(channels, False, dims) + self.x_upd = Upsample(channels, False, dims) + elif down: + self.h_upd = Downsample(channels, False, dims) + self.x_upd = Downsample(channels, False, dims) + else: + self.h_upd = self.x_upd = nn.Identity() + + self.emb_layers = nn.Sequential( + nn.SiLU(), + linear( + emb_channels, + 2 * self.out_channels if use_scale_shift_norm else self.out_channels, + ), + ) + self.out_layers = nn.Sequential( + normalization(self.out_channels), + nn.SiLU(), + nn.Dropout(p=dropout), + zero_module( + conv_nd(dims, self.out_channels, self.out_channels, 3, padding=1) + ), + ) + + if self.out_channels == channels: + self.skip_connection = nn.Identity() + elif use_conv: + self.skip_connection = conv_nd( + dims, channels, self.out_channels, 3, padding=1 + ) + else: + self.skip_connection = conv_nd(dims, channels, self.out_channels, 1) + + def forward(self, x, emb): + """ + Apply the block to a Tensor, conditioned on a timestep embedding. + :param x: an [N x C x ...] Tensor of features. + :param emb: an [N x emb_channels] Tensor of timestep embeddings. + :return: an [N x C x ...] Tensor of outputs. + """ + return checkpoint( + self._forward, (x, emb), self.parameters(), self.use_checkpoint + ) + + + def _forward(self, x, emb): + if self.updown: + in_rest, in_conv = self.in_layers[:-1], self.in_layers[-1] + h = in_rest(x) + h = self.h_upd(h) + x = self.x_upd(x) + h = in_conv(h) + else: + h = self.in_layers(x) + emb_out = self.emb_layers(emb).type(h.dtype) + while len(emb_out.shape) < len(h.shape): + emb_out = emb_out[..., None] + if self.use_scale_shift_norm: + out_norm, out_rest = self.out_layers[0], self.out_layers[1:] + scale, shift = th.chunk(emb_out, 2, dim=1) + h = out_norm(h) * (1 + scale) + shift + h = out_rest(h) + else: + h = h + emb_out + h = self.out_layers(h) + return self.skip_connection(x) + h + + +class AttentionBlock(nn.Module): + """ + An attention block that allows spatial positions to attend to each other. + Originally ported from here, but adapted to the N-d case. + https://github.com/hojonathanho/diffusion/blob/1e0dceb3b3495bbe19116a5e1b3596cd0706c543/diffusion_tf/models/unet.py#L66. + """ + + def __init__( + self, + channels, + num_heads=1, + num_head_channels=-1, + use_checkpoint=False, + use_new_attention_order=False, + ): + super().__init__() + self.channels = channels + if num_head_channels == -1: + self.num_heads = num_heads + else: + assert ( + channels % num_head_channels == 0 + ), f"q,k,v channels {channels} is not divisible by num_head_channels {num_head_channels}" + self.num_heads = channels // num_head_channels + self.use_checkpoint = use_checkpoint + self.norm = normalization(channels) + self.qkv = conv_nd(1, channels, channels * 3, 1) + if use_new_attention_order: + # split qkv before split heads + self.attention = QKVAttention(self.num_heads) + else: + # split heads before split qkv + self.attention = QKVAttentionLegacy(self.num_heads) + + self.proj_out = zero_module(conv_nd(1, channels, channels, 1)) + + def forward(self, x): + return checkpoint(self._forward, (x,), self.parameters(), True) # TODO: check checkpoint usage, is True # TODO: fix the .half call!!! + #return pt_checkpoint(self._forward, x) # pytorch + + def _forward(self, x): + b, c, *spatial = x.shape + x = x.reshape(b, c, -1) + qkv = self.qkv(self.norm(x)) + h = self.attention(qkv) + h = self.proj_out(h) + return (x + h).reshape(b, c, *spatial) + + +def count_flops_attn(model, _x, y): + """ + A counter for the `thop` package to count the operations in an + attention operation. + Meant to be used like: + macs, params = thop.profile( + model, + inputs=(inputs, timestamps), + custom_ops={QKVAttention: QKVAttention.count_flops}, + ) + """ + b, c, *spatial = y[0].shape + num_spatial = int(np.prod(spatial)) + # We perform two matmuls with the same number of ops. + # The first computes the weight matrix, the second computes + # the combination of the value vectors. + matmul_ops = 2 * b * (num_spatial ** 2) * c + model.total_ops += th.DoubleTensor([matmul_ops]) + + +class QKVAttentionLegacy(nn.Module): + """ + A module which performs QKV attention. Matches legacy QKVAttention + input/ouput heads shaping + """ + + def __init__(self, n_heads): + super().__init__() + self.n_heads = n_heads + + def forward(self, qkv): + """ + Apply QKV attention. + :param qkv: an [N x (H * 3 * C) x T] tensor of Qs, Ks, and Vs. + :return: an [N x (H * C) x T] tensor after attention. + """ + bs, width, length = qkv.shape + assert width % (3 * self.n_heads) == 0 + ch = width // (3 * self.n_heads) + q, k, v = qkv.reshape(bs * self.n_heads, ch * 3, length).split(ch, dim=1) + scale = 1 / math.sqrt(math.sqrt(ch)) + weight = th.einsum( + "bct,bcs->bts", q * scale, k * scale + ) # More stable with f16 than dividing afterwards + weight = th.softmax(weight.float(), dim=-1).type(weight.dtype) + a = th.einsum("bts,bcs->bct", weight, v) + return a.reshape(bs, -1, length) + + @staticmethod + def count_flops(model, _x, y): + return count_flops_attn(model, _x, y) + + +class QKVAttention(nn.Module): + """ + A module which performs QKV attention and splits in a different order. + """ + + def __init__(self, n_heads): + super().__init__() + self.n_heads = n_heads + + def forward(self, qkv): + """ + Apply QKV attention. + :param qkv: an [N x (3 * H * C) x T] tensor of Qs, Ks, and Vs. + :return: an [N x (H * C) x T] tensor after attention. + """ + bs, width, length = qkv.shape + assert width % (3 * self.n_heads) == 0 + ch = width // (3 * self.n_heads) + q, k, v = qkv.chunk(3, dim=1) + scale = 1 / math.sqrt(math.sqrt(ch)) + weight = th.einsum( + "bct,bcs->bts", + (q * scale).view(bs * self.n_heads, ch, length), + (k * scale).view(bs * self.n_heads, ch, length), + ) # More stable with f16 than dividing afterwards + weight = th.softmax(weight.float(), dim=-1).type(weight.dtype) + a = th.einsum("bts,bcs->bct", weight, v.reshape(bs * self.n_heads, ch, length)) + return a.reshape(bs, -1, length) + + @staticmethod + def count_flops(model, _x, y): + return count_flops_attn(model, _x, y) + + +class UNetModel(nn.Module): + """ + The full UNet model with attention and timestep embedding. + :param in_channels: channels in the input Tensor. + :param model_channels: base channel count for the model. + :param out_channels: channels in the output Tensor. + :param num_res_blocks: number of residual blocks per downsample. + :param attention_resolutions: a collection of downsample rates at which + attention will take place. May be a set, list, or tuple. + For example, if this contains 4, then at 4x downsampling, attention + will be used. + :param dropout: the dropout probability. + :param channel_mult: channel multiplier for each level of the UNet. + :param conv_resample: if True, use learned convolutions for upsampling and + downsampling. + :param dims: determines if the signal is 1D, 2D, or 3D. + :param num_classes: if specified (as an int), then this model will be + class-conditional with `num_classes` classes. + :param use_checkpoint: use gradient checkpointing to reduce memory usage. + :param num_heads: the number of attention heads in each attention layer. + :param num_heads_channels: if specified, ignore num_heads and instead use + a fixed channel width per attention head. + :param num_heads_upsample: works with num_heads to set a different number + of heads for upsampling. Deprecated. + :param use_scale_shift_norm: use a FiLM-like conditioning mechanism. + :param resblock_updown: use residual blocks for up/downsampling. + :param use_new_attention_order: use a different attention pattern for potentially + increased efficiency. + """ + + def __init__( + self, + image_size, + in_channels, + model_channels, + out_channels, + num_res_blocks, + attention_resolutions, + dropout=0, + channel_mult=(1, 2, 4, 8), + conv_resample=True, + dims=2, + num_classes=None, + use_checkpoint=False, + use_fp16=False, + num_heads=-1, + num_head_channels=-1, + num_heads_upsample=-1, + use_scale_shift_norm=False, + resblock_updown=False, + use_new_attention_order=False, + use_spatial_transformer=False, # custom transformer support + transformer_depth=1, # custom transformer support + context_dim=None, # custom transformer support + n_embed=None, # custom support for prediction of discrete ids into codebook of first stage vq model + legacy=True, + disable_self_attentions=None, + num_attention_blocks=None, + disable_middle_self_attn=False, + use_linear_in_transformer=False, + ): + super().__init__() + if use_spatial_transformer: + assert context_dim is not None, 'Fool!! You forgot to include the dimension of your cross-attention conditioning...' + + if context_dim is not None: + assert use_spatial_transformer, 'Fool!! You forgot to use the spatial transformer for your cross-attention conditioning...' + from omegaconf.listconfig import ListConfig + if type(context_dim) == ListConfig: + context_dim = list(context_dim) + + if num_heads_upsample == -1: + num_heads_upsample = num_heads + + if num_heads == -1: + assert num_head_channels != -1, 'Either num_heads or num_head_channels has to be set' + + if num_head_channels == -1: + assert num_heads != -1, 'Either num_heads or num_head_channels has to be set' + + self.image_size = image_size + self.in_channels = in_channels + self.model_channels = model_channels + self.out_channels = out_channels + if isinstance(num_res_blocks, int): + self.num_res_blocks = len(channel_mult) * [num_res_blocks] + else: + if len(num_res_blocks) != len(channel_mult): + raise ValueError("provide num_res_blocks either as an int (globally constant) or " + "as a list/tuple (per-level) with the same length as channel_mult") + self.num_res_blocks = num_res_blocks + if disable_self_attentions is not None: + # should be a list of booleans, indicating whether to disable self-attention in TransformerBlocks or not + assert len(disable_self_attentions) == len(channel_mult) + if num_attention_blocks is not None: + assert len(num_attention_blocks) == len(self.num_res_blocks) + assert all(map(lambda i: self.num_res_blocks[i] >= num_attention_blocks[i], range(len(num_attention_blocks)))) + print(f"Constructor of UNetModel received num_attention_blocks={num_attention_blocks}. " + f"This option has LESS priority than attention_resolutions {attention_resolutions}, " + f"i.e., in cases where num_attention_blocks[i] > 0 but 2**i not in attention_resolutions, " + f"attention will still not be set.") + + self.attention_resolutions = attention_resolutions + self.dropout = dropout + self.channel_mult = channel_mult + self.conv_resample = conv_resample + self.num_classes = num_classes + self.use_checkpoint = use_checkpoint + self.dtype = th.float16 if use_fp16 else th.float32 + self.num_heads = num_heads + self.num_head_channels = num_head_channels + self.num_heads_upsample = num_heads_upsample + self.predict_codebook_ids = n_embed is not None + + time_embed_dim = model_channels * 4 + self.time_embed = nn.Sequential( + linear(model_channels, time_embed_dim), + nn.SiLU(), + linear(time_embed_dim, time_embed_dim), + ) + + if self.num_classes is not None: + if isinstance(self.num_classes, int): + self.label_emb = nn.Embedding(num_classes, time_embed_dim) + elif self.num_classes == "continuous": + print("setting up linear c_adm embedding layer") + self.label_emb = nn.Linear(1, time_embed_dim) + else: + raise ValueError() + + self.input_blocks = nn.ModuleList( + [ + TimestepEmbedSequential( + conv_nd(dims, in_channels, model_channels, 3, padding=1) + ) + ] + ) + self._feature_size = model_channels + input_block_chans = [model_channels] + ch = model_channels + ds = 1 + for level, mult in enumerate(channel_mult): + for nr in range(self.num_res_blocks[level]): + layers = [ + ResBlock( + ch, + time_embed_dim, + dropout, + out_channels=mult * model_channels, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ) + ] + ch = mult * model_channels + if ds in attention_resolutions: + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + if exists(disable_self_attentions): + disabled_sa = disable_self_attentions[level] + else: + disabled_sa = False + + if not exists(num_attention_blocks) or nr < num_attention_blocks[level]: + layers.append( + AttentionBlock( + ch, + use_checkpoint=use_checkpoint, + num_heads=num_heads, + num_head_channels=dim_head, + use_new_attention_order=use_new_attention_order, + ) if not use_spatial_transformer else SpatialTransformer( + ch, num_heads, dim_head, depth=transformer_depth, context_dim=context_dim, + disable_self_attn=disabled_sa, use_linear=use_linear_in_transformer, + use_checkpoint=use_checkpoint + ) + ) + self.input_blocks.append(TimestepEmbedSequential(*layers)) + self._feature_size += ch + input_block_chans.append(ch) + if level != len(channel_mult) - 1: + out_ch = ch + self.input_blocks.append( + TimestepEmbedSequential( + ResBlock( + ch, + time_embed_dim, + dropout, + out_channels=out_ch, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + down=True, + ) + if resblock_updown + else Downsample( + ch, conv_resample, dims=dims, out_channels=out_ch + ) + ) + ) + ch = out_ch + input_block_chans.append(ch) + ds *= 2 + self._feature_size += ch + + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + self.middle_block = TimestepEmbedSequential( + ResBlock( + ch, + time_embed_dim, + dropout, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ), + AttentionBlock( + ch, + use_checkpoint=use_checkpoint, + num_heads=num_heads, + num_head_channels=dim_head, + use_new_attention_order=use_new_attention_order, + ) if not use_spatial_transformer else SpatialTransformer( # always uses a self-attn + ch, num_heads, dim_head, depth=transformer_depth, context_dim=context_dim, + disable_self_attn=disable_middle_self_attn, use_linear=use_linear_in_transformer, + use_checkpoint=use_checkpoint + ), + ResBlock( + ch, + time_embed_dim, + dropout, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ), + ) + self._feature_size += ch + + self.output_blocks = nn.ModuleList([]) + for level, mult in list(enumerate(channel_mult))[::-1]: + for i in range(self.num_res_blocks[level] + 1): + ich = input_block_chans.pop() + layers = [ + ResBlock( + ch + ich, + time_embed_dim, + dropout, + out_channels=model_channels * mult, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ) + ] + ch = model_channels * mult + if ds in attention_resolutions: + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + if exists(disable_self_attentions): + disabled_sa = disable_self_attentions[level] + else: + disabled_sa = False + + if not exists(num_attention_blocks) or i < num_attention_blocks[level]: + layers.append( + AttentionBlock( + ch, + use_checkpoint=use_checkpoint, + num_heads=num_heads_upsample, + num_head_channels=dim_head, + use_new_attention_order=use_new_attention_order, + ) if not use_spatial_transformer else SpatialTransformer( + ch, num_heads, dim_head, depth=transformer_depth, context_dim=context_dim, + disable_self_attn=disabled_sa, use_linear=use_linear_in_transformer, + use_checkpoint=use_checkpoint + ) + ) + if level and i == self.num_res_blocks[level]: + out_ch = ch + layers.append( + ResBlock( + ch, + time_embed_dim, + dropout, + out_channels=out_ch, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + up=True, + ) + if resblock_updown + else Upsample(ch, conv_resample, dims=dims, out_channels=out_ch) + ) + ds //= 2 + self.output_blocks.append(TimestepEmbedSequential(*layers)) + self._feature_size += ch + + self.out = nn.Sequential( + normalization(ch), + nn.SiLU(), + zero_module(conv_nd(dims, model_channels, out_channels, 3, padding=1)), + ) + if self.predict_codebook_ids: + self.id_predictor = nn.Sequential( + normalization(ch), + conv_nd(dims, model_channels, n_embed, 1), + #nn.LogSoftmax(dim=1) # change to cross_entropy and produce non-normalized logits + ) + + def convert_to_fp16(self): + """ + Convert the torso of the model to float16. + """ + self.input_blocks.apply(convert_module_to_f16) + self.middle_block.apply(convert_module_to_f16) + self.output_blocks.apply(convert_module_to_f16) + + def convert_to_fp32(self): + """ + Convert the torso of the model to float32. + """ + self.input_blocks.apply(convert_module_to_f32) + self.middle_block.apply(convert_module_to_f32) + self.output_blocks.apply(convert_module_to_f32) + + def forward(self, x, timesteps=None, context=None, y=None,**kwargs): + """ + Apply the model to an input batch. + :param x: an [N x C x ...] Tensor of inputs. + :param timesteps: a 1-D batch of timesteps. + :param context: conditioning plugged in via crossattn + :param y: an [N] Tensor of labels, if class-conditional. + :return: an [N x C x ...] Tensor of outputs. + """ + assert (y is not None) == ( + self.num_classes is not None + ), "must specify y if and only if the model is class-conditional" + hs = [] + t_emb = timestep_embedding(timesteps, self.model_channels, repeat_only=False) + emb = self.time_embed(t_emb) + + if self.num_classes is not None: + assert y.shape[0] == x.shape[0] + emb = emb + self.label_emb(y) + + h = x.type(self.dtype) + for module in self.input_blocks: + h = module(h, emb, context) + hs.append(h) + h = self.middle_block(h, emb, context) + for module in self.output_blocks: + h = th.cat([h, hs.pop()], dim=1) + h = module(h, emb, context) + h = h.type(x.dtype) + if self.predict_codebook_ids: + return self.id_predictor(h) + else: + return self.out(h) diff --git a/sd/stablediffusion/ldm/modules/diffusionmodules/upscaling.py b/sd/stablediffusion/ldm/modules/diffusionmodules/upscaling.py new file mode 100644 index 0000000000000000000000000000000000000000..03816662098ce1ffac79bd939b892e867ab91988 --- /dev/null +++ b/sd/stablediffusion/ldm/modules/diffusionmodules/upscaling.py @@ -0,0 +1,81 @@ +import torch +import torch.nn as nn +import numpy as np +from functools import partial + +from ldm.modules.diffusionmodules.util import extract_into_tensor, make_beta_schedule +from ldm.util import default + + +class AbstractLowScaleModel(nn.Module): + # for concatenating a downsampled image to the latent representation + def __init__(self, noise_schedule_config=None): + super(AbstractLowScaleModel, self).__init__() + if noise_schedule_config is not None: + self.register_schedule(**noise_schedule_config) + + def register_schedule(self, beta_schedule="linear", timesteps=1000, + linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + betas = make_beta_schedule(beta_schedule, timesteps, linear_start=linear_start, linear_end=linear_end, + cosine_s=cosine_s) + alphas = 1. - betas + alphas_cumprod = np.cumprod(alphas, axis=0) + alphas_cumprod_prev = np.append(1., alphas_cumprod[:-1]) + + timesteps, = betas.shape + self.num_timesteps = int(timesteps) + self.linear_start = linear_start + self.linear_end = linear_end + assert alphas_cumprod.shape[0] == self.num_timesteps, 'alphas have to be defined for each timestep' + + to_torch = partial(torch.tensor, dtype=torch.float32) + + self.register_buffer('betas', to_torch(betas)) + self.register_buffer('alphas_cumprod', to_torch(alphas_cumprod)) + self.register_buffer('alphas_cumprod_prev', to_torch(alphas_cumprod_prev)) + + # calculations for diffusion q(x_t | x_{t-1}) and others + self.register_buffer('sqrt_alphas_cumprod', to_torch(np.sqrt(alphas_cumprod))) + self.register_buffer('sqrt_one_minus_alphas_cumprod', to_torch(np.sqrt(1. - alphas_cumprod))) + self.register_buffer('log_one_minus_alphas_cumprod', to_torch(np.log(1. - alphas_cumprod))) + self.register_buffer('sqrt_recip_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod))) + self.register_buffer('sqrt_recipm1_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod - 1))) + + def q_sample(self, x_start, t, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + return (extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start + + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape) * noise) + + def forward(self, x): + return x, None + + def decode(self, x): + return x + + +class SimpleImageConcat(AbstractLowScaleModel): + # no noise level conditioning + def __init__(self): + super(SimpleImageConcat, self).__init__(noise_schedule_config=None) + self.max_noise_level = 0 + + def forward(self, x): + # fix to constant noise level + return x, torch.zeros(x.shape[0], device=x.device).long() + + +class ImageConcatWithNoiseAugmentation(AbstractLowScaleModel): + def __init__(self, noise_schedule_config, max_noise_level=1000, to_cuda=False): + super().__init__(noise_schedule_config=noise_schedule_config) + self.max_noise_level = max_noise_level + + def forward(self, x, noise_level=None): + if noise_level is None: + noise_level = torch.randint(0, self.max_noise_level, (x.shape[0],), device=x.device).long() + else: + assert isinstance(noise_level, torch.Tensor) + z = self.q_sample(x, noise_level) + return z, noise_level + + + diff --git a/sd/stablediffusion/ldm/modules/diffusionmodules/util.py b/sd/stablediffusion/ldm/modules/diffusionmodules/util.py new file mode 100644 index 0000000000000000000000000000000000000000..637363dfe34799e70cfdbcd11445212df9d9ca1f --- /dev/null +++ b/sd/stablediffusion/ldm/modules/diffusionmodules/util.py @@ -0,0 +1,270 @@ +# adopted from +# https://github.com/openai/improved-diffusion/blob/main/improved_diffusion/gaussian_diffusion.py +# and +# https://github.com/lucidrains/denoising-diffusion-pytorch/blob/7706bdfc6f527f58d33f84b7b522e61e6e3164b3/denoising_diffusion_pytorch/denoising_diffusion_pytorch.py +# and +# https://github.com/openai/guided-diffusion/blob/0ba878e517b276c45d1195eb29f6f5f72659a05b/guided_diffusion/nn.py +# +# thanks! + + +import os +import math +import torch +import torch.nn as nn +import numpy as np +from einops import repeat + +from ldm.util import instantiate_from_config + + +def make_beta_schedule(schedule, n_timestep, linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + if schedule == "linear": + betas = ( + torch.linspace(linear_start ** 0.5, linear_end ** 0.5, n_timestep, dtype=torch.float64) ** 2 + ) + + elif schedule == "cosine": + timesteps = ( + torch.arange(n_timestep + 1, dtype=torch.float64) / n_timestep + cosine_s + ) + alphas = timesteps / (1 + cosine_s) * np.pi / 2 + alphas = torch.cos(alphas).pow(2) + alphas = alphas / alphas[0] + betas = 1 - alphas[1:] / alphas[:-1] + betas = np.clip(betas, a_min=0, a_max=0.999) + + elif schedule == "sqrt_linear": + betas = torch.linspace(linear_start, linear_end, n_timestep, dtype=torch.float64) + elif schedule == "sqrt": + betas = torch.linspace(linear_start, linear_end, n_timestep, dtype=torch.float64) ** 0.5 + else: + raise ValueError(f"schedule '{schedule}' unknown.") + return betas.numpy() + + +def make_ddim_timesteps(ddim_discr_method, num_ddim_timesteps, num_ddpm_timesteps, verbose=True): + if ddim_discr_method == 'uniform': + c = num_ddpm_timesteps // num_ddim_timesteps + ddim_timesteps = np.asarray(list(range(0, num_ddpm_timesteps, c))) + elif ddim_discr_method == 'quad': + ddim_timesteps = ((np.linspace(0, np.sqrt(num_ddpm_timesteps * .8), num_ddim_timesteps)) ** 2).astype(int) + else: + raise NotImplementedError(f'There is no ddim discretization method called "{ddim_discr_method}"') + + # assert ddim_timesteps.shape[0] == num_ddim_timesteps + # add one to get the final alpha values right (the ones from first scale to data during sampling) + steps_out = ddim_timesteps + 1 + if verbose: + print(f'Selected timesteps for ddim sampler: {steps_out}') + return steps_out + + +def make_ddim_sampling_parameters(alphacums, ddim_timesteps, eta, verbose=True): + # select alphas for computing the variance schedule + alphas = alphacums[ddim_timesteps] + alphas_prev = np.asarray([alphacums[0]] + alphacums[ddim_timesteps[:-1]].tolist()) + + # according the the formula provided in https://arxiv.org/abs/2010.02502 + sigmas = eta * np.sqrt((1 - alphas_prev) / (1 - alphas) * (1 - alphas / alphas_prev)) + if verbose: + print(f'Selected alphas for ddim sampler: a_t: {alphas}; a_(t-1): {alphas_prev}') + print(f'For the chosen value of eta, which is {eta}, ' + f'this results in the following sigma_t schedule for ddim sampler {sigmas}') + return sigmas, alphas, alphas_prev + + +def betas_for_alpha_bar(num_diffusion_timesteps, alpha_bar, max_beta=0.999): + """ + Create a beta schedule that discretizes the given alpha_t_bar function, + which defines the cumulative product of (1-beta) over time from t = [0,1]. + :param num_diffusion_timesteps: the number of betas to produce. + :param alpha_bar: a lambda that takes an argument t from 0 to 1 and + produces the cumulative product of (1-beta) up to that + part of the diffusion process. + :param max_beta: the maximum beta to use; use values lower than 1 to + prevent singularities. + """ + betas = [] + for i in range(num_diffusion_timesteps): + t1 = i / num_diffusion_timesteps + t2 = (i + 1) / num_diffusion_timesteps + betas.append(min(1 - alpha_bar(t2) / alpha_bar(t1), max_beta)) + return np.array(betas) + + +def extract_into_tensor(a, t, x_shape): + b, *_ = t.shape + out = a.gather(-1, t) + return out.reshape(b, *((1,) * (len(x_shape) - 1))) + + +def checkpoint(func, inputs, params, flag): + """ + Evaluate a function without caching intermediate activations, allowing for + reduced memory at the expense of extra compute in the backward pass. + :param func: the function to evaluate. + :param inputs: the argument sequence to pass to `func`. + :param params: a sequence of parameters `func` depends on but does not + explicitly take as arguments. + :param flag: if False, disable gradient checkpointing. + """ + if flag: + args = tuple(inputs) + tuple(params) + return CheckpointFunction.apply(func, len(inputs), *args) + else: + return func(*inputs) + + +class CheckpointFunction(torch.autograd.Function): + @staticmethod + def forward(ctx, run_function, length, *args): + ctx.run_function = run_function + ctx.input_tensors = list(args[:length]) + ctx.input_params = list(args[length:]) + ctx.gpu_autocast_kwargs = {"enabled": torch.is_autocast_enabled(), + "dtype": torch.get_autocast_gpu_dtype(), + "cache_enabled": torch.is_autocast_cache_enabled()} + with torch.no_grad(): + output_tensors = ctx.run_function(*ctx.input_tensors) + return output_tensors + + @staticmethod + def backward(ctx, *output_grads): + ctx.input_tensors = [x.detach().requires_grad_(True) for x in ctx.input_tensors] + with torch.enable_grad(), \ + torch.cuda.amp.autocast(**ctx.gpu_autocast_kwargs): + # Fixes a bug where the first op in run_function modifies the + # Tensor storage in place, which is not allowed for detach()'d + # Tensors. + shallow_copies = [x.view_as(x) for x in ctx.input_tensors] + output_tensors = ctx.run_function(*shallow_copies) + input_grads = torch.autograd.grad( + output_tensors, + ctx.input_tensors + ctx.input_params, + output_grads, + allow_unused=True, + ) + del ctx.input_tensors + del ctx.input_params + del output_tensors + return (None, None) + input_grads + + +def timestep_embedding(timesteps, dim, max_period=10000, repeat_only=False): + """ + Create sinusoidal timestep embeddings. + :param timesteps: a 1-D Tensor of N indices, one per batch element. + These may be fractional. + :param dim: the dimension of the output. + :param max_period: controls the minimum frequency of the embeddings. + :return: an [N x dim] Tensor of positional embeddings. + """ + if not repeat_only: + half = dim // 2 + freqs = torch.exp( + -math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half + ).to(device=timesteps.device) + args = timesteps[:, None].float() * freqs[None] + embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1) + if dim % 2: + embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1) + else: + embedding = repeat(timesteps, 'b -> b d', d=dim) + return embedding + + +def zero_module(module): + """ + Zero out the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().zero_() + return module + + +def scale_module(module, scale): + """ + Scale the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().mul_(scale) + return module + + +def mean_flat(tensor): + """ + Take the mean over all non-batch dimensions. + """ + return tensor.mean(dim=list(range(1, len(tensor.shape)))) + + +def normalization(channels): + """ + Make a standard normalization layer. + :param channels: number of input channels. + :return: an nn.Module for normalization. + """ + return GroupNorm32(32, channels) + + +# PyTorch 1.7 has SiLU, but we support PyTorch 1.5. +class SiLU(nn.Module): + def forward(self, x): + return x * torch.sigmoid(x) + + +class GroupNorm32(nn.GroupNorm): + def forward(self, x): + return super().forward(x.float()).type(x.dtype) + +def conv_nd(dims, *args, **kwargs): + """ + Create a 1D, 2D, or 3D convolution module. + """ + if dims == 1: + return nn.Conv1d(*args, **kwargs) + elif dims == 2: + return nn.Conv2d(*args, **kwargs) + elif dims == 3: + return nn.Conv3d(*args, **kwargs) + raise ValueError(f"unsupported dimensions: {dims}") + + +def linear(*args, **kwargs): + """ + Create a linear module. + """ + return nn.Linear(*args, **kwargs) + + +def avg_pool_nd(dims, *args, **kwargs): + """ + Create a 1D, 2D, or 3D average pooling module. + """ + if dims == 1: + return nn.AvgPool1d(*args, **kwargs) + elif dims == 2: + return nn.AvgPool2d(*args, **kwargs) + elif dims == 3: + return nn.AvgPool3d(*args, **kwargs) + raise ValueError(f"unsupported dimensions: {dims}") + + +class HybridConditioner(nn.Module): + + def __init__(self, c_concat_config, c_crossattn_config): + super().__init__() + self.concat_conditioner = instantiate_from_config(c_concat_config) + self.crossattn_conditioner = instantiate_from_config(c_crossattn_config) + + def forward(self, c_concat, c_crossattn): + c_concat = self.concat_conditioner(c_concat) + c_crossattn = self.crossattn_conditioner(c_crossattn) + return {'c_concat': [c_concat], 'c_crossattn': [c_crossattn]} + + +def noise_like(shape, device, repeat=False): + repeat_noise = lambda: torch.randn((1, *shape[1:]), device=device).repeat(shape[0], *((1,) * (len(shape) - 1))) + noise = lambda: torch.randn(shape, device=device) + return repeat_noise() if repeat else noise() \ No newline at end of file diff --git a/sd/stablediffusion/ldm/modules/distributions/__init__.py b/sd/stablediffusion/ldm/modules/distributions/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/ldm/modules/distributions/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/ldm/modules/distributions/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5625bb1424ce0656ebc513028a27e606d9acc946 Binary files /dev/null and b/sd/stablediffusion/ldm/modules/distributions/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/distributions/__pycache__/distributions.cpython-39.pyc b/sd/stablediffusion/ldm/modules/distributions/__pycache__/distributions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..83a79ec51c1b911c20209c95ea118f830d39b69c Binary files /dev/null and b/sd/stablediffusion/ldm/modules/distributions/__pycache__/distributions.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/distributions/distributions.py b/sd/stablediffusion/ldm/modules/distributions/distributions.py new file mode 100644 index 0000000000000000000000000000000000000000..f2b8ef901130efc171aa69742ca0244d94d3f2e9 --- /dev/null +++ b/sd/stablediffusion/ldm/modules/distributions/distributions.py @@ -0,0 +1,92 @@ +import torch +import numpy as np + + +class AbstractDistribution: + def sample(self): + raise NotImplementedError() + + def mode(self): + raise NotImplementedError() + + +class DiracDistribution(AbstractDistribution): + def __init__(self, value): + self.value = value + + def sample(self): + return self.value + + def mode(self): + return self.value + + +class DiagonalGaussianDistribution(object): + def __init__(self, parameters, deterministic=False): + self.parameters = parameters + self.mean, self.logvar = torch.chunk(parameters, 2, dim=1) + self.logvar = torch.clamp(self.logvar, -30.0, 20.0) + self.deterministic = deterministic + self.std = torch.exp(0.5 * self.logvar) + self.var = torch.exp(self.logvar) + if self.deterministic: + self.var = self.std = torch.zeros_like(self.mean).to(device=self.parameters.device) + + def sample(self): + x = self.mean + self.std * torch.randn(self.mean.shape).to(device=self.parameters.device) + return x + + def kl(self, other=None): + if self.deterministic: + return torch.Tensor([0.]) + else: + if other is None: + return 0.5 * torch.sum(torch.pow(self.mean, 2) + + self.var - 1.0 - self.logvar, + dim=[1, 2, 3]) + else: + return 0.5 * torch.sum( + torch.pow(self.mean - other.mean, 2) / other.var + + self.var / other.var - 1.0 - self.logvar + other.logvar, + dim=[1, 2, 3]) + + def nll(self, sample, dims=[1,2,3]): + if self.deterministic: + return torch.Tensor([0.]) + logtwopi = np.log(2.0 * np.pi) + return 0.5 * torch.sum( + logtwopi + self.logvar + torch.pow(sample - self.mean, 2) / self.var, + dim=dims) + + def mode(self): + return self.mean + + +def normal_kl(mean1, logvar1, mean2, logvar2): + """ + source: https://github.com/openai/guided-diffusion/blob/27c20a8fab9cb472df5d6bdd6c8d11c8f430b924/guided_diffusion/losses.py#L12 + Compute the KL divergence between two gaussians. + Shapes are automatically broadcasted, so batches can be compared to + scalars, among other use cases. + """ + tensor = None + for obj in (mean1, logvar1, mean2, logvar2): + if isinstance(obj, torch.Tensor): + tensor = obj + break + assert tensor is not None, "at least one argument must be a Tensor" + + # Force variances to be Tensors. Broadcasting helps convert scalars to + # Tensors, but it does not work for torch.exp(). + logvar1, logvar2 = [ + x if isinstance(x, torch.Tensor) else torch.tensor(x).to(tensor) + for x in (logvar1, logvar2) + ] + + return 0.5 * ( + -1.0 + + logvar2 + - logvar1 + + torch.exp(logvar1 - logvar2) + + ((mean1 - mean2) ** 2) * torch.exp(-logvar2) + ) diff --git a/sd/stablediffusion/ldm/modules/ema.py b/sd/stablediffusion/ldm/modules/ema.py new file mode 100644 index 0000000000000000000000000000000000000000..bded25019b9bcbcd0260f0b8185f8c7859ca58c4 --- /dev/null +++ b/sd/stablediffusion/ldm/modules/ema.py @@ -0,0 +1,80 @@ +import torch +from torch import nn + + +class LitEma(nn.Module): + def __init__(self, model, decay=0.9999, use_num_upates=True): + super().__init__() + if decay < 0.0 or decay > 1.0: + raise ValueError('Decay must be between 0 and 1') + + self.m_name2s_name = {} + self.register_buffer('decay', torch.tensor(decay, dtype=torch.float32)) + self.register_buffer('num_updates', torch.tensor(0, dtype=torch.int) if use_num_upates + else torch.tensor(-1, dtype=torch.int)) + + for name, p in model.named_parameters(): + if p.requires_grad: + # remove as '.'-character is not allowed in buffers + s_name = name.replace('.', '') + self.m_name2s_name.update({name: s_name}) + self.register_buffer(s_name, p.clone().detach().data) + + self.collected_params = [] + + def reset_num_updates(self): + del self.num_updates + self.register_buffer('num_updates', torch.tensor(0, dtype=torch.int)) + + def forward(self, model): + decay = self.decay + + if self.num_updates >= 0: + self.num_updates += 1 + decay = min(self.decay, (1 + self.num_updates) / (10 + self.num_updates)) + + one_minus_decay = 1.0 - decay + + with torch.no_grad(): + m_param = dict(model.named_parameters()) + shadow_params = dict(self.named_buffers()) + + for key in m_param: + if m_param[key].requires_grad: + sname = self.m_name2s_name[key] + shadow_params[sname] = shadow_params[sname].type_as(m_param[key]) + shadow_params[sname].sub_(one_minus_decay * (shadow_params[sname] - m_param[key])) + else: + assert not key in self.m_name2s_name + + def copy_to(self, model): + m_param = dict(model.named_parameters()) + shadow_params = dict(self.named_buffers()) + for key in m_param: + if m_param[key].requires_grad: + m_param[key].data.copy_(shadow_params[self.m_name2s_name[key]].data) + else: + assert not key in self.m_name2s_name + + def store(self, parameters): + """ + Save the current parameters for restoring later. + Args: + parameters: Iterable of `torch.nn.Parameter`; the parameters to be + temporarily stored. + """ + self.collected_params = [param.clone() for param in parameters] + + def restore(self, parameters): + """ + Restore the parameters stored with the `store` method. + Useful to validate the model with EMA parameters without affecting the + original optimization process. Store the parameters before the + `copy_to` method. After validation (or model saving), use this to + restore the former parameters. + Args: + parameters: Iterable of `torch.nn.Parameter`; the parameters to be + updated with the stored parameters. + """ + for c_param, param in zip(self.collected_params, parameters): + param.data.copy_(c_param.data) diff --git a/sd/stablediffusion/ldm/modules/encoders/__init__.py b/sd/stablediffusion/ldm/modules/encoders/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/ldm/modules/encoders/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/ldm/modules/encoders/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..518ae080240b43b749345ea63316e0cef9ba6a39 Binary files /dev/null and b/sd/stablediffusion/ldm/modules/encoders/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/encoders/__pycache__/modules.cpython-39.pyc b/sd/stablediffusion/ldm/modules/encoders/__pycache__/modules.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab8e9632f03bd153a7fe302f9359444c8c1708ae Binary files /dev/null and b/sd/stablediffusion/ldm/modules/encoders/__pycache__/modules.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/encoders/modules.py b/sd/stablediffusion/ldm/modules/encoders/modules.py new file mode 100644 index 0000000000000000000000000000000000000000..4edd5496b9e668ea72a5be39db9cca94b6a42f9b --- /dev/null +++ b/sd/stablediffusion/ldm/modules/encoders/modules.py @@ -0,0 +1,213 @@ +import torch +import torch.nn as nn +from torch.utils.checkpoint import checkpoint + +from transformers import T5Tokenizer, T5EncoderModel, CLIPTokenizer, CLIPTextModel + +import open_clip +from ldm.util import default, count_params + + +class AbstractEncoder(nn.Module): + def __init__(self): + super().__init__() + + def encode(self, *args, **kwargs): + raise NotImplementedError + + +class IdentityEncoder(AbstractEncoder): + + def encode(self, x): + return x + + +class ClassEmbedder(nn.Module): + def __init__(self, embed_dim, n_classes=1000, key='class', ucg_rate=0.1): + super().__init__() + self.key = key + self.embedding = nn.Embedding(n_classes, embed_dim) + self.n_classes = n_classes + self.ucg_rate = ucg_rate + + def forward(self, batch, key=None, disable_dropout=False): + if key is None: + key = self.key + # this is for use in crossattn + c = batch[key][:, None] + if self.ucg_rate > 0. and not disable_dropout: + mask = 1. - torch.bernoulli(torch.ones_like(c) * self.ucg_rate) + c = mask * c + (1-mask) * torch.ones_like(c)*(self.n_classes-1) + c = c.long() + c = self.embedding(c) + return c + + def get_unconditional_conditioning(self, bs, device="cuda"): + uc_class = self.n_classes - 1 # 1000 classes --> 0 ... 999, one extra class for ucg (class 1000) + uc = torch.ones((bs,), device=device) * uc_class + uc = {self.key: uc} + return uc + + +def disabled_train(self, mode=True): + """Overwrite model.train with this function to make sure train/eval mode + does not change anymore.""" + return self + + +class FrozenT5Embedder(AbstractEncoder): + """Uses the T5 transformer encoder for text""" + def __init__(self, version="google/t5-v1_1-large", device="cuda", max_length=77, freeze=True): # others are google/t5-v1_1-xl and google/t5-v1_1-xxl + super().__init__() + self.tokenizer = T5Tokenizer.from_pretrained(version) + self.transformer = T5EncoderModel.from_pretrained(version) + self.device = device + self.max_length = max_length # TODO: typical value? + if freeze: + self.freeze() + + def freeze(self): + self.transformer = self.transformer.eval() + #self.train = disabled_train + for param in self.parameters(): + param.requires_grad = False + + def forward(self, text): + batch_encoding = self.tokenizer(text, truncation=True, max_length=self.max_length, return_length=True, + return_overflowing_tokens=False, padding="max_length", return_tensors="pt") + tokens = batch_encoding["input_ids"].to(self.device) + outputs = self.transformer(input_ids=tokens) + + z = outputs.last_hidden_state + return z + + def encode(self, text): + return self(text) + + +class FrozenCLIPEmbedder(AbstractEncoder): + """Uses the CLIP transformer encoder for text (from huggingface)""" + LAYERS = [ + "last", + "pooled", + "hidden" + ] + def __init__(self, version="openai/clip-vit-large-patch14", device="cuda", max_length=77, + freeze=True, layer="last", layer_idx=None): # clip-vit-base-patch32 + super().__init__() + assert layer in self.LAYERS + self.tokenizer = CLIPTokenizer.from_pretrained(version) + self.transformer = CLIPTextModel.from_pretrained(version) + self.device = device + self.max_length = max_length + if freeze: + self.freeze() + self.layer = layer + self.layer_idx = layer_idx + if layer == "hidden": + assert layer_idx is not None + assert 0 <= abs(layer_idx) <= 12 + + def freeze(self): + self.transformer = self.transformer.eval() + #self.train = disabled_train + for param in self.parameters(): + param.requires_grad = False + + def forward(self, text): + batch_encoding = self.tokenizer(text, truncation=True, max_length=self.max_length, return_length=True, + return_overflowing_tokens=False, padding="max_length", return_tensors="pt") + tokens = batch_encoding["input_ids"].to(self.device) + outputs = self.transformer(input_ids=tokens, output_hidden_states=self.layer=="hidden") + if self.layer == "last": + z = outputs.last_hidden_state + elif self.layer == "pooled": + z = outputs.pooler_output[:, None, :] + else: + z = outputs.hidden_states[self.layer_idx] + return z + + def encode(self, text): + return self(text) + + +class FrozenOpenCLIPEmbedder(AbstractEncoder): + """ + Uses the OpenCLIP transformer encoder for text + """ + LAYERS = [ + #"pooled", + "last", + "penultimate" + ] + def __init__(self, arch="ViT-H-14", version="laion2b_s32b_b79k", device="cuda", max_length=77, + freeze=True, layer="last"): + super().__init__() + assert layer in self.LAYERS + model, _, _ = open_clip.create_model_and_transforms(arch, device=torch.device('cpu'), pretrained=version) + del model.visual + self.model = model + + self.device = device + self.max_length = max_length + if freeze: + self.freeze() + self.layer = layer + if self.layer == "last": + self.layer_idx = 0 + elif self.layer == "penultimate": + self.layer_idx = 1 + else: + raise NotImplementedError() + + def freeze(self): + self.model = self.model.eval() + for param in self.parameters(): + param.requires_grad = False + + def forward(self, text): + tokens = open_clip.tokenize(text) + z = self.encode_with_transformer(tokens.to(self.device)) + return z + + def encode_with_transformer(self, text): + x = self.model.token_embedding(text) # [batch_size, n_ctx, d_model] + x = x + self.model.positional_embedding + x = x.permute(1, 0, 2) # NLD -> LND + x = self.text_transformer_forward(x, attn_mask=self.model.attn_mask) + x = x.permute(1, 0, 2) # LND -> NLD + x = self.model.ln_final(x) + return x + + def text_transformer_forward(self, x: torch.Tensor, attn_mask = None): + for i, r in enumerate(self.model.transformer.resblocks): + if i == len(self.model.transformer.resblocks) - self.layer_idx: + break + if self.model.transformer.grad_checkpointing and not torch.jit.is_scripting(): + x = checkpoint(r, x, attn_mask) + else: + x = r(x, attn_mask=attn_mask) + return x + + def encode(self, text): + return self(text) + + +class FrozenCLIPT5Encoder(AbstractEncoder): + def __init__(self, clip_version="openai/clip-vit-large-patch14", t5_version="google/t5-v1_1-xl", device="cuda", + clip_max_length=77, t5_max_length=77): + super().__init__() + self.clip_encoder = FrozenCLIPEmbedder(clip_version, device, max_length=clip_max_length) + self.t5_encoder = FrozenT5Embedder(t5_version, device, max_length=t5_max_length) + print(f"{self.clip_encoder.__class__.__name__} has {count_params(self.clip_encoder)*1.e-6:.2f} M parameters, " + f"{self.t5_encoder.__class__.__name__} comes with {count_params(self.t5_encoder)*1.e-6:.2f} M params.") + + def encode(self, text): + return self(text) + + def forward(self, text): + clip_z = self.clip_encoder.encode(text) + t5_z = self.t5_encoder.encode(text) + return [clip_z, t5_z] + + diff --git a/sd/stablediffusion/ldm/modules/image_degradation/__init__.py b/sd/stablediffusion/ldm/modules/image_degradation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7836cada81f90ded99c58d5942eea4c3477f58fc --- /dev/null +++ b/sd/stablediffusion/ldm/modules/image_degradation/__init__.py @@ -0,0 +1,2 @@ +from ldm.modules.image_degradation.bsrgan import degradation_bsrgan_variant as degradation_fn_bsr +from ldm.modules.image_degradation.bsrgan_light import degradation_bsrgan_variant as degradation_fn_bsr_light diff --git a/sd/stablediffusion/ldm/modules/image_degradation/bsrgan.py b/sd/stablediffusion/ldm/modules/image_degradation/bsrgan.py new file mode 100644 index 0000000000000000000000000000000000000000..32ef56169978e550090261cddbcf5eb611a6173b --- /dev/null +++ b/sd/stablediffusion/ldm/modules/image_degradation/bsrgan.py @@ -0,0 +1,730 @@ +# -*- coding: utf-8 -*- +""" +# -------------------------------------------- +# Super-Resolution +# -------------------------------------------- +# +# Kai Zhang (cskaizhang@gmail.com) +# https://github.com/cszn +# From 2019/03--2021/08 +# -------------------------------------------- +""" + +import numpy as np +import cv2 +import torch + +from functools import partial +import random +from scipy import ndimage +import scipy +import scipy.stats as ss +from scipy.interpolate import interp2d +from scipy.linalg import orth +import albumentations + +import ldm.modules.image_degradation.utils_image as util + + +def modcrop_np(img, sf): + ''' + Args: + img: numpy image, WxH or WxHxC + sf: scale factor + Return: + cropped image + ''' + w, h = img.shape[:2] + im = np.copy(img) + return im[:w - w % sf, :h - h % sf, ...] + + +""" +# -------------------------------------------- +# anisotropic Gaussian kernels +# -------------------------------------------- +""" + + +def analytic_kernel(k): + """Calculate the X4 kernel from the X2 kernel (for proof see appendix in paper)""" + k_size = k.shape[0] + # Calculate the big kernels size + big_k = np.zeros((3 * k_size - 2, 3 * k_size - 2)) + # Loop over the small kernel to fill the big one + for r in range(k_size): + for c in range(k_size): + big_k[2 * r:2 * r + k_size, 2 * c:2 * c + k_size] += k[r, c] * k + # Crop the edges of the big kernel to ignore very small values and increase run time of SR + crop = k_size // 2 + cropped_big_k = big_k[crop:-crop, crop:-crop] + # Normalize to 1 + return cropped_big_k / cropped_big_k.sum() + + +def anisotropic_Gaussian(ksize=15, theta=np.pi, l1=6, l2=6): + """ generate an anisotropic Gaussian kernel + Args: + ksize : e.g., 15, kernel size + theta : [0, pi], rotation angle range + l1 : [0.1,50], scaling of eigenvalues + l2 : [0.1,l1], scaling of eigenvalues + If l1 = l2, will get an isotropic Gaussian kernel. + Returns: + k : kernel + """ + + v = np.dot(np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]), np.array([1., 0.])) + V = np.array([[v[0], v[1]], [v[1], -v[0]]]) + D = np.array([[l1, 0], [0, l2]]) + Sigma = np.dot(np.dot(V, D), np.linalg.inv(V)) + k = gm_blur_kernel(mean=[0, 0], cov=Sigma, size=ksize) + + return k + + +def gm_blur_kernel(mean, cov, size=15): + center = size / 2.0 + 0.5 + k = np.zeros([size, size]) + for y in range(size): + for x in range(size): + cy = y - center + 1 + cx = x - center + 1 + k[y, x] = ss.multivariate_normal.pdf([cx, cy], mean=mean, cov=cov) + + k = k / np.sum(k) + return k + + +def shift_pixel(x, sf, upper_left=True): + """shift pixel for super-resolution with different scale factors + Args: + x: WxHxC or WxH + sf: scale factor + upper_left: shift direction + """ + h, w = x.shape[:2] + shift = (sf - 1) * 0.5 + xv, yv = np.arange(0, w, 1.0), np.arange(0, h, 1.0) + if upper_left: + x1 = xv + shift + y1 = yv + shift + else: + x1 = xv - shift + y1 = yv - shift + + x1 = np.clip(x1, 0, w - 1) + y1 = np.clip(y1, 0, h - 1) + + if x.ndim == 2: + x = interp2d(xv, yv, x)(x1, y1) + if x.ndim == 3: + for i in range(x.shape[-1]): + x[:, :, i] = interp2d(xv, yv, x[:, :, i])(x1, y1) + + return x + + +def blur(x, k): + ''' + x: image, NxcxHxW + k: kernel, Nx1xhxw + ''' + n, c = x.shape[:2] + p1, p2 = (k.shape[-2] - 1) // 2, (k.shape[-1] - 1) // 2 + x = torch.nn.functional.pad(x, pad=(p1, p2, p1, p2), mode='replicate') + k = k.repeat(1, c, 1, 1) + k = k.view(-1, 1, k.shape[2], k.shape[3]) + x = x.view(1, -1, x.shape[2], x.shape[3]) + x = torch.nn.functional.conv2d(x, k, bias=None, stride=1, padding=0, groups=n * c) + x = x.view(n, c, x.shape[2], x.shape[3]) + + return x + + +def gen_kernel(k_size=np.array([15, 15]), scale_factor=np.array([4, 4]), min_var=0.6, max_var=10., noise_level=0): + """" + # modified version of https://github.com/assafshocher/BlindSR_dataset_generator + # Kai Zhang + # min_var = 0.175 * sf # variance of the gaussian kernel will be sampled between min_var and max_var + # max_var = 2.5 * sf + """ + # Set random eigen-vals (lambdas) and angle (theta) for COV matrix + lambda_1 = min_var + np.random.rand() * (max_var - min_var) + lambda_2 = min_var + np.random.rand() * (max_var - min_var) + theta = np.random.rand() * np.pi # random theta + noise = -noise_level + np.random.rand(*k_size) * noise_level * 2 + + # Set COV matrix using Lambdas and Theta + LAMBDA = np.diag([lambda_1, lambda_2]) + Q = np.array([[np.cos(theta), -np.sin(theta)], + [np.sin(theta), np.cos(theta)]]) + SIGMA = Q @ LAMBDA @ Q.T + INV_SIGMA = np.linalg.inv(SIGMA)[None, None, :, :] + + # Set expectation position (shifting kernel for aligned image) + MU = k_size // 2 - 0.5 * (scale_factor - 1) # - 0.5 * (scale_factor - k_size % 2) + MU = MU[None, None, :, None] + + # Create meshgrid for Gaussian + [X, Y] = np.meshgrid(range(k_size[0]), range(k_size[1])) + Z = np.stack([X, Y], 2)[:, :, :, None] + + # Calcualte Gaussian for every pixel of the kernel + ZZ = Z - MU + ZZ_t = ZZ.transpose(0, 1, 3, 2) + raw_kernel = np.exp(-0.5 * np.squeeze(ZZ_t @ INV_SIGMA @ ZZ)) * (1 + noise) + + # shift the kernel so it will be centered + # raw_kernel_centered = kernel_shift(raw_kernel, scale_factor) + + # Normalize the kernel and return + # kernel = raw_kernel_centered / np.sum(raw_kernel_centered) + kernel = raw_kernel / np.sum(raw_kernel) + return kernel + + +def fspecial_gaussian(hsize, sigma): + hsize = [hsize, hsize] + siz = [(hsize[0] - 1.0) / 2.0, (hsize[1] - 1.0) / 2.0] + std = sigma + [x, y] = np.meshgrid(np.arange(-siz[1], siz[1] + 1), np.arange(-siz[0], siz[0] + 1)) + arg = -(x * x + y * y) / (2 * std * std) + h = np.exp(arg) + h[h < scipy.finfo(float).eps * h.max()] = 0 + sumh = h.sum() + if sumh != 0: + h = h / sumh + return h + + +def fspecial_laplacian(alpha): + alpha = max([0, min([alpha, 1])]) + h1 = alpha / (alpha + 1) + h2 = (1 - alpha) / (alpha + 1) + h = [[h1, h2, h1], [h2, -4 / (alpha + 1), h2], [h1, h2, h1]] + h = np.array(h) + return h + + +def fspecial(filter_type, *args, **kwargs): + ''' + python code from: + https://github.com/ronaldosena/imagens-medicas-2/blob/40171a6c259edec7827a6693a93955de2bd39e76/Aulas/aula_2_-_uniform_filter/matlab_fspecial.py + ''' + if filter_type == 'gaussian': + return fspecial_gaussian(*args, **kwargs) + if filter_type == 'laplacian': + return fspecial_laplacian(*args, **kwargs) + + +""" +# -------------------------------------------- +# degradation models +# -------------------------------------------- +""" + + +def bicubic_degradation(x, sf=3): + ''' + Args: + x: HxWxC image, [0, 1] + sf: down-scale factor + Return: + bicubicly downsampled LR image + ''' + x = util.imresize_np(x, scale=1 / sf) + return x + + +def srmd_degradation(x, k, sf=3): + ''' blur + bicubic downsampling + Args: + x: HxWxC image, [0, 1] + k: hxw, double + sf: down-scale factor + Return: + downsampled LR image + Reference: + @inproceedings{zhang2018learning, + title={Learning a single convolutional super-resolution network for multiple degradations}, + author={Zhang, Kai and Zuo, Wangmeng and Zhang, Lei}, + booktitle={IEEE Conference on Computer Vision and Pattern Recognition}, + pages={3262--3271}, + year={2018} + } + ''' + x = ndimage.filters.convolve(x, np.expand_dims(k, axis=2), mode='wrap') # 'nearest' | 'mirror' + x = bicubic_degradation(x, sf=sf) + return x + + +def dpsr_degradation(x, k, sf=3): + ''' bicubic downsampling + blur + Args: + x: HxWxC image, [0, 1] + k: hxw, double + sf: down-scale factor + Return: + downsampled LR image + Reference: + @inproceedings{zhang2019deep, + title={Deep Plug-and-Play Super-Resolution for Arbitrary Blur Kernels}, + author={Zhang, Kai and Zuo, Wangmeng and Zhang, Lei}, + booktitle={IEEE Conference on Computer Vision and Pattern Recognition}, + pages={1671--1681}, + year={2019} + } + ''' + x = bicubic_degradation(x, sf=sf) + x = ndimage.filters.convolve(x, np.expand_dims(k, axis=2), mode='wrap') + return x + + +def classical_degradation(x, k, sf=3): + ''' blur + downsampling + Args: + x: HxWxC image, [0, 1]/[0, 255] + k: hxw, double + sf: down-scale factor + Return: + downsampled LR image + ''' + x = ndimage.filters.convolve(x, np.expand_dims(k, axis=2), mode='wrap') + # x = filters.correlate(x, np.expand_dims(np.flip(k), axis=2)) + st = 0 + return x[st::sf, st::sf, ...] + + +def add_sharpening(img, weight=0.5, radius=50, threshold=10): + """USM sharpening. borrowed from real-ESRGAN + Input image: I; Blurry image: B. + 1. K = I + weight * (I - B) + 2. Mask = 1 if abs(I - B) > threshold, else: 0 + 3. Blur mask: + 4. Out = Mask * K + (1 - Mask) * I + Args: + img (Numpy array): Input image, HWC, BGR; float32, [0, 1]. + weight (float): Sharp weight. Default: 1. + radius (float): Kernel size of Gaussian blur. Default: 50. + threshold (int): + """ + if radius % 2 == 0: + radius += 1 + blur = cv2.GaussianBlur(img, (radius, radius), 0) + residual = img - blur + mask = np.abs(residual) * 255 > threshold + mask = mask.astype('float32') + soft_mask = cv2.GaussianBlur(mask, (radius, radius), 0) + + K = img + weight * residual + K = np.clip(K, 0, 1) + return soft_mask * K + (1 - soft_mask) * img + + +def add_blur(img, sf=4): + wd2 = 4.0 + sf + wd = 2.0 + 0.2 * sf + if random.random() < 0.5: + l1 = wd2 * random.random() + l2 = wd2 * random.random() + k = anisotropic_Gaussian(ksize=2 * random.randint(2, 11) + 3, theta=random.random() * np.pi, l1=l1, l2=l2) + else: + k = fspecial('gaussian', 2 * random.randint(2, 11) + 3, wd * random.random()) + img = ndimage.filters.convolve(img, np.expand_dims(k, axis=2), mode='mirror') + + return img + + +def add_resize(img, sf=4): + rnum = np.random.rand() + if rnum > 0.8: # up + sf1 = random.uniform(1, 2) + elif rnum < 0.7: # down + sf1 = random.uniform(0.5 / sf, 1) + else: + sf1 = 1.0 + img = cv2.resize(img, (int(sf1 * img.shape[1]), int(sf1 * img.shape[0])), interpolation=random.choice([1, 2, 3])) + img = np.clip(img, 0.0, 1.0) + + return img + + +# def add_Gaussian_noise(img, noise_level1=2, noise_level2=25): +# noise_level = random.randint(noise_level1, noise_level2) +# rnum = np.random.rand() +# if rnum > 0.6: # add color Gaussian noise +# img += np.random.normal(0, noise_level / 255.0, img.shape).astype(np.float32) +# elif rnum < 0.4: # add grayscale Gaussian noise +# img += np.random.normal(0, noise_level / 255.0, (*img.shape[:2], 1)).astype(np.float32) +# else: # add noise +# L = noise_level2 / 255. +# D = np.diag(np.random.rand(3)) +# U = orth(np.random.rand(3, 3)) +# conv = np.dot(np.dot(np.transpose(U), D), U) +# img += np.random.multivariate_normal([0, 0, 0], np.abs(L ** 2 * conv), img.shape[:2]).astype(np.float32) +# img = np.clip(img, 0.0, 1.0) +# return img + +def add_Gaussian_noise(img, noise_level1=2, noise_level2=25): + noise_level = random.randint(noise_level1, noise_level2) + rnum = np.random.rand() + if rnum > 0.6: # add color Gaussian noise + img = img + np.random.normal(0, noise_level / 255.0, img.shape).astype(np.float32) + elif rnum < 0.4: # add grayscale Gaussian noise + img = img + np.random.normal(0, noise_level / 255.0, (*img.shape[:2], 1)).astype(np.float32) + else: # add noise + L = noise_level2 / 255. + D = np.diag(np.random.rand(3)) + U = orth(np.random.rand(3, 3)) + conv = np.dot(np.dot(np.transpose(U), D), U) + img = img + np.random.multivariate_normal([0, 0, 0], np.abs(L ** 2 * conv), img.shape[:2]).astype(np.float32) + img = np.clip(img, 0.0, 1.0) + return img + + +def add_speckle_noise(img, noise_level1=2, noise_level2=25): + noise_level = random.randint(noise_level1, noise_level2) + img = np.clip(img, 0.0, 1.0) + rnum = random.random() + if rnum > 0.6: + img += img * np.random.normal(0, noise_level / 255.0, img.shape).astype(np.float32) + elif rnum < 0.4: + img += img * np.random.normal(0, noise_level / 255.0, (*img.shape[:2], 1)).astype(np.float32) + else: + L = noise_level2 / 255. + D = np.diag(np.random.rand(3)) + U = orth(np.random.rand(3, 3)) + conv = np.dot(np.dot(np.transpose(U), D), U) + img += img * np.random.multivariate_normal([0, 0, 0], np.abs(L ** 2 * conv), img.shape[:2]).astype(np.float32) + img = np.clip(img, 0.0, 1.0) + return img + + +def add_Poisson_noise(img): + img = np.clip((img * 255.0).round(), 0, 255) / 255. + vals = 10 ** (2 * random.random() + 2.0) # [2, 4] + if random.random() < 0.5: + img = np.random.poisson(img * vals).astype(np.float32) / vals + else: + img_gray = np.dot(img[..., :3], [0.299, 0.587, 0.114]) + img_gray = np.clip((img_gray * 255.0).round(), 0, 255) / 255. + noise_gray = np.random.poisson(img_gray * vals).astype(np.float32) / vals - img_gray + img += noise_gray[:, :, np.newaxis] + img = np.clip(img, 0.0, 1.0) + return img + + +def add_JPEG_noise(img): + quality_factor = random.randint(30, 95) + img = cv2.cvtColor(util.single2uint(img), cv2.COLOR_RGB2BGR) + result, encimg = cv2.imencode('.jpg', img, [int(cv2.IMWRITE_JPEG_QUALITY), quality_factor]) + img = cv2.imdecode(encimg, 1) + img = cv2.cvtColor(util.uint2single(img), cv2.COLOR_BGR2RGB) + return img + + +def random_crop(lq, hq, sf=4, lq_patchsize=64): + h, w = lq.shape[:2] + rnd_h = random.randint(0, h - lq_patchsize) + rnd_w = random.randint(0, w - lq_patchsize) + lq = lq[rnd_h:rnd_h + lq_patchsize, rnd_w:rnd_w + lq_patchsize, :] + + rnd_h_H, rnd_w_H = int(rnd_h * sf), int(rnd_w * sf) + hq = hq[rnd_h_H:rnd_h_H + lq_patchsize * sf, rnd_w_H:rnd_w_H + lq_patchsize * sf, :] + return lq, hq + + +def degradation_bsrgan(img, sf=4, lq_patchsize=72, isp_model=None): + """ + This is the degradation model of BSRGAN from the paper + "Designing a Practical Degradation Model for Deep Blind Image Super-Resolution" + ---------- + img: HXWXC, [0, 1], its size should be large than (lq_patchsizexsf)x(lq_patchsizexsf) + sf: scale factor + isp_model: camera ISP model + Returns + ------- + img: low-quality patch, size: lq_patchsizeXlq_patchsizeXC, range: [0, 1] + hq: corresponding high-quality patch, size: (lq_patchsizexsf)X(lq_patchsizexsf)XC, range: [0, 1] + """ + isp_prob, jpeg_prob, scale2_prob = 0.25, 0.9, 0.25 + sf_ori = sf + + h1, w1 = img.shape[:2] + img = img.copy()[:w1 - w1 % sf, :h1 - h1 % sf, ...] # mod crop + h, w = img.shape[:2] + + if h < lq_patchsize * sf or w < lq_patchsize * sf: + raise ValueError(f'img size ({h1}X{w1}) is too small!') + + hq = img.copy() + + if sf == 4 and random.random() < scale2_prob: # downsample1 + if np.random.rand() < 0.5: + img = cv2.resize(img, (int(1 / 2 * img.shape[1]), int(1 / 2 * img.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + img = util.imresize_np(img, 1 / 2, True) + img = np.clip(img, 0.0, 1.0) + sf = 2 + + shuffle_order = random.sample(range(7), 7) + idx1, idx2 = shuffle_order.index(2), shuffle_order.index(3) + if idx1 > idx2: # keep downsample3 last + shuffle_order[idx1], shuffle_order[idx2] = shuffle_order[idx2], shuffle_order[idx1] + + for i in shuffle_order: + + if i == 0: + img = add_blur(img, sf=sf) + + elif i == 1: + img = add_blur(img, sf=sf) + + elif i == 2: + a, b = img.shape[1], img.shape[0] + # downsample2 + if random.random() < 0.75: + sf1 = random.uniform(1, 2 * sf) + img = cv2.resize(img, (int(1 / sf1 * img.shape[1]), int(1 / sf1 * img.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + k = fspecial('gaussian', 25, random.uniform(0.1, 0.6 * sf)) + k_shifted = shift_pixel(k, sf) + k_shifted = k_shifted / k_shifted.sum() # blur with shifted kernel + img = ndimage.filters.convolve(img, np.expand_dims(k_shifted, axis=2), mode='mirror') + img = img[0::sf, 0::sf, ...] # nearest downsampling + img = np.clip(img, 0.0, 1.0) + + elif i == 3: + # downsample3 + img = cv2.resize(img, (int(1 / sf * a), int(1 / sf * b)), interpolation=random.choice([1, 2, 3])) + img = np.clip(img, 0.0, 1.0) + + elif i == 4: + # add Gaussian noise + img = add_Gaussian_noise(img, noise_level1=2, noise_level2=25) + + elif i == 5: + # add JPEG noise + if random.random() < jpeg_prob: + img = add_JPEG_noise(img) + + elif i == 6: + # add processed camera sensor noise + if random.random() < isp_prob and isp_model is not None: + with torch.no_grad(): + img, hq = isp_model.forward(img.copy(), hq) + + # add final JPEG compression noise + img = add_JPEG_noise(img) + + # random crop + img, hq = random_crop(img, hq, sf_ori, lq_patchsize) + + return img, hq + + +# todo no isp_model? +def degradation_bsrgan_variant(image, sf=4, isp_model=None): + """ + This is the degradation model of BSRGAN from the paper + "Designing a Practical Degradation Model for Deep Blind Image Super-Resolution" + ---------- + sf: scale factor + isp_model: camera ISP model + Returns + ------- + img: low-quality patch, size: lq_patchsizeXlq_patchsizeXC, range: [0, 1] + hq: corresponding high-quality patch, size: (lq_patchsizexsf)X(lq_patchsizexsf)XC, range: [0, 1] + """ + image = util.uint2single(image) + isp_prob, jpeg_prob, scale2_prob = 0.25, 0.9, 0.25 + sf_ori = sf + + h1, w1 = image.shape[:2] + image = image.copy()[:w1 - w1 % sf, :h1 - h1 % sf, ...] # mod crop + h, w = image.shape[:2] + + hq = image.copy() + + if sf == 4 and random.random() < scale2_prob: # downsample1 + if np.random.rand() < 0.5: + image = cv2.resize(image, (int(1 / 2 * image.shape[1]), int(1 / 2 * image.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + image = util.imresize_np(image, 1 / 2, True) + image = np.clip(image, 0.0, 1.0) + sf = 2 + + shuffle_order = random.sample(range(7), 7) + idx1, idx2 = shuffle_order.index(2), shuffle_order.index(3) + if idx1 > idx2: # keep downsample3 last + shuffle_order[idx1], shuffle_order[idx2] = shuffle_order[idx2], shuffle_order[idx1] + + for i in shuffle_order: + + if i == 0: + image = add_blur(image, sf=sf) + + elif i == 1: + image = add_blur(image, sf=sf) + + elif i == 2: + a, b = image.shape[1], image.shape[0] + # downsample2 + if random.random() < 0.75: + sf1 = random.uniform(1, 2 * sf) + image = cv2.resize(image, (int(1 / sf1 * image.shape[1]), int(1 / sf1 * image.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + k = fspecial('gaussian', 25, random.uniform(0.1, 0.6 * sf)) + k_shifted = shift_pixel(k, sf) + k_shifted = k_shifted / k_shifted.sum() # blur with shifted kernel + image = ndimage.filters.convolve(image, np.expand_dims(k_shifted, axis=2), mode='mirror') + image = image[0::sf, 0::sf, ...] # nearest downsampling + image = np.clip(image, 0.0, 1.0) + + elif i == 3: + # downsample3 + image = cv2.resize(image, (int(1 / sf * a), int(1 / sf * b)), interpolation=random.choice([1, 2, 3])) + image = np.clip(image, 0.0, 1.0) + + elif i == 4: + # add Gaussian noise + image = add_Gaussian_noise(image, noise_level1=2, noise_level2=25) + + elif i == 5: + # add JPEG noise + if random.random() < jpeg_prob: + image = add_JPEG_noise(image) + + # elif i == 6: + # # add processed camera sensor noise + # if random.random() < isp_prob and isp_model is not None: + # with torch.no_grad(): + # img, hq = isp_model.forward(img.copy(), hq) + + # add final JPEG compression noise + image = add_JPEG_noise(image) + image = util.single2uint(image) + example = {"image":image} + return example + + +# TODO incase there is a pickle error one needs to replace a += x with a = a + x in add_speckle_noise etc... +def degradation_bsrgan_plus(img, sf=4, shuffle_prob=0.5, use_sharp=True, lq_patchsize=64, isp_model=None): + """ + This is an extended degradation model by combining + the degradation models of BSRGAN and Real-ESRGAN + ---------- + img: HXWXC, [0, 1], its size should be large than (lq_patchsizexsf)x(lq_patchsizexsf) + sf: scale factor + use_shuffle: the degradation shuffle + use_sharp: sharpening the img + Returns + ------- + img: low-quality patch, size: lq_patchsizeXlq_patchsizeXC, range: [0, 1] + hq: corresponding high-quality patch, size: (lq_patchsizexsf)X(lq_patchsizexsf)XC, range: [0, 1] + """ + + h1, w1 = img.shape[:2] + img = img.copy()[:w1 - w1 % sf, :h1 - h1 % sf, ...] # mod crop + h, w = img.shape[:2] + + if h < lq_patchsize * sf or w < lq_patchsize * sf: + raise ValueError(f'img size ({h1}X{w1}) is too small!') + + if use_sharp: + img = add_sharpening(img) + hq = img.copy() + + if random.random() < shuffle_prob: + shuffle_order = random.sample(range(13), 13) + else: + shuffle_order = list(range(13)) + # local shuffle for noise, JPEG is always the last one + shuffle_order[2:6] = random.sample(shuffle_order[2:6], len(range(2, 6))) + shuffle_order[9:13] = random.sample(shuffle_order[9:13], len(range(9, 13))) + + poisson_prob, speckle_prob, isp_prob = 0.1, 0.1, 0.1 + + for i in shuffle_order: + if i == 0: + img = add_blur(img, sf=sf) + elif i == 1: + img = add_resize(img, sf=sf) + elif i == 2: + img = add_Gaussian_noise(img, noise_level1=2, noise_level2=25) + elif i == 3: + if random.random() < poisson_prob: + img = add_Poisson_noise(img) + elif i == 4: + if random.random() < speckle_prob: + img = add_speckle_noise(img) + elif i == 5: + if random.random() < isp_prob and isp_model is not None: + with torch.no_grad(): + img, hq = isp_model.forward(img.copy(), hq) + elif i == 6: + img = add_JPEG_noise(img) + elif i == 7: + img = add_blur(img, sf=sf) + elif i == 8: + img = add_resize(img, sf=sf) + elif i == 9: + img = add_Gaussian_noise(img, noise_level1=2, noise_level2=25) + elif i == 10: + if random.random() < poisson_prob: + img = add_Poisson_noise(img) + elif i == 11: + if random.random() < speckle_prob: + img = add_speckle_noise(img) + elif i == 12: + if random.random() < isp_prob and isp_model is not None: + with torch.no_grad(): + img, hq = isp_model.forward(img.copy(), hq) + else: + print('check the shuffle!') + + # resize to desired size + img = cv2.resize(img, (int(1 / sf * hq.shape[1]), int(1 / sf * hq.shape[0])), + interpolation=random.choice([1, 2, 3])) + + # add final JPEG compression noise + img = add_JPEG_noise(img) + + # random crop + img, hq = random_crop(img, hq, sf, lq_patchsize) + + return img, hq + + +if __name__ == '__main__': + print("hey") + img = util.imread_uint('utils/test.png', 3) + print(img) + img = util.uint2single(img) + print(img) + img = img[:448, :448] + h = img.shape[0] // 4 + print("resizing to", h) + sf = 4 + deg_fn = partial(degradation_bsrgan_variant, sf=sf) + for i in range(20): + print(i) + img_lq = deg_fn(img) + print(img_lq) + img_lq_bicubic = albumentations.SmallestMaxSize(max_size=h, interpolation=cv2.INTER_CUBIC)(image=img)["image"] + print(img_lq.shape) + print("bicubic", img_lq_bicubic.shape) + print(img_hq.shape) + lq_nearest = cv2.resize(util.single2uint(img_lq), (int(sf * img_lq.shape[1]), int(sf * img_lq.shape[0])), + interpolation=0) + lq_bicubic_nearest = cv2.resize(util.single2uint(img_lq_bicubic), (int(sf * img_lq.shape[1]), int(sf * img_lq.shape[0])), + interpolation=0) + img_concat = np.concatenate([lq_bicubic_nearest, lq_nearest, util.single2uint(img_hq)], axis=1) + util.imsave(img_concat, str(i) + '.png') + + diff --git a/sd/stablediffusion/ldm/modules/image_degradation/bsrgan_light.py b/sd/stablediffusion/ldm/modules/image_degradation/bsrgan_light.py new file mode 100644 index 0000000000000000000000000000000000000000..808c7f882cb75e2ba2340d5b55881d11927351f0 --- /dev/null +++ b/sd/stablediffusion/ldm/modules/image_degradation/bsrgan_light.py @@ -0,0 +1,651 @@ +# -*- coding: utf-8 -*- +import numpy as np +import cv2 +import torch + +from functools import partial +import random +from scipy import ndimage +import scipy +import scipy.stats as ss +from scipy.interpolate import interp2d +from scipy.linalg import orth +import albumentations + +import ldm.modules.image_degradation.utils_image as util + +""" +# -------------------------------------------- +# Super-Resolution +# -------------------------------------------- +# +# Kai Zhang (cskaizhang@gmail.com) +# https://github.com/cszn +# From 2019/03--2021/08 +# -------------------------------------------- +""" + +def modcrop_np(img, sf): + ''' + Args: + img: numpy image, WxH or WxHxC + sf: scale factor + Return: + cropped image + ''' + w, h = img.shape[:2] + im = np.copy(img) + return im[:w - w % sf, :h - h % sf, ...] + + +""" +# -------------------------------------------- +# anisotropic Gaussian kernels +# -------------------------------------------- +""" + + +def analytic_kernel(k): + """Calculate the X4 kernel from the X2 kernel (for proof see appendix in paper)""" + k_size = k.shape[0] + # Calculate the big kernels size + big_k = np.zeros((3 * k_size - 2, 3 * k_size - 2)) + # Loop over the small kernel to fill the big one + for r in range(k_size): + for c in range(k_size): + big_k[2 * r:2 * r + k_size, 2 * c:2 * c + k_size] += k[r, c] * k + # Crop the edges of the big kernel to ignore very small values and increase run time of SR + crop = k_size // 2 + cropped_big_k = big_k[crop:-crop, crop:-crop] + # Normalize to 1 + return cropped_big_k / cropped_big_k.sum() + + +def anisotropic_Gaussian(ksize=15, theta=np.pi, l1=6, l2=6): + """ generate an anisotropic Gaussian kernel + Args: + ksize : e.g., 15, kernel size + theta : [0, pi], rotation angle range + l1 : [0.1,50], scaling of eigenvalues + l2 : [0.1,l1], scaling of eigenvalues + If l1 = l2, will get an isotropic Gaussian kernel. + Returns: + k : kernel + """ + + v = np.dot(np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]), np.array([1., 0.])) + V = np.array([[v[0], v[1]], [v[1], -v[0]]]) + D = np.array([[l1, 0], [0, l2]]) + Sigma = np.dot(np.dot(V, D), np.linalg.inv(V)) + k = gm_blur_kernel(mean=[0, 0], cov=Sigma, size=ksize) + + return k + + +def gm_blur_kernel(mean, cov, size=15): + center = size / 2.0 + 0.5 + k = np.zeros([size, size]) + for y in range(size): + for x in range(size): + cy = y - center + 1 + cx = x - center + 1 + k[y, x] = ss.multivariate_normal.pdf([cx, cy], mean=mean, cov=cov) + + k = k / np.sum(k) + return k + + +def shift_pixel(x, sf, upper_left=True): + """shift pixel for super-resolution with different scale factors + Args: + x: WxHxC or WxH + sf: scale factor + upper_left: shift direction + """ + h, w = x.shape[:2] + shift = (sf - 1) * 0.5 + xv, yv = np.arange(0, w, 1.0), np.arange(0, h, 1.0) + if upper_left: + x1 = xv + shift + y1 = yv + shift + else: + x1 = xv - shift + y1 = yv - shift + + x1 = np.clip(x1, 0, w - 1) + y1 = np.clip(y1, 0, h - 1) + + if x.ndim == 2: + x = interp2d(xv, yv, x)(x1, y1) + if x.ndim == 3: + for i in range(x.shape[-1]): + x[:, :, i] = interp2d(xv, yv, x[:, :, i])(x1, y1) + + return x + + +def blur(x, k): + ''' + x: image, NxcxHxW + k: kernel, Nx1xhxw + ''' + n, c = x.shape[:2] + p1, p2 = (k.shape[-2] - 1) // 2, (k.shape[-1] - 1) // 2 + x = torch.nn.functional.pad(x, pad=(p1, p2, p1, p2), mode='replicate') + k = k.repeat(1, c, 1, 1) + k = k.view(-1, 1, k.shape[2], k.shape[3]) + x = x.view(1, -1, x.shape[2], x.shape[3]) + x = torch.nn.functional.conv2d(x, k, bias=None, stride=1, padding=0, groups=n * c) + x = x.view(n, c, x.shape[2], x.shape[3]) + + return x + + +def gen_kernel(k_size=np.array([15, 15]), scale_factor=np.array([4, 4]), min_var=0.6, max_var=10., noise_level=0): + """" + # modified version of https://github.com/assafshocher/BlindSR_dataset_generator + # Kai Zhang + # min_var = 0.175 * sf # variance of the gaussian kernel will be sampled between min_var and max_var + # max_var = 2.5 * sf + """ + # Set random eigen-vals (lambdas) and angle (theta) for COV matrix + lambda_1 = min_var + np.random.rand() * (max_var - min_var) + lambda_2 = min_var + np.random.rand() * (max_var - min_var) + theta = np.random.rand() * np.pi # random theta + noise = -noise_level + np.random.rand(*k_size) * noise_level * 2 + + # Set COV matrix using Lambdas and Theta + LAMBDA = np.diag([lambda_1, lambda_2]) + Q = np.array([[np.cos(theta), -np.sin(theta)], + [np.sin(theta), np.cos(theta)]]) + SIGMA = Q @ LAMBDA @ Q.T + INV_SIGMA = np.linalg.inv(SIGMA)[None, None, :, :] + + # Set expectation position (shifting kernel for aligned image) + MU = k_size // 2 - 0.5 * (scale_factor - 1) # - 0.5 * (scale_factor - k_size % 2) + MU = MU[None, None, :, None] + + # Create meshgrid for Gaussian + [X, Y] = np.meshgrid(range(k_size[0]), range(k_size[1])) + Z = np.stack([X, Y], 2)[:, :, :, None] + + # Calcualte Gaussian for every pixel of the kernel + ZZ = Z - MU + ZZ_t = ZZ.transpose(0, 1, 3, 2) + raw_kernel = np.exp(-0.5 * np.squeeze(ZZ_t @ INV_SIGMA @ ZZ)) * (1 + noise) + + # shift the kernel so it will be centered + # raw_kernel_centered = kernel_shift(raw_kernel, scale_factor) + + # Normalize the kernel and return + # kernel = raw_kernel_centered / np.sum(raw_kernel_centered) + kernel = raw_kernel / np.sum(raw_kernel) + return kernel + + +def fspecial_gaussian(hsize, sigma): + hsize = [hsize, hsize] + siz = [(hsize[0] - 1.0) / 2.0, (hsize[1] - 1.0) / 2.0] + std = sigma + [x, y] = np.meshgrid(np.arange(-siz[1], siz[1] + 1), np.arange(-siz[0], siz[0] + 1)) + arg = -(x * x + y * y) / (2 * std * std) + h = np.exp(arg) + h[h < scipy.finfo(float).eps * h.max()] = 0 + sumh = h.sum() + if sumh != 0: + h = h / sumh + return h + + +def fspecial_laplacian(alpha): + alpha = max([0, min([alpha, 1])]) + h1 = alpha / (alpha + 1) + h2 = (1 - alpha) / (alpha + 1) + h = [[h1, h2, h1], [h2, -4 / (alpha + 1), h2], [h1, h2, h1]] + h = np.array(h) + return h + + +def fspecial(filter_type, *args, **kwargs): + ''' + python code from: + https://github.com/ronaldosena/imagens-medicas-2/blob/40171a6c259edec7827a6693a93955de2bd39e76/Aulas/aula_2_-_uniform_filter/matlab_fspecial.py + ''' + if filter_type == 'gaussian': + return fspecial_gaussian(*args, **kwargs) + if filter_type == 'laplacian': + return fspecial_laplacian(*args, **kwargs) + + +""" +# -------------------------------------------- +# degradation models +# -------------------------------------------- +""" + + +def bicubic_degradation(x, sf=3): + ''' + Args: + x: HxWxC image, [0, 1] + sf: down-scale factor + Return: + bicubicly downsampled LR image + ''' + x = util.imresize_np(x, scale=1 / sf) + return x + + +def srmd_degradation(x, k, sf=3): + ''' blur + bicubic downsampling + Args: + x: HxWxC image, [0, 1] + k: hxw, double + sf: down-scale factor + Return: + downsampled LR image + Reference: + @inproceedings{zhang2018learning, + title={Learning a single convolutional super-resolution network for multiple degradations}, + author={Zhang, Kai and Zuo, Wangmeng and Zhang, Lei}, + booktitle={IEEE Conference on Computer Vision and Pattern Recognition}, + pages={3262--3271}, + year={2018} + } + ''' + x = ndimage.convolve(x, np.expand_dims(k, axis=2), mode='wrap') # 'nearest' | 'mirror' + x = bicubic_degradation(x, sf=sf) + return x + + +def dpsr_degradation(x, k, sf=3): + ''' bicubic downsampling + blur + Args: + x: HxWxC image, [0, 1] + k: hxw, double + sf: down-scale factor + Return: + downsampled LR image + Reference: + @inproceedings{zhang2019deep, + title={Deep Plug-and-Play Super-Resolution for Arbitrary Blur Kernels}, + author={Zhang, Kai and Zuo, Wangmeng and Zhang, Lei}, + booktitle={IEEE Conference on Computer Vision and Pattern Recognition}, + pages={1671--1681}, + year={2019} + } + ''' + x = bicubic_degradation(x, sf=sf) + x = ndimage.convolve(x, np.expand_dims(k, axis=2), mode='wrap') + return x + + +def classical_degradation(x, k, sf=3): + ''' blur + downsampling + Args: + x: HxWxC image, [0, 1]/[0, 255] + k: hxw, double + sf: down-scale factor + Return: + downsampled LR image + ''' + x = ndimage.convolve(x, np.expand_dims(k, axis=2), mode='wrap') + # x = filters.correlate(x, np.expand_dims(np.flip(k), axis=2)) + st = 0 + return x[st::sf, st::sf, ...] + + +def add_sharpening(img, weight=0.5, radius=50, threshold=10): + """USM sharpening. borrowed from real-ESRGAN + Input image: I; Blurry image: B. + 1. K = I + weight * (I - B) + 2. Mask = 1 if abs(I - B) > threshold, else: 0 + 3. Blur mask: + 4. Out = Mask * K + (1 - Mask) * I + Args: + img (Numpy array): Input image, HWC, BGR; float32, [0, 1]. + weight (float): Sharp weight. Default: 1. + radius (float): Kernel size of Gaussian blur. Default: 50. + threshold (int): + """ + if radius % 2 == 0: + radius += 1 + blur = cv2.GaussianBlur(img, (radius, radius), 0) + residual = img - blur + mask = np.abs(residual) * 255 > threshold + mask = mask.astype('float32') + soft_mask = cv2.GaussianBlur(mask, (radius, radius), 0) + + K = img + weight * residual + K = np.clip(K, 0, 1) + return soft_mask * K + (1 - soft_mask) * img + + +def add_blur(img, sf=4): + wd2 = 4.0 + sf + wd = 2.0 + 0.2 * sf + + wd2 = wd2/4 + wd = wd/4 + + if random.random() < 0.5: + l1 = wd2 * random.random() + l2 = wd2 * random.random() + k = anisotropic_Gaussian(ksize=random.randint(2, 11) + 3, theta=random.random() * np.pi, l1=l1, l2=l2) + else: + k = fspecial('gaussian', random.randint(2, 4) + 3, wd * random.random()) + img = ndimage.convolve(img, np.expand_dims(k, axis=2), mode='mirror') + + return img + + +def add_resize(img, sf=4): + rnum = np.random.rand() + if rnum > 0.8: # up + sf1 = random.uniform(1, 2) + elif rnum < 0.7: # down + sf1 = random.uniform(0.5 / sf, 1) + else: + sf1 = 1.0 + img = cv2.resize(img, (int(sf1 * img.shape[1]), int(sf1 * img.shape[0])), interpolation=random.choice([1, 2, 3])) + img = np.clip(img, 0.0, 1.0) + + return img + + +# def add_Gaussian_noise(img, noise_level1=2, noise_level2=25): +# noise_level = random.randint(noise_level1, noise_level2) +# rnum = np.random.rand() +# if rnum > 0.6: # add color Gaussian noise +# img += np.random.normal(0, noise_level / 255.0, img.shape).astype(np.float32) +# elif rnum < 0.4: # add grayscale Gaussian noise +# img += np.random.normal(0, noise_level / 255.0, (*img.shape[:2], 1)).astype(np.float32) +# else: # add noise +# L = noise_level2 / 255. +# D = np.diag(np.random.rand(3)) +# U = orth(np.random.rand(3, 3)) +# conv = np.dot(np.dot(np.transpose(U), D), U) +# img += np.random.multivariate_normal([0, 0, 0], np.abs(L ** 2 * conv), img.shape[:2]).astype(np.float32) +# img = np.clip(img, 0.0, 1.0) +# return img + +def add_Gaussian_noise(img, noise_level1=2, noise_level2=25): + noise_level = random.randint(noise_level1, noise_level2) + rnum = np.random.rand() + if rnum > 0.6: # add color Gaussian noise + img = img + np.random.normal(0, noise_level / 255.0, img.shape).astype(np.float32) + elif rnum < 0.4: # add grayscale Gaussian noise + img = img + np.random.normal(0, noise_level / 255.0, (*img.shape[:2], 1)).astype(np.float32) + else: # add noise + L = noise_level2 / 255. + D = np.diag(np.random.rand(3)) + U = orth(np.random.rand(3, 3)) + conv = np.dot(np.dot(np.transpose(U), D), U) + img = img + np.random.multivariate_normal([0, 0, 0], np.abs(L ** 2 * conv), img.shape[:2]).astype(np.float32) + img = np.clip(img, 0.0, 1.0) + return img + + +def add_speckle_noise(img, noise_level1=2, noise_level2=25): + noise_level = random.randint(noise_level1, noise_level2) + img = np.clip(img, 0.0, 1.0) + rnum = random.random() + if rnum > 0.6: + img += img * np.random.normal(0, noise_level / 255.0, img.shape).astype(np.float32) + elif rnum < 0.4: + img += img * np.random.normal(0, noise_level / 255.0, (*img.shape[:2], 1)).astype(np.float32) + else: + L = noise_level2 / 255. + D = np.diag(np.random.rand(3)) + U = orth(np.random.rand(3, 3)) + conv = np.dot(np.dot(np.transpose(U), D), U) + img += img * np.random.multivariate_normal([0, 0, 0], np.abs(L ** 2 * conv), img.shape[:2]).astype(np.float32) + img = np.clip(img, 0.0, 1.0) + return img + + +def add_Poisson_noise(img): + img = np.clip((img * 255.0).round(), 0, 255) / 255. + vals = 10 ** (2 * random.random() + 2.0) # [2, 4] + if random.random() < 0.5: + img = np.random.poisson(img * vals).astype(np.float32) / vals + else: + img_gray = np.dot(img[..., :3], [0.299, 0.587, 0.114]) + img_gray = np.clip((img_gray * 255.0).round(), 0, 255) / 255. + noise_gray = np.random.poisson(img_gray * vals).astype(np.float32) / vals - img_gray + img += noise_gray[:, :, np.newaxis] + img = np.clip(img, 0.0, 1.0) + return img + + +def add_JPEG_noise(img): + quality_factor = random.randint(80, 95) + img = cv2.cvtColor(util.single2uint(img), cv2.COLOR_RGB2BGR) + result, encimg = cv2.imencode('.jpg', img, [int(cv2.IMWRITE_JPEG_QUALITY), quality_factor]) + img = cv2.imdecode(encimg, 1) + img = cv2.cvtColor(util.uint2single(img), cv2.COLOR_BGR2RGB) + return img + + +def random_crop(lq, hq, sf=4, lq_patchsize=64): + h, w = lq.shape[:2] + rnd_h = random.randint(0, h - lq_patchsize) + rnd_w = random.randint(0, w - lq_patchsize) + lq = lq[rnd_h:rnd_h + lq_patchsize, rnd_w:rnd_w + lq_patchsize, :] + + rnd_h_H, rnd_w_H = int(rnd_h * sf), int(rnd_w * sf) + hq = hq[rnd_h_H:rnd_h_H + lq_patchsize * sf, rnd_w_H:rnd_w_H + lq_patchsize * sf, :] + return lq, hq + + +def degradation_bsrgan(img, sf=4, lq_patchsize=72, isp_model=None): + """ + This is the degradation model of BSRGAN from the paper + "Designing a Practical Degradation Model for Deep Blind Image Super-Resolution" + ---------- + img: HXWXC, [0, 1], its size should be large than (lq_patchsizexsf)x(lq_patchsizexsf) + sf: scale factor + isp_model: camera ISP model + Returns + ------- + img: low-quality patch, size: lq_patchsizeXlq_patchsizeXC, range: [0, 1] + hq: corresponding high-quality patch, size: (lq_patchsizexsf)X(lq_patchsizexsf)XC, range: [0, 1] + """ + isp_prob, jpeg_prob, scale2_prob = 0.25, 0.9, 0.25 + sf_ori = sf + + h1, w1 = img.shape[:2] + img = img.copy()[:w1 - w1 % sf, :h1 - h1 % sf, ...] # mod crop + h, w = img.shape[:2] + + if h < lq_patchsize * sf or w < lq_patchsize * sf: + raise ValueError(f'img size ({h1}X{w1}) is too small!') + + hq = img.copy() + + if sf == 4 and random.random() < scale2_prob: # downsample1 + if np.random.rand() < 0.5: + img = cv2.resize(img, (int(1 / 2 * img.shape[1]), int(1 / 2 * img.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + img = util.imresize_np(img, 1 / 2, True) + img = np.clip(img, 0.0, 1.0) + sf = 2 + + shuffle_order = random.sample(range(7), 7) + idx1, idx2 = shuffle_order.index(2), shuffle_order.index(3) + if idx1 > idx2: # keep downsample3 last + shuffle_order[idx1], shuffle_order[idx2] = shuffle_order[idx2], shuffle_order[idx1] + + for i in shuffle_order: + + if i == 0: + img = add_blur(img, sf=sf) + + elif i == 1: + img = add_blur(img, sf=sf) + + elif i == 2: + a, b = img.shape[1], img.shape[0] + # downsample2 + if random.random() < 0.75: + sf1 = random.uniform(1, 2 * sf) + img = cv2.resize(img, (int(1 / sf1 * img.shape[1]), int(1 / sf1 * img.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + k = fspecial('gaussian', 25, random.uniform(0.1, 0.6 * sf)) + k_shifted = shift_pixel(k, sf) + k_shifted = k_shifted / k_shifted.sum() # blur with shifted kernel + img = ndimage.convolve(img, np.expand_dims(k_shifted, axis=2), mode='mirror') + img = img[0::sf, 0::sf, ...] # nearest downsampling + img = np.clip(img, 0.0, 1.0) + + elif i == 3: + # downsample3 + img = cv2.resize(img, (int(1 / sf * a), int(1 / sf * b)), interpolation=random.choice([1, 2, 3])) + img = np.clip(img, 0.0, 1.0) + + elif i == 4: + # add Gaussian noise + img = add_Gaussian_noise(img, noise_level1=2, noise_level2=8) + + elif i == 5: + # add JPEG noise + if random.random() < jpeg_prob: + img = add_JPEG_noise(img) + + elif i == 6: + # add processed camera sensor noise + if random.random() < isp_prob and isp_model is not None: + with torch.no_grad(): + img, hq = isp_model.forward(img.copy(), hq) + + # add final JPEG compression noise + img = add_JPEG_noise(img) + + # random crop + img, hq = random_crop(img, hq, sf_ori, lq_patchsize) + + return img, hq + + +# todo no isp_model? +def degradation_bsrgan_variant(image, sf=4, isp_model=None, up=False): + """ + This is the degradation model of BSRGAN from the paper + "Designing a Practical Degradation Model for Deep Blind Image Super-Resolution" + ---------- + sf: scale factor + isp_model: camera ISP model + Returns + ------- + img: low-quality patch, size: lq_patchsizeXlq_patchsizeXC, range: [0, 1] + hq: corresponding high-quality patch, size: (lq_patchsizexsf)X(lq_patchsizexsf)XC, range: [0, 1] + """ + image = util.uint2single(image) + isp_prob, jpeg_prob, scale2_prob = 0.25, 0.9, 0.25 + sf_ori = sf + + h1, w1 = image.shape[:2] + image = image.copy()[:w1 - w1 % sf, :h1 - h1 % sf, ...] # mod crop + h, w = image.shape[:2] + + hq = image.copy() + + if sf == 4 and random.random() < scale2_prob: # downsample1 + if np.random.rand() < 0.5: + image = cv2.resize(image, (int(1 / 2 * image.shape[1]), int(1 / 2 * image.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + image = util.imresize_np(image, 1 / 2, True) + image = np.clip(image, 0.0, 1.0) + sf = 2 + + shuffle_order = random.sample(range(7), 7) + idx1, idx2 = shuffle_order.index(2), shuffle_order.index(3) + if idx1 > idx2: # keep downsample3 last + shuffle_order[idx1], shuffle_order[idx2] = shuffle_order[idx2], shuffle_order[idx1] + + for i in shuffle_order: + + if i == 0: + image = add_blur(image, sf=sf) + + # elif i == 1: + # image = add_blur(image, sf=sf) + + if i == 0: + pass + + elif i == 2: + a, b = image.shape[1], image.shape[0] + # downsample2 + if random.random() < 0.8: + sf1 = random.uniform(1, 2 * sf) + image = cv2.resize(image, (int(1 / sf1 * image.shape[1]), int(1 / sf1 * image.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + k = fspecial('gaussian', 25, random.uniform(0.1, 0.6 * sf)) + k_shifted = shift_pixel(k, sf) + k_shifted = k_shifted / k_shifted.sum() # blur with shifted kernel + image = ndimage.convolve(image, np.expand_dims(k_shifted, axis=2), mode='mirror') + image = image[0::sf, 0::sf, ...] # nearest downsampling + + image = np.clip(image, 0.0, 1.0) + + elif i == 3: + # downsample3 + image = cv2.resize(image, (int(1 / sf * a), int(1 / sf * b)), interpolation=random.choice([1, 2, 3])) + image = np.clip(image, 0.0, 1.0) + + elif i == 4: + # add Gaussian noise + image = add_Gaussian_noise(image, noise_level1=1, noise_level2=2) + + elif i == 5: + # add JPEG noise + if random.random() < jpeg_prob: + image = add_JPEG_noise(image) + # + # elif i == 6: + # # add processed camera sensor noise + # if random.random() < isp_prob and isp_model is not None: + # with torch.no_grad(): + # img, hq = isp_model.forward(img.copy(), hq) + + # add final JPEG compression noise + image = add_JPEG_noise(image) + image = util.single2uint(image) + if up: + image = cv2.resize(image, (w1, h1), interpolation=cv2.INTER_CUBIC) # todo: random, as above? want to condition on it then + example = {"image": image} + return example + + + + +if __name__ == '__main__': + print("hey") + img = util.imread_uint('utils/test.png', 3) + img = img[:448, :448] + h = img.shape[0] // 4 + print("resizing to", h) + sf = 4 + deg_fn = partial(degradation_bsrgan_variant, sf=sf) + for i in range(20): + print(i) + img_hq = img + img_lq = deg_fn(img)["image"] + img_hq, img_lq = util.uint2single(img_hq), util.uint2single(img_lq) + print(img_lq) + img_lq_bicubic = albumentations.SmallestMaxSize(max_size=h, interpolation=cv2.INTER_CUBIC)(image=img_hq)["image"] + print(img_lq.shape) + print("bicubic", img_lq_bicubic.shape) + print(img_hq.shape) + lq_nearest = cv2.resize(util.single2uint(img_lq), (int(sf * img_lq.shape[1]), int(sf * img_lq.shape[0])), + interpolation=0) + lq_bicubic_nearest = cv2.resize(util.single2uint(img_lq_bicubic), + (int(sf * img_lq.shape[1]), int(sf * img_lq.shape[0])), + interpolation=0) + img_concat = np.concatenate([lq_bicubic_nearest, lq_nearest, util.single2uint(img_hq)], axis=1) + util.imsave(img_concat, str(i) + '.png') diff --git a/sd/stablediffusion/ldm/modules/image_degradation/utils/test.png b/sd/stablediffusion/ldm/modules/image_degradation/utils/test.png new file mode 100644 index 0000000000000000000000000000000000000000..4249b43de0f22707758d13c240268a401642f6e6 Binary files /dev/null and b/sd/stablediffusion/ldm/modules/image_degradation/utils/test.png differ diff --git a/sd/stablediffusion/ldm/modules/image_degradation/utils_image.py b/sd/stablediffusion/ldm/modules/image_degradation/utils_image.py new file mode 100644 index 0000000000000000000000000000000000000000..0175f155ad900ae33c3c46ed87f49b352e3faf98 --- /dev/null +++ b/sd/stablediffusion/ldm/modules/image_degradation/utils_image.py @@ -0,0 +1,916 @@ +import os +import math +import random +import numpy as np +import torch +import cv2 +from torchvision.utils import make_grid +from datetime import datetime +#import matplotlib.pyplot as plt # TODO: check with Dominik, also bsrgan.py vs bsrgan_light.py + + +os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE" + + +''' +# -------------------------------------------- +# Kai Zhang (github: https://github.com/cszn) +# 03/Mar/2019 +# -------------------------------------------- +# https://github.com/twhui/SRGAN-pyTorch +# https://github.com/xinntao/BasicSR +# -------------------------------------------- +''' + + +IMG_EXTENSIONS = ['.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP', '.tif'] + + +def is_image_file(filename): + return any(filename.endswith(extension) for extension in IMG_EXTENSIONS) + + +def get_timestamp(): + return datetime.now().strftime('%y%m%d-%H%M%S') + + +def imshow(x, title=None, cbar=False, figsize=None): + plt.figure(figsize=figsize) + plt.imshow(np.squeeze(x), interpolation='nearest', cmap='gray') + if title: + plt.title(title) + if cbar: + plt.colorbar() + plt.show() + + +def surf(Z, cmap='rainbow', figsize=None): + plt.figure(figsize=figsize) + ax3 = plt.axes(projection='3d') + + w, h = Z.shape[:2] + xx = np.arange(0,w,1) + yy = np.arange(0,h,1) + X, Y = np.meshgrid(xx, yy) + ax3.plot_surface(X,Y,Z,cmap=cmap) + #ax3.contour(X,Y,Z, zdim='z',offset=-2,cmap=cmap) + plt.show() + + +''' +# -------------------------------------------- +# get image pathes +# -------------------------------------------- +''' + + +def get_image_paths(dataroot): + paths = None # return None if dataroot is None + if dataroot is not None: + paths = sorted(_get_paths_from_images(dataroot)) + return paths + + +def _get_paths_from_images(path): + assert os.path.isdir(path), '{:s} is not a valid directory'.format(path) + images = [] + for dirpath, _, fnames in sorted(os.walk(path)): + for fname in sorted(fnames): + if is_image_file(fname): + img_path = os.path.join(dirpath, fname) + images.append(img_path) + assert images, '{:s} has no valid image file'.format(path) + return images + + +''' +# -------------------------------------------- +# split large images into small images +# -------------------------------------------- +''' + + +def patches_from_image(img, p_size=512, p_overlap=64, p_max=800): + w, h = img.shape[:2] + patches = [] + if w > p_max and h > p_max: + w1 = list(np.arange(0, w-p_size, p_size-p_overlap, dtype=np.int)) + h1 = list(np.arange(0, h-p_size, p_size-p_overlap, dtype=np.int)) + w1.append(w-p_size) + h1.append(h-p_size) +# print(w1) +# print(h1) + for i in w1: + for j in h1: + patches.append(img[i:i+p_size, j:j+p_size,:]) + else: + patches.append(img) + + return patches + + +def imssave(imgs, img_path): + """ + imgs: list, N images of size WxHxC + """ + img_name, ext = os.path.splitext(os.path.basename(img_path)) + + for i, img in enumerate(imgs): + if img.ndim == 3: + img = img[:, :, [2, 1, 0]] + new_path = os.path.join(os.path.dirname(img_path), img_name+str('_s{:04d}'.format(i))+'.png') + cv2.imwrite(new_path, img) + + +def split_imageset(original_dataroot, taget_dataroot, n_channels=3, p_size=800, p_overlap=96, p_max=1000): + """ + split the large images from original_dataroot into small overlapped images with size (p_size)x(p_size), + and save them into taget_dataroot; only the images with larger size than (p_max)x(p_max) + will be splitted. + Args: + original_dataroot: + taget_dataroot: + p_size: size of small images + p_overlap: patch size in training is a good choice + p_max: images with smaller size than (p_max)x(p_max) keep unchanged. + """ + paths = get_image_paths(original_dataroot) + for img_path in paths: + # img_name, ext = os.path.splitext(os.path.basename(img_path)) + img = imread_uint(img_path, n_channels=n_channels) + patches = patches_from_image(img, p_size, p_overlap, p_max) + imssave(patches, os.path.join(taget_dataroot,os.path.basename(img_path))) + #if original_dataroot == taget_dataroot: + #del img_path + +''' +# -------------------------------------------- +# makedir +# -------------------------------------------- +''' + + +def mkdir(path): + if not os.path.exists(path): + os.makedirs(path) + + +def mkdirs(paths): + if isinstance(paths, str): + mkdir(paths) + else: + for path in paths: + mkdir(path) + + +def mkdir_and_rename(path): + if os.path.exists(path): + new_name = path + '_archived_' + get_timestamp() + print('Path already exists. Rename it to [{:s}]'.format(new_name)) + os.rename(path, new_name) + os.makedirs(path) + + +''' +# -------------------------------------------- +# read image from path +# opencv is fast, but read BGR numpy image +# -------------------------------------------- +''' + + +# -------------------------------------------- +# get uint8 image of size HxWxn_channles (RGB) +# -------------------------------------------- +def imread_uint(path, n_channels=3): + # input: path + # output: HxWx3(RGB or GGG), or HxWx1 (G) + if n_channels == 1: + img = cv2.imread(path, 0) # cv2.IMREAD_GRAYSCALE + img = np.expand_dims(img, axis=2) # HxWx1 + elif n_channels == 3: + img = cv2.imread(path, cv2.IMREAD_UNCHANGED) # BGR or G + if img.ndim == 2: + img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) # GGG + else: + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # RGB + return img + + +# -------------------------------------------- +# matlab's imwrite +# -------------------------------------------- +def imsave(img, img_path): + img = np.squeeze(img) + if img.ndim == 3: + img = img[:, :, [2, 1, 0]] + cv2.imwrite(img_path, img) + +def imwrite(img, img_path): + img = np.squeeze(img) + if img.ndim == 3: + img = img[:, :, [2, 1, 0]] + cv2.imwrite(img_path, img) + + + +# -------------------------------------------- +# get single image of size HxWxn_channles (BGR) +# -------------------------------------------- +def read_img(path): + # read image by cv2 + # return: Numpy float32, HWC, BGR, [0,1] + img = cv2.imread(path, cv2.IMREAD_UNCHANGED) # cv2.IMREAD_GRAYSCALE + img = img.astype(np.float32) / 255. + if img.ndim == 2: + img = np.expand_dims(img, axis=2) + # some images have 4 channels + if img.shape[2] > 3: + img = img[:, :, :3] + return img + + +''' +# -------------------------------------------- +# image format conversion +# -------------------------------------------- +# numpy(single) <---> numpy(unit) +# numpy(single) <---> tensor +# numpy(unit) <---> tensor +# -------------------------------------------- +''' + + +# -------------------------------------------- +# numpy(single) [0, 1] <---> numpy(unit) +# -------------------------------------------- + + +def uint2single(img): + + return np.float32(img/255.) + + +def single2uint(img): + + return np.uint8((img.clip(0, 1)*255.).round()) + + +def uint162single(img): + + return np.float32(img/65535.) + + +def single2uint16(img): + + return np.uint16((img.clip(0, 1)*65535.).round()) + + +# -------------------------------------------- +# numpy(unit) (HxWxC or HxW) <---> tensor +# -------------------------------------------- + + +# convert uint to 4-dimensional torch tensor +def uint2tensor4(img): + if img.ndim == 2: + img = np.expand_dims(img, axis=2) + return torch.from_numpy(np.ascontiguousarray(img)).permute(2, 0, 1).float().div(255.).unsqueeze(0) + + +# convert uint to 3-dimensional torch tensor +def uint2tensor3(img): + if img.ndim == 2: + img = np.expand_dims(img, axis=2) + return torch.from_numpy(np.ascontiguousarray(img)).permute(2, 0, 1).float().div(255.) + + +# convert 2/3/4-dimensional torch tensor to uint +def tensor2uint(img): + img = img.data.squeeze().float().clamp_(0, 1).cpu().numpy() + if img.ndim == 3: + img = np.transpose(img, (1, 2, 0)) + return np.uint8((img*255.0).round()) + + +# -------------------------------------------- +# numpy(single) (HxWxC) <---> tensor +# -------------------------------------------- + + +# convert single (HxWxC) to 3-dimensional torch tensor +def single2tensor3(img): + return torch.from_numpy(np.ascontiguousarray(img)).permute(2, 0, 1).float() + + +# convert single (HxWxC) to 4-dimensional torch tensor +def single2tensor4(img): + return torch.from_numpy(np.ascontiguousarray(img)).permute(2, 0, 1).float().unsqueeze(0) + + +# convert torch tensor to single +def tensor2single(img): + img = img.data.squeeze().float().cpu().numpy() + if img.ndim == 3: + img = np.transpose(img, (1, 2, 0)) + + return img + +# convert torch tensor to single +def tensor2single3(img): + img = img.data.squeeze().float().cpu().numpy() + if img.ndim == 3: + img = np.transpose(img, (1, 2, 0)) + elif img.ndim == 2: + img = np.expand_dims(img, axis=2) + return img + + +def single2tensor5(img): + return torch.from_numpy(np.ascontiguousarray(img)).permute(2, 0, 1, 3).float().unsqueeze(0) + + +def single32tensor5(img): + return torch.from_numpy(np.ascontiguousarray(img)).float().unsqueeze(0).unsqueeze(0) + + +def single42tensor4(img): + return torch.from_numpy(np.ascontiguousarray(img)).permute(2, 0, 1, 3).float() + + +# from skimage.io import imread, imsave +def tensor2img(tensor, out_type=np.uint8, min_max=(0, 1)): + ''' + Converts a torch Tensor into an image Numpy array of BGR channel order + Input: 4D(B,(3/1),H,W), 3D(C,H,W), or 2D(H,W), any range, RGB channel order + Output: 3D(H,W,C) or 2D(H,W), [0,255], np.uint8 (default) + ''' + tensor = tensor.squeeze().float().cpu().clamp_(*min_max) # squeeze first, then clamp + tensor = (tensor - min_max[0]) / (min_max[1] - min_max[0]) # to range [0,1] + n_dim = tensor.dim() + if n_dim == 4: + n_img = len(tensor) + img_np = make_grid(tensor, nrow=int(math.sqrt(n_img)), normalize=False).numpy() + img_np = np.transpose(img_np[[2, 1, 0], :, :], (1, 2, 0)) # HWC, BGR + elif n_dim == 3: + img_np = tensor.numpy() + img_np = np.transpose(img_np[[2, 1, 0], :, :], (1, 2, 0)) # HWC, BGR + elif n_dim == 2: + img_np = tensor.numpy() + else: + raise TypeError( + 'Only support 4D, 3D and 2D tensor. But received with dimension: {:d}'.format(n_dim)) + if out_type == np.uint8: + img_np = (img_np * 255.0).round() + # Important. Unlike matlab, numpy.unit8() WILL NOT round by default. + return img_np.astype(out_type) + + +''' +# -------------------------------------------- +# Augmentation, flipe and/or rotate +# -------------------------------------------- +# The following two are enough. +# (1) augmet_img: numpy image of WxHxC or WxH +# (2) augment_img_tensor4: tensor image 1xCxWxH +# -------------------------------------------- +''' + + +def augment_img(img, mode=0): + '''Kai Zhang (github: https://github.com/cszn) + ''' + if mode == 0: + return img + elif mode == 1: + return np.flipud(np.rot90(img)) + elif mode == 2: + return np.flipud(img) + elif mode == 3: + return np.rot90(img, k=3) + elif mode == 4: + return np.flipud(np.rot90(img, k=2)) + elif mode == 5: + return np.rot90(img) + elif mode == 6: + return np.rot90(img, k=2) + elif mode == 7: + return np.flipud(np.rot90(img, k=3)) + + +def augment_img_tensor4(img, mode=0): + '''Kai Zhang (github: https://github.com/cszn) + ''' + if mode == 0: + return img + elif mode == 1: + return img.rot90(1, [2, 3]).flip([2]) + elif mode == 2: + return img.flip([2]) + elif mode == 3: + return img.rot90(3, [2, 3]) + elif mode == 4: + return img.rot90(2, [2, 3]).flip([2]) + elif mode == 5: + return img.rot90(1, [2, 3]) + elif mode == 6: + return img.rot90(2, [2, 3]) + elif mode == 7: + return img.rot90(3, [2, 3]).flip([2]) + + +def augment_img_tensor(img, mode=0): + '''Kai Zhang (github: https://github.com/cszn) + ''' + img_size = img.size() + img_np = img.data.cpu().numpy() + if len(img_size) == 3: + img_np = np.transpose(img_np, (1, 2, 0)) + elif len(img_size) == 4: + img_np = np.transpose(img_np, (2, 3, 1, 0)) + img_np = augment_img(img_np, mode=mode) + img_tensor = torch.from_numpy(np.ascontiguousarray(img_np)) + if len(img_size) == 3: + img_tensor = img_tensor.permute(2, 0, 1) + elif len(img_size) == 4: + img_tensor = img_tensor.permute(3, 2, 0, 1) + + return img_tensor.type_as(img) + + +def augment_img_np3(img, mode=0): + if mode == 0: + return img + elif mode == 1: + return img.transpose(1, 0, 2) + elif mode == 2: + return img[::-1, :, :] + elif mode == 3: + img = img[::-1, :, :] + img = img.transpose(1, 0, 2) + return img + elif mode == 4: + return img[:, ::-1, :] + elif mode == 5: + img = img[:, ::-1, :] + img = img.transpose(1, 0, 2) + return img + elif mode == 6: + img = img[:, ::-1, :] + img = img[::-1, :, :] + return img + elif mode == 7: + img = img[:, ::-1, :] + img = img[::-1, :, :] + img = img.transpose(1, 0, 2) + return img + + +def augment_imgs(img_list, hflip=True, rot=True): + # horizontal flip OR rotate + hflip = hflip and random.random() < 0.5 + vflip = rot and random.random() < 0.5 + rot90 = rot and random.random() < 0.5 + + def _augment(img): + if hflip: + img = img[:, ::-1, :] + if vflip: + img = img[::-1, :, :] + if rot90: + img = img.transpose(1, 0, 2) + return img + + return [_augment(img) for img in img_list] + + +''' +# -------------------------------------------- +# modcrop and shave +# -------------------------------------------- +''' + + +def modcrop(img_in, scale): + # img_in: Numpy, HWC or HW + img = np.copy(img_in) + if img.ndim == 2: + H, W = img.shape + H_r, W_r = H % scale, W % scale + img = img[:H - H_r, :W - W_r] + elif img.ndim == 3: + H, W, C = img.shape + H_r, W_r = H % scale, W % scale + img = img[:H - H_r, :W - W_r, :] + else: + raise ValueError('Wrong img ndim: [{:d}].'.format(img.ndim)) + return img + + +def shave(img_in, border=0): + # img_in: Numpy, HWC or HW + img = np.copy(img_in) + h, w = img.shape[:2] + img = img[border:h-border, border:w-border] + return img + + +''' +# -------------------------------------------- +# image processing process on numpy image +# channel_convert(in_c, tar_type, img_list): +# rgb2ycbcr(img, only_y=True): +# bgr2ycbcr(img, only_y=True): +# ycbcr2rgb(img): +# -------------------------------------------- +''' + + +def rgb2ycbcr(img, only_y=True): + '''same as matlab rgb2ycbcr + only_y: only return Y channel + Input: + uint8, [0, 255] + float, [0, 1] + ''' + in_img_type = img.dtype + img.astype(np.float32) + if in_img_type != np.uint8: + img *= 255. + # convert + if only_y: + rlt = np.dot(img, [65.481, 128.553, 24.966]) / 255.0 + 16.0 + else: + rlt = np.matmul(img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], + [24.966, 112.0, -18.214]]) / 255.0 + [16, 128, 128] + if in_img_type == np.uint8: + rlt = rlt.round() + else: + rlt /= 255. + return rlt.astype(in_img_type) + + +def ycbcr2rgb(img): + '''same as matlab ycbcr2rgb + Input: + uint8, [0, 255] + float, [0, 1] + ''' + in_img_type = img.dtype + img.astype(np.float32) + if in_img_type != np.uint8: + img *= 255. + # convert + rlt = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], [0, -0.00153632, 0.00791071], + [0.00625893, -0.00318811, 0]]) * 255.0 + [-222.921, 135.576, -276.836] + if in_img_type == np.uint8: + rlt = rlt.round() + else: + rlt /= 255. + return rlt.astype(in_img_type) + + +def bgr2ycbcr(img, only_y=True): + '''bgr version of rgb2ycbcr + only_y: only return Y channel + Input: + uint8, [0, 255] + float, [0, 1] + ''' + in_img_type = img.dtype + img.astype(np.float32) + if in_img_type != np.uint8: + img *= 255. + # convert + if only_y: + rlt = np.dot(img, [24.966, 128.553, 65.481]) / 255.0 + 16.0 + else: + rlt = np.matmul(img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], + [65.481, -37.797, 112.0]]) / 255.0 + [16, 128, 128] + if in_img_type == np.uint8: + rlt = rlt.round() + else: + rlt /= 255. + return rlt.astype(in_img_type) + + +def channel_convert(in_c, tar_type, img_list): + # conversion among BGR, gray and y + if in_c == 3 and tar_type == 'gray': # BGR to gray + gray_list = [cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) for img in img_list] + return [np.expand_dims(img, axis=2) for img in gray_list] + elif in_c == 3 and tar_type == 'y': # BGR to y + y_list = [bgr2ycbcr(img, only_y=True) for img in img_list] + return [np.expand_dims(img, axis=2) for img in y_list] + elif in_c == 1 and tar_type == 'RGB': # gray/y to BGR + return [cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) for img in img_list] + else: + return img_list + + +''' +# -------------------------------------------- +# metric, PSNR and SSIM +# -------------------------------------------- +''' + + +# -------------------------------------------- +# PSNR +# -------------------------------------------- +def calculate_psnr(img1, img2, border=0): + # img1 and img2 have range [0, 255] + #img1 = img1.squeeze() + #img2 = img2.squeeze() + if not img1.shape == img2.shape: + raise ValueError('Input images must have the same dimensions.') + h, w = img1.shape[:2] + img1 = img1[border:h-border, border:w-border] + img2 = img2[border:h-border, border:w-border] + + img1 = img1.astype(np.float64) + img2 = img2.astype(np.float64) + mse = np.mean((img1 - img2)**2) + if mse == 0: + return float('inf') + return 20 * math.log10(255.0 / math.sqrt(mse)) + + +# -------------------------------------------- +# SSIM +# -------------------------------------------- +def calculate_ssim(img1, img2, border=0): + '''calculate SSIM + the same outputs as MATLAB's + img1, img2: [0, 255] + ''' + #img1 = img1.squeeze() + #img2 = img2.squeeze() + if not img1.shape == img2.shape: + raise ValueError('Input images must have the same dimensions.') + h, w = img1.shape[:2] + img1 = img1[border:h-border, border:w-border] + img2 = img2[border:h-border, border:w-border] + + if img1.ndim == 2: + return ssim(img1, img2) + elif img1.ndim == 3: + if img1.shape[2] == 3: + ssims = [] + for i in range(3): + ssims.append(ssim(img1[:,:,i], img2[:,:,i])) + return np.array(ssims).mean() + elif img1.shape[2] == 1: + return ssim(np.squeeze(img1), np.squeeze(img2)) + else: + raise ValueError('Wrong input image dimensions.') + + +def ssim(img1, img2): + C1 = (0.01 * 255)**2 + C2 = (0.03 * 255)**2 + + img1 = img1.astype(np.float64) + img2 = img2.astype(np.float64) + kernel = cv2.getGaussianKernel(11, 1.5) + window = np.outer(kernel, kernel.transpose()) + + mu1 = cv2.filter2D(img1, -1, window)[5:-5, 5:-5] # valid + mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5] + mu1_sq = mu1**2 + mu2_sq = mu2**2 + mu1_mu2 = mu1 * mu2 + sigma1_sq = cv2.filter2D(img1**2, -1, window)[5:-5, 5:-5] - mu1_sq + sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq + sigma12 = cv2.filter2D(img1 * img2, -1, window)[5:-5, 5:-5] - mu1_mu2 + + ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) * + (sigma1_sq + sigma2_sq + C2)) + return ssim_map.mean() + + +''' +# -------------------------------------------- +# matlab's bicubic imresize (numpy and torch) [0, 1] +# -------------------------------------------- +''' + + +# matlab 'imresize' function, now only support 'bicubic' +def cubic(x): + absx = torch.abs(x) + absx2 = absx**2 + absx3 = absx**3 + return (1.5*absx3 - 2.5*absx2 + 1) * ((absx <= 1).type_as(absx)) + \ + (-0.5*absx3 + 2.5*absx2 - 4*absx + 2) * (((absx > 1)*(absx <= 2)).type_as(absx)) + + +def calculate_weights_indices(in_length, out_length, scale, kernel, kernel_width, antialiasing): + if (scale < 1) and (antialiasing): + # Use a modified kernel to simultaneously interpolate and antialias- larger kernel width + kernel_width = kernel_width / scale + + # Output-space coordinates + x = torch.linspace(1, out_length, out_length) + + # Input-space coordinates. Calculate the inverse mapping such that 0.5 + # in output space maps to 0.5 in input space, and 0.5+scale in output + # space maps to 1.5 in input space. + u = x / scale + 0.5 * (1 - 1 / scale) + + # What is the left-most pixel that can be involved in the computation? + left = torch.floor(u - kernel_width / 2) + + # What is the maximum number of pixels that can be involved in the + # computation? Note: it's OK to use an extra pixel here; if the + # corresponding weights are all zero, it will be eliminated at the end + # of this function. + P = math.ceil(kernel_width) + 2 + + # The indices of the input pixels involved in computing the k-th output + # pixel are in row k of the indices matrix. + indices = left.view(out_length, 1).expand(out_length, P) + torch.linspace(0, P - 1, P).view( + 1, P).expand(out_length, P) + + # The weights used to compute the k-th output pixel are in row k of the + # weights matrix. + distance_to_center = u.view(out_length, 1).expand(out_length, P) - indices + # apply cubic kernel + if (scale < 1) and (antialiasing): + weights = scale * cubic(distance_to_center * scale) + else: + weights = cubic(distance_to_center) + # Normalize the weights matrix so that each row sums to 1. + weights_sum = torch.sum(weights, 1).view(out_length, 1) + weights = weights / weights_sum.expand(out_length, P) + + # If a column in weights is all zero, get rid of it. only consider the first and last column. + weights_zero_tmp = torch.sum((weights == 0), 0) + if not math.isclose(weights_zero_tmp[0], 0, rel_tol=1e-6): + indices = indices.narrow(1, 1, P - 2) + weights = weights.narrow(1, 1, P - 2) + if not math.isclose(weights_zero_tmp[-1], 0, rel_tol=1e-6): + indices = indices.narrow(1, 0, P - 2) + weights = weights.narrow(1, 0, P - 2) + weights = weights.contiguous() + indices = indices.contiguous() + sym_len_s = -indices.min() + 1 + sym_len_e = indices.max() - in_length + indices = indices + sym_len_s - 1 + return weights, indices, int(sym_len_s), int(sym_len_e) + + +# -------------------------------------------- +# imresize for tensor image [0, 1] +# -------------------------------------------- +def imresize(img, scale, antialiasing=True): + # Now the scale should be the same for H and W + # input: img: pytorch tensor, CHW or HW [0,1] + # output: CHW or HW [0,1] w/o round + need_squeeze = True if img.dim() == 2 else False + if need_squeeze: + img.unsqueeze_(0) + in_C, in_H, in_W = img.size() + out_C, out_H, out_W = in_C, math.ceil(in_H * scale), math.ceil(in_W * scale) + kernel_width = 4 + kernel = 'cubic' + + # Return the desired dimension order for performing the resize. The + # strategy is to perform the resize first along the dimension with the + # smallest scale factor. + # Now we do not support this. + + # get weights and indices + weights_H, indices_H, sym_len_Hs, sym_len_He = calculate_weights_indices( + in_H, out_H, scale, kernel, kernel_width, antialiasing) + weights_W, indices_W, sym_len_Ws, sym_len_We = calculate_weights_indices( + in_W, out_W, scale, kernel, kernel_width, antialiasing) + # process H dimension + # symmetric copying + img_aug = torch.FloatTensor(in_C, in_H + sym_len_Hs + sym_len_He, in_W) + img_aug.narrow(1, sym_len_Hs, in_H).copy_(img) + + sym_patch = img[:, :sym_len_Hs, :] + inv_idx = torch.arange(sym_patch.size(1) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(1, inv_idx) + img_aug.narrow(1, 0, sym_len_Hs).copy_(sym_patch_inv) + + sym_patch = img[:, -sym_len_He:, :] + inv_idx = torch.arange(sym_patch.size(1) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(1, inv_idx) + img_aug.narrow(1, sym_len_Hs + in_H, sym_len_He).copy_(sym_patch_inv) + + out_1 = torch.FloatTensor(in_C, out_H, in_W) + kernel_width = weights_H.size(1) + for i in range(out_H): + idx = int(indices_H[i][0]) + for j in range(out_C): + out_1[j, i, :] = img_aug[j, idx:idx + kernel_width, :].transpose(0, 1).mv(weights_H[i]) + + # process W dimension + # symmetric copying + out_1_aug = torch.FloatTensor(in_C, out_H, in_W + sym_len_Ws + sym_len_We) + out_1_aug.narrow(2, sym_len_Ws, in_W).copy_(out_1) + + sym_patch = out_1[:, :, :sym_len_Ws] + inv_idx = torch.arange(sym_patch.size(2) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(2, inv_idx) + out_1_aug.narrow(2, 0, sym_len_Ws).copy_(sym_patch_inv) + + sym_patch = out_1[:, :, -sym_len_We:] + inv_idx = torch.arange(sym_patch.size(2) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(2, inv_idx) + out_1_aug.narrow(2, sym_len_Ws + in_W, sym_len_We).copy_(sym_patch_inv) + + out_2 = torch.FloatTensor(in_C, out_H, out_W) + kernel_width = weights_W.size(1) + for i in range(out_W): + idx = int(indices_W[i][0]) + for j in range(out_C): + out_2[j, :, i] = out_1_aug[j, :, idx:idx + kernel_width].mv(weights_W[i]) + if need_squeeze: + out_2.squeeze_() + return out_2 + + +# -------------------------------------------- +# imresize for numpy image [0, 1] +# -------------------------------------------- +def imresize_np(img, scale, antialiasing=True): + # Now the scale should be the same for H and W + # input: img: Numpy, HWC or HW [0,1] + # output: HWC or HW [0,1] w/o round + img = torch.from_numpy(img) + need_squeeze = True if img.dim() == 2 else False + if need_squeeze: + img.unsqueeze_(2) + + in_H, in_W, in_C = img.size() + out_C, out_H, out_W = in_C, math.ceil(in_H * scale), math.ceil(in_W * scale) + kernel_width = 4 + kernel = 'cubic' + + # Return the desired dimension order for performing the resize. The + # strategy is to perform the resize first along the dimension with the + # smallest scale factor. + # Now we do not support this. + + # get weights and indices + weights_H, indices_H, sym_len_Hs, sym_len_He = calculate_weights_indices( + in_H, out_H, scale, kernel, kernel_width, antialiasing) + weights_W, indices_W, sym_len_Ws, sym_len_We = calculate_weights_indices( + in_W, out_W, scale, kernel, kernel_width, antialiasing) + # process H dimension + # symmetric copying + img_aug = torch.FloatTensor(in_H + sym_len_Hs + sym_len_He, in_W, in_C) + img_aug.narrow(0, sym_len_Hs, in_H).copy_(img) + + sym_patch = img[:sym_len_Hs, :, :] + inv_idx = torch.arange(sym_patch.size(0) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(0, inv_idx) + img_aug.narrow(0, 0, sym_len_Hs).copy_(sym_patch_inv) + + sym_patch = img[-sym_len_He:, :, :] + inv_idx = torch.arange(sym_patch.size(0) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(0, inv_idx) + img_aug.narrow(0, sym_len_Hs + in_H, sym_len_He).copy_(sym_patch_inv) + + out_1 = torch.FloatTensor(out_H, in_W, in_C) + kernel_width = weights_H.size(1) + for i in range(out_H): + idx = int(indices_H[i][0]) + for j in range(out_C): + out_1[i, :, j] = img_aug[idx:idx + kernel_width, :, j].transpose(0, 1).mv(weights_H[i]) + + # process W dimension + # symmetric copying + out_1_aug = torch.FloatTensor(out_H, in_W + sym_len_Ws + sym_len_We, in_C) + out_1_aug.narrow(1, sym_len_Ws, in_W).copy_(out_1) + + sym_patch = out_1[:, :sym_len_Ws, :] + inv_idx = torch.arange(sym_patch.size(1) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(1, inv_idx) + out_1_aug.narrow(1, 0, sym_len_Ws).copy_(sym_patch_inv) + + sym_patch = out_1[:, -sym_len_We:, :] + inv_idx = torch.arange(sym_patch.size(1) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(1, inv_idx) + out_1_aug.narrow(1, sym_len_Ws + in_W, sym_len_We).copy_(sym_patch_inv) + + out_2 = torch.FloatTensor(out_H, out_W, in_C) + kernel_width = weights_W.size(1) + for i in range(out_W): + idx = int(indices_W[i][0]) + for j in range(out_C): + out_2[:, i, j] = out_1_aug[:, idx:idx + kernel_width, j].mv(weights_W[i]) + if need_squeeze: + out_2.squeeze_() + + return out_2.numpy() + + +if __name__ == '__main__': + print('---') +# img = imread_uint('test.bmp', 3) +# img = uint2single(img) +# img_bicubic = imresize_np(img, 1/4) \ No newline at end of file diff --git a/sd/stablediffusion/ldm/modules/midas/__init__.py b/sd/stablediffusion/ldm/modules/midas/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/ldm/modules/midas/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/ldm/modules/midas/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00f4ea0979aa52c821adbef652c49d0a4adf3d0e Binary files /dev/null and b/sd/stablediffusion/ldm/modules/midas/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/midas/__pycache__/api.cpython-39.pyc b/sd/stablediffusion/ldm/modules/midas/__pycache__/api.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..226f09734b17c96052be051befcef8a901c37096 Binary files /dev/null and b/sd/stablediffusion/ldm/modules/midas/__pycache__/api.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/midas/api.py b/sd/stablediffusion/ldm/modules/midas/api.py new file mode 100644 index 0000000000000000000000000000000000000000..b58ebbffd942a2fc22264f0ab47e400c26b9f41c --- /dev/null +++ b/sd/stablediffusion/ldm/modules/midas/api.py @@ -0,0 +1,170 @@ +# based on https://github.com/isl-org/MiDaS + +import cv2 +import torch +import torch.nn as nn +from torchvision.transforms import Compose + +from ldm.modules.midas.midas.dpt_depth import DPTDepthModel +from ldm.modules.midas.midas.midas_net import MidasNet +from ldm.modules.midas.midas.midas_net_custom import MidasNet_small +from ldm.modules.midas.midas.transforms import Resize, NormalizeImage, PrepareForNet + + +ISL_PATHS = { + "dpt_large": "midas_models/dpt_large-midas-2f21e586.pt", + "dpt_hybrid": "midas_models/dpt_hybrid-midas-501f0c75.pt", + "midas_v21": "", + "midas_v21_small": "", +} + + +def disabled_train(self, mode=True): + """Overwrite model.train with this function to make sure train/eval mode + does not change anymore.""" + return self + + +def load_midas_transform(model_type): + # https://github.com/isl-org/MiDaS/blob/master/run.py + # load transform only + if model_type == "dpt_large": # DPT-Large + net_w, net_h = 384, 384 + resize_mode = "minimal" + normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + + elif model_type == "dpt_hybrid": # DPT-Hybrid + net_w, net_h = 384, 384 + resize_mode = "minimal" + normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + + elif model_type == "midas_v21": + net_w, net_h = 384, 384 + resize_mode = "upper_bound" + normalization = NormalizeImage(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) + + elif model_type == "midas_v21_small": + net_w, net_h = 256, 256 + resize_mode = "upper_bound" + normalization = NormalizeImage(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) + + else: + assert False, f"model_type '{model_type}' not implemented, use: --model_type large" + + transform = Compose( + [ + Resize( + net_w, + net_h, + resize_target=None, + keep_aspect_ratio=True, + ensure_multiple_of=32, + resize_method=resize_mode, + image_interpolation_method=cv2.INTER_CUBIC, + ), + normalization, + PrepareForNet(), + ] + ) + + return transform + + +def load_model(model_type): + # https://github.com/isl-org/MiDaS/blob/master/run.py + # load network + model_path = ISL_PATHS[model_type] + if model_type == "dpt_large": # DPT-Large + model = DPTDepthModel( + path=model_path, + backbone="vitl16_384", + non_negative=True, + ) + net_w, net_h = 384, 384 + resize_mode = "minimal" + normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + + elif model_type == "dpt_hybrid": # DPT-Hybrid + model = DPTDepthModel( + path=model_path, + backbone="vitb_rn50_384", + non_negative=True, + ) + net_w, net_h = 384, 384 + resize_mode = "minimal" + normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + + elif model_type == "midas_v21": + model = MidasNet(model_path, non_negative=True) + net_w, net_h = 384, 384 + resize_mode = "upper_bound" + normalization = NormalizeImage( + mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] + ) + + elif model_type == "midas_v21_small": + model = MidasNet_small(model_path, features=64, backbone="efficientnet_lite3", exportable=True, + non_negative=True, blocks={'expand': True}) + net_w, net_h = 256, 256 + resize_mode = "upper_bound" + normalization = NormalizeImage( + mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] + ) + + else: + print(f"model_type '{model_type}' not implemented, use: --model_type large") + assert False + + transform = Compose( + [ + Resize( + net_w, + net_h, + resize_target=None, + keep_aspect_ratio=True, + ensure_multiple_of=32, + resize_method=resize_mode, + image_interpolation_method=cv2.INTER_CUBIC, + ), + normalization, + PrepareForNet(), + ] + ) + + return model.eval(), transform + + +class MiDaSInference(nn.Module): + MODEL_TYPES_TORCH_HUB = [ + "DPT_Large", + "DPT_Hybrid", + "MiDaS_small" + ] + MODEL_TYPES_ISL = [ + "dpt_large", + "dpt_hybrid", + "midas_v21", + "midas_v21_small", + ] + + def __init__(self, model_type): + super().__init__() + assert (model_type in self.MODEL_TYPES_ISL) + model, _ = load_model(model_type) + self.model = model + self.model.train = disabled_train + + def forward(self, x): + # x in 0..1 as produced by calling self.transform on a 0..1 float64 numpy array + # NOTE: we expect that the correct transform has been called during dataloading. + with torch.no_grad(): + prediction = self.model(x) + prediction = torch.nn.functional.interpolate( + prediction.unsqueeze(1), + size=x.shape[2:], + mode="bicubic", + align_corners=False, + ) + assert prediction.shape == (x.shape[0], 1, x.shape[2], x.shape[3]) + return prediction + diff --git a/sd/stablediffusion/ldm/modules/midas/midas/__init__.py b/sd/stablediffusion/ldm/modules/midas/midas/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ce02e73b4409a6d6a6d4138b8107bd874dc9ec9 Binary files /dev/null and b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/base_model.cpython-39.pyc b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/base_model.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb8bb9aea6cd56b83788ac391c7487ca48cab700 Binary files /dev/null and b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/base_model.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/blocks.cpython-39.pyc b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/blocks.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f2eda4629424f480e22f300f864b2470fdb962a5 Binary files /dev/null and b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/blocks.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/dpt_depth.cpython-39.pyc b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/dpt_depth.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fbc16d9709db7557dc50ae10219beedd2f28cdc Binary files /dev/null and b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/dpt_depth.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/midas_net.cpython-39.pyc b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/midas_net.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4aabf2d12c078f041d2ef86dabcf73a987928381 Binary files /dev/null and b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/midas_net.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/midas_net_custom.cpython-39.pyc b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/midas_net_custom.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1597b3dc6ef4c66fe332a6ec3b2da0b059940cbd Binary files /dev/null and b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/midas_net_custom.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/transforms.cpython-39.pyc b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/transforms.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db5d9f58c005854e5a15e8b2f1adf27b60aa2a15 Binary files /dev/null and b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/transforms.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/vit.cpython-39.pyc b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/vit.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50d82a083d0604c7cd7cdb1a881b8fe483d3e498 Binary files /dev/null and b/sd/stablediffusion/ldm/modules/midas/midas/__pycache__/vit.cpython-39.pyc differ diff --git a/sd/stablediffusion/ldm/modules/midas/midas/base_model.py b/sd/stablediffusion/ldm/modules/midas/midas/base_model.py new file mode 100644 index 0000000000000000000000000000000000000000..5cf430239b47ec5ec07531263f26f5c24a2311cd --- /dev/null +++ b/sd/stablediffusion/ldm/modules/midas/midas/base_model.py @@ -0,0 +1,16 @@ +import torch + + +class BaseModel(torch.nn.Module): + def load(self, path): + """Load model from file. + + Args: + path (str): file path + """ + parameters = torch.load(path, map_location=torch.device('cpu')) + + if "optimizer" in parameters: + parameters = parameters["model"] + + self.load_state_dict(parameters) diff --git a/sd/stablediffusion/ldm/modules/midas/midas/blocks.py b/sd/stablediffusion/ldm/modules/midas/midas/blocks.py new file mode 100644 index 0000000000000000000000000000000000000000..2145d18fa98060a618536d9a64fe6589e9be4f78 --- /dev/null +++ b/sd/stablediffusion/ldm/modules/midas/midas/blocks.py @@ -0,0 +1,342 @@ +import torch +import torch.nn as nn + +from .vit import ( + _make_pretrained_vitb_rn50_384, + _make_pretrained_vitl16_384, + _make_pretrained_vitb16_384, + forward_vit, +) + +def _make_encoder(backbone, features, use_pretrained, groups=1, expand=False, exportable=True, hooks=None, use_vit_only=False, use_readout="ignore",): + if backbone == "vitl16_384": + pretrained = _make_pretrained_vitl16_384( + use_pretrained, hooks=hooks, use_readout=use_readout + ) + scratch = _make_scratch( + [256, 512, 1024, 1024], features, groups=groups, expand=expand + ) # ViT-L/16 - 85.0% Top1 (backbone) + elif backbone == "vitb_rn50_384": + pretrained = _make_pretrained_vitb_rn50_384( + use_pretrained, + hooks=hooks, + use_vit_only=use_vit_only, + use_readout=use_readout, + ) + scratch = _make_scratch( + [256, 512, 768, 768], features, groups=groups, expand=expand + ) # ViT-H/16 - 85.0% Top1 (backbone) + elif backbone == "vitb16_384": + pretrained = _make_pretrained_vitb16_384( + use_pretrained, hooks=hooks, use_readout=use_readout + ) + scratch = _make_scratch( + [96, 192, 384, 768], features, groups=groups, expand=expand + ) # ViT-B/16 - 84.6% Top1 (backbone) + elif backbone == "resnext101_wsl": + pretrained = _make_pretrained_resnext101_wsl(use_pretrained) + scratch = _make_scratch([256, 512, 1024, 2048], features, groups=groups, expand=expand) # efficientnet_lite3 + elif backbone == "efficientnet_lite3": + pretrained = _make_pretrained_efficientnet_lite3(use_pretrained, exportable=exportable) + scratch = _make_scratch([32, 48, 136, 384], features, groups=groups, expand=expand) # efficientnet_lite3 + else: + print(f"Backbone '{backbone}' not implemented") + assert False + + return pretrained, scratch + + +def _make_scratch(in_shape, out_shape, groups=1, expand=False): + scratch = nn.Module() + + out_shape1 = out_shape + out_shape2 = out_shape + out_shape3 = out_shape + out_shape4 = out_shape + if expand==True: + out_shape1 = out_shape + out_shape2 = out_shape*2 + out_shape3 = out_shape*4 + out_shape4 = out_shape*8 + + scratch.layer1_rn = nn.Conv2d( + in_shape[0], out_shape1, kernel_size=3, stride=1, padding=1, bias=False, groups=groups + ) + scratch.layer2_rn = nn.Conv2d( + in_shape[1], out_shape2, kernel_size=3, stride=1, padding=1, bias=False, groups=groups + ) + scratch.layer3_rn = nn.Conv2d( + in_shape[2], out_shape3, kernel_size=3, stride=1, padding=1, bias=False, groups=groups + ) + scratch.layer4_rn = nn.Conv2d( + in_shape[3], out_shape4, kernel_size=3, stride=1, padding=1, bias=False, groups=groups + ) + + return scratch + + +def _make_pretrained_efficientnet_lite3(use_pretrained, exportable=False): + efficientnet = torch.hub.load( + "rwightman/gen-efficientnet-pytorch", + "tf_efficientnet_lite3", + pretrained=use_pretrained, + exportable=exportable + ) + return _make_efficientnet_backbone(efficientnet) + + +def _make_efficientnet_backbone(effnet): + pretrained = nn.Module() + + pretrained.layer1 = nn.Sequential( + effnet.conv_stem, effnet.bn1, effnet.act1, *effnet.blocks[0:2] + ) + pretrained.layer2 = nn.Sequential(*effnet.blocks[2:3]) + pretrained.layer3 = nn.Sequential(*effnet.blocks[3:5]) + pretrained.layer4 = nn.Sequential(*effnet.blocks[5:9]) + + return pretrained + + +def _make_resnet_backbone(resnet): + pretrained = nn.Module() + pretrained.layer1 = nn.Sequential( + resnet.conv1, resnet.bn1, resnet.relu, resnet.maxpool, resnet.layer1 + ) + + pretrained.layer2 = resnet.layer2 + pretrained.layer3 = resnet.layer3 + pretrained.layer4 = resnet.layer4 + + return pretrained + + +def _make_pretrained_resnext101_wsl(use_pretrained): + resnet = torch.hub.load("facebookresearch/WSL-Images", "resnext101_32x8d_wsl") + return _make_resnet_backbone(resnet) + + + +class Interpolate(nn.Module): + """Interpolation module. + """ + + def __init__(self, scale_factor, mode, align_corners=False): + """Init. + + Args: + scale_factor (float): scaling + mode (str): interpolation mode + """ + super(Interpolate, self).__init__() + + self.interp = nn.functional.interpolate + self.scale_factor = scale_factor + self.mode = mode + self.align_corners = align_corners + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input + + Returns: + tensor: interpolated data + """ + + x = self.interp( + x, scale_factor=self.scale_factor, mode=self.mode, align_corners=self.align_corners + ) + + return x + + +class ResidualConvUnit(nn.Module): + """Residual convolution module. + """ + + def __init__(self, features): + """Init. + + Args: + features (int): number of features + """ + super().__init__() + + self.conv1 = nn.Conv2d( + features, features, kernel_size=3, stride=1, padding=1, bias=True + ) + + self.conv2 = nn.Conv2d( + features, features, kernel_size=3, stride=1, padding=1, bias=True + ) + + self.relu = nn.ReLU(inplace=True) + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input + + Returns: + tensor: output + """ + out = self.relu(x) + out = self.conv1(out) + out = self.relu(out) + out = self.conv2(out) + + return out + x + + +class FeatureFusionBlock(nn.Module): + """Feature fusion block. + """ + + def __init__(self, features): + """Init. + + Args: + features (int): number of features + """ + super(FeatureFusionBlock, self).__init__() + + self.resConfUnit1 = ResidualConvUnit(features) + self.resConfUnit2 = ResidualConvUnit(features) + + def forward(self, *xs): + """Forward pass. + + Returns: + tensor: output + """ + output = xs[0] + + if len(xs) == 2: + output += self.resConfUnit1(xs[1]) + + output = self.resConfUnit2(output) + + output = nn.functional.interpolate( + output, scale_factor=2, mode="bilinear", align_corners=True + ) + + return output + + + + +class ResidualConvUnit_custom(nn.Module): + """Residual convolution module. + """ + + def __init__(self, features, activation, bn): + """Init. + + Args: + features (int): number of features + """ + super().__init__() + + self.bn = bn + + self.groups=1 + + self.conv1 = nn.Conv2d( + features, features, kernel_size=3, stride=1, padding=1, bias=True, groups=self.groups + ) + + self.conv2 = nn.Conv2d( + features, features, kernel_size=3, stride=1, padding=1, bias=True, groups=self.groups + ) + + if self.bn==True: + self.bn1 = nn.BatchNorm2d(features) + self.bn2 = nn.BatchNorm2d(features) + + self.activation = activation + + self.skip_add = nn.quantized.FloatFunctional() + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input + + Returns: + tensor: output + """ + + out = self.activation(x) + out = self.conv1(out) + if self.bn==True: + out = self.bn1(out) + + out = self.activation(out) + out = self.conv2(out) + if self.bn==True: + out = self.bn2(out) + + if self.groups > 1: + out = self.conv_merge(out) + + return self.skip_add.add(out, x) + + # return out + x + + +class FeatureFusionBlock_custom(nn.Module): + """Feature fusion block. + """ + + def __init__(self, features, activation, deconv=False, bn=False, expand=False, align_corners=True): + """Init. + + Args: + features (int): number of features + """ + super(FeatureFusionBlock_custom, self).__init__() + + self.deconv = deconv + self.align_corners = align_corners + + self.groups=1 + + self.expand = expand + out_features = features + if self.expand==True: + out_features = features//2 + + self.out_conv = nn.Conv2d(features, out_features, kernel_size=1, stride=1, padding=0, bias=True, groups=1) + + self.resConfUnit1 = ResidualConvUnit_custom(features, activation, bn) + self.resConfUnit2 = ResidualConvUnit_custom(features, activation, bn) + + self.skip_add = nn.quantized.FloatFunctional() + + def forward(self, *xs): + """Forward pass. + + Returns: + tensor: output + """ + output = xs[0] + + if len(xs) == 2: + res = self.resConfUnit1(xs[1]) + output = self.skip_add.add(output, res) + # output += res + + output = self.resConfUnit2(output) + + output = nn.functional.interpolate( + output, scale_factor=2, mode="bilinear", align_corners=self.align_corners + ) + + output = self.out_conv(output) + + return output + diff --git a/sd/stablediffusion/ldm/modules/midas/midas/dpt_depth.py b/sd/stablediffusion/ldm/modules/midas/midas/dpt_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..4e9aab5d2767dffea39da5b3f30e2798688216f1 --- /dev/null +++ b/sd/stablediffusion/ldm/modules/midas/midas/dpt_depth.py @@ -0,0 +1,109 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +from .base_model import BaseModel +from .blocks import ( + FeatureFusionBlock, + FeatureFusionBlock_custom, + Interpolate, + _make_encoder, + forward_vit, +) + + +def _make_fusion_block(features, use_bn): + return FeatureFusionBlock_custom( + features, + nn.ReLU(False), + deconv=False, + bn=use_bn, + expand=False, + align_corners=True, + ) + + +class DPT(BaseModel): + def __init__( + self, + head, + features=256, + backbone="vitb_rn50_384", + readout="project", + channels_last=False, + use_bn=False, + ): + + super(DPT, self).__init__() + + self.channels_last = channels_last + + hooks = { + "vitb_rn50_384": [0, 1, 8, 11], + "vitb16_384": [2, 5, 8, 11], + "vitl16_384": [5, 11, 17, 23], + } + + # Instantiate backbone and reassemble blocks + self.pretrained, self.scratch = _make_encoder( + backbone, + features, + False, # Set to true of you want to train from scratch, uses ImageNet weights + groups=1, + expand=False, + exportable=False, + hooks=hooks[backbone], + use_readout=readout, + ) + + self.scratch.refinenet1 = _make_fusion_block(features, use_bn) + self.scratch.refinenet2 = _make_fusion_block(features, use_bn) + self.scratch.refinenet3 = _make_fusion_block(features, use_bn) + self.scratch.refinenet4 = _make_fusion_block(features, use_bn) + + self.scratch.output_conv = head + + + def forward(self, x): + if self.channels_last == True: + x.contiguous(memory_format=torch.channels_last) + + layer_1, layer_2, layer_3, layer_4 = forward_vit(self.pretrained, x) + + layer_1_rn = self.scratch.layer1_rn(layer_1) + layer_2_rn = self.scratch.layer2_rn(layer_2) + layer_3_rn = self.scratch.layer3_rn(layer_3) + layer_4_rn = self.scratch.layer4_rn(layer_4) + + path_4 = self.scratch.refinenet4(layer_4_rn) + path_3 = self.scratch.refinenet3(path_4, layer_3_rn) + path_2 = self.scratch.refinenet2(path_3, layer_2_rn) + path_1 = self.scratch.refinenet1(path_2, layer_1_rn) + + out = self.scratch.output_conv(path_1) + + return out + + +class DPTDepthModel(DPT): + def __init__(self, path=None, non_negative=True, **kwargs): + features = kwargs["features"] if "features" in kwargs else 256 + + head = nn.Sequential( + nn.Conv2d(features, features // 2, kernel_size=3, stride=1, padding=1), + Interpolate(scale_factor=2, mode="bilinear", align_corners=True), + nn.Conv2d(features // 2, 32, kernel_size=3, stride=1, padding=1), + nn.ReLU(True), + nn.Conv2d(32, 1, kernel_size=1, stride=1, padding=0), + nn.ReLU(True) if non_negative else nn.Identity(), + nn.Identity(), + ) + + super().__init__(head, **kwargs) + + if path is not None: + self.load(path) + + def forward(self, x): + return super().forward(x).squeeze(dim=1) + diff --git a/sd/stablediffusion/ldm/modules/midas/midas/midas_net.py b/sd/stablediffusion/ldm/modules/midas/midas/midas_net.py new file mode 100644 index 0000000000000000000000000000000000000000..8a954977800b0a0f48807e80fa63041910e33c1f --- /dev/null +++ b/sd/stablediffusion/ldm/modules/midas/midas/midas_net.py @@ -0,0 +1,76 @@ +"""MidashNet: Network for monocular depth estimation trained by mixing several datasets. +This file contains code that is adapted from +https://github.com/thomasjpfan/pytorch_refinenet/blob/master/pytorch_refinenet/refinenet/refinenet_4cascade.py +""" +import torch +import torch.nn as nn + +from .base_model import BaseModel +from .blocks import FeatureFusionBlock, Interpolate, _make_encoder + + +class MidasNet(BaseModel): + """Network for monocular depth estimation. + """ + + def __init__(self, path=None, features=256, non_negative=True): + """Init. + + Args: + path (str, optional): Path to saved model. Defaults to None. + features (int, optional): Number of features. Defaults to 256. + backbone (str, optional): Backbone network for encoder. Defaults to resnet50 + """ + print("Loading weights: ", path) + + super(MidasNet, self).__init__() + + use_pretrained = False if path is None else True + + self.pretrained, self.scratch = _make_encoder(backbone="resnext101_wsl", features=features, use_pretrained=use_pretrained) + + self.scratch.refinenet4 = FeatureFusionBlock(features) + self.scratch.refinenet3 = FeatureFusionBlock(features) + self.scratch.refinenet2 = FeatureFusionBlock(features) + self.scratch.refinenet1 = FeatureFusionBlock(features) + + self.scratch.output_conv = nn.Sequential( + nn.Conv2d(features, 128, kernel_size=3, stride=1, padding=1), + Interpolate(scale_factor=2, mode="bilinear"), + nn.Conv2d(128, 32, kernel_size=3, stride=1, padding=1), + nn.ReLU(True), + nn.Conv2d(32, 1, kernel_size=1, stride=1, padding=0), + nn.ReLU(True) if non_negative else nn.Identity(), + ) + + if path: + self.load(path) + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input data (image) + + Returns: + tensor: depth + """ + + layer_1 = self.pretrained.layer1(x) + layer_2 = self.pretrained.layer2(layer_1) + layer_3 = self.pretrained.layer3(layer_2) + layer_4 = self.pretrained.layer4(layer_3) + + layer_1_rn = self.scratch.layer1_rn(layer_1) + layer_2_rn = self.scratch.layer2_rn(layer_2) + layer_3_rn = self.scratch.layer3_rn(layer_3) + layer_4_rn = self.scratch.layer4_rn(layer_4) + + path_4 = self.scratch.refinenet4(layer_4_rn) + path_3 = self.scratch.refinenet3(path_4, layer_3_rn) + path_2 = self.scratch.refinenet2(path_3, layer_2_rn) + path_1 = self.scratch.refinenet1(path_2, layer_1_rn) + + out = self.scratch.output_conv(path_1) + + return torch.squeeze(out, dim=1) diff --git a/sd/stablediffusion/ldm/modules/midas/midas/midas_net_custom.py b/sd/stablediffusion/ldm/modules/midas/midas/midas_net_custom.py new file mode 100644 index 0000000000000000000000000000000000000000..50e4acb5e53d5fabefe3dde16ab49c33c2b7797c --- /dev/null +++ b/sd/stablediffusion/ldm/modules/midas/midas/midas_net_custom.py @@ -0,0 +1,128 @@ +"""MidashNet: Network for monocular depth estimation trained by mixing several datasets. +This file contains code that is adapted from +https://github.com/thomasjpfan/pytorch_refinenet/blob/master/pytorch_refinenet/refinenet/refinenet_4cascade.py +""" +import torch +import torch.nn as nn + +from .base_model import BaseModel +from .blocks import FeatureFusionBlock, FeatureFusionBlock_custom, Interpolate, _make_encoder + + +class MidasNet_small(BaseModel): + """Network for monocular depth estimation. + """ + + def __init__(self, path=None, features=64, backbone="efficientnet_lite3", non_negative=True, exportable=True, channels_last=False, align_corners=True, + blocks={'expand': True}): + """Init. + + Args: + path (str, optional): Path to saved model. Defaults to None. + features (int, optional): Number of features. Defaults to 256. + backbone (str, optional): Backbone network for encoder. Defaults to resnet50 + """ + print("Loading weights: ", path) + + super(MidasNet_small, self).__init__() + + use_pretrained = False if path else True + + self.channels_last = channels_last + self.blocks = blocks + self.backbone = backbone + + self.groups = 1 + + features1=features + features2=features + features3=features + features4=features + self.expand = False + if "expand" in self.blocks and self.blocks['expand'] == True: + self.expand = True + features1=features + features2=features*2 + features3=features*4 + features4=features*8 + + self.pretrained, self.scratch = _make_encoder(self.backbone, features, use_pretrained, groups=self.groups, expand=self.expand, exportable=exportable) + + self.scratch.activation = nn.ReLU(False) + + self.scratch.refinenet4 = FeatureFusionBlock_custom(features4, self.scratch.activation, deconv=False, bn=False, expand=self.expand, align_corners=align_corners) + self.scratch.refinenet3 = FeatureFusionBlock_custom(features3, self.scratch.activation, deconv=False, bn=False, expand=self.expand, align_corners=align_corners) + self.scratch.refinenet2 = FeatureFusionBlock_custom(features2, self.scratch.activation, deconv=False, bn=False, expand=self.expand, align_corners=align_corners) + self.scratch.refinenet1 = FeatureFusionBlock_custom(features1, self.scratch.activation, deconv=False, bn=False, align_corners=align_corners) + + + self.scratch.output_conv = nn.Sequential( + nn.Conv2d(features, features//2, kernel_size=3, stride=1, padding=1, groups=self.groups), + Interpolate(scale_factor=2, mode="bilinear"), + nn.Conv2d(features//2, 32, kernel_size=3, stride=1, padding=1), + self.scratch.activation, + nn.Conv2d(32, 1, kernel_size=1, stride=1, padding=0), + nn.ReLU(True) if non_negative else nn.Identity(), + nn.Identity(), + ) + + if path: + self.load(path) + + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input data (image) + + Returns: + tensor: depth + """ + if self.channels_last==True: + print("self.channels_last = ", self.channels_last) + x.contiguous(memory_format=torch.channels_last) + + + layer_1 = self.pretrained.layer1(x) + layer_2 = self.pretrained.layer2(layer_1) + layer_3 = self.pretrained.layer3(layer_2) + layer_4 = self.pretrained.layer4(layer_3) + + layer_1_rn = self.scratch.layer1_rn(layer_1) + layer_2_rn = self.scratch.layer2_rn(layer_2) + layer_3_rn = self.scratch.layer3_rn(layer_3) + layer_4_rn = self.scratch.layer4_rn(layer_4) + + + path_4 = self.scratch.refinenet4(layer_4_rn) + path_3 = self.scratch.refinenet3(path_4, layer_3_rn) + path_2 = self.scratch.refinenet2(path_3, layer_2_rn) + path_1 = self.scratch.refinenet1(path_2, layer_1_rn) + + out = self.scratch.output_conv(path_1) + + return torch.squeeze(out, dim=1) + + + +def fuse_model(m): + prev_previous_type = nn.Identity() + prev_previous_name = '' + previous_type = nn.Identity() + previous_name = '' + for name, module in m.named_modules(): + if prev_previous_type == nn.Conv2d and previous_type == nn.BatchNorm2d and type(module) == nn.ReLU: + # print("FUSED ", prev_previous_name, previous_name, name) + torch.quantization.fuse_modules(m, [prev_previous_name, previous_name, name], inplace=True) + elif prev_previous_type == nn.Conv2d and previous_type == nn.BatchNorm2d: + # print("FUSED ", prev_previous_name, previous_name) + torch.quantization.fuse_modules(m, [prev_previous_name, previous_name], inplace=True) + # elif previous_type == nn.Conv2d and type(module) == nn.ReLU: + # print("FUSED ", previous_name, name) + # torch.quantization.fuse_modules(m, [previous_name, name], inplace=True) + + prev_previous_type = previous_type + prev_previous_name = previous_name + previous_type = type(module) + previous_name = name \ No newline at end of file diff --git a/sd/stablediffusion/ldm/modules/midas/midas/transforms.py b/sd/stablediffusion/ldm/modules/midas/midas/transforms.py new file mode 100644 index 0000000000000000000000000000000000000000..350cbc11662633ad7f8968eb10be2e7de6e384e9 --- /dev/null +++ b/sd/stablediffusion/ldm/modules/midas/midas/transforms.py @@ -0,0 +1,234 @@ +import numpy as np +import cv2 +import math + + +def apply_min_size(sample, size, image_interpolation_method=cv2.INTER_AREA): + """Rezise the sample to ensure the given size. Keeps aspect ratio. + + Args: + sample (dict): sample + size (tuple): image size + + Returns: + tuple: new size + """ + shape = list(sample["disparity"].shape) + + if shape[0] >= size[0] and shape[1] >= size[1]: + return sample + + scale = [0, 0] + scale[0] = size[0] / shape[0] + scale[1] = size[1] / shape[1] + + scale = max(scale) + + shape[0] = math.ceil(scale * shape[0]) + shape[1] = math.ceil(scale * shape[1]) + + # resize + sample["image"] = cv2.resize( + sample["image"], tuple(shape[::-1]), interpolation=image_interpolation_method + ) + + sample["disparity"] = cv2.resize( + sample["disparity"], tuple(shape[::-1]), interpolation=cv2.INTER_NEAREST + ) + sample["mask"] = cv2.resize( + sample["mask"].astype(np.float32), + tuple(shape[::-1]), + interpolation=cv2.INTER_NEAREST, + ) + sample["mask"] = sample["mask"].astype(bool) + + return tuple(shape) + + +class Resize(object): + """Resize sample to given size (width, height). + """ + + def __init__( + self, + width, + height, + resize_target=True, + keep_aspect_ratio=False, + ensure_multiple_of=1, + resize_method="lower_bound", + image_interpolation_method=cv2.INTER_AREA, + ): + """Init. + + Args: + width (int): desired output width + height (int): desired output height + resize_target (bool, optional): + True: Resize the full sample (image, mask, target). + False: Resize image only. + Defaults to True. + keep_aspect_ratio (bool, optional): + True: Keep the aspect ratio of the input sample. + Output sample might not have the given width and height, and + resize behaviour depends on the parameter 'resize_method'. + Defaults to False. + ensure_multiple_of (int, optional): + Output width and height is constrained to be multiple of this parameter. + Defaults to 1. + resize_method (str, optional): + "lower_bound": Output will be at least as large as the given size. + "upper_bound": Output will be at max as large as the given size. (Output size might be smaller than given size.) + "minimal": Scale as least as possible. (Output size might be smaller than given size.) + Defaults to "lower_bound". + """ + self.__width = width + self.__height = height + + self.__resize_target = resize_target + self.__keep_aspect_ratio = keep_aspect_ratio + self.__multiple_of = ensure_multiple_of + self.__resize_method = resize_method + self.__image_interpolation_method = image_interpolation_method + + def constrain_to_multiple_of(self, x, min_val=0, max_val=None): + y = (np.round(x / self.__multiple_of) * self.__multiple_of).astype(int) + + if max_val is not None and y > max_val: + y = (np.floor(x / self.__multiple_of) * self.__multiple_of).astype(int) + + if y < min_val: + y = (np.ceil(x / self.__multiple_of) * self.__multiple_of).astype(int) + + return y + + def get_size(self, width, height): + # determine new height and width + scale_height = self.__height / height + scale_width = self.__width / width + + if self.__keep_aspect_ratio: + if self.__resize_method == "lower_bound": + # scale such that output size is lower bound + if scale_width > scale_height: + # fit width + scale_height = scale_width + else: + # fit height + scale_width = scale_height + elif self.__resize_method == "upper_bound": + # scale such that output size is upper bound + if scale_width < scale_height: + # fit width + scale_height = scale_width + else: + # fit height + scale_width = scale_height + elif self.__resize_method == "minimal": + # scale as least as possbile + if abs(1 - scale_width) < abs(1 - scale_height): + # fit width + scale_height = scale_width + else: + # fit height + scale_width = scale_height + else: + raise ValueError( + f"resize_method {self.__resize_method} not implemented" + ) + + if self.__resize_method == "lower_bound": + new_height = self.constrain_to_multiple_of( + scale_height * height, min_val=self.__height + ) + new_width = self.constrain_to_multiple_of( + scale_width * width, min_val=self.__width + ) + elif self.__resize_method == "upper_bound": + new_height = self.constrain_to_multiple_of( + scale_height * height, max_val=self.__height + ) + new_width = self.constrain_to_multiple_of( + scale_width * width, max_val=self.__width + ) + elif self.__resize_method == "minimal": + new_height = self.constrain_to_multiple_of(scale_height * height) + new_width = self.constrain_to_multiple_of(scale_width * width) + else: + raise ValueError(f"resize_method {self.__resize_method} not implemented") + + return (new_width, new_height) + + def __call__(self, sample): + width, height = self.get_size( + sample["image"].shape[1], sample["image"].shape[0] + ) + + # resize sample + sample["image"] = cv2.resize( + sample["image"], + (width, height), + interpolation=self.__image_interpolation_method, + ) + + if self.__resize_target: + if "disparity" in sample: + sample["disparity"] = cv2.resize( + sample["disparity"], + (width, height), + interpolation=cv2.INTER_NEAREST, + ) + + if "depth" in sample: + sample["depth"] = cv2.resize( + sample["depth"], (width, height), interpolation=cv2.INTER_NEAREST + ) + + sample["mask"] = cv2.resize( + sample["mask"].astype(np.float32), + (width, height), + interpolation=cv2.INTER_NEAREST, + ) + sample["mask"] = sample["mask"].astype(bool) + + return sample + + +class NormalizeImage(object): + """Normlize image by given mean and std. + """ + + def __init__(self, mean, std): + self.__mean = mean + self.__std = std + + def __call__(self, sample): + sample["image"] = (sample["image"] - self.__mean) / self.__std + + return sample + + +class PrepareForNet(object): + """Prepare sample for usage as network input. + """ + + def __init__(self): + pass + + def __call__(self, sample): + image = np.transpose(sample["image"], (2, 0, 1)) + sample["image"] = np.ascontiguousarray(image).astype(np.float32) + + if "mask" in sample: + sample["mask"] = sample["mask"].astype(np.float32) + sample["mask"] = np.ascontiguousarray(sample["mask"]) + + if "disparity" in sample: + disparity = sample["disparity"].astype(np.float32) + sample["disparity"] = np.ascontiguousarray(disparity) + + if "depth" in sample: + depth = sample["depth"].astype(np.float32) + sample["depth"] = np.ascontiguousarray(depth) + + return sample diff --git a/sd/stablediffusion/ldm/modules/midas/midas/vit.py b/sd/stablediffusion/ldm/modules/midas/midas/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..ea46b1be88b261b0dec04f3da0256f5f66f88a74 --- /dev/null +++ b/sd/stablediffusion/ldm/modules/midas/midas/vit.py @@ -0,0 +1,491 @@ +import torch +import torch.nn as nn +import timm +import types +import math +import torch.nn.functional as F + + +class Slice(nn.Module): + def __init__(self, start_index=1): + super(Slice, self).__init__() + self.start_index = start_index + + def forward(self, x): + return x[:, self.start_index :] + + +class AddReadout(nn.Module): + def __init__(self, start_index=1): + super(AddReadout, self).__init__() + self.start_index = start_index + + def forward(self, x): + if self.start_index == 2: + readout = (x[:, 0] + x[:, 1]) / 2 + else: + readout = x[:, 0] + return x[:, self.start_index :] + readout.unsqueeze(1) + + +class ProjectReadout(nn.Module): + def __init__(self, in_features, start_index=1): + super(ProjectReadout, self).__init__() + self.start_index = start_index + + self.project = nn.Sequential(nn.Linear(2 * in_features, in_features), nn.GELU()) + + def forward(self, x): + readout = x[:, 0].unsqueeze(1).expand_as(x[:, self.start_index :]) + features = torch.cat((x[:, self.start_index :], readout), -1) + + return self.project(features) + + +class Transpose(nn.Module): + def __init__(self, dim0, dim1): + super(Transpose, self).__init__() + self.dim0 = dim0 + self.dim1 = dim1 + + def forward(self, x): + x = x.transpose(self.dim0, self.dim1) + return x + + +def forward_vit(pretrained, x): + b, c, h, w = x.shape + + glob = pretrained.model.forward_flex(x) + + layer_1 = pretrained.activations["1"] + layer_2 = pretrained.activations["2"] + layer_3 = pretrained.activations["3"] + layer_4 = pretrained.activations["4"] + + layer_1 = pretrained.act_postprocess1[0:2](layer_1) + layer_2 = pretrained.act_postprocess2[0:2](layer_2) + layer_3 = pretrained.act_postprocess3[0:2](layer_3) + layer_4 = pretrained.act_postprocess4[0:2](layer_4) + + unflatten = nn.Sequential( + nn.Unflatten( + 2, + torch.Size( + [ + h // pretrained.model.patch_size[1], + w // pretrained.model.patch_size[0], + ] + ), + ) + ) + + if layer_1.ndim == 3: + layer_1 = unflatten(layer_1) + if layer_2.ndim == 3: + layer_2 = unflatten(layer_2) + if layer_3.ndim == 3: + layer_3 = unflatten(layer_3) + if layer_4.ndim == 3: + layer_4 = unflatten(layer_4) + + layer_1 = pretrained.act_postprocess1[3 : len(pretrained.act_postprocess1)](layer_1) + layer_2 = pretrained.act_postprocess2[3 : len(pretrained.act_postprocess2)](layer_2) + layer_3 = pretrained.act_postprocess3[3 : len(pretrained.act_postprocess3)](layer_3) + layer_4 = pretrained.act_postprocess4[3 : len(pretrained.act_postprocess4)](layer_4) + + return layer_1, layer_2, layer_3, layer_4 + + +def _resize_pos_embed(self, posemb, gs_h, gs_w): + posemb_tok, posemb_grid = ( + posemb[:, : self.start_index], + posemb[0, self.start_index :], + ) + + gs_old = int(math.sqrt(len(posemb_grid))) + + posemb_grid = posemb_grid.reshape(1, gs_old, gs_old, -1).permute(0, 3, 1, 2) + posemb_grid = F.interpolate(posemb_grid, size=(gs_h, gs_w), mode="bilinear") + posemb_grid = posemb_grid.permute(0, 2, 3, 1).reshape(1, gs_h * gs_w, -1) + + posemb = torch.cat([posemb_tok, posemb_grid], dim=1) + + return posemb + + +def forward_flex(self, x): + b, c, h, w = x.shape + + pos_embed = self._resize_pos_embed( + self.pos_embed, h // self.patch_size[1], w // self.patch_size[0] + ) + + B = x.shape[0] + + if hasattr(self.patch_embed, "backbone"): + x = self.patch_embed.backbone(x) + if isinstance(x, (list, tuple)): + x = x[-1] # last feature if backbone outputs list/tuple of features + + x = self.patch_embed.proj(x).flatten(2).transpose(1, 2) + + if getattr(self, "dist_token", None) is not None: + cls_tokens = self.cls_token.expand( + B, -1, -1 + ) # stole cls_tokens impl from Phil Wang, thanks + dist_token = self.dist_token.expand(B, -1, -1) + x = torch.cat((cls_tokens, dist_token, x), dim=1) + else: + cls_tokens = self.cls_token.expand( + B, -1, -1 + ) # stole cls_tokens impl from Phil Wang, thanks + x = torch.cat((cls_tokens, x), dim=1) + + x = x + pos_embed + x = self.pos_drop(x) + + for blk in self.blocks: + x = blk(x) + + x = self.norm(x) + + return x + + +activations = {} + + +def get_activation(name): + def hook(model, input, output): + activations[name] = output + + return hook + + +def get_readout_oper(vit_features, features, use_readout, start_index=1): + if use_readout == "ignore": + readout_oper = [Slice(start_index)] * len(features) + elif use_readout == "add": + readout_oper = [AddReadout(start_index)] * len(features) + elif use_readout == "project": + readout_oper = [ + ProjectReadout(vit_features, start_index) for out_feat in features + ] + else: + assert ( + False + ), "wrong operation for readout token, use_readout can be 'ignore', 'add', or 'project'" + + return readout_oper + + +def _make_vit_b16_backbone( + model, + features=[96, 192, 384, 768], + size=[384, 384], + hooks=[2, 5, 8, 11], + vit_features=768, + use_readout="ignore", + start_index=1, +): + pretrained = nn.Module() + + pretrained.model = model + pretrained.model.blocks[hooks[0]].register_forward_hook(get_activation("1")) + pretrained.model.blocks[hooks[1]].register_forward_hook(get_activation("2")) + pretrained.model.blocks[hooks[2]].register_forward_hook(get_activation("3")) + pretrained.model.blocks[hooks[3]].register_forward_hook(get_activation("4")) + + pretrained.activations = activations + + readout_oper = get_readout_oper(vit_features, features, use_readout, start_index) + + # 32, 48, 136, 384 + pretrained.act_postprocess1 = nn.Sequential( + readout_oper[0], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[0], + kernel_size=1, + stride=1, + padding=0, + ), + nn.ConvTranspose2d( + in_channels=features[0], + out_channels=features[0], + kernel_size=4, + stride=4, + padding=0, + bias=True, + dilation=1, + groups=1, + ), + ) + + pretrained.act_postprocess2 = nn.Sequential( + readout_oper[1], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[1], + kernel_size=1, + stride=1, + padding=0, + ), + nn.ConvTranspose2d( + in_channels=features[1], + out_channels=features[1], + kernel_size=2, + stride=2, + padding=0, + bias=True, + dilation=1, + groups=1, + ), + ) + + pretrained.act_postprocess3 = nn.Sequential( + readout_oper[2], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[2], + kernel_size=1, + stride=1, + padding=0, + ), + ) + + pretrained.act_postprocess4 = nn.Sequential( + readout_oper[3], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[3], + kernel_size=1, + stride=1, + padding=0, + ), + nn.Conv2d( + in_channels=features[3], + out_channels=features[3], + kernel_size=3, + stride=2, + padding=1, + ), + ) + + pretrained.model.start_index = start_index + pretrained.model.patch_size = [16, 16] + + # We inject this function into the VisionTransformer instances so that + # we can use it with interpolated position embeddings without modifying the library source. + pretrained.model.forward_flex = types.MethodType(forward_flex, pretrained.model) + pretrained.model._resize_pos_embed = types.MethodType( + _resize_pos_embed, pretrained.model + ) + + return pretrained + + +def _make_pretrained_vitl16_384(pretrained, use_readout="ignore", hooks=None): + model = timm.create_model("vit_large_patch16_384", pretrained=pretrained) + + hooks = [5, 11, 17, 23] if hooks == None else hooks + return _make_vit_b16_backbone( + model, + features=[256, 512, 1024, 1024], + hooks=hooks, + vit_features=1024, + use_readout=use_readout, + ) + + +def _make_pretrained_vitb16_384(pretrained, use_readout="ignore", hooks=None): + model = timm.create_model("vit_base_patch16_384", pretrained=pretrained) + + hooks = [2, 5, 8, 11] if hooks == None else hooks + return _make_vit_b16_backbone( + model, features=[96, 192, 384, 768], hooks=hooks, use_readout=use_readout + ) + + +def _make_pretrained_deitb16_384(pretrained, use_readout="ignore", hooks=None): + model = timm.create_model("vit_deit_base_patch16_384", pretrained=pretrained) + + hooks = [2, 5, 8, 11] if hooks == None else hooks + return _make_vit_b16_backbone( + model, features=[96, 192, 384, 768], hooks=hooks, use_readout=use_readout + ) + + +def _make_pretrained_deitb16_distil_384(pretrained, use_readout="ignore", hooks=None): + model = timm.create_model( + "vit_deit_base_distilled_patch16_384", pretrained=pretrained + ) + + hooks = [2, 5, 8, 11] if hooks == None else hooks + return _make_vit_b16_backbone( + model, + features=[96, 192, 384, 768], + hooks=hooks, + use_readout=use_readout, + start_index=2, + ) + + +def _make_vit_b_rn50_backbone( + model, + features=[256, 512, 768, 768], + size=[384, 384], + hooks=[0, 1, 8, 11], + vit_features=768, + use_vit_only=False, + use_readout="ignore", + start_index=1, +): + pretrained = nn.Module() + + pretrained.model = model + + if use_vit_only == True: + pretrained.model.blocks[hooks[0]].register_forward_hook(get_activation("1")) + pretrained.model.blocks[hooks[1]].register_forward_hook(get_activation("2")) + else: + pretrained.model.patch_embed.backbone.stages[0].register_forward_hook( + get_activation("1") + ) + pretrained.model.patch_embed.backbone.stages[1].register_forward_hook( + get_activation("2") + ) + + pretrained.model.blocks[hooks[2]].register_forward_hook(get_activation("3")) + pretrained.model.blocks[hooks[3]].register_forward_hook(get_activation("4")) + + pretrained.activations = activations + + readout_oper = get_readout_oper(vit_features, features, use_readout, start_index) + + if use_vit_only == True: + pretrained.act_postprocess1 = nn.Sequential( + readout_oper[0], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[0], + kernel_size=1, + stride=1, + padding=0, + ), + nn.ConvTranspose2d( + in_channels=features[0], + out_channels=features[0], + kernel_size=4, + stride=4, + padding=0, + bias=True, + dilation=1, + groups=1, + ), + ) + + pretrained.act_postprocess2 = nn.Sequential( + readout_oper[1], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[1], + kernel_size=1, + stride=1, + padding=0, + ), + nn.ConvTranspose2d( + in_channels=features[1], + out_channels=features[1], + kernel_size=2, + stride=2, + padding=0, + bias=True, + dilation=1, + groups=1, + ), + ) + else: + pretrained.act_postprocess1 = nn.Sequential( + nn.Identity(), nn.Identity(), nn.Identity() + ) + pretrained.act_postprocess2 = nn.Sequential( + nn.Identity(), nn.Identity(), nn.Identity() + ) + + pretrained.act_postprocess3 = nn.Sequential( + readout_oper[2], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[2], + kernel_size=1, + stride=1, + padding=0, + ), + ) + + pretrained.act_postprocess4 = nn.Sequential( + readout_oper[3], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[3], + kernel_size=1, + stride=1, + padding=0, + ), + nn.Conv2d( + in_channels=features[3], + out_channels=features[3], + kernel_size=3, + stride=2, + padding=1, + ), + ) + + pretrained.model.start_index = start_index + pretrained.model.patch_size = [16, 16] + + # We inject this function into the VisionTransformer instances so that + # we can use it with interpolated position embeddings without modifying the library source. + pretrained.model.forward_flex = types.MethodType(forward_flex, pretrained.model) + + # We inject this function into the VisionTransformer instances so that + # we can use it with interpolated position embeddings without modifying the library source. + pretrained.model._resize_pos_embed = types.MethodType( + _resize_pos_embed, pretrained.model + ) + + return pretrained + + +def _make_pretrained_vitb_rn50_384( + pretrained, use_readout="ignore", hooks=None, use_vit_only=False +): + model = timm.create_model("vit_base_resnet50_384", pretrained=pretrained) + + hooks = [0, 1, 8, 11] if hooks == None else hooks + return _make_vit_b_rn50_backbone( + model, + features=[256, 512, 768, 768], + size=[384, 384], + hooks=hooks, + use_vit_only=use_vit_only, + use_readout=use_readout, + ) diff --git a/sd/stablediffusion/ldm/modules/midas/utils.py b/sd/stablediffusion/ldm/modules/midas/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..9a9d3b5b66370fa98da9e067ba53ead848ea9a59 --- /dev/null +++ b/sd/stablediffusion/ldm/modules/midas/utils.py @@ -0,0 +1,189 @@ +"""Utils for monoDepth.""" +import sys +import re +import numpy as np +import cv2 +import torch + + +def read_pfm(path): + """Read pfm file. + + Args: + path (str): path to file + + Returns: + tuple: (data, scale) + """ + with open(path, "rb") as file: + + color = None + width = None + height = None + scale = None + endian = None + + header = file.readline().rstrip() + if header.decode("ascii") == "PF": + color = True + elif header.decode("ascii") == "Pf": + color = False + else: + raise Exception("Not a PFM file: " + path) + + dim_match = re.match(r"^(\d+)\s(\d+)\s$", file.readline().decode("ascii")) + if dim_match: + width, height = list(map(int, dim_match.groups())) + else: + raise Exception("Malformed PFM header.") + + scale = float(file.readline().decode("ascii").rstrip()) + if scale < 0: + # little-endian + endian = "<" + scale = -scale + else: + # big-endian + endian = ">" + + data = np.fromfile(file, endian + "f") + shape = (height, width, 3) if color else (height, width) + + data = np.reshape(data, shape) + data = np.flipud(data) + + return data, scale + + +def write_pfm(path, image, scale=1): + """Write pfm file. + + Args: + path (str): pathto file + image (array): data + scale (int, optional): Scale. Defaults to 1. + """ + + with open(path, "wb") as file: + color = None + + if image.dtype.name != "float32": + raise Exception("Image dtype must be float32.") + + image = np.flipud(image) + + if len(image.shape) == 3 and image.shape[2] == 3: # color image + color = True + elif ( + len(image.shape) == 2 or len(image.shape) == 3 and image.shape[2] == 1 + ): # greyscale + color = False + else: + raise Exception("Image must have H x W x 3, H x W x 1 or H x W dimensions.") + + file.write("PF\n" if color else "Pf\n".encode()) + file.write("%d %d\n".encode() % (image.shape[1], image.shape[0])) + + endian = image.dtype.byteorder + + if endian == "<" or endian == "=" and sys.byteorder == "little": + scale = -scale + + file.write("%f\n".encode() % scale) + + image.tofile(file) + + +def read_image(path): + """Read image and output RGB image (0-1). + + Args: + path (str): path to file + + Returns: + array: RGB image (0-1) + """ + img = cv2.imread(path) + + if img.ndim == 2: + img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) + + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) / 255.0 + + return img + + +def resize_image(img): + """Resize image and make it fit for network. + + Args: + img (array): image + + Returns: + tensor: data ready for network + """ + height_orig = img.shape[0] + width_orig = img.shape[1] + + if width_orig > height_orig: + scale = width_orig / 384 + else: + scale = height_orig / 384 + + height = (np.ceil(height_orig / scale / 32) * 32).astype(int) + width = (np.ceil(width_orig / scale / 32) * 32).astype(int) + + img_resized = cv2.resize(img, (width, height), interpolation=cv2.INTER_AREA) + + img_resized = ( + torch.from_numpy(np.transpose(img_resized, (2, 0, 1))).contiguous().float() + ) + img_resized = img_resized.unsqueeze(0) + + return img_resized + + +def resize_depth(depth, width, height): + """Resize depth map and bring to CPU (numpy). + + Args: + depth (tensor): depth + width (int): image width + height (int): image height + + Returns: + array: processed depth + """ + depth = torch.squeeze(depth[0, :, :, :]).to("cpu") + + depth_resized = cv2.resize( + depth.numpy(), (width, height), interpolation=cv2.INTER_CUBIC + ) + + return depth_resized + +def write_depth(path, depth, bits=1): + """Write depth map to pfm and png file. + + Args: + path (str): filepath without extension + depth (array): depth + """ + write_pfm(path + ".pfm", depth.astype(np.float32)) + + depth_min = depth.min() + depth_max = depth.max() + + max_val = (2**(8*bits))-1 + + if depth_max - depth_min > np.finfo("float").eps: + out = max_val * (depth - depth_min) / (depth_max - depth_min) + else: + out = np.zeros(depth.shape, dtype=depth.type) + + if bits == 1: + cv2.imwrite(path + ".png", out.astype("uint8")) + elif bits == 2: + cv2.imwrite(path + ".png", out.astype("uint16")) + + return diff --git a/sd/stablediffusion/ldm/util.py b/sd/stablediffusion/ldm/util.py new file mode 100644 index 0000000000000000000000000000000000000000..8c09ca1c72f7ceb3f9d7f9546aae5561baf62b13 --- /dev/null +++ b/sd/stablediffusion/ldm/util.py @@ -0,0 +1,197 @@ +import importlib + +import torch +from torch import optim +import numpy as np + +from inspect import isfunction +from PIL import Image, ImageDraw, ImageFont + + +def log_txt_as_img(wh, xc, size=10): + # wh a tuple of (width, height) + # xc a list of captions to plot + b = len(xc) + txts = list() + for bi in range(b): + txt = Image.new("RGB", wh, color="white") + draw = ImageDraw.Draw(txt) + font = ImageFont.truetype('data/DejaVuSans.ttf', size=size) + nc = int(40 * (wh[0] / 256)) + lines = "\n".join(xc[bi][start:start + nc] for start in range(0, len(xc[bi]), nc)) + + try: + draw.text((0, 0), lines, fill="black", font=font) + except UnicodeEncodeError: + print("Cant encode string for logging. Skipping.") + + txt = np.array(txt).transpose(2, 0, 1) / 127.5 - 1.0 + txts.append(txt) + txts = np.stack(txts) + txts = torch.tensor(txts) + return txts + + +def ismap(x): + if not isinstance(x, torch.Tensor): + return False + return (len(x.shape) == 4) and (x.shape[1] > 3) + + +def isimage(x): + if not isinstance(x,torch.Tensor): + return False + return (len(x.shape) == 4) and (x.shape[1] == 3 or x.shape[1] == 1) + + +def exists(x): + return x is not None + + +def default(val, d): + if exists(val): + return val + return d() if isfunction(d) else d + + +def mean_flat(tensor): + """ + https://github.com/openai/guided-diffusion/blob/27c20a8fab9cb472df5d6bdd6c8d11c8f430b924/guided_diffusion/nn.py#L86 + Take the mean over all non-batch dimensions. + """ + return tensor.mean(dim=list(range(1, len(tensor.shape)))) + + +def count_params(model, verbose=False): + total_params = sum(p.numel() for p in model.parameters()) + if verbose: + print(f"{model.__class__.__name__} has {total_params*1.e-6:.2f} M params.") + return total_params + + +def instantiate_from_config(config): + if not "target" in config: + if config == '__is_first_stage__': + return None + elif config == "__is_unconditional__": + return None + raise KeyError("Expected key `target` to instantiate.") + return get_obj_from_str(config["target"])(**config.get("params", dict())) + + +def get_obj_from_str(string, reload=False): + module, cls = string.rsplit(".", 1) + if reload: + module_imp = importlib.import_module(module) + importlib.reload(module_imp) + return getattr(importlib.import_module(module, package=None), cls) + + +class AdamWwithEMAandWings(optim.Optimizer): + # credit to https://gist.github.com/crowsonkb/65f7265353f403714fce3b2595e0b298 + def __init__(self, params, lr=1.e-3, betas=(0.9, 0.999), eps=1.e-8, # TODO: check hyperparameters before using + weight_decay=1.e-2, amsgrad=False, ema_decay=0.9999, # ema decay to match previous code + ema_power=1., param_names=()): + """AdamW that saves EMA versions of the parameters.""" + if not 0.0 <= lr: + raise ValueError("Invalid learning rate: {}".format(lr)) + if not 0.0 <= eps: + raise ValueError("Invalid epsilon value: {}".format(eps)) + if not 0.0 <= betas[0] < 1.0: + raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0])) + if not 0.0 <= betas[1] < 1.0: + raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1])) + if not 0.0 <= weight_decay: + raise ValueError("Invalid weight_decay value: {}".format(weight_decay)) + if not 0.0 <= ema_decay <= 1.0: + raise ValueError("Invalid ema_decay value: {}".format(ema_decay)) + defaults = dict(lr=lr, betas=betas, eps=eps, + weight_decay=weight_decay, amsgrad=amsgrad, ema_decay=ema_decay, + ema_power=ema_power, param_names=param_names) + super().__init__(params, defaults) + + def __setstate__(self, state): + super().__setstate__(state) + for group in self.param_groups: + group.setdefault('amsgrad', False) + + @torch.no_grad() + def step(self, closure=None): + """Performs a single optimization step. + Args: + closure (callable, optional): A closure that reevaluates the model + and returns the loss. + """ + loss = None + if closure is not None: + with torch.enable_grad(): + loss = closure() + + for group in self.param_groups: + params_with_grad = [] + grads = [] + exp_avgs = [] + exp_avg_sqs = [] + ema_params_with_grad = [] + state_sums = [] + max_exp_avg_sqs = [] + state_steps = [] + amsgrad = group['amsgrad'] + beta1, beta2 = group['betas'] + ema_decay = group['ema_decay'] + ema_power = group['ema_power'] + + for p in group['params']: + if p.grad is None: + continue + params_with_grad.append(p) + if p.grad.is_sparse: + raise RuntimeError('AdamW does not support sparse gradients') + grads.append(p.grad) + + state = self.state[p] + + # State initialization + if len(state) == 0: + state['step'] = 0 + # Exponential moving average of gradient values + state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format) + # Exponential moving average of squared gradient values + state['exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format) + if amsgrad: + # Maintains max of all exp. moving avg. of sq. grad. values + state['max_exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format) + # Exponential moving average of parameter values + state['param_exp_avg'] = p.detach().float().clone() + + exp_avgs.append(state['exp_avg']) + exp_avg_sqs.append(state['exp_avg_sq']) + ema_params_with_grad.append(state['param_exp_avg']) + + if amsgrad: + max_exp_avg_sqs.append(state['max_exp_avg_sq']) + + # update the steps for each param group update + state['step'] += 1 + # record the step after step update + state_steps.append(state['step']) + + optim._functional.adamw(params_with_grad, + grads, + exp_avgs, + exp_avg_sqs, + max_exp_avg_sqs, + state_steps, + amsgrad=amsgrad, + beta1=beta1, + beta2=beta2, + lr=group['lr'], + weight_decay=group['weight_decay'], + eps=group['eps'], + maximize=False) + + cur_ema_decay = min(ema_decay, 1 - state['step'] ** -ema_power) + for param, ema_param in zip(params_with_grad, ema_params_with_grad): + ema_param.mul_(cur_ema_decay).add_(param.float(), alpha=1 - cur_ema_decay) + + return loss \ No newline at end of file diff --git a/sd/stablediffusion/modelcard.md b/sd/stablediffusion/modelcard.md new file mode 100644 index 0000000000000000000000000000000000000000..787f15c2f0bedaec6e9f67b6dd506d8fdf531ad0 --- /dev/null +++ b/sd/stablediffusion/modelcard.md @@ -0,0 +1,146 @@ +# Stable Diffusion v2 Model Card +This model card focuses on the models associated with the Stable Diffusion v2, available [here](https://github.com/Stability-AI/stablediffusion/). + +## Model Details +- **Developed by:** Robin Rombach, Patrick Esser +- **Model type:** Diffusion-based text-to-image generation model +- **Language(s):** English +- **License:** CreativeML Open RAIL++-M License +- **Model Description:** This is a model that can be used to generate and modify images based on text prompts. It is a [Latent Diffusion Model](https://arxiv.org/abs/2112.10752) that uses a fixed, pretrained text encoder ([OpenCLIP-ViT/H](https://github.com/mlfoundations/open_clip)). +- **Resources for more information:** [GitHub Repository](https://github.com/Stability-AI/). +- **Cite as:** + + @InProceedings{Rombach_2022_CVPR, + author = {Rombach, Robin and Blattmann, Andreas and Lorenz, Dominik and Esser, Patrick and Ommer, Bj\"orn}, + title = {High-Resolution Image Synthesis With Latent Diffusion Models}, + booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)}, + month = {June}, + year = {2022}, + pages = {10684-10695} + } + +# Uses + +## Direct Use +The model is intended for research purposes only. Possible research areas and tasks include + +- Safe deployment of models which have the potential to generate harmful content. +- Probing and understanding the limitations and biases of generative models. +- Generation of artworks and use in design and other artistic processes. +- Applications in educational or creative tools. +- Research on generative models. + +Excluded uses are described below. + + ### Misuse, Malicious Use, and Out-of-Scope Use +_Note: This section is originally taken from the [DALLE-MINI model card](https://huggingface.co/dalle-mini/dalle-mini), was used for Stable Diffusion v1, but applies in the same way to Stable Diffusion v2_. + +The model should not be used to intentionally create or disseminate images that create hostile or alienating environments for people. This includes generating images that people would foreseeably find disturbing, distressing, or offensive; or content that propagates historical or current stereotypes. + +#### Out-of-Scope Use +The model was not trained to be factual or true representations of people or events, and therefore using the model to generate such content is out-of-scope for the abilities of this model. + +#### Misuse and Malicious Use +Using the model to generate content that is cruel to individuals is a misuse of this model. This includes, but is not limited to: + +- Generating demeaning, dehumanizing, or otherwise harmful representations of people or their environments, cultures, religions, etc. +- Intentionally promoting or propagating discriminatory content or harmful stereotypes. +- Impersonating individuals without their consent. +- Sexual content without consent of the people who might see it. +- Mis- and disinformation +- Representations of egregious violence and gore +- Sharing of copyrighted or licensed material in violation of its terms of use. +- Sharing content that is an alteration of copyrighted or licensed material in violation of its terms of use. + +## Limitations and Bias + +### Limitations + +- The model does not achieve perfect photorealism +- The model cannot render legible text +- The model does not perform well on more difficult tasks which involve compositionality, such as rendering an image corresponding to “A red cube on top of a blue sphere” +- Faces and people in general may not be generated properly. +- The model was trained mainly with English captions and will not work as well in other languages. +- The autoencoding part of the model is lossy +- The model was trained on a subset of the large-scale dataset + [LAION-5B](https://laion.ai/blog/laion-5b/), which contains adult, violent and sexual content. To partially mitigate this, we have filtered the dataset using LAION's NFSW detector (see Training section). + +### Bias +While the capabilities of image generation models are impressive, they can also reinforce or exacerbate social biases. +Stable Diffusion vw was primarily trained on subsets of [LAION-2B(en)](https://laion.ai/blog/laion-5b/), +which consists of images that are limited to English descriptions. +Texts and images from communities and cultures that use other languages are likely to be insufficiently accounted for. +This affects the overall output of the model, as white and western cultures are often set as the default. Further, the +ability of the model to generate content with non-English prompts is significantly worse than with English-language prompts. +Stable Diffusion v2 mirrors and exacerbates biases to such a degree that viewer discretion must be advised irrespective of the input or its intent. + + +## Training + +**Training Data** +The model developers used the following dataset for training the model: + +- LAION-5B and subsets (details below). The training data is further filtered using LAION's NSFW detector. For more details, please refer to LAION-5B's [NeurIPS 2022](https://openreview.net/forum?id=M3Y74vmsMcY) paper and reviewer discussions on the topic. + +**Training Procedure** +Stable Diffusion v2 is a latent diffusion model which combines an autoencoder with a diffusion model that is trained in the latent space of the autoencoder. During training, + +- Images are encoded through an encoder, which turns images into latent representations. The autoencoder uses a relative downsampling factor of 8 and maps images of shape H x W x 3 to latents of shape H/f x W/f x 4 +- Text prompts are encoded through the OpenCLIP-ViT/H text-encoder. +- The output of the text encoder is fed into the UNet backbone of the latent diffusion model via cross-attention. +- The loss is a reconstruction objective between the noise that was added to the latent and the prediction made by the UNet. We also use the so-called _v-objective_, see https://arxiv.org/abs/2202.00512. + +We currently provide the following checkpoints, for various versions: + +### Version 2.1 + +- `512-base-ema.ckpt`: Fine-tuned on `512-base-ema.ckpt` 2.0 with 220k extra steps taken, with `punsafe=0.98` on the same dataset. +- `768-v-ema.ckpt`: Resumed from `768-v-ema.ckpt` 2.0 with an additional 55k steps on the same dataset (`punsafe=0.1`), and then fine-tuned for another 155k extra steps with `punsafe=0.98`. +### Version 2.0 + +- `512-base-ema.ckpt`: 550k steps at resolution `256x256` on a subset of [LAION-5B](https://laion.ai/blog/laion-5b/) filtered for explicit pornographic material, using the [LAION-NSFW classifier](https://github.com/LAION-AI/CLIP-based-NSFW-Detector) with `punsafe=0.1` and an [aesthetic score](https://github.com/christophschuhmann/improved-aesthetic-predictor) >= `4.5`. + 850k steps at resolution `512x512` on the same dataset with resolution `>= 512x512`. +- `768-v-ema.ckpt`: Resumed from `512-base-ema.ckpt` and trained for 150k steps using a [v-objective](https://arxiv.org/abs/2202.00512) on the same dataset. Resumed for another 140k steps on a `768x768` subset of our dataset. +- `512-depth-ema.ckpt`: Resumed from `512-base-ema.ckpt` and finetuned for 200k steps. Added an extra input channel to process the (relative) depth prediction produced by [MiDaS](https://github.com/isl-org/MiDaS) (`dpt_hybrid`) which is used as an additional conditioning. +The additional input channels of the U-Net which process this extra information were zero-initialized. +- `512-inpainting-ema.ckpt`: Resumed from `512-base-ema.ckpt` and trained for another 200k steps. Follows the mask-generation strategy presented in [LAMA](https://github.com/saic-mdal/lama) which, in combination with the latent VAE representations of the masked image, are used as an additional conditioning. +The additional input channels of the U-Net which process this extra information were zero-initialized. The same strategy was used to train the [1.5-inpainting checkpoint](https://github.com/saic-mdal/lama). +- `x4-upscaling-ema.ckpt`: Trained for 1.25M steps on a 10M subset of LAION containing images `>2048x2048`. The model was trained on crops of size `512x512` and is a text-guided [latent upscaling diffusion model](https://arxiv.org/abs/2112.10752). +In addition to the textual input, it receives a `noise_level` as an input parameter, which can be used to add noise to the low-resolution input according to a [predefined diffusion schedule](configs/stable-diffusion/x4-upscaling.yaml). + +- **Hardware:** 32 x 8 x A100 GPUs +- **Optimizer:** AdamW +- **Gradient Accumulations**: 1 +- **Batch:** 32 x 8 x 2 x 4 = 2048 +- **Learning rate:** warmup to 0.0001 for 10,000 steps and then kept constant + +## Evaluation Results +Evaluations with different classifier-free guidance scales (1.5, 2.0, 3.0, 4.0, +5.0, 6.0, 7.0, 8.0) and 50 steps DDIM sampling steps show the relative improvements of the checkpoints: + +![pareto](assets/model-variants.jpg) + +Evaluated using 50 DDIM steps and 10000 random prompts from the COCO2017 validation set, evaluated at 512x512 resolution. Not optimized for FID scores. + +## Environmental Impact + +**Stable Diffusion v1** **Estimated Emissions** +Based on that information, we estimate the following CO2 emissions using the [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://arxiv.org/abs/1910.09700). The hardware, runtime, cloud provider, and compute region were utilized to estimate the carbon impact. + +- **Hardware Type:** A100 PCIe 40GB +- **Hours used:** 200000 +- **Cloud Provider:** AWS +- **Compute Region:** US-east +- **Carbon Emitted (Power consumption x Time x Carbon produced based on location of power grid):** 15000 kg CO2 eq. + +## Citation + @InProceedings{Rombach_2022_CVPR, + author = {Rombach, Robin and Blattmann, Andreas and Lorenz, Dominik and Esser, Patrick and Ommer, Bj\"orn}, + title = {High-Resolution Image Synthesis With Latent Diffusion Models}, + booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)}, + month = {June}, + year = {2022}, + pages = {10684-10695} + } + +*This model card was written by: Robin Rombach, Patrick Esser and David Ha and is based on the [Stable Diffusion v1](https://github.com/CompVis/stable-diffusion/blob/main/Stable_Diffusion_v1_Model_Card.md) and [DALL-E Mini model card](https://huggingface.co/dalle-mini/dalle-mini).* diff --git a/sd/stablediffusion/requirements.txt b/sd/stablediffusion/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..6cf1b418a7867ed3536d4dd680f9e7c55eecc84c --- /dev/null +++ b/sd/stablediffusion/requirements.txt @@ -0,0 +1,18 @@ +albumentations==0.4.3 +opencv-python +pudb==2019.2 +imageio==2.9.0 +imageio-ffmpeg==0.4.2 +pytorch-lightning==1.4.2 +torchmetrics==0.6 +omegaconf==2.1.1 +test-tube>=0.7.5 +streamlit>=0.73.1 +einops==0.3.0 +transformers==4.19.2 +webdataset==0.2.5 +open-clip-torch==2.7.0 +gradio==3.11 +kornia==0.6 +invisible-watermark>=0.1.5 +streamlit-drawable-canvas==0.8.0 \ No newline at end of file diff --git a/sd/stablediffusion/scripts/gradio/depth2img.py b/sd/stablediffusion/scripts/gradio/depth2img.py new file mode 100644 index 0000000000000000000000000000000000000000..c791a4d0b2a510b3525658f4d852d14704ea9f1a --- /dev/null +++ b/sd/stablediffusion/scripts/gradio/depth2img.py @@ -0,0 +1,184 @@ +import sys +import torch +import numpy as np +import gradio as gr +from PIL import Image +from omegaconf import OmegaConf +from einops import repeat, rearrange +from pytorch_lightning import seed_everything +from imwatermark import WatermarkEncoder + +from scripts.txt2img import put_watermark +from ldm.util import instantiate_from_config +from ldm.models.diffusion.ddim import DDIMSampler +from ldm.data.util import AddMiDaS + +torch.set_grad_enabled(False) + + +def initialize_model(config, ckpt): + config = OmegaConf.load(config) + model = instantiate_from_config(config.model) + model.load_state_dict(torch.load(ckpt)["state_dict"], strict=False) + + device = torch.device( + "cuda") if torch.cuda.is_available() else torch.device("cpu") + model = model.to(device) + sampler = DDIMSampler(model) + return sampler + + +def make_batch_sd( + image, + txt, + device, + num_samples=1, + model_type="dpt_hybrid" +): + image = np.array(image.convert("RGB")) + image = torch.from_numpy(image).to(dtype=torch.float32) / 127.5 - 1.0 + # sample['jpg'] is tensor hwc in [-1, 1] at this point + midas_trafo = AddMiDaS(model_type=model_type) + batch = { + "jpg": image, + "txt": num_samples * [txt], + } + batch = midas_trafo(batch) + batch["jpg"] = rearrange(batch["jpg"], 'h w c -> 1 c h w') + batch["jpg"] = repeat(batch["jpg"].to(device=device), + "1 ... -> n ...", n=num_samples) + batch["midas_in"] = repeat(torch.from_numpy(batch["midas_in"][None, ...]).to( + device=device), "1 ... -> n ...", n=num_samples) + return batch + + +def paint(sampler, image, prompt, t_enc, seed, scale, num_samples=1, callback=None, + do_full_sample=False): + device = torch.device( + "cuda") if torch.cuda.is_available() else torch.device("cpu") + model = sampler.model + seed_everything(seed) + + print("Creating invisible watermark encoder (see https://github.com/ShieldMnt/invisible-watermark)...") + wm = "SDV2" + wm_encoder = WatermarkEncoder() + wm_encoder.set_watermark('bytes', wm.encode('utf-8')) + + with torch.no_grad(),\ + torch.autocast("cuda"): + batch = make_batch_sd( + image, txt=prompt, device=device, num_samples=num_samples) + z = model.get_first_stage_encoding(model.encode_first_stage( + batch[model.first_stage_key])) # move to latent space + c = model.cond_stage_model.encode(batch["txt"]) + c_cat = list() + for ck in model.concat_keys: + cc = batch[ck] + cc = model.depth_model(cc) + depth_min, depth_max = torch.amin(cc, dim=[1, 2, 3], keepdim=True), torch.amax(cc, dim=[1, 2, 3], + keepdim=True) + display_depth = (cc - depth_min) / (depth_max - depth_min) + depth_image = Image.fromarray( + (display_depth[0, 0, ...].cpu().numpy() * 255.).astype(np.uint8)) + cc = torch.nn.functional.interpolate( + cc, + size=z.shape[2:], + mode="bicubic", + align_corners=False, + ) + depth_min, depth_max = torch.amin(cc, dim=[1, 2, 3], keepdim=True), torch.amax(cc, dim=[1, 2, 3], + keepdim=True) + cc = 2. * (cc - depth_min) / (depth_max - depth_min) - 1. + c_cat.append(cc) + c_cat = torch.cat(c_cat, dim=1) + # cond + cond = {"c_concat": [c_cat], "c_crossattn": [c]} + + # uncond cond + uc_cross = model.get_unconditional_conditioning(num_samples, "") + uc_full = {"c_concat": [c_cat], "c_crossattn": [uc_cross]} + if not do_full_sample: + # encode (scaled latent) + z_enc = sampler.stochastic_encode( + z, torch.tensor([t_enc] * num_samples).to(model.device)) + else: + z_enc = torch.randn_like(z) + # decode it + samples = sampler.decode(z_enc, cond, t_enc, unconditional_guidance_scale=scale, + unconditional_conditioning=uc_full, callback=callback) + x_samples_ddim = model.decode_first_stage(samples) + result = torch.clamp((x_samples_ddim + 1.0) / 2.0, min=0.0, max=1.0) + result = result.cpu().numpy().transpose(0, 2, 3, 1) * 255 + return [depth_image] + [put_watermark(Image.fromarray(img.astype(np.uint8)), wm_encoder) for img in result] + + +def pad_image(input_image): + pad_w, pad_h = np.max(((2, 2), np.ceil( + np.array(input_image.size) / 64).astype(int)), axis=0) * 64 - input_image.size + im_padded = Image.fromarray( + np.pad(np.array(input_image), ((0, pad_h), (0, pad_w), (0, 0)), mode='edge')) + return im_padded + + +def predict(input_image, prompt, steps, num_samples, scale, seed, eta, strength): + init_image = input_image.convert("RGB") + image = pad_image(init_image) # resize to integer multiple of 32 + + sampler.make_schedule(steps, ddim_eta=eta, verbose=True) + assert 0. <= strength <= 1., 'can only work with strength in [0.0, 1.0]' + do_full_sample = strength == 1. + t_enc = min(int(strength * steps), steps-1) + result = paint( + sampler=sampler, + image=image, + prompt=prompt, + t_enc=t_enc, + seed=seed, + scale=scale, + num_samples=num_samples, + callback=None, + do_full_sample=do_full_sample + ) + return result + + +sampler = initialize_model(sys.argv[1], sys.argv[2]) + +block = gr.Blocks().queue() +with block: + with gr.Row(): + gr.Markdown("## Stable Diffusion Depth2Img") + + with gr.Row(): + with gr.Column(): + input_image = gr.Image(source='upload', type="pil") + prompt = gr.Textbox(label="Prompt") + run_button = gr.Button(label="Run") + with gr.Accordion("Advanced options", open=False): + num_samples = gr.Slider( + label="Images", minimum=1, maximum=4, value=1, step=1) + ddim_steps = gr.Slider(label="Steps", minimum=1, + maximum=50, value=50, step=1) + scale = gr.Slider( + label="Guidance Scale", minimum=0.1, maximum=30.0, value=9.0, step=0.1 + ) + strength = gr.Slider( + label="Strength", minimum=0.0, maximum=1.0, value=0.9, step=0.01 + ) + seed = gr.Slider( + label="Seed", + minimum=0, + maximum=2147483647, + step=1, + randomize=True, + ) + eta = gr.Number(label="eta (DDIM)", value=0.0) + with gr.Column(): + gallery = gr.Gallery(label="Generated images", show_label=False).style( + grid=[2], height="auto") + + run_button.click(fn=predict, inputs=[ + input_image, prompt, ddim_steps, num_samples, scale, seed, eta, strength], outputs=[gallery]) + + +block.launch() diff --git a/sd/stablediffusion/scripts/gradio/inpainting.py b/sd/stablediffusion/scripts/gradio/inpainting.py new file mode 100644 index 0000000000000000000000000000000000000000..09d44f3ddc528011d7421966915b93d0e2803ba5 --- /dev/null +++ b/sd/stablediffusion/scripts/gradio/inpainting.py @@ -0,0 +1,195 @@ +import sys +import cv2 +import torch +import numpy as np +import gradio as gr +from PIL import Image +from omegaconf import OmegaConf +from einops import repeat +from imwatermark import WatermarkEncoder +from pathlib import Path + +from ldm.models.diffusion.ddim import DDIMSampler +from ldm.util import instantiate_from_config + + +torch.set_grad_enabled(False) + + +def put_watermark(img, wm_encoder=None): + if wm_encoder is not None: + img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) + img = wm_encoder.encode(img, 'dwtDct') + img = Image.fromarray(img[:, :, ::-1]) + return img + + +def initialize_model(config, ckpt): + config = OmegaConf.load(config) + model = instantiate_from_config(config.model) + + model.load_state_dict(torch.load(ckpt)["state_dict"], strict=False) + + device = torch.device( + "cuda") if torch.cuda.is_available() else torch.device("cpu") + model = model.to(device) + sampler = DDIMSampler(model) + + return sampler + + +def make_batch_sd( + image, + mask, + txt, + device, + num_samples=1): + image = np.array(image.convert("RGB")) + image = image[None].transpose(0, 3, 1, 2) + image = torch.from_numpy(image).to(dtype=torch.float32) / 127.5 - 1.0 + + mask = np.array(mask.convert("L")) + mask = mask.astype(np.float32) / 255.0 + mask = mask[None, None] + mask[mask < 0.5] = 0 + mask[mask >= 0.5] = 1 + mask = torch.from_numpy(mask) + + masked_image = image * (mask < 0.5) + + batch = { + "image": repeat(image.to(device=device), "1 ... -> n ...", n=num_samples), + "txt": num_samples * [txt], + "mask": repeat(mask.to(device=device), "1 ... -> n ...", n=num_samples), + "masked_image": repeat(masked_image.to(device=device), "1 ... -> n ...", n=num_samples), + } + return batch + + +def inpaint(sampler, image, mask, prompt, seed, scale, ddim_steps, num_samples=1, w=512, h=512): + device = torch.device( + "cuda") if torch.cuda.is_available() else torch.device("cpu") + model = sampler.model + + print("Creating invisible watermark encoder (see https://github.com/ShieldMnt/invisible-watermark)...") + wm = "SDV2" + wm_encoder = WatermarkEncoder() + wm_encoder.set_watermark('bytes', wm.encode('utf-8')) + + prng = np.random.RandomState(seed) + start_code = prng.randn(num_samples, 4, h // 8, w // 8) + start_code = torch.from_numpy(start_code).to( + device=device, dtype=torch.float32) + + with torch.no_grad(), \ + torch.autocast("cuda"): + batch = make_batch_sd(image, mask, txt=prompt, + device=device, num_samples=num_samples) + + c = model.cond_stage_model.encode(batch["txt"]) + + c_cat = list() + for ck in model.concat_keys: + cc = batch[ck].float() + if ck != model.masked_image_key: + bchw = [num_samples, 4, h // 8, w // 8] + cc = torch.nn.functional.interpolate(cc, size=bchw[-2:]) + else: + cc = model.get_first_stage_encoding( + model.encode_first_stage(cc)) + c_cat.append(cc) + c_cat = torch.cat(c_cat, dim=1) + + # cond + cond = {"c_concat": [c_cat], "c_crossattn": [c]} + + # uncond cond + uc_cross = model.get_unconditional_conditioning(num_samples, "") + uc_full = {"c_concat": [c_cat], "c_crossattn": [uc_cross]} + + shape = [model.channels, h // 8, w // 8] + samples_cfg, intermediates = sampler.sample( + ddim_steps, + num_samples, + shape, + cond, + verbose=False, + eta=1.0, + unconditional_guidance_scale=scale, + unconditional_conditioning=uc_full, + x_T=start_code, + ) + x_samples_ddim = model.decode_first_stage(samples_cfg) + + result = torch.clamp((x_samples_ddim + 1.0) / 2.0, + min=0.0, max=1.0) + + result = result.cpu().numpy().transpose(0, 2, 3, 1) * 255 + return [put_watermark(Image.fromarray(img.astype(np.uint8)), wm_encoder) for img in result] + +def pad_image(input_image): + pad_w, pad_h = np.max(((2, 2), np.ceil( + np.array(input_image.size) / 64).astype(int)), axis=0) * 64 - input_image.size + im_padded = Image.fromarray( + np.pad(np.array(input_image), ((0, pad_h), (0, pad_w), (0, 0)), mode='edge')) + return im_padded + +def predict(input_image, prompt, ddim_steps, num_samples, scale, seed): + init_image = input_image["image"].convert("RGB") + init_mask = input_image["mask"].convert("RGB") + image = pad_image(init_image) # resize to integer multiple of 32 + mask = pad_image(init_mask) # resize to integer multiple of 32 + width, height = image.size + print("Inpainting...", width, height) + + result = inpaint( + sampler=sampler, + image=image, + mask=mask, + prompt=prompt, + seed=seed, + scale=scale, + ddim_steps=ddim_steps, + num_samples=num_samples, + h=height, w=width + ) + + return result + + +sampler = initialize_model(sys.argv[1], sys.argv[2]) + +block = gr.Blocks().queue() +with block: + with gr.Row(): + gr.Markdown("## Stable Diffusion Inpainting") + + with gr.Row(): + with gr.Column(): + input_image = gr.Image(source='upload', tool='sketch', type="pil") + prompt = gr.Textbox(label="Prompt") + run_button = gr.Button(label="Run") + with gr.Accordion("Advanced options", open=False): + num_samples = gr.Slider( + label="Images", minimum=1, maximum=4, value=4, step=1) + ddim_steps = gr.Slider(label="Steps", minimum=1, + maximum=50, value=45, step=1) + scale = gr.Slider( + label="Guidance Scale", minimum=0.1, maximum=30.0, value=10, step=0.1 + ) + seed = gr.Slider( + label="Seed", + minimum=0, + maximum=2147483647, + step=1, + randomize=True, + ) + with gr.Column(): + gallery = gr.Gallery(label="Generated images", show_label=False).style( + grid=[2], height="auto") + + run_button.click(fn=predict, inputs=[ + input_image, prompt, ddim_steps, num_samples, scale, seed], outputs=[gallery]) + + +block.launch() diff --git a/sd/stablediffusion/scripts/gradio/superresolution.py b/sd/stablediffusion/scripts/gradio/superresolution.py new file mode 100644 index 0000000000000000000000000000000000000000..3d08fbfae4f9639165e669f3c69c76763c5b32a8 --- /dev/null +++ b/sd/stablediffusion/scripts/gradio/superresolution.py @@ -0,0 +1,197 @@ +import sys +import torch +import numpy as np +import gradio as gr +from PIL import Image +from omegaconf import OmegaConf +from einops import repeat, rearrange +from pytorch_lightning import seed_everything +from imwatermark import WatermarkEncoder + +from scripts.txt2img import put_watermark +from ldm.models.diffusion.ddim import DDIMSampler +from ldm.models.diffusion.ddpm import LatentUpscaleDiffusion, LatentUpscaleFinetuneDiffusion +from ldm.util import exists, instantiate_from_config + + +torch.set_grad_enabled(False) + + +def initialize_model(config, ckpt): + config = OmegaConf.load(config) + model = instantiate_from_config(config.model) + model.load_state_dict(torch.load(ckpt)["state_dict"], strict=False) + + device = torch.device( + "cuda") if torch.cuda.is_available() else torch.device("cpu") + model = model.to(device) + sampler = DDIMSampler(model) + return sampler + + +def make_batch_sd( + image, + txt, + device, + num_samples=1, +): + image = np.array(image.convert("RGB")) + image = torch.from_numpy(image).to(dtype=torch.float32) / 127.5 - 1.0 + batch = { + "lr": rearrange(image, 'h w c -> 1 c h w'), + "txt": num_samples * [txt], + } + batch["lr"] = repeat(batch["lr"].to(device=device), + "1 ... -> n ...", n=num_samples) + return batch + + +def make_noise_augmentation(model, batch, noise_level=None): + x_low = batch[model.low_scale_key] + x_low = x_low.to(memory_format=torch.contiguous_format).float() + x_aug, noise_level = model.low_scale_model(x_low, noise_level) + return x_aug, noise_level + + +def paint(sampler, image, prompt, seed, scale, h, w, steps, num_samples=1, callback=None, eta=0., noise_level=None): + device = torch.device( + "cuda") if torch.cuda.is_available() else torch.device("cpu") + model = sampler.model + seed_everything(seed) + prng = np.random.RandomState(seed) + start_code = prng.randn(num_samples, model.channels, h, w) + start_code = torch.from_numpy(start_code).to( + device=device, dtype=torch.float32) + + print("Creating invisible watermark encoder (see https://github.com/ShieldMnt/invisible-watermark)...") + wm = "SDV2" + wm_encoder = WatermarkEncoder() + wm_encoder.set_watermark('bytes', wm.encode('utf-8')) + with torch.no_grad(),\ + torch.autocast("cuda"): + batch = make_batch_sd( + image, txt=prompt, device=device, num_samples=num_samples) + c = model.cond_stage_model.encode(batch["txt"]) + c_cat = list() + if isinstance(model, LatentUpscaleFinetuneDiffusion): + for ck in model.concat_keys: + cc = batch[ck] + if exists(model.reshuffle_patch_size): + assert isinstance(model.reshuffle_patch_size, int) + cc = rearrange(cc, 'b c (p1 h) (p2 w) -> b (p1 p2 c) h w', + p1=model.reshuffle_patch_size, p2=model.reshuffle_patch_size) + c_cat.append(cc) + c_cat = torch.cat(c_cat, dim=1) + # cond + cond = {"c_concat": [c_cat], "c_crossattn": [c]} + # uncond cond + uc_cross = model.get_unconditional_conditioning(num_samples, "") + uc_full = {"c_concat": [c_cat], "c_crossattn": [uc_cross]} + elif isinstance(model, LatentUpscaleDiffusion): + x_augment, noise_level = make_noise_augmentation( + model, batch, noise_level) + cond = {"c_concat": [x_augment], + "c_crossattn": [c], "c_adm": noise_level} + # uncond cond + uc_cross = model.get_unconditional_conditioning(num_samples, "") + uc_full = {"c_concat": [x_augment], "c_crossattn": [ + uc_cross], "c_adm": noise_level} + else: + raise NotImplementedError() + + shape = [model.channels, h, w] + samples, intermediates = sampler.sample( + steps, + num_samples, + shape, + cond, + verbose=False, + eta=eta, + unconditional_guidance_scale=scale, + unconditional_conditioning=uc_full, + x_T=start_code, + callback=callback + ) + with torch.no_grad(): + x_samples_ddim = model.decode_first_stage(samples) + result = torch.clamp((x_samples_ddim + 1.0) / 2.0, min=0.0, max=1.0) + result = result.cpu().numpy().transpose(0, 2, 3, 1) * 255 + return [put_watermark(Image.fromarray(img.astype(np.uint8)), wm_encoder) for img in result] + + +def pad_image(input_image): + pad_w, pad_h = np.max(((2, 2), np.ceil( + np.array(input_image.size) / 64).astype(int)), axis=0) * 64 - input_image.size + im_padded = Image.fromarray( + np.pad(np.array(input_image), ((0, pad_h), (0, pad_w), (0, 0)), mode='edge')) + return im_padded + + +def predict(input_image, prompt, steps, num_samples, scale, seed, eta, noise_level): + init_image = input_image.convert("RGB") + image = pad_image(init_image) # resize to integer multiple of 32 + width, height = image.size + + noise_level = torch.Tensor( + num_samples * [noise_level]).to(sampler.model.device).long() + sampler.make_schedule(steps, ddim_eta=eta, verbose=True) + result = paint( + sampler=sampler, + image=image, + prompt=prompt, + seed=seed, + scale=scale, + h=height, w=width, steps=steps, + num_samples=num_samples, + callback=None, + noise_level=noise_level + ) + return result + + +sampler = initialize_model(sys.argv[1], sys.argv[2]) + +block = gr.Blocks().queue() +with block: + with gr.Row(): + gr.Markdown("## Stable Diffusion Upscaling") + + with gr.Row(): + with gr.Column(): + input_image = gr.Image(source='upload', type="pil") + gr.Markdown( + "Tip: Add a description of the object that should be upscaled, e.g.: 'a professional photograph of a cat") + prompt = gr.Textbox(label="Prompt") + run_button = gr.Button(label="Run") + with gr.Accordion("Advanced options", open=False): + num_samples = gr.Slider( + label="Number of Samples", minimum=1, maximum=4, value=1, step=1) + steps = gr.Slider(label="DDIM Steps", minimum=2, + maximum=200, value=75, step=1) + scale = gr.Slider( + label="Scale", minimum=0.1, maximum=30.0, value=10, step=0.1 + ) + seed = gr.Slider( + label="Seed", + minimum=0, + maximum=2147483647, + step=1, + randomize=True, + ) + eta = gr.Number(label="eta (DDIM)", + value=0.0, min=0.0, max=1.0) + noise_level = None + if isinstance(sampler.model, LatentUpscaleDiffusion): + # TODO: make this work for all models + noise_level = gr.Number( + label="Noise Augmentation", min=0, max=350, value=20, step=1) + + with gr.Column(): + gallery = gr.Gallery(label="Generated images", show_label=False).style( + grid=[2], height="auto") + + run_button.click(fn=predict, inputs=[ + input_image, prompt, steps, num_samples, scale, seed, eta, noise_level], outputs=[gallery]) + + +block.launch() diff --git a/sd/stablediffusion/scripts/img2img.py b/sd/stablediffusion/scripts/img2img.py new file mode 100644 index 0000000000000000000000000000000000000000..9085ba9d37ea6402b9ee543e82f7d8c56a1c273a --- /dev/null +++ b/sd/stablediffusion/scripts/img2img.py @@ -0,0 +1,279 @@ +"""make variations of input image""" + +import argparse, os +import PIL +import torch +import numpy as np +from omegaconf import OmegaConf +from PIL import Image +from tqdm import tqdm, trange +from itertools import islice +from einops import rearrange, repeat +from torchvision.utils import make_grid +from torch import autocast +from contextlib import nullcontext +from pytorch_lightning import seed_everything +from imwatermark import WatermarkEncoder + + +from scripts.txt2img import put_watermark +from ldm.util import instantiate_from_config +from ldm.models.diffusion.ddim import DDIMSampler + + +def chunk(it, size): + it = iter(it) + return iter(lambda: tuple(islice(it, size)), ()) + + +def load_model_from_config(config, ckpt, verbose=False): + print(f"Loading model from {ckpt}") + pl_sd = torch.load(ckpt, map_location="cpu") + if "global_step" in pl_sd: + print(f"Global Step: {pl_sd['global_step']}") + sd = pl_sd["state_dict"] + model = instantiate_from_config(config.model) + m, u = model.load_state_dict(sd, strict=False) + if len(m) > 0 and verbose: + print("missing keys:") + print(m) + if len(u) > 0 and verbose: + print("unexpected keys:") + print(u) + + model.cuda() + model.eval() + return model + + +def load_img(path): + image = Image.open(path).convert("RGB") + w, h = image.size + print(f"loaded input image of size ({w}, {h}) from {path}") + w, h = map(lambda x: x - x % 64, (w, h)) # resize to integer multiple of 64 + image = image.resize((w, h), resample=PIL.Image.LANCZOS) + image = np.array(image).astype(np.float32) / 255.0 + image = image[None].transpose(0, 3, 1, 2) + image = torch.from_numpy(image) + return 2. * image - 1. + + +def main(): + parser = argparse.ArgumentParser() + + parser.add_argument( + "--prompt", + type=str, + nargs="?", + default="a painting of a virus monster playing guitar", + help="the prompt to render" + ) + + parser.add_argument( + "--init-img", + type=str, + nargs="?", + help="path to the input image" + ) + + parser.add_argument( + "--outdir", + type=str, + nargs="?", + help="dir to write results to", + default="outputs/img2img-samples" + ) + + parser.add_argument( + "--ddim_steps", + type=int, + default=50, + help="number of ddim sampling steps", + ) + + parser.add_argument( + "--fixed_code", + action='store_true', + help="if enabled, uses the same starting code across all samples ", + ) + + parser.add_argument( + "--ddim_eta", + type=float, + default=0.0, + help="ddim eta (eta=0.0 corresponds to deterministic sampling", + ) + parser.add_argument( + "--n_iter", + type=int, + default=1, + help="sample this often", + ) + + parser.add_argument( + "--C", + type=int, + default=4, + help="latent channels", + ) + parser.add_argument( + "--f", + type=int, + default=8, + help="downsampling factor, most often 8 or 16", + ) + + parser.add_argument( + "--n_samples", + type=int, + default=2, + help="how many samples to produce for each given prompt. A.k.a batch size", + ) + + parser.add_argument( + "--n_rows", + type=int, + default=0, + help="rows in the grid (default: n_samples)", + ) + + parser.add_argument( + "--scale", + type=float, + default=9.0, + help="unconditional guidance scale: eps = eps(x, empty) + scale * (eps(x, cond) - eps(x, empty))", + ) + + parser.add_argument( + "--strength", + type=float, + default=0.8, + help="strength for noising/unnoising. 1.0 corresponds to full destruction of information in init image", + ) + + parser.add_argument( + "--from-file", + type=str, + help="if specified, load prompts from this file", + ) + parser.add_argument( + "--config", + type=str, + default="configs/stable-diffusion/v2-inference.yaml", + help="path to config which constructs model", + ) + parser.add_argument( + "--ckpt", + type=str, + help="path to checkpoint of model", + ) + parser.add_argument( + "--seed", + type=int, + default=42, + help="the seed (for reproducible sampling)", + ) + parser.add_argument( + "--precision", + type=str, + help="evaluate at this precision", + choices=["full", "autocast"], + default="autocast" + ) + + opt = parser.parse_args() + seed_everything(opt.seed) + + config = OmegaConf.load(f"{opt.config}") + model = load_model_from_config(config, f"{opt.ckpt}") + + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + model = model.to(device) + + sampler = DDIMSampler(model) + + os.makedirs(opt.outdir, exist_ok=True) + outpath = opt.outdir + + print("Creating invisible watermark encoder (see https://github.com/ShieldMnt/invisible-watermark)...") + wm = "SDV2" + wm_encoder = WatermarkEncoder() + wm_encoder.set_watermark('bytes', wm.encode('utf-8')) + + batch_size = opt.n_samples + n_rows = opt.n_rows if opt.n_rows > 0 else batch_size + if not opt.from_file: + prompt = opt.prompt + assert prompt is not None + data = [batch_size * [prompt]] + + else: + print(f"reading prompts from {opt.from_file}") + with open(opt.from_file, "r") as f: + data = f.read().splitlines() + data = list(chunk(data, batch_size)) + + sample_path = os.path.join(outpath, "samples") + os.makedirs(sample_path, exist_ok=True) + base_count = len(os.listdir(sample_path)) + grid_count = len(os.listdir(outpath)) - 1 + + assert os.path.isfile(opt.init_img) + init_image = load_img(opt.init_img).to(device) + init_image = repeat(init_image, '1 ... -> b ...', b=batch_size) + init_latent = model.get_first_stage_encoding(model.encode_first_stage(init_image)) # move to latent space + + sampler.make_schedule(ddim_num_steps=opt.ddim_steps, ddim_eta=opt.ddim_eta, verbose=False) + + assert 0. <= opt.strength <= 1., 'can only work with strength in [0.0, 1.0]' + t_enc = int(opt.strength * opt.ddim_steps) + print(f"target t_enc is {t_enc} steps") + + precision_scope = autocast if opt.precision == "autocast" else nullcontext + with torch.no_grad(): + with precision_scope("cuda"): + with model.ema_scope(): + all_samples = list() + for n in trange(opt.n_iter, desc="Sampling"): + for prompts in tqdm(data, desc="data"): + uc = None + if opt.scale != 1.0: + uc = model.get_learned_conditioning(batch_size * [""]) + if isinstance(prompts, tuple): + prompts = list(prompts) + c = model.get_learned_conditioning(prompts) + + # encode (scaled latent) + z_enc = sampler.stochastic_encode(init_latent, torch.tensor([t_enc] * batch_size).to(device)) + # decode it + samples = sampler.decode(z_enc, c, t_enc, unconditional_guidance_scale=opt.scale, + unconditional_conditioning=uc, ) + + x_samples = model.decode_first_stage(samples) + x_samples = torch.clamp((x_samples + 1.0) / 2.0, min=0.0, max=1.0) + + for x_sample in x_samples: + x_sample = 255. * rearrange(x_sample.cpu().numpy(), 'c h w -> h w c') + img = Image.fromarray(x_sample.astype(np.uint8)) + img = put_watermark(img, wm_encoder) + img.save(os.path.join(sample_path, f"{base_count:05}.png")) + base_count += 1 + all_samples.append(x_samples) + + # additionally, save as grid + grid = torch.stack(all_samples, 0) + grid = rearrange(grid, 'n b c h w -> (n b) c h w') + grid = make_grid(grid, nrow=n_rows) + + # to image + grid = 255. * rearrange(grid, 'c h w -> h w c').cpu().numpy() + grid = Image.fromarray(grid.astype(np.uint8)) + grid = put_watermark(grid, wm_encoder) + grid.save(os.path.join(outpath, f'grid-{grid_count:04}.png')) + grid_count += 1 + + print(f"Your samples are ready and waiting for you here: \n{outpath} \nEnjoy.") + + +if __name__ == "__main__": + main() diff --git a/sd/stablediffusion/scripts/streamlit/depth2img.py b/sd/stablediffusion/scripts/streamlit/depth2img.py new file mode 100644 index 0000000000000000000000000000000000000000..7f80223405a26ded02964513293b8b3316c34344 --- /dev/null +++ b/sd/stablediffusion/scripts/streamlit/depth2img.py @@ -0,0 +1,157 @@ +import sys +import torch +import numpy as np +import streamlit as st +from PIL import Image +from omegaconf import OmegaConf +from einops import repeat, rearrange +from pytorch_lightning import seed_everything +from imwatermark import WatermarkEncoder + +from scripts.txt2img import put_watermark +from ldm.util import instantiate_from_config +from ldm.models.diffusion.ddim import DDIMSampler +from ldm.data.util import AddMiDaS + +torch.set_grad_enabled(False) + + +@st.cache(allow_output_mutation=True) +def initialize_model(config, ckpt): + config = OmegaConf.load(config) + model = instantiate_from_config(config.model) + model.load_state_dict(torch.load(ckpt)["state_dict"], strict=False) + + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + model = model.to(device) + sampler = DDIMSampler(model) + return sampler + + +def make_batch_sd( + image, + txt, + device, + num_samples=1, + model_type="dpt_hybrid" +): + image = np.array(image.convert("RGB")) + image = torch.from_numpy(image).to(dtype=torch.float32) / 127.5 - 1.0 + # sample['jpg'] is tensor hwc in [-1, 1] at this point + midas_trafo = AddMiDaS(model_type=model_type) + batch = { + "jpg": image, + "txt": num_samples * [txt], + } + batch = midas_trafo(batch) + batch["jpg"] = rearrange(batch["jpg"], 'h w c -> 1 c h w') + batch["jpg"] = repeat(batch["jpg"].to(device=device), "1 ... -> n ...", n=num_samples) + batch["midas_in"] = repeat(torch.from_numpy(batch["midas_in"][None, ...]).to(device=device), "1 ... -> n ...", n=num_samples) + return batch + + +def paint(sampler, image, prompt, t_enc, seed, scale, num_samples=1, callback=None, + do_full_sample=False): + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + model = sampler.model + seed_everything(seed) + + print("Creating invisible watermark encoder (see https://github.com/ShieldMnt/invisible-watermark)...") + wm = "SDV2" + wm_encoder = WatermarkEncoder() + wm_encoder.set_watermark('bytes', wm.encode('utf-8')) + + with torch.no_grad(),\ + torch.autocast("cuda"): + batch = make_batch_sd(image, txt=prompt, device=device, num_samples=num_samples) + z = model.get_first_stage_encoding(model.encode_first_stage(batch[model.first_stage_key])) # move to latent space + c = model.cond_stage_model.encode(batch["txt"]) + c_cat = list() + for ck in model.concat_keys: + cc = batch[ck] + cc = model.depth_model(cc) + depth_min, depth_max = torch.amin(cc, dim=[1, 2, 3], keepdim=True), torch.amax(cc, dim=[1, 2, 3], + keepdim=True) + display_depth = (cc - depth_min) / (depth_max - depth_min) + st.image(Image.fromarray((display_depth[0, 0, ...].cpu().numpy() * 255.).astype(np.uint8))) + cc = torch.nn.functional.interpolate( + cc, + size=z.shape[2:], + mode="bicubic", + align_corners=False, + ) + depth_min, depth_max = torch.amin(cc, dim=[1, 2, 3], keepdim=True), torch.amax(cc, dim=[1, 2, 3], + keepdim=True) + cc = 2. * (cc - depth_min) / (depth_max - depth_min) - 1. + c_cat.append(cc) + c_cat = torch.cat(c_cat, dim=1) + # cond + cond = {"c_concat": [c_cat], "c_crossattn": [c]} + + # uncond cond + uc_cross = model.get_unconditional_conditioning(num_samples, "") + uc_full = {"c_concat": [c_cat], "c_crossattn": [uc_cross]} + if not do_full_sample: + # encode (scaled latent) + z_enc = sampler.stochastic_encode(z, torch.tensor([t_enc] * num_samples).to(model.device)) + else: + z_enc = torch.randn_like(z) + # decode it + samples = sampler.decode(z_enc, cond, t_enc, unconditional_guidance_scale=scale, + unconditional_conditioning=uc_full, callback=callback) + x_samples_ddim = model.decode_first_stage(samples) + result = torch.clamp((x_samples_ddim + 1.0) / 2.0, min=0.0, max=1.0) + result = result.cpu().numpy().transpose(0, 2, 3, 1) * 255 + return [put_watermark(Image.fromarray(img.astype(np.uint8)), wm_encoder) for img in result] + + +def run(): + st.title("Stable Diffusion Depth2Img") + # run via streamlit run scripts/demo/depth2img.py + sampler = initialize_model(sys.argv[1], sys.argv[2]) + + image = st.file_uploader("Image", ["jpg", "png"]) + if image: + image = Image.open(image) + w, h = image.size + st.text(f"loaded input image of size ({w}, {h})") + width, height = map(lambda x: x - x % 64, (w, h)) # resize to integer multiple of 64 + image = image.resize((width, height)) + st.text(f"resized input image to size ({width}, {height} (w, h))") + st.image(image) + + prompt = st.text_input("Prompt") + + seed = st.number_input("Seed", min_value=0, max_value=1000000, value=0) + num_samples = st.number_input("Number of Samples", min_value=1, max_value=64, value=1) + scale = st.slider("Scale", min_value=0.1, max_value=30.0, value=9.0, step=0.1) + steps = st.slider("DDIM Steps", min_value=0, max_value=50, value=50, step=1) + strength = st.slider("Strength", min_value=0., max_value=1., value=0.9) + + t_progress = st.progress(0) + def t_callback(t): + t_progress.progress(min((t + 1) / t_enc, 1.)) + + assert 0. <= strength <= 1., 'can only work with strength in [0.0, 1.0]' + do_full_sample = strength == 1. + t_enc = min(int(strength * steps), steps-1) + sampler.make_schedule(steps, ddim_eta=0., verbose=True) + if st.button("Sample"): + result = paint( + sampler=sampler, + image=image, + prompt=prompt, + t_enc=t_enc, + seed=seed, + scale=scale, + num_samples=num_samples, + callback=t_callback, + do_full_sample=do_full_sample, + ) + st.write("Result") + for image in result: + st.image(image, output_format='PNG') + + +if __name__ == "__main__": + run() diff --git a/sd/stablediffusion/scripts/streamlit/inpainting.py b/sd/stablediffusion/scripts/streamlit/inpainting.py new file mode 100644 index 0000000000000000000000000000000000000000..c35772f063da8bdf0091e3931dbe12b1869dd11a --- /dev/null +++ b/sd/stablediffusion/scripts/streamlit/inpainting.py @@ -0,0 +1,195 @@ +import sys +import cv2 +import torch +import numpy as np +import streamlit as st +from PIL import Image +from omegaconf import OmegaConf +from einops import repeat +from streamlit_drawable_canvas import st_canvas +from imwatermark import WatermarkEncoder + +from ldm.models.diffusion.ddim import DDIMSampler +from ldm.util import instantiate_from_config + + +torch.set_grad_enabled(False) + + +def put_watermark(img, wm_encoder=None): + if wm_encoder is not None: + img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) + img = wm_encoder.encode(img, 'dwtDct') + img = Image.fromarray(img[:, :, ::-1]) + return img + + +@st.cache(allow_output_mutation=True) +def initialize_model(config, ckpt): + config = OmegaConf.load(config) + model = instantiate_from_config(config.model) + + model.load_state_dict(torch.load(ckpt)["state_dict"], strict=False) + + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + model = model.to(device) + sampler = DDIMSampler(model) + + return sampler + + +def make_batch_sd( + image, + mask, + txt, + device, + num_samples=1): + image = np.array(image.convert("RGB")) + image = image[None].transpose(0, 3, 1, 2) + image = torch.from_numpy(image).to(dtype=torch.float32) / 127.5 - 1.0 + + mask = np.array(mask.convert("L")) + mask = mask.astype(np.float32) / 255.0 + mask = mask[None, None] + mask[mask < 0.5] = 0 + mask[mask >= 0.5] = 1 + mask = torch.from_numpy(mask) + + masked_image = image * (mask < 0.5) + + batch = { + "image": repeat(image.to(device=device), "1 ... -> n ...", n=num_samples), + "txt": num_samples * [txt], + "mask": repeat(mask.to(device=device), "1 ... -> n ...", n=num_samples), + "masked_image": repeat(masked_image.to(device=device), "1 ... -> n ...", n=num_samples), + } + return batch + + +def inpaint(sampler, image, mask, prompt, seed, scale, ddim_steps, num_samples=1, w=512, h=512, eta=1.): + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + model = sampler.model + + print("Creating invisible watermark encoder (see https://github.com/ShieldMnt/invisible-watermark)...") + wm = "SDV2" + wm_encoder = WatermarkEncoder() + wm_encoder.set_watermark('bytes', wm.encode('utf-8')) + + prng = np.random.RandomState(seed) + start_code = prng.randn(num_samples, 4, h // 8, w // 8) + start_code = torch.from_numpy(start_code).to(device=device, dtype=torch.float32) + + with torch.no_grad(), \ + torch.autocast("cuda"): + batch = make_batch_sd(image, mask, txt=prompt, device=device, num_samples=num_samples) + + c = model.cond_stage_model.encode(batch["txt"]) + + c_cat = list() + for ck in model.concat_keys: + cc = batch[ck].float() + if ck != model.masked_image_key: + bchw = [num_samples, 4, h // 8, w // 8] + cc = torch.nn.functional.interpolate(cc, size=bchw[-2:]) + else: + cc = model.get_first_stage_encoding(model.encode_first_stage(cc)) + c_cat.append(cc) + c_cat = torch.cat(c_cat, dim=1) + + # cond + cond = {"c_concat": [c_cat], "c_crossattn": [c]} + + # uncond cond + uc_cross = model.get_unconditional_conditioning(num_samples, "") + uc_full = {"c_concat": [c_cat], "c_crossattn": [uc_cross]} + + shape = [model.channels, h // 8, w // 8] + samples_cfg, intermediates = sampler.sample( + ddim_steps, + num_samples, + shape, + cond, + verbose=False, + eta=eta, + unconditional_guidance_scale=scale, + unconditional_conditioning=uc_full, + x_T=start_code, + ) + x_samples_ddim = model.decode_first_stage(samples_cfg) + + result = torch.clamp((x_samples_ddim + 1.0) / 2.0, + min=0.0, max=1.0) + + result = result.cpu().numpy().transpose(0, 2, 3, 1) * 255 + return [put_watermark(Image.fromarray(img.astype(np.uint8)), wm_encoder) for img in result] + + +def run(): + st.title("Stable Diffusion Inpainting") + + sampler = initialize_model(sys.argv[1], sys.argv[2]) + + image = st.file_uploader("Image", ["jpg", "png"]) + if image: + image = Image.open(image) + w, h = image.size + print(f"loaded input image of size ({w}, {h})") + width, height = map(lambda x: x - x % 64, (w, h)) # resize to integer multiple of 32 + image = image.resize((width, height)) + + prompt = st.text_input("Prompt") + + seed = st.number_input("Seed", min_value=0, max_value=1000000, value=0) + num_samples = st.number_input("Number of Samples", min_value=1, max_value=64, value=1) + scale = st.slider("Scale", min_value=0.1, max_value=30.0, value=10., step=0.1) + ddim_steps = st.slider("DDIM Steps", min_value=0, max_value=50, value=50, step=1) + eta = st.sidebar.number_input("eta (DDIM)", value=0., min_value=0., max_value=1.) + + fill_color = "rgba(255, 255, 255, 0.0)" + stroke_width = st.number_input("Brush Size", + value=64, + min_value=1, + max_value=100) + stroke_color = "rgba(255, 255, 255, 1.0)" + bg_color = "rgba(0, 0, 0, 1.0)" + drawing_mode = "freedraw" + + st.write("Canvas") + st.caption( + "Draw a mask to inpaint, then click the 'Send to Streamlit' button (bottom left, with an arrow on it).") + canvas_result = st_canvas( + fill_color=fill_color, + stroke_width=stroke_width, + stroke_color=stroke_color, + background_color=bg_color, + background_image=image, + update_streamlit=False, + height=height, + width=width, + drawing_mode=drawing_mode, + key="canvas", + ) + if canvas_result: + mask = canvas_result.image_data + mask = mask[:, :, -1] > 0 + if mask.sum() > 0: + mask = Image.fromarray(mask) + + result = inpaint( + sampler=sampler, + image=image, + mask=mask, + prompt=prompt, + seed=seed, + scale=scale, + ddim_steps=ddim_steps, + num_samples=num_samples, + h=height, w=width, eta=eta + ) + st.write("Inpainted") + for image in result: + st.image(image, output_format='PNG') + + +if __name__ == "__main__": + run() \ No newline at end of file diff --git a/sd/stablediffusion/scripts/streamlit/superresolution.py b/sd/stablediffusion/scripts/streamlit/superresolution.py new file mode 100644 index 0000000000000000000000000000000000000000..c1172b02ea8141781118d4536ba28de0f24404a1 --- /dev/null +++ b/sd/stablediffusion/scripts/streamlit/superresolution.py @@ -0,0 +1,170 @@ +import sys +import torch +import numpy as np +import streamlit as st +from PIL import Image +from omegaconf import OmegaConf +from einops import repeat, rearrange +from pytorch_lightning import seed_everything +from imwatermark import WatermarkEncoder + +from scripts.txt2img import put_watermark +from ldm.models.diffusion.ddim import DDIMSampler +from ldm.models.diffusion.ddpm import LatentUpscaleDiffusion, LatentUpscaleFinetuneDiffusion +from ldm.util import exists, instantiate_from_config + + +torch.set_grad_enabled(False) + + +@st.cache(allow_output_mutation=True) +def initialize_model(config, ckpt): + config = OmegaConf.load(config) + model = instantiate_from_config(config.model) + model.load_state_dict(torch.load(ckpt)["state_dict"], strict=False) + + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + model = model.to(device) + sampler = DDIMSampler(model) + return sampler + + +def make_batch_sd( + image, + txt, + device, + num_samples=1, +): + image = np.array(image.convert("RGB")) + image = torch.from_numpy(image).to(dtype=torch.float32) / 127.5 - 1.0 + batch = { + "lr": rearrange(image, 'h w c -> 1 c h w'), + "txt": num_samples * [txt], + } + batch["lr"] = repeat(batch["lr"].to(device=device), "1 ... -> n ...", n=num_samples) + return batch + + +def make_noise_augmentation(model, batch, noise_level=None): + x_low = batch[model.low_scale_key] + x_low = x_low.to(memory_format=torch.contiguous_format).float() + x_aug, noise_level = model.low_scale_model(x_low, noise_level) + return x_aug, noise_level + + +def paint(sampler, image, prompt, seed, scale, h, w, steps, num_samples=1, callback=None, eta=0., noise_level=None): + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + model = sampler.model + seed_everything(seed) + prng = np.random.RandomState(seed) + start_code = prng.randn(num_samples, model.channels, h , w) + start_code = torch.from_numpy(start_code).to(device=device, dtype=torch.float32) + + print("Creating invisible watermark encoder (see https://github.com/ShieldMnt/invisible-watermark)...") + wm = "SDV2" + wm_encoder = WatermarkEncoder() + wm_encoder.set_watermark('bytes', wm.encode('utf-8')) + with torch.no_grad(),\ + torch.autocast("cuda"): + batch = make_batch_sd(image, txt=prompt, device=device, num_samples=num_samples) + c = model.cond_stage_model.encode(batch["txt"]) + c_cat = list() + if isinstance(model, LatentUpscaleFinetuneDiffusion): + for ck in model.concat_keys: + cc = batch[ck] + if exists(model.reshuffle_patch_size): + assert isinstance(model.reshuffle_patch_size, int) + cc = rearrange(cc, 'b c (p1 h) (p2 w) -> b (p1 p2 c) h w', + p1=model.reshuffle_patch_size, p2=model.reshuffle_patch_size) + c_cat.append(cc) + c_cat = torch.cat(c_cat, dim=1) + # cond + cond = {"c_concat": [c_cat], "c_crossattn": [c]} + # uncond cond + uc_cross = model.get_unconditional_conditioning(num_samples, "") + uc_full = {"c_concat": [c_cat], "c_crossattn": [uc_cross]} + elif isinstance(model, LatentUpscaleDiffusion): + x_augment, noise_level = make_noise_augmentation(model, batch, noise_level) + cond = {"c_concat": [x_augment], "c_crossattn": [c], "c_adm": noise_level} + # uncond cond + uc_cross = model.get_unconditional_conditioning(num_samples, "") + uc_full = {"c_concat": [x_augment], "c_crossattn": [uc_cross], "c_adm": noise_level} + else: + raise NotImplementedError() + + shape = [model.channels, h, w] + samples, intermediates = sampler.sample( + steps, + num_samples, + shape, + cond, + verbose=False, + eta=eta, + unconditional_guidance_scale=scale, + unconditional_conditioning=uc_full, + x_T=start_code, + callback=callback + ) + with torch.no_grad(): + x_samples_ddim = model.decode_first_stage(samples) + result = torch.clamp((x_samples_ddim + 1.0) / 2.0, min=0.0, max=1.0) + result = result.cpu().numpy().transpose(0, 2, 3, 1) * 255 + st.text(f"upscaled image shape: {result.shape}") + return [put_watermark(Image.fromarray(img.astype(np.uint8)), wm_encoder) for img in result] + + +def run(): + st.title("Stable Diffusion Upscaling") + # run via streamlit run scripts/demo/depth2img.py + sampler = initialize_model(sys.argv[1], sys.argv[2]) + + image = st.file_uploader("Image", ["jpg", "png"]) + if image: + image = Image.open(image) + w, h = image.size + st.text(f"loaded input image of size ({w}, {h})") + width, height = map(lambda x: x - x % 64, (w, h)) # resize to integer multiple of 64 + image = image.resize((width, height)) + st.text(f"resized input image to size ({width}, {height} (w, h))") + st.image(image) + + st.write(f"\n Tip: Add a description of the object that should be upscaled, e.g.: 'a professional photograph of a cat'") + prompt = st.text_input("Prompt", "a high quality professional photograph") + + seed = st.number_input("Seed", min_value=0, max_value=1000000, value=0) + num_samples = st.number_input("Number of Samples", min_value=1, max_value=64, value=1) + scale = st.slider("Scale", min_value=0.1, max_value=30.0, value=9.0, step=0.1) + steps = st.slider("DDIM Steps", min_value=2, max_value=250, value=50, step=1) + eta = st.sidebar.number_input("eta (DDIM)", value=0., min_value=0., max_value=1.) + + noise_level = None + if isinstance(sampler.model, LatentUpscaleDiffusion): + # TODO: make this work for all models + noise_level = st.sidebar.number_input("Noise Augmentation", min_value=0, max_value=350, value=20) + noise_level = torch.Tensor(num_samples * [noise_level]).to(sampler.model.device).long() + + t_progress = st.progress(0) + def t_callback(t): + t_progress.progress(min((t + 1) / steps, 1.)) + + sampler.make_schedule(steps, ddim_eta=eta, verbose=True) + if st.button("Sample"): + result = paint( + sampler=sampler, + image=image, + prompt=prompt, + seed=seed, + scale=scale, + h=height, w=width, steps=steps, + num_samples=num_samples, + callback=t_callback, + noise_level=noise_level, + eta=eta + ) + st.write("Result") + for image in result: + st.image(image, output_format='PNG') + + +if __name__ == "__main__": + run() diff --git a/sd/stablediffusion/scripts/tests/test_watermark.py b/sd/stablediffusion/scripts/tests/test_watermark.py new file mode 100644 index 0000000000000000000000000000000000000000..f93f8a6e70763c0e284157bc8225827520b2f5ef --- /dev/null +++ b/sd/stablediffusion/scripts/tests/test_watermark.py @@ -0,0 +1,18 @@ +import cv2 +import fire +from imwatermark import WatermarkDecoder + + +def testit(img_path): + bgr = cv2.imread(img_path) + decoder = WatermarkDecoder('bytes', 136) + watermark = decoder.decode(bgr, 'dwtDct') + try: + dec = watermark.decode('utf-8') + except: + dec = "null" + print(dec) + + +if __name__ == "__main__": + fire.Fire(testit) \ No newline at end of file diff --git a/sd/stablediffusion/scripts/txt2img.py b/sd/stablediffusion/scripts/txt2img.py new file mode 100644 index 0000000000000000000000000000000000000000..1ed42a3cd87347998e947362e8845f28bf580fdd --- /dev/null +++ b/sd/stablediffusion/scripts/txt2img.py @@ -0,0 +1,289 @@ +import argparse, os +import cv2 +import torch +import numpy as np +from omegaconf import OmegaConf +from PIL import Image +from tqdm import tqdm, trange +from itertools import islice +from einops import rearrange +from torchvision.utils import make_grid +from pytorch_lightning import seed_everything +from torch import autocast +from contextlib import nullcontext +from imwatermark import WatermarkEncoder + +from ldm.util import instantiate_from_config +from ldm.models.diffusion.ddim import DDIMSampler +from ldm.models.diffusion.plms import PLMSSampler +from ldm.models.diffusion.dpm_solver import DPMSolverSampler + +torch.set_grad_enabled(False) + +def chunk(it, size): + it = iter(it) + return iter(lambda: tuple(islice(it, size)), ()) + + +def load_model_from_config(config, ckpt, verbose=False): + print(f"Loading model from {ckpt}") + pl_sd = torch.load(ckpt, map_location="cpu") + if "global_step" in pl_sd: + print(f"Global Step: {pl_sd['global_step']}") + sd = pl_sd["state_dict"] + model = instantiate_from_config(config.model) + m, u = model.load_state_dict(sd, strict=False) + if len(m) > 0 and verbose: + print("missing keys:") + print(m) + if len(u) > 0 and verbose: + print("unexpected keys:") + print(u) + + model.cuda() + model.eval() + return model + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--prompt", + type=str, + nargs="?", + default="a professional photograph of an astronaut riding a triceratops", + help="the prompt to render" + ) + parser.add_argument( + "--outdir", + type=str, + nargs="?", + help="dir to write results to", + default="outputs/txt2img-samples" + ) + parser.add_argument( + "--steps", + type=int, + default=50, + help="number of ddim sampling steps", + ) + parser.add_argument( + "--plms", + action='store_true', + help="use plms sampling", + ) + parser.add_argument( + "--dpm", + action='store_true', + help="use DPM (2) sampler", + ) + parser.add_argument( + "--fixed_code", + action='store_true', + help="if enabled, uses the same starting code across all samples ", + ) + parser.add_argument( + "--ddim_eta", + type=float, + default=0.0, + help="ddim eta (eta=0.0 corresponds to deterministic sampling", + ) + parser.add_argument( + "--n_iter", + type=int, + default=3, + help="sample this often", + ) + parser.add_argument( + "--H", + type=int, + default=512, + help="image height, in pixel space", + ) + parser.add_argument( + "--W", + type=int, + default=512, + help="image width, in pixel space", + ) + parser.add_argument( + "--C", + type=int, + default=4, + help="latent channels", + ) + parser.add_argument( + "--f", + type=int, + default=8, + help="downsampling factor, most often 8 or 16", + ) + parser.add_argument( + "--n_samples", + type=int, + default=3, + help="how many samples to produce for each given prompt. A.k.a batch size", + ) + parser.add_argument( + "--n_rows", + type=int, + default=0, + help="rows in the grid (default: n_samples)", + ) + parser.add_argument( + "--scale", + type=float, + default=9.0, + help="unconditional guidance scale: eps = eps(x, empty) + scale * (eps(x, cond) - eps(x, empty))", + ) + parser.add_argument( + "--from-file", + type=str, + help="if specified, load prompts from this file, separated by newlines", + ) + parser.add_argument( + "--config", + type=str, + default="configs/stable-diffusion/v2-inference.yaml", + help="path to config which constructs model", + ) + parser.add_argument( + "--ckpt", + type=str, + help="path to checkpoint of model", + ) + parser.add_argument( + "--seed", + type=int, + default=42, + help="the seed (for reproducible sampling)", + ) + parser.add_argument( + "--precision", + type=str, + help="evaluate at this precision", + choices=["full", "autocast"], + default="autocast" + ) + parser.add_argument( + "--repeat", + type=int, + default=1, + help="repeat each prompt in file this often", + ) + opt = parser.parse_args() + return opt + + +def put_watermark(img, wm_encoder=None): + if wm_encoder is not None: + img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) + img = wm_encoder.encode(img, 'dwtDct') + img = Image.fromarray(img[:, :, ::-1]) + return img + + +def main(opt): + seed_everything(opt.seed) + + config = OmegaConf.load(f"{opt.config}") + model = load_model_from_config(config, f"{opt.ckpt}") + + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + model = model.to(device) + + if opt.plms: + sampler = PLMSSampler(model) + elif opt.dpm: + sampler = DPMSolverSampler(model) + else: + sampler = DDIMSampler(model) + + os.makedirs(opt.outdir, exist_ok=True) + outpath = opt.outdir + + print("Creating invisible watermark encoder (see https://github.com/ShieldMnt/invisible-watermark)...") + wm = "SDV2" + wm_encoder = WatermarkEncoder() + wm_encoder.set_watermark('bytes', wm.encode('utf-8')) + + batch_size = opt.n_samples + n_rows = opt.n_rows if opt.n_rows > 0 else batch_size + if not opt.from_file: + prompt = opt.prompt + assert prompt is not None + data = [batch_size * [prompt]] + + else: + print(f"reading prompts from {opt.from_file}") + with open(opt.from_file, "r") as f: + data = f.read().splitlines() + data = [p for p in data for i in range(opt.repeat)] + data = list(chunk(data, batch_size)) + + sample_path = os.path.join(outpath, "samples") + os.makedirs(sample_path, exist_ok=True) + sample_count = 0 + base_count = len(os.listdir(sample_path)) + grid_count = len(os.listdir(outpath)) - 1 + + start_code = None + if opt.fixed_code: + start_code = torch.randn([opt.n_samples, opt.C, opt.H // opt.f, opt.W // opt.f], device=device) + + precision_scope = autocast if opt.precision == "autocast" else nullcontext + with torch.no_grad(), \ + precision_scope("cuda"), \ + model.ema_scope(): + all_samples = list() + for n in trange(opt.n_iter, desc="Sampling"): + for prompts in tqdm(data, desc="data"): + uc = None + if opt.scale != 1.0: + uc = model.get_learned_conditioning(batch_size * [""]) + if isinstance(prompts, tuple): + prompts = list(prompts) + c = model.get_learned_conditioning(prompts) + shape = [opt.C, opt.H // opt.f, opt.W // opt.f] + samples, _ = sampler.sample(S=opt.steps, + conditioning=c, + batch_size=opt.n_samples, + shape=shape, + verbose=False, + unconditional_guidance_scale=opt.scale, + unconditional_conditioning=uc, + eta=opt.ddim_eta, + x_T=start_code) + + x_samples = model.decode_first_stage(samples) + x_samples = torch.clamp((x_samples + 1.0) / 2.0, min=0.0, max=1.0) + + for x_sample in x_samples: + x_sample = 255. * rearrange(x_sample.cpu().numpy(), 'c h w -> h w c') + img = Image.fromarray(x_sample.astype(np.uint8)) + img = put_watermark(img, wm_encoder) + img.save(os.path.join(sample_path, f"{base_count:05}.png")) + base_count += 1 + sample_count += 1 + + all_samples.append(x_samples) + + # additionally, save as grid + grid = torch.stack(all_samples, 0) + grid = rearrange(grid, 'n b c h w -> (n b) c h w') + grid = make_grid(grid, nrow=n_rows) + + # to image + grid = 255. * rearrange(grid, 'c h w -> h w c').cpu().numpy() + grid = Image.fromarray(grid.astype(np.uint8)) + grid = put_watermark(grid, wm_encoder) + grid.save(os.path.join(outpath, f'grid-{grid_count:04}.png')) + grid_count += 1 + + print(f"Your samples are ready and waiting for you here: \n{outpath} \n" + f" \nEnjoy.") + + +if __name__ == "__main__": + opt = parse_args() + main(opt) diff --git a/sd/stablediffusion/setup.py b/sd/stablediffusion/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..00f5b4d874f0f19ece54fac2dd50b39774b86c5b --- /dev/null +++ b/sd/stablediffusion/setup.py @@ -0,0 +1,13 @@ +from setuptools import setup, find_packages + +setup( + name='stable-diffusion', + version='0.0.1', + description='', + packages=find_packages(), + install_requires=[ + 'torch', + 'numpy', + 'tqdm', + ], +) \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/BLIP.gif b/sd/stablediffusion/src/blip/BLIP.gif new file mode 100644 index 0000000000000000000000000000000000000000..f97959778a4d3a9c1d5c06793c96d96204fe2081 --- /dev/null +++ b/sd/stablediffusion/src/blip/BLIP.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7757a1a1133807158ec4e696a8187f289e64c30a86aa470d8e0a93948a02be22 +size 6707660 diff --git a/sd/stablediffusion/src/blip/CODEOWNERS b/sd/stablediffusion/src/blip/CODEOWNERS new file mode 100644 index 0000000000000000000000000000000000000000..522fa4a0f715cd0328b9b9dbacae00e060193f43 --- /dev/null +++ b/sd/stablediffusion/src/blip/CODEOWNERS @@ -0,0 +1,2 @@ +# Comment line immediately above ownership line is reserved for related gus information. Please be careful while editing. +#ECCN:Open Source diff --git a/sd/stablediffusion/src/blip/CODE_OF_CONDUCT.md b/sd/stablediffusion/src/blip/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000000000000000000000000000000000..b6724718c9512d730bb7f1bcc5848cd420241407 --- /dev/null +++ b/sd/stablediffusion/src/blip/CODE_OF_CONDUCT.md @@ -0,0 +1,105 @@ +# Salesforce Open Source Community Code of Conduct + +## About the Code of Conduct + +Equality is a core value at Salesforce. We believe a diverse and inclusive +community fosters innovation and creativity, and are committed to building a +culture where everyone feels included. + +Salesforce open-source projects are committed to providing a friendly, safe, and +welcoming environment for all, regardless of gender identity and expression, +sexual orientation, disability, physical appearance, body size, ethnicity, nationality, +race, age, religion, level of experience, education, socioeconomic status, or +other similar personal characteristics. + +The goal of this code of conduct is to specify a baseline standard of behavior so +that people with different social values and communication styles can work +together effectively, productively, and respectfully in our open source community. +It also establishes a mechanism for reporting issues and resolving conflicts. + +All questions and reports of abusive, harassing, or otherwise unacceptable behavior +in a Salesforce open-source project may be reported by contacting the Salesforce +Open Source Conduct Committee at ossconduct@salesforce.com. + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of gender +identity and expression, sexual orientation, disability, physical appearance, +body size, ethnicity, nationality, race, age, religion, level of experience, education, +socioeconomic status, or other similar personal characteristics. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy toward other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Personal attacks, insulting/derogatory comments, or trolling +* Public or private harassment +* Publishing, or threatening to publish, others' private information—such as +a physical or electronic address—without explicit permission +* Other conduct which could reasonably be considered inappropriate in a +professional setting +* Advocating for or encouraging any of the above behaviors + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned with this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project email +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the Salesforce Open Source Conduct Committee +at ossconduct@salesforce.com. All complaints will be reviewed and investigated +and will result in a response that is deemed necessary and appropriate to the +circumstances. The committee is obligated to maintain confidentiality with +regard to the reporter of an incident. Further details of specific enforcement +policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership and the Salesforce Open Source Conduct +Committee. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant-home], +version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html. +It includes adaptions and additions from [Go Community Code of Conduct][golang-coc], +[CNCF Code of Conduct][cncf-coc], and [Microsoft Open Source Code of Conduct][microsoft-coc]. + +This Code of Conduct is licensed under the [Creative Commons Attribution 3.0 License][cc-by-3-us]. + +[contributor-covenant-home]: https://www.contributor-covenant.org (https://www.contributor-covenant.org/) +[golang-coc]: https://golang.org/conduct +[cncf-coc]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md +[microsoft-coc]: https://opensource.microsoft.com/codeofconduct/ +[cc-by-3-us]: https://creativecommons.org/licenses/by/3.0/us/ diff --git a/sd/stablediffusion/src/blip/LICENSE.txt b/sd/stablediffusion/src/blip/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..a63e87f4e1e90c96861648a16a7304d97d3c3f7b --- /dev/null +++ b/sd/stablediffusion/src/blip/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) 2022, Salesforce.com, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/sd/stablediffusion/src/blip/README.md b/sd/stablediffusion/src/blip/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7923e2119361be467ef4da36ad167cdcc1344e08 --- /dev/null +++ b/sd/stablediffusion/src/blip/README.md @@ -0,0 +1,116 @@ +## BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation + +## Announcement: BLIP is now officially integrated into [LAVIS](https://github.com/salesforce/LAVIS) - a one-stop library for language-and-vision research and applications! + + + +This is the PyTorch code of the BLIP paper [[blog](https://blog.salesforceairesearch.com/blip-bootstrapping-language-image-pretraining/)]. The code has been tested on PyTorch 1.10. +To install the dependencies, run
    pip install -r requirements.txt
    + +Catalog: +- [x] Inference demo +- [x] Pre-trained and finetuned checkpoints +- [x] Finetuning code for Image-Text Retrieval, Image Captioning, VQA, and NLVR2 +- [x] Pre-training code +- [x] Zero-shot video-text retrieval +- [x] Download of bootstrapped pre-training datasets + + +### Inference demo: +Run our interactive demo using [Colab notebook](https://colab.research.google.com/github/salesforce/BLIP/blob/main/demo.ipynb) (no GPU needed). +The demo includes code for: +1. Image captioning +2. Open-ended visual question answering +3. Multimodal / unimodal feature extraction +4. Image-text matching + +Try out the [Web demo](https://huggingface.co/spaces/Salesforce/BLIP), integrated into [Huggingface Spaces 🤗](https://huggingface.co/spaces) using [Gradio](https://github.com/gradio-app/gradio). + +Replicate web demo and Docker image is also available at [![Replicate](https://replicate.com/salesforce/blip/badge)](https://replicate.com/salesforce/blip) + +### Pre-trained checkpoints: +Num. pre-train images | BLIP w/ ViT-B | BLIP w/ ViT-B and CapFilt-L | BLIP w/ ViT-L +--- | :---: | :---: | :---: +14M | Download| - | - +129M | Download| Download | Download + +### Finetuned checkpoints: +Task | BLIP w/ ViT-B | BLIP w/ ViT-B and CapFilt-L | BLIP w/ ViT-L +--- | :---: | :---: | :---: +Image-Text Retrieval (COCO) | Download| - | Download +Image-Text Retrieval (Flickr30k) | Download| - | Download +Image Captioning (COCO) | - | Download| Download | +VQA | Download| Download | - +NLVR2 | Download| - | - + + +### Image-Text Retrieval: +1. Download COCO and Flickr30k datasets from the original websites, and set 'image_root' in configs/retrieval_{dataset}.yaml accordingly. +2. To evaluate the finetuned BLIP model on COCO, run: +
    python -m torch.distributed.run --nproc_per_node=8 train_retrieval.py \
    +--config ./configs/retrieval_coco.yaml \
    +--output_dir output/retrieval_coco \
    +--evaluate
    +3. To finetune the pre-trained checkpoint using 8 A100 GPUs, first set 'pretrained' in configs/retrieval_coco.yaml as "https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base.pth". Then run: +
    python -m torch.distributed.run --nproc_per_node=8 train_retrieval.py \
    +--config ./configs/retrieval_coco.yaml \
    +--output_dir output/retrieval_coco 
    + +### Image-Text Captioning: +1. Download COCO and NoCaps datasets from the original websites, and set 'image_root' in configs/caption_coco.yaml and configs/nocaps.yaml accordingly. +2. To evaluate the finetuned BLIP model on COCO, run: +
    python -m torch.distributed.run --nproc_per_node=8 train_caption.py --evaluate
    +3. To evaluate the finetuned BLIP model on NoCaps, generate results with: (evaluation needs to be performed on official server) +
    python -m torch.distributed.run --nproc_per_node=8 eval_nocaps.py 
    +4. To finetune the pre-trained checkpoint using 8 A100 GPUs, first set 'pretrained' in configs/caption_coco.yaml as "https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_capfilt_large.pth". Then run: +
    python -m torch.distributed.run --nproc_per_node=8 train_caption.py 
    + +### VQA: +1. Download VQA v2 dataset and Visual Genome dataset from the original websites, and set 'vqa_root' and 'vg_root' in configs/vqa.yaml. +2. To evaluate the finetuned BLIP model, generate results with: (evaluation needs to be performed on official server) +
    python -m torch.distributed.run --nproc_per_node=8 train_vqa.py --evaluate
    +3. To finetune the pre-trained checkpoint using 16 A100 GPUs, first set 'pretrained' in configs/vqa.yaml as "https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_capfilt_large.pth". Then run: +
    python -m torch.distributed.run --nproc_per_node=16 train_vqa.py 
    + +### NLVR2: +1. Download NLVR2 dataset from the original websites, and set 'image_root' in configs/nlvr.yaml. +2. To evaluate the finetuned BLIP model, run +
    python -m torch.distributed.run --nproc_per_node=8 train_nlvr.py --evaluate
    +3. To finetune the pre-trained checkpoint using 16 A100 GPUs, first set 'pretrained' in configs/nlvr.yaml as "https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base.pth". Then run: +
    python -m torch.distributed.run --nproc_per_node=16 train_nlvr.py 
    + +### Finetune with ViT-L: +In order to finetune a model with ViT-L, simply change the config file to set 'vit' as large. Batch size and learning rate may also need to be adjusted accordingly (please see the paper's appendix for hyper-parameter details). Gradient checkpoint can also be activated in the config file to reduce GPU memory usage. + +### Pre-train: +1. Prepare training json files where each json file contains a list. Each item in the list is a dictonary with two key-value pairs: {'image': path_of_image, 'caption': text_of_image}. +2. In configs/pretrain.yaml, set 'train_file' as the paths for the json files . +3. Pre-train the model using 8 A100 GPUs: +
    python -m torch.distributed.run --nproc_per_node=8 pretrain.py --config ./configs/Pretrain.yaml --output_dir output/Pretrain 
    + +### Zero-shot video-text retrieval: +1. Download MSRVTT dataset following the instructions from https://github.com/salesforce/ALPRO, and set 'video_root' accordingly in configs/retrieval_msrvtt.yaml. +2. Install [decord](https://github.com/dmlc/decord) with
    pip install decord
    +3. To perform zero-shot evaluation, run +
    python -m torch.distributed.run --nproc_per_node=8 eval_retrieval_video.py
    + +### Pre-training datasets download: +We provide bootstrapped pre-training datasets as json files. Each json file contains a list. Each item in the list is a dictonary with two key-value pairs: {'url': url_of_image, 'caption': text_of_image}. + +Image source | Filtered web caption | Filtered synthetic caption by ViT-B | Filtered synthetic caption by ViT-L +--- | :---: | :---: | :---: +CC3M+CC12M+SBU | Download| Download| Download +LAION115M | Download| Download| Download + +### Citation +If you find this code to be useful for your research, please consider citing. +
    +@inproceedings{li2022blip,
    +      title={BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation}, 
    +      author={Junnan Li and Dongxu Li and Caiming Xiong and Steven Hoi},
    +      year={2022},
    +      booktitle={ICML},
    +}
    + +### Acknowledgement +The implementation of BLIP relies on resources from ALBEF, Huggingface Transformers, and timm. We thank the original authors for their open-sourcing. diff --git a/sd/stablediffusion/src/blip/SECURITY.md b/sd/stablediffusion/src/blip/SECURITY.md new file mode 100644 index 0000000000000000000000000000000000000000..8249025739809035264e7776583b2f3ec100553c --- /dev/null +++ b/sd/stablediffusion/src/blip/SECURITY.md @@ -0,0 +1,7 @@ +## Security + +Please report any security issue to [security@salesforce.com](mailto:security@salesforce.com) +as soon as it is discovered. This library limits its runtime dependencies in +order to reduce the total cost of ownership as much as can be, but all consumers +should remain vigilant and have their security stakeholders review all third-party +products (3PP) like this one and their dependencies. diff --git a/sd/stablediffusion/src/blip/cog.yaml b/sd/stablediffusion/src/blip/cog.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c1dfcc430a4cab0fdd2a60a682336219a61c4a4f --- /dev/null +++ b/sd/stablediffusion/src/blip/cog.yaml @@ -0,0 +1,17 @@ +build: + gpu: true + cuda: "11.1" + python_version: "3.8" + system_packages: + - "libgl1-mesa-glx" + - "libglib2.0-0" + python_packages: + - "ipython==7.30.1" + - "torchvision==0.11.1" + - "torch==1.10.0" + - "timm==0.4.12" + - "transformers==4.15.0" + - "fairscale==0.4.4" + - "pycocoevalcap==1.2" + +predict: "predict.py:Predictor" diff --git a/sd/stablediffusion/src/blip/configs/bert_config.json b/sd/stablediffusion/src/blip/configs/bert_config.json new file mode 100644 index 0000000000000000000000000000000000000000..3ef38aabc7f966b53079e9d559dc59e459cc0051 --- /dev/null +++ b/sd/stablediffusion/src/blip/configs/bert_config.json @@ -0,0 +1,21 @@ +{ + "architectures": [ + "BertModel" + ], + "attention_probs_dropout_prob": 0.1, + "hidden_act": "gelu", + "hidden_dropout_prob": 0.1, + "hidden_size": 768, + "initializer_range": 0.02, + "intermediate_size": 3072, + "layer_norm_eps": 1e-12, + "max_position_embeddings": 512, + "model_type": "bert", + "num_attention_heads": 12, + "num_hidden_layers": 12, + "pad_token_id": 0, + "type_vocab_size": 2, + "vocab_size": 30522, + "encoder_width": 768, + "add_cross_attention": true +} diff --git a/sd/stablediffusion/src/blip/configs/caption_coco.yaml b/sd/stablediffusion/src/blip/configs/caption_coco.yaml new file mode 100644 index 0000000000000000000000000000000000000000..42eab7030c0310ba2f265baf36fa1400aa6e5846 --- /dev/null +++ b/sd/stablediffusion/src/blip/configs/caption_coco.yaml @@ -0,0 +1,33 @@ +image_root: '/export/share/datasets/vision/coco/images/' +ann_root: 'annotation' +coco_gt_root: 'annotation/coco_gt' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_caption_capfilt_large.pth' + +# size of vit model; base or large +vit: 'base' +vit_grad_ckpt: False +vit_ckpt_layer: 0 +batch_size: 32 +init_lr: 1e-5 + +# vit: 'large' +# vit_grad_ckpt: True +# vit_ckpt_layer: 5 +# batch_size: 16 +# init_lr: 2e-6 + +image_size: 384 + +# generation configs +max_length: 20 +min_length: 5 +num_beams: 3 +prompt: 'a picture of ' + +# optimizer +weight_decay: 0.05 +min_lr: 0 +max_epoch: 5 + diff --git a/sd/stablediffusion/src/blip/configs/med_config.json b/sd/stablediffusion/src/blip/configs/med_config.json new file mode 100644 index 0000000000000000000000000000000000000000..0ffad0a6f3c2f9f11b8faa84529d9860bb70327a --- /dev/null +++ b/sd/stablediffusion/src/blip/configs/med_config.json @@ -0,0 +1,21 @@ +{ + "architectures": [ + "BertModel" + ], + "attention_probs_dropout_prob": 0.1, + "hidden_act": "gelu", + "hidden_dropout_prob": 0.1, + "hidden_size": 768, + "initializer_range": 0.02, + "intermediate_size": 3072, + "layer_norm_eps": 1e-12, + "max_position_embeddings": 512, + "model_type": "bert", + "num_attention_heads": 12, + "num_hidden_layers": 12, + "pad_token_id": 0, + "type_vocab_size": 2, + "vocab_size": 30524, + "encoder_width": 768, + "add_cross_attention": true +} diff --git a/sd/stablediffusion/src/blip/configs/nlvr.yaml b/sd/stablediffusion/src/blip/configs/nlvr.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2d1122aadb1a776bd347068233096b0c984f648b --- /dev/null +++ b/sd/stablediffusion/src/blip/configs/nlvr.yaml @@ -0,0 +1,21 @@ +image_root: '/export/share/datasets/vision/NLVR2/' +ann_root: 'annotation' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_nlvr.pth' + +#size of vit model; base or large +vit: 'base' +batch_size_train: 16 +batch_size_test: 64 +vit_grad_ckpt: False +vit_ckpt_layer: 0 +max_epoch: 15 + +image_size: 384 + +# optimizer +weight_decay: 0.05 +init_lr: 3e-5 +min_lr: 0 + diff --git a/sd/stablediffusion/src/blip/configs/nocaps.yaml b/sd/stablediffusion/src/blip/configs/nocaps.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9028135859b94aef5324c85c80e376c609d8a089 --- /dev/null +++ b/sd/stablediffusion/src/blip/configs/nocaps.yaml @@ -0,0 +1,15 @@ +image_root: '/export/share/datasets/vision/nocaps/' +ann_root: 'annotation' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_caption_capfilt_large.pth' + +vit: 'base' +batch_size: 32 + +image_size: 384 + +max_length: 20 +min_length: 5 +num_beams: 3 +prompt: 'a picture of ' \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/configs/pretrain.yaml b/sd/stablediffusion/src/blip/configs/pretrain.yaml new file mode 100644 index 0000000000000000000000000000000000000000..02355ee0228932803c661616485bf315e862b826 --- /dev/null +++ b/sd/stablediffusion/src/blip/configs/pretrain.yaml @@ -0,0 +1,27 @@ +train_file: ['/export/share/junnan-li/VL_pretrain/annotation/coco_karpathy_train.json', + '/export/share/junnan-li/VL_pretrain/annotation/vg_caption.json', + ] +laion_path: '' + +# size of vit model; base or large +vit: 'base' +vit_grad_ckpt: False +vit_ckpt_layer: 0 + +image_size: 224 +batch_size: 75 + +queue_size: 57600 +alpha: 0.4 + +# optimizer +weight_decay: 0.05 +init_lr: 3e-4 +min_lr: 1e-6 +warmup_lr: 1e-6 +lr_decay_rate: 0.9 +max_epoch: 20 +warmup_steps: 3000 + + + diff --git a/sd/stablediffusion/src/blip/configs/retrieval_coco.yaml b/sd/stablediffusion/src/blip/configs/retrieval_coco.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a8569e9b67112fe3605ac25e4fdc0231f7975378 --- /dev/null +++ b/sd/stablediffusion/src/blip/configs/retrieval_coco.yaml @@ -0,0 +1,34 @@ +image_root: '/export/share/datasets/vision/coco/images/' +ann_root: 'annotation' +dataset: 'coco' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_coco.pth' + +# size of vit model; base or large + +vit: 'base' +batch_size_train: 32 +batch_size_test: 64 +vit_grad_ckpt: True +vit_ckpt_layer: 4 +init_lr: 1e-5 + +# vit: 'large' +# batch_size_train: 16 +# batch_size_test: 32 +# vit_grad_ckpt: True +# vit_ckpt_layer: 12 +# init_lr: 5e-6 + +image_size: 384 +queue_size: 57600 +alpha: 0.4 +k_test: 256 +negative_all_rank: True + +# optimizer +weight_decay: 0.05 +min_lr: 0 +max_epoch: 6 + diff --git a/sd/stablediffusion/src/blip/configs/retrieval_flickr.yaml b/sd/stablediffusion/src/blip/configs/retrieval_flickr.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d75ea4eed87c9a001523c5e5914998c5e737594d --- /dev/null +++ b/sd/stablediffusion/src/blip/configs/retrieval_flickr.yaml @@ -0,0 +1,34 @@ +image_root: '/export/share/datasets/vision/flickr30k/' +ann_root: 'annotation' +dataset: 'flickr' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_flickr.pth' + +# size of vit model; base or large + +vit: 'base' +batch_size_train: 32 +batch_size_test: 64 +vit_grad_ckpt: True +vit_ckpt_layer: 4 +init_lr: 1e-5 + +# vit: 'large' +# batch_size_train: 16 +# batch_size_test: 32 +# vit_grad_ckpt: True +# vit_ckpt_layer: 10 +# init_lr: 5e-6 + +image_size: 384 +queue_size: 57600 +alpha: 0.4 +k_test: 128 +negative_all_rank: False + +# optimizer +weight_decay: 0.05 +min_lr: 0 +max_epoch: 6 + diff --git a/sd/stablediffusion/src/blip/configs/retrieval_msrvtt.yaml b/sd/stablediffusion/src/blip/configs/retrieval_msrvtt.yaml new file mode 100644 index 0000000000000000000000000000000000000000..395f62542bb22d706b8e19e2455d2c7298984d0b --- /dev/null +++ b/sd/stablediffusion/src/blip/configs/retrieval_msrvtt.yaml @@ -0,0 +1,12 @@ +video_root: '/export/share/dongxuli/data/msrvtt_retrieval/videos' +ann_root: 'annotation' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_coco.pth' + +# size of vit model; base or large +vit: 'base' +batch_size: 64 +k_test: 128 +image_size: 384 +num_frm_test: 8 \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/configs/vqa.yaml b/sd/stablediffusion/src/blip/configs/vqa.yaml new file mode 100644 index 0000000000000000000000000000000000000000..74327e6d0a34672023b44569558fe8beeb052548 --- /dev/null +++ b/sd/stablediffusion/src/blip/configs/vqa.yaml @@ -0,0 +1,25 @@ +vqa_root: '/export/share/datasets/vision/VQA/Images/mscoco/' #followed by train2014/ +vg_root: '/export/share/datasets/vision/visual-genome/' #followed by image/ +train_files: ['vqa_train','vqa_val','vg_qa'] +ann_root: 'annotation' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_vqa_capfilt_large.pth' + +# size of vit model; base or large +vit: 'base' +batch_size_train: 16 +batch_size_test: 32 +vit_grad_ckpt: False +vit_ckpt_layer: 0 +init_lr: 2e-5 + +image_size: 480 + +k_test: 128 +inference: 'rank' + +# optimizer +weight_decay: 0.05 +min_lr: 0 +max_epoch: 10 \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/data/__init__.py b/sd/stablediffusion/src/blip/data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0be209acf415855ea6ef753efedf903b5decb6b9 --- /dev/null +++ b/sd/stablediffusion/src/blip/data/__init__.py @@ -0,0 +1,101 @@ +import torch +from torch.utils.data import DataLoader +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode + +from data.coco_karpathy_dataset import coco_karpathy_train, coco_karpathy_caption_eval, coco_karpathy_retrieval_eval +from data.nocaps_dataset import nocaps_eval +from data.flickr30k_dataset import flickr30k_train, flickr30k_retrieval_eval +from data.vqa_dataset import vqa_dataset +from data.nlvr_dataset import nlvr_dataset +from data.pretrain_dataset import pretrain_dataset +from transform.randaugment import RandomAugment + +def create_dataset(dataset, config, min_scale=0.5): + + normalize = transforms.Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711)) + + transform_train = transforms.Compose([ + transforms.RandomResizedCrop(config['image_size'],scale=(min_scale, 1.0),interpolation=InterpolationMode.BICUBIC), + transforms.RandomHorizontalFlip(), + RandomAugment(2,5,isPIL=True,augs=['Identity','AutoContrast','Brightness','Sharpness','Equalize', + 'ShearX', 'ShearY', 'TranslateX', 'TranslateY', 'Rotate']), + transforms.ToTensor(), + normalize, + ]) + transform_test = transforms.Compose([ + transforms.Resize((config['image_size'],config['image_size']),interpolation=InterpolationMode.BICUBIC), + transforms.ToTensor(), + normalize, + ]) + + if dataset=='pretrain': + dataset = pretrain_dataset(config['train_file'], config['laion_path'], transform_train) + return dataset + + elif dataset=='caption_coco': + train_dataset = coco_karpathy_train(transform_train, config['image_root'], config['ann_root'], prompt=config['prompt']) + val_dataset = coco_karpathy_caption_eval(transform_test, config['image_root'], config['ann_root'], 'val') + test_dataset = coco_karpathy_caption_eval(transform_test, config['image_root'], config['ann_root'], 'test') + return train_dataset, val_dataset, test_dataset + + elif dataset=='nocaps': + val_dataset = nocaps_eval(transform_test, config['image_root'], config['ann_root'], 'val') + test_dataset = nocaps_eval(transform_test, config['image_root'], config['ann_root'], 'test') + return val_dataset, test_dataset + + elif dataset=='retrieval_coco': + train_dataset = coco_karpathy_train(transform_train, config['image_root'], config['ann_root']) + val_dataset = coco_karpathy_retrieval_eval(transform_test, config['image_root'], config['ann_root'], 'val') + test_dataset = coco_karpathy_retrieval_eval(transform_test, config['image_root'], config['ann_root'], 'test') + return train_dataset, val_dataset, test_dataset + + elif dataset=='retrieval_flickr': + train_dataset = flickr30k_train(transform_train, config['image_root'], config['ann_root']) + val_dataset = flickr30k_retrieval_eval(transform_test, config['image_root'], config['ann_root'], 'val') + test_dataset = flickr30k_retrieval_eval(transform_test, config['image_root'], config['ann_root'], 'test') + return train_dataset, val_dataset, test_dataset + + elif dataset=='vqa': + train_dataset = vqa_dataset(transform_train, config['ann_root'], config['vqa_root'], config['vg_root'], + train_files = config['train_files'], split='train') + test_dataset = vqa_dataset(transform_test, config['ann_root'], config['vqa_root'], config['vg_root'], split='test') + return train_dataset, test_dataset + + elif dataset=='nlvr': + train_dataset = nlvr_dataset(transform_train, config['image_root'], config['ann_root'],'train') + val_dataset = nlvr_dataset(transform_test, config['image_root'], config['ann_root'],'val') + test_dataset = nlvr_dataset(transform_test, config['image_root'], config['ann_root'],'test') + return train_dataset, val_dataset, test_dataset + + +def create_sampler(datasets, shuffles, num_tasks, global_rank): + samplers = [] + for dataset,shuffle in zip(datasets,shuffles): + sampler = torch.utils.data.DistributedSampler(dataset, num_replicas=num_tasks, rank=global_rank, shuffle=shuffle) + samplers.append(sampler) + return samplers + + +def create_loader(datasets, samplers, batch_size, num_workers, is_trains, collate_fns): + loaders = [] + for dataset,sampler,bs,n_worker,is_train,collate_fn in zip(datasets,samplers,batch_size,num_workers,is_trains,collate_fns): + if is_train: + shuffle = (sampler is None) + drop_last = True + else: + shuffle = False + drop_last = False + loader = DataLoader( + dataset, + batch_size=bs, + num_workers=n_worker, + pin_memory=True, + sampler=sampler, + shuffle=shuffle, + collate_fn=collate_fn, + drop_last=drop_last, + ) + loaders.append(loader) + return loaders + diff --git a/sd/stablediffusion/src/blip/data/coco_karpathy_dataset.py b/sd/stablediffusion/src/blip/data/coco_karpathy_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..a34d29205f42aa09695b160ac9c91958ba041bb3 --- /dev/null +++ b/sd/stablediffusion/src/blip/data/coco_karpathy_dataset.py @@ -0,0 +1,126 @@ +import os +import json + +from torch.utils.data import Dataset +from torchvision.datasets.utils import download_url + +from PIL import Image + +from data.utils import pre_caption + +class coco_karpathy_train(Dataset): + def __init__(self, transform, image_root, ann_root, max_words=30, prompt=''): + ''' + image_root (string): Root directory of images (e.g. coco/images/) + ann_root (string): directory to store the annotation file + ''' + url = 'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_train.json' + filename = 'coco_karpathy_train.json' + + download_url(url,ann_root) + + self.annotation = json.load(open(os.path.join(ann_root,filename),'r')) + self.transform = transform + self.image_root = image_root + self.max_words = max_words + self.prompt = prompt + + self.img_ids = {} + n = 0 + for ann in self.annotation: + img_id = ann['image_id'] + if img_id not in self.img_ids.keys(): + self.img_ids[img_id] = n + n += 1 + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + image_path = os.path.join(self.image_root,ann['image']) + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + caption = self.prompt+pre_caption(ann['caption'], self.max_words) + + return image, caption, self.img_ids[ann['image_id']] + + +class coco_karpathy_caption_eval(Dataset): + def __init__(self, transform, image_root, ann_root, split): + ''' + image_root (string): Root directory of images (e.g. coco/images/) + ann_root (string): directory to store the annotation file + split (string): val or test + ''' + urls = {'val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_val.json', + 'test':'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_test.json'} + filenames = {'val':'coco_karpathy_val.json','test':'coco_karpathy_test.json'} + + download_url(urls[split],ann_root) + + self.annotation = json.load(open(os.path.join(ann_root,filenames[split]),'r')) + self.transform = transform + self.image_root = image_root + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + image_path = os.path.join(self.image_root,ann['image']) + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + img_id = ann['image'].split('/')[-1].strip('.jpg').split('_')[-1] + + return image, int(img_id) + + +class coco_karpathy_retrieval_eval(Dataset): + def __init__(self, transform, image_root, ann_root, split, max_words=30): + ''' + image_root (string): Root directory of images (e.g. coco/images/) + ann_root (string): directory to store the annotation file + split (string): val or test + ''' + urls = {'val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_val.json', + 'test':'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_test.json'} + filenames = {'val':'coco_karpathy_val.json','test':'coco_karpathy_test.json'} + + download_url(urls[split],ann_root) + + self.annotation = json.load(open(os.path.join(ann_root,filenames[split]),'r')) + self.transform = transform + self.image_root = image_root + + self.text = [] + self.image = [] + self.txt2img = {} + self.img2txt = {} + + txt_id = 0 + for img_id, ann in enumerate(self.annotation): + self.image.append(ann['image']) + self.img2txt[img_id] = [] + for i, caption in enumerate(ann['caption']): + self.text.append(pre_caption(caption,max_words)) + self.img2txt[img_id].append(txt_id) + self.txt2img[txt_id] = img_id + txt_id += 1 + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + image_path = os.path.join(self.image_root, self.annotation[index]['image']) + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + return image, index \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/data/flickr30k_dataset.py b/sd/stablediffusion/src/blip/data/flickr30k_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..018ab387014ddaf554c4d3184cfc0e2ba8b2d487 --- /dev/null +++ b/sd/stablediffusion/src/blip/data/flickr30k_dataset.py @@ -0,0 +1,93 @@ +import os +import json + +from torch.utils.data import Dataset +from torchvision.datasets.utils import download_url + +from PIL import Image + +from data.utils import pre_caption + +class flickr30k_train(Dataset): + def __init__(self, transform, image_root, ann_root, max_words=30, prompt=''): + ''' + image_root (string): Root directory of images (e.g. flickr30k/) + ann_root (string): directory to store the annotation file + ''' + url = 'https://storage.googleapis.com/sfr-vision-language-research/datasets/flickr30k_train.json' + filename = 'flickr30k_train.json' + + download_url(url,ann_root) + + self.annotation = json.load(open(os.path.join(ann_root,filename),'r')) + self.transform = transform + self.image_root = image_root + self.max_words = max_words + self.prompt = prompt + + self.img_ids = {} + n = 0 + for ann in self.annotation: + img_id = ann['image_id'] + if img_id not in self.img_ids.keys(): + self.img_ids[img_id] = n + n += 1 + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + image_path = os.path.join(self.image_root,ann['image']) + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + caption = self.prompt+pre_caption(ann['caption'], self.max_words) + + return image, caption, self.img_ids[ann['image_id']] + + +class flickr30k_retrieval_eval(Dataset): + def __init__(self, transform, image_root, ann_root, split, max_words=30): + ''' + image_root (string): Root directory of images (e.g. flickr30k/) + ann_root (string): directory to store the annotation file + split (string): val or test + ''' + urls = {'val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/flickr30k_val.json', + 'test':'https://storage.googleapis.com/sfr-vision-language-research/datasets/flickr30k_test.json'} + filenames = {'val':'flickr30k_val.json','test':'flickr30k_test.json'} + + download_url(urls[split],ann_root) + + self.annotation = json.load(open(os.path.join(ann_root,filenames[split]),'r')) + self.transform = transform + self.image_root = image_root + + self.text = [] + self.image = [] + self.txt2img = {} + self.img2txt = {} + + txt_id = 0 + for img_id, ann in enumerate(self.annotation): + self.image.append(ann['image']) + self.img2txt[img_id] = [] + for i, caption in enumerate(ann['caption']): + self.text.append(pre_caption(caption,max_words)) + self.img2txt[img_id].append(txt_id) + self.txt2img[txt_id] = img_id + txt_id += 1 + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + image_path = os.path.join(self.image_root, self.annotation[index]['image']) + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + return image, index \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/data/nlvr_dataset.py b/sd/stablediffusion/src/blip/data/nlvr_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..a8d6b2d7cd8d3260bd279c7dca80de53bacc691a --- /dev/null +++ b/sd/stablediffusion/src/blip/data/nlvr_dataset.py @@ -0,0 +1,78 @@ +import os +import json +import random + +from torch.utils.data import Dataset +from torchvision.datasets.utils import download_url + +from PIL import Image + +from data.utils import pre_caption + +class nlvr_dataset(Dataset): + def __init__(self, transform, image_root, ann_root, split): + ''' + image_root (string): Root directory of images + ann_root (string): directory to store the annotation file + split (string): train, val or test + ''' + urls = {'train':'https://storage.googleapis.com/sfr-vision-language-research/datasets/nlvr_train.json', + 'val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/nlvr_dev.json', + 'test':'https://storage.googleapis.com/sfr-vision-language-research/datasets/nlvr_test.json'} + filenames = {'train':'nlvr_train.json','val':'nlvr_dev.json','test':'nlvr_test.json'} + + download_url(urls[split],ann_root) + self.annotation = json.load(open(os.path.join(ann_root,filenames[split]),'r')) + + self.transform = transform + self.image_root = image_root + + + def __len__(self): + return len(self.annotation) + + + def __getitem__(self, index): + + ann = self.annotation[index] + + image0_path = os.path.join(self.image_root,ann['images'][0]) + image0 = Image.open(image0_path).convert('RGB') + image0 = self.transform(image0) + + image1_path = os.path.join(self.image_root,ann['images'][1]) + image1 = Image.open(image1_path).convert('RGB') + image1 = self.transform(image1) + + sentence = pre_caption(ann['sentence'], 40) + + if ann['label']=='True': + label = 1 + else: + label = 0 + + words = sentence.split(' ') + + if 'left' not in words and 'right' not in words: + if random.random()<0.5: + return image0, image1, sentence, label + else: + return image1, image0, sentence, label + else: + if random.random()<0.5: + return image0, image1, sentence, label + else: + new_words = [] + for word in words: + if word=='left': + new_words.append('right') + elif word=='right': + new_words.append('left') + else: + new_words.append(word) + + sentence = ' '.join(new_words) + return image1, image0, sentence, label + + + \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/data/nocaps_dataset.py b/sd/stablediffusion/src/blip/data/nocaps_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..ba0bed06d8af3dbaccf18a56e725f101e585503e --- /dev/null +++ b/sd/stablediffusion/src/blip/data/nocaps_dataset.py @@ -0,0 +1,32 @@ +import os +import json + +from torch.utils.data import Dataset +from torchvision.datasets.utils import download_url + +from PIL import Image + +class nocaps_eval(Dataset): + def __init__(self, transform, image_root, ann_root, split): + urls = {'val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/nocaps_val.json', + 'test':'https://storage.googleapis.com/sfr-vision-language-research/datasets/nocaps_test.json'} + filenames = {'val':'nocaps_val.json','test':'nocaps_test.json'} + + download_url(urls[split],ann_root) + + self.annotation = json.load(open(os.path.join(ann_root,filenames[split]),'r')) + self.transform = transform + self.image_root = image_root + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + image_path = os.path.join(self.image_root,ann['image']) + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + return image, int(ann['img_id']) \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/data/pretrain_dataset.py b/sd/stablediffusion/src/blip/data/pretrain_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..703d543ab5267fdc6fe2b7c84ef6a631d8af90ad --- /dev/null +++ b/sd/stablediffusion/src/blip/data/pretrain_dataset.py @@ -0,0 +1,59 @@ +import json +import os +import random + +from torch.utils.data import Dataset + +from PIL import Image +from PIL import ImageFile +ImageFile.LOAD_TRUNCATED_IMAGES = True +Image.MAX_IMAGE_PIXELS = None + +from data.utils import pre_caption +import os,glob + +class pretrain_dataset(Dataset): + def __init__(self, ann_file, laion_path, transform): + + self.ann_pretrain = [] + for f in ann_file: + print('loading '+f) + ann = json.load(open(f,'r')) + self.ann_pretrain += ann + + self.laion_path = laion_path + if self.laion_path: + self.laion_files = glob.glob(os.path.join(laion_path,'*.json')) + + print('loading '+self.laion_files[0]) + with open(self.laion_files[0],'r') as f: + self.ann_laion = json.load(f) + + self.annotation = self.ann_pretrain + self.ann_laion + else: + self.annotation = self.ann_pretrain + + self.transform = transform + + + def reload_laion(self, epoch): + n = epoch%len(self.laion_files) + print('loading '+self.laion_files[n]) + with open(self.laion_files[n],'r') as f: + self.ann_laion = json.load(f) + + self.annotation = self.ann_pretrain + self.ann_laion + + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + image = Image.open(ann['image']).convert('RGB') + image = self.transform(image) + caption = pre_caption(ann['caption'],30) + + return image, caption \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/data/utils.py b/sd/stablediffusion/src/blip/data/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..628894844becd462d444584b8b2b01a84ee4b8f7 --- /dev/null +++ b/sd/stablediffusion/src/blip/data/utils.py @@ -0,0 +1,112 @@ +import re +import json +import os + +import torch +import torch.distributed as dist + +import utils + +def pre_caption(caption,max_words=50): + caption = re.sub( + r"([.!\"()*#:;~])", + ' ', + caption.lower(), + ) + caption = re.sub( + r"\s{2,}", + ' ', + caption, + ) + caption = caption.rstrip('\n') + caption = caption.strip(' ') + + #truncate caption + caption_words = caption.split(' ') + if len(caption_words)>max_words: + caption = ' '.join(caption_words[:max_words]) + + return caption + +def pre_question(question,max_ques_words=50): + question = re.sub( + r"([.!\"()*#:;~])", + '', + question.lower(), + ) + question = question.rstrip(' ') + + #truncate question + question_words = question.split(' ') + if len(question_words)>max_ques_words: + question = ' '.join(question_words[:max_ques_words]) + + return question + + +def save_result(result, result_dir, filename, remove_duplicate=''): + result_file = os.path.join(result_dir, '%s_rank%d.json'%(filename,utils.get_rank())) + final_result_file = os.path.join(result_dir, '%s.json'%filename) + + json.dump(result,open(result_file,'w')) + + dist.barrier() + + if utils.is_main_process(): + # combine results from all processes + result = [] + + for rank in range(utils.get_world_size()): + result_file = os.path.join(result_dir, '%s_rank%d.json'%(filename,rank)) + res = json.load(open(result_file,'r')) + result += res + + if remove_duplicate: + result_new = [] + id_list = [] + for res in result: + if res[remove_duplicate] not in id_list: + id_list.append(res[remove_duplicate]) + result_new.append(res) + result = result_new + + json.dump(result,open(final_result_file,'w')) + print('result file saved to %s'%final_result_file) + + return final_result_file + + + +from pycocotools.coco import COCO +from pycocoevalcap.eval import COCOEvalCap +from torchvision.datasets.utils import download_url + +def coco_caption_eval(coco_gt_root, results_file, split): + urls = {'val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_val_gt.json', + 'test':'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_test_gt.json'} + filenames = {'val':'coco_karpathy_val_gt.json','test':'coco_karpathy_test_gt.json'} + + download_url(urls[split],coco_gt_root) + annotation_file = os.path.join(coco_gt_root,filenames[split]) + + # create coco object and coco_result object + coco = COCO(annotation_file) + coco_result = coco.loadRes(results_file) + + # create coco_eval object by taking coco and coco_result + coco_eval = COCOEvalCap(coco, coco_result) + + # evaluate on a subset of images by setting + # coco_eval.params['image_id'] = coco_result.getImgIds() + # please remove this line when evaluating the full validation set + # coco_eval.params['image_id'] = coco_result.getImgIds() + + # evaluate results + # SPICE will take a few minutes the first time, but speeds up due to caching + coco_eval.evaluate() + + # print output evaluation scores + for metric, score in coco_eval.eval.items(): + print(f'{metric}: {score:.3f}') + + return coco_eval \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/data/video_dataset.py b/sd/stablediffusion/src/blip/data/video_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..0a6f8a61105bbd4285f98b3abe9445b73fd4c7ef --- /dev/null +++ b/sd/stablediffusion/src/blip/data/video_dataset.py @@ -0,0 +1,110 @@ +from torch.utils.data import Dataset +from torchvision.datasets.utils import download_url + +from PIL import Image +import torch +import numpy as np +import random +import decord +from decord import VideoReader +import json +import os +from data.utils import pre_caption + +decord.bridge.set_bridge("torch") + +class ImageNorm(object): + """Apply Normalization to Image Pixels on GPU + """ + def __init__(self, mean, std): + self.mean = torch.tensor(mean).view(1, 3, 1, 1) + self.std = torch.tensor(std).view(1, 3, 1, 1) + + def __call__(self, img): + + if torch.max(img) > 1 and self.mean.max() <= 1: + img.div_(255.) + return img.sub_(self.mean).div_(self.std) + +def load_jsonl(filename): + with open(filename, "r") as f: + return [json.loads(l.strip("\n")) for l in f.readlines()] + + +class VideoDataset(Dataset): + + def __init__(self, video_root, ann_root, num_frm=4, frm_sampling_strategy="rand", max_img_size=384, video_fmt='.mp4'): + ''' + image_root (string): Root directory of video + ann_root (string): directory to store the annotation file + ''' + url = 'https://storage.googleapis.com/sfr-vision-language-research/datasets/msrvtt_test.jsonl' + filename = 'msrvtt_test.jsonl' + + download_url(url,ann_root) + self.annotation = load_jsonl(os.path.join(ann_root,filename)) + + self.num_frm = num_frm + self.frm_sampling_strategy = frm_sampling_strategy + self.max_img_size = max_img_size + self.video_root = video_root + self.video_fmt = video_fmt + self.img_norm = ImageNorm(mean=(0.48145466, 0.4578275, 0.40821073), std=(0.26862954, 0.26130258, 0.27577711)) + + self.text = [pre_caption(ann['caption'],40) for ann in self.annotation] + self.txt2video = [i for i in range(len(self.annotation))] + self.video2txt = self.txt2video + + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + video_path = os.path.join(self.video_root, ann['clip_name'] + self.video_fmt) + + vid_frm_array = self._load_video_from_path_decord(video_path, height=self.max_img_size, width=self.max_img_size) + + video = self.img_norm(vid_frm_array.float()) + + return video, ann['clip_name'] + + + + def _load_video_from_path_decord(self, video_path, height=None, width=None, start_time=None, end_time=None, fps=-1): + try: + if not height or not width: + vr = VideoReader(video_path) + else: + vr = VideoReader(video_path, width=width, height=height) + + vlen = len(vr) + + if start_time or end_time: + assert fps > 0, 'must provide video fps if specifying start and end time.' + + start_idx = min(int(start_time * fps), vlen) + end_idx = min(int(end_time * fps), vlen) + else: + start_idx, end_idx = 0, vlen + + if self.frm_sampling_strategy == 'uniform': + frame_indices = np.arange(start_idx, end_idx, vlen / self.num_frm, dtype=int) + elif self.frm_sampling_strategy == 'rand': + frame_indices = sorted(random.sample(range(vlen), self.num_frm)) + elif self.frm_sampling_strategy == 'headtail': + frame_indices_head = sorted(random.sample(range(vlen // 2), self.num_frm // 2)) + frame_indices_tail = sorted(random.sample(range(vlen // 2, vlen), self.num_frm // 2)) + frame_indices = frame_indices_head + frame_indices_tail + else: + raise NotImplementedError('Invalid sampling strategy {} '.format(self.frm_sampling_strategy)) + + raw_sample_frms = vr.get_batch(frame_indices) + except Exception as e: + return None + + raw_sample_frms = raw_sample_frms.permute(0, 3, 1, 2) + + return raw_sample_frms diff --git a/sd/stablediffusion/src/blip/data/vqa_dataset.py b/sd/stablediffusion/src/blip/data/vqa_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..92ec1df429b3910316ddd554bfea01c6e7922cae --- /dev/null +++ b/sd/stablediffusion/src/blip/data/vqa_dataset.py @@ -0,0 +1,88 @@ +import os +import json +import random +from PIL import Image + +import torch +from torch.utils.data import Dataset +from data.utils import pre_question + +from torchvision.datasets.utils import download_url + +class vqa_dataset(Dataset): + def __init__(self, transform, ann_root, vqa_root, vg_root, train_files=[], split="train"): + self.split = split + + self.transform = transform + self.vqa_root = vqa_root + self.vg_root = vg_root + + if split=='train': + urls = {'vqa_train':'https://storage.googleapis.com/sfr-vision-language-research/datasets/vqa_train.json', + 'vqa_val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/vqa_val.json', + 'vg_qa':'https://storage.googleapis.com/sfr-vision-language-research/datasets/vg_qa.json'} + + self.annotation = [] + for f in train_files: + download_url(urls[f],ann_root) + self.annotation += json.load(open(os.path.join(ann_root,'%s.json'%f),'r')) + else: + download_url('https://storage.googleapis.com/sfr-vision-language-research/datasets/vqa_test.json',ann_root) + self.annotation = json.load(open(os.path.join(ann_root,'vqa_test.json'),'r')) + + download_url('https://storage.googleapis.com/sfr-vision-language-research/datasets/answer_list.json',ann_root) + self.answer_list = json.load(open(os.path.join(ann_root,'answer_list.json'),'r')) + + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + if ann['dataset']=='vqa': + image_path = os.path.join(self.vqa_root,ann['image']) + elif ann['dataset']=='vg': + image_path = os.path.join(self.vg_root,ann['image']) + + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + if self.split == 'test': + question = pre_question(ann['question']) + question_id = ann['question_id'] + return image, question, question_id + + + elif self.split=='train': + + question = pre_question(ann['question']) + + if ann['dataset']=='vqa': + answer_weight = {} + for answer in ann['answer']: + if answer in answer_weight.keys(): + answer_weight[answer] += 1/len(ann['answer']) + else: + answer_weight[answer] = 1/len(ann['answer']) + + answers = list(answer_weight.keys()) + weights = list(answer_weight.values()) + + elif ann['dataset']=='vg': + answers = [ann['answer']] + weights = [0.2] + + return image, question, answers, weights + + +def vqa_collate_fn(batch): + image_list, question_list, answer_list, weight_list, n = [], [], [], [], [] + for image, question, answer, weights in batch: + image_list.append(image) + question_list.append(question) + weight_list += weights + answer_list += answer + n.append(len(answer)) + return torch.stack(image_list,dim=0), question_list, answer_list, torch.Tensor(weight_list), n \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/demo.ipynb b/sd/stablediffusion/src/blip/demo.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3077a1a42c584f3ef535903f64cf6b3bb722490e --- /dev/null +++ b/sd/stablediffusion/src/blip/demo.ipynb @@ -0,0 +1,301 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2b949f9f", + "metadata": {}, + "source": [ + "# BLIP: Inference Demo\n", + " - [Image Captioning](#Image-Captioning)\n", + " - [VQA](#VQA)\n", + " - [Feature Extraction](#Feature-Extraction)\n", + " - [Image Text Matching](#Image-Text-Matching)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "cbcb066b", + "metadata": {}, + "outputs": [], + "source": [ + "# install requirements\n", + "import sys\n", + "if 'google.colab' in sys.modules:\n", + " print('Running in Colab.')\n", + " !pip3 install transformers==4.15.0 timm==0.4.12 fairscale==0.4.4\n", + " !git clone https://github.com/salesforce/BLIP\n", + " %cd BLIP" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a811a65f", + "metadata": {}, + "outputs": [], + "source": [ + "from PIL import Image\n", + "import requests\n", + "import torch\n", + "from torchvision import transforms\n", + "from torchvision.transforms.functional import InterpolationMode\n", + "\n", + "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n", + "\n", + "def load_demo_image(image_size,device):\n", + " img_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/demo.jpg' \n", + " raw_image = Image.open(requests.get(img_url, stream=True).raw).convert('RGB') \n", + "\n", + " w,h = raw_image.size\n", + " display(raw_image.resize((w//5,h//5)))\n", + " \n", + " transform = transforms.Compose([\n", + " transforms.Resize((image_size,image_size),interpolation=InterpolationMode.BICUBIC),\n", + " transforms.ToTensor(),\n", + " transforms.Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711))\n", + " ]) \n", + " image = transform(raw_image).unsqueeze(0).to(device) \n", + " return image" + ] + }, + { + "cell_type": "markdown", + "id": "f72f4406", + "metadata": {}, + "source": [ + "# Image Captioning\n", + "Perform image captioning using finetuned BLIP model" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6835daef", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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sU7lt5fln3/7HCz386n2ziKbG+s4Jb6rIlAS3NURwRDWzOc5tXDIPM1FciLiCcMLF28L6OfN/3qv//0/e//NyrwPVhvqP77c91+uj8deW6NSalcDRCIILzmDCXgqa9IwNyVO0caBxpzmQgjCMk1SUm616ei993ob14YvX673Pr77cP7+qeQyk9ApnQPor396/flK+L/9P/yXCWMq5d7VzC9T/nBe1rlsDr9srbdR5hWIh6kkkZRM7askm6X3fhAcc04B0LcN3HOelstpXpZlykL4eOyf3976Vol49JZLef7wnhlVbbtvL59f8umUp6LmKSdiqvedhUtK63nN6xkBs1CEz0TnxOphph8KMkIMVY8aQMvZIFob133svTngelqKJA0HQACotbEQEl2vt9FHBDiQlFKY3r9/nudiQK11BChZRMTMWx9gLiXttSFizvn4bKgZmBJGLtNQc4s2GgEmlvU09W5ER2eTeutlmlLiMLu/bZJpqL78/PPby5dwBSQb2vbHX/yjf3C5XLa3FxF5PLba6nK+jN63t+vzd99Pl6f79Z4Q1tPyuL6+fXkNob/9D3/Z635+focEJDKtT/PlYqp//a//n+d379599507LKen999/t8wzAK6XdyycCT0iALJIOATB4/EoKYmk4dZqXZd5qIWbjz7UT+fTNE17rUfWfNsrEyah3jqxpCStNRHiVPatQQwmIkkl595q3TdtfVkXydP7D++OnhAyjaGPxwMgeh+SSwRsj0cRWdcllwThbkbMrba5ZJJU21DVdZ5LSULk4VvvECBEZt7NWY4lCR7qY48Yveecp5wjwtyJKNzXZQEAj0D3gDjOaAjhgXutJNJqA4RlKnbcCiDAw9wlCSKVLAhgHm4GhxWevqrq4Ot/yHWYuZkFgU1Mz0sh190AWKzVIyK/18oR82n1PpZMp7kE8qPblNPe+tv1GmAdEkpy8zBrrZ3P62Vdr7crhENEd+CUAtDGADNDevvyMp+XWbhtG4nU3r2Pp2X+7t0JiLvHLy+3x17nks19AIYbu5GbEhOifo0ogmtPLGWZKacj5xEQTLTdrt5rD1T1MToxPWf+Z9+sv39te28zGnK61zanfC58njhMHyMqpYfhbk7EKQm4qWrbW29tPa2HhVqYI0LHsMPQ4+E61tNKiHXbzRQiGOHjzBNFHzpLoI/f//J6735KdFqX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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "load checkpoint from https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_base_caption.pth\n", + "caption: a woman sitting on the beach with a dog\n" + ] + } + ], + "source": [ + "from models.blip import blip_decoder\n", + "\n", + "image_size = 384\n", + "image = load_demo_image(image_size=image_size, device=device)\n", + "\n", + "model_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_capfilt_large.pth'\n", + " \n", + "model = blip_decoder(pretrained=model_url, image_size=image_size, vit='base')\n", + "model.eval()\n", + "model = model.to(device)\n", + "\n", + "with torch.no_grad():\n", + " # beam search\n", + " caption = model.generate(image, sample=False, num_beams=3, max_length=20, min_length=5) \n", + " # nucleus sampling\n", + " # caption = model.generate(image, sample=True, top_p=0.9, max_length=20, min_length=5) \n", + " print('caption: '+caption[0])" + ] + }, + { + "cell_type": "markdown", + "id": "fac320a2", + "metadata": {}, + "source": [ + "# VQA\n", + "Perform visual question answering using finetuned BLIP model" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "5e6f3fb1", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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sU7lt5fln3/7HCz386n2ziKbG+s4Jb6rIlAS3NURwRDWzOc5tXDIPM1FciLiCcMLF28L6OfN/3qv//0/e//NyrwPVhvqP77c91+uj8deW6NSalcDRCIILzmDCXgqa9IwNyVO0caBxpzmQgjCMk1SUm616ei993ob14YvX673Pr77cP7+qeQyk9ApnQPor396/flK+L/9P/yXCWMq5d7VzC9T/nBe1rlsDr9srbdR5hWIh6kkkZRM7askm6X3fhAcc04B0LcN3HOelstpXpZlykL4eOyf3976Vol49JZLef7wnhlVbbtvL59f8umUp6LmKSdiqvedhUtK63nN6xkBs1CEz0TnxOphph8KMkIMVY8aQMvZIFob133svTngelqKJA0HQACotbEQEl2vt9FHBDiQlFKY3r9/nudiQK11BChZRMTMWx9gLiXttSFizvn4bKgZmBJGLtNQc4s2GgEmlvU09W5ER2eTeutlmlLiMLu/bZJpqL78/PPby5dwBSQb2vbHX/yjf3C5XLa3FxF5PLba6nK+jN63t+vzd99Pl6f79Z4Q1tPyuL6+fXkNob/9D3/Z635+focEJDKtT/PlYqp//a//n+d379599507LKen999/t8wzAK6XdyycCT0iALJIOATB4/EoKYmk4dZqXZd5qIWbjz7UT+fTNE17rUfWfNsrEyah3jqxpCStNRHiVPatQQwmIkkl595q3TdtfVkXydP7D++OnhAyjaGPxwMgeh+SSwRsj0cRWdcllwThbkbMrba5ZJJU21DVdZ5LSULk4VvvECBEZt7NWY4lCR7qY48Yveecp5wjwtyJKNzXZQEAj0D3gDjOaAjhgXutJNJqA4RlKnbcCiDAw9wlCSKVLAhgHm4GhxWevqrq4Ot/yHWYuZkFgU1Mz0sh190AWKzVIyK/18oR82n1PpZMp7kE8qPblNPe+tv1GmAdEkpy8zBrrZ3P62Vdr7crhENEd+CUAtDGADNDevvyMp+XWbhtG4nU3r2Pp2X+7t0JiLvHLy+3x17nks19AIYbu5GbEhOifo0ogmtPLGWZKacj5xEQTLTdrt5rD1T1MToxPWf+Z9+sv39te28zGnK61zanfC58njhMHyMqpYfhbk7EKQm4qWrbW29tPa2HhVqYI0LHsMPQ4+E61tNKiHXbzRQiGOHjzBNFHzpLoI/f//J6735KdFqX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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "load checkpoint from https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_vqa.pth\n", + "answer: on beach\n" + ] + } + ], + "source": [ + "from models.blip_vqa import blip_vqa\n", + "\n", + "image_size = 480\n", + "image = load_demo_image(image_size=image_size, device=device) \n", + "\n", + "model_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_vqa_capfilt_large.pth'\n", + " \n", + "model = blip_vqa(pretrained=model_url, image_size=image_size, vit='base')\n", + "model.eval()\n", + "model = model.to(device)\n", + "\n", + "question = 'where is the woman sitting?'\n", + "\n", + "with torch.no_grad():\n", + " answer = model(image, question, train=False, inference='generate') \n", + " print('answer: '+answer[0])" + ] + }, + { + "cell_type": "markdown", + "id": "6100e519", + "metadata": {}, + "source": [ + "# Feature Extraction" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "4f8f21ed", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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sU7lt5fln3/7HCz386n2ziKbG+s4Jb6rIlAS3NURwRDWzOc5tXDIPM1FciLiCcMLF28L6OfN/3qv//0/e//NyrwPVhvqP77c91+uj8deW6NSalcDRCIILzmDCXgqa9IwNyVO0caBxpzmQgjCMk1SUm616ei993ob14YvX673Pr77cP7+qeQyk9ApnQPor396/flK+L/9P/yXCWMq5d7VzC9T/nBe1rlsDr9srbdR5hWIh6kkkZRM7askm6X3fhAcc04B0LcN3HOelstpXpZlykL4eOyf3976Vol49JZLef7wnhlVbbtvL59f8umUp6LmKSdiqvedhUtK63nN6xkBs1CEz0TnxOphph8KMkIMVY8aQMvZIFob133svTngelqKJA0HQACotbEQEl2vt9FHBDiQlFKY3r9/nudiQK11BChZRMTMWx9gLiXttSFizvn4bKgZmBJGLtNQc4s2GgEmlvU09W5ER2eTeutlmlLiMLu/bZJpqL78/PPby5dwBSQb2vbHX/yjf3C5XLa3FxF5PLba6nK+jN63t+vzd99Pl6f79Z4Q1tPyuL6+fXkNob/9D3/Z635+focEJDKtT/PlYqp//a//n+d379599507LKen999/t8wzAK6XdyycCT0iALJIOATB4/EoKYmk4dZqXZd5qIWbjz7UT+fTNE17rUfWfNsrEyah3jqxpCStNRHiVPatQQwmIkkl595q3TdtfVkXydP7D++OnhAyjaGPxwMgeh+SSwRsj0cRWdcllwThbkbMrba5ZJJU21DVdZ5LSULk4VvvECBEZt7NWY4lCR7qY48Yveecp5wjwtyJKNzXZQEAj0D3gDjOaAjhgXutJNJqA4RlKnbcCiDAw9wlCSKVLAhgHm4GhxWevqrq4Ot/yHWYuZkFgU1Mz0sh190AWKzVIyK/18oR82n1PpZMp7kE8qPblNPe+tv1GmAdEkpy8zBrrZ3P62Vdr7crhENEd+CUAtDGADNDevvyMp+XWbhtG4nU3r2Pp2X+7t0JiLvHLy+3x17nks19AIYbu5GbEhOifo0ogmtPLGWZKacj5xEQTLTdrt5rD1T1MToxPWf+Z9+sv39te28zGnK61zanfC58njhMHyMqpYfhbk7EKQm4qWrbW29tPa2HhVqYI0LHsMPQ4+E61tNKiHXbzRQiGOHjzBNFHzpLoI/f//J6735KdFqX8zL9g4+zQPynX66/PoZMJWVGcMiEx9awWmxvN2E6pQIplSQ58a+v+37frSx9DDSdpslSVjVwR0pDDYmkzMJ4ADas18+3V7BxHJ7n0yqCBAIYL59+3bcHIj62zimLjrZpEqlvd2A2j9aiumq7l8ctPHofgVhKQveunpfTH5a5t9a2SokxYsr39bxOJacpDfC362OYJiJOkkuZS5my1FoDcZrXD9+sSRIgEB7+Q3TA2karfV4mC5xYksDeOwBttbn6PBXEYHApqVZvPaalEPjj8Xg6X5LwPE8B0EfLJbsqAgDilNNc0gFNvd/vdd9v9zuiQy6P143Amcs3f/H3p6n8/Iffp1wuy5rdP//68/XzFyccfXAu27Zvtdbr7en56fR0ppJR0rvf/tlPf/PXfZgkIeT9/jbGyPOiFq+fv0zn5zSvspyC0svL24cP73JiSdK2moSJqO3Hk51P63J9eywLah9msO9dhAAYKNVx9/vW1VxtnTIgFOFSsqqeTus0Tb3V7bpvt8F5Ws6XYXx9/UKMMUYgtm1HIg8T2R3scd9yLqfzaS6lJPn8+XNEpJSOdDER37c9HncGyKUEDPd4bLdpmVwHBlYMxAlLaa3tey8l195yyZPkgMACEGBjBLHrEElTmXpvKWc3O/663u/LsoD7GAPicIMmRsTwy2ltQ1tEQBDhruoRxKR9TDkfNPl9b0QYEK31ZZ7DRuYcgIAx5YQQ1tvELuQdHRCnJN/M8mfPpy/b2DUuH6Za6zZ0k6IG65Kny1xb89afLydCoPY4Z/715dO//09/ePfb3333zYfn56dwj3UKdwqdUrre7u/mlJgpSaudfazsspyjprE/pnmJlLvZqZRv3q3P67SNYHJ3/+05/ew9i5+W6W1ENWTC69ub1iYUBQFDxxjVvJkFk2+7mgPA7fVLmmbKxfo4kjcJJKX0NNHf+80Hl7u5vV/ku3en1320oU9zev/uaT0tfX/86//fD3/96b711g+7ZxJTPchVRHQ4NJMIEcKc+1bVrYjUx2O0FmamysJzTh9W+affn1hb7ZqZlgzfP5X/9OlB4F823163bd++v5T/3u+e+Mcv+L/5V/+nIlzkaHvCQXlW10T0/HQ+n1dB/P6URx+f67gP2PrYrlcphZezedDxwUUws2mesmCv++N23+93Ux1qpnqsR8GDhE3NejcbTCzreX56R24eBhGSJ0Bx8F4b+OAwQgwEZGZOpRRgRJlymeGwcuTkpm+fX95/+81pXcidEPI8D4e67cdw5DDXntc15cTTpMAQMZUy6p5L3tsIh3mZRZAAt1qF08f37/ZWP336nMokWfp+xP+mwtSHugWLMMO+7RpxWRdCyiVvWx3aT/MytAeS5IwRo7c6vNVa93q/vgHg+vzsgL3WMk8iwq7j7dP17frtb3+bc7l+/rTXer/f6lalFGJ8vL56hDsQBQKenp+e3n88v//wyx//+NiqpKnVrd1ep2V9//1v9ttNezu//1DmZT2dP3zzbW8tMxJimpcxRhKapzK6pSR77YTugEUEGbet1q0v67yuEyG13kQOMD9MOZkNV2NmdbDRhYmFmKl1+/nnXx/7rr2padtq2x7r03m/307nc5jWbVtOJwso87yez5fz8/m8/PjjL9vjvqwnycV1iEggTfMszGUqSYQAP7+89qG97giIhPNUSs5Pz8/7Xok5AlTHNBVAZDygvknVdAx3B0IESEnCQ82QiIgiAnSYWiAOs5JLyWI65jKpH1wFrq0NtQMkRETaKosEwOhjXZfemntI4tCvx8y9VfE4P51Ih4/2Yc0n4SD+w88v51OZwh4KNaI284iS+FSEp/nz632ecirl9z/9+mGdFcDGuF7vHaG7A8t5Ku+fni/r9K7wnHi4j66fH9u9uxqclvK8lG9PeUZX80PlXbuZx5fbvgFZa89FcsmIft37r7f26eWl7/XP//w3gfTl7X6Qkyl0ztR6vN32CHXwXvWb92cGvzdXs6PDlMuEzJISIVnbSxZ0U/UIIKtr4m/P8zpNTK69f/vxfL/X//iHn//t3/18fey919GHiABETgkAc86pZDc1tflg8xKFOxGv82SjT+vCRD66jZoRetvJVIce99PvLyXMfn3oy6aN8ONa3k34z3/7/h/+/Q9/9e//E/4v/vf/FREy0lTyt6cyJ3prfmujaQSAEOWpnEqaCDw8ABNT76MF1KBAYmE3Azcb2ltr+2Nsu4ePMQCQU0p/Akuaf+V899alJM6JJRExMyELS5ZpZpH9em2tAYT3KoxIDEgYkHKilFByWdZwz/OSStHW8lSScL1vOsbxxZhayllKTqWIJFV7/vA+iUC4pNyHIctU0rzML29XG7asy2mdrffW6zrN52X5+eW1tc5CFvjyel2XMpVyv90weDmvSEhhTGwA5FZr0wCIQEQGsLB9r8u8lHkKBHXQodr6MD0iYEcTgJnCFKzfX15SSSXlaZmHwjRPj+v1en0bY7z9+uuvP/wxTdNoVfuQkqd5Tin/5h/8/R//9u/my/Pp/XvrioTzaf3u+++XMksWJOqtbbfH0/OTSNLRdQwHPN4ofPwN8zh+HiGyiJn2rkQU4cSEAR6Qkrj60N5bJyLtbds3RlId5+cnN89Cp3UdZq+3+8vPP7x8/kzzab/dt9sLEwlRmJVpYmKDsNbm0/r88ePlcv786+fboz59+LCs59FauOZSJKdwZ+Kn5/frsiCRR4wxJKUDZcwk4YEUzFL3dkQUc8nh9vLTrwEwn9Y0FzcPwr7XnPO6TNM0mbuZ9d6stfa4y3Kqe0MihxAhjMhlmueJSVrvOTEGBgQz1r0O8zRlsgCicBOinJNZIOI8Z9T2yy+/PrZ91KqIwmkWWda59a6PWx2DyskZWXIuZZomCp8ZX673bfTn8xlF1CzUHreblGyqGB4RZV3nafpmnSd0GN1t/PLrp9v19vSb38yXJ6EE7r+Z+bROu8aJgcAd8Lws85J673fF11vNDBH+tJSfXre/+uXtjz9/6abn04IBABDu81ISRCLsQ4d2KbMNfb/m9+u0abw8Gnh07YiYy0QRAT7GSKmYjq3tERAIrsY23s35n337dB86z+Wc8S//8Mu/++sfrtvmcaCC99GauwMEIumRPVcjQgwXSZxkXZZ5WYT5dFpLKWGjPm73x+NRe92aOwSYUCRiQiRAJ+Qk79eylvTteT4n3ari/+q//FcRAAEBccny/WVOwiLyMHjtdn9Udw+IP1GePQsTsrkHImeJwHCz0U1NTX2oH7RikQCUlEhYWw0z1QEBGIAieZkRkFmAUHJBQHcr8xpubd81DtqhSxKWbB5ySBAJ1/OZmAHR7dDfSi65t25jMECe8qO211+/LOuyrKvqCID5dF7PJ0Rs98fzhw8pJwIIomEQocKIgL279nY6zc/ns2qvXT3C3W+Ph2pIotv1pqMTgKSUS04sgJynabS91TatK1hs95u7xTEGTolYiGlopJJMR5iV9QKAEE4EjKB9jFFNVYiXZX3++PGwmfz4ww+np+d3755bqwcO+/PPP+3btpzO+2PTXgnxh//0N7/9h/8oz+Xdx4/f/fZ3ANC7Csn5sqpqbw2QGCCXdLgnzDylBADbVnPOKZGpY5LWB4YHwFfAE8Ch1+xqxyi99qZmYV7rTkxg7u5PT+cANPfMvMyTDjWtf/Xv/90Pv/9BzdOcttcXQmRJR9kzL7PWaqrLaV7W80+//wOX6XR+fv7uu/X81Ot+e/k1WsvzFMQfvvleynQ6n5/OlwAgotY7Iq1zcbM+NOc0hiLg0OER5q571WHLukynSdWQjtZ6chtMHBGt94PnnlJKTODexwCiY6aWmSUlNT9Cv72PnCX8qIOSe7S9onAWJvQkEh7ddL9vU4x2u/4//s1fpmlezycklJyZEEwPJs1hIyVTjJBpplT2x+NxfcuZhIgkpVJ0jLZXZjSNcjrnlFJKkghUH29v+7bVx73u99vtJsLLsggzUpKc3r97F2MgRE5CzCTlssznKX/7/vm0Lh8uMwltj3ZZ5p9v9YeXt7/95dWIjFiHFmYpyTxGawQQpvM0bdv2zZq/fToxYa1VTQ149J6FtTbh+Ln6UJ+W2QLaUDMfY4y6W6uJGUUg4h9+WP7J95e//vHz3/z05e16//TYR61Hgt3Ujgtc3WvfH6Y95zLPCxIkSSI8TdMyldqaqplra0pw8LQ13BHBjyxpBNKB4AIATDkJckqC/+v/6l+5QwC4ewCsU0mI35wLBtwMb63X2sw93OHwZ7i7x0ElJyYdGuGAwSzEDBFqCgGc8+FAiQgwtyO7wBTmeZ7SNNuwlMvhepFcfAwkdFUdAw4991SEmIi5TAiBCJLSPC8RjozgqGachADUXXvXx4PA0rKqeS4TsFjXQCAmJnAPIDpdnhAQesOc6zCKECZOvMwrCk059b3utU7zVHKutW/7vreuavWx6Wiu3cZYL5fz5ZLnGSPaY2v7LlkcMMxYEqeUkrhb3yol4VLmZWn3DcKxzAQArqM1CJcs2hoiny+Xpw8fiAXdWq3/zb//D//oX/zzkpfR2zxP61Tw2CcARKC71dYtIjGF+/l8IiQEqK0ZQErZw4/H0GgNiYWl1g2QwH2eCwBGgJmLcCq51qY6WDhx+iqr+VP2qo+hZuqBTG5gQ0EIMSKCwLMkOhQ2wm5OYNfr9d/+f/7NT3/4PTPVx4MQiFhKmqa5bo/D8BYRo26//vHH3/zDfzyvp/OH9/N6KWX65Yffb18+panM5+dvfvPbaV4Q4Hw6pyRmjsQHUIQJ6dAhMocbBAaCuR/vGIRAcLe4PR6t9Wkqamatl7kgs3AaqjklgDiW70SYJOWU1A0jiNA9VANEwA3iq7kuMx5u55xZR48ANRg6Xl6v+75dlvJ47F9er3XfrO55zlPJRDSfLgQw1MKGPm5MAAT1sY3eI0xyDgtJKRzUFFnMY3l+9+7DR5YEphAGEPXx+PTjjyK43TcPe/n5Z21tOi3TPHNKkpN1VffT5ZJyBuBUprJe8jSv8/yupL848d/73bdA8rKNMcYsgAQ/3P33n17vW53W2cP5CKYGCLGOhoBgPie8zEnr7kgY9iFD68bz/Bhx26sCmgdINg8Pt956bR7BzOYDTb9Z8t//cP7t+yX69n/5v/+7P3y56fCqw/qI8HDvYxzfA2IsaSollZKzUCm57vvtdotwSTkCVPWwgrrZMSiICAAnxOMawURHe/TD0yruwcIRwSxj6FYbE85FLnMWGwmB16X3XvfqHlKyqekhOkIcw8M9IoDwoHRDBBIjEng4OAIiExAJZZZEIto7Ebk5Ens42MGKYORwG+5AqbAIp+QBHjhN5fR0EqQyTUkkl2LubYxt24moVgUMsK6jUxJ3qtuOzJ0GRwTF6IOMFABZCOl+vQOCjw6SUp44sYFPaeqjR/fb29jvj1yKI5kaES6nU1ngdrvbca9gyQstl1OapvVy7rX14TG0qR8UrpTF1a6fP5sbiQi4u1NEAJbT5Zgozes69h3B2GWon5/Pl6fLup5qrfdtb70u51NJEgAK8fnzS5unp3dP5l7vd3TjUnKe1MZ+f3TVrrYsE1iYBSfxoSRsgHXfiQgDzNWB1CICUH0q6aBZMHPv3d0QQEQIQ0dvHsLMCBox3G9vV84JkHpXkeTDgSAJbfc7IYCD5EyE7pBSulze/Q/+i//i85cvnz+/vHz+tN8fiPDxN9//vb/4iz/+3d91j7o9dOjLTz+ev/nN5bvfIpIqmkMdvrz7lsspzfP79+/B7PXzlyT8/PTUh0qSeSrHvUHNEgYmNtMpZ0SobWRmCL9e32ptYwxkftxuakpIpaRe69LmJFLKVMqEbsN876OPMU2lDqe9zXMRJkRaClsOB2hVWcQBHvvuRm3fKSXApHq4kWSayrunk7u9PeqyLH/27t31evv5D39Epj5MslAqNion6T5c0rZt9f6mrWFK8+lcnj+aAQshs7hPpRATIrqH1RYYDB5mhOjhj3tt+74/7siEScy81pYC1KLubX1+5jJZH4ER0SxuhkQiGvDLffvi8rv3J3T7cm9frvc/e7e8X+fpu6ffv+Wq3vae1iUL99bGaHzkkJO+m9Pf/3D+y7+rL00D6GVTsPE7yZe5uMPLY3d3wjHaQMKckqlyQIRhwLD4dbfHT29b7X/v4/I//Gd/8ecvj74//ps//Fp9fjyqmuaccy7ECOFTScs6C1NrfXs8tm0bY0TEGMYix/OLWI4+H7ibm0cIi0Asif7ifSk5pVyeLyv+7/6P/2dAOOpEqjb6QIjM/HRec5JqnkoJgFZ77x1TgojH9WamHgCHiyni+PVIanBK4R5mcNSXCX0oIlKS47JDyP9ZNoOELJlETC0iWHg9nySlsMAk6zIJ8QHwS5KYeV1nYmFJe91rG19er6aqddPeiCjlaVrmQDyCQqZm7oAgxJwTEiMyYoy9Ls9P87LUx8OGni5PcbQAkQCxzCXMrDdhOawKvbWvI5ucTLXf7wCAzJRE9biKwKi1Xa+n8zr6uF2vJHJIA48bcV7PZTnttzcIZ0ltezBDkkQi01QmltO7d3vTPrSPkVN693RBpjH69ctbLuXp/RMDbrcr+eBcWGTfa0jampra99++77UxiYZzxLrOqmYRCNhaz1OBCEJwJAJMWZAozCJi36sIpyQ2zHQEogFe5sKI98djqD62HXNqdbTWhRAPHkVvow9AmOc5lwmFVb3k6bzO6zwxk6ptrW3bph4lp9M8B1EEbvdb2x+t62OvPnpZ5pLn0/nsAClJzomQvPeXl1divFxOp3X1ADiU7MwIoKqEmFNy95xkDLUI8BDCX19eau/7Y4/j/UFoqhghOeWSRhsYkEte5uW7bz5Kks+v92GmOlR1Om4AAERMjImptRHhp3Uy865a9713PaRHrQ1KlAmnuXSN2kZvlQACqfVe9z0RqCpJwvBUplY3iAiPMRozu0cqebm8c3XwkUshFnTIUwGAum9hLlNG1/G49fp43O+ff/7JTW/XKwRISqmUMpVU5jStX37+ZZ7yfDrF4RAVQZLnjx9FUsq5rDO4F4To7fX19ng8SpYiMpVsKM6cJL/7+A51gDZEFEJzZ5aL4D/53TeP2/3T/fHusj6v5T/84eXxuJ+WKUhue21dnXmY9XocVJUQjk3Rkct3d4JIhIIxM/6DD0uY7kNfBm3DbXRAZOZtf2jXl0+f+lDH2B/bfr+bu6pKEjy4IgjTNE/TNHofrfUxAFGYvznnf/Hnz//yz9798W381ZcuxBKqQHS4p5gZCyEhQVxbj20HANx2Fs4pA6Kp5pSXyzmOvq25JIHD36rDzEgSMZnZQarq+xYBph4QDEhIgATMrmamLAk5AZKUaTpnYSLE9bwSUkSknMf+uL688TxLybV1ALher9M8TfM85XxaJiBsrT/ACQBY8umcc9HenQ+SnAYShNfW2TzPcy68v70BUgDuWzXHCA4WTgncbfRj3wruXf1+v+UsgITEOWfAYGIdezsiV4Qk6UBKqKpuFTBut5uZyTxLKWhhNkjSwX2+fflMjBQweq/btpzmiCjzfADX749appKnYh6ErG6hAyDW82qmL58+99YPeYlIOz89yTQDpfcThjsBliwACCFuNsz8T8G6LFyOzbw7MxwXNCYKiFZb+vptDSlZmdRDEMzdHE7n0+Px2PY+um33u0wTpYSI+/V19GYORITqznaeT31UYLxum0GcliUClmkp0/zYt9Hb3jsAllLev3um9885Z3fftt3cp2kS5lYrM0XgVLKs5bv3Zwtk5qEWAHtt3Q2HESMiHk1xQGiqj8fjUPIU4Zzz3rsjELGOYU0BwsfAWg9j0FeunIdGfP/xw2kuTbV1YmYAbGMwIgu4mpdyr5VT6rdtuz8kpa66bxu4g2GYI3jrDRCDhIS9wjD1PhzpGKGaWWsDEaTMANRbHb3Py6zmow8ibve7B+SShFlyCoTRKiGOWgMhmhGiQ3z68Qd1e9weaqrDUinECUnGsDH2p9O7+XQ2Ha0b5UwoHkAQ9/v9cn7SML/dwe1t2wujYXx5fQ3wkvN0ukRA3TZG+MMf8jRN58vZRxeCiCDm+vHDp7/5sm8b696Gsa0z6J99e3K37hiDt9b3asQERAHUHtt2fzjCVCQiRutqA4K+5hZs/HTbP8xpSdyIRyClguFt6P1eb7fr2+ubmwNFmHvAOByFEWF+xFeZOCcZvZkbswDAUvKHd88Py/+3//Z+HbANM2+y7zvndByyze04nzgAOFgABBDRqGMcKVCPpg7EaZqnnDH8aE0zCyJ7+NF/g3AWDo9ea++j7XX0jkRAyEmmeRmthzsS56lMy1zmJeeMANu+2bARGh51r71WYkYiN48IjwD3cb/drrd1Xcs8l2liIjMDIkqynC/CAsL68LBIy6KtmXmYSZ4IyNTyPAmlxJJKKaUIcyrp8IaYIBNzSqaUHAIQCcODWTglN3OPrgpJfAwIcHNtPdwdwdwgQtVMe7JIZbIDTcckSGYKjB6u+845T1NBJJJEzM9P73LOCuQ6IpyIEKLW7mFJ5HG755xPy7wRG7gOqsO4juXEbmNOuamBgiDc9pbmaW89AAiCCJdlhQh0kyyINMzNPDTq/lVAfX66JEZzBMRjyuNu+94sgg3neTZ3VTvNH5hTmUpOMt4/taGO0Pb6eNR5WdZ5Pp/OW2297jp8f9Rc5LQse+voOOWp5DzcjqiqsLg7E4tIJkoiIoxREPH17Y2FnMjNzGydpykJEjKCeWy1uQdAAITVnQ+AogghmVtTFaallIN6Olo/yM+AZKr19TotC+dMKfWhse+f3m5Py3KaC7OoKSI/tscxLhweFP7x+VS7OiCl5IAQKCkT0+N2J0Ttg4lf3x7TXADpsHnaGGYWCISU11Ns+2j7vj0g4kh8WCDnydwDcIwxzbOImJrpjkzbvtvXBZ/mnLSPacoB7A7n99+keSk5+xjh/fZ6UwuZJ1MFSUgpElMuRAJuo3UAzCWL9bFtj9tLktzr/vzhOQm+frmOUoZ7yqXtDwBDoH0rj8fjmIQ/PT0lUXm7peVUW3/c9x9v7V//7a+J8Ek8dHieyvlCZdrv19vtypIQEMJqrQ4AWMx8tCZMy3nVPpy0pKnV/W+3neKgJROJeB/MgIc4nMV8mPo0zchJ3fGYXUEgIbG4+/3+MB2ACODTVEj4xy/bT9wwSZLjs2lSR8+ICUEYwy0AjyMNERMxEmGS8ACPw/ZCETkLMRBjADHzgSRHDGZRVVUlETAngPPlIsxDda/HuK0ggDATUSoFPMYYyASAddvqvtuxpDjoo6plWXLOEG4BHuBmx+8CwN6aAez7HnGwWRIG+L7h+TwvMzMyopnZXOq2Tx/fny7P22NLOeWUzT0nKTnpGKP3er8SU1lPOU8H65xznuaJiQDQdNTaUciP/YGkqA2QzNT74JLBAkyDBAEgQHiWnFutWntaJrvdfej6/l3vQ2tPmXTbdIw8r6fndx/fv1+XNSIS8/3eSykBx40kVMcYipKW07pMJc9z63qEe3WoOzDGTz/8dL8+Tpe1b9sIfP/9N+s0ixAEBOD9tnkoI2RhJNYIADTzfdt1WEocHhp2EFkPcU0fFuGt7m0LROi9ExFE+GjDuksCQIHIuWSISZhTYrSShDE9wpgPGqW4WWZ+Oi9wcAT6aNoHk+YDatSOJcIwc3dmioCnywXwGFlASrn2QaDTVAgpHxY6oNpaAKhZaw0AIAKJDuzyPE3zNJ1O6xjjss4WcL1eP//y6zENqLUWwMTMIh9Op+elkKC6hgcjq6qZKULrbmbbVr/58HyasgaWLBFQ99r6cPP5vADiY4y6d0pkDqrtaB2gpL7vSKR9mLukVFjutysjWThE+H1bni8pF0lpKlPKyc1q69O6ksjTvKrqaI0Ap2UicMF49+G9h0/T3LueS14TsY/W+s/XHUQYorV2f3l9vd66ORAgS2bS3r788AMRhdvj9tZrDYDeq1m0bbu+fJnXUy6H6F49IOduo67L6dtz+c27QgTrKT9dpvsEv2+PZS3ndV4I1ozh+Def3lj3pay6ZtAMkkarxPz88f3oAwByxiSifbAk5oQNSsk2OoXfXl7dLedyzKO+XsSYScRbR6I+BiJO0/wnNkyIMEB4AEYgsrszY87ZA2rbU0qFSGGEh6oKeJjrbVfpAkRw6HnhoKRnYXZXAw8EIk4siQ8w9VEhMhdOKbsZRWCEm+kY6OFMQhzREY45mhABRNRarY/z8xMI970hIhoSM8uxMdBjJhIRQBzHnwPwK0DnyDSmIoftq2TtzfqotzsgDNWyrk4kSYiImJFRH0okScroA836fWgagBBejkFfa73XvpzPZsGMEbBvdZonAdJhZs3Ne++2jdZanhZAAmREcFcIDMfeu+TMwQAGoyOzO/RawU0iR+Dh/hp1lNMaoTZGnpbzu3dPT8+c0t4GQiSxJNyHRgARnU4LuhvQ3tr753MW+fJ6JaQwG+bzugjzz3/8cW/j+ftvOCUj1t5r6wgYqgigESlPEd63W2HhnANRzRJzSgkBEKD3HhGtVQDoQ5npMC1EBLqnnERk3zZi0t6BekpJxzg6Hsw42kDAVPI0lVKKECFA3Tf3AhGIgEgpMQALc9fjQQxmfnA3zay3lkTMaYyRc2aEPBVwNx2qYRDRurohoJsd3oZpKn1YuKtb6+3wTdNMrpqyTEnmRO/WeZh/8/75si51DHMffSDRuixLyc9Pp/dLeXTbNZgQAByBEW+3u6SckoT76/Xh7qWUY06HxCWjuQ3t53U5C7fWDckt/rNhZJhRxOXdu+1+H72Hu4eHx/1xy/PEQpGo1YaAkjOnRCJlWeCgzX6dCLNE4pRRhAHq/RquOsa+dSll1DZyOs3p44f3l6fx5e1xPs0AyN+9Yx+/vtUvTrW20eunX5v2vjydOLjE6cH88vr6+edPy1QuS8E5T/OUz+d3794TxH67MdPlcjqX/BfvTv/kzz4+mhKl7759rm382YmnIufTHH0gwDqV35wJCEnyX/8U73k1kS+b9G2PRHR6sgAiCDdTZWILz2k2Mw8/uDuApGphdvwwuDszT/NyiARGbQEhOVGEt+YR6MwskpIORfJ5mpZlFuFDK3OkoI8wWLgLAoQHAqqOY999MEVVVTyMCMKJGZgDIEwjiUWADkmEAWNvYHGoaiMAAdwsPIhKEDkCS/Khx/XzQL9yTmqmjw2JhKX3ThHTPK/r2rsOU9U+hlJKfd+NB3FCJimFqDAnklSmSYfmJGWa6t4kFzOn3qd1yTkHAEsGiLCYz5ekRgSAmOdFdUhORDTNc9ubCF+en5mplNxaF6GcshUhlqHWbfSux4R+ezza3s1iWtdUcm9NSmmPvd6voeYePrqb1se2Pj+t56cP33wsU86lYOAYbdt3pBSBSH46n0QyCzNh673W4aaX8xIWGiRZPKL3QQClMM9Ta73uNacUEPdbBcT9ceckIfT0/l0pBcLp6XQC2h77y+ub1Yphy/k0Tdncax2DVUyvX96QkcEpTAPfffPdMOutmQ4PZ+aw0NEisCwzhLdbXc/nlAszI3LtDd2BiBLWbZec1T2JoPD1/oDrjfmgtowylfV0Pp3WnFIQpZScnBEJMCVxdkRiJlVFIDdXs4PNY+77vmMAM8/LPFS3vSKAe6TEGhFI5pBEIqdosUzzISrNOWNmU7vf77o/lmVyoDqGR5jqXms/NOA6bJ6ajh8D5mWd5pkJmUKEiDmXknNmonutQnQwxBHJ3A5uogNWZ9s7EYzWJaXhkUueZZ7nEh6n08zM798/a+vMxAzm8Prltda2t1bmKb4qaWheT3M5jsPRx1DzXqv3fr6sxIgsvffusF0fSJjnBIGqet17rvnnXW179NbwS2pdc5Y///jum2+Xb/L0qGOh/uuHJ3LNmdnteebT0+Vv//jLH374VHK6rGXK6fz09OtuR70Un5+IuLZ+8/g3v9Sfx+uHdTrLyAQly1rSXtvnvf983Xuv5wQfz+XD8+XDb95d1uXXXz9/ern/7XXb9pqzrOciDNq1tSbCCEiEIowQBICAkoupfo2mIg1zQgwAznm9PEVo32vvPeVEyG5x/FjmlIiQD2MDoyQxN/dAoJwLMaMZMwch/k/+Z/9LTgIeX1UoRFIyIn5dRiAhITOTCBJbb0jMOQMEcxJJOgYz55ICQCRDgLo5YP4qhslIWO+Pw5J7DDxSShBBzABfmQSIB0MFAjCI3Kz1joGjbixpOl8oCQsfUFNCiXDTUeapb7XtlZMEgLlN85RSLtOUUtofdzc3AAhMwjkXTtL6PvZWSinLTCRmGnE8zmGrPee0zIUQwuOIO5i5SB691Vpb62O0gLBWR+syzdpH33f3kDIJoSSZT8uHbz7O03zwXnIpR5oLEIKlDTuYNq210VqMsVzOHkCEJXFOGZhNjYnMPVyZEACzpDhahWbq8djqY9vXdcly7Kw53NyNiWvrtTYkCncMzzlL4u3REKNM+fXL27QsY3voqPOyzOdLWNgYSOThh59Q3Y8zwiGATrmklExH33aZpjwVHbrtOzNLyV9Fsx5jDGHWMVSVc5IkTMSAU57meb48XVhY7SCMWSklAkT4fntMU8o5qZqpp3yAunrJSVhKSnZ8e44kkvt2fzw9PR3kWdfhEOYQFkfZg+kYdOD15SUwMKBH3G51q7W11vddSmaWUrL2DgHzXE6n83I+U0QWPljSrmruasAYktPoqkORqeRMiETY1VhY++itl6k4ABJCBCGZWd0e2/Z49+GbdZmO5vk8FWHe9vrlei9TMT1wVgEAetznzRCiTPPt7U1Vf/Pb79QBgTzisT227aGtl8PagxCATNi2u6ozSRCVaQIMHab7tq4zMe/XKwKc1mnfam/7JeH3pyKMmubny5Oa//HW9m7dtNZWa0WEMs9q7mZlyoxYwuac6+N+zrQu5cvLTd1+uu/e26g1i1xOy/dr+ovv3z+ta3X/f/1w7wBhwUxHadEdEmPYcAjJmRF73W+3++O+9d7dgwi+RspFkjASogMz2NAAcNPWuschSAEKhwBzZ2IiQIgxhqnmnFMpeoD0IcJdiFDNwz2ldFQy6SuimA/vE8vXZweie4DWlomI2L15eKhFOAkyipsdITdkAXBtlSE4CwMEIQQGYkqJRExVmFtrpppS4pwRgJkcAAidv04HU75QyillFpaUgKiUjIC9tmVZSNh4pCmnnCMgIpBQ+8hJuo3wI2gBaSopyeFnWqZ5D9737bDIqKoPZSImliJJGABbazaGIxIxiqh2C0s5A/EYfXt9HXUnZmKBgOn8xISny+ndhw/CTIgAMVpv1ZEoSI72YikFXE9zaZ0DgZkq0n2ouYvkMANAjfDWWuvEHDoej7uqzvM8TdPBLDvyHDTPYNbevtxbDdcwDwQfXpYp3NVivjyrBVLUVlPK+/3BiNpTmrKZQypTKdrr/fWlTAsngQAfQJlKmaY01X0jxK8L3EAh6HWvtV6WGRA5Z1R1M20tAlptGIFEtffH61ueJi7FPADAwrXtzW1rrZTCTMe3aaj11olRI8yttUDErztKdzPz4N67mx05odZGStx6V/PWex9dWNpey5RbV1PLU6KAxCSJBGNaFkUYtaNZmUobQ9WdCDlN8zzN5cvj0Vvvo99vt/ntVObptJ7mKa8lUyINbuaZQIdmgjzlgx/UevNuOSUBkJwYYJrLvu9uRELax+H9VPVWKxMRuHrUpqe1BGBOOWdWcDViQR9d/2QvHr2mIu8+vKtbGxbuvt3e+jDKkqdJJNkYgdDdwlyEgZgTMwtJYiZJiUWZaLjNc6bTaXR/AI+MLuXTaL98blm4rMbb6zrNzqxCwyLPS17XVvdwTzkRZgIYvbWIq9W69593pGvbrlfVrq2qdmIm97cX+7vP8P/+4e3d5TwLVrWn98/TJHPJY9CXqzk4EjHJGKO3AREQsa6rMN+31ls100PmokOtD9NxAObMHMIPRQ4zE4H7sVMxQKIsfQwbgwmnqQRQbWOMAQCIMHSI+ldYirkT4VHJFjgOC8ncAdGOt3UEcwIk8CCmI1SGiGFmapjYVCHCQflofKl5hKgw4KHzUXMbY4wh8+wRTCTTJCIAQCIsnIhUtW07k6R5QkJE0t4TzznnAAz7mthGRGudhTiVOJAF4bU2VettlyTr+SLMdBxdiBAhpVSSjD5YeNs28JCSRlda5rLMwmSjt1Z1jCPg4UdZeQxOEoCIuJ4vhLRvD9NBInOZnt6/z1kIiSD6XpGOQ41EeHhsWwWIVMpee6t7Tp2FVU1Yzuu8npbX17dtu4Lr9kAgJogDAtH27UiuA8D9djc3BwjAulVRH60O11H3vm/MmEoew9q1u/l8uozeDIKcUp6OwzUy1q3OlwyEnFJOqYaLMKXk4blMmICYMWVCOJ3PqibCvVZCQuLlfDIPA+y1fg18EW+PO5fpEIpra0DIOZGwu0NAHyrMJU2O2N2191b7us5ZJIEP7WuZ3fXxaJI4J1FVYq619d6HTmZxXE9yzs1s23dVdWSsrbXONHofQdT7IJFt2+vWLudlUhlHbxAIJJnaUSk5PydzFZE5pyzy/ptvbrddpgxmADCGOsBQgwJqZu6JydWY8FF7kkTkmRhy3nRHIo0giIBoQ1UdwoXSsc08P13KNB2I12kuOUtr47G182lZl4kJz5fy08+fmxmEb49tuz/yXFIpb19eIKLkKVRb70HECZkZEPf6OBJOcmyiHObzEyO6uYj0uhPAVIrMM4Kf1kWHPbbNIjRgDK0bhZTz5UwQfd+rW2GZp5xSqnVDxFLKfn8sa57n+XG7WziTAMK8zMx8RCLq7f62bWZHwYQROc9FEO+1bxiFAer2fjp9s+DH9+//4w9vf3y99THUouScs2gfYRgRc1rXZfr0S793HX0ceTSmo6fk4REIBw3YIVpr4cFEqiPAiSia+5+eeoFkBuZ+NIvcwy1EwzEAAd09ApEQHIe7mYr8SQ3K7BFEBOCcxCMQoaQC4AjQ+9A+yMEPUePoY3ROwpQcoHdFiON4iCzm7hFJBFmY2d0BIOXESb5eQSNEWEQCUDUITTLTsWM1NVNTw5RdBwCQsKsFBAKGueSU58mHIrOa7duevvqi8LDRWa/adiZyZoseEcvllHMGBHXvfRwuxYO7zci9j9EHqh5W2rKsl/fvl/P5kHKWUg51YwQCuEMkzoR+bCLLVFpXRKyttb2GuepRAjNXm86naSrMpGPc3l6JwvqYlkWYv56VAuq2H5Tq8djSPAMx8EGkJ0wTOOQyee99dEoT5+QRKEIlEWAEcc4xOjD30ed1CbfRKyCrjJzLseDre5/XNJcZic3ddIBHBCBJnuapJB9DRNziy+vb6AqAy2Wt2x7EbdusD8riBgRAkkDE3G1oILo5IOVSSmYCkFLqMDVPHjoUedQ6zO39ejGzMbQQtdaAWS0sLBQdQsdgSZh4Xue9DWSe5wUgKCViOpep69h1OOLhJ0QMU9vaaOp701wmKcmtIaL3/rrdmehyefrm2495yn3fBUF1aNvA+NXqy+eX3loWySWfTqe3fYwxiIiQTudz3TYlAmYUYpZELDmJJHCfhIngoCeIyFBrraeUw72rvd03QgS3nOl2u3769dPhGBxDaZecc7ib2TzNp/MieTKP07KUeTpiA0O19t5rKyUf7atWGzOnJFySufe9u+lpXR9bhfCSJcLVQ1DQsplBOELkkoloqHvbJaXLMo8xdu1JODy2beujIeIYx3LMknAQnU6nkvI8z8MMEJBoyfnEkcJY6HJaEfw5x0LhdetXiO0mfU+pXN0Y/LuV8ykxehGu6q9bY3suKb2+vD1UAcHVDuY1HoloBFePiN66JCEkDw+IOFbeCMKcpiJ59jYwgBhU9Su9HQAjAhE8PAyIiDNTEmQ6svyqg4gA0d35q4MbItxdRQgBmQ9VhyGRcAai0Vs4gGASOcqgruoRiMqS1nlOOR9ImWOVRoBoFoc5J0JECDEsGEGSAGA317rrXutjI6JpXY7AV54KIXJKRHx8eAIAcwoPd0PEcDsqA+G2Px6jNaA/PZeJwcO/fuWM6JITp0RIyKRmRFTcbahH9NanZUk5BwDNRZgBQN3i2NIdHTo8WmLk7ghMzAgG7m4xz8s05QjX4XutytZqH2OcL5ckycMfb68RIMvJh9reZcoB0VpTNRK2gKgDqbu6lxIOaMopAZI7MCeIMI2yTGFea4cASaXXXf1Q64J6jG33iDIXFAEW8DBFKfMYiqzEQcQemHM6nRIBHCDCIJKUkghJal3HcBQaBVxbDBsRsFVi6RreWlkWV+VUADE88mUuOSNSymlKiRmPN1/Kxc2nqbTWwqyUklNBjOeniwXMpYRbANTa3Y9rB4zWizAzgUMfJoxJeCpZBiaEtZTz+TQJInon9trNTd29N1UbY6zrbKqqo0xTLtlteI0SDtp//vHn4QFhItJGf/3lF2ae13VZZwMe6knE3T/9+hMhJslmDkwpT+ijTHNZTsI0lVRKSYzuxkSTcACHGyDIlOtW931/upyi+3I6l/vjcb+rmg7NnILFQh2je9zrYIMkTCKIuJTMEGFu7o/axlAdve6NGbsq7HtK2QH2velQQCnFe+vMwgQl56lITkf+y11HuKeSCejx2MbozBkZlzIZCSbp2g+CQyQwNxs41OjrqpFO754J6UAqFYr3M0+gZP0ff+R/+4fHf/iiEfp6rW2/DzVVZSYPEMLbr2lJ+TLnf/K7d2eB5q2IfPfN+6fL+fZ4mNrj/ni73tz0eKa4k6q6+Rj9mNS7AxEm4ZxSmdJpKshSh7s3OOJgAIiAiMJfJYqGRMiAhOaOEYf0Ied8LNe/tsQB6NhMuYWFsEiSnLND4PHA4zTUxiGiCAAPYSQWTOLuIvlIvqkO0+POHMyEiKoa5nkqiXOYA1EwsDsRDVU11T7cLa2nJJIyA4CHMxHSV5krAIUrIh0LGiJgQkIgBAIMIvDglJBE+4hjcozBSGbRWyO0nBJLEknTVPro5oHhmAQA+OmSS95qMzURhgAAEGL72g8bwkdMCk3dzEjI1CTJkWOYlzkLMrEXD7PB/BXoHCEY333zsT2db29XMy/zHOkIcBIEtdqXy4nI676ba8oTAQWEm1ISYJIyaW0OBu6jjwBkhPAgdjeglJAMmYCES1lKKcscAK4KCMvTxXQwwKFJPorWhDHqjhEG+PnXu6pezqcylXWeSrI8laF+Y1af4gzX6w0hRm9125G4b21ZZ8lJluW0LKd5zjm5WWLOOTEjEz/2utXOCE+niZ5Odd8ZME9JVZOw2hHmwJQki9TW4bgOuEsSIgyPUhIAJCY3BdMYXQjBddudCYl4mmdOmkS+1p5OqyQpKbXajigyQZQkM6P16l2H2U9//MOmpu6ttvvry+n5eVtXG0oivbVUinms6zqfzz7cFLdtD9ec91KHJEGIZZ6eni455eOsjYgMkRB768sy9dFa7ymlofb8/sN8Opvq9tjM/XQ658NJ5uZmSRILu3ur1UenJFkSIaUsAah9tN5JZNt37UPHJinlec5zbPfHaCyJgUm7ITKaRngiPvYbrpAIiyBEue84uqopI0LEIbUhpGMZF+6dR69d3RiPyaYj4hHE393+9qGt7eI2dPz6sA0TYsak3nggVBta62g1TH8h4ZTnUv6/f/Pr8yzL02UELZOcl3Re3veh42lZ13y7PWrtex3hjgBEOJVZciLEqaSSZZ3LUiameCpsgD92BQgWJg/OGCLHeuTrUwoBJcmxBft6M/6TyYZFDkLG8c+IyCwWOvoIBzMlYeIDiE7HxsfDY/QeCikzIDG6R20dceSSzQwRzQYhSkrVlCghYuv9SPmj/CkscpAog1ikSDnsjimng3sRiKaqrqiWUh5x+CsoMMYwRhZ266OnxCklYWYm5ixkAcTSx1Fc9N47ISK4hSLSGN3UjroouBOi9da1Y0AiZkJAOs6zx7iaAAwgIJBou+2MeFrONjQlZkY30tYF5aDFi0gRahrX6xXdRMjMSkrp44e2D8lJ5GveaoxxdKS2650lI9OxVA5IrUbdK3w1dMSBW7CBqUxAKZVEzDllADCLJedpXQhpXmZ3u11vo7ayLjklO26zSBAArojU23h7uZJQmWZAIcLWdagjs5k+Eaxlmt+du7m7n9dZPVQHqOa5EBIjuZu6MTEToNuUxc0IwdTaaI/7rgDzlLatBkJv3SIcAcNbGw5Qch69D7NSykE9cLOUMwK4OTMfBRAmSuDeNSiGh94fQMxEahUQiWgqE2BEjcNpb2YsguETE0KoeUxrlvTbPz9dCv/Lv/ju9eX1drv94Tz/++1xf7vV2qwPThIRqSsSASCk6XjQjDHADYhoNA/z8H2vavH+aU2eCosDuGpKKcxcxzoVD1B3TpIJy1Qi4HQ+H3Pk8/nsh797DCbuvT+2PQkZM6ru7uF/2vplucg8zVOd0u1236uySM6p7hU8HvtdSi7FEXFvlRAgrHkwUU6SiFjtbavBxIwRgRA6wk0BIU8FhSGAmFRHbOOwvmKSA4dDACkLp1xre9Q+ugHRf/3HB0WUoud1+ngqOPFPr3dXC4sRqOophevYbFielvM8k2QEgbgIIsGX7o+m2nUq5St4KZwJCVFNmQUBMBwgIMJtcMDTlDzgj2bMBBgAwJwB0N3lwGh8fWwhppQw4Mh0RDghBcCR5jdVIjr+t3JiRDyafxHGkg8Qx7wsJImI3MHV5Ku2y1nZ3ABQhG00R3IPJDCzvj2AUPKMX3MbEQCSMhGJJFAgTkJJUiLAcBeS8LAxxlAjROZcCkgQAXu0vSMoHGBQFiJE8CA8QHCIkJiIWNwJEAnNo+4VEfNUWFKEuruatdYP80WoESExqmFESAIOAgw3PV4gGBSA+94AsGTMOXFKNkzHwIg00TIXV3X3urcgBiJBAMFSShAN17Y3qGM5n6Z1TsIpJUYsOakpQLhHXxeWJCyuet+24b49csu5t6p9hCqSEBfJmSUfOhXhVOYZwnNOy7IwERMBUWvGRMvT5fJ0XkqOkggDgYaqufcjr3A6O0RGYjaZs5R8vd0z5WkqjtRUiYiYIGLKYgE0l8QM4YQsjEO1DzU/fiih5GJuQ9XMzIFLIvfRx1EZX89nZux9lCSSxNSTiLlrhO6NAI7+5nH+BUSPGKPX2myZn+YijNDCA8cYw2o4UE6mxoAppW3fkDHlAmYQQYyJWTC7+72qA13WycKqxvvT5fnp3ZzwX263f/zbj3/5l3/19narCRmR5lNZ1qammNZ5CYjXz1+YCd1TRGFE15TSx+dLkLyfy58/zyTpbfjLdTfyyALIWGuac1PrPTis1YapuDnnRMzu1ts4Nk3okDKLMACY+faoaubHGV8SbLDMUzcToimlp/OpDRUMHiDnRT48b49Hb52EjyjL8b5lkW1vRLQuJQmDuxyDP0RFc0B1i9oifJpmIRThdDlPKQ+3AAgEUUPCkhOobe5BNJ9OhFQfN7PogS+3XTO+z/Tbp+k08U+f3sQzTbLMs0WQ5I/Pp989LwFw3bua/fxyS4m/3Nvnt/tj2wFCj6MWs2LIIT/t7cjZhgcTJqYkXFuHiDoCANw9MRJB76qq+D/+n/7PERERSilHjowABI/ObYyhw8whDubEMQw6ghRIaOYHswwIIcLVcskpZRJGEvdwd0D46rYJd3OIQDhGMxERNtTdJCeWzMyAYOZIxJJSmeg/B1GAyjJLKXkq67IM1frYAYFEAuAQi6QjkfSfPzMplynb0CSEKbsampd5kiM4AQAA6tC7jjH80LjFEdSinIWYzdzNEUKYJWczOywBjAhEpooALAkD1FQ9gAgcdGieS0mJAI4+2DKVlKTulYgopd4auLMIiThArW3fKxHlnBjp6FOs81RK3h4bICRJdnwUhclBVae5AFHrum3b43HXcZC+F3O3gEDcbveU87zMIuxmJaXnp0sSaWPc7g8mWqZ8SMjj2EW4j945J1MTkT7MPKxWdz1fLuu6ttG3bc8pTVPe9iYiOafex7HHxIOpyxQeB2B6r9XMmCjlnHM6EpKEPNTmeUKI1oeqiiRhUrPa+pEd7b0v69pVj85QFg4AInSzQxNi5kiwbfskfCkSoZ9e71QmYLk+HqpOxAhwfPhH11QkZUH1peRpnoapm/fWc8kAwMzW27nIOSezOJ2WE0ZgsFXfHt26t/4wms/nEXB9tHkp9fH4/Q+/XtaFCV0yAEzTlJL84++f3gb+zafrhPH++WmZ8gjYzcdwQ9wfWxtD3R3wyI5KSkPtOC1GwHFTAfAxBiNxSgdlprZ28Fvu+34kigGwj45jOOLltKD7NKUlZQegJH9KeEUd1oceD3EUGarCBK6EIHSUrx2ZQs3cltNJR9/uGwNclgkxHOkoEqkqCGvddfS55GZx29px5bQ+emuQRFjqtoUb2aDR5ql093DjiNNc9q5BkhLrvj3aaF9xiSFJlkkA4vHYbVhrTVVHHxEmRBqO9JXxe9RIIoKQv+bFmAAdAYqwmdWuiCARjkiIZGZI5KoEaD4QwR1URzeFCBsGhEzsYTZ01IoI5iaSkIiQ3M1U6wZH6QARAQn5gHchHBaJg+kY8HWxcNxtEakyABzHqNE68kEvIQBiYmBConTNkqfzu3dzKZIk52Tm+vWhwEcnzt2naQKAbmFmOjQiLIjMzC0RqVnv3U1zSutpxa9fAOx7D3cWdhth7nrsHIQp4L+j7cEARByqETRUCSEBlpwSp4QYSDo8AKwPZ8wlh7sOrUqmeqRSzdRUkUQQIbwwScmHjQ4OAKwHIZr6vT/6GOaesk/CJUsg9qEpS0oy1BGilLRM74eZm0Jg3WuMMS3LMn9kQmGCcAg+L8uUCAnd6el8IiS17m4RwAxt2PX1bbs/yjqvpwURW2tMtJxXQGARQBDCdZoAwS2OGF0f2nqH8Jzk2LFoOBMd6YqS8zANj4horekwIAxXM0OMknM+ppB0JHMPFgkfJDUdQ4cyAGEws0awEHio6fGZd8DwQCKLqNs21JA0LHrto6v2lqYpT2WYmVu0yCmf1vI0T0NHM/eIbkpKLKKmb7fbp09tLdMwX07zJacUMC/ZYfbISvZWG7/2lGR0N4p5Pn37u+zIoF0daxtbVR6RvtQ8TwPo1+vjU4fvnpYpJXUjZnQQIYdkrSXipt0BOYAJEydiGubCMsYgxBCutfveWJgpBIGQ5mkqhcdwM6u9E1IzI6LrY2eibdidGgJwYgqY51JKKjlabedpyUhfqnUPd1OlsOHuyOTh5IAAWYQiiqROWO+PznF5WtuIMCXwCOPAKYtapb7XDkxMgMjAKMzkdERYCLN45HrHoQCOHsSS1KjbCNWoXYcSkx3kSxEDvO8Dw4/+1pQXN9v3unIUipeqQKJBAcHgjMFEgexIE6Fqv3VHxK03czvaMnIwTxDZPcYYgIgRYQ7hh9ESAD287xURmBjCLA4G7XE7HYhfj15mDgAa0AkP6BgdIcjDjXok3I/RmxlEGAQEMLEOhQhAJREMcDV3EBZkgJQlTxCeynR693y+XETSkX9xdw1wJAdAJDUjRBH2iEJghEciGAIAjlkTRwQKo4MHmDtEJAQuKSLMzLqGmiEgMg5zd3RAJvPISeJPA0Quxc0BDvOxv71eDwmj5MkDEJFzJuLean1sQ41bBgcESMzIvKyzMAdA78dTjRPL0G6mzFxKARzHDTcQSQTcA/m+dXeflyK5GMB9r61WwjjNsw4NIBFGkfF4uPlX55v7MW8iRHcnYURCxGEqIof9t9cmnC7PZ0lJskyl7L2Hw/E66b2PVkMN3A7urFkkwtp7NwtVTgyA0zRtj4eZUc61tpTkcjn77hqKB8TVPUtSHwjRWj9eOapahxITM7uqe0pJInz0DscBBNmGAgASEzP6cfUECzsyXIxFgWptWhsSh0WYIgS4j70fnpFlysuUhPn18XjcH3hkGUTMrNW273tA7K1TmZZ13rp2C61N7gJEW91TzrW2um9lmggZH2Oak5tvW3MbBzzjgIxf+8jM4JaY9n37w2hZaPraarBpysSUmHPJOOjteqt9LOuifWitRFRK/lp9UTO3iMiSw+1+e6SSTa3kvBRBFEYfGlM671v92sYH2McgCHbutX369HmeyrzMAL4mupzzGDqyMOfeZW94pBXlMJFlJjpYjEPVIKc90O59tE4IQhjuqjEV4Ty33s2HTNxrI8ecsmRwiD4oMSKxg2dmG8MjXO1AfqoqRFjEsi5I0HbUoZKEiFut1jsgMBI5YEDJ6ZxxEpAZmPg+ANwumZ6WzISbGgH80w/8x8/4X/9QAQMAgVIA6FBBJIhwVwAEB3fHiIhwHWN0M0PAgNDeCNCIDgwQBJopoEM4BBxWAkcGCAwg5GMXEOEAfExqgY4wMGdmd4gwcQcDzEwH9BqAU5KUPYBEciksiUVYEiHN6zyfTiTSRz8iP4TA7qbmiAZGEcy4b01ySsKmGqY6DHJMeUan49YjxDgLAboDhEcEEydBHRbqOgawcAZzDzOKECnApBGINNSJkCOIkAnd1DQCOYjaMCD7Eze8jU2vb6/77b4+XZ7WlZlHH0F8HCGZqY2xbRujLOus4eYOga4OBU/rXJGCSdUCwHQgiUUjodYHErFIrX273sB0rP2xd87ldFokyfndEwK2NtQ0l5ISm3v3qNd7mVLJBZCPCFieBACbqgRMU04p9d4BIDEvp5xLfux127sOa7WnxKwGHjnLNBVmJrcgYuIIdPMjJ3gQLbd9P64wB0CYmY28t8bCRykCIWiechZTIyZAcLc+VEQQ6aAtH8d6BweP3vrw0DEkcYBOpYTwttXrY1+WaT6pGwjTXLIPTRhCpBYNsawnER5m42CHMiNi2/cyTzpGeLBkD53KnHMxDxtDRCgnSKnX7gG9d2Fal0Vy5py1Nh1HmmmwJCQyszAl4cfjsUVEOIcDiwMS4jyX0cde69Pz82lZiKKNexFZS9q7jj4AYtt3AiTGAyAhwvGwYQ7UwMwRSNKj9Ucdy5TWuZymMoZbfK21uEdrbdSKiMtpkZLHGI/a2hg+xuOxfbkuSADM00GxNTOzzBIQ1sYA6GZDBxOHh6oFUVdHHeuciuDjWkFSDb3t/ThWSwQChh+wMwJ3oaDEB6un5OSEgRxmYSPc4fjXAJgJmeSIjwqrGhEFc7gDoZoPVfTYFJmwCAvjGCPMwskABIHc/8Vv1//RP738X/+NiwyC6G7mNrqa6v8fhVAgsfiBI9sAAAAASUVORK5CYII=\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "load checkpoint from https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base.pth\n" + ] + } + ], + "source": [ + "from models.blip import blip_feature_extractor\n", + "\n", + "image_size = 224\n", + "image = load_demo_image(image_size=image_size, device=device) \n", + "\n", + "model_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base.pth'\n", + " \n", + "model = blip_feature_extractor(pretrained=model_url, image_size=image_size, vit='base')\n", + "model.eval()\n", + "model = model.to(device)\n", + "\n", + "caption = 'a woman sitting on the beach with a dog'\n", + "\n", + "multimodal_feature = model(image, caption, mode='multimodal')[0,0]\n", + "image_feature = model(image, caption, mode='image')[0,0]\n", + "text_feature = model(image, caption, mode='text')[0,0]" + ] + }, + { + "cell_type": "markdown", + "id": "201e1146", + "metadata": {}, + "source": [ + "# Image-Text Matching" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "49ba5906", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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sU7lt5fln3/7HCz386n2ziKbG+s4Jb6rIlAS3NURwRDWzOc5tXDIPM1FciLiCcMLF28L6OfN/3qv//0/e//NyrwPVhvqP77c91+uj8deW6NSalcDRCIILzmDCXgqa9IwNyVO0caBxpzmQgjCMk1SUm616ei993ob14YvX673Pr77cP7+qeQyk9ApnQPor396/flK+L/9P/yXCWMq5d7VzC9T/nBe1rlsDr9srbdR5hWIh6kkkZRM7askm6X3fhAcc04B0LcN3HOelstpXpZlykL4eOyf3976Vol49JZLef7wnhlVbbtvL59f8umUp6LmKSdiqvedhUtK63nN6xkBs1CEz0TnxOphph8KMkIMVY8aQMvZIFob133svTngelqKJA0HQACotbEQEl2vt9FHBDiQlFKY3r9/nudiQK11BChZRMTMWx9gLiXttSFizvn4bKgZmBJGLtNQc4s2GgEmlvU09W5ER2eTeutlmlLiMLu/bZJpqL78/PPby5dwBSQb2vbHX/yjf3C5XLa3FxF5PLba6nK+jN63t+vzd99Pl6f79Z4Q1tPyuL6+fXkNob/9D3/Z635+focEJDKtT/PlYqp//a//n+d379599507LKen999/t8wzAK6XdyycCT0iALJIOATB4/EoKYmk4dZqXZd5qIWbjz7UT+fTNE17rUfWfNsrEyah3jqxpCStNRHiVPatQQwmIkkl595q3TdtfVkXydP7D++OnhAyjaGPxwMgeh+SSwRsj0cRWdcllwThbkbMrba5ZJJU21DVdZ5LSULk4VvvECBEZt7NWY4lCR7qY48Yveecp5wjwtyJKNzXZQEAj0D3gDjOaAjhgXutJNJqA4RlKnbcCiDAw9wlCSKVLAhgHm4GhxWevqrq4Ot/yHWYuZkFgU1Mz0sh190AWKzVIyK/18oR82n1PpZMp7kE8qPblNPe+tv1GmAdEkpy8zBrrZ3P62Vdr7crhENEd+CUAtDGADNDevvyMp+XWbhtG4nU3r2Pp2X+7t0JiLvHLy+3x17nks19AIYbu5GbEhOifo0ogmtPLGWZKacj5xEQTLTdrt5rD1T1MToxPWf+Z9+sv39te28zGnK61zanfC58njhMHyMqpYfhbk7EKQm4qWrbW29tPa2HhVqYI0LHsMPQ4+E61tNKiHXbzRQiGOHjzBNFHzpLoI/f//J6735KdFqX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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "load checkpoint from https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_coco.pth\n", + "text: a woman sitting on the beach with a dog\n", + "The image and text is matched with a probability of 0.9960\n", + "The image feature and text feature has a cosine similarity of 0.5262\n" + ] + } + ], + "source": [ + "from models.blip_itm import blip_itm\n", + "\n", + "image_size = 384\n", + "image = load_demo_image(image_size=image_size,device=device)\n", + "\n", + "model_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_coco.pth'\n", + " \n", + "model = blip_itm(pretrained=model_url, image_size=image_size, vit='base')\n", + "model.eval()\n", + "model = model.to(device='cpu')\n", + "\n", + "caption = 'a woman sitting on the beach with a dog'\n", + "\n", + "print('text: %s' %caption)\n", + "\n", + "itm_output = model(image,caption,match_head='itm')\n", + "itm_score = torch.nn.functional.softmax(itm_output,dim=1)[:,1]\n", + "print('The image and text is matched with a probability of %.4f'%itm_score)\n", + "\n", + "itc_score = model(image,caption,match_head='itc')\n", + "print('The image feature and text feature has a cosine similarity of %.4f'%itc_score)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/sd/stablediffusion/src/blip/eval_nocaps.py b/sd/stablediffusion/src/blip/eval_nocaps.py new file mode 100644 index 0000000000000000000000000000000000000000..3cbb09a8cc7771605c013583d721aa95d9413b42 --- /dev/null +++ b/sd/stablediffusion/src/blip/eval_nocaps.py @@ -0,0 +1,118 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip import blip_decoder +import utils +from data import create_dataset, create_sampler, create_loader +from data.utils import save_result + +@torch.no_grad() +def evaluate(model, data_loader, device, config): + # evaluate + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Evaluation:' + print_freq = 10 + + result = [] + for image, image_id in metric_logger.log_every(data_loader, print_freq, header): + + image = image.to(device) + + captions = model.generate(image, sample=False, num_beams=config['num_beams'], max_length=config['max_length'], + min_length=config['min_length'], repetition_penalty=1.1) + + for caption, img_id in zip(captions, image_id): + result.append({"image_id": img_id.item(), "caption": caption}) + + return result + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating captioning dataset") + val_dataset, test_dataset = create_dataset('nocaps', config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler([val_dataset,test_dataset], [False,False], num_tasks, global_rank) + else: + samplers = [None,None] + + val_loader, test_loader = create_loader([val_dataset, test_dataset],samplers, + batch_size=[config['batch_size']]*2,num_workers=[4,4], + is_trains=[False, False], collate_fns=[None,None]) + + #### Model #### + print("Creating model") + model = blip_decoder(pretrained=config['pretrained'], image_size=config['image_size'], vit=config['vit'], + prompt=config['prompt']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + val_result = evaluate(model_without_ddp, val_loader, device, config) + val_result_file = save_result(val_result, args.result_dir, 'val', remove_duplicate='image_id') + test_result = evaluate(model_without_ddp, test_loader, device, config) + test_result_file = save_result(test_result, args.result_dir, 'test', remove_duplicate='image_id') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/nocaps.yaml') + parser.add_argument('--output_dir', default='output/NoCaps') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + args.result_dir = os.path.join(args.output_dir, 'result') + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + Path(args.result_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/eval_retrieval_video.py b/sd/stablediffusion/src/blip/eval_retrieval_video.py new file mode 100644 index 0000000000000000000000000000000000000000..07ebab7f41f6466f6f46130002e2e0df1266486a --- /dev/null +++ b/sd/stablediffusion/src/blip/eval_retrieval_video.py @@ -0,0 +1,250 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip_retrieval import blip_retrieval +import utils +from data.video_dataset import VideoDataset + + +@torch.no_grad() +def evaluation(model, data_loader, tokenizer, device, config): + # test + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Evaluation:' + + print('Computing features for evaluation...') + start_time = time.time() + + texts = data_loader.dataset.text + num_text = len(texts) + text_bs = 256 + text_ids = [] + text_embeds = [] + text_atts = [] + for i in range(0, num_text, text_bs): + text = texts[i: min(num_text, i+text_bs)] + text_input = tokenizer(text, padding='max_length', truncation=True, max_length=35, return_tensors="pt").to(device) + text_output = model.text_encoder(text_input.input_ids, attention_mask = text_input.attention_mask, mode='text') + text_embed = F.normalize(model.text_proj(text_output.last_hidden_state[:,0,:])) + text_embeds.append(text_embed) + text_ids.append(text_input.input_ids) + text_atts.append(text_input.attention_mask) + + text_embeds = torch.cat(text_embeds,dim=0) + text_ids = torch.cat(text_ids,dim=0) + text_atts = torch.cat(text_atts,dim=0) + text_ids[:,0] = tokenizer.additional_special_tokens_ids[0] + + video_feats = [] + video_embeds = [] + for video, video_id in data_loader: + + B,N,C,W,H = video.size() + video = video.view(-1,C,W,H) + video = video.to(device,non_blocking=True) + video_feat = model.visual_encoder(video) + video_embed = model.vision_proj(video_feat[:,0,:]) + video_embed = video_embed.view(B,N,-1).mean(dim=1) + video_embed = F.normalize(video_embed,dim=-1) + + video_feat = video_feat.view(B,-1,video_feat.shape[-1]) + video_feats.append(video_feat.cpu()) + video_embeds.append(video_embed) + + video_feats = torch.cat(video_feats,dim=0) + video_embeds = torch.cat(video_embeds,dim=0) + + sims_matrix = video_embeds @ text_embeds.t() + score_matrix_v2t = torch.full((len(texts),len(texts)),-100.0).to(device) + + num_tasks = utils.get_world_size() + rank = utils.get_rank() + step = sims_matrix.size(0)//num_tasks + 1 + start = rank*step + end = min(sims_matrix.size(0),start+step) + + for i,sims in enumerate(metric_logger.log_every(sims_matrix[start:end], 50, header)): + topk_sim, topk_idx = sims.topk(k=config['k_test'], dim=0) + + encoder_output = video_feats[start+i].repeat(config['k_test'],1,1).to(device,non_blocking=True) + encoder_att = torch.ones(encoder_output.size()[:-1],dtype=torch.long).to(device,non_blocking=True) + output = model.text_encoder(text_ids[topk_idx], + attention_mask = text_atts[topk_idx], + encoder_hidden_states = encoder_output, + encoder_attention_mask = encoder_att, + return_dict = True, + ) + score = model.itm_head(output.last_hidden_state[:,0,:])[:,1] + score_matrix_v2t[start+i,topk_idx] = score + topk_sim + + sims_matrix = sims_matrix.t() + score_matrix_t2v = torch.full((len(texts),len(texts)),-100.0).to(device) + + step = sims_matrix.size(0)//num_tasks + 1 + start = rank*step + end = min(sims_matrix.size(0),start+step) + + for i,sims in enumerate(metric_logger.log_every(sims_matrix[start:end], 50, header)): + + topk_sim, topk_idx = sims.topk(k=config['k_test'], dim=0) + encoder_output = video_feats[topk_idx].to(device,non_blocking=True) + encoder_att = torch.ones(encoder_output.size()[:-1],dtype=torch.long).to(device,non_blocking=True) + output = model.text_encoder(text_ids[start+i].repeat(config['k_test'],1), + attention_mask = text_atts[start+i].repeat(config['k_test'],1), + encoder_hidden_states = encoder_output, + encoder_attention_mask = encoder_att, + return_dict = True, + ) + score = model.itm_head(output.last_hidden_state[:,0,:])[:,1] + score_matrix_t2v[start+i,topk_idx] = score + topk_sim + + if args.distributed: + dist.barrier() + torch.distributed.all_reduce(score_matrix_v2t, op=torch.distributed.ReduceOp.SUM) + torch.distributed.all_reduce(score_matrix_t2v, op=torch.distributed.ReduceOp.SUM) + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Evaluation time {}'.format(total_time_str)) + + return score_matrix_v2t.cpu().numpy(), score_matrix_t2v.cpu().numpy() + + + +@torch.no_grad() +def itm_eval(scores_v2t, scores_t2v, txt2vmg, vid2txt): + + #Video->Text + ranks = np.zeros(scores_v2t.shape[0]) + for index,score in enumerate(scores_v2t): + inds = np.argsort(score)[::-1] + ranks[index] = np.where(inds == vid2txt[index])[0][0] + + # Compute metrics + tr1 = 100.0 * len(np.where(ranks < 1)[0]) / len(ranks) + tr5 = 100.0 * len(np.where(ranks < 5)[0]) / len(ranks) + tr10 = 100.0 * len(np.where(ranks < 10)[0]) / len(ranks) + + #Text->Video + ranks = np.zeros(scores_t2v.shape[0]) + + for index,score in enumerate(scores_t2v): + inds = np.argsort(score)[::-1] + ranks[index] = np.where(inds == txt2vmg[index])[0][0] + + mdR = np.median(ranks+1) + + # Compute metrics + vr1 = 100.0 * len(np.where(ranks < 1)[0]) / len(ranks) + vr5 = 100.0 * len(np.where(ranks < 5)[0]) / len(ranks) + vr10 = 100.0 * len(np.where(ranks < 10)[0]) / len(ranks) + + tr_mean = (tr1 + tr5 + tr10) / 3 + vr_mean = (vr1 + vr5 + vr10) / 3 + r_mean = (tr_mean + vr_mean) / 2 + + eval_result = {'txt_r1': tr1, + 'txt_r5': tr5, + 'txt_r10': tr10, + 'txt_r_mean': tr_mean, + 'vid_r1': vr1, + 'vid_r5': vr5, + 'vid_r10': vr10, + 'vid_r_mean': vr_mean, + 'vid_mdR': mdR, + 'r_mean': r_mean} + return eval_result + + + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating retrieval dataset") + test_dataset = VideoDataset(config['video_root'],config['ann_root'],num_frm=config['num_frm_test'], + max_img_size=config['image_size'], frm_sampling_strategy='uniform') + + test_loader = DataLoader( + test_dataset, + batch_size=config['batch_size'], + num_workers=4, + pin_memory=True, + drop_last=False, + shuffle=False, + ) + + #### Model #### + print("Creating model") + model = blip_retrieval(pretrained=config['pretrained'], image_size=config['image_size'], vit=config['vit']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + score_v2t, score_t2v, = evaluation(model_without_ddp, test_loader, model_without_ddp.tokenizer, device, config) + + if utils.is_main_process(): + + test_result = itm_eval(score_v2t, score_t2v, test_loader.dataset.txt2video, test_loader.dataset.video2txt) + print(test_result) + + log_stats = {**{f'{k}': v for k, v in test_result.items()},} + with open(os.path.join(args.output_dir, "test_result.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/retrieval_msrvtt.yaml') + parser.add_argument('--output_dir', default='output/Retrieval_msrvtt') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/models/__init__.py b/sd/stablediffusion/src/blip/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/src/blip/models/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/src/blip/models/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49a294641cb8c3e78ec54011661c23cf3fd0916b Binary files /dev/null and b/sd/stablediffusion/src/blip/models/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/blip/models/__pycache__/blip.cpython-39.pyc b/sd/stablediffusion/src/blip/models/__pycache__/blip.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98c8dc7a54d1b46203d267245f265e2037a2da2a Binary files /dev/null and b/sd/stablediffusion/src/blip/models/__pycache__/blip.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/blip/models/__pycache__/med.cpython-39.pyc b/sd/stablediffusion/src/blip/models/__pycache__/med.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53ddd995e107ae8baef3798750b79402dbc14996 Binary files /dev/null and b/sd/stablediffusion/src/blip/models/__pycache__/med.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/blip/models/__pycache__/vit.cpython-39.pyc b/sd/stablediffusion/src/blip/models/__pycache__/vit.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dbf2d0fd74e949be0ab427b47541e6725898def5 Binary files /dev/null and b/sd/stablediffusion/src/blip/models/__pycache__/vit.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/blip/models/blip.py b/sd/stablediffusion/src/blip/models/blip.py new file mode 100644 index 0000000000000000000000000000000000000000..38678f65ea2c276b351c2c97d429ebc2525ddcf7 --- /dev/null +++ b/sd/stablediffusion/src/blip/models/blip.py @@ -0,0 +1,238 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import warnings +warnings.filterwarnings("ignore") + +from models.vit import VisionTransformer, interpolate_pos_embed +from models.med import BertConfig, BertModel, BertLMHeadModel +from transformers import BertTokenizer + +import torch +from torch import nn +import torch.nn.functional as F + +import os +from urllib.parse import urlparse +from timm.models.hub import download_cached_file + +class BLIP_Base(nn.Module): + def __init__(self, + med_config = 'configs/med_config.json', + image_size = 224, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_encoder = BertModel(config=med_config, add_pooling_layer=False) + + + def forward(self, image, caption, mode): + + assert mode in ['image', 'text', 'multimodal'], "mode parameter must be image, text, or multimodal" + text = self.tokenizer(caption, return_tensors="pt").to(image.device) + + if mode=='image': + # return image features + image_embeds = self.visual_encoder(image) + return image_embeds + + elif mode=='text': + # return text features + text_output = self.text_encoder(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + return text_output.last_hidden_state + + elif mode=='multimodal': + # return multimodel features + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + + text.input_ids[:,0] = self.tokenizer.enc_token_id + output = self.text_encoder(text.input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True, + ) + return output.last_hidden_state + + + +class BLIP_Decoder(nn.Module): + def __init__(self, + med_config = 'configs/med_config.json', + image_size = 384, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + prompt = 'a picture of ', + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_decoder = BertLMHeadModel(config=med_config) + + self.prompt = prompt + self.prompt_length = len(self.tokenizer(self.prompt).input_ids)-1 + + + def forward(self, image, caption): + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + + text = self.tokenizer(caption, padding='longest', truncation=True, max_length=40, return_tensors="pt").to(image.device) + + text.input_ids[:,0] = self.tokenizer.bos_token_id + + decoder_targets = text.input_ids.masked_fill(text.input_ids == self.tokenizer.pad_token_id, -100) + decoder_targets[:,:self.prompt_length] = -100 + + decoder_output = self.text_decoder(text.input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + labels = decoder_targets, + return_dict = True, + ) + loss_lm = decoder_output.loss + + return loss_lm + + def generate(self, image, sample=False, num_beams=3, max_length=30, min_length=10, top_p=0.9, repetition_penalty=1.0): + image_embeds = self.visual_encoder(image) + + if not sample: + image_embeds = image_embeds.repeat_interleave(num_beams,dim=0) + + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + model_kwargs = {"encoder_hidden_states": image_embeds, "encoder_attention_mask":image_atts} + + prompt = [self.prompt] * image.size(0) + input_ids = self.tokenizer(prompt, return_tensors="pt").input_ids.to(image.device) + input_ids[:,0] = self.tokenizer.bos_token_id + input_ids = input_ids[:, :-1] + + if sample: + #nucleus sampling + outputs = self.text_decoder.generate(input_ids=input_ids, + max_length=max_length, + min_length=min_length, + do_sample=True, + top_p=top_p, + num_return_sequences=1, + eos_token_id=self.tokenizer.sep_token_id, + pad_token_id=self.tokenizer.pad_token_id, + repetition_penalty=1.1, + **model_kwargs) + else: + #beam search + outputs = self.text_decoder.generate(input_ids=input_ids, + max_length=max_length, + min_length=min_length, + num_beams=num_beams, + eos_token_id=self.tokenizer.sep_token_id, + pad_token_id=self.tokenizer.pad_token_id, + repetition_penalty=repetition_penalty, + **model_kwargs) + + captions = [] + for output in outputs: + caption = self.tokenizer.decode(output, skip_special_tokens=True) + captions.append(caption[len(self.prompt):]) + return captions + + +def blip_decoder(pretrained='',**kwargs): + model = BLIP_Decoder(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + assert(len(msg.missing_keys)==0) + return model + +def blip_feature_extractor(pretrained='',**kwargs): + model = BLIP_Base(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + assert(len(msg.missing_keys)==0) + return model + +def init_tokenizer(): + tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') + tokenizer.add_special_tokens({'bos_token':'[DEC]'}) + tokenizer.add_special_tokens({'additional_special_tokens':['[ENC]']}) + tokenizer.enc_token_id = tokenizer.additional_special_tokens_ids[0] + return tokenizer + + +def create_vit(vit, image_size, use_grad_checkpointing=False, ckpt_layer=0, drop_path_rate=0): + + assert vit in ['base', 'large'], "vit parameter must be base or large" + if vit=='base': + vision_width = 768 + visual_encoder = VisionTransformer(img_size=image_size, patch_size=16, embed_dim=vision_width, depth=12, + num_heads=12, use_grad_checkpointing=use_grad_checkpointing, ckpt_layer=ckpt_layer, + drop_path_rate=0 or drop_path_rate + ) + elif vit=='large': + vision_width = 1024 + visual_encoder = VisionTransformer(img_size=image_size, patch_size=16, embed_dim=vision_width, depth=24, + num_heads=16, use_grad_checkpointing=use_grad_checkpointing, ckpt_layer=ckpt_layer, + drop_path_rate=0.1 or drop_path_rate + ) + return visual_encoder, vision_width + +def is_url(url_or_filename): + parsed = urlparse(url_or_filename) + return parsed.scheme in ("http", "https") + +def load_checkpoint(model,url_or_filename): + if is_url(url_or_filename): + cached_file = download_cached_file(url_or_filename, check_hash=False, progress=True) + checkpoint = torch.load(cached_file, map_location='cpu') + elif os.path.isfile(url_or_filename): + checkpoint = torch.load(url_or_filename, map_location='cpu') + else: + raise RuntimeError('checkpoint url or path is invalid') + + state_dict = checkpoint['model'] + + state_dict['visual_encoder.pos_embed'] = interpolate_pos_embed(state_dict['visual_encoder.pos_embed'],model.visual_encoder) + if 'visual_encoder_m.pos_embed' in model.state_dict().keys(): + state_dict['visual_encoder_m.pos_embed'] = interpolate_pos_embed(state_dict['visual_encoder_m.pos_embed'], + model.visual_encoder_m) + for key in model.state_dict().keys(): + if key in state_dict.keys(): + if state_dict[key].shape!=model.state_dict()[key].shape: + del state_dict[key] + + msg = model.load_state_dict(state_dict,strict=False) + print('load checkpoint from %s'%url_or_filename) + return model,msg + diff --git a/sd/stablediffusion/src/blip/models/blip_itm.py b/sd/stablediffusion/src/blip/models/blip_itm.py new file mode 100644 index 0000000000000000000000000000000000000000..cf354c829564bf5a1f56089a2d745093d51e0fa2 --- /dev/null +++ b/sd/stablediffusion/src/blip/models/blip_itm.py @@ -0,0 +1,76 @@ +from models.med import BertConfig, BertModel +from transformers import BertTokenizer + +import torch +from torch import nn +import torch.nn.functional as F + +from models.blip import create_vit, init_tokenizer, load_checkpoint + +class BLIP_ITM(nn.Module): + def __init__(self, + med_config = 'configs/med_config.json', + image_size = 384, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + embed_dim = 256, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_encoder = BertModel(config=med_config, add_pooling_layer=False) + + text_width = self.text_encoder.config.hidden_size + + self.vision_proj = nn.Linear(vision_width, embed_dim) + self.text_proj = nn.Linear(text_width, embed_dim) + + self.itm_head = nn.Linear(text_width, 2) + + + def forward(self, image, caption, match_head='itm'): + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + + text = self.tokenizer(caption, padding='max_length', truncation=True, max_length=35, + return_tensors="pt").to(image.device) + + + if match_head=='itm': + output = self.text_encoder(text.input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True, + ) + itm_output = self.itm_head(output.last_hidden_state[:,0,:]) + return itm_output + + elif match_head=='itc': + text_output = self.text_encoder(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + image_feat = F.normalize(self.vision_proj(image_embeds[:,0,:]),dim=-1) + text_feat = F.normalize(self.text_proj(text_output.last_hidden_state[:,0,:]),dim=-1) + + sim = image_feat @ text_feat.t() + return sim + + +def blip_itm(pretrained='',**kwargs): + model = BLIP_ITM(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + assert(len(msg.missing_keys)==0) + return model + \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/models/blip_nlvr.py b/sd/stablediffusion/src/blip/models/blip_nlvr.py new file mode 100644 index 0000000000000000000000000000000000000000..84837167bfa6874d3c3e41fb9b37271113910b7f --- /dev/null +++ b/sd/stablediffusion/src/blip/models/blip_nlvr.py @@ -0,0 +1,103 @@ +from models.med import BertConfig +from models.nlvr_encoder import BertModel +from models.vit import interpolate_pos_embed +from models.blip import create_vit, init_tokenizer, is_url + +from timm.models.hub import download_cached_file + +import torch +from torch import nn +import torch.nn.functional as F +from transformers import BertTokenizer +import numpy as np + +class BLIP_NLVR(nn.Module): + def __init__(self, + med_config = 'configs/med_config.json', + image_size = 480, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer, drop_path_rate=0.1) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_encoder = BertModel(config=med_config, add_pooling_layer=False) + + self.cls_head = nn.Sequential( + nn.Linear(self.text_encoder.config.hidden_size, self.text_encoder.config.hidden_size), + nn.ReLU(), + nn.Linear(self.text_encoder.config.hidden_size, 2) + ) + + def forward(self, image, text, targets, train=True): + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + image0_embeds, image1_embeds = torch.split(image_embeds,targets.size(0)) + + text = self.tokenizer(text, padding='longest', return_tensors="pt").to(image.device) + text.input_ids[:,0] = self.tokenizer.enc_token_id + + output = self.text_encoder(text.input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = [image0_embeds,image1_embeds], + encoder_attention_mask = [image_atts[:image0_embeds.size(0)], + image_atts[image0_embeds.size(0):]], + return_dict = True, + ) + hidden_state = output.last_hidden_state[:,0,:] + prediction = self.cls_head(hidden_state) + + if train: + loss = F.cross_entropy(prediction, targets) + return loss + else: + return prediction + +def blip_nlvr(pretrained='',**kwargs): + model = BLIP_NLVR(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + print("missing keys:") + print(msg.missing_keys) + return model + + +def load_checkpoint(model,url_or_filename): + if is_url(url_or_filename): + cached_file = download_cached_file(url_or_filename, check_hash=False, progress=True) + checkpoint = torch.load(cached_file, map_location='cpu') + elif os.path.isfile(url_or_filename): + checkpoint = torch.load(url_or_filename, map_location='cpu') + else: + raise RuntimeError('checkpoint url or path is invalid') + state_dict = checkpoint['model'] + + state_dict['visual_encoder.pos_embed'] = interpolate_pos_embed(state_dict['visual_encoder.pos_embed'],model.visual_encoder) + + for key in list(state_dict.keys()): + if 'crossattention.self.' in key: + new_key0 = key.replace('self','self0') + new_key1 = key.replace('self','self1') + state_dict[new_key0] = state_dict[key] + state_dict[new_key1] = state_dict[key] + elif 'crossattention.output.dense.' in key: + new_key0 = key.replace('dense','dense0') + new_key1 = key.replace('dense','dense1') + state_dict[new_key0] = state_dict[key] + state_dict[new_key1] = state_dict[key] + + msg = model.load_state_dict(state_dict,strict=False) + print('load checkpoint from %s'%url_or_filename) + return model,msg + \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/models/blip_pretrain.py b/sd/stablediffusion/src/blip/models/blip_pretrain.py new file mode 100644 index 0000000000000000000000000000000000000000..e42ce5f998b0a51e6f731ee6b5c8bae6d02a8664 --- /dev/null +++ b/sd/stablediffusion/src/blip/models/blip_pretrain.py @@ -0,0 +1,339 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +from models.med import BertConfig, BertModel, BertLMHeadModel +from transformers import BertTokenizer +import transformers +transformers.logging.set_verbosity_error() + +import torch +from torch import nn +import torch.nn.functional as F + +from models.blip import create_vit, init_tokenizer, load_checkpoint + +class BLIP_Pretrain(nn.Module): + def __init__(self, + med_config = 'configs/bert_config.json', + image_size = 224, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + embed_dim = 256, + queue_size = 57600, + momentum = 0.995, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer, 0) + + if vit=='base': + checkpoint = torch.hub.load_state_dict_from_url( + url="https://dl.fbaipublicfiles.com/deit/deit_base_patch16_224-b5f2ef4d.pth", + map_location="cpu", check_hash=True) + state_dict = checkpoint["model"] + msg = self.visual_encoder.load_state_dict(state_dict,strict=False) + elif vit=='large': + from timm.models.helpers import load_custom_pretrained + from timm.models.vision_transformer import default_cfgs + load_custom_pretrained(self.visual_encoder,default_cfgs['vit_large_patch16_224_in21k']) + + self.tokenizer = init_tokenizer() + encoder_config = BertConfig.from_json_file(med_config) + encoder_config.encoder_width = vision_width + self.text_encoder = BertModel.from_pretrained('bert-base-uncased',config=encoder_config, add_pooling_layer=False) + self.text_encoder.resize_token_embeddings(len(self.tokenizer)) + + text_width = self.text_encoder.config.hidden_size + + self.vision_proj = nn.Linear(vision_width, embed_dim) + self.text_proj = nn.Linear(text_width, embed_dim) + + self.itm_head = nn.Linear(text_width, 2) + + # create momentum encoders + self.visual_encoder_m, vision_width = create_vit(vit,image_size) + self.vision_proj_m = nn.Linear(vision_width, embed_dim) + self.text_encoder_m = BertModel(config=encoder_config, add_pooling_layer=False) + self.text_proj_m = nn.Linear(text_width, embed_dim) + + self.model_pairs = [[self.visual_encoder,self.visual_encoder_m], + [self.vision_proj,self.vision_proj_m], + [self.text_encoder,self.text_encoder_m], + [self.text_proj,self.text_proj_m], + ] + self.copy_params() + + # create the queue + self.register_buffer("image_queue", torch.randn(embed_dim, queue_size)) + self.register_buffer("text_queue", torch.randn(embed_dim, queue_size)) + self.register_buffer("queue_ptr", torch.zeros(1, dtype=torch.long)) + + self.image_queue = nn.functional.normalize(self.image_queue, dim=0) + self.text_queue = nn.functional.normalize(self.text_queue, dim=0) + + self.queue_size = queue_size + self.momentum = momentum + self.temp = nn.Parameter(0.07*torch.ones([])) + + # create the decoder + decoder_config = BertConfig.from_json_file(med_config) + decoder_config.encoder_width = vision_width + self.text_decoder = BertLMHeadModel.from_pretrained('bert-base-uncased',config=decoder_config) + self.text_decoder.resize_token_embeddings(len(self.tokenizer)) + tie_encoder_decoder_weights(self.text_encoder,self.text_decoder.bert,'','/attention') + + + def forward(self, image, caption, alpha): + with torch.no_grad(): + self.temp.clamp_(0.001,0.5) + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + image_feat = F.normalize(self.vision_proj(image_embeds[:,0,:]),dim=-1) + + text = self.tokenizer(caption, padding='max_length', truncation=True, max_length=30, + return_tensors="pt").to(image.device) + text_output = self.text_encoder(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + text_feat = F.normalize(self.text_proj(text_output.last_hidden_state[:,0,:]),dim=-1) + + # get momentum features + with torch.no_grad(): + self._momentum_update() + image_embeds_m = self.visual_encoder_m(image) + image_feat_m = F.normalize(self.vision_proj_m(image_embeds_m[:,0,:]),dim=-1) + image_feat_all = torch.cat([image_feat_m.t(),self.image_queue.clone().detach()],dim=1) + + text_output_m = self.text_encoder_m(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + text_feat_m = F.normalize(self.text_proj_m(text_output_m.last_hidden_state[:,0,:]),dim=-1) + text_feat_all = torch.cat([text_feat_m.t(),self.text_queue.clone().detach()],dim=1) + + sim_i2t_m = image_feat_m @ text_feat_all / self.temp + sim_t2i_m = text_feat_m @ image_feat_all / self.temp + + sim_targets = torch.zeros(sim_i2t_m.size()).to(image.device) + sim_targets.fill_diagonal_(1) + + sim_i2t_targets = alpha * F.softmax(sim_i2t_m, dim=1) + (1 - alpha) * sim_targets + sim_t2i_targets = alpha * F.softmax(sim_t2i_m, dim=1) + (1 - alpha) * sim_targets + + sim_i2t = image_feat @ text_feat_all / self.temp + sim_t2i = text_feat @ image_feat_all / self.temp + + loss_i2t = -torch.sum(F.log_softmax(sim_i2t, dim=1)*sim_i2t_targets,dim=1).mean() + loss_t2i = -torch.sum(F.log_softmax(sim_t2i, dim=1)*sim_t2i_targets,dim=1).mean() + + loss_ita = (loss_i2t+loss_t2i)/2 + + self._dequeue_and_enqueue(image_feat_m, text_feat_m) + + ###============== Image-text Matching ===================### + encoder_input_ids = text.input_ids.clone() + encoder_input_ids[:,0] = self.tokenizer.enc_token_id + + # forward the positve image-text pair + bs = image.size(0) + output_pos = self.text_encoder(encoder_input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True, + ) + with torch.no_grad(): + weights_t2i = F.softmax(sim_t2i[:,:bs],dim=1)+1e-4 + weights_t2i.fill_diagonal_(0) + weights_i2t = F.softmax(sim_i2t[:,:bs],dim=1)+1e-4 + weights_i2t.fill_diagonal_(0) + + # select a negative image for each text + image_embeds_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_t2i[b], 1).item() + image_embeds_neg.append(image_embeds[neg_idx]) + image_embeds_neg = torch.stack(image_embeds_neg,dim=0) + + # select a negative text for each image + text_ids_neg = [] + text_atts_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_i2t[b], 1).item() + text_ids_neg.append(encoder_input_ids[neg_idx]) + text_atts_neg.append(text.attention_mask[neg_idx]) + + text_ids_neg = torch.stack(text_ids_neg,dim=0) + text_atts_neg = torch.stack(text_atts_neg,dim=0) + + text_ids_all = torch.cat([encoder_input_ids, text_ids_neg],dim=0) + text_atts_all = torch.cat([text.attention_mask, text_atts_neg],dim=0) + + image_embeds_all = torch.cat([image_embeds_neg,image_embeds],dim=0) + image_atts_all = torch.cat([image_atts,image_atts],dim=0) + + output_neg = self.text_encoder(text_ids_all, + attention_mask = text_atts_all, + encoder_hidden_states = image_embeds_all, + encoder_attention_mask = image_atts_all, + return_dict = True, + ) + + vl_embeddings = torch.cat([output_pos.last_hidden_state[:,0,:], output_neg.last_hidden_state[:,0,:]],dim=0) + vl_output = self.itm_head(vl_embeddings) + + itm_labels = torch.cat([torch.ones(bs,dtype=torch.long),torch.zeros(2*bs,dtype=torch.long)], + dim=0).to(image.device) + loss_itm = F.cross_entropy(vl_output, itm_labels) + + ##================= LM ========================## + decoder_input_ids = text.input_ids.clone() + decoder_input_ids[:,0] = self.tokenizer.bos_token_id + decoder_targets = decoder_input_ids.masked_fill(decoder_input_ids == self.tokenizer.pad_token_id, -100) + + decoder_output = self.text_decoder(decoder_input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + labels = decoder_targets, + return_dict = True, + ) + + loss_lm = decoder_output.loss + return loss_ita, loss_itm, loss_lm + + + + @torch.no_grad() + def copy_params(self): + for model_pair in self.model_pairs: + for param, param_m in zip(model_pair[0].parameters(), model_pair[1].parameters()): + param_m.data.copy_(param.data) # initialize + param_m.requires_grad = False # not update by gradient + + + @torch.no_grad() + def _momentum_update(self): + for model_pair in self.model_pairs: + for param, param_m in zip(model_pair[0].parameters(), model_pair[1].parameters()): + param_m.data = param_m.data * self.momentum + param.data * (1. - self.momentum) + + + @torch.no_grad() + def _dequeue_and_enqueue(self, image_feat, text_feat): + # gather keys before updating queue + image_feats = concat_all_gather(image_feat) + text_feats = concat_all_gather(text_feat) + + batch_size = image_feats.shape[0] + + ptr = int(self.queue_ptr) + assert self.queue_size % batch_size == 0 # for simplicity + + # replace the keys at ptr (dequeue and enqueue) + self.image_queue[:, ptr:ptr + batch_size] = image_feats.T + self.text_queue[:, ptr:ptr + batch_size] = text_feats.T + ptr = (ptr + batch_size) % self.queue_size # move pointer + + self.queue_ptr[0] = ptr + + +def blip_pretrain(**kwargs): + model = BLIP_Pretrain(**kwargs) + return model + + +@torch.no_grad() +def concat_all_gather(tensor): + """ + Performs all_gather operation on the provided tensors. + *** Warning ***: torch.distributed.all_gather has no gradient. + """ + tensors_gather = [torch.ones_like(tensor) + for _ in range(torch.distributed.get_world_size())] + torch.distributed.all_gather(tensors_gather, tensor, async_op=False) + + output = torch.cat(tensors_gather, dim=0) + return output + + +from typing import List +def tie_encoder_decoder_weights(encoder: nn.Module, decoder: nn.Module, base_model_prefix: str, skip_key:str): + uninitialized_encoder_weights: List[str] = [] + if decoder.__class__ != encoder.__class__: + logger.info( + f"{decoder.__class__} and {encoder.__class__} are not equal. In this case make sure that all encoder weights are correctly initialized." + ) + + def tie_encoder_to_decoder_recursively( + decoder_pointer: nn.Module, + encoder_pointer: nn.Module, + module_name: str, + uninitialized_encoder_weights: List[str], + skip_key: str, + depth=0, + ): + assert isinstance(decoder_pointer, nn.Module) and isinstance( + encoder_pointer, nn.Module + ), f"{decoder_pointer} and {encoder_pointer} have to be of type torch.nn.Module" + if hasattr(decoder_pointer, "weight") and skip_key not in module_name: + assert hasattr(encoder_pointer, "weight") + encoder_pointer.weight = decoder_pointer.weight + if hasattr(decoder_pointer, "bias"): + assert hasattr(encoder_pointer, "bias") + encoder_pointer.bias = decoder_pointer.bias + print(module_name+' is tied') + return + + encoder_modules = encoder_pointer._modules + decoder_modules = decoder_pointer._modules + if len(decoder_modules) > 0: + assert ( + len(encoder_modules) > 0 + ), f"Encoder module {encoder_pointer} does not match decoder module {decoder_pointer}" + + all_encoder_weights = set([module_name + "/" + sub_name for sub_name in encoder_modules.keys()]) + encoder_layer_pos = 0 + for name, module in decoder_modules.items(): + if name.isdigit(): + encoder_name = str(int(name) + encoder_layer_pos) + decoder_name = name + if not isinstance(decoder_modules[decoder_name], type(encoder_modules[encoder_name])) and len( + encoder_modules + ) != len(decoder_modules): + # this can happen if the name corresponds to the position in a list module list of layers + # in this case the decoder has added a cross-attention that the encoder does not have + # thus skip this step and subtract one layer pos from encoder + encoder_layer_pos -= 1 + continue + elif name not in encoder_modules: + continue + elif depth > 500: + raise ValueError( + "Max depth of recursive function `tie_encoder_to_decoder` reached. It seems that there is a circular dependency between two or more `nn.Modules` of your model." + ) + else: + decoder_name = encoder_name = name + tie_encoder_to_decoder_recursively( + decoder_modules[decoder_name], + encoder_modules[encoder_name], + module_name + "/" + name, + uninitialized_encoder_weights, + skip_key, + depth=depth + 1, + ) + all_encoder_weights.remove(module_name + "/" + encoder_name) + + uninitialized_encoder_weights += list(all_encoder_weights) + + # tie weights recursively + tie_encoder_to_decoder_recursively(decoder, encoder, base_model_prefix, uninitialized_encoder_weights, skip_key) diff --git a/sd/stablediffusion/src/blip/models/blip_retrieval.py b/sd/stablediffusion/src/blip/models/blip_retrieval.py new file mode 100644 index 0000000000000000000000000000000000000000..1debe7e2e664f8dd603f8d4c537e3599c68638d7 --- /dev/null +++ b/sd/stablediffusion/src/blip/models/blip_retrieval.py @@ -0,0 +1,319 @@ +from models.med import BertConfig, BertModel +from transformers import BertTokenizer + +import torch +from torch import nn +import torch.nn.functional as F + +from models.blip import create_vit, init_tokenizer, load_checkpoint + +class BLIP_Retrieval(nn.Module): + def __init__(self, + med_config = 'configs/med_config.json', + image_size = 384, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + embed_dim = 256, + queue_size = 57600, + momentum = 0.995, + negative_all_rank = False, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_encoder = BertModel(config=med_config, add_pooling_layer=False) + + text_width = self.text_encoder.config.hidden_size + + self.vision_proj = nn.Linear(vision_width, embed_dim) + self.text_proj = nn.Linear(text_width, embed_dim) + + self.itm_head = nn.Linear(text_width, 2) + + # create momentum encoders + self.visual_encoder_m, vision_width = create_vit(vit,image_size) + self.vision_proj_m = nn.Linear(vision_width, embed_dim) + self.text_encoder_m = BertModel(config=med_config, add_pooling_layer=False) + self.text_proj_m = nn.Linear(text_width, embed_dim) + + self.model_pairs = [[self.visual_encoder,self.visual_encoder_m], + [self.vision_proj,self.vision_proj_m], + [self.text_encoder,self.text_encoder_m], + [self.text_proj,self.text_proj_m], + ] + self.copy_params() + + # create the queue + self.register_buffer("image_queue", torch.randn(embed_dim, queue_size)) + self.register_buffer("text_queue", torch.randn(embed_dim, queue_size)) + self.register_buffer("idx_queue", torch.full((1,queue_size),-100)) + self.register_buffer("ptr_queue", torch.zeros(1, dtype=torch.long)) + + self.image_queue = nn.functional.normalize(self.image_queue, dim=0) + self.text_queue = nn.functional.normalize(self.text_queue, dim=0) + + self.queue_size = queue_size + self.momentum = momentum + self.temp = nn.Parameter(0.07*torch.ones([])) + + self.negative_all_rank = negative_all_rank + + + def forward(self, image, caption, alpha, idx): + with torch.no_grad(): + self.temp.clamp_(0.001,0.5) + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + image_feat = F.normalize(self.vision_proj(image_embeds[:,0,:]),dim=-1) + + text = self.tokenizer(caption, padding='max_length', truncation=True, max_length=35, + return_tensors="pt").to(image.device) + + text_output = self.text_encoder(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + text_feat = F.normalize(self.text_proj(text_output.last_hidden_state[:,0,:]),dim=-1) + + ###============== Image-text Contrastive Learning ===================### + idx = idx.view(-1,1) + idx_all = torch.cat([idx.t(), self.idx_queue.clone().detach()],dim=1) + pos_idx = torch.eq(idx, idx_all).float() + sim_targets = pos_idx / pos_idx.sum(1,keepdim=True) + + # get momentum features + with torch.no_grad(): + self._momentum_update() + image_embeds_m = self.visual_encoder_m(image) + image_feat_m = F.normalize(self.vision_proj_m(image_embeds_m[:,0,:]),dim=-1) + image_feat_m_all = torch.cat([image_feat_m.t(),self.image_queue.clone().detach()],dim=1) + + text_output_m = self.text_encoder_m(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + text_feat_m = F.normalize(self.text_proj_m(text_output_m.last_hidden_state[:,0,:]),dim=-1) + text_feat_m_all = torch.cat([text_feat_m.t(),self.text_queue.clone().detach()],dim=1) + + sim_i2t_m = image_feat_m @ text_feat_m_all / self.temp + sim_t2i_m = text_feat_m @ image_feat_m_all / self.temp + + sim_i2t_targets = alpha * F.softmax(sim_i2t_m, dim=1) + (1 - alpha) * sim_targets + sim_t2i_targets = alpha * F.softmax(sim_t2i_m, dim=1) + (1 - alpha) * sim_targets + + sim_i2t = image_feat @ text_feat_m_all / self.temp + sim_t2i = text_feat @ image_feat_m_all / self.temp + + loss_i2t = -torch.sum(F.log_softmax(sim_i2t, dim=1)*sim_i2t_targets,dim=1).mean() + loss_t2i = -torch.sum(F.log_softmax(sim_t2i, dim=1)*sim_t2i_targets,dim=1).mean() + + loss_ita = (loss_i2t+loss_t2i)/2 + + idxs = concat_all_gather(idx) + self._dequeue_and_enqueue(image_feat_m, text_feat_m, idxs) + + ###============== Image-text Matching ===================### + encoder_input_ids = text.input_ids.clone() + encoder_input_ids[:,0] = self.tokenizer.enc_token_id + + # forward the positve image-text pair + bs = image.size(0) + output_pos = self.text_encoder(encoder_input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True, + ) + + + if self.negative_all_rank: + # compute sample similarity + with torch.no_grad(): + mask = torch.eq(idx, idxs.t()) + + image_feat_world = concat_all_gather(image_feat) + text_feat_world = concat_all_gather(text_feat) + + sim_i2t = image_feat @ text_feat_world.t() / self.temp + sim_t2i = text_feat @ image_feat_world.t() / self.temp + + weights_i2t = F.softmax(sim_i2t,dim=1) + weights_i2t.masked_fill_(mask, 0) + + weights_t2i = F.softmax(sim_t2i,dim=1) + weights_t2i.masked_fill_(mask, 0) + + image_embeds_world = all_gather_with_grad(image_embeds) + + # select a negative image (from all ranks) for each text + image_embeds_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_t2i[b], 1).item() + image_embeds_neg.append(image_embeds_world[neg_idx]) + image_embeds_neg = torch.stack(image_embeds_neg,dim=0) + + # select a negative text (from all ranks) for each image + input_ids_world = concat_all_gather(encoder_input_ids) + att_mask_world = concat_all_gather(text.attention_mask) + + text_ids_neg = [] + text_atts_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_i2t[b], 1).item() + text_ids_neg.append(input_ids_world[neg_idx]) + text_atts_neg.append(att_mask_world[neg_idx]) + + else: + with torch.no_grad(): + mask = torch.eq(idx, idx.t()) + + sim_i2t = image_feat @ text_feat.t() / self.temp + sim_t2i = text_feat @ image_feat.t() / self.temp + + weights_i2t = F.softmax(sim_i2t,dim=1) + weights_i2t.masked_fill_(mask, 0) + + weights_t2i = F.softmax(sim_t2i,dim=1) + weights_t2i.masked_fill_(mask, 0) + + # select a negative image (from same rank) for each text + image_embeds_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_t2i[b], 1).item() + image_embeds_neg.append(image_embeds[neg_idx]) + image_embeds_neg = torch.stack(image_embeds_neg,dim=0) + + # select a negative text (from same rank) for each image + text_ids_neg = [] + text_atts_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_i2t[b], 1).item() + text_ids_neg.append(encoder_input_ids[neg_idx]) + text_atts_neg.append(text.attention_mask[neg_idx]) + + text_ids_neg = torch.stack(text_ids_neg,dim=0) + text_atts_neg = torch.stack(text_atts_neg,dim=0) + + text_ids_all = torch.cat([encoder_input_ids, text_ids_neg],dim=0) + text_atts_all = torch.cat([text.attention_mask, text_atts_neg],dim=0) + + image_embeds_all = torch.cat([image_embeds_neg,image_embeds],dim=0) + image_atts_all = torch.cat([image_atts,image_atts],dim=0) + + output_neg = self.text_encoder(text_ids_all, + attention_mask = text_atts_all, + encoder_hidden_states = image_embeds_all, + encoder_attention_mask = image_atts_all, + return_dict = True, + ) + + + vl_embeddings = torch.cat([output_pos.last_hidden_state[:,0,:], output_neg.last_hidden_state[:,0,:]],dim=0) + vl_output = self.itm_head(vl_embeddings) + + itm_labels = torch.cat([torch.ones(bs,dtype=torch.long),torch.zeros(2*bs,dtype=torch.long)], + dim=0).to(image.device) + loss_itm = F.cross_entropy(vl_output, itm_labels) + + return loss_ita, loss_itm + + + @torch.no_grad() + def copy_params(self): + for model_pair in self.model_pairs: + for param, param_m in zip(model_pair[0].parameters(), model_pair[1].parameters()): + param_m.data.copy_(param.data) # initialize + param_m.requires_grad = False # not update by gradient + + + @torch.no_grad() + def _momentum_update(self): + for model_pair in self.model_pairs: + for param, param_m in zip(model_pair[0].parameters(), model_pair[1].parameters()): + param_m.data = param_m.data * self.momentum + param.data * (1. - self.momentum) + + + @torch.no_grad() + def _dequeue_and_enqueue(self, image_feat, text_feat, idxs): + # gather keys before updating queue + image_feats = concat_all_gather(image_feat) + text_feats = concat_all_gather(text_feat) + + + batch_size = image_feats.shape[0] + + ptr = int(self.ptr_queue) + assert self.queue_size % batch_size == 0 # for simplicity + + # replace the keys at ptr (dequeue and enqueue) + self.image_queue[:, ptr:ptr + batch_size] = image_feats.T + self.text_queue[:, ptr:ptr + batch_size] = text_feats.T + self.idx_queue[:, ptr:ptr + batch_size] = idxs.T + ptr = (ptr + batch_size) % self.queue_size # move pointer + + self.ptr_queue[0] = ptr + + +def blip_retrieval(pretrained='',**kwargs): + model = BLIP_Retrieval(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + print("missing keys:") + print(msg.missing_keys) + return model + + +@torch.no_grad() +def concat_all_gather(tensor): + """ + Performs all_gather operation on the provided tensors. + *** Warning ***: torch.distributed.all_gather has no gradient. + """ + tensors_gather = [torch.ones_like(tensor) + for _ in range(torch.distributed.get_world_size())] + torch.distributed.all_gather(tensors_gather, tensor, async_op=False) + + output = torch.cat(tensors_gather, dim=0) + return output + + +class GatherLayer(torch.autograd.Function): + """ + Gather tensors from all workers with support for backward propagation: + This implementation does not cut the gradients as torch.distributed.all_gather does. + """ + + @staticmethod + def forward(ctx, x): + output = [torch.zeros_like(x) for _ in range(torch.distributed.get_world_size())] + torch.distributed.all_gather(output, x) + return tuple(output) + + @staticmethod + def backward(ctx, *grads): + all_gradients = torch.stack(grads) + torch.distributed.all_reduce(all_gradients) + return all_gradients[torch.distributed.get_rank()] + + +def all_gather_with_grad(tensors): + """ + Performs all_gather operation on the provided tensors. + Graph remains connected for backward grad computation. + """ + # Queue the gathered tensors + world_size = torch.distributed.get_world_size() + # There is no need for reduction in the single-proc case + if world_size == 1: + return tensors + + tensor_all = GatherLayer.apply(tensors) + + return torch.cat(tensor_all, dim=0) diff --git a/sd/stablediffusion/src/blip/models/blip_vqa.py b/sd/stablediffusion/src/blip/models/blip_vqa.py new file mode 100644 index 0000000000000000000000000000000000000000..d4cb3688fad03888f8568ec65437ee20452c6cb8 --- /dev/null +++ b/sd/stablediffusion/src/blip/models/blip_vqa.py @@ -0,0 +1,186 @@ +from models.med import BertConfig, BertModel, BertLMHeadModel +from models.blip import create_vit, init_tokenizer, load_checkpoint + +import torch +from torch import nn +import torch.nn.functional as F +from transformers import BertTokenizer +import numpy as np + +class BLIP_VQA(nn.Module): + def __init__(self, + med_config = 'configs/med_config.json', + image_size = 480, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit, image_size, vit_grad_ckpt, vit_ckpt_layer, drop_path_rate=0.1) + self.tokenizer = init_tokenizer() + + encoder_config = BertConfig.from_json_file(med_config) + encoder_config.encoder_width = vision_width + self.text_encoder = BertModel(config=encoder_config, add_pooling_layer=False) + + decoder_config = BertConfig.from_json_file(med_config) + self.text_decoder = BertLMHeadModel(config=decoder_config) + + + def forward(self, image, question, answer=None, n=None, weights=None, train=True, inference='rank', k_test=128): + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + + question = self.tokenizer(question, padding='longest', truncation=True, max_length=35, + return_tensors="pt").to(image.device) + question.input_ids[:,0] = self.tokenizer.enc_token_id + + if train: + ''' + n: number of answers for each question + weights: weight for each answer + ''' + answer = self.tokenizer(answer, padding='longest', return_tensors="pt").to(image.device) + answer.input_ids[:,0] = self.tokenizer.bos_token_id + answer_targets = answer.input_ids.masked_fill(answer.input_ids == self.tokenizer.pad_token_id, -100) + + question_output = self.text_encoder(question.input_ids, + attention_mask = question.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True) + + question_states = [] + question_atts = [] + for b, n in enumerate(n): + question_states += [question_output.last_hidden_state[b]]*n + question_atts += [question.attention_mask[b]]*n + question_states = torch.stack(question_states,0) + question_atts = torch.stack(question_atts,0) + + answer_output = self.text_decoder(answer.input_ids, + attention_mask = answer.attention_mask, + encoder_hidden_states = question_states, + encoder_attention_mask = question_atts, + labels = answer_targets, + return_dict = True, + reduction = 'none', + ) + + loss = weights * answer_output.loss + loss = loss.sum()/image.size(0) + + return loss + + + else: + question_output = self.text_encoder(question.input_ids, + attention_mask = question.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True) + + if inference=='generate': + num_beams = 3 + question_states = question_output.last_hidden_state.repeat_interleave(num_beams,dim=0) + question_atts = torch.ones(question_states.size()[:-1],dtype=torch.long).to(question_states.device) + model_kwargs = {"encoder_hidden_states": question_states, "encoder_attention_mask":question_atts} + + bos_ids = torch.full((image.size(0),1),fill_value=self.tokenizer.bos_token_id,device=image.device) + + outputs = self.text_decoder.generate(input_ids=bos_ids, + max_length=10, + min_length=1, + num_beams=num_beams, + eos_token_id=self.tokenizer.sep_token_id, + pad_token_id=self.tokenizer.pad_token_id, + **model_kwargs) + + answers = [] + for output in outputs: + answer = self.tokenizer.decode(output, skip_special_tokens=True) + answers.append(answer) + return answers + + elif inference=='rank': + max_ids = self.rank_answer(question_output.last_hidden_state, question.attention_mask, + answer.input_ids, answer.attention_mask, k_test) + return max_ids + + + + def rank_answer(self, question_states, question_atts, answer_ids, answer_atts, k): + + num_ques = question_states.size(0) + start_ids = answer_ids[0,0].repeat(num_ques,1) # bos token + + start_output = self.text_decoder(start_ids, + encoder_hidden_states = question_states, + encoder_attention_mask = question_atts, + return_dict = True, + reduction = 'none') + logits = start_output.logits[:,0,:] # first token's logit + + # topk_probs: top-k probability + # topk_ids: [num_question, k] + answer_first_token = answer_ids[:,1] + prob_first_token = F.softmax(logits,dim=1).index_select(dim=1, index=answer_first_token) + topk_probs, topk_ids = prob_first_token.topk(k,dim=1) + + # answer input: [num_question*k, answer_len] + input_ids = [] + input_atts = [] + for b, topk_id in enumerate(topk_ids): + input_ids.append(answer_ids.index_select(dim=0, index=topk_id)) + input_atts.append(answer_atts.index_select(dim=0, index=topk_id)) + input_ids = torch.cat(input_ids,dim=0) + input_atts = torch.cat(input_atts,dim=0) + + targets_ids = input_ids.masked_fill(input_ids == self.tokenizer.pad_token_id, -100) + + # repeat encoder's output for top-k answers + question_states = tile(question_states, 0, k) + question_atts = tile(question_atts, 0, k) + + output = self.text_decoder(input_ids, + attention_mask = input_atts, + encoder_hidden_states = question_states, + encoder_attention_mask = question_atts, + labels = targets_ids, + return_dict = True, + reduction = 'none') + + log_probs_sum = -output.loss + log_probs_sum = log_probs_sum.view(num_ques,k) + + max_topk_ids = log_probs_sum.argmax(dim=1) + max_ids = topk_ids[max_topk_ids>=0,max_topk_ids] + + return max_ids + + +def blip_vqa(pretrained='',**kwargs): + model = BLIP_VQA(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) +# assert(len(msg.missing_keys)==0) + return model + + +def tile(x, dim, n_tile): + init_dim = x.size(dim) + repeat_idx = [1] * x.dim() + repeat_idx[dim] = n_tile + x = x.repeat(*(repeat_idx)) + order_index = torch.LongTensor(np.concatenate([init_dim * np.arange(n_tile) + i for i in range(init_dim)])) + return torch.index_select(x, dim, order_index.to(x.device)) + + \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/models/med.py b/sd/stablediffusion/src/blip/models/med.py new file mode 100644 index 0000000000000000000000000000000000000000..7b00a35450b736180a805d4f4664b4fb95aeba01 --- /dev/null +++ b/sd/stablediffusion/src/blip/models/med.py @@ -0,0 +1,955 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li + * Based on huggingface code base + * https://github.com/huggingface/transformers/blob/v4.15.0/src/transformers/models/bert +''' + +import math +import os +import warnings +from dataclasses import dataclass +from typing import Optional, Tuple + +import torch +from torch import Tensor, device, dtype, nn +import torch.utils.checkpoint +from torch import nn +from torch.nn import CrossEntropyLoss +import torch.nn.functional as F + +from transformers.activations import ACT2FN +from transformers.file_utils import ( + ModelOutput, +) +from transformers.modeling_outputs import ( + BaseModelOutputWithPastAndCrossAttentions, + BaseModelOutputWithPoolingAndCrossAttentions, + CausalLMOutputWithCrossAttentions, + MaskedLMOutput, + MultipleChoiceModelOutput, + NextSentencePredictorOutput, + QuestionAnsweringModelOutput, + SequenceClassifierOutput, + TokenClassifierOutput, +) +from transformers.modeling_utils import ( + PreTrainedModel, + apply_chunking_to_forward, + find_pruneable_heads_and_indices, + prune_linear_layer, +) +from transformers.utils import logging +from transformers.models.bert.configuration_bert import BertConfig + + +logger = logging.get_logger(__name__) + + +class BertEmbeddings(nn.Module): + """Construct the embeddings from word and position embeddings.""" + + def __init__(self, config): + super().__init__() + self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=config.pad_token_id) + self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size) + + # self.LayerNorm is not snake-cased to stick with TensorFlow model variable name and be able to load + # any TensorFlow checkpoint file + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + # position_ids (1, len position emb) is contiguous in memory and exported when serialized + self.register_buffer("position_ids", torch.arange(config.max_position_embeddings).expand((1, -1))) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + + self.config = config + + def forward( + self, input_ids=None, position_ids=None, inputs_embeds=None, past_key_values_length=0 + ): + if input_ids is not None: + input_shape = input_ids.size() + else: + input_shape = inputs_embeds.size()[:-1] + + seq_length = input_shape[1] + + if position_ids is None: + position_ids = self.position_ids[:, past_key_values_length : seq_length + past_key_values_length] + + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + + embeddings = inputs_embeds + + if self.position_embedding_type == "absolute": + position_embeddings = self.position_embeddings(position_ids) + embeddings += position_embeddings + embeddings = self.LayerNorm(embeddings) + embeddings = self.dropout(embeddings) + return embeddings + + +class BertSelfAttention(nn.Module): + def __init__(self, config, is_cross_attention): + super().__init__() + self.config = config + if config.hidden_size % config.num_attention_heads != 0 and not hasattr(config, "embedding_size"): + raise ValueError( + "The hidden size (%d) is not a multiple of the number of attention " + "heads (%d)" % (config.hidden_size, config.num_attention_heads) + ) + + self.num_attention_heads = config.num_attention_heads + self.attention_head_size = int(config.hidden_size / config.num_attention_heads) + self.all_head_size = self.num_attention_heads * self.attention_head_size + + self.query = nn.Linear(config.hidden_size, self.all_head_size) + if is_cross_attention: + self.key = nn.Linear(config.encoder_width, self.all_head_size) + self.value = nn.Linear(config.encoder_width, self.all_head_size) + else: + self.key = nn.Linear(config.hidden_size, self.all_head_size) + self.value = nn.Linear(config.hidden_size, self.all_head_size) + + self.dropout = nn.Dropout(config.attention_probs_dropout_prob) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + self.max_position_embeddings = config.max_position_embeddings + self.distance_embedding = nn.Embedding(2 * config.max_position_embeddings - 1, self.attention_head_size) + self.save_attention = False + + def save_attn_gradients(self, attn_gradients): + self.attn_gradients = attn_gradients + + def get_attn_gradients(self): + return self.attn_gradients + + def save_attention_map(self, attention_map): + self.attention_map = attention_map + + def get_attention_map(self): + return self.attention_map + + def transpose_for_scores(self, x): + new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size) + x = x.view(*new_x_shape) + return x.permute(0, 2, 1, 3) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + mixed_query_layer = self.query(hidden_states) + + # If this is instantiated as a cross-attention module, the keys + # and values come from an encoder; the attention mask needs to be + # such that the encoder's padding tokens are not attended to. + is_cross_attention = encoder_hidden_states is not None + + if is_cross_attention: + key_layer = self.transpose_for_scores(self.key(encoder_hidden_states)) + value_layer = self.transpose_for_scores(self.value(encoder_hidden_states)) + attention_mask = encoder_attention_mask + elif past_key_value is not None: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + key_layer = torch.cat([past_key_value[0], key_layer], dim=2) + value_layer = torch.cat([past_key_value[1], value_layer], dim=2) + else: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + + query_layer = self.transpose_for_scores(mixed_query_layer) + + past_key_value = (key_layer, value_layer) + + # Take the dot product between "query" and "key" to get the raw attention scores. + attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2)) + + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + seq_length = hidden_states.size()[1] + position_ids_l = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(-1, 1) + position_ids_r = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(1, -1) + distance = position_ids_l - position_ids_r + positional_embedding = self.distance_embedding(distance + self.max_position_embeddings - 1) + positional_embedding = positional_embedding.to(dtype=query_layer.dtype) # fp16 compatibility + + if self.position_embedding_type == "relative_key": + relative_position_scores = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores + elif self.position_embedding_type == "relative_key_query": + relative_position_scores_query = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + relative_position_scores_key = torch.einsum("bhrd,lrd->bhlr", key_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores_query + relative_position_scores_key + + attention_scores = attention_scores / math.sqrt(self.attention_head_size) + if attention_mask is not None: + # Apply the attention mask is (precomputed for all layers in BertModel forward() function) + attention_scores = attention_scores + attention_mask + + # Normalize the attention scores to probabilities. + attention_probs = nn.Softmax(dim=-1)(attention_scores) + + if is_cross_attention and self.save_attention: + self.save_attention_map(attention_probs) + attention_probs.register_hook(self.save_attn_gradients) + + # This is actually dropping out entire tokens to attend to, which might + # seem a bit unusual, but is taken from the original Transformer paper. + attention_probs_dropped = self.dropout(attention_probs) + + # Mask heads if we want to + if head_mask is not None: + attention_probs_dropped = attention_probs_dropped * head_mask + + context_layer = torch.matmul(attention_probs_dropped, value_layer) + + context_layer = context_layer.permute(0, 2, 1, 3).contiguous() + new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,) + context_layer = context_layer.view(*new_context_layer_shape) + + outputs = (context_layer, attention_probs) if output_attentions else (context_layer,) + + outputs = outputs + (past_key_value,) + return outputs + + +class BertSelfOutput(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertAttention(nn.Module): + def __init__(self, config, is_cross_attention=False): + super().__init__() + self.self = BertSelfAttention(config, is_cross_attention) + self.output = BertSelfOutput(config) + self.pruned_heads = set() + + def prune_heads(self, heads): + if len(heads) == 0: + return + heads, index = find_pruneable_heads_and_indices( + heads, self.self.num_attention_heads, self.self.attention_head_size, self.pruned_heads + ) + + # Prune linear layers + self.self.query = prune_linear_layer(self.self.query, index) + self.self.key = prune_linear_layer(self.self.key, index) + self.self.value = prune_linear_layer(self.self.value, index) + self.output.dense = prune_linear_layer(self.output.dense, index, dim=1) + + # Update hyper params and store pruned heads + self.self.num_attention_heads = self.self.num_attention_heads - len(heads) + self.self.all_head_size = self.self.attention_head_size * self.self.num_attention_heads + self.pruned_heads = self.pruned_heads.union(heads) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + self_outputs = self.self( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + ) + attention_output = self.output(self_outputs[0], hidden_states) + outputs = (attention_output,) + self_outputs[1:] # add attentions if we output them + return outputs + + +class BertIntermediate(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.intermediate_size) + if isinstance(config.hidden_act, str): + self.intermediate_act_fn = ACT2FN[config.hidden_act] + else: + self.intermediate_act_fn = config.hidden_act + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.intermediate_act_fn(hidden_states) + return hidden_states + + +class BertOutput(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.intermediate_size, config.hidden_size) + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertLayer(nn.Module): + def __init__(self, config, layer_num): + super().__init__() + self.config = config + self.chunk_size_feed_forward = config.chunk_size_feed_forward + self.seq_len_dim = 1 + self.attention = BertAttention(config) + self.layer_num = layer_num + if self.config.add_cross_attention: + self.crossattention = BertAttention(config, is_cross_attention=self.config.add_cross_attention) + self.intermediate = BertIntermediate(config) + self.output = BertOutput(config) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + mode=None, + ): + # decoder uni-directional self-attention cached key/values tuple is at positions 1,2 + self_attn_past_key_value = past_key_value[:2] if past_key_value is not None else None + self_attention_outputs = self.attention( + hidden_states, + attention_mask, + head_mask, + output_attentions=output_attentions, + past_key_value=self_attn_past_key_value, + ) + attention_output = self_attention_outputs[0] + + outputs = self_attention_outputs[1:-1] + present_key_value = self_attention_outputs[-1] + + if mode=='multimodal': + assert encoder_hidden_states is not None, "encoder_hidden_states must be given for cross-attention layers" + + cross_attention_outputs = self.crossattention( + attention_output, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + output_attentions=output_attentions, + ) + attention_output = cross_attention_outputs[0] + outputs = outputs + cross_attention_outputs[1:-1] # add cross attentions if we output attention weights + layer_output = apply_chunking_to_forward( + self.feed_forward_chunk, self.chunk_size_feed_forward, self.seq_len_dim, attention_output + ) + outputs = (layer_output,) + outputs + + outputs = outputs + (present_key_value,) + + return outputs + + def feed_forward_chunk(self, attention_output): + intermediate_output = self.intermediate(attention_output) + layer_output = self.output(intermediate_output, attention_output) + return layer_output + + +class BertEncoder(nn.Module): + def __init__(self, config): + super().__init__() + self.config = config + self.layer = nn.ModuleList([BertLayer(config,i) for i in range(config.num_hidden_layers)]) + self.gradient_checkpointing = False + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=False, + output_hidden_states=False, + return_dict=True, + mode='multimodal', + ): + all_hidden_states = () if output_hidden_states else None + all_self_attentions = () if output_attentions else None + all_cross_attentions = () if output_attentions and self.config.add_cross_attention else None + + next_decoder_cache = () if use_cache else None + + for i in range(self.config.num_hidden_layers): + layer_module = self.layer[i] + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + layer_head_mask = head_mask[i] if head_mask is not None else None + past_key_value = past_key_values[i] if past_key_values is not None else None + + if self.gradient_checkpointing and self.training: + + if use_cache: + logger.warn( + "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..." + ) + use_cache = False + + def create_custom_forward(module): + def custom_forward(*inputs): + return module(*inputs, past_key_value, output_attentions) + + return custom_forward + + layer_outputs = torch.utils.checkpoint.checkpoint( + create_custom_forward(layer_module), + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + mode=mode, + ) + else: + layer_outputs = layer_module( + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + mode=mode, + ) + + hidden_states = layer_outputs[0] + if use_cache: + next_decoder_cache += (layer_outputs[-1],) + if output_attentions: + all_self_attentions = all_self_attentions + (layer_outputs[1],) + + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + if not return_dict: + return tuple( + v + for v in [ + hidden_states, + next_decoder_cache, + all_hidden_states, + all_self_attentions, + all_cross_attentions, + ] + if v is not None + ) + return BaseModelOutputWithPastAndCrossAttentions( + last_hidden_state=hidden_states, + past_key_values=next_decoder_cache, + hidden_states=all_hidden_states, + attentions=all_self_attentions, + cross_attentions=all_cross_attentions, + ) + + +class BertPooler(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.activation = nn.Tanh() + + def forward(self, hidden_states): + # We "pool" the model by simply taking the hidden state corresponding + # to the first token. + first_token_tensor = hidden_states[:, 0] + pooled_output = self.dense(first_token_tensor) + pooled_output = self.activation(pooled_output) + return pooled_output + + +class BertPredictionHeadTransform(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + if isinstance(config.hidden_act, str): + self.transform_act_fn = ACT2FN[config.hidden_act] + else: + self.transform_act_fn = config.hidden_act + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.transform_act_fn(hidden_states) + hidden_states = self.LayerNorm(hidden_states) + return hidden_states + + +class BertLMPredictionHead(nn.Module): + def __init__(self, config): + super().__init__() + self.transform = BertPredictionHeadTransform(config) + + # The output weights are the same as the input embeddings, but there is + # an output-only bias for each token. + self.decoder = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + + self.bias = nn.Parameter(torch.zeros(config.vocab_size)) + + # Need a link between the two variables so that the bias is correctly resized with `resize_token_embeddings` + self.decoder.bias = self.bias + + def forward(self, hidden_states): + hidden_states = self.transform(hidden_states) + hidden_states = self.decoder(hidden_states) + return hidden_states + + +class BertOnlyMLMHead(nn.Module): + def __init__(self, config): + super().__init__() + self.predictions = BertLMPredictionHead(config) + + def forward(self, sequence_output): + prediction_scores = self.predictions(sequence_output) + return prediction_scores + + +class BertPreTrainedModel(PreTrainedModel): + """ + An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained + models. + """ + + config_class = BertConfig + base_model_prefix = "bert" + _keys_to_ignore_on_load_missing = [r"position_ids"] + + def _init_weights(self, module): + """ Initialize the weights """ + if isinstance(module, (nn.Linear, nn.Embedding)): + # Slightly different from the TF version which uses truncated_normal for initialization + # cf https://github.com/pytorch/pytorch/pull/5617 + module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + + +class BertModel(BertPreTrainedModel): + """ + The model can behave as an encoder (with only self-attention) as well as a decoder, in which case a layer of + cross-attention is added between the self-attention layers, following the architecture described in `Attention is + all you need `__ by Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, + Llion Jones, Aidan N. Gomez, Lukasz Kaiser and Illia Polosukhin. + argument and :obj:`add_cross_attention` set to :obj:`True`; an :obj:`encoder_hidden_states` is then expected as an + input to the forward pass. + """ + + def __init__(self, config, add_pooling_layer=True): + super().__init__(config) + self.config = config + + self.embeddings = BertEmbeddings(config) + + self.encoder = BertEncoder(config) + + self.pooler = BertPooler(config) if add_pooling_layer else None + + self.init_weights() + + + def get_input_embeddings(self): + return self.embeddings.word_embeddings + + def set_input_embeddings(self, value): + self.embeddings.word_embeddings = value + + def _prune_heads(self, heads_to_prune): + """ + Prunes heads of the model. heads_to_prune: dict of {layer_num: list of heads to prune in this layer} See base + class PreTrainedModel + """ + for layer, heads in heads_to_prune.items(): + self.encoder.layer[layer].attention.prune_heads(heads) + + + def get_extended_attention_mask(self, attention_mask: Tensor, input_shape: Tuple[int], device: device, is_decoder: bool) -> Tensor: + """ + Makes broadcastable attention and causal masks so that future and masked tokens are ignored. + + Arguments: + attention_mask (:obj:`torch.Tensor`): + Mask with ones indicating tokens to attend to, zeros for tokens to ignore. + input_shape (:obj:`Tuple[int]`): + The shape of the input to the model. + device: (:obj:`torch.device`): + The device of the input to the model. + + Returns: + :obj:`torch.Tensor` The extended attention mask, with a the same dtype as :obj:`attention_mask.dtype`. + """ + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + if attention_mask.dim() == 3: + extended_attention_mask = attention_mask[:, None, :, :] + elif attention_mask.dim() == 2: + # Provided a padding mask of dimensions [batch_size, seq_length] + # - if the model is a decoder, apply a causal mask in addition to the padding mask + # - if the model is an encoder, make the mask broadcastable to [batch_size, num_heads, seq_length, seq_length] + if is_decoder: + batch_size, seq_length = input_shape + + seq_ids = torch.arange(seq_length, device=device) + causal_mask = seq_ids[None, None, :].repeat(batch_size, seq_length, 1) <= seq_ids[None, :, None] + # in case past_key_values are used we need to add a prefix ones mask to the causal mask + # causal and attention masks must have same type with pytorch version < 1.3 + causal_mask = causal_mask.to(attention_mask.dtype) + + if causal_mask.shape[1] < attention_mask.shape[1]: + prefix_seq_len = attention_mask.shape[1] - causal_mask.shape[1] + causal_mask = torch.cat( + [ + torch.ones((batch_size, seq_length, prefix_seq_len), device=device, dtype=causal_mask.dtype), + causal_mask, + ], + axis=-1, + ) + + extended_attention_mask = causal_mask[:, None, :, :] * attention_mask[:, None, None, :] + else: + extended_attention_mask = attention_mask[:, None, None, :] + else: + raise ValueError( + "Wrong shape for input_ids (shape {}) or attention_mask (shape {})".format( + input_shape, attention_mask.shape + ) + ) + + # Since attention_mask is 1.0 for positions we want to attend and 0.0 for + # masked positions, this operation will create a tensor which is 0.0 for + # positions we want to attend and -10000.0 for masked positions. + # Since we are adding it to the raw scores before the softmax, this is + # effectively the same as removing these entirely. + extended_attention_mask = extended_attention_mask.to(dtype=self.dtype) # fp16 compatibility + extended_attention_mask = (1.0 - extended_attention_mask) * -10000.0 + return extended_attention_mask + + def forward( + self, + input_ids=None, + attention_mask=None, + position_ids=None, + head_mask=None, + inputs_embeds=None, + encoder_embeds=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + is_decoder=False, + mode='multimodal', + ): + r""" + encoder_hidden_states (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): + Sequence of hidden-states at the output of the last layer of the encoder. Used in the cross-attention if + the model is configured as a decoder. + encoder_attention_mask (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Mask to avoid performing attention on the padding token indices of the encoder input. This mask is used in + the cross-attention if the model is configured as a decoder. Mask values selected in ``[0, 1]``: + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + past_key_values (:obj:`tuple(tuple(torch.FloatTensor))` of length :obj:`config.n_layers` with each tuple having 4 tensors of shape :obj:`(batch_size, num_heads, sequence_length - 1, embed_size_per_head)`): + Contains precomputed key and value hidden states of the attention blocks. Can be used to speed up decoding. + If :obj:`past_key_values` are used, the user can optionally input only the last :obj:`decoder_input_ids` + (those that don't have their past key value states given to this model) of shape :obj:`(batch_size, 1)` + instead of all :obj:`decoder_input_ids` of shape :obj:`(batch_size, sequence_length)`. + use_cache (:obj:`bool`, `optional`): + If set to :obj:`True`, :obj:`past_key_values` key value states are returned and can be used to speed up + decoding (see :obj:`past_key_values`). + """ + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions + output_hidden_states = ( + output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states + ) + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + if is_decoder: + use_cache = use_cache if use_cache is not None else self.config.use_cache + else: + use_cache = False + + if input_ids is not None and inputs_embeds is not None: + raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time") + elif input_ids is not None: + input_shape = input_ids.size() + batch_size, seq_length = input_shape + device = input_ids.device + elif inputs_embeds is not None: + input_shape = inputs_embeds.size()[:-1] + batch_size, seq_length = input_shape + device = inputs_embeds.device + elif encoder_embeds is not None: + input_shape = encoder_embeds.size()[:-1] + batch_size, seq_length = input_shape + device = encoder_embeds.device + else: + raise ValueError("You have to specify either input_ids or inputs_embeds or encoder_embeds") + + # past_key_values_length + past_key_values_length = past_key_values[0][0].shape[2] if past_key_values is not None else 0 + + if attention_mask is None: + attention_mask = torch.ones(((batch_size, seq_length + past_key_values_length)), device=device) + + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + extended_attention_mask: torch.Tensor = self.get_extended_attention_mask(attention_mask, input_shape, + device, is_decoder) + + # If a 2D or 3D attention mask is provided for the cross-attention + # we need to make broadcastable to [batch_size, num_heads, seq_length, seq_length] + if encoder_hidden_states is not None: + if type(encoder_hidden_states) == list: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states[0].size() + else: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states.size() + encoder_hidden_shape = (encoder_batch_size, encoder_sequence_length) + + if type(encoder_attention_mask) == list: + encoder_extended_attention_mask = [self.invert_attention_mask(mask) for mask in encoder_attention_mask] + elif encoder_attention_mask is None: + encoder_attention_mask = torch.ones(encoder_hidden_shape, device=device) + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = None + + # Prepare head mask if needed + # 1.0 in head_mask indicate we keep the head + # attention_probs has shape bsz x n_heads x N x N + # input head_mask has shape [num_heads] or [num_hidden_layers x num_heads] + # and head_mask is converted to shape [num_hidden_layers x batch x num_heads x seq_length x seq_length] + head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers) + + if encoder_embeds is None: + embedding_output = self.embeddings( + input_ids=input_ids, + position_ids=position_ids, + inputs_embeds=inputs_embeds, + past_key_values_length=past_key_values_length, + ) + else: + embedding_output = encoder_embeds + + encoder_outputs = self.encoder( + embedding_output, + attention_mask=extended_attention_mask, + head_mask=head_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_extended_attention_mask, + past_key_values=past_key_values, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + mode=mode, + ) + sequence_output = encoder_outputs[0] + pooled_output = self.pooler(sequence_output) if self.pooler is not None else None + + if not return_dict: + return (sequence_output, pooled_output) + encoder_outputs[1:] + + return BaseModelOutputWithPoolingAndCrossAttentions( + last_hidden_state=sequence_output, + pooler_output=pooled_output, + past_key_values=encoder_outputs.past_key_values, + hidden_states=encoder_outputs.hidden_states, + attentions=encoder_outputs.attentions, + cross_attentions=encoder_outputs.cross_attentions, + ) + + + +class BertLMHeadModel(BertPreTrainedModel): + + _keys_to_ignore_on_load_unexpected = [r"pooler"] + _keys_to_ignore_on_load_missing = [r"position_ids", r"predictions.decoder.bias"] + + def __init__(self, config): + super().__init__(config) + + self.bert = BertModel(config, add_pooling_layer=False) + self.cls = BertOnlyMLMHead(config) + + self.init_weights() + + def get_output_embeddings(self): + return self.cls.predictions.decoder + + def set_output_embeddings(self, new_embeddings): + self.cls.predictions.decoder = new_embeddings + + def forward( + self, + input_ids=None, + attention_mask=None, + position_ids=None, + head_mask=None, + inputs_embeds=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + labels=None, + past_key_values=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + return_logits=False, + is_decoder=True, + reduction='mean', + mode='multimodal', + ): + r""" + encoder_hidden_states (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): + Sequence of hidden-states at the output of the last layer of the encoder. Used in the cross-attention if + the model is configured as a decoder. + encoder_attention_mask (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Mask to avoid performing attention on the padding token indices of the encoder input. This mask is used in + the cross-attention if the model is configured as a decoder. Mask values selected in ``[0, 1]``: + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + labels (:obj:`torch.LongTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Labels for computing the left-to-right language modeling loss (next word prediction). Indices should be in + ``[-100, 0, ..., config.vocab_size]`` (see ``input_ids`` docstring) Tokens with indices set to ``-100`` are + ignored (masked), the loss is only computed for the tokens with labels n ``[0, ..., config.vocab_size]`` + past_key_values (:obj:`tuple(tuple(torch.FloatTensor))` of length :obj:`config.n_layers` with each tuple having 4 tensors of shape :obj:`(batch_size, num_heads, sequence_length - 1, embed_size_per_head)`): + Contains precomputed key and value hidden states of the attention blocks. Can be used to speed up decoding. + If :obj:`past_key_values` are used, the user can optionally input only the last :obj:`decoder_input_ids` + (those that don't have their past key value states given to this model) of shape :obj:`(batch_size, 1)` + instead of all :obj:`decoder_input_ids` of shape :obj:`(batch_size, sequence_length)`. + use_cache (:obj:`bool`, `optional`): + If set to :obj:`True`, :obj:`past_key_values` key value states are returned and can be used to speed up + decoding (see :obj:`past_key_values`). + Returns: + Example:: + >>> from transformers import BertTokenizer, BertLMHeadModel, BertConfig + >>> import torch + >>> tokenizer = BertTokenizer.from_pretrained('bert-base-cased') + >>> config = BertConfig.from_pretrained("bert-base-cased") + >>> model = BertLMHeadModel.from_pretrained('bert-base-cased', config=config) + >>> inputs = tokenizer("Hello, my dog is cute", return_tensors="pt") + >>> outputs = model(**inputs) + >>> prediction_logits = outputs.logits + """ + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + if labels is not None: + use_cache = False + + outputs = self.bert( + input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + head_mask=head_mask, + inputs_embeds=inputs_embeds, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + past_key_values=past_key_values, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + is_decoder=is_decoder, + mode=mode, + ) + + sequence_output = outputs[0] + prediction_scores = self.cls(sequence_output) + + if return_logits: + return prediction_scores[:, :-1, :].contiguous() + + lm_loss = None + if labels is not None: + # we are doing next-token prediction; shift prediction scores and input ids by one + shifted_prediction_scores = prediction_scores[:, :-1, :].contiguous() + labels = labels[:, 1:].contiguous() + loss_fct = CrossEntropyLoss(reduction=reduction, label_smoothing=0.1) + lm_loss = loss_fct(shifted_prediction_scores.view(-1, self.config.vocab_size), labels.view(-1)) + if reduction=='none': + lm_loss = lm_loss.view(prediction_scores.size(0),-1).sum(1) + + if not return_dict: + output = (prediction_scores,) + outputs[2:] + return ((lm_loss,) + output) if lm_loss is not None else output + + return CausalLMOutputWithCrossAttentions( + loss=lm_loss, + logits=prediction_scores, + past_key_values=outputs.past_key_values, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + cross_attentions=outputs.cross_attentions, + ) + + def prepare_inputs_for_generation(self, input_ids, past=None, attention_mask=None, **model_kwargs): + input_shape = input_ids.shape + # if model is used as a decoder in encoder-decoder model, the decoder attention mask is created on the fly + if attention_mask is None: + attention_mask = input_ids.new_ones(input_shape) + + # cut decoder_input_ids if past is used + if past is not None: + input_ids = input_ids[:, -1:] + + return { + "input_ids": input_ids, + "attention_mask": attention_mask, + "past_key_values": past, + "encoder_hidden_states": model_kwargs.get("encoder_hidden_states", None), + "encoder_attention_mask": model_kwargs.get("encoder_attention_mask", None), + "is_decoder": True, + } + + def _reorder_cache(self, past, beam_idx): + reordered_past = () + for layer_past in past: + reordered_past += (tuple(past_state.index_select(0, beam_idx) for past_state in layer_past),) + return reordered_past diff --git a/sd/stablediffusion/src/blip/models/nlvr_encoder.py b/sd/stablediffusion/src/blip/models/nlvr_encoder.py new file mode 100644 index 0000000000000000000000000000000000000000..1946bb4a300f75afa4848f6622839445903c34a9 --- /dev/null +++ b/sd/stablediffusion/src/blip/models/nlvr_encoder.py @@ -0,0 +1,843 @@ +import math +import os +import warnings +from dataclasses import dataclass +from typing import Optional, Tuple + +import torch +from torch import Tensor, device, dtype, nn +import torch.utils.checkpoint +from torch import nn +from torch.nn import CrossEntropyLoss +import torch.nn.functional as F + +from transformers.activations import ACT2FN +from transformers.file_utils import ( + ModelOutput, +) +from transformers.modeling_outputs import ( + BaseModelOutputWithPastAndCrossAttentions, + BaseModelOutputWithPoolingAndCrossAttentions, + CausalLMOutputWithCrossAttentions, + MaskedLMOutput, + MultipleChoiceModelOutput, + NextSentencePredictorOutput, + QuestionAnsweringModelOutput, + SequenceClassifierOutput, + TokenClassifierOutput, +) +from transformers.modeling_utils import ( + PreTrainedModel, + apply_chunking_to_forward, + find_pruneable_heads_and_indices, + prune_linear_layer, +) +from transformers.utils import logging +from transformers.models.bert.configuration_bert import BertConfig + + +logger = logging.get_logger(__name__) + + +class BertEmbeddings(nn.Module): + """Construct the embeddings from word and position embeddings.""" + + def __init__(self, config): + super().__init__() + self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=config.pad_token_id) + self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size) + + # self.LayerNorm is not snake-cased to stick with TensorFlow model variable name and be able to load + # any TensorFlow checkpoint file + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + # position_ids (1, len position emb) is contiguous in memory and exported when serialized + self.register_buffer("position_ids", torch.arange(config.max_position_embeddings).expand((1, -1))) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + + self.config = config + + def forward( + self, input_ids=None, position_ids=None, inputs_embeds=None, past_key_values_length=0 + ): + if input_ids is not None: + input_shape = input_ids.size() + else: + input_shape = inputs_embeds.size()[:-1] + + seq_length = input_shape[1] + + if position_ids is None: + position_ids = self.position_ids[:, past_key_values_length : seq_length + past_key_values_length] + + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + + embeddings = inputs_embeds + + if self.position_embedding_type == "absolute": + position_embeddings = self.position_embeddings(position_ids) + embeddings += position_embeddings + embeddings = self.LayerNorm(embeddings) + embeddings = self.dropout(embeddings) + return embeddings + + +class BertSelfAttention(nn.Module): + def __init__(self, config, is_cross_attention): + super().__init__() + self.config = config + if config.hidden_size % config.num_attention_heads != 0 and not hasattr(config, "embedding_size"): + raise ValueError( + "The hidden size (%d) is not a multiple of the number of attention " + "heads (%d)" % (config.hidden_size, config.num_attention_heads) + ) + + self.num_attention_heads = config.num_attention_heads + self.attention_head_size = int(config.hidden_size / config.num_attention_heads) + self.all_head_size = self.num_attention_heads * self.attention_head_size + + self.query = nn.Linear(config.hidden_size, self.all_head_size) + if is_cross_attention: + self.key = nn.Linear(config.encoder_width, self.all_head_size) + self.value = nn.Linear(config.encoder_width, self.all_head_size) + else: + self.key = nn.Linear(config.hidden_size, self.all_head_size) + self.value = nn.Linear(config.hidden_size, self.all_head_size) + + self.dropout = nn.Dropout(config.attention_probs_dropout_prob) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + self.max_position_embeddings = config.max_position_embeddings + self.distance_embedding = nn.Embedding(2 * config.max_position_embeddings - 1, self.attention_head_size) + self.save_attention = False + + def save_attn_gradients(self, attn_gradients): + self.attn_gradients = attn_gradients + + def get_attn_gradients(self): + return self.attn_gradients + + def save_attention_map(self, attention_map): + self.attention_map = attention_map + + def get_attention_map(self): + return self.attention_map + + def transpose_for_scores(self, x): + new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size) + x = x.view(*new_x_shape) + return x.permute(0, 2, 1, 3) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + mixed_query_layer = self.query(hidden_states) + + # If this is instantiated as a cross-attention module, the keys + # and values come from an encoder; the attention mask needs to be + # such that the encoder's padding tokens are not attended to. + is_cross_attention = encoder_hidden_states is not None + + if is_cross_attention: + key_layer = self.transpose_for_scores(self.key(encoder_hidden_states)) + value_layer = self.transpose_for_scores(self.value(encoder_hidden_states)) + attention_mask = encoder_attention_mask + elif past_key_value is not None: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + key_layer = torch.cat([past_key_value[0], key_layer], dim=2) + value_layer = torch.cat([past_key_value[1], value_layer], dim=2) + else: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + + query_layer = self.transpose_for_scores(mixed_query_layer) + + past_key_value = (key_layer, value_layer) + + # Take the dot product between "query" and "key" to get the raw attention scores. + attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2)) + + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + seq_length = hidden_states.size()[1] + position_ids_l = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(-1, 1) + position_ids_r = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(1, -1) + distance = position_ids_l - position_ids_r + positional_embedding = self.distance_embedding(distance + self.max_position_embeddings - 1) + positional_embedding = positional_embedding.to(dtype=query_layer.dtype) # fp16 compatibility + + if self.position_embedding_type == "relative_key": + relative_position_scores = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores + elif self.position_embedding_type == "relative_key_query": + relative_position_scores_query = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + relative_position_scores_key = torch.einsum("bhrd,lrd->bhlr", key_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores_query + relative_position_scores_key + + attention_scores = attention_scores / math.sqrt(self.attention_head_size) + if attention_mask is not None: + # Apply the attention mask is (precomputed for all layers in BertModel forward() function) + attention_scores = attention_scores + attention_mask + + # Normalize the attention scores to probabilities. + attention_probs = nn.Softmax(dim=-1)(attention_scores) + + if is_cross_attention and self.save_attention: + self.save_attention_map(attention_probs) + attention_probs.register_hook(self.save_attn_gradients) + + # This is actually dropping out entire tokens to attend to, which might + # seem a bit unusual, but is taken from the original Transformer paper. + attention_probs_dropped = self.dropout(attention_probs) + + # Mask heads if we want to + if head_mask is not None: + attention_probs_dropped = attention_probs_dropped * head_mask + + context_layer = torch.matmul(attention_probs_dropped, value_layer) + + context_layer = context_layer.permute(0, 2, 1, 3).contiguous() + new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,) + context_layer = context_layer.view(*new_context_layer_shape) + + outputs = (context_layer, attention_probs) if output_attentions else (context_layer,) + + outputs = outputs + (past_key_value,) + return outputs + + +class BertSelfOutput(nn.Module): + def __init__(self, config, twin=False, merge=False): + super().__init__() + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + if twin: + self.dense0 = nn.Linear(config.hidden_size, config.hidden_size) + self.dense1 = nn.Linear(config.hidden_size, config.hidden_size) + else: + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + if merge: + self.act = ACT2FN[config.hidden_act] + self.merge_layer = nn.Linear(config.hidden_size * 2, config.hidden_size) + self.merge = True + else: + self.merge = False + + def forward(self, hidden_states, input_tensor): + if type(hidden_states) == list: + hidden_states0 = self.dense0(hidden_states[0]) + hidden_states1 = self.dense1(hidden_states[1]) + if self.merge: + #hidden_states = self.merge_layer(self.act(torch.cat([hidden_states0,hidden_states1],dim=-1))) + hidden_states = self.merge_layer(torch.cat([hidden_states0,hidden_states1],dim=-1)) + else: + hidden_states = (hidden_states0+hidden_states1)/2 + else: + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertAttention(nn.Module): + def __init__(self, config, is_cross_attention=False, layer_num=-1): + super().__init__() + if is_cross_attention: + self.self0 = BertSelfAttention(config, is_cross_attention) + self.self1 = BertSelfAttention(config, is_cross_attention) + else: + self.self = BertSelfAttention(config, is_cross_attention) + self.output = BertSelfOutput(config, twin=is_cross_attention, merge=(is_cross_attention and layer_num>=6)) + self.pruned_heads = set() + + def prune_heads(self, heads): + if len(heads) == 0: + return + heads, index = find_pruneable_heads_and_indices( + heads, self.self.num_attention_heads, self.self.attention_head_size, self.pruned_heads + ) + + # Prune linear layers + self.self.query = prune_linear_layer(self.self.query, index) + self.self.key = prune_linear_layer(self.self.key, index) + self.self.value = prune_linear_layer(self.self.value, index) + self.output.dense = prune_linear_layer(self.output.dense, index, dim=1) + + # Update hyper params and store pruned heads + self.self.num_attention_heads = self.self.num_attention_heads - len(heads) + self.self.all_head_size = self.self.attention_head_size * self.self.num_attention_heads + self.pruned_heads = self.pruned_heads.union(heads) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + if type(encoder_hidden_states)==list: + self_outputs0 = self.self0( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states[0], + encoder_attention_mask[0], + past_key_value, + output_attentions, + ) + self_outputs1 = self.self1( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states[1], + encoder_attention_mask[1], + past_key_value, + output_attentions, + ) + attention_output = self.output([self_outputs0[0],self_outputs1[0]], hidden_states) + + outputs = (attention_output,) + self_outputs0[1:] # add attentions if we output them + else: + self_outputs = self.self( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + ) + attention_output = self.output(self_outputs[0], hidden_states) + outputs = (attention_output,) + self_outputs[1:] # add attentions if we output them + return outputs + + +class BertIntermediate(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.intermediate_size) + if isinstance(config.hidden_act, str): + self.intermediate_act_fn = ACT2FN[config.hidden_act] + else: + self.intermediate_act_fn = config.hidden_act + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.intermediate_act_fn(hidden_states) + return hidden_states + + +class BertOutput(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.intermediate_size, config.hidden_size) + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertLayer(nn.Module): + def __init__(self, config, layer_num): + super().__init__() + self.config = config + self.chunk_size_feed_forward = config.chunk_size_feed_forward + self.seq_len_dim = 1 + self.attention = BertAttention(config) + self.layer_num = layer_num + if self.config.add_cross_attention: + self.crossattention = BertAttention(config, is_cross_attention=self.config.add_cross_attention, layer_num=layer_num) + self.intermediate = BertIntermediate(config) + self.output = BertOutput(config) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + mode=None, + ): + # decoder uni-directional self-attention cached key/values tuple is at positions 1,2 + self_attn_past_key_value = past_key_value[:2] if past_key_value is not None else None + self_attention_outputs = self.attention( + hidden_states, + attention_mask, + head_mask, + output_attentions=output_attentions, + past_key_value=self_attn_past_key_value, + ) + attention_output = self_attention_outputs[0] + + outputs = self_attention_outputs[1:-1] + present_key_value = self_attention_outputs[-1] + + if mode=='multimodal': + assert encoder_hidden_states is not None, "encoder_hidden_states must be given for cross-attention layers" + cross_attention_outputs = self.crossattention( + attention_output, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + output_attentions=output_attentions, + ) + attention_output = cross_attention_outputs[0] + outputs = outputs + cross_attention_outputs[1:-1] # add cross attentions if we output attention weights + layer_output = apply_chunking_to_forward( + self.feed_forward_chunk, self.chunk_size_feed_forward, self.seq_len_dim, attention_output + ) + outputs = (layer_output,) + outputs + + outputs = outputs + (present_key_value,) + + return outputs + + def feed_forward_chunk(self, attention_output): + intermediate_output = self.intermediate(attention_output) + layer_output = self.output(intermediate_output, attention_output) + return layer_output + + +class BertEncoder(nn.Module): + def __init__(self, config): + super().__init__() + self.config = config + self.layer = nn.ModuleList([BertLayer(config,i) for i in range(config.num_hidden_layers)]) + self.gradient_checkpointing = False + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=False, + output_hidden_states=False, + return_dict=True, + mode='multimodal', + ): + all_hidden_states = () if output_hidden_states else None + all_self_attentions = () if output_attentions else None + all_cross_attentions = () if output_attentions and self.config.add_cross_attention else None + + next_decoder_cache = () if use_cache else None + + for i in range(self.config.num_hidden_layers): + layer_module = self.layer[i] + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + layer_head_mask = head_mask[i] if head_mask is not None else None + past_key_value = past_key_values[i] if past_key_values is not None else None + + if self.gradient_checkpointing and self.training: + + if use_cache: + logger.warn( + "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..." + ) + use_cache = False + + def create_custom_forward(module): + def custom_forward(*inputs): + return module(*inputs, past_key_value, output_attentions) + + return custom_forward + + layer_outputs = torch.utils.checkpoint.checkpoint( + create_custom_forward(layer_module), + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + mode=mode, + ) + else: + layer_outputs = layer_module( + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + mode=mode, + ) + + hidden_states = layer_outputs[0] + if use_cache: + next_decoder_cache += (layer_outputs[-1],) + if output_attentions: + all_self_attentions = all_self_attentions + (layer_outputs[1],) + + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + if not return_dict: + return tuple( + v + for v in [ + hidden_states, + next_decoder_cache, + all_hidden_states, + all_self_attentions, + all_cross_attentions, + ] + if v is not None + ) + return BaseModelOutputWithPastAndCrossAttentions( + last_hidden_state=hidden_states, + past_key_values=next_decoder_cache, + hidden_states=all_hidden_states, + attentions=all_self_attentions, + cross_attentions=all_cross_attentions, + ) + + +class BertPooler(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.activation = nn.Tanh() + + def forward(self, hidden_states): + # We "pool" the model by simply taking the hidden state corresponding + # to the first token. + first_token_tensor = hidden_states[:, 0] + pooled_output = self.dense(first_token_tensor) + pooled_output = self.activation(pooled_output) + return pooled_output + + +class BertPredictionHeadTransform(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + if isinstance(config.hidden_act, str): + self.transform_act_fn = ACT2FN[config.hidden_act] + else: + self.transform_act_fn = config.hidden_act + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.transform_act_fn(hidden_states) + hidden_states = self.LayerNorm(hidden_states) + return hidden_states + + +class BertLMPredictionHead(nn.Module): + def __init__(self, config): + super().__init__() + self.transform = BertPredictionHeadTransform(config) + + # The output weights are the same as the input embeddings, but there is + # an output-only bias for each token. + self.decoder = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + + self.bias = nn.Parameter(torch.zeros(config.vocab_size)) + + # Need a link between the two variables so that the bias is correctly resized with `resize_token_embeddings` + self.decoder.bias = self.bias + + def forward(self, hidden_states): + hidden_states = self.transform(hidden_states) + hidden_states = self.decoder(hidden_states) + return hidden_states + + +class BertOnlyMLMHead(nn.Module): + def __init__(self, config): + super().__init__() + self.predictions = BertLMPredictionHead(config) + + def forward(self, sequence_output): + prediction_scores = self.predictions(sequence_output) + return prediction_scores + + +class BertPreTrainedModel(PreTrainedModel): + """ + An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained + models. + """ + + config_class = BertConfig + base_model_prefix = "bert" + _keys_to_ignore_on_load_missing = [r"position_ids"] + + def _init_weights(self, module): + """ Initialize the weights """ + if isinstance(module, (nn.Linear, nn.Embedding)): + # Slightly different from the TF version which uses truncated_normal for initialization + # cf https://github.com/pytorch/pytorch/pull/5617 + module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + + +class BertModel(BertPreTrainedModel): + """ + The model can behave as an encoder (with only self-attention) as well as a decoder, in which case a layer of + cross-attention is added between the self-attention layers, following the architecture described in `Attention is + all you need `__ by Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, + Llion Jones, Aidan N. Gomez, Lukasz Kaiser and Illia Polosukhin. + argument and :obj:`add_cross_attention` set to :obj:`True`; an :obj:`encoder_hidden_states` is then expected as an + input to the forward pass. + """ + + def __init__(self, config, add_pooling_layer=True): + super().__init__(config) + self.config = config + + self.embeddings = BertEmbeddings(config) + + self.encoder = BertEncoder(config) + + self.pooler = BertPooler(config) if add_pooling_layer else None + + self.init_weights() + + + def get_input_embeddings(self): + return self.embeddings.word_embeddings + + def set_input_embeddings(self, value): + self.embeddings.word_embeddings = value + + def _prune_heads(self, heads_to_prune): + """ + Prunes heads of the model. heads_to_prune: dict of {layer_num: list of heads to prune in this layer} See base + class PreTrainedModel + """ + for layer, heads in heads_to_prune.items(): + self.encoder.layer[layer].attention.prune_heads(heads) + + + def get_extended_attention_mask(self, attention_mask: Tensor, input_shape: Tuple[int], device: device, is_decoder: bool) -> Tensor: + """ + Makes broadcastable attention and causal masks so that future and masked tokens are ignored. + + Arguments: + attention_mask (:obj:`torch.Tensor`): + Mask with ones indicating tokens to attend to, zeros for tokens to ignore. + input_shape (:obj:`Tuple[int]`): + The shape of the input to the model. + device: (:obj:`torch.device`): + The device of the input to the model. + + Returns: + :obj:`torch.Tensor` The extended attention mask, with a the same dtype as :obj:`attention_mask.dtype`. + """ + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + if attention_mask.dim() == 3: + extended_attention_mask = attention_mask[:, None, :, :] + elif attention_mask.dim() == 2: + # Provided a padding mask of dimensions [batch_size, seq_length] + # - if the model is a decoder, apply a causal mask in addition to the padding mask + # - if the model is an encoder, make the mask broadcastable to [batch_size, num_heads, seq_length, seq_length] + if is_decoder: + batch_size, seq_length = input_shape + + seq_ids = torch.arange(seq_length, device=device) + causal_mask = seq_ids[None, None, :].repeat(batch_size, seq_length, 1) <= seq_ids[None, :, None] + # in case past_key_values are used we need to add a prefix ones mask to the causal mask + # causal and attention masks must have same type with pytorch version < 1.3 + causal_mask = causal_mask.to(attention_mask.dtype) + + if causal_mask.shape[1] < attention_mask.shape[1]: + prefix_seq_len = attention_mask.shape[1] - causal_mask.shape[1] + causal_mask = torch.cat( + [ + torch.ones((batch_size, seq_length, prefix_seq_len), device=device, dtype=causal_mask.dtype), + causal_mask, + ], + axis=-1, + ) + + extended_attention_mask = causal_mask[:, None, :, :] * attention_mask[:, None, None, :] + else: + extended_attention_mask = attention_mask[:, None, None, :] + else: + raise ValueError( + "Wrong shape for input_ids (shape {}) or attention_mask (shape {})".format( + input_shape, attention_mask.shape + ) + ) + + # Since attention_mask is 1.0 for positions we want to attend and 0.0 for + # masked positions, this operation will create a tensor which is 0.0 for + # positions we want to attend and -10000.0 for masked positions. + # Since we are adding it to the raw scores before the softmax, this is + # effectively the same as removing these entirely. + extended_attention_mask = extended_attention_mask.to(dtype=self.dtype) # fp16 compatibility + extended_attention_mask = (1.0 - extended_attention_mask) * -10000.0 + return extended_attention_mask + + def forward( + self, + input_ids=None, + attention_mask=None, + position_ids=None, + head_mask=None, + inputs_embeds=None, + encoder_embeds=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + is_decoder=False, + mode='multimodal', + ): + r""" + encoder_hidden_states (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): + Sequence of hidden-states at the output of the last layer of the encoder. Used in the cross-attention if + the model is configured as a decoder. + encoder_attention_mask (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Mask to avoid performing attention on the padding token indices of the encoder input. This mask is used in + the cross-attention if the model is configured as a decoder. Mask values selected in ``[0, 1]``: + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + past_key_values (:obj:`tuple(tuple(torch.FloatTensor))` of length :obj:`config.n_layers` with each tuple having 4 tensors of shape :obj:`(batch_size, num_heads, sequence_length - 1, embed_size_per_head)`): + Contains precomputed key and value hidden states of the attention blocks. Can be used to speed up decoding. + If :obj:`past_key_values` are used, the user can optionally input only the last :obj:`decoder_input_ids` + (those that don't have their past key value states given to this model) of shape :obj:`(batch_size, 1)` + instead of all :obj:`decoder_input_ids` of shape :obj:`(batch_size, sequence_length)`. + use_cache (:obj:`bool`, `optional`): + If set to :obj:`True`, :obj:`past_key_values` key value states are returned and can be used to speed up + decoding (see :obj:`past_key_values`). + """ + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions + output_hidden_states = ( + output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states + ) + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + if is_decoder: + use_cache = use_cache if use_cache is not None else self.config.use_cache + else: + use_cache = False + + if input_ids is not None and inputs_embeds is not None: + raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time") + elif input_ids is not None: + input_shape = input_ids.size() + batch_size, seq_length = input_shape + device = input_ids.device + elif inputs_embeds is not None: + input_shape = inputs_embeds.size()[:-1] + batch_size, seq_length = input_shape + device = inputs_embeds.device + elif encoder_embeds is not None: + input_shape = encoder_embeds.size()[:-1] + batch_size, seq_length = input_shape + device = encoder_embeds.device + else: + raise ValueError("You have to specify either input_ids or inputs_embeds or encoder_embeds") + + # past_key_values_length + past_key_values_length = past_key_values[0][0].shape[2] if past_key_values is not None else 0 + + if attention_mask is None: + attention_mask = torch.ones(((batch_size, seq_length + past_key_values_length)), device=device) + + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + extended_attention_mask: torch.Tensor = self.get_extended_attention_mask(attention_mask, input_shape, + device, is_decoder) + + # If a 2D or 3D attention mask is provided for the cross-attention + # we need to make broadcastable to [batch_size, num_heads, seq_length, seq_length] + if encoder_hidden_states is not None: + if type(encoder_hidden_states) == list: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states[0].size() + else: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states.size() + encoder_hidden_shape = (encoder_batch_size, encoder_sequence_length) + + if type(encoder_attention_mask) == list: + encoder_extended_attention_mask = [self.invert_attention_mask(mask) for mask in encoder_attention_mask] + elif encoder_attention_mask is None: + encoder_attention_mask = torch.ones(encoder_hidden_shape, device=device) + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = None + + # Prepare head mask if needed + # 1.0 in head_mask indicate we keep the head + # attention_probs has shape bsz x n_heads x N x N + # input head_mask has shape [num_heads] or [num_hidden_layers x num_heads] + # and head_mask is converted to shape [num_hidden_layers x batch x num_heads x seq_length x seq_length] + head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers) + + if encoder_embeds is None: + embedding_output = self.embeddings( + input_ids=input_ids, + position_ids=position_ids, + inputs_embeds=inputs_embeds, + past_key_values_length=past_key_values_length, + ) + else: + embedding_output = encoder_embeds + + encoder_outputs = self.encoder( + embedding_output, + attention_mask=extended_attention_mask, + head_mask=head_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_extended_attention_mask, + past_key_values=past_key_values, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + mode=mode, + ) + sequence_output = encoder_outputs[0] + pooled_output = self.pooler(sequence_output) if self.pooler is not None else None + + if not return_dict: + return (sequence_output, pooled_output) + encoder_outputs[1:] + + return BaseModelOutputWithPoolingAndCrossAttentions( + last_hidden_state=sequence_output, + pooler_output=pooled_output, + past_key_values=encoder_outputs.past_key_values, + hidden_states=encoder_outputs.hidden_states, + attentions=encoder_outputs.attentions, + cross_attentions=encoder_outputs.cross_attentions, + ) + diff --git a/sd/stablediffusion/src/blip/models/vit.py b/sd/stablediffusion/src/blip/models/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..cec3d8e08ed4451d65392feb2e9f4848d1ef3899 --- /dev/null +++ b/sd/stablediffusion/src/blip/models/vit.py @@ -0,0 +1,305 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li + * Based on timm code base + * https://github.com/rwightman/pytorch-image-models/tree/master/timm +''' + +import torch +import torch.nn as nn +import torch.nn.functional as F +from functools import partial + +from timm.models.vision_transformer import _cfg, PatchEmbed +from timm.models.registry import register_model +from timm.models.layers import trunc_normal_, DropPath +from timm.models.helpers import named_apply, adapt_input_conv + +from fairscale.nn.checkpoint.checkpoint_activations import checkpoint_wrapper + +class Mlp(nn.Module): + """ MLP as used in Vision Transformer, MLP-Mixer and related networks + """ + def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +class Attention(nn.Module): + def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.): + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + # NOTE scale factor was wrong in my original version, can set manually to be compat with prev weights + self.scale = qk_scale or head_dim ** -0.5 + self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + self.attn_gradients = None + self.attention_map = None + + def save_attn_gradients(self, attn_gradients): + self.attn_gradients = attn_gradients + + def get_attn_gradients(self): + return self.attn_gradients + + def save_attention_map(self, attention_map): + self.attention_map = attention_map + + def get_attention_map(self): + return self.attention_map + + def forward(self, x, register_hook=False): + B, N, C = x.shape + qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4) + q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) + + attn = (q @ k.transpose(-2, -1)) * self.scale + attn = attn.softmax(dim=-1) + attn = self.attn_drop(attn) + + if register_hook: + self.save_attention_map(attn) + attn.register_hook(self.save_attn_gradients) + + x = (attn @ v).transpose(1, 2).reshape(B, N, C) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Block(nn.Module): + + def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop=0., attn_drop=0., + drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm, use_grad_checkpointing=False): + super().__init__() + self.norm1 = norm_layer(dim) + self.attn = Attention( + dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop) + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) + + if use_grad_checkpointing: + self.attn = checkpoint_wrapper(self.attn) + self.mlp = checkpoint_wrapper(self.mlp) + + def forward(self, x, register_hook=False): + x = x + self.drop_path(self.attn(self.norm1(x), register_hook=register_hook)) + x = x + self.drop_path(self.mlp(self.norm2(x))) + return x + + +class VisionTransformer(nn.Module): + """ Vision Transformer + A PyTorch impl of : `An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale` - + https://arxiv.org/abs/2010.11929 + """ + def __init__(self, img_size=224, patch_size=16, in_chans=3, num_classes=1000, embed_dim=768, depth=12, + num_heads=12, mlp_ratio=4., qkv_bias=True, qk_scale=None, representation_size=None, + drop_rate=0., attn_drop_rate=0., drop_path_rate=0., norm_layer=None, + use_grad_checkpointing=False, ckpt_layer=0): + """ + Args: + img_size (int, tuple): input image size + patch_size (int, tuple): patch size + in_chans (int): number of input channels + num_classes (int): number of classes for classification head + embed_dim (int): embedding dimension + depth (int): depth of transformer + num_heads (int): number of attention heads + mlp_ratio (int): ratio of mlp hidden dim to embedding dim + qkv_bias (bool): enable bias for qkv if True + qk_scale (float): override default qk scale of head_dim ** -0.5 if set + representation_size (Optional[int]): enable and set representation layer (pre-logits) to this value if set + drop_rate (float): dropout rate + attn_drop_rate (float): attention dropout rate + drop_path_rate (float): stochastic depth rate + norm_layer: (nn.Module): normalization layer + """ + super().__init__() + self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models + norm_layer = norm_layer or partial(nn.LayerNorm, eps=1e-6) + + self.patch_embed = PatchEmbed( + img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim) + + num_patches = self.patch_embed.num_patches + + self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) + self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim)) + self.pos_drop = nn.Dropout(p=drop_rate) + + dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule + self.blocks = nn.ModuleList([ + Block( + dim=embed_dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i], norm_layer=norm_layer, + use_grad_checkpointing=(use_grad_checkpointing and i>=depth-ckpt_layer) + ) + for i in range(depth)]) + self.norm = norm_layer(embed_dim) + + trunc_normal_(self.pos_embed, std=.02) + trunc_normal_(self.cls_token, std=.02) + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight, std=.02) + if isinstance(m, nn.Linear) and m.bias is not None: + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.LayerNorm): + nn.init.constant_(m.bias, 0) + nn.init.constant_(m.weight, 1.0) + + @torch.jit.ignore + def no_weight_decay(self): + return {'pos_embed', 'cls_token'} + + def forward(self, x, register_blk=-1): + B = x.shape[0] + x = self.patch_embed(x) + + cls_tokens = self.cls_token.expand(B, -1, -1) # stole cls_tokens impl from Phil Wang, thanks + x = torch.cat((cls_tokens, x), dim=1) + + x = x + self.pos_embed[:,:x.size(1),:] + x = self.pos_drop(x) + + for i,blk in enumerate(self.blocks): + x = blk(x, register_blk==i) + x = self.norm(x) + + return x + + @torch.jit.ignore() + def load_pretrained(self, checkpoint_path, prefix=''): + _load_weights(self, checkpoint_path, prefix) + + +@torch.no_grad() +def _load_weights(model: VisionTransformer, checkpoint_path: str, prefix: str = ''): + """ Load weights from .npz checkpoints for official Google Brain Flax implementation + """ + import numpy as np + + def _n2p(w, t=True): + if w.ndim == 4 and w.shape[0] == w.shape[1] == w.shape[2] == 1: + w = w.flatten() + if t: + if w.ndim == 4: + w = w.transpose([3, 2, 0, 1]) + elif w.ndim == 3: + w = w.transpose([2, 0, 1]) + elif w.ndim == 2: + w = w.transpose([1, 0]) + return torch.from_numpy(w) + + w = np.load(checkpoint_path) + if not prefix and 'opt/target/embedding/kernel' in w: + prefix = 'opt/target/' + + if hasattr(model.patch_embed, 'backbone'): + # hybrid + backbone = model.patch_embed.backbone + stem_only = not hasattr(backbone, 'stem') + stem = backbone if stem_only else backbone.stem + stem.conv.weight.copy_(adapt_input_conv(stem.conv.weight.shape[1], _n2p(w[f'{prefix}conv_root/kernel']))) + stem.norm.weight.copy_(_n2p(w[f'{prefix}gn_root/scale'])) + stem.norm.bias.copy_(_n2p(w[f'{prefix}gn_root/bias'])) + if not stem_only: + for i, stage in enumerate(backbone.stages): + for j, block in enumerate(stage.blocks): + bp = f'{prefix}block{i + 1}/unit{j + 1}/' + for r in range(3): + getattr(block, f'conv{r + 1}').weight.copy_(_n2p(w[f'{bp}conv{r + 1}/kernel'])) + getattr(block, f'norm{r + 1}').weight.copy_(_n2p(w[f'{bp}gn{r + 1}/scale'])) + getattr(block, f'norm{r + 1}').bias.copy_(_n2p(w[f'{bp}gn{r + 1}/bias'])) + if block.downsample is not None: + block.downsample.conv.weight.copy_(_n2p(w[f'{bp}conv_proj/kernel'])) + block.downsample.norm.weight.copy_(_n2p(w[f'{bp}gn_proj/scale'])) + block.downsample.norm.bias.copy_(_n2p(w[f'{bp}gn_proj/bias'])) + embed_conv_w = _n2p(w[f'{prefix}embedding/kernel']) + else: + embed_conv_w = adapt_input_conv( + model.patch_embed.proj.weight.shape[1], _n2p(w[f'{prefix}embedding/kernel'])) + model.patch_embed.proj.weight.copy_(embed_conv_w) + model.patch_embed.proj.bias.copy_(_n2p(w[f'{prefix}embedding/bias'])) + model.cls_token.copy_(_n2p(w[f'{prefix}cls'], t=False)) + pos_embed_w = _n2p(w[f'{prefix}Transformer/posembed_input/pos_embedding'], t=False) + if pos_embed_w.shape != model.pos_embed.shape: + pos_embed_w = resize_pos_embed( # resize pos embedding when different size from pretrained weights + pos_embed_w, model.pos_embed, getattr(model, 'num_tokens', 1), model.patch_embed.grid_size) + model.pos_embed.copy_(pos_embed_w) + model.norm.weight.copy_(_n2p(w[f'{prefix}Transformer/encoder_norm/scale'])) + model.norm.bias.copy_(_n2p(w[f'{prefix}Transformer/encoder_norm/bias'])) +# if isinstance(model.head, nn.Linear) and model.head.bias.shape[0] == w[f'{prefix}head/bias'].shape[-1]: +# model.head.weight.copy_(_n2p(w[f'{prefix}head/kernel'])) +# model.head.bias.copy_(_n2p(w[f'{prefix}head/bias'])) +# if isinstance(getattr(model.pre_logits, 'fc', None), nn.Linear) and f'{prefix}pre_logits/bias' in w: +# model.pre_logits.fc.weight.copy_(_n2p(w[f'{prefix}pre_logits/kernel'])) +# model.pre_logits.fc.bias.copy_(_n2p(w[f'{prefix}pre_logits/bias'])) + for i, block in enumerate(model.blocks.children()): + block_prefix = f'{prefix}Transformer/encoderblock_{i}/' + mha_prefix = block_prefix + 'MultiHeadDotProductAttention_1/' + block.norm1.weight.copy_(_n2p(w[f'{block_prefix}LayerNorm_0/scale'])) + block.norm1.bias.copy_(_n2p(w[f'{block_prefix}LayerNorm_0/bias'])) + block.attn.qkv.weight.copy_(torch.cat([ + _n2p(w[f'{mha_prefix}{n}/kernel'], t=False).flatten(1).T for n in ('query', 'key', 'value')])) + block.attn.qkv.bias.copy_(torch.cat([ + _n2p(w[f'{mha_prefix}{n}/bias'], t=False).reshape(-1) for n in ('query', 'key', 'value')])) + block.attn.proj.weight.copy_(_n2p(w[f'{mha_prefix}out/kernel']).flatten(1)) + block.attn.proj.bias.copy_(_n2p(w[f'{mha_prefix}out/bias'])) + for r in range(2): + getattr(block.mlp, f'fc{r + 1}').weight.copy_(_n2p(w[f'{block_prefix}MlpBlock_3/Dense_{r}/kernel'])) + getattr(block.mlp, f'fc{r + 1}').bias.copy_(_n2p(w[f'{block_prefix}MlpBlock_3/Dense_{r}/bias'])) + block.norm2.weight.copy_(_n2p(w[f'{block_prefix}LayerNorm_2/scale'])) + block.norm2.bias.copy_(_n2p(w[f'{block_prefix}LayerNorm_2/bias'])) + + +def interpolate_pos_embed(pos_embed_checkpoint, visual_encoder): + # interpolate position embedding + embedding_size = pos_embed_checkpoint.shape[-1] + num_patches = visual_encoder.patch_embed.num_patches + num_extra_tokens = visual_encoder.pos_embed.shape[-2] - num_patches + # height (== width) for the checkpoint position embedding + orig_size = int((pos_embed_checkpoint.shape[-2] - num_extra_tokens) ** 0.5) + # height (== width) for the new position embedding + new_size = int(num_patches ** 0.5) + + if orig_size!=new_size: + # class_token and dist_token are kept unchanged + extra_tokens = pos_embed_checkpoint[:, :num_extra_tokens] + # only the position tokens are interpolated + pos_tokens = pos_embed_checkpoint[:, num_extra_tokens:] + pos_tokens = pos_tokens.reshape(-1, orig_size, orig_size, embedding_size).permute(0, 3, 1, 2) + pos_tokens = torch.nn.functional.interpolate( + pos_tokens, size=(new_size, new_size), mode='bicubic', align_corners=False) + pos_tokens = pos_tokens.permute(0, 2, 3, 1).flatten(1, 2) + new_pos_embed = torch.cat((extra_tokens, pos_tokens), dim=1) + print('reshape position embedding from %d to %d'%(orig_size ** 2,new_size ** 2)) + + return new_pos_embed + else: + return pos_embed_checkpoint \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/predict.py b/sd/stablediffusion/src/blip/predict.py new file mode 100644 index 0000000000000000000000000000000000000000..35426cadcbb3bf8c3d8cb9c910511c154e451f4e --- /dev/null +++ b/sd/stablediffusion/src/blip/predict.py @@ -0,0 +1,98 @@ +""" +Download the weights in ./checkpoints beforehand for fast inference +wget https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_base_caption.pth +wget https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_vqa.pth +wget https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_coco.pth +""" + +from pathlib import Path + +from PIL import Image +import torch +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode +import cog + +from models.blip import blip_decoder +from models.blip_vqa import blip_vqa +from models.blip_itm import blip_itm + + +class Predictor(cog.Predictor): + def setup(self): + self.device = "cuda:0" + + self.models = { + 'image_captioning': blip_decoder(pretrained='checkpoints/model*_base_caption.pth', + image_size=384, vit='base'), + 'visual_question_answering': blip_vqa(pretrained='checkpoints/model*_vqa.pth', + image_size=480, vit='base'), + 'image_text_matching': blip_itm(pretrained='checkpoints/model_base_retrieval_coco.pth', + image_size=384, vit='base') + } + + @cog.input( + "image", + type=Path, + help="input image", + ) + @cog.input( + "task", + type=str, + default='image_captioning', + options=['image_captioning', 'visual_question_answering', 'image_text_matching'], + help="Choose a task.", + ) + @cog.input( + "question", + type=str, + default=None, + help="Type question for the input image for visual question answering task.", + ) + @cog.input( + "caption", + type=str, + default=None, + help="Type caption for the input image for image text matching task.", + ) + def predict(self, image, task, question, caption): + if task == 'visual_question_answering': + assert question is not None, 'Please type a question for visual question answering task.' + if task == 'image_text_matching': + assert caption is not None, 'Please type a caption for mage text matching task.' + + im = load_image(image, image_size=480 if task == 'visual_question_answering' else 384, device=self.device) + model = self.models[task] + model.eval() + model = model.to(self.device) + + if task == 'image_captioning': + with torch.no_grad(): + caption = model.generate(im, sample=False, num_beams=3, max_length=20, min_length=5) + return 'Caption: ' + caption[0] + + if task == 'visual_question_answering': + with torch.no_grad(): + answer = model(im, question, train=False, inference='generate') + return 'Answer: ' + answer[0] + + # image_text_matching + itm_output = model(im, caption, match_head='itm') + itm_score = torch.nn.functional.softmax(itm_output, dim=1)[:, 1] + itc_score = model(im, caption, match_head='itc') + return f'The image and text is matched with a probability of {itm_score.item():.4f}.\n' \ + f'The image feature and text feature has a cosine similarity of {itc_score.item():.4f}.' + + +def load_image(image, image_size, device): + raw_image = Image.open(str(image)).convert('RGB') + + w, h = raw_image.size + + transform = transforms.Compose([ + transforms.Resize((image_size, image_size), interpolation=InterpolationMode.BICUBIC), + transforms.ToTensor(), + transforms.Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711)) + ]) + image = transform(raw_image).unsqueeze(0).to(device) + return image diff --git a/sd/stablediffusion/src/blip/pretrain.py b/sd/stablediffusion/src/blip/pretrain.py new file mode 100644 index 0000000000000000000000000000000000000000..c9490ec8eb8ff5f074b5772ada55cd27ec673a12 --- /dev/null +++ b/sd/stablediffusion/src/blip/pretrain.py @@ -0,0 +1,173 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip_pretrain import blip_pretrain +import utils +from utils import warmup_lr_schedule, step_lr_schedule +from data import create_dataset, create_sampler, create_loader + +def train(model, data_loader, optimizer, epoch, device, config): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=50, fmt='{value:.6f}')) + metric_logger.add_meter('loss_ita', utils.SmoothedValue(window_size=50, fmt='{value:.4f}')) + metric_logger.add_meter('loss_itm', utils.SmoothedValue(window_size=50, fmt='{value:.4f}')) + metric_logger.add_meter('loss_lm', utils.SmoothedValue(window_size=50, fmt='{value:.4f}')) + + header = 'Train Epoch: [{}]'.format(epoch) + print_freq = 50 + + if config['laion_path']: + data_loader.dataset.reload_laion(epoch) + + data_loader.sampler.set_epoch(epoch) + + for i, (image, caption) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + + if epoch==0: + warmup_lr_schedule(optimizer, i, config['warmup_steps'], config['warmup_lr'], config['init_lr']) + + optimizer.zero_grad() + + image = image.to(device,non_blocking=True) + + # ramp up alpha in the first 2 epochs + alpha = config['alpha']*min(1,(epoch*len(data_loader)+i)/(2*len(data_loader))) + + loss_ita, loss_itm, loss_lm = model(image, caption, alpha = alpha) + loss = loss_ita + loss_itm + loss_lm + + loss.backward() + optimizer.step() + + metric_logger.update(loss_ita=loss_ita.item()) + metric_logger.update(loss_itm=loss_itm.item()) + metric_logger.update(loss_lm=loss_lm.item()) + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.3f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating dataset") + datasets = [create_dataset('pretrain', config, min_scale=0.2)] + print('number of training samples: %d'%len(datasets[0])) + + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler(datasets, [True], num_tasks, global_rank) + + data_loader = create_loader(datasets,samplers,batch_size=[config['batch_size']], num_workers=[4], is_trains=[True], collate_fns=[None])[0] + + #### Model #### + print("Creating model") + model = blip_pretrain(image_size=config['image_size'], vit=config['vit'], vit_grad_ckpt=config['vit_grad_ckpt'], + vit_ckpt_layer=config['vit_ckpt_layer'], queue_size=config['queue_size']) + + model = model.to(device) + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + start_epoch = 0 + if args.checkpoint: + checkpoint = torch.load(args.checkpoint, map_location='cpu') + state_dict = checkpoint['model'] + model.load_state_dict(state_dict) + + optimizer.load_state_dict(checkpoint['optimizer']) + start_epoch = checkpoint['epoch']+1 + print('resume checkpoint from %s'%args.checkpoint) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + print("Start training") + start_time = time.time() + for epoch in range(start_epoch, config['max_epoch']): + + step_lr_schedule(optimizer, epoch, config['init_lr'], config['min_lr'], config['lr_decay_rate']) + + train_stats = train(model, data_loader, optimizer, epoch, device, config) + if utils.is_main_process(): + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + 'epoch': epoch, + } + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_%02d.pth'%epoch)) + + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + dist.barrier() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/pretrain.yaml') + parser.add_argument('--output_dir', default='output/Pretrain') + parser.add_argument('--checkpoint', default='') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/requirements.txt b/sd/stablediffusion/src/blip/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..d897bc6a08712f4beb2f78ca2592dcbe06a3e2db --- /dev/null +++ b/sd/stablediffusion/src/blip/requirements.txt @@ -0,0 +1,4 @@ +timm==0.4.12 +transformers==4.15.0 +fairscale==0.4.4 +pycocoevalcap diff --git a/sd/stablediffusion/src/blip/train_caption.py b/sd/stablediffusion/src/blip/train_caption.py new file mode 100644 index 0000000000000000000000000000000000000000..7c639ac646b9a1b8074b6e9c2343b961de76db05 --- /dev/null +++ b/sd/stablediffusion/src/blip/train_caption.py @@ -0,0 +1,206 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip import blip_decoder +import utils +from utils import cosine_lr_schedule +from data import create_dataset, create_sampler, create_loader +from data.utils import save_result, coco_caption_eval + +def train(model, data_loader, optimizer, epoch, device): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value:.6f}')) + metric_logger.add_meter('loss', utils.SmoothedValue(window_size=1, fmt='{value:.4f}')) + header = 'Train Caption Epoch: [{}]'.format(epoch) + print_freq = 50 + + for i, (image, caption, _) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + image = image.to(device) + + loss = model(image, caption) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metric_logger.update(loss=loss.item()) + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.3f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +@torch.no_grad() +def evaluate(model, data_loader, device, config): + # evaluate + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Caption generation:' + print_freq = 10 + + result = [] + for image, image_id in metric_logger.log_every(data_loader, print_freq, header): + + image = image.to(device) + + captions = model.generate(image, sample=False, num_beams=config['num_beams'], max_length=config['max_length'], + min_length=config['min_length']) + + for caption, img_id in zip(captions, image_id): + result.append({"image_id": img_id.item(), "caption": caption}) + + return result + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating captioning dataset") + train_dataset, val_dataset, test_dataset = create_dataset('caption_coco', config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler([train_dataset,val_dataset,test_dataset], [True,False,False], num_tasks, global_rank) + else: + samplers = [None, None, None] + + train_loader, val_loader, test_loader = create_loader([train_dataset, val_dataset, test_dataset],samplers, + batch_size=[config['batch_size']]*3,num_workers=[4,4,4], + is_trains=[True, False, False], collate_fns=[None,None,None]) + + #### Model #### + print("Creating model") + model = blip_decoder(pretrained=config['pretrained'], image_size=config['image_size'], vit=config['vit'], + vit_grad_ckpt=config['vit_grad_ckpt'], vit_ckpt_layer=config['vit_ckpt_layer'], + prompt=config['prompt']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + best = 0 + best_epoch = 0 + + print("Start training") + start_time = time.time() + for epoch in range(0, config['max_epoch']): + if not args.evaluate: + if args.distributed: + train_loader.sampler.set_epoch(epoch) + + cosine_lr_schedule(optimizer, epoch, config['max_epoch'], config['init_lr'], config['min_lr']) + + train_stats = train(model, train_loader, optimizer, epoch, device) + + val_result = evaluate(model_without_ddp, val_loader, device, config) + val_result_file = save_result(val_result, args.result_dir, 'val_epoch%d'%epoch, remove_duplicate='image_id') + + test_result = evaluate(model_without_ddp, test_loader, device, config) + test_result_file = save_result(test_result, args.result_dir, 'test_epoch%d'%epoch, remove_duplicate='image_id') + + if utils.is_main_process(): + coco_val = coco_caption_eval(config['coco_gt_root'],val_result_file,'val') + coco_test = coco_caption_eval(config['coco_gt_root'],test_result_file,'test') + + if args.evaluate: + log_stats = {**{f'val_{k}': v for k, v in coco_val.eval.items()}, + **{f'test_{k}': v for k, v in coco_test.eval.items()}, + } + with open(os.path.join(args.output_dir, "evaluate.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + else: + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + + if coco_val.eval['CIDEr'] + coco_val.eval['Bleu_4'] > best: + best = coco_val.eval['CIDEr'] + coco_val.eval['Bleu_4'] + best_epoch = epoch + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_best.pth')) + + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + **{f'val_{k}': v for k, v in coco_val.eval.items()}, + **{f'test_{k}': v for k, v in coco_test.eval.items()}, + 'epoch': epoch, + 'best_epoch': best_epoch, + } + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + if args.evaluate: + break + dist.barrier() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/caption_coco.yaml') + parser.add_argument('--output_dir', default='output/Caption_coco') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + args.result_dir = os.path.join(args.output_dir, 'result') + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + Path(args.result_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/train_nlvr.py b/sd/stablediffusion/src/blip/train_nlvr.py new file mode 100644 index 0000000000000000000000000000000000000000..84b247bda2334c1fd894b6c11d33ef48c8e7df28 --- /dev/null +++ b/sd/stablediffusion/src/blip/train_nlvr.py @@ -0,0 +1,213 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path +import json +import pickle + +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.utils.data import DataLoader +import torch.backends.cudnn as cudnn +import torch.distributed as dist + +from models.blip_nlvr import blip_nlvr + +import utils +from utils import cosine_lr_schedule, warmup_lr_schedule +from data import create_dataset, create_sampler, create_loader + +def train(model, data_loader, optimizer, epoch, device, config): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=50, fmt='{value:.6f}')) + metric_logger.add_meter('loss', utils.SmoothedValue(window_size=50, fmt='{value:.4f}')) + + header = 'Train Epoch: [{}]'.format(epoch) + print_freq = 50 + step_size = 10 + + for i,(image0, image1, text, targets) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + + images = torch.cat([image0, image1], dim=0) + images, targets = images.to(device), targets.to(device) + + loss = model(images, text, targets=targets, train=True) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + metric_logger.update(loss=loss.item()) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.4f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +@torch.no_grad() +def evaluate(model, data_loader, device, config): + # test + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + + header = 'Evaluation:' + print_freq = 50 + + for image0, image1, text, targets in metric_logger.log_every(data_loader, print_freq, header): + images = torch.cat([image0, image1], dim=0) + images, targets = images.to(device), targets.to(device) + + prediction = model(images, text, targets=targets, train=False) + + _, pred_class = prediction.max(1) + accuracy = (targets==pred_class).sum() / targets.size(0) + + metric_logger.meters['acc'].update(accuracy.item(), n=image0.size(0)) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.4f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating dataset") + datasets = create_dataset('nlvr', config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler(datasets, [True,False,False], num_tasks, global_rank) + else: + samplers = [None, None, None] + + batch_size=[config['batch_size_train'],config['batch_size_test'],config['batch_size_test']] + train_loader, val_loader, test_loader = create_loader(datasets,samplers,batch_size=batch_size, + num_workers=[4,4,4],is_trains=[True,False,False], + collate_fns=[None,None,None]) + + #### Model #### + print("Creating model") + model = blip_nlvr(pretrained=config['pretrained'], image_size=config['image_size'], + vit=config['vit'], vit_grad_ckpt=config['vit_grad_ckpt'], vit_ckpt_layer=config['vit_ckpt_layer']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + print("Start training") + start_time = time.time() + best = 0 + best_epoch = 0 + + for epoch in range(0, config['max_epoch']): + if not args.evaluate: + if args.distributed: + train_loader.sampler.set_epoch(epoch) + + cosine_lr_schedule(optimizer, epoch, config['max_epoch'], config['init_lr'], config['min_lr']) + + train_stats = train(model, train_loader, optimizer, epoch, device, config) + + val_stats = evaluate(model, val_loader, device, config) + test_stats = evaluate(model, test_loader, device, config) + + if utils.is_main_process(): + if args.evaluate: + log_stats = {**{f'val_{k}': v for k, v in val_stats.items()}, + **{f'test_{k}': v for k, v in test_stats.items()}, + } + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + else: + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + **{f'val_{k}': v for k, v in val_stats.items()}, + **{f'test_{k}': v for k, v in test_stats.items()}, + 'epoch': epoch, + } + + if float(val_stats['acc'])>best: + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_best.pth')) + best = float(val_stats['acc']) + best_epoch = epoch + + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + if args.evaluate: + break + + dist.barrier() + + if utils.is_main_process(): + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write("best epoch: %d"%best_epoch) + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/nlvr.yaml') + parser.add_argument('--output_dir', default='output/NLVR') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/train_retrieval.py b/sd/stablediffusion/src/blip/train_retrieval.py new file mode 100644 index 0000000000000000000000000000000000000000..574f03382cc8197b97971a11ae54b632bcfe6655 --- /dev/null +++ b/sd/stablediffusion/src/blip/train_retrieval.py @@ -0,0 +1,345 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip_retrieval import blip_retrieval +import utils +from utils import cosine_lr_schedule +from data import create_dataset, create_sampler, create_loader + + +def train(model, data_loader, optimizer, epoch, device, config): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value:.6f}')) + metric_logger.add_meter('loss_itm', utils.SmoothedValue(window_size=1, fmt='{value:.4f}')) + metric_logger.add_meter('loss_ita', utils.SmoothedValue(window_size=1, fmt='{value:.4f}')) + header = 'Train Epoch: [{}]'.format(epoch) + print_freq = 50 + + for i,(image, caption, idx) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + image = image.to(device,non_blocking=True) + idx = idx.to(device,non_blocking=True) + + if epoch>0: + alpha = config['alpha'] + else: + alpha = config['alpha']*min(1,i/len(data_loader)) + + loss_ita, loss_itm = model(image, caption, alpha=alpha, idx=idx) + loss = loss_ita + loss_itm + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metric_logger.update(loss_itm=loss_itm.item()) + metric_logger.update(loss_ita=loss_ita.item()) + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.3f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +@torch.no_grad() +def evaluation(model, data_loader, device, config): + # test + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Evaluation:' + + print('Computing features for evaluation...') + start_time = time.time() + + texts = data_loader.dataset.text + num_text = len(texts) + text_bs = 256 + text_ids = [] + text_embeds = [] + text_atts = [] + for i in range(0, num_text, text_bs): + text = texts[i: min(num_text, i+text_bs)] + text_input = model.tokenizer(text, padding='max_length', truncation=True, max_length=35, return_tensors="pt").to(device) + text_output = model.text_encoder(text_input.input_ids, attention_mask = text_input.attention_mask, mode='text') + text_embed = F.normalize(model.text_proj(text_output.last_hidden_state[:,0,:])) + text_embeds.append(text_embed) + text_ids.append(text_input.input_ids) + text_atts.append(text_input.attention_mask) + + text_embeds = torch.cat(text_embeds,dim=0) + text_ids = torch.cat(text_ids,dim=0) + text_atts = torch.cat(text_atts,dim=0) + text_ids[:,0] = model.tokenizer.enc_token_id + + image_feats = [] + image_embeds = [] + for image, img_id in data_loader: + image = image.to(device) + image_feat = model.visual_encoder(image) + image_embed = model.vision_proj(image_feat[:,0,:]) + image_embed = F.normalize(image_embed,dim=-1) + + image_feats.append(image_feat.cpu()) + image_embeds.append(image_embed) + + image_feats = torch.cat(image_feats,dim=0) + image_embeds = torch.cat(image_embeds,dim=0) + + sims_matrix = image_embeds @ text_embeds.t() + score_matrix_i2t = torch.full((len(data_loader.dataset.image),len(texts)),-100.0).to(device) + + num_tasks = utils.get_world_size() + rank = utils.get_rank() + step = sims_matrix.size(0)//num_tasks + 1 + start = rank*step + end = min(sims_matrix.size(0),start+step) + + for i,sims in enumerate(metric_logger.log_every(sims_matrix[start:end], 50, header)): + topk_sim, topk_idx = sims.topk(k=config['k_test'], dim=0) + + encoder_output = image_feats[start+i].repeat(config['k_test'],1,1).to(device) + encoder_att = torch.ones(encoder_output.size()[:-1],dtype=torch.long).to(device) + output = model.text_encoder(text_ids[topk_idx], + attention_mask = text_atts[topk_idx], + encoder_hidden_states = encoder_output, + encoder_attention_mask = encoder_att, + return_dict = True, + ) + score = model.itm_head(output.last_hidden_state[:,0,:])[:,1] + score_matrix_i2t[start+i,topk_idx] = score + topk_sim + + sims_matrix = sims_matrix.t() + score_matrix_t2i = torch.full((len(texts),len(data_loader.dataset.image)),-100.0).to(device) + + step = sims_matrix.size(0)//num_tasks + 1 + start = rank*step + end = min(sims_matrix.size(0),start+step) + + for i,sims in enumerate(metric_logger.log_every(sims_matrix[start:end], 50, header)): + + topk_sim, topk_idx = sims.topk(k=config['k_test'], dim=0) + encoder_output = image_feats[topk_idx].to(device) + encoder_att = torch.ones(encoder_output.size()[:-1],dtype=torch.long).to(device) + output = model.text_encoder(text_ids[start+i].repeat(config['k_test'],1), + attention_mask = text_atts[start+i].repeat(config['k_test'],1), + encoder_hidden_states = encoder_output, + encoder_attention_mask = encoder_att, + return_dict = True, + ) + score = model.itm_head(output.last_hidden_state[:,0,:])[:,1] + score_matrix_t2i[start+i,topk_idx] = score + topk_sim + + if args.distributed: + dist.barrier() + torch.distributed.all_reduce(score_matrix_i2t, op=torch.distributed.ReduceOp.SUM) + torch.distributed.all_reduce(score_matrix_t2i, op=torch.distributed.ReduceOp.SUM) + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Evaluation time {}'.format(total_time_str)) + + return score_matrix_i2t.cpu().numpy(), score_matrix_t2i.cpu().numpy() + + + +@torch.no_grad() +def itm_eval(scores_i2t, scores_t2i, txt2img, img2txt): + + #Images->Text + ranks = np.zeros(scores_i2t.shape[0]) + for index,score in enumerate(scores_i2t): + inds = np.argsort(score)[::-1] + # Score + rank = 1e20 + for i in img2txt[index]: + tmp = np.where(inds == i)[0][0] + if tmp < rank: + rank = tmp + ranks[index] = rank + + # Compute metrics + tr1 = 100.0 * len(np.where(ranks < 1)[0]) / len(ranks) + tr5 = 100.0 * len(np.where(ranks < 5)[0]) / len(ranks) + tr10 = 100.0 * len(np.where(ranks < 10)[0]) / len(ranks) + + #Text->Images + ranks = np.zeros(scores_t2i.shape[0]) + + for index,score in enumerate(scores_t2i): + inds = np.argsort(score)[::-1] + ranks[index] = np.where(inds == txt2img[index])[0][0] + + # Compute metrics + ir1 = 100.0 * len(np.where(ranks < 1)[0]) / len(ranks) + ir5 = 100.0 * len(np.where(ranks < 5)[0]) / len(ranks) + ir10 = 100.0 * len(np.where(ranks < 10)[0]) / len(ranks) + + tr_mean = (tr1 + tr5 + tr10) / 3 + ir_mean = (ir1 + ir5 + ir10) / 3 + r_mean = (tr_mean + ir_mean) / 2 + + eval_result = {'txt_r1': tr1, + 'txt_r5': tr5, + 'txt_r10': tr10, + 'txt_r_mean': tr_mean, + 'img_r1': ir1, + 'img_r5': ir5, + 'img_r10': ir10, + 'img_r_mean': ir_mean, + 'r_mean': r_mean} + return eval_result + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating retrieval dataset") + train_dataset, val_dataset, test_dataset = create_dataset('retrieval_%s'%config['dataset'], config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler([train_dataset], [True], num_tasks, global_rank) + [None, None] + else: + samplers = [None, None, None] + + train_loader, val_loader, test_loader = create_loader([train_dataset, val_dataset, test_dataset],samplers, + batch_size=[config['batch_size_train']]+[config['batch_size_test']]*2, + num_workers=[4,4,4], + is_trains=[True, False, False], + collate_fns=[None,None,None]) + + + #### Model #### + print("Creating model") + model = blip_retrieval(pretrained=config['pretrained'], image_size=config['image_size'], vit=config['vit'], + vit_grad_ckpt=config['vit_grad_ckpt'], vit_ckpt_layer=config['vit_ckpt_layer'], + queue_size=config['queue_size'], negative_all_rank=config['negative_all_rank']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + best = 0 + best_epoch = 0 + + print("Start training") + start_time = time.time() + + for epoch in range(0, config['max_epoch']): + if not args.evaluate: + if args.distributed: + train_loader.sampler.set_epoch(epoch) + + cosine_lr_schedule(optimizer, epoch, config['max_epoch'], config['init_lr'], config['min_lr']) + + train_stats = train(model, train_loader, optimizer, epoch, device, config) + + score_val_i2t, score_val_t2i, = evaluation(model_without_ddp, val_loader, device, config) + score_test_i2t, score_test_t2i = evaluation(model_without_ddp, test_loader, device, config) + + if utils.is_main_process(): + + val_result = itm_eval(score_val_i2t, score_val_t2i, val_loader.dataset.txt2img, val_loader.dataset.img2txt) + print(val_result) + + if val_result['r_mean']>best: + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_best.pth')) + best = val_result['r_mean'] + best_epoch = epoch + + test_result = itm_eval(score_test_i2t, score_test_t2i, test_loader.dataset.txt2img, test_loader.dataset.img2txt) + print(test_result) + + if args.evaluate: + log_stats = {**{f'val_{k}': v for k, v in val_result.items()}, + **{f'test_{k}': v for k, v in test_result.items()}, + } + with open(os.path.join(args.output_dir, "evaluate.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + else: + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + **{f'val_{k}': v for k, v in val_result.items()}, + **{f'test_{k}': v for k, v in test_result.items()}, + 'epoch': epoch, + 'best_epoch': best_epoch, + } + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + if args.evaluate: + break + + dist.barrier() + torch.cuda.empty_cache() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/retrieval_flickr.yaml') + parser.add_argument('--output_dir', default='output/Retrieval_flickr') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/train_vqa.py b/sd/stablediffusion/src/blip/train_vqa.py new file mode 100644 index 0000000000000000000000000000000000000000..89eb7490862e517cc660f842396033c21d441a20 --- /dev/null +++ b/sd/stablediffusion/src/blip/train_vqa.py @@ -0,0 +1,202 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.utils.data import DataLoader +import torch.backends.cudnn as cudnn +import torch.distributed as dist + +from models.blip_vqa import blip_vqa +import utils +from utils import cosine_lr_schedule +from data import create_dataset, create_sampler, create_loader +from data.vqa_dataset import vqa_collate_fn +from data.utils import save_result + + +def train(model, data_loader, optimizer, epoch, device): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value:.6f}')) + metric_logger.add_meter('loss', utils.SmoothedValue(window_size=1, fmt='{value:.4f}')) + + header = 'Train Epoch: [{}]'.format(epoch) + print_freq = 50 + + for i,(image, question, answer, weights, n) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + image, weights = image.to(device,non_blocking=True), weights.to(device,non_blocking=True) + + loss = model(image, question, answer, train=True, n=n, weights=weights) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metric_logger.update(loss=loss.item()) + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.3f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +@torch.no_grad() +def evaluation(model, data_loader, device, config) : + # test + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Generate VQA test result:' + print_freq = 50 + + result = [] + + if config['inference']=='rank': + answer_list = data_loader.dataset.answer_list + answer_candidates = model.tokenizer(answer_list, padding='longest', return_tensors='pt').to(device) + answer_candidates.input_ids[:,0] = model.tokenizer.bos_token_id + + for n, (image, question, question_id) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + image = image.to(device,non_blocking=True) + + if config['inference']=='generate': + answers = model(image, question, train=False, inference='generate') + + for answer, ques_id in zip(answers, question_id): + ques_id = int(ques_id.item()) + result.append({"question_id":ques_id, "answer":answer}) + + elif config['inference']=='rank': + answer_ids = model(image, question, answer_candidates, train=False, inference='rank', k_test=config['k_test']) + + for ques_id, answer_id in zip(question_id, answer_ids): + result.append({"question_id":int(ques_id.item()), "answer":answer_list[answer_id]}) + + return result + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating vqa datasets") + datasets = create_dataset('vqa', config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler(datasets, [True, False], num_tasks, global_rank) + else: + samplers = [None, None] + + train_loader, test_loader = create_loader(datasets,samplers, + batch_size=[config['batch_size_train'],config['batch_size_test']], + num_workers=[4,4],is_trains=[True, False], + collate_fns=[vqa_collate_fn,None]) + #### Model #### + print("Creating model") + model = blip_vqa(pretrained=config['pretrained'], image_size=config['image_size'], + vit=config['vit'], vit_grad_ckpt=config['vit_grad_ckpt'], vit_ckpt_layer=config['vit_ckpt_layer']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + best = 0 + best_epoch = 0 + + print("Start training") + start_time = time.time() + for epoch in range(0, config['max_epoch']): + if not args.evaluate: + if args.distributed: + train_loader.sampler.set_epoch(epoch) + + cosine_lr_schedule(optimizer, epoch, config['max_epoch'], config['init_lr'], config['min_lr']) + + train_stats = train(model, train_loader, optimizer, epoch, device) + + else: + break + + if utils.is_main_process(): + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + 'epoch': epoch, + } + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_%02d.pth'%epoch)) + + dist.barrier() + + vqa_result = evaluation(model_without_ddp, test_loader, device, config) + result_file = save_result(vqa_result, args.result_dir, 'vqa_result') + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/vqa.yaml') + parser.add_argument('--output_dir', default='output/VQA') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + args.result_dir = os.path.join(args.output_dir, 'result') + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + Path(args.result_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/transform/randaugment.py b/sd/stablediffusion/src/blip/transform/randaugment.py new file mode 100644 index 0000000000000000000000000000000000000000..094d9f4cacc93146d2bab7311d9dc04feb07032c --- /dev/null +++ b/sd/stablediffusion/src/blip/transform/randaugment.py @@ -0,0 +1,340 @@ +import cv2 +import numpy as np + + +## aug functions +def identity_func(img): + return img + + +def autocontrast_func(img, cutoff=0): + ''' + same output as PIL.ImageOps.autocontrast + ''' + n_bins = 256 + + def tune_channel(ch): + n = ch.size + cut = cutoff * n // 100 + if cut == 0: + high, low = ch.max(), ch.min() + else: + hist = cv2.calcHist([ch], [0], None, [n_bins], [0, n_bins]) + low = np.argwhere(np.cumsum(hist) > cut) + low = 0 if low.shape[0] == 0 else low[0] + high = np.argwhere(np.cumsum(hist[::-1]) > cut) + high = n_bins - 1 if high.shape[0] == 0 else n_bins - 1 - high[0] + if high <= low: + table = np.arange(n_bins) + else: + scale = (n_bins - 1) / (high - low) + offset = -low * scale + table = np.arange(n_bins) * scale + offset + table[table < 0] = 0 + table[table > n_bins - 1] = n_bins - 1 + table = table.clip(0, 255).astype(np.uint8) + return table[ch] + + channels = [tune_channel(ch) for ch in cv2.split(img)] + out = cv2.merge(channels) + return out + + +def equalize_func(img): + ''' + same output as PIL.ImageOps.equalize + PIL's implementation is different from cv2.equalize + ''' + n_bins = 256 + + def tune_channel(ch): + hist = cv2.calcHist([ch], [0], None, [n_bins], [0, n_bins]) + non_zero_hist = hist[hist != 0].reshape(-1) + step = np.sum(non_zero_hist[:-1]) // (n_bins - 1) + if step == 0: return ch + n = np.empty_like(hist) + n[0] = step // 2 + n[1:] = hist[:-1] + table = (np.cumsum(n) // step).clip(0, 255).astype(np.uint8) + return table[ch] + + channels = [tune_channel(ch) for ch in cv2.split(img)] + out = cv2.merge(channels) + return out + + +def rotate_func(img, degree, fill=(0, 0, 0)): + ''' + like PIL, rotate by degree, not radians + ''' + H, W = img.shape[0], img.shape[1] + center = W / 2, H / 2 + M = cv2.getRotationMatrix2D(center, degree, 1) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill) + return out + + +def solarize_func(img, thresh=128): + ''' + same output as PIL.ImageOps.posterize + ''' + table = np.array([el if el < thresh else 255 - el for el in range(256)]) + table = table.clip(0, 255).astype(np.uint8) + out = table[img] + return out + + +def color_func(img, factor): + ''' + same output as PIL.ImageEnhance.Color + ''' + ## implementation according to PIL definition, quite slow + # degenerate = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)[:, :, np.newaxis] + # out = blend(degenerate, img, factor) + # M = ( + # np.eye(3) * factor + # + np.float32([0.114, 0.587, 0.299]).reshape(3, 1) * (1. - factor) + # )[np.newaxis, np.newaxis, :] + M = ( + np.float32([ + [0.886, -0.114, -0.114], + [-0.587, 0.413, -0.587], + [-0.299, -0.299, 0.701]]) * factor + + np.float32([[0.114], [0.587], [0.299]]) + ) + out = np.matmul(img, M).clip(0, 255).astype(np.uint8) + return out + + +def contrast_func(img, factor): + """ + same output as PIL.ImageEnhance.Contrast + """ + mean = np.sum(np.mean(img, axis=(0, 1)) * np.array([0.114, 0.587, 0.299])) + table = np.array([( + el - mean) * factor + mean + for el in range(256) + ]).clip(0, 255).astype(np.uint8) + out = table[img] + return out + + +def brightness_func(img, factor): + ''' + same output as PIL.ImageEnhance.Contrast + ''' + table = (np.arange(256, dtype=np.float32) * factor).clip(0, 255).astype(np.uint8) + out = table[img] + return out + + +def sharpness_func(img, factor): + ''' + The differences the this result and PIL are all on the 4 boundaries, the center + areas are same + ''' + kernel = np.ones((3, 3), dtype=np.float32) + kernel[1][1] = 5 + kernel /= 13 + degenerate = cv2.filter2D(img, -1, kernel) + if factor == 0.0: + out = degenerate + elif factor == 1.0: + out = img + else: + out = img.astype(np.float32) + degenerate = degenerate.astype(np.float32)[1:-1, 1:-1, :] + out[1:-1, 1:-1, :] = degenerate + factor * (out[1:-1, 1:-1, :] - degenerate) + out = out.astype(np.uint8) + return out + + +def shear_x_func(img, factor, fill=(0, 0, 0)): + H, W = img.shape[0], img.shape[1] + M = np.float32([[1, factor, 0], [0, 1, 0]]) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill, flags=cv2.INTER_LINEAR).astype(np.uint8) + return out + + +def translate_x_func(img, offset, fill=(0, 0, 0)): + ''' + same output as PIL.Image.transform + ''' + H, W = img.shape[0], img.shape[1] + M = np.float32([[1, 0, -offset], [0, 1, 0]]) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill, flags=cv2.INTER_LINEAR).astype(np.uint8) + return out + + +def translate_y_func(img, offset, fill=(0, 0, 0)): + ''' + same output as PIL.Image.transform + ''' + H, W = img.shape[0], img.shape[1] + M = np.float32([[1, 0, 0], [0, 1, -offset]]) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill, flags=cv2.INTER_LINEAR).astype(np.uint8) + return out + + +def posterize_func(img, bits): + ''' + same output as PIL.ImageOps.posterize + ''' + out = np.bitwise_and(img, np.uint8(255 << (8 - bits))) + return out + + +def shear_y_func(img, factor, fill=(0, 0, 0)): + H, W = img.shape[0], img.shape[1] + M = np.float32([[1, 0, 0], [factor, 1, 0]]) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill, flags=cv2.INTER_LINEAR).astype(np.uint8) + return out + + +def cutout_func(img, pad_size, replace=(0, 0, 0)): + replace = np.array(replace, dtype=np.uint8) + H, W = img.shape[0], img.shape[1] + rh, rw = np.random.random(2) + pad_size = pad_size // 2 + ch, cw = int(rh * H), int(rw * W) + x1, x2 = max(ch - pad_size, 0), min(ch + pad_size, H) + y1, y2 = max(cw - pad_size, 0), min(cw + pad_size, W) + out = img.copy() + out[x1:x2, y1:y2, :] = replace + return out + + +### level to args +def enhance_level_to_args(MAX_LEVEL): + def level_to_args(level): + return ((level / MAX_LEVEL) * 1.8 + 0.1,) + return level_to_args + + +def shear_level_to_args(MAX_LEVEL, replace_value): + def level_to_args(level): + level = (level / MAX_LEVEL) * 0.3 + if np.random.random() > 0.5: level = -level + return (level, replace_value) + + return level_to_args + + +def translate_level_to_args(translate_const, MAX_LEVEL, replace_value): + def level_to_args(level): + level = (level / MAX_LEVEL) * float(translate_const) + if np.random.random() > 0.5: level = -level + return (level, replace_value) + + return level_to_args + + +def cutout_level_to_args(cutout_const, MAX_LEVEL, replace_value): + def level_to_args(level): + level = int((level / MAX_LEVEL) * cutout_const) + return (level, replace_value) + + return level_to_args + + +def solarize_level_to_args(MAX_LEVEL): + def level_to_args(level): + level = int((level / MAX_LEVEL) * 256) + return (level, ) + return level_to_args + + +def none_level_to_args(level): + return () + + +def posterize_level_to_args(MAX_LEVEL): + def level_to_args(level): + level = int((level / MAX_LEVEL) * 4) + return (level, ) + return level_to_args + + +def rotate_level_to_args(MAX_LEVEL, replace_value): + def level_to_args(level): + level = (level / MAX_LEVEL) * 30 + if np.random.random() < 0.5: + level = -level + return (level, replace_value) + + return level_to_args + + +func_dict = { + 'Identity': identity_func, + 'AutoContrast': autocontrast_func, + 'Equalize': equalize_func, + 'Rotate': rotate_func, + 'Solarize': solarize_func, + 'Color': color_func, + 'Contrast': contrast_func, + 'Brightness': brightness_func, + 'Sharpness': sharpness_func, + 'ShearX': shear_x_func, + 'TranslateX': translate_x_func, + 'TranslateY': translate_y_func, + 'Posterize': posterize_func, + 'ShearY': shear_y_func, +} + +translate_const = 10 +MAX_LEVEL = 10 +replace_value = (128, 128, 128) +arg_dict = { + 'Identity': none_level_to_args, + 'AutoContrast': none_level_to_args, + 'Equalize': none_level_to_args, + 'Rotate': rotate_level_to_args(MAX_LEVEL, replace_value), + 'Solarize': solarize_level_to_args(MAX_LEVEL), + 'Color': enhance_level_to_args(MAX_LEVEL), + 'Contrast': enhance_level_to_args(MAX_LEVEL), + 'Brightness': enhance_level_to_args(MAX_LEVEL), + 'Sharpness': enhance_level_to_args(MAX_LEVEL), + 'ShearX': shear_level_to_args(MAX_LEVEL, replace_value), + 'TranslateX': translate_level_to_args( + translate_const, MAX_LEVEL, replace_value + ), + 'TranslateY': translate_level_to_args( + translate_const, MAX_LEVEL, replace_value + ), + 'Posterize': posterize_level_to_args(MAX_LEVEL), + 'ShearY': shear_level_to_args(MAX_LEVEL, replace_value), +} + + +class RandomAugment(object): + + def __init__(self, N=2, M=10, isPIL=False, augs=[]): + self.N = N + self.M = M + self.isPIL = isPIL + if augs: + self.augs = augs + else: + self.augs = list(arg_dict.keys()) + + def get_random_ops(self): + sampled_ops = np.random.choice(self.augs, self.N) + return [(op, 0.5, self.M) for op in sampled_ops] + + def __call__(self, img): + if self.isPIL: + img = np.array(img) + ops = self.get_random_ops() + for name, prob, level in ops: + if np.random.random() > prob: + continue + args = arg_dict[name](level) + img = func_dict[name](img, *args) + return img + + +if __name__ == '__main__': + a = RandomAugment() + img = np.random.randn(32, 32, 3) + a(img) \ No newline at end of file diff --git a/sd/stablediffusion/src/blip/utils.py b/sd/stablediffusion/src/blip/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..ebe0e1dc2f5d200156d5dd1acc305a8b7b7b98da --- /dev/null +++ b/sd/stablediffusion/src/blip/utils.py @@ -0,0 +1,278 @@ +import math +def cosine_lr_schedule(optimizer, epoch, max_epoch, init_lr, min_lr): + """Decay the learning rate""" + lr = (init_lr - min_lr) * 0.5 * (1. + math.cos(math.pi * epoch / max_epoch)) + min_lr + for param_group in optimizer.param_groups: + param_group['lr'] = lr + +def warmup_lr_schedule(optimizer, step, max_step, init_lr, max_lr): + """Warmup the learning rate""" + lr = min(max_lr, init_lr + (max_lr - init_lr) * step / max_step) + for param_group in optimizer.param_groups: + param_group['lr'] = lr + +def step_lr_schedule(optimizer, epoch, init_lr, min_lr, decay_rate): + """Decay the learning rate""" + lr = max(min_lr, init_lr * (decay_rate**epoch)) + for param_group in optimizer.param_groups: + param_group['lr'] = lr + +import numpy as np +import io +import os +import time +from collections import defaultdict, deque +import datetime + +import torch +import torch.distributed as dist + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float64, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) + + +class MetricLogger(object): + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def global_avg(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {:.4f}".format(name, meter.global_avg) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = [ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ] + if torch.cuda.is_available(): + log_msg.append('max mem: {memory:.0f}') + log_msg = self.delimiter.join(log_msg) + MB = 1024.0 * 1024.0 + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0 or i == len(iterable) - 1: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + if torch.cuda.is_available(): + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time), + memory=torch.cuda.max_memory_allocated() / MB)) + else: + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {} ({:.4f} s / it)'.format( + header, total_time_str, total_time / len(iterable))) + + +class AttrDict(dict): + def __init__(self, *args, **kwargs): + super(AttrDict, self).__init__(*args, **kwargs) + self.__dict__ = self + + +def compute_acc(logits, label, reduction='mean'): + ret = (torch.argmax(logits, dim=1) == label).float() + if reduction == 'none': + return ret.detach() + elif reduction == 'mean': + return ret.mean().item() + +def compute_n_params(model, return_str=True): + tot = 0 + for p in model.parameters(): + w = 1 + for x in p.shape: + w *= x + tot += w + if return_str: + if tot >= 1e6: + return '{:.1f}M'.format(tot / 1e6) + else: + return '{:.1f}K'.format(tot / 1e3) + else: + return tot + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + +def is_main_process(): + return get_rank() == 0 + + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False + return + + args.distributed = True + + torch.cuda.set_device(args.gpu) + args.dist_backend = 'nccl' + print('| distributed init (rank {}, word {}): {}'.format( + args.rank, args.world_size, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=args.dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) + + \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/.gitignore b/sd/stablediffusion/src/codeformer/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..15f690d1628f48e9d7d755a9472a44975fa06839 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/.gitignore @@ -0,0 +1,128 @@ +.vscode + +# ignored files +version.py + +# ignored files with suffix +*.html +# *.png +# *.jpeg +# *.jpg +*.pt +*.gif +*.pth +*.dat +*.zip + +# template + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# project +results/ +dlib/ +*_old* + diff --git a/sd/stablediffusion/src/codeformer/LICENSE b/sd/stablediffusion/src/codeformer/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..44bf750a27c1c2439a418a71c94925db83ad9d37 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/LICENSE @@ -0,0 +1,35 @@ +S-Lab License 1.0 + +Copyright 2022 S-Lab + +Redistribution and use for non-commercial purpose in source and +binary forms, with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +In the event that redistribution and/or use for commercial purpose in +source or binary forms, with or without modification is required, +please contact the contributor(s) of the work. \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/README.md b/sd/stablediffusion/src/codeformer/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d791b767360a14a9c3ef16c53220ac0879ef6790 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/README.md @@ -0,0 +1,144 @@ +

    + +

    + +## Towards Robust Blind Face Restoration with Codebook Lookup Transformer (NeurIPS 2022) + +[Paper](https://arxiv.org/abs/2206.11253) | [Project Page](https://shangchenzhou.com/projects/CodeFormer/) | [Video](https://youtu.be/d3VDpkXlueI) + + +google colab logo [![Hugging Face](https://img.shields.io/badge/Demo-%F0%9F%A4%97%20Hugging%20Face-blue)](https://huggingface.co/spaces/sczhou/CodeFormer) [![Replicate](https://img.shields.io/badge/Demo-%F0%9F%9A%80%20Replicate-blue)](https://replicate.com/sczhou/codeformer) ![visitors](https://visitor-badge-sczhou.glitch.me/badge?page_id=sczhou/CodeFormer) + + + +[Shangchen Zhou](https://shangchenzhou.com/), [Kelvin C.K. Chan](https://ckkelvinchan.github.io/), [Chongyi Li](https://li-chongyi.github.io/), [Chen Change Loy](https://www.mmlab-ntu.com/person/ccloy/) + +S-Lab, Nanyang Technological University + + + + +:star: If CodeFormer is helpful to your images or projects, please help star this repo. Thanks! :hugs: + +**[News]**: :whale: *Due to copyright issues, we have to delay the release of the training code (expected by the end of this year). Please star and stay tuned for our future updates!* +### Update +- **2022.10.05**: Support video input `--input_path [YOUR_VIDOE.mp4]`. Try it to enhance your videos! :clapper: +- **2022.09.14**: Integrated to :hugs: [Hugging Face](https://huggingface.co/spaces). Try out online demo! [![Hugging Face](https://img.shields.io/badge/Demo-%F0%9F%A4%97%20Hugging%20Face-blue)](https://huggingface.co/spaces/sczhou/CodeFormer) +- **2022.09.09**: Integrated to :rocket: [Replicate](https://replicate.com/explore). Try out online demo! [![Replicate](https://img.shields.io/badge/Demo-%F0%9F%9A%80%20Replicate-blue)](https://replicate.com/sczhou/codeformer) +- **2022.09.04**: Add face upsampling `--face_upsample` for high-resolution AI-created face enhancement. +- **2022.08.23**: Some modifications on face detection and fusion for better AI-created face enhancement. +- **2022.08.07**: Integrate [Real-ESRGAN](https://github.com/xinntao/Real-ESRGAN) to support background image enhancement. +- **2022.07.29**: Integrate new face detectors of `['RetinaFace'(default), 'YOLOv5']`. +- **2022.07.17**: Add Colab demo of CodeFormer. google colab logo +- **2022.07.16**: Release inference code for face restoration. :blush: +- **2022.06.21**: This repo is created. + +### TODO +- [ ] Add checkpoint for face inpainting +- [ ] Add checkpoint for face colorization +- [ ] Add training code and config files +- [x] ~~Add background image enhancement~~ + +#### :panda_face: Try Enhancing Old Photos / Fixing AI-arts +[](https://imgsli.com/MTI3NTE2) [](https://imgsli.com/MTI3NTE1) [](https://imgsli.com/MTI3NTIw) + +#### Face Restoration + + + + +#### Face Color Enhancement and Restoration + + + +#### Face Inpainting + + + + + +### Dependencies and Installation + +- Pytorch >= 1.7.1 +- CUDA >= 10.1 +- Other required packages in `requirements.txt` +``` +# git clone this repository +git clone https://github.com/sczhou/CodeFormer +cd CodeFormer + +# create new anaconda env +conda create -n codeformer python=3.8 -y +conda activate codeformer + +# install python dependencies +pip3 install -r requirements.txt +python basicsr/setup.py develop +``` + + +### Quick Inference + +#### Download Pre-trained Models: +Download the facelib pretrained models from [[Google Drive](https://drive.google.com/drive/folders/1b_3qwrzY_kTQh0-SnBoGBgOrJ_PLZSKm?usp=sharing) | [OneDrive](https://entuedu-my.sharepoint.com/:f:/g/personal/s200094_e_ntu_edu_sg/EvDxR7FcAbZMp_MA9ouq7aQB8XTppMb3-T0uGZ_2anI2mg?e=DXsJFo)] to the `weights/facelib` folder. You can manually download the pretrained models OR download by running the following command. +``` +python scripts/download_pretrained_models.py facelib +``` + +Download the CodeFormer pretrained models from [[Google Drive](https://drive.google.com/drive/folders/1CNNByjHDFt0b95q54yMVp6Ifo5iuU6QS?usp=sharing) | [OneDrive](https://entuedu-my.sharepoint.com/:f:/g/personal/s200094_e_ntu_edu_sg/EoKFj4wo8cdIn2-TY2IV6CYBhZ0pIG4kUOeHdPR_A5nlbg?e=AO8UN9)] to the `weights/CodeFormer` folder. You can manually download the pretrained models OR download by running the following command. +``` +python scripts/download_pretrained_models.py CodeFormer +``` + +#### Prepare Testing Data: +You can put the testing images in the `inputs/TestWhole` folder. If you would like to test on cropped and aligned faces, you can put them in the `inputs/cropped_faces` folder. + + +#### Testing on Face Restoration: +[Note] If you want to compare CodeFormer in your paper, please run the following command indicating `--has_aligned` (for cropped and aligned face), as the command for the whole image will involve a process of face-background fusion that may damage hair texture on the boundary, which leads to unfair comparison. + +🧑🏻 Face Restoration (cropped and aligned face) +``` +# For cropped and aligned faces +python inference_codeformer.py -w 0.5 --has_aligned --input_path [input folder] +``` + +:framed_picture: Whole Image Enhancement +``` +# For whole image +# Add '--bg_upsampler realesrgan' to enhance the background regions with Real-ESRGAN +# Add '--face_upsample' to further upsample restorated face with Real-ESRGAN +python inference_codeformer.py -w 0.7 --input_path [image folder/image path] +``` + +:clapper: Video Enhancement +``` +# For video clips +python inference_codeformer.py --bg_upsampler realesrgan --face_upsample -w 1.0 --input_path [video path] +``` + + +Fidelity weight *w* lays in [0, 1]. Generally, smaller *w* tends to produce a higher-quality result, while larger *w* yields a higher-fidelity result. + +The results will be saved in the `results` folder. + +### Citation +If our work is useful for your research, please consider citing: + + @inproceedings{zhou2022codeformer, + author = {Zhou, Shangchen and Chan, Kelvin C.K. and Li, Chongyi and Loy, Chen Change}, + title = {Towards Robust Blind Face Restoration with Codebook Lookup TransFormer}, + booktitle = {NeurIPS}, + year = {2022} + } + +### License + +This project is licensed under NTU S-Lab License 1.0. Redistribution and use should follow this license. + +### Acknowledgement + +This project is based on [BasicSR](https://github.com/XPixelGroup/BasicSR). Some codes are brought from [Unleashing Transformers](https://github.com/samb-t/unleashing-transformers), [YOLOv5-face](https://github.com/deepcam-cn/yolov5-face), and [FaceXLib](https://github.com/xinntao/facexlib). We also adopt [Real-ESRGAN](https://github.com/xinntao/Real-ESRGAN) to support background image enhancement. Thanks for their awesome works. + +### Contact +If you have any question, please feel free to reach me out at `shangchenzhou@gmail.com`. diff --git a/sd/stablediffusion/src/codeformer/assets/CodeFormer_logo.png b/sd/stablediffusion/src/codeformer/assets/CodeFormer_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..024cb724f43c2b5cff7039c69b78f261a5a4898c Binary files /dev/null and b/sd/stablediffusion/src/codeformer/assets/CodeFormer_logo.png differ diff --git a/sd/stablediffusion/src/codeformer/assets/color_enhancement_result1.png b/sd/stablediffusion/src/codeformer/assets/color_enhancement_result1.png new file mode 100644 index 0000000000000000000000000000000000000000..34433db6378b37cb47a1e544217e4d7f679f7038 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/assets/color_enhancement_result1.png differ diff --git a/sd/stablediffusion/src/codeformer/assets/color_enhancement_result2.png b/sd/stablediffusion/src/codeformer/assets/color_enhancement_result2.png new file mode 100644 index 0000000000000000000000000000000000000000..228690ac9b1453e67e0212ab2952bea887543a09 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/assets/color_enhancement_result2.png differ diff --git a/sd/stablediffusion/src/codeformer/assets/imgsli_2.jpg b/sd/stablediffusion/src/codeformer/assets/imgsli_2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..42dd7f43867aabdf1790ee66e73d8b13fd31ec32 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/assets/imgsli_2.jpg differ diff --git a/sd/stablediffusion/src/codeformer/assets/imgsli_3.jpg b/sd/stablediffusion/src/codeformer/assets/imgsli_3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c3f67d9d3526233f5cd5122a571db48135827325 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/assets/imgsli_3.jpg differ diff --git a/sd/stablediffusion/src/codeformer/assets/inpainting_result1.png b/sd/stablediffusion/src/codeformer/assets/inpainting_result1.png new file mode 100644 index 0000000000000000000000000000000000000000..2c6fa68ad4340c0281e096f7928d28be831c00c1 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/assets/inpainting_result1.png differ diff --git a/sd/stablediffusion/src/codeformer/assets/inpainting_result2.png b/sd/stablediffusion/src/codeformer/assets/inpainting_result2.png new file mode 100644 index 0000000000000000000000000000000000000000..2945f9f91c93c329c5e66d4e8519dbb3f90fa1b5 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/assets/inpainting_result2.png differ diff --git a/sd/stablediffusion/src/codeformer/assets/network.jpg b/sd/stablediffusion/src/codeformer/assets/network.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5aaa6bd1b0f71bf28e5f175c2cda1e7b34b8aa5f Binary files /dev/null and b/sd/stablediffusion/src/codeformer/assets/network.jpg differ diff --git a/sd/stablediffusion/src/codeformer/assets/restoration_result1.png b/sd/stablediffusion/src/codeformer/assets/restoration_result1.png new file mode 100644 index 0000000000000000000000000000000000000000..8fd3b67ec9a5c9b7606ea0515a5b071c1e7a1118 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/assets/restoration_result1.png differ diff --git a/sd/stablediffusion/src/codeformer/assets/restoration_result2.png b/sd/stablediffusion/src/codeformer/assets/restoration_result2.png new file mode 100644 index 0000000000000000000000000000000000000000..a2ff282701b6c66a612b3b669512e8d99595ee9f Binary files /dev/null and b/sd/stablediffusion/src/codeformer/assets/restoration_result2.png differ diff --git a/sd/stablediffusion/src/codeformer/assets/restoration_result3.png b/sd/stablediffusion/src/codeformer/assets/restoration_result3.png new file mode 100644 index 0000000000000000000000000000000000000000..022d764266b4d43f4ffea6b1f7ccca63b32e180c Binary files /dev/null and b/sd/stablediffusion/src/codeformer/assets/restoration_result3.png differ diff --git a/sd/stablediffusion/src/codeformer/assets/restoration_result4.png b/sd/stablediffusion/src/codeformer/assets/restoration_result4.png new file mode 100644 index 0000000000000000000000000000000000000000..5e965076c7b5fae051dc2df354f74c0864ec4214 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/assets/restoration_result4.png differ diff --git a/sd/stablediffusion/src/codeformer/basicsr/VERSION b/sd/stablediffusion/src/codeformer/basicsr/VERSION new file mode 100644 index 0000000000000000000000000000000000000000..1892b926767774e9ba91f1e584fa71b4c56abb69 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/VERSION @@ -0,0 +1 @@ +1.3.2 diff --git a/sd/stablediffusion/src/codeformer/basicsr/__init__.py b/sd/stablediffusion/src/codeformer/basicsr/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c7ffcccd7fc0f33b59d99d73d0436d60e561b0fc --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/__init__.py @@ -0,0 +1,11 @@ +# https://github.com/xinntao/BasicSR +# flake8: noqa +from .archs import * +from .data import * +from .losses import * +from .metrics import * +from .models import * +from .ops import * +from .train import * +from .utils import * +from .version import __gitsha__, __version__ diff --git a/sd/stablediffusion/src/codeformer/basicsr/archs/__init__.py b/sd/stablediffusion/src/codeformer/basicsr/archs/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..cfb1e4d7bb221c429082bd389d9140e5b1cc07b0 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/archs/__init__.py @@ -0,0 +1,25 @@ +import importlib +from copy import deepcopy +from os import path as osp + +from basicsr.utils import get_root_logger, scandir +from basicsr.utils.registry import ARCH_REGISTRY + +__all__ = ['build_network'] + +# automatically scan and import arch modules for registry +# scan all the files under the 'archs' folder and collect files ending with +# '_arch.py' +arch_folder = osp.dirname(osp.abspath(__file__)) +arch_filenames = [osp.splitext(osp.basename(v))[0] for v in scandir(arch_folder) if v.endswith('_arch.py')] +# import all the arch modules +_arch_modules = [importlib.import_module(f'basicsr.archs.{file_name}') for file_name in arch_filenames] + + +def build_network(opt): + opt = deepcopy(opt) + network_type = opt.pop('type') + net = ARCH_REGISTRY.get(network_type)(**opt) + logger = get_root_logger() + logger.info(f'Network [{net.__class__.__name__}] is created.') + return net diff --git a/sd/stablediffusion/src/codeformer/basicsr/archs/arcface_arch.py b/sd/stablediffusion/src/codeformer/basicsr/archs/arcface_arch.py new file mode 100644 index 0000000000000000000000000000000000000000..fe5afb7bd2b359e0c2b7efdf628ab10b63964d87 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/archs/arcface_arch.py @@ -0,0 +1,245 @@ +import torch.nn as nn +from basicsr.utils.registry import ARCH_REGISTRY + + +def conv3x3(inplanes, outplanes, stride=1): + """A simple wrapper for 3x3 convolution with padding. + + Args: + inplanes (int): Channel number of inputs. + outplanes (int): Channel number of outputs. + stride (int): Stride in convolution. Default: 1. + """ + return nn.Conv2d(inplanes, outplanes, kernel_size=3, stride=stride, padding=1, bias=False) + + +class BasicBlock(nn.Module): + """Basic residual block used in the ResNetArcFace architecture. + + Args: + inplanes (int): Channel number of inputs. + planes (int): Channel number of outputs. + stride (int): Stride in convolution. Default: 1. + downsample (nn.Module): The downsample module. Default: None. + """ + expansion = 1 # output channel expansion ratio + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(BasicBlock, self).__init__() + self.conv1 = conv3x3(inplanes, planes, stride) + self.bn1 = nn.BatchNorm2d(planes) + self.relu = nn.ReLU(inplace=True) + self.conv2 = conv3x3(planes, planes) + self.bn2 = nn.BatchNorm2d(planes) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class IRBlock(nn.Module): + """Improved residual block (IR Block) used in the ResNetArcFace architecture. + + Args: + inplanes (int): Channel number of inputs. + planes (int): Channel number of outputs. + stride (int): Stride in convolution. Default: 1. + downsample (nn.Module): The downsample module. Default: None. + use_se (bool): Whether use the SEBlock (squeeze and excitation block). Default: True. + """ + expansion = 1 # output channel expansion ratio + + def __init__(self, inplanes, planes, stride=1, downsample=None, use_se=True): + super(IRBlock, self).__init__() + self.bn0 = nn.BatchNorm2d(inplanes) + self.conv1 = conv3x3(inplanes, inplanes) + self.bn1 = nn.BatchNorm2d(inplanes) + self.prelu = nn.PReLU() + self.conv2 = conv3x3(inplanes, planes, stride) + self.bn2 = nn.BatchNorm2d(planes) + self.downsample = downsample + self.stride = stride + self.use_se = use_se + if self.use_se: + self.se = SEBlock(planes) + + def forward(self, x): + residual = x + out = self.bn0(x) + out = self.conv1(out) + out = self.bn1(out) + out = self.prelu(out) + + out = self.conv2(out) + out = self.bn2(out) + if self.use_se: + out = self.se(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.prelu(out) + + return out + + +class Bottleneck(nn.Module): + """Bottleneck block used in the ResNetArcFace architecture. + + Args: + inplanes (int): Channel number of inputs. + planes (int): Channel number of outputs. + stride (int): Stride in convolution. Default: 1. + downsample (nn.Module): The downsample module. Default: None. + """ + expansion = 4 # output channel expansion ratio + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(Bottleneck, self).__init__() + self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) + self.bn1 = nn.BatchNorm2d(planes) + self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False) + self.bn2 = nn.BatchNorm2d(planes) + self.conv3 = nn.Conv2d(planes, planes * self.expansion, kernel_size=1, bias=False) + self.bn3 = nn.BatchNorm2d(planes * self.expansion) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class SEBlock(nn.Module): + """The squeeze-and-excitation block (SEBlock) used in the IRBlock. + + Args: + channel (int): Channel number of inputs. + reduction (int): Channel reduction ration. Default: 16. + """ + + def __init__(self, channel, reduction=16): + super(SEBlock, self).__init__() + self.avg_pool = nn.AdaptiveAvgPool2d(1) # pool to 1x1 without spatial information + self.fc = nn.Sequential( + nn.Linear(channel, channel // reduction), nn.PReLU(), nn.Linear(channel // reduction, channel), + nn.Sigmoid()) + + def forward(self, x): + b, c, _, _ = x.size() + y = self.avg_pool(x).view(b, c) + y = self.fc(y).view(b, c, 1, 1) + return x * y + + +@ARCH_REGISTRY.register() +class ResNetArcFace(nn.Module): + """ArcFace with ResNet architectures. + + Ref: ArcFace: Additive Angular Margin Loss for Deep Face Recognition. + + Args: + block (str): Block used in the ArcFace architecture. + layers (tuple(int)): Block numbers in each layer. + use_se (bool): Whether use the SEBlock (squeeze and excitation block). Default: True. + """ + + def __init__(self, block, layers, use_se=True): + if block == 'IRBlock': + block = IRBlock + self.inplanes = 64 + self.use_se = use_se + super(ResNetArcFace, self).__init__() + + self.conv1 = nn.Conv2d(1, 64, kernel_size=3, padding=1, bias=False) + self.bn1 = nn.BatchNorm2d(64) + self.prelu = nn.PReLU() + self.maxpool = nn.MaxPool2d(kernel_size=2, stride=2) + self.layer1 = self._make_layer(block, 64, layers[0]) + self.layer2 = self._make_layer(block, 128, layers[1], stride=2) + self.layer3 = self._make_layer(block, 256, layers[2], stride=2) + self.layer4 = self._make_layer(block, 512, layers[3], stride=2) + self.bn4 = nn.BatchNorm2d(512) + self.dropout = nn.Dropout() + self.fc5 = nn.Linear(512 * 8 * 8, 512) + self.bn5 = nn.BatchNorm1d(512) + + # initialization + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.xavier_normal_(m.weight) + elif isinstance(m, nn.BatchNorm2d) or isinstance(m, nn.BatchNorm1d): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + nn.init.xavier_normal_(m.weight) + nn.init.constant_(m.bias, 0) + + def _make_layer(self, block, planes, num_blocks, stride=1): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2d(self.inplanes, planes * block.expansion, kernel_size=1, stride=stride, bias=False), + nn.BatchNorm2d(planes * block.expansion), + ) + layers = [] + layers.append(block(self.inplanes, planes, stride, downsample, use_se=self.use_se)) + self.inplanes = planes + for _ in range(1, num_blocks): + layers.append(block(self.inplanes, planes, use_se=self.use_se)) + + return nn.Sequential(*layers) + + def forward(self, x): + x = self.conv1(x) + x = self.bn1(x) + x = self.prelu(x) + x = self.maxpool(x) + + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + x = self.layer4(x) + x = self.bn4(x) + x = self.dropout(x) + x = x.view(x.size(0), -1) + x = self.fc5(x) + x = self.bn5(x) + + return x \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/basicsr/archs/arch_util.py b/sd/stablediffusion/src/codeformer/basicsr/archs/arch_util.py new file mode 100644 index 0000000000000000000000000000000000000000..bad45ab34e901c47fb539152fca714a3795b0de2 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/archs/arch_util.py @@ -0,0 +1,318 @@ +import collections.abc +import math +import torch +import torchvision +import warnings +from distutils.version import LooseVersion +from itertools import repeat +from torch import nn as nn +from torch.nn import functional as F +from torch.nn import init as init +from torch.nn.modules.batchnorm import _BatchNorm + +from basicsr.ops.dcn import ModulatedDeformConvPack, modulated_deform_conv +from basicsr.utils import get_root_logger + + +@torch.no_grad() +def default_init_weights(module_list, scale=1, bias_fill=0, **kwargs): + """Initialize network weights. + + Args: + module_list (list[nn.Module] | nn.Module): Modules to be initialized. + scale (float): Scale initialized weights, especially for residual + blocks. Default: 1. + bias_fill (float): The value to fill bias. Default: 0 + kwargs (dict): Other arguments for initialization function. + """ + if not isinstance(module_list, list): + module_list = [module_list] + for module in module_list: + for m in module.modules(): + if isinstance(m, nn.Conv2d): + init.kaiming_normal_(m.weight, **kwargs) + m.weight.data *= scale + if m.bias is not None: + m.bias.data.fill_(bias_fill) + elif isinstance(m, nn.Linear): + init.kaiming_normal_(m.weight, **kwargs) + m.weight.data *= scale + if m.bias is not None: + m.bias.data.fill_(bias_fill) + elif isinstance(m, _BatchNorm): + init.constant_(m.weight, 1) + if m.bias is not None: + m.bias.data.fill_(bias_fill) + + +def make_layer(basic_block, num_basic_block, **kwarg): + """Make layers by stacking the same blocks. + + Args: + basic_block (nn.module): nn.module class for basic block. + num_basic_block (int): number of blocks. + + Returns: + nn.Sequential: Stacked blocks in nn.Sequential. + """ + layers = [] + for _ in range(num_basic_block): + layers.append(basic_block(**kwarg)) + return nn.Sequential(*layers) + + +class ResidualBlockNoBN(nn.Module): + """Residual block without BN. + + It has a style of: + ---Conv-ReLU-Conv-+- + |________________| + + Args: + num_feat (int): Channel number of intermediate features. + Default: 64. + res_scale (float): Residual scale. Default: 1. + pytorch_init (bool): If set to True, use pytorch default init, + otherwise, use default_init_weights. Default: False. + """ + + def __init__(self, num_feat=64, res_scale=1, pytorch_init=False): + super(ResidualBlockNoBN, self).__init__() + self.res_scale = res_scale + self.conv1 = nn.Conv2d(num_feat, num_feat, 3, 1, 1, bias=True) + self.conv2 = nn.Conv2d(num_feat, num_feat, 3, 1, 1, bias=True) + self.relu = nn.ReLU(inplace=True) + + if not pytorch_init: + default_init_weights([self.conv1, self.conv2], 0.1) + + def forward(self, x): + identity = x + out = self.conv2(self.relu(self.conv1(x))) + return identity + out * self.res_scale + + +class Upsample(nn.Sequential): + """Upsample module. + + Args: + scale (int): Scale factor. Supported scales: 2^n and 3. + num_feat (int): Channel number of intermediate features. + """ + + def __init__(self, scale, num_feat): + m = [] + if (scale & (scale - 1)) == 0: # scale = 2^n + for _ in range(int(math.log(scale, 2))): + m.append(nn.Conv2d(num_feat, 4 * num_feat, 3, 1, 1)) + m.append(nn.PixelShuffle(2)) + elif scale == 3: + m.append(nn.Conv2d(num_feat, 9 * num_feat, 3, 1, 1)) + m.append(nn.PixelShuffle(3)) + else: + raise ValueError(f'scale {scale} is not supported. Supported scales: 2^n and 3.') + super(Upsample, self).__init__(*m) + + +def flow_warp(x, flow, interp_mode='bilinear', padding_mode='zeros', align_corners=True): + """Warp an image or feature map with optical flow. + + Args: + x (Tensor): Tensor with size (n, c, h, w). + flow (Tensor): Tensor with size (n, h, w, 2), normal value. + interp_mode (str): 'nearest' or 'bilinear'. Default: 'bilinear'. + padding_mode (str): 'zeros' or 'border' or 'reflection'. + Default: 'zeros'. + align_corners (bool): Before pytorch 1.3, the default value is + align_corners=True. After pytorch 1.3, the default value is + align_corners=False. Here, we use the True as default. + + Returns: + Tensor: Warped image or feature map. + """ + assert x.size()[-2:] == flow.size()[1:3] + _, _, h, w = x.size() + # create mesh grid + grid_y, grid_x = torch.meshgrid(torch.arange(0, h).type_as(x), torch.arange(0, w).type_as(x)) + grid = torch.stack((grid_x, grid_y), 2).float() # W(x), H(y), 2 + grid.requires_grad = False + + vgrid = grid + flow + # scale grid to [-1,1] + vgrid_x = 2.0 * vgrid[:, :, :, 0] / max(w - 1, 1) - 1.0 + vgrid_y = 2.0 * vgrid[:, :, :, 1] / max(h - 1, 1) - 1.0 + vgrid_scaled = torch.stack((vgrid_x, vgrid_y), dim=3) + output = F.grid_sample(x, vgrid_scaled, mode=interp_mode, padding_mode=padding_mode, align_corners=align_corners) + + # TODO, what if align_corners=False + return output + + +def resize_flow(flow, size_type, sizes, interp_mode='bilinear', align_corners=False): + """Resize a flow according to ratio or shape. + + Args: + flow (Tensor): Precomputed flow. shape [N, 2, H, W]. + size_type (str): 'ratio' or 'shape'. + sizes (list[int | float]): the ratio for resizing or the final output + shape. + 1) The order of ratio should be [ratio_h, ratio_w]. For + downsampling, the ratio should be smaller than 1.0 (i.e., ratio + < 1.0). For upsampling, the ratio should be larger than 1.0 (i.e., + ratio > 1.0). + 2) The order of output_size should be [out_h, out_w]. + interp_mode (str): The mode of interpolation for resizing. + Default: 'bilinear'. + align_corners (bool): Whether align corners. Default: False. + + Returns: + Tensor: Resized flow. + """ + _, _, flow_h, flow_w = flow.size() + if size_type == 'ratio': + output_h, output_w = int(flow_h * sizes[0]), int(flow_w * sizes[1]) + elif size_type == 'shape': + output_h, output_w = sizes[0], sizes[1] + else: + raise ValueError(f'Size type should be ratio or shape, but got type {size_type}.') + + input_flow = flow.clone() + ratio_h = output_h / flow_h + ratio_w = output_w / flow_w + input_flow[:, 0, :, :] *= ratio_w + input_flow[:, 1, :, :] *= ratio_h + resized_flow = F.interpolate( + input=input_flow, size=(output_h, output_w), mode=interp_mode, align_corners=align_corners) + return resized_flow + + +# TODO: may write a cpp file +def pixel_unshuffle(x, scale): + """ Pixel unshuffle. + + Args: + x (Tensor): Input feature with shape (b, c, hh, hw). + scale (int): Downsample ratio. + + Returns: + Tensor: the pixel unshuffled feature. + """ + b, c, hh, hw = x.size() + out_channel = c * (scale**2) + assert hh % scale == 0 and hw % scale == 0 + h = hh // scale + w = hw // scale + x_view = x.view(b, c, h, scale, w, scale) + return x_view.permute(0, 1, 3, 5, 2, 4).reshape(b, out_channel, h, w) + + +class DCNv2Pack(ModulatedDeformConvPack): + """Modulated deformable conv for deformable alignment. + + Different from the official DCNv2Pack, which generates offsets and masks + from the preceding features, this DCNv2Pack takes another different + features to generate offsets and masks. + + Ref: + Delving Deep into Deformable Alignment in Video Super-Resolution. + """ + + def forward(self, x, feat): + out = self.conv_offset(feat) + o1, o2, mask = torch.chunk(out, 3, dim=1) + offset = torch.cat((o1, o2), dim=1) + mask = torch.sigmoid(mask) + + offset_absmean = torch.mean(torch.abs(offset)) + if offset_absmean > 50: + logger = get_root_logger() + logger.warning(f'Offset abs mean is {offset_absmean}, larger than 50.') + + if LooseVersion(torchvision.__version__) >= LooseVersion('0.9.0'): + return torchvision.ops.deform_conv2d(x, offset, self.weight, self.bias, self.stride, self.padding, + self.dilation, mask) + else: + return modulated_deform_conv(x, offset, mask, self.weight, self.bias, self.stride, self.padding, + self.dilation, self.groups, self.deformable_groups) + + +def _no_grad_trunc_normal_(tensor, mean, std, a, b): + # From: https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/layers/weight_init.py + # Cut & paste from PyTorch official master until it's in a few official releases - RW + # Method based on https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf + def norm_cdf(x): + # Computes standard normal cumulative distribution function + return (1. + math.erf(x / math.sqrt(2.))) / 2. + + if (mean < a - 2 * std) or (mean > b + 2 * std): + warnings.warn( + 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' + 'The distribution of values may be incorrect.', + stacklevel=2) + + with torch.no_grad(): + # Values are generated by using a truncated uniform distribution and + # then using the inverse CDF for the normal distribution. + # Get upper and lower cdf values + low = norm_cdf((a - mean) / std) + up = norm_cdf((b - mean) / std) + + # Uniformly fill tensor with values from [low, up], then translate to + # [2l-1, 2u-1]. + tensor.uniform_(2 * low - 1, 2 * up - 1) + + # Use inverse cdf transform for normal distribution to get truncated + # standard normal + tensor.erfinv_() + + # Transform to proper mean, std + tensor.mul_(std * math.sqrt(2.)) + tensor.add_(mean) + + # Clamp to ensure it's in the proper range + tensor.clamp_(min=a, max=b) + return tensor + + +def trunc_normal_(tensor, mean=0., std=1., a=-2., b=2.): + r"""Fills the input Tensor with values drawn from a truncated + normal distribution. + + From: https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/layers/weight_init.py + + The values are effectively drawn from the + normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` + with values outside :math:`[a, b]` redrawn until they are within + the bounds. The method used for generating the random values works + best when :math:`a \leq \text{mean} \leq b`. + + Args: + tensor: an n-dimensional `torch.Tensor` + mean: the mean of the normal distribution + std: the standard deviation of the normal distribution + a: the minimum cutoff value + b: the maximum cutoff value + + Examples: + >>> w = torch.empty(3, 5) + >>> nn.init.trunc_normal_(w) + """ + return _no_grad_trunc_normal_(tensor, mean, std, a, b) + + +# From PyTorch +def _ntuple(n): + + def parse(x): + if isinstance(x, collections.abc.Iterable): + return x + return tuple(repeat(x, n)) + + return parse + + +to_1tuple = _ntuple(1) +to_2tuple = _ntuple(2) +to_3tuple = _ntuple(3) +to_4tuple = _ntuple(4) +to_ntuple = _ntuple \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/basicsr/archs/codeformer_arch.py b/sd/stablediffusion/src/codeformer/basicsr/archs/codeformer_arch.py new file mode 100644 index 0000000000000000000000000000000000000000..4d0d8027c8c4ffb26af6f4ba361514e93e320e8d --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/archs/codeformer_arch.py @@ -0,0 +1,276 @@ +import math +import numpy as np +import torch +from torch import nn, Tensor +import torch.nn.functional as F +from typing import Optional, List + +from basicsr.archs.vqgan_arch import * +from basicsr.utils import get_root_logger +from basicsr.utils.registry import ARCH_REGISTRY + +def calc_mean_std(feat, eps=1e-5): + """Calculate mean and std for adaptive_instance_normalization. + + Args: + feat (Tensor): 4D tensor. + eps (float): A small value added to the variance to avoid + divide-by-zero. Default: 1e-5. + """ + size = feat.size() + assert len(size) == 4, 'The input feature should be 4D tensor.' + b, c = size[:2] + feat_var = feat.view(b, c, -1).var(dim=2) + eps + feat_std = feat_var.sqrt().view(b, c, 1, 1) + feat_mean = feat.view(b, c, -1).mean(dim=2).view(b, c, 1, 1) + return feat_mean, feat_std + + +def adaptive_instance_normalization(content_feat, style_feat): + """Adaptive instance normalization. + + Adjust the reference features to have the similar color and illuminations + as those in the degradate features. + + Args: + content_feat (Tensor): The reference feature. + style_feat (Tensor): The degradate features. + """ + size = content_feat.size() + style_mean, style_std = calc_mean_std(style_feat) + content_mean, content_std = calc_mean_std(content_feat) + normalized_feat = (content_feat - content_mean.expand(size)) / content_std.expand(size) + return normalized_feat * style_std.expand(size) + style_mean.expand(size) + + +class PositionEmbeddingSine(nn.Module): + """ + This is a more standard version of the position embedding, very similar to the one + used by the Attention is all you need paper, generalized to work on images. + """ + + def __init__(self, num_pos_feats=64, temperature=10000, normalize=False, scale=None): + super().__init__() + self.num_pos_feats = num_pos_feats + self.temperature = temperature + self.normalize = normalize + if scale is not None and normalize is False: + raise ValueError("normalize should be True if scale is passed") + if scale is None: + scale = 2 * math.pi + self.scale = scale + + def forward(self, x, mask=None): + if mask is None: + mask = torch.zeros((x.size(0), x.size(2), x.size(3)), device=x.device, dtype=torch.bool) + not_mask = ~mask + y_embed = not_mask.cumsum(1, dtype=torch.float32) + x_embed = not_mask.cumsum(2, dtype=torch.float32) + if self.normalize: + eps = 1e-6 + y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale + x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale + + dim_t = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device) + dim_t = self.temperature ** (2 * (dim_t // 2) / self.num_pos_feats) + + pos_x = x_embed[:, :, :, None] / dim_t + pos_y = y_embed[:, :, :, None] / dim_t + pos_x = torch.stack( + (pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4 + ).flatten(3) + pos_y = torch.stack( + (pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4 + ).flatten(3) + pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2) + return pos + +def _get_activation_fn(activation): + """Return an activation function given a string""" + if activation == "relu": + return F.relu + if activation == "gelu": + return F.gelu + if activation == "glu": + return F.glu + raise RuntimeError(F"activation should be relu/gelu, not {activation}.") + + +class TransformerSALayer(nn.Module): + def __init__(self, embed_dim, nhead=8, dim_mlp=2048, dropout=0.0, activation="gelu"): + super().__init__() + self.self_attn = nn.MultiheadAttention(embed_dim, nhead, dropout=dropout) + # Implementation of Feedforward model - MLP + self.linear1 = nn.Linear(embed_dim, dim_mlp) + self.dropout = nn.Dropout(dropout) + self.linear2 = nn.Linear(dim_mlp, embed_dim) + + self.norm1 = nn.LayerNorm(embed_dim) + self.norm2 = nn.LayerNorm(embed_dim) + self.dropout1 = nn.Dropout(dropout) + self.dropout2 = nn.Dropout(dropout) + + self.activation = _get_activation_fn(activation) + + def with_pos_embed(self, tensor, pos: Optional[Tensor]): + return tensor if pos is None else tensor + pos + + def forward(self, tgt, + tgt_mask: Optional[Tensor] = None, + tgt_key_padding_mask: Optional[Tensor] = None, + query_pos: Optional[Tensor] = None): + + # self attention + tgt2 = self.norm1(tgt) + q = k = self.with_pos_embed(tgt2, query_pos) + tgt2 = self.self_attn(q, k, value=tgt2, attn_mask=tgt_mask, + key_padding_mask=tgt_key_padding_mask)[0] + tgt = tgt + self.dropout1(tgt2) + + # ffn + tgt2 = self.norm2(tgt) + tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt2)))) + tgt = tgt + self.dropout2(tgt2) + return tgt + +class Fuse_sft_block(nn.Module): + def __init__(self, in_ch, out_ch): + super().__init__() + self.encode_enc = ResBlock(2*in_ch, out_ch) + + self.scale = nn.Sequential( + nn.Conv2d(in_ch, out_ch, kernel_size=3, padding=1), + nn.LeakyReLU(0.2, True), + nn.Conv2d(out_ch, out_ch, kernel_size=3, padding=1)) + + self.shift = nn.Sequential( + nn.Conv2d(in_ch, out_ch, kernel_size=3, padding=1), + nn.LeakyReLU(0.2, True), + nn.Conv2d(out_ch, out_ch, kernel_size=3, padding=1)) + + def forward(self, enc_feat, dec_feat, w=1): + enc_feat = self.encode_enc(torch.cat([enc_feat, dec_feat], dim=1)) + scale = self.scale(enc_feat) + shift = self.shift(enc_feat) + residual = w * (dec_feat * scale + shift) + out = dec_feat + residual + return out + + +@ARCH_REGISTRY.register() +class CodeFormer(VQAutoEncoder): + def __init__(self, dim_embd=512, n_head=8, n_layers=9, + codebook_size=1024, latent_size=256, + connect_list=['32', '64', '128', '256'], + fix_modules=['quantize','generator']): + super(CodeFormer, self).__init__(512, 64, [1, 2, 2, 4, 4, 8], 'nearest',2, [16], codebook_size) + + if fix_modules is not None: + for module in fix_modules: + for param in getattr(self, module).parameters(): + param.requires_grad = False + + self.connect_list = connect_list + self.n_layers = n_layers + self.dim_embd = dim_embd + self.dim_mlp = dim_embd*2 + + self.position_emb = nn.Parameter(torch.zeros(latent_size, self.dim_embd)) + self.feat_emb = nn.Linear(256, self.dim_embd) + + # transformer + self.ft_layers = nn.Sequential(*[TransformerSALayer(embed_dim=dim_embd, nhead=n_head, dim_mlp=self.dim_mlp, dropout=0.0) + for _ in range(self.n_layers)]) + + # logits_predict head + self.idx_pred_layer = nn.Sequential( + nn.LayerNorm(dim_embd), + nn.Linear(dim_embd, codebook_size, bias=False)) + + self.channels = { + '16': 512, + '32': 256, + '64': 256, + '128': 128, + '256': 128, + '512': 64, + } + + # after second residual block for > 16, before attn layer for ==16 + self.fuse_encoder_block = {'512':2, '256':5, '128':8, '64':11, '32':14, '16':18} + # after first residual block for > 16, before attn layer for ==16 + self.fuse_generator_block = {'16':6, '32': 9, '64':12, '128':15, '256':18, '512':21} + + # fuse_convs_dict + self.fuse_convs_dict = nn.ModuleDict() + for f_size in self.connect_list: + in_ch = self.channels[f_size] + self.fuse_convs_dict[f_size] = Fuse_sft_block(in_ch, in_ch) + + def _init_weights(self, module): + if isinstance(module, (nn.Linear, nn.Embedding)): + module.weight.data.normal_(mean=0.0, std=0.02) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + + def forward(self, x, w=0, detach_16=True, code_only=False, adain=False): + # ################### Encoder ##################### + enc_feat_dict = {} + out_list = [self.fuse_encoder_block[f_size] for f_size in self.connect_list] + for i, block in enumerate(self.encoder.blocks): + x = block(x) + if i in out_list: + enc_feat_dict[str(x.shape[-1])] = x.clone() + + lq_feat = x + # ################# Transformer ################### + # quant_feat, codebook_loss, quant_stats = self.quantize(lq_feat) + pos_emb = self.position_emb.unsqueeze(1).repeat(1,x.shape[0],1) + # BCHW -> BC(HW) -> (HW)BC + feat_emb = self.feat_emb(lq_feat.flatten(2).permute(2,0,1)) + query_emb = feat_emb + # Transformer encoder + for layer in self.ft_layers: + query_emb = layer(query_emb, query_pos=pos_emb) + + # output logits + logits = self.idx_pred_layer(query_emb) # (hw)bn + logits = logits.permute(1,0,2) # (hw)bn -> b(hw)n + + if code_only: # for training stage II + # logits doesn't need softmax before cross_entropy loss + return logits, lq_feat + + # ################# Quantization ################### + # if self.training: + # quant_feat = torch.einsum('btn,nc->btc', [soft_one_hot, self.quantize.embedding.weight]) + # # b(hw)c -> bc(hw) -> bchw + # quant_feat = quant_feat.permute(0,2,1).view(lq_feat.shape) + # ------------ + soft_one_hot = F.softmax(logits, dim=2) + _, top_idx = torch.topk(soft_one_hot, 1, dim=2) + quant_feat = self.quantize.get_codebook_feat(top_idx, shape=[x.shape[0],16,16,256]) + # preserve gradients + # quant_feat = lq_feat + (quant_feat - lq_feat).detach() + + if detach_16: + quant_feat = quant_feat.detach() # for training stage III + if adain: + quant_feat = adaptive_instance_normalization(quant_feat, lq_feat) + + # ################## Generator #################### + x = quant_feat + fuse_list = [self.fuse_generator_block[f_size] for f_size in self.connect_list] + + for i, block in enumerate(self.generator.blocks): + x = block(x) + if i in fuse_list: # fuse after i-th block + f_size = str(x.shape[-1]) + if w>0: + x = self.fuse_convs_dict[f_size](enc_feat_dict[f_size].detach(), x, w) + out = x + # logits doesn't need softmax before cross_entropy loss + return out, logits, lq_feat \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/basicsr/archs/rrdbnet_arch.py b/sd/stablediffusion/src/codeformer/basicsr/archs/rrdbnet_arch.py new file mode 100644 index 0000000000000000000000000000000000000000..49a2d6c204557cba53ada7550deb587541855cfb --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/archs/rrdbnet_arch.py @@ -0,0 +1,119 @@ +import torch +from torch import nn as nn +from torch.nn import functional as F + +from basicsr.utils.registry import ARCH_REGISTRY +from .arch_util import default_init_weights, make_layer, pixel_unshuffle + + +class ResidualDenseBlock(nn.Module): + """Residual Dense Block. + + Used in RRDB block in ESRGAN. + + Args: + num_feat (int): Channel number of intermediate features. + num_grow_ch (int): Channels for each growth. + """ + + def __init__(self, num_feat=64, num_grow_ch=32): + super(ResidualDenseBlock, self).__init__() + self.conv1 = nn.Conv2d(num_feat, num_grow_ch, 3, 1, 1) + self.conv2 = nn.Conv2d(num_feat + num_grow_ch, num_grow_ch, 3, 1, 1) + self.conv3 = nn.Conv2d(num_feat + 2 * num_grow_ch, num_grow_ch, 3, 1, 1) + self.conv4 = nn.Conv2d(num_feat + 3 * num_grow_ch, num_grow_ch, 3, 1, 1) + self.conv5 = nn.Conv2d(num_feat + 4 * num_grow_ch, num_feat, 3, 1, 1) + + self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) + + # initialization + default_init_weights([self.conv1, self.conv2, self.conv3, self.conv4, self.conv5], 0.1) + + def forward(self, x): + x1 = self.lrelu(self.conv1(x)) + x2 = self.lrelu(self.conv2(torch.cat((x, x1), 1))) + x3 = self.lrelu(self.conv3(torch.cat((x, x1, x2), 1))) + x4 = self.lrelu(self.conv4(torch.cat((x, x1, x2, x3), 1))) + x5 = self.conv5(torch.cat((x, x1, x2, x3, x4), 1)) + # Emperically, we use 0.2 to scale the residual for better performance + return x5 * 0.2 + x + + +class RRDB(nn.Module): + """Residual in Residual Dense Block. + + Used in RRDB-Net in ESRGAN. + + Args: + num_feat (int): Channel number of intermediate features. + num_grow_ch (int): Channels for each growth. + """ + + def __init__(self, num_feat, num_grow_ch=32): + super(RRDB, self).__init__() + self.rdb1 = ResidualDenseBlock(num_feat, num_grow_ch) + self.rdb2 = ResidualDenseBlock(num_feat, num_grow_ch) + self.rdb3 = ResidualDenseBlock(num_feat, num_grow_ch) + + def forward(self, x): + out = self.rdb1(x) + out = self.rdb2(out) + out = self.rdb3(out) + # Emperically, we use 0.2 to scale the residual for better performance + return out * 0.2 + x + + +@ARCH_REGISTRY.register() +class RRDBNet(nn.Module): + """Networks consisting of Residual in Residual Dense Block, which is used + in ESRGAN. + + ESRGAN: Enhanced Super-Resolution Generative Adversarial Networks. + + We extend ESRGAN for scale x2 and scale x1. + Note: This is one option for scale 1, scale 2 in RRDBNet. + We first employ the pixel-unshuffle (an inverse operation of pixelshuffle to reduce the spatial size + and enlarge the channel size before feeding inputs into the main ESRGAN architecture. + + Args: + num_in_ch (int): Channel number of inputs. + num_out_ch (int): Channel number of outputs. + num_feat (int): Channel number of intermediate features. + Default: 64 + num_block (int): Block number in the trunk network. Defaults: 23 + num_grow_ch (int): Channels for each growth. Default: 32. + """ + + def __init__(self, num_in_ch, num_out_ch, scale=4, num_feat=64, num_block=23, num_grow_ch=32): + super(RRDBNet, self).__init__() + self.scale = scale + if scale == 2: + num_in_ch = num_in_ch * 4 + elif scale == 1: + num_in_ch = num_in_ch * 16 + self.conv_first = nn.Conv2d(num_in_ch, num_feat, 3, 1, 1) + self.body = make_layer(RRDB, num_block, num_feat=num_feat, num_grow_ch=num_grow_ch) + self.conv_body = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + # upsample + self.conv_up1 = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + self.conv_up2 = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + self.conv_hr = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + self.conv_last = nn.Conv2d(num_feat, num_out_ch, 3, 1, 1) + + self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) + + def forward(self, x): + if self.scale == 2: + feat = pixel_unshuffle(x, scale=2) + elif self.scale == 1: + feat = pixel_unshuffle(x, scale=4) + else: + feat = x + feat = self.conv_first(feat) + body_feat = self.conv_body(self.body(feat)) + feat = feat + body_feat + # upsample + feat = self.lrelu(self.conv_up1(F.interpolate(feat, scale_factor=2, mode='nearest'))) + feat = self.lrelu(self.conv_up2(F.interpolate(feat, scale_factor=2, mode='nearest'))) + out = self.conv_last(self.lrelu(self.conv_hr(feat))) + return out \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/basicsr/archs/vgg_arch.py b/sd/stablediffusion/src/codeformer/basicsr/archs/vgg_arch.py new file mode 100644 index 0000000000000000000000000000000000000000..23bb0103c8b14ef2588028f7177753db9af62cae --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/archs/vgg_arch.py @@ -0,0 +1,161 @@ +import os +import torch +from collections import OrderedDict +from torch import nn as nn +from torchvision.models import vgg as vgg + +from basicsr.utils.registry import ARCH_REGISTRY + +VGG_PRETRAIN_PATH = 'experiments/pretrained_models/vgg19-dcbb9e9d.pth' +NAMES = { + 'vgg11': [ + 'conv1_1', 'relu1_1', 'pool1', 'conv2_1', 'relu2_1', 'pool2', 'conv3_1', 'relu3_1', 'conv3_2', 'relu3_2', + 'pool3', 'conv4_1', 'relu4_1', 'conv4_2', 'relu4_2', 'pool4', 'conv5_1', 'relu5_1', 'conv5_2', 'relu5_2', + 'pool5' + ], + 'vgg13': [ + 'conv1_1', 'relu1_1', 'conv1_2', 'relu1_2', 'pool1', 'conv2_1', 'relu2_1', 'conv2_2', 'relu2_2', 'pool2', + 'conv3_1', 'relu3_1', 'conv3_2', 'relu3_2', 'pool3', 'conv4_1', 'relu4_1', 'conv4_2', 'relu4_2', 'pool4', + 'conv5_1', 'relu5_1', 'conv5_2', 'relu5_2', 'pool5' + ], + 'vgg16': [ + 'conv1_1', 'relu1_1', 'conv1_2', 'relu1_2', 'pool1', 'conv2_1', 'relu2_1', 'conv2_2', 'relu2_2', 'pool2', + 'conv3_1', 'relu3_1', 'conv3_2', 'relu3_2', 'conv3_3', 'relu3_3', 'pool3', 'conv4_1', 'relu4_1', 'conv4_2', + 'relu4_2', 'conv4_3', 'relu4_3', 'pool4', 'conv5_1', 'relu5_1', 'conv5_2', 'relu5_2', 'conv5_3', 'relu5_3', + 'pool5' + ], + 'vgg19': [ + 'conv1_1', 'relu1_1', 'conv1_2', 'relu1_2', 'pool1', 'conv2_1', 'relu2_1', 'conv2_2', 'relu2_2', 'pool2', + 'conv3_1', 'relu3_1', 'conv3_2', 'relu3_2', 'conv3_3', 'relu3_3', 'conv3_4', 'relu3_4', 'pool3', 'conv4_1', + 'relu4_1', 'conv4_2', 'relu4_2', 'conv4_3', 'relu4_3', 'conv4_4', 'relu4_4', 'pool4', 'conv5_1', 'relu5_1', + 'conv5_2', 'relu5_2', 'conv5_3', 'relu5_3', 'conv5_4', 'relu5_4', 'pool5' + ] +} + + +def insert_bn(names): + """Insert bn layer after each conv. + + Args: + names (list): The list of layer names. + + Returns: + list: The list of layer names with bn layers. + """ + names_bn = [] + for name in names: + names_bn.append(name) + if 'conv' in name: + position = name.replace('conv', '') + names_bn.append('bn' + position) + return names_bn + + +@ARCH_REGISTRY.register() +class VGGFeatureExtractor(nn.Module): + """VGG network for feature extraction. + + In this implementation, we allow users to choose whether use normalization + in the input feature and the type of vgg network. Note that the pretrained + path must fit the vgg type. + + Args: + layer_name_list (list[str]): Forward function returns the corresponding + features according to the layer_name_list. + Example: {'relu1_1', 'relu2_1', 'relu3_1'}. + vgg_type (str): Set the type of vgg network. Default: 'vgg19'. + use_input_norm (bool): If True, normalize the input image. Importantly, + the input feature must in the range [0, 1]. Default: True. + range_norm (bool): If True, norm images with range [-1, 1] to [0, 1]. + Default: False. + requires_grad (bool): If true, the parameters of VGG network will be + optimized. Default: False. + remove_pooling (bool): If true, the max pooling operations in VGG net + will be removed. Default: False. + pooling_stride (int): The stride of max pooling operation. Default: 2. + """ + + def __init__(self, + layer_name_list, + vgg_type='vgg19', + use_input_norm=True, + range_norm=False, + requires_grad=False, + remove_pooling=False, + pooling_stride=2): + super(VGGFeatureExtractor, self).__init__() + + self.layer_name_list = layer_name_list + self.use_input_norm = use_input_norm + self.range_norm = range_norm + + self.names = NAMES[vgg_type.replace('_bn', '')] + if 'bn' in vgg_type: + self.names = insert_bn(self.names) + + # only borrow layers that will be used to avoid unused params + max_idx = 0 + for v in layer_name_list: + idx = self.names.index(v) + if idx > max_idx: + max_idx = idx + + if os.path.exists(VGG_PRETRAIN_PATH): + vgg_net = getattr(vgg, vgg_type)(pretrained=False) + state_dict = torch.load(VGG_PRETRAIN_PATH, map_location=lambda storage, loc: storage) + vgg_net.load_state_dict(state_dict) + else: + vgg_net = getattr(vgg, vgg_type)(pretrained=True) + + features = vgg_net.features[:max_idx + 1] + + modified_net = OrderedDict() + for k, v in zip(self.names, features): + if 'pool' in k: + # if remove_pooling is true, pooling operation will be removed + if remove_pooling: + continue + else: + # in some cases, we may want to change the default stride + modified_net[k] = nn.MaxPool2d(kernel_size=2, stride=pooling_stride) + else: + modified_net[k] = v + + self.vgg_net = nn.Sequential(modified_net) + + if not requires_grad: + self.vgg_net.eval() + for param in self.parameters(): + param.requires_grad = False + else: + self.vgg_net.train() + for param in self.parameters(): + param.requires_grad = True + + if self.use_input_norm: + # the mean is for image with range [0, 1] + self.register_buffer('mean', torch.Tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1)) + # the std is for image with range [0, 1] + self.register_buffer('std', torch.Tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1)) + + def forward(self, x): + """Forward function. + + Args: + x (Tensor): Input tensor with shape (n, c, h, w). + + Returns: + Tensor: Forward results. + """ + if self.range_norm: + x = (x + 1) / 2 + if self.use_input_norm: + x = (x - self.mean) / self.std + output = {} + + for key, layer in self.vgg_net._modules.items(): + x = layer(x) + if key in self.layer_name_list: + output[key] = x.clone() + + return output diff --git a/sd/stablediffusion/src/codeformer/basicsr/archs/vqgan_arch.py b/sd/stablediffusion/src/codeformer/basicsr/archs/vqgan_arch.py new file mode 100644 index 0000000000000000000000000000000000000000..5ac692633ae67c616a8588ee8f7f41dcf4fc35f4 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/archs/vqgan_arch.py @@ -0,0 +1,434 @@ +''' +VQGAN code, adapted from the original created by the Unleashing Transformers authors: +https://github.com/samb-t/unleashing-transformers/blob/master/models/vqgan.py + +''' +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +import copy +from basicsr.utils import get_root_logger +from basicsr.utils.registry import ARCH_REGISTRY + +def normalize(in_channels): + return torch.nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True) + + +@torch.jit.script +def swish(x): + return x*torch.sigmoid(x) + + +# Define VQVAE classes +class VectorQuantizer(nn.Module): + def __init__(self, codebook_size, emb_dim, beta): + super(VectorQuantizer, self).__init__() + self.codebook_size = codebook_size # number of embeddings + self.emb_dim = emb_dim # dimension of embedding + self.beta = beta # commitment cost used in loss term, beta * ||z_e(x)-sg[e]||^2 + self.embedding = nn.Embedding(self.codebook_size, self.emb_dim) + self.embedding.weight.data.uniform_(-1.0 / self.codebook_size, 1.0 / self.codebook_size) + + def forward(self, z): + # reshape z -> (batch, height, width, channel) and flatten + z = z.permute(0, 2, 3, 1).contiguous() + z_flattened = z.view(-1, self.emb_dim) + + # distances from z to embeddings e_j (z - e)^2 = z^2 + e^2 - 2 e * z + d = (z_flattened ** 2).sum(dim=1, keepdim=True) + (self.embedding.weight**2).sum(1) - \ + 2 * torch.matmul(z_flattened, self.embedding.weight.t()) + + mean_distance = torch.mean(d) + # find closest encodings + min_encoding_indices = torch.argmin(d, dim=1).unsqueeze(1) + # min_encoding_scores, min_encoding_indices = torch.topk(d, 1, dim=1, largest=False) + # [0-1], higher score, higher confidence + # min_encoding_scores = torch.exp(-min_encoding_scores/10) + + min_encodings = torch.zeros(min_encoding_indices.shape[0], self.codebook_size).to(z) + min_encodings.scatter_(1, min_encoding_indices, 1) + + # get quantized latent vectors + z_q = torch.matmul(min_encodings, self.embedding.weight).view(z.shape) + # compute loss for embedding + loss = torch.mean((z_q.detach()-z)**2) + self.beta * torch.mean((z_q - z.detach()) ** 2) + # preserve gradients + z_q = z + (z_q - z).detach() + + # perplexity + e_mean = torch.mean(min_encodings, dim=0) + perplexity = torch.exp(-torch.sum(e_mean * torch.log(e_mean + 1e-10))) + # reshape back to match original input shape + z_q = z_q.permute(0, 3, 1, 2).contiguous() + + return z_q, loss, { + "perplexity": perplexity, + "min_encodings": min_encodings, + "min_encoding_indices": min_encoding_indices, + "mean_distance": mean_distance + } + + def get_codebook_feat(self, indices, shape): + # input indices: batch*token_num -> (batch*token_num)*1 + # shape: batch, height, width, channel + indices = indices.view(-1,1) + min_encodings = torch.zeros(indices.shape[0], self.codebook_size).to(indices) + min_encodings.scatter_(1, indices, 1) + # get quantized latent vectors + z_q = torch.matmul(min_encodings.float(), self.embedding.weight) + + if shape is not None: # reshape back to match original input shape + z_q = z_q.view(shape).permute(0, 3, 1, 2).contiguous() + + return z_q + + +class GumbelQuantizer(nn.Module): + def __init__(self, codebook_size, emb_dim, num_hiddens, straight_through=False, kl_weight=5e-4, temp_init=1.0): + super().__init__() + self.codebook_size = codebook_size # number of embeddings + self.emb_dim = emb_dim # dimension of embedding + self.straight_through = straight_through + self.temperature = temp_init + self.kl_weight = kl_weight + self.proj = nn.Conv2d(num_hiddens, codebook_size, 1) # projects last encoder layer to quantized logits + self.embed = nn.Embedding(codebook_size, emb_dim) + + def forward(self, z): + hard = self.straight_through if self.training else True + + logits = self.proj(z) + + soft_one_hot = F.gumbel_softmax(logits, tau=self.temperature, dim=1, hard=hard) + + z_q = torch.einsum("b n h w, n d -> b d h w", soft_one_hot, self.embed.weight) + + # + kl divergence to the prior loss + qy = F.softmax(logits, dim=1) + diff = self.kl_weight * torch.sum(qy * torch.log(qy * self.codebook_size + 1e-10), dim=1).mean() + min_encoding_indices = soft_one_hot.argmax(dim=1) + + return z_q, diff, { + "min_encoding_indices": min_encoding_indices + } + + +class Downsample(nn.Module): + def __init__(self, in_channels): + super().__init__() + self.conv = torch.nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=2, padding=0) + + def forward(self, x): + pad = (0, 1, 0, 1) + x = torch.nn.functional.pad(x, pad, mode="constant", value=0) + x = self.conv(x) + return x + + +class Upsample(nn.Module): + def __init__(self, in_channels): + super().__init__() + self.conv = nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=1, padding=1) + + def forward(self, x): + x = F.interpolate(x, scale_factor=2.0, mode="nearest") + x = self.conv(x) + + return x + + +class ResBlock(nn.Module): + def __init__(self, in_channels, out_channels=None): + super(ResBlock, self).__init__() + self.in_channels = in_channels + self.out_channels = in_channels if out_channels is None else out_channels + self.norm1 = normalize(in_channels) + self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1) + self.norm2 = normalize(out_channels) + self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1) + if self.in_channels != self.out_channels: + self.conv_out = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0) + + def forward(self, x_in): + x = x_in + x = self.norm1(x) + x = swish(x) + x = self.conv1(x) + x = self.norm2(x) + x = swish(x) + x = self.conv2(x) + if self.in_channels != self.out_channels: + x_in = self.conv_out(x_in) + + return x + x_in + + +class AttnBlock(nn.Module): + def __init__(self, in_channels): + super().__init__() + self.in_channels = in_channels + + self.norm = normalize(in_channels) + self.q = torch.nn.Conv2d( + in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0 + ) + self.k = torch.nn.Conv2d( + in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0 + ) + self.v = torch.nn.Conv2d( + in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0 + ) + self.proj_out = torch.nn.Conv2d( + in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0 + ) + + def forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + # compute attention + b, c, h, w = q.shape + q = q.reshape(b, c, h*w) + q = q.permute(0, 2, 1) + k = k.reshape(b, c, h*w) + w_ = torch.bmm(q, k) + w_ = w_ * (int(c)**(-0.5)) + w_ = F.softmax(w_, dim=2) + + # attend to values + v = v.reshape(b, c, h*w) + w_ = w_.permute(0, 2, 1) + h_ = torch.bmm(v, w_) + h_ = h_.reshape(b, c, h, w) + + h_ = self.proj_out(h_) + + return x+h_ + + +class Encoder(nn.Module): + def __init__(self, in_channels, nf, emb_dim, ch_mult, num_res_blocks, resolution, attn_resolutions): + super().__init__() + self.nf = nf + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.attn_resolutions = attn_resolutions + + curr_res = self.resolution + in_ch_mult = (1,)+tuple(ch_mult) + + blocks = [] + # initial convultion + blocks.append(nn.Conv2d(in_channels, nf, kernel_size=3, stride=1, padding=1)) + + # residual and downsampling blocks, with attention on smaller res (16x16) + for i in range(self.num_resolutions): + block_in_ch = nf * in_ch_mult[i] + block_out_ch = nf * ch_mult[i] + for _ in range(self.num_res_blocks): + blocks.append(ResBlock(block_in_ch, block_out_ch)) + block_in_ch = block_out_ch + if curr_res in attn_resolutions: + blocks.append(AttnBlock(block_in_ch)) + + if i != self.num_resolutions - 1: + blocks.append(Downsample(block_in_ch)) + curr_res = curr_res // 2 + + # non-local attention block + blocks.append(ResBlock(block_in_ch, block_in_ch)) + blocks.append(AttnBlock(block_in_ch)) + blocks.append(ResBlock(block_in_ch, block_in_ch)) + + # normalise and convert to latent size + blocks.append(normalize(block_in_ch)) + blocks.append(nn.Conv2d(block_in_ch, emb_dim, kernel_size=3, stride=1, padding=1)) + self.blocks = nn.ModuleList(blocks) + + def forward(self, x): + for block in self.blocks: + x = block(x) + + return x + + +class Generator(nn.Module): + def __init__(self, nf, emb_dim, ch_mult, res_blocks, img_size, attn_resolutions): + super().__init__() + self.nf = nf + self.ch_mult = ch_mult + self.num_resolutions = len(self.ch_mult) + self.num_res_blocks = res_blocks + self.resolution = img_size + self.attn_resolutions = attn_resolutions + self.in_channels = emb_dim + self.out_channels = 3 + block_in_ch = self.nf * self.ch_mult[-1] + curr_res = self.resolution // 2 ** (self.num_resolutions-1) + + blocks = [] + # initial conv + blocks.append(nn.Conv2d(self.in_channels, block_in_ch, kernel_size=3, stride=1, padding=1)) + + # non-local attention block + blocks.append(ResBlock(block_in_ch, block_in_ch)) + blocks.append(AttnBlock(block_in_ch)) + blocks.append(ResBlock(block_in_ch, block_in_ch)) + + for i in reversed(range(self.num_resolutions)): + block_out_ch = self.nf * self.ch_mult[i] + + for _ in range(self.num_res_blocks): + blocks.append(ResBlock(block_in_ch, block_out_ch)) + block_in_ch = block_out_ch + + if curr_res in self.attn_resolutions: + blocks.append(AttnBlock(block_in_ch)) + + if i != 0: + blocks.append(Upsample(block_in_ch)) + curr_res = curr_res * 2 + + blocks.append(normalize(block_in_ch)) + blocks.append(nn.Conv2d(block_in_ch, self.out_channels, kernel_size=3, stride=1, padding=1)) + + self.blocks = nn.ModuleList(blocks) + + + def forward(self, x): + for block in self.blocks: + x = block(x) + + return x + + +@ARCH_REGISTRY.register() +class VQAutoEncoder(nn.Module): + def __init__(self, img_size, nf, ch_mult, quantizer="nearest", res_blocks=2, attn_resolutions=[16], codebook_size=1024, emb_dim=256, + beta=0.25, gumbel_straight_through=False, gumbel_kl_weight=1e-8, model_path=None): + super().__init__() + logger = get_root_logger() + self.in_channels = 3 + self.nf = nf + self.n_blocks = res_blocks + self.codebook_size = codebook_size + self.embed_dim = emb_dim + self.ch_mult = ch_mult + self.resolution = img_size + self.attn_resolutions = attn_resolutions + self.quantizer_type = quantizer + self.encoder = Encoder( + self.in_channels, + self.nf, + self.embed_dim, + self.ch_mult, + self.n_blocks, + self.resolution, + self.attn_resolutions + ) + if self.quantizer_type == "nearest": + self.beta = beta #0.25 + self.quantize = VectorQuantizer(self.codebook_size, self.embed_dim, self.beta) + elif self.quantizer_type == "gumbel": + self.gumbel_num_hiddens = emb_dim + self.straight_through = gumbel_straight_through + self.kl_weight = gumbel_kl_weight + self.quantize = GumbelQuantizer( + self.codebook_size, + self.embed_dim, + self.gumbel_num_hiddens, + self.straight_through, + self.kl_weight + ) + self.generator = Generator( + self.nf, + self.embed_dim, + self.ch_mult, + self.n_blocks, + self.resolution, + self.attn_resolutions + ) + + if model_path is not None: + chkpt = torch.load(model_path, map_location='cpu') + if 'params_ema' in chkpt: + self.load_state_dict(torch.load(model_path, map_location='cpu')['params_ema']) + logger.info(f'vqgan is loaded from: {model_path} [params_ema]') + elif 'params' in chkpt: + self.load_state_dict(torch.load(model_path, map_location='cpu')['params']) + logger.info(f'vqgan is loaded from: {model_path} [params]') + else: + raise ValueError(f'Wrong params!') + + + def forward(self, x): + x = self.encoder(x) + quant, codebook_loss, quant_stats = self.quantize(x) + x = self.generator(quant) + return x, codebook_loss, quant_stats + + + +# patch based discriminator +@ARCH_REGISTRY.register() +class VQGANDiscriminator(nn.Module): + def __init__(self, nc=3, ndf=64, n_layers=4, model_path=None): + super().__init__() + + layers = [nn.Conv2d(nc, ndf, kernel_size=4, stride=2, padding=1), nn.LeakyReLU(0.2, True)] + ndf_mult = 1 + ndf_mult_prev = 1 + for n in range(1, n_layers): # gradually increase the number of filters + ndf_mult_prev = ndf_mult + ndf_mult = min(2 ** n, 8) + layers += [ + nn.Conv2d(ndf * ndf_mult_prev, ndf * ndf_mult, kernel_size=4, stride=2, padding=1, bias=False), + nn.BatchNorm2d(ndf * ndf_mult), + nn.LeakyReLU(0.2, True) + ] + + ndf_mult_prev = ndf_mult + ndf_mult = min(2 ** n_layers, 8) + + layers += [ + nn.Conv2d(ndf * ndf_mult_prev, ndf * ndf_mult, kernel_size=4, stride=1, padding=1, bias=False), + nn.BatchNorm2d(ndf * ndf_mult), + nn.LeakyReLU(0.2, True) + ] + + layers += [ + nn.Conv2d(ndf * ndf_mult, 1, kernel_size=4, stride=1, padding=1)] # output 1 channel prediction map + self.main = nn.Sequential(*layers) + + if model_path is not None: + chkpt = torch.load(model_path, map_location='cpu') + if 'params_d' in chkpt: + self.load_state_dict(torch.load(model_path, map_location='cpu')['params_d']) + elif 'params' in chkpt: + self.load_state_dict(torch.load(model_path, map_location='cpu')['params']) + else: + raise ValueError(f'Wrong params!') + + def forward(self, x): + return self.main(x) \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/basicsr/data/__init__.py b/sd/stablediffusion/src/codeformer/basicsr/data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c6adb4bb6a926af7a46aaec4794eee95fda02a33 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/data/__init__.py @@ -0,0 +1,100 @@ +import importlib +import numpy as np +import random +import torch +import torch.utils.data +from copy import deepcopy +from functools import partial +from os import path as osp + +from basicsr.data.prefetch_dataloader import PrefetchDataLoader +from basicsr.utils import get_root_logger, scandir +from basicsr.utils.dist_util import get_dist_info +from basicsr.utils.registry import DATASET_REGISTRY + +__all__ = ['build_dataset', 'build_dataloader'] + +# automatically scan and import dataset modules for registry +# scan all the files under the data folder with '_dataset' in file names +data_folder = osp.dirname(osp.abspath(__file__)) +dataset_filenames = [osp.splitext(osp.basename(v))[0] for v in scandir(data_folder) if v.endswith('_dataset.py')] +# import all the dataset modules +_dataset_modules = [importlib.import_module(f'basicsr.data.{file_name}') for file_name in dataset_filenames] + + +def build_dataset(dataset_opt): + """Build dataset from options. + + Args: + dataset_opt (dict): Configuration for dataset. It must constain: + name (str): Dataset name. + type (str): Dataset type. + """ + dataset_opt = deepcopy(dataset_opt) + dataset = DATASET_REGISTRY.get(dataset_opt['type'])(dataset_opt) + logger = get_root_logger() + logger.info(f'Dataset [{dataset.__class__.__name__}] - {dataset_opt["name"]} ' 'is built.') + return dataset + + +def build_dataloader(dataset, dataset_opt, num_gpu=1, dist=False, sampler=None, seed=None): + """Build dataloader. + + Args: + dataset (torch.utils.data.Dataset): Dataset. + dataset_opt (dict): Dataset options. It contains the following keys: + phase (str): 'train' or 'val'. + num_worker_per_gpu (int): Number of workers for each GPU. + batch_size_per_gpu (int): Training batch size for each GPU. + num_gpu (int): Number of GPUs. Used only in the train phase. + Default: 1. + dist (bool): Whether in distributed training. Used only in the train + phase. Default: False. + sampler (torch.utils.data.sampler): Data sampler. Default: None. + seed (int | None): Seed. Default: None + """ + phase = dataset_opt['phase'] + rank, _ = get_dist_info() + if phase == 'train': + if dist: # distributed training + batch_size = dataset_opt['batch_size_per_gpu'] + num_workers = dataset_opt['num_worker_per_gpu'] + else: # non-distributed training + multiplier = 1 if num_gpu == 0 else num_gpu + batch_size = dataset_opt['batch_size_per_gpu'] * multiplier + num_workers = dataset_opt['num_worker_per_gpu'] * multiplier + dataloader_args = dict( + dataset=dataset, + batch_size=batch_size, + shuffle=False, + num_workers=num_workers, + sampler=sampler, + drop_last=True) + if sampler is None: + dataloader_args['shuffle'] = True + dataloader_args['worker_init_fn'] = partial( + worker_init_fn, num_workers=num_workers, rank=rank, seed=seed) if seed is not None else None + elif phase in ['val', 'test']: # validation + dataloader_args = dict(dataset=dataset, batch_size=1, shuffle=False, num_workers=0) + else: + raise ValueError(f'Wrong dataset phase: {phase}. ' "Supported ones are 'train', 'val' and 'test'.") + + dataloader_args['pin_memory'] = dataset_opt.get('pin_memory', False) + + prefetch_mode = dataset_opt.get('prefetch_mode') + if prefetch_mode == 'cpu': # CPUPrefetcher + num_prefetch_queue = dataset_opt.get('num_prefetch_queue', 1) + logger = get_root_logger() + logger.info(f'Use {prefetch_mode} prefetch dataloader: ' f'num_prefetch_queue = {num_prefetch_queue}') + return PrefetchDataLoader(num_prefetch_queue=num_prefetch_queue, **dataloader_args) + else: + # prefetch_mode=None: Normal dataloader + # prefetch_mode='cuda': dataloader for CUDAPrefetcher + return torch.utils.data.DataLoader(**dataloader_args) + + +def worker_init_fn(worker_id, num_workers, rank, seed): + # Set the worker seed to num_workers * rank + worker_id + seed + worker_seed = num_workers * rank + worker_id + seed + np.random.seed(worker_seed) + random.seed(worker_seed) diff --git a/sd/stablediffusion/src/codeformer/basicsr/data/data_sampler.py b/sd/stablediffusion/src/codeformer/basicsr/data/data_sampler.py new file mode 100644 index 0000000000000000000000000000000000000000..575452d9f844a928f7f42296c81635cfbadec7c2 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/data/data_sampler.py @@ -0,0 +1,48 @@ +import math +import torch +from torch.utils.data.sampler import Sampler + + +class EnlargedSampler(Sampler): + """Sampler that restricts data loading to a subset of the dataset. + + Modified from torch.utils.data.distributed.DistributedSampler + Support enlarging the dataset for iteration-based training, for saving + time when restart the dataloader after each epoch + + Args: + dataset (torch.utils.data.Dataset): Dataset used for sampling. + num_replicas (int | None): Number of processes participating in + the training. It is usually the world_size. + rank (int | None): Rank of the current process within num_replicas. + ratio (int): Enlarging ratio. Default: 1. + """ + + def __init__(self, dataset, num_replicas, rank, ratio=1): + self.dataset = dataset + self.num_replicas = num_replicas + self.rank = rank + self.epoch = 0 + self.num_samples = math.ceil(len(self.dataset) * ratio / self.num_replicas) + self.total_size = self.num_samples * self.num_replicas + + def __iter__(self): + # deterministically shuffle based on epoch + g = torch.Generator() + g.manual_seed(self.epoch) + indices = torch.randperm(self.total_size, generator=g).tolist() + + dataset_size = len(self.dataset) + indices = [v % dataset_size for v in indices] + + # subsample + indices = indices[self.rank:self.total_size:self.num_replicas] + assert len(indices) == self.num_samples + + return iter(indices) + + def __len__(self): + return self.num_samples + + def set_epoch(self, epoch): + self.epoch = epoch diff --git a/sd/stablediffusion/src/codeformer/basicsr/data/data_util.py b/sd/stablediffusion/src/codeformer/basicsr/data/data_util.py new file mode 100644 index 0000000000000000000000000000000000000000..63b1bce8e089485182c962e830a163d6d0059da8 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/data/data_util.py @@ -0,0 +1,305 @@ +import cv2 +import numpy as np +import torch +from os import path as osp +from torch.nn import functional as F + +from basicsr.data.transforms import mod_crop +from basicsr.utils import img2tensor, scandir + + +def read_img_seq(path, require_mod_crop=False, scale=1): + """Read a sequence of images from a given folder path. + + Args: + path (list[str] | str): List of image paths or image folder path. + require_mod_crop (bool): Require mod crop for each image. + Default: False. + scale (int): Scale factor for mod_crop. Default: 1. + + Returns: + Tensor: size (t, c, h, w), RGB, [0, 1]. + """ + if isinstance(path, list): + img_paths = path + else: + img_paths = sorted(list(scandir(path, full_path=True))) + imgs = [cv2.imread(v).astype(np.float32) / 255. for v in img_paths] + if require_mod_crop: + imgs = [mod_crop(img, scale) for img in imgs] + imgs = img2tensor(imgs, bgr2rgb=True, float32=True) + imgs = torch.stack(imgs, dim=0) + return imgs + + +def generate_frame_indices(crt_idx, max_frame_num, num_frames, padding='reflection'): + """Generate an index list for reading `num_frames` frames from a sequence + of images. + + Args: + crt_idx (int): Current center index. + max_frame_num (int): Max number of the sequence of images (from 1). + num_frames (int): Reading num_frames frames. + padding (str): Padding mode, one of + 'replicate' | 'reflection' | 'reflection_circle' | 'circle' + Examples: current_idx = 0, num_frames = 5 + The generated frame indices under different padding mode: + replicate: [0, 0, 0, 1, 2] + reflection: [2, 1, 0, 1, 2] + reflection_circle: [4, 3, 0, 1, 2] + circle: [3, 4, 0, 1, 2] + + Returns: + list[int]: A list of indices. + """ + assert num_frames % 2 == 1, 'num_frames should be an odd number.' + assert padding in ('replicate', 'reflection', 'reflection_circle', 'circle'), f'Wrong padding mode: {padding}.' + + max_frame_num = max_frame_num - 1 # start from 0 + num_pad = num_frames // 2 + + indices = [] + for i in range(crt_idx - num_pad, crt_idx + num_pad + 1): + if i < 0: + if padding == 'replicate': + pad_idx = 0 + elif padding == 'reflection': + pad_idx = -i + elif padding == 'reflection_circle': + pad_idx = crt_idx + num_pad - i + else: + pad_idx = num_frames + i + elif i > max_frame_num: + if padding == 'replicate': + pad_idx = max_frame_num + elif padding == 'reflection': + pad_idx = max_frame_num * 2 - i + elif padding == 'reflection_circle': + pad_idx = (crt_idx - num_pad) - (i - max_frame_num) + else: + pad_idx = i - num_frames + else: + pad_idx = i + indices.append(pad_idx) + return indices + + +def paired_paths_from_lmdb(folders, keys): + """Generate paired paths from lmdb files. + + Contents of lmdb. Taking the `lq.lmdb` for example, the file structure is: + + lq.lmdb + ├── data.mdb + ├── lock.mdb + ├── meta_info.txt + + The data.mdb and lock.mdb are standard lmdb files and you can refer to + https://lmdb.readthedocs.io/en/release/ for more details. + + The meta_info.txt is a specified txt file to record the meta information + of our datasets. It will be automatically created when preparing + datasets by our provided dataset tools. + Each line in the txt file records + 1)image name (with extension), + 2)image shape, + 3)compression level, separated by a white space. + Example: `baboon.png (120,125,3) 1` + + We use the image name without extension as the lmdb key. + Note that we use the same key for the corresponding lq and gt images. + + Args: + folders (list[str]): A list of folder path. The order of list should + be [input_folder, gt_folder]. + keys (list[str]): A list of keys identifying folders. The order should + be in consistent with folders, e.g., ['lq', 'gt']. + Note that this key is different from lmdb keys. + + Returns: + list[str]: Returned path list. + """ + assert len(folders) == 2, ('The len of folders should be 2 with [input_folder, gt_folder]. ' + f'But got {len(folders)}') + assert len(keys) == 2, ('The len of keys should be 2 with [input_key, gt_key]. ' f'But got {len(keys)}') + input_folder, gt_folder = folders + input_key, gt_key = keys + + if not (input_folder.endswith('.lmdb') and gt_folder.endswith('.lmdb')): + raise ValueError(f'{input_key} folder and {gt_key} folder should both in lmdb ' + f'formats. But received {input_key}: {input_folder}; ' + f'{gt_key}: {gt_folder}') + # ensure that the two meta_info files are the same + with open(osp.join(input_folder, 'meta_info.txt')) as fin: + input_lmdb_keys = [line.split('.')[0] for line in fin] + with open(osp.join(gt_folder, 'meta_info.txt')) as fin: + gt_lmdb_keys = [line.split('.')[0] for line in fin] + if set(input_lmdb_keys) != set(gt_lmdb_keys): + raise ValueError(f'Keys in {input_key}_folder and {gt_key}_folder are different.') + else: + paths = [] + for lmdb_key in sorted(input_lmdb_keys): + paths.append(dict([(f'{input_key}_path', lmdb_key), (f'{gt_key}_path', lmdb_key)])) + return paths + + +def paired_paths_from_meta_info_file(folders, keys, meta_info_file, filename_tmpl): + """Generate paired paths from an meta information file. + + Each line in the meta information file contains the image names and + image shape (usually for gt), separated by a white space. + + Example of an meta information file: + ``` + 0001_s001.png (480,480,3) + 0001_s002.png (480,480,3) + ``` + + Args: + folders (list[str]): A list of folder path. The order of list should + be [input_folder, gt_folder]. + keys (list[str]): A list of keys identifying folders. The order should + be in consistent with folders, e.g., ['lq', 'gt']. + meta_info_file (str): Path to the meta information file. + filename_tmpl (str): Template for each filename. Note that the + template excludes the file extension. Usually the filename_tmpl is + for files in the input folder. + + Returns: + list[str]: Returned path list. + """ + assert len(folders) == 2, ('The len of folders should be 2 with [input_folder, gt_folder]. ' + f'But got {len(folders)}') + assert len(keys) == 2, ('The len of keys should be 2 with [input_key, gt_key]. ' f'But got {len(keys)}') + input_folder, gt_folder = folders + input_key, gt_key = keys + + with open(meta_info_file, 'r') as fin: + gt_names = [line.split(' ')[0] for line in fin] + + paths = [] + for gt_name in gt_names: + basename, ext = osp.splitext(osp.basename(gt_name)) + input_name = f'{filename_tmpl.format(basename)}{ext}' + input_path = osp.join(input_folder, input_name) + gt_path = osp.join(gt_folder, gt_name) + paths.append(dict([(f'{input_key}_path', input_path), (f'{gt_key}_path', gt_path)])) + return paths + + +def paired_paths_from_folder(folders, keys, filename_tmpl): + """Generate paired paths from folders. + + Args: + folders (list[str]): A list of folder path. The order of list should + be [input_folder, gt_folder]. + keys (list[str]): A list of keys identifying folders. The order should + be in consistent with folders, e.g., ['lq', 'gt']. + filename_tmpl (str): Template for each filename. Note that the + template excludes the file extension. Usually the filename_tmpl is + for files in the input folder. + + Returns: + list[str]: Returned path list. + """ + assert len(folders) == 2, ('The len of folders should be 2 with [input_folder, gt_folder]. ' + f'But got {len(folders)}') + assert len(keys) == 2, ('The len of keys should be 2 with [input_key, gt_key]. ' f'But got {len(keys)}') + input_folder, gt_folder = folders + input_key, gt_key = keys + + input_paths = list(scandir(input_folder)) + gt_paths = list(scandir(gt_folder)) + assert len(input_paths) == len(gt_paths), (f'{input_key} and {gt_key} datasets have different number of images: ' + f'{len(input_paths)}, {len(gt_paths)}.') + paths = [] + for gt_path in gt_paths: + basename, ext = osp.splitext(osp.basename(gt_path)) + input_name = f'{filename_tmpl.format(basename)}{ext}' + input_path = osp.join(input_folder, input_name) + assert input_name in input_paths, (f'{input_name} is not in ' f'{input_key}_paths.') + gt_path = osp.join(gt_folder, gt_path) + paths.append(dict([(f'{input_key}_path', input_path), (f'{gt_key}_path', gt_path)])) + return paths + + +def paths_from_folder(folder): + """Generate paths from folder. + + Args: + folder (str): Folder path. + + Returns: + list[str]: Returned path list. + """ + + paths = list(scandir(folder)) + paths = [osp.join(folder, path) for path in paths] + return paths + + +def paths_from_lmdb(folder): + """Generate paths from lmdb. + + Args: + folder (str): Folder path. + + Returns: + list[str]: Returned path list. + """ + if not folder.endswith('.lmdb'): + raise ValueError(f'Folder {folder}folder should in lmdb format.') + with open(osp.join(folder, 'meta_info.txt')) as fin: + paths = [line.split('.')[0] for line in fin] + return paths + + +def generate_gaussian_kernel(kernel_size=13, sigma=1.6): + """Generate Gaussian kernel used in `duf_downsample`. + + Args: + kernel_size (int): Kernel size. Default: 13. + sigma (float): Sigma of the Gaussian kernel. Default: 1.6. + + Returns: + np.array: The Gaussian kernel. + """ + from scipy.ndimage import filters as filters + kernel = np.zeros((kernel_size, kernel_size)) + # set element at the middle to one, a dirac delta + kernel[kernel_size // 2, kernel_size // 2] = 1 + # gaussian-smooth the dirac, resulting in a gaussian filter + return filters.gaussian_filter(kernel, sigma) + + +def duf_downsample(x, kernel_size=13, scale=4): + """Downsamping with Gaussian kernel used in the DUF official code. + + Args: + x (Tensor): Frames to be downsampled, with shape (b, t, c, h, w). + kernel_size (int): Kernel size. Default: 13. + scale (int): Downsampling factor. Supported scale: (2, 3, 4). + Default: 4. + + Returns: + Tensor: DUF downsampled frames. + """ + assert scale in (2, 3, 4), f'Only support scale (2, 3, 4), but got {scale}.' + + squeeze_flag = False + if x.ndim == 4: + squeeze_flag = True + x = x.unsqueeze(0) + b, t, c, h, w = x.size() + x = x.view(-1, 1, h, w) + pad_w, pad_h = kernel_size // 2 + scale * 2, kernel_size // 2 + scale * 2 + x = F.pad(x, (pad_w, pad_w, pad_h, pad_h), 'reflect') + + gaussian_filter = generate_gaussian_kernel(kernel_size, 0.4 * scale) + gaussian_filter = torch.from_numpy(gaussian_filter).type_as(x).unsqueeze(0).unsqueeze(0) + x = F.conv2d(x, gaussian_filter, stride=scale) + x = x[:, :, 2:-2, 2:-2] + x = x.view(b, t, c, x.size(2), x.size(3)) + if squeeze_flag: + x = x.squeeze(0) + return x diff --git a/sd/stablediffusion/src/codeformer/basicsr/data/prefetch_dataloader.py b/sd/stablediffusion/src/codeformer/basicsr/data/prefetch_dataloader.py new file mode 100644 index 0000000000000000000000000000000000000000..5088425050d4cc98114a9b93eb50ea60273f35a0 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/data/prefetch_dataloader.py @@ -0,0 +1,125 @@ +import queue as Queue +import threading +import torch +from torch.utils.data import DataLoader + + +class PrefetchGenerator(threading.Thread): + """A general prefetch generator. + + Ref: + https://stackoverflow.com/questions/7323664/python-generator-pre-fetch + + Args: + generator: Python generator. + num_prefetch_queue (int): Number of prefetch queue. + """ + + def __init__(self, generator, num_prefetch_queue): + threading.Thread.__init__(self) + self.queue = Queue.Queue(num_prefetch_queue) + self.generator = generator + self.daemon = True + self.start() + + def run(self): + for item in self.generator: + self.queue.put(item) + self.queue.put(None) + + def __next__(self): + next_item = self.queue.get() + if next_item is None: + raise StopIteration + return next_item + + def __iter__(self): + return self + + +class PrefetchDataLoader(DataLoader): + """Prefetch version of dataloader. + + Ref: + https://github.com/IgorSusmelj/pytorch-styleguide/issues/5# + + TODO: + Need to test on single gpu and ddp (multi-gpu). There is a known issue in + ddp. + + Args: + num_prefetch_queue (int): Number of prefetch queue. + kwargs (dict): Other arguments for dataloader. + """ + + def __init__(self, num_prefetch_queue, **kwargs): + self.num_prefetch_queue = num_prefetch_queue + super(PrefetchDataLoader, self).__init__(**kwargs) + + def __iter__(self): + return PrefetchGenerator(super().__iter__(), self.num_prefetch_queue) + + +class CPUPrefetcher(): + """CPU prefetcher. + + Args: + loader: Dataloader. + """ + + def __init__(self, loader): + self.ori_loader = loader + self.loader = iter(loader) + + def next(self): + try: + return next(self.loader) + except StopIteration: + return None + + def reset(self): + self.loader = iter(self.ori_loader) + + +class CUDAPrefetcher(): + """CUDA prefetcher. + + Ref: + https://github.com/NVIDIA/apex/issues/304# + + It may consums more GPU memory. + + Args: + loader: Dataloader. + opt (dict): Options. + """ + + def __init__(self, loader, opt): + self.ori_loader = loader + self.loader = iter(loader) + self.opt = opt + self.stream = torch.cuda.Stream() + self.device = torch.device('cuda' if opt['num_gpu'] != 0 else 'cpu') + self.preload() + + def preload(self): + try: + self.batch = next(self.loader) # self.batch is a dict + except StopIteration: + self.batch = None + return None + # put tensors to gpu + with torch.cuda.stream(self.stream): + for k, v in self.batch.items(): + if torch.is_tensor(v): + self.batch[k] = self.batch[k].to(device=self.device, non_blocking=True) + + def next(self): + torch.cuda.current_stream().wait_stream(self.stream) + batch = self.batch + self.preload() + return batch + + def reset(self): + self.loader = iter(self.ori_loader) + self.preload() diff --git a/sd/stablediffusion/src/codeformer/basicsr/data/transforms.py b/sd/stablediffusion/src/codeformer/basicsr/data/transforms.py new file mode 100644 index 0000000000000000000000000000000000000000..aead9dc73ed063e1c5865040eaa2652b26aa3ad3 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/data/transforms.py @@ -0,0 +1,165 @@ +import cv2 +import random + + +def mod_crop(img, scale): + """Mod crop images, used during testing. + + Args: + img (ndarray): Input image. + scale (int): Scale factor. + + Returns: + ndarray: Result image. + """ + img = img.copy() + if img.ndim in (2, 3): + h, w = img.shape[0], img.shape[1] + h_remainder, w_remainder = h % scale, w % scale + img = img[:h - h_remainder, :w - w_remainder, ...] + else: + raise ValueError(f'Wrong img ndim: {img.ndim}.') + return img + + +def paired_random_crop(img_gts, img_lqs, gt_patch_size, scale, gt_path): + """Paired random crop. + + It crops lists of lq and gt images with corresponding locations. + + Args: + img_gts (list[ndarray] | ndarray): GT images. Note that all images + should have the same shape. If the input is an ndarray, it will + be transformed to a list containing itself. + img_lqs (list[ndarray] | ndarray): LQ images. Note that all images + should have the same shape. If the input is an ndarray, it will + be transformed to a list containing itself. + gt_patch_size (int): GT patch size. + scale (int): Scale factor. + gt_path (str): Path to ground-truth. + + Returns: + list[ndarray] | ndarray: GT images and LQ images. If returned results + only have one element, just return ndarray. + """ + + if not isinstance(img_gts, list): + img_gts = [img_gts] + if not isinstance(img_lqs, list): + img_lqs = [img_lqs] + + h_lq, w_lq, _ = img_lqs[0].shape + h_gt, w_gt, _ = img_gts[0].shape + lq_patch_size = gt_patch_size // scale + + if h_gt != h_lq * scale or w_gt != w_lq * scale: + raise ValueError(f'Scale mismatches. GT ({h_gt}, {w_gt}) is not {scale}x ', + f'multiplication of LQ ({h_lq}, {w_lq}).') + if h_lq < lq_patch_size or w_lq < lq_patch_size: + raise ValueError(f'LQ ({h_lq}, {w_lq}) is smaller than patch size ' + f'({lq_patch_size}, {lq_patch_size}). ' + f'Please remove {gt_path}.') + + # randomly choose top and left coordinates for lq patch + top = random.randint(0, h_lq - lq_patch_size) + left = random.randint(0, w_lq - lq_patch_size) + + # crop lq patch + img_lqs = [v[top:top + lq_patch_size, left:left + lq_patch_size, ...] for v in img_lqs] + + # crop corresponding gt patch + top_gt, left_gt = int(top * scale), int(left * scale) + img_gts = [v[top_gt:top_gt + gt_patch_size, left_gt:left_gt + gt_patch_size, ...] for v in img_gts] + if len(img_gts) == 1: + img_gts = img_gts[0] + if len(img_lqs) == 1: + img_lqs = img_lqs[0] + return img_gts, img_lqs + + +def augment(imgs, hflip=True, rotation=True, flows=None, return_status=False): + """Augment: horizontal flips OR rotate (0, 90, 180, 270 degrees). + + We use vertical flip and transpose for rotation implementation. + All the images in the list use the same augmentation. + + Args: + imgs (list[ndarray] | ndarray): Images to be augmented. If the input + is an ndarray, it will be transformed to a list. + hflip (bool): Horizontal flip. Default: True. + rotation (bool): Ratotation. Default: True. + flows (list[ndarray]: Flows to be augmented. If the input is an + ndarray, it will be transformed to a list. + Dimension is (h, w, 2). Default: None. + return_status (bool): Return the status of flip and rotation. + Default: False. + + Returns: + list[ndarray] | ndarray: Augmented images and flows. If returned + results only have one element, just return ndarray. + + """ + hflip = hflip and random.random() < 0.5 + vflip = rotation and random.random() < 0.5 + rot90 = rotation and random.random() < 0.5 + + def _augment(img): + if hflip: # horizontal + cv2.flip(img, 1, img) + if vflip: # vertical + cv2.flip(img, 0, img) + if rot90: + img = img.transpose(1, 0, 2) + return img + + def _augment_flow(flow): + if hflip: # horizontal + cv2.flip(flow, 1, flow) + flow[:, :, 0] *= -1 + if vflip: # vertical + cv2.flip(flow, 0, flow) + flow[:, :, 1] *= -1 + if rot90: + flow = flow.transpose(1, 0, 2) + flow = flow[:, :, [1, 0]] + return flow + + if not isinstance(imgs, list): + imgs = [imgs] + imgs = [_augment(img) for img in imgs] + if len(imgs) == 1: + imgs = imgs[0] + + if flows is not None: + if not isinstance(flows, list): + flows = [flows] + flows = [_augment_flow(flow) for flow in flows] + if len(flows) == 1: + flows = flows[0] + return imgs, flows + else: + if return_status: + return imgs, (hflip, vflip, rot90) + else: + return imgs + + +def img_rotate(img, angle, center=None, scale=1.0): + """Rotate image. + + Args: + img (ndarray): Image to be rotated. + angle (float): Rotation angle in degrees. Positive values mean + counter-clockwise rotation. + center (tuple[int]): Rotation center. If the center is None, + initialize it as the center of the image. Default: None. + scale (float): Isotropic scale factor. Default: 1.0. + """ + (h, w) = img.shape[:2] + + if center is None: + center = (w // 2, h // 2) + + matrix = cv2.getRotationMatrix2D(center, angle, scale) + rotated_img = cv2.warpAffine(img, matrix, (w, h)) + return rotated_img diff --git a/sd/stablediffusion/src/codeformer/basicsr/losses/__init__.py b/sd/stablediffusion/src/codeformer/basicsr/losses/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2b184e74c861e6fca0c548692a9a949a6100b0aa --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/losses/__init__.py @@ -0,0 +1,26 @@ +from copy import deepcopy + +from basicsr.utils import get_root_logger +from basicsr.utils.registry import LOSS_REGISTRY +from .losses import (CharbonnierLoss, GANLoss, L1Loss, MSELoss, PerceptualLoss, WeightedTVLoss, g_path_regularize, + gradient_penalty_loss, r1_penalty) + +__all__ = [ + 'L1Loss', 'MSELoss', 'CharbonnierLoss', 'WeightedTVLoss', 'PerceptualLoss', 'GANLoss', 'gradient_penalty_loss', + 'r1_penalty', 'g_path_regularize' +] + + +def build_loss(opt): + """Build loss from options. + + Args: + opt (dict): Configuration. It must constain: + type (str): Model type. + """ + opt = deepcopy(opt) + loss_type = opt.pop('type') + loss = LOSS_REGISTRY.get(loss_type)(**opt) + logger = get_root_logger() + logger.info(f'Loss [{loss.__class__.__name__}] is created.') + return loss diff --git a/sd/stablediffusion/src/codeformer/basicsr/losses/loss_util.py b/sd/stablediffusion/src/codeformer/basicsr/losses/loss_util.py new file mode 100644 index 0000000000000000000000000000000000000000..744eeb46d1f3b5a7b4553ca23237ddd9c899a698 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/losses/loss_util.py @@ -0,0 +1,95 @@ +import functools +from torch.nn import functional as F + + +def reduce_loss(loss, reduction): + """Reduce loss as specified. + + Args: + loss (Tensor): Elementwise loss tensor. + reduction (str): Options are 'none', 'mean' and 'sum'. + + Returns: + Tensor: Reduced loss tensor. + """ + reduction_enum = F._Reduction.get_enum(reduction) + # none: 0, elementwise_mean:1, sum: 2 + if reduction_enum == 0: + return loss + elif reduction_enum == 1: + return loss.mean() + else: + return loss.sum() + + +def weight_reduce_loss(loss, weight=None, reduction='mean'): + """Apply element-wise weight and reduce loss. + + Args: + loss (Tensor): Element-wise loss. + weight (Tensor): Element-wise weights. Default: None. + reduction (str): Same as built-in losses of PyTorch. Options are + 'none', 'mean' and 'sum'. Default: 'mean'. + + Returns: + Tensor: Loss values. + """ + # if weight is specified, apply element-wise weight + if weight is not None: + assert weight.dim() == loss.dim() + assert weight.size(1) == 1 or weight.size(1) == loss.size(1) + loss = loss * weight + + # if weight is not specified or reduction is sum, just reduce the loss + if weight is None or reduction == 'sum': + loss = reduce_loss(loss, reduction) + # if reduction is mean, then compute mean over weight region + elif reduction == 'mean': + if weight.size(1) > 1: + weight = weight.sum() + else: + weight = weight.sum() * loss.size(1) + loss = loss.sum() / weight + + return loss + + +def weighted_loss(loss_func): + """Create a weighted version of a given loss function. + + To use this decorator, the loss function must have the signature like + `loss_func(pred, target, **kwargs)`. The function only needs to compute + element-wise loss without any reduction. This decorator will add weight + and reduction arguments to the function. The decorated function will have + the signature like `loss_func(pred, target, weight=None, reduction='mean', + **kwargs)`. + + :Example: + + >>> import torch + >>> @weighted_loss + >>> def l1_loss(pred, target): + >>> return (pred - target).abs() + + >>> pred = torch.Tensor([0, 2, 3]) + >>> target = torch.Tensor([1, 1, 1]) + >>> weight = torch.Tensor([1, 0, 1]) + + >>> l1_loss(pred, target) + tensor(1.3333) + >>> l1_loss(pred, target, weight) + tensor(1.5000) + >>> l1_loss(pred, target, reduction='none') + tensor([1., 1., 2.]) + >>> l1_loss(pred, target, weight, reduction='sum') + tensor(3.) + """ + + @functools.wraps(loss_func) + def wrapper(pred, target, weight=None, reduction='mean', **kwargs): + # get element-wise loss + loss = loss_func(pred, target, **kwargs) + loss = weight_reduce_loss(loss, weight, reduction) + return loss + + return wrapper diff --git a/sd/stablediffusion/src/codeformer/basicsr/losses/losses.py b/sd/stablediffusion/src/codeformer/basicsr/losses/losses.py new file mode 100644 index 0000000000000000000000000000000000000000..1bcf272cfb756d99451a3005567ea4d4c9059067 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/losses/losses.py @@ -0,0 +1,455 @@ +import math +import lpips +import torch +from torch import autograd as autograd +from torch import nn as nn +from torch.nn import functional as F + +from basicsr.archs.vgg_arch import VGGFeatureExtractor +from basicsr.utils.registry import LOSS_REGISTRY +from .loss_util import weighted_loss + +_reduction_modes = ['none', 'mean', 'sum'] + + +@weighted_loss +def l1_loss(pred, target): + return F.l1_loss(pred, target, reduction='none') + + +@weighted_loss +def mse_loss(pred, target): + return F.mse_loss(pred, target, reduction='none') + + +@weighted_loss +def charbonnier_loss(pred, target, eps=1e-12): + return torch.sqrt((pred - target)**2 + eps) + + +@LOSS_REGISTRY.register() +class L1Loss(nn.Module): + """L1 (mean absolute error, MAE) loss. + + Args: + loss_weight (float): Loss weight for L1 loss. Default: 1.0. + reduction (str): Specifies the reduction to apply to the output. + Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. + """ + + def __init__(self, loss_weight=1.0, reduction='mean'): + super(L1Loss, self).__init__() + if reduction not in ['none', 'mean', 'sum']: + raise ValueError(f'Unsupported reduction mode: {reduction}. ' f'Supported ones are: {_reduction_modes}') + + self.loss_weight = loss_weight + self.reduction = reduction + + def forward(self, pred, target, weight=None, **kwargs): + """ + Args: + pred (Tensor): of shape (N, C, H, W). Predicted tensor. + target (Tensor): of shape (N, C, H, W). Ground truth tensor. + weight (Tensor, optional): of shape (N, C, H, W). Element-wise + weights. Default: None. + """ + return self.loss_weight * l1_loss(pred, target, weight, reduction=self.reduction) + + +@LOSS_REGISTRY.register() +class MSELoss(nn.Module): + """MSE (L2) loss. + + Args: + loss_weight (float): Loss weight for MSE loss. Default: 1.0. + reduction (str): Specifies the reduction to apply to the output. + Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. + """ + + def __init__(self, loss_weight=1.0, reduction='mean'): + super(MSELoss, self).__init__() + if reduction not in ['none', 'mean', 'sum']: + raise ValueError(f'Unsupported reduction mode: {reduction}. ' f'Supported ones are: {_reduction_modes}') + + self.loss_weight = loss_weight + self.reduction = reduction + + def forward(self, pred, target, weight=None, **kwargs): + """ + Args: + pred (Tensor): of shape (N, C, H, W). Predicted tensor. + target (Tensor): of shape (N, C, H, W). Ground truth tensor. + weight (Tensor, optional): of shape (N, C, H, W). Element-wise + weights. Default: None. + """ + return self.loss_weight * mse_loss(pred, target, weight, reduction=self.reduction) + + +@LOSS_REGISTRY.register() +class CharbonnierLoss(nn.Module): + """Charbonnier loss (one variant of Robust L1Loss, a differentiable + variant of L1Loss). + + Described in "Deep Laplacian Pyramid Networks for Fast and Accurate + Super-Resolution". + + Args: + loss_weight (float): Loss weight for L1 loss. Default: 1.0. + reduction (str): Specifies the reduction to apply to the output. + Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. + eps (float): A value used to control the curvature near zero. + Default: 1e-12. + """ + + def __init__(self, loss_weight=1.0, reduction='mean', eps=1e-12): + super(CharbonnierLoss, self).__init__() + if reduction not in ['none', 'mean', 'sum']: + raise ValueError(f'Unsupported reduction mode: {reduction}. ' f'Supported ones are: {_reduction_modes}') + + self.loss_weight = loss_weight + self.reduction = reduction + self.eps = eps + + def forward(self, pred, target, weight=None, **kwargs): + """ + Args: + pred (Tensor): of shape (N, C, H, W). Predicted tensor. + target (Tensor): of shape (N, C, H, W). Ground truth tensor. + weight (Tensor, optional): of shape (N, C, H, W). Element-wise + weights. Default: None. + """ + return self.loss_weight * charbonnier_loss(pred, target, weight, eps=self.eps, reduction=self.reduction) + + +@LOSS_REGISTRY.register() +class WeightedTVLoss(L1Loss): + """Weighted TV loss. + + Args: + loss_weight (float): Loss weight. Default: 1.0. + """ + + def __init__(self, loss_weight=1.0): + super(WeightedTVLoss, self).__init__(loss_weight=loss_weight) + + def forward(self, pred, weight=None): + y_diff = super(WeightedTVLoss, self).forward(pred[:, :, :-1, :], pred[:, :, 1:, :], weight=weight[:, :, :-1, :]) + x_diff = super(WeightedTVLoss, self).forward(pred[:, :, :, :-1], pred[:, :, :, 1:], weight=weight[:, :, :, :-1]) + + loss = x_diff + y_diff + + return loss + + +@LOSS_REGISTRY.register() +class PerceptualLoss(nn.Module): + """Perceptual loss with commonly used style loss. + + Args: + layer_weights (dict): The weight for each layer of vgg feature. + Here is an example: {'conv5_4': 1.}, which means the conv5_4 + feature layer (before relu5_4) will be extracted with weight + 1.0 in calculting losses. + vgg_type (str): The type of vgg network used as feature extractor. + Default: 'vgg19'. + use_input_norm (bool): If True, normalize the input image in vgg. + Default: True. + range_norm (bool): If True, norm images with range [-1, 1] to [0, 1]. + Default: False. + perceptual_weight (float): If `perceptual_weight > 0`, the perceptual + loss will be calculated and the loss will multiplied by the + weight. Default: 1.0. + style_weight (float): If `style_weight > 0`, the style loss will be + calculated and the loss will multiplied by the weight. + Default: 0. + criterion (str): Criterion used for perceptual loss. Default: 'l1'. + """ + + def __init__(self, + layer_weights, + vgg_type='vgg19', + use_input_norm=True, + range_norm=False, + perceptual_weight=1.0, + style_weight=0., + criterion='l1'): + super(PerceptualLoss, self).__init__() + self.perceptual_weight = perceptual_weight + self.style_weight = style_weight + self.layer_weights = layer_weights + self.vgg = VGGFeatureExtractor( + layer_name_list=list(layer_weights.keys()), + vgg_type=vgg_type, + use_input_norm=use_input_norm, + range_norm=range_norm) + + self.criterion_type = criterion + if self.criterion_type == 'l1': + self.criterion = torch.nn.L1Loss() + elif self.criterion_type == 'l2': + self.criterion = torch.nn.L2loss() + elif self.criterion_type == 'mse': + self.criterion = torch.nn.MSELoss(reduction='mean') + elif self.criterion_type == 'fro': + self.criterion = None + else: + raise NotImplementedError(f'{criterion} criterion has not been supported.') + + def forward(self, x, gt): + """Forward function. + + Args: + x (Tensor): Input tensor with shape (n, c, h, w). + gt (Tensor): Ground-truth tensor with shape (n, c, h, w). + + Returns: + Tensor: Forward results. + """ + # extract vgg features + x_features = self.vgg(x) + gt_features = self.vgg(gt.detach()) + + # calculate perceptual loss + if self.perceptual_weight > 0: + percep_loss = 0 + for k in x_features.keys(): + if self.criterion_type == 'fro': + percep_loss += torch.norm(x_features[k] - gt_features[k], p='fro') * self.layer_weights[k] + else: + percep_loss += self.criterion(x_features[k], gt_features[k]) * self.layer_weights[k] + percep_loss *= self.perceptual_weight + else: + percep_loss = None + + # calculate style loss + if self.style_weight > 0: + style_loss = 0 + for k in x_features.keys(): + if self.criterion_type == 'fro': + style_loss += torch.norm( + self._gram_mat(x_features[k]) - self._gram_mat(gt_features[k]), p='fro') * self.layer_weights[k] + else: + style_loss += self.criterion(self._gram_mat(x_features[k]), self._gram_mat( + gt_features[k])) * self.layer_weights[k] + style_loss *= self.style_weight + else: + style_loss = None + + return percep_loss, style_loss + + def _gram_mat(self, x): + """Calculate Gram matrix. + + Args: + x (torch.Tensor): Tensor with shape of (n, c, h, w). + + Returns: + torch.Tensor: Gram matrix. + """ + n, c, h, w = x.size() + features = x.view(n, c, w * h) + features_t = features.transpose(1, 2) + gram = features.bmm(features_t) / (c * h * w) + return gram + + +@LOSS_REGISTRY.register() +class LPIPSLoss(nn.Module): + def __init__(self, + loss_weight=1.0, + use_input_norm=True, + range_norm=False,): + super(LPIPSLoss, self).__init__() + self.perceptual = lpips.LPIPS(net="vgg", spatial=False).eval() + self.loss_weight = loss_weight + self.use_input_norm = use_input_norm + self.range_norm = range_norm + + if self.use_input_norm: + # the mean is for image with range [0, 1] + self.register_buffer('mean', torch.Tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1)) + # the std is for image with range [0, 1] + self.register_buffer('std', torch.Tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1)) + + def forward(self, pred, target): + if self.range_norm: + pred = (pred + 1) / 2 + target = (target + 1) / 2 + if self.use_input_norm: + pred = (pred - self.mean) / self.std + target = (target - self.mean) / self.std + lpips_loss = self.perceptual(target.contiguous(), pred.contiguous()) + return self.loss_weight * lpips_loss.mean() + + +@LOSS_REGISTRY.register() +class GANLoss(nn.Module): + """Define GAN loss. + + Args: + gan_type (str): Support 'vanilla', 'lsgan', 'wgan', 'hinge'. + real_label_val (float): The value for real label. Default: 1.0. + fake_label_val (float): The value for fake label. Default: 0.0. + loss_weight (float): Loss weight. Default: 1.0. + Note that loss_weight is only for generators; and it is always 1.0 + for discriminators. + """ + + def __init__(self, gan_type, real_label_val=1.0, fake_label_val=0.0, loss_weight=1.0): + super(GANLoss, self).__init__() + self.gan_type = gan_type + self.loss_weight = loss_weight + self.real_label_val = real_label_val + self.fake_label_val = fake_label_val + + if self.gan_type == 'vanilla': + self.loss = nn.BCEWithLogitsLoss() + elif self.gan_type == 'lsgan': + self.loss = nn.MSELoss() + elif self.gan_type == 'wgan': + self.loss = self._wgan_loss + elif self.gan_type == 'wgan_softplus': + self.loss = self._wgan_softplus_loss + elif self.gan_type == 'hinge': + self.loss = nn.ReLU() + else: + raise NotImplementedError(f'GAN type {self.gan_type} is not implemented.') + + def _wgan_loss(self, input, target): + """wgan loss. + + Args: + input (Tensor): Input tensor. + target (bool): Target label. + + Returns: + Tensor: wgan loss. + """ + return -input.mean() if target else input.mean() + + def _wgan_softplus_loss(self, input, target): + """wgan loss with soft plus. softplus is a smooth approximation to the + ReLU function. + + In StyleGAN2, it is called: + Logistic loss for discriminator; + Non-saturating loss for generator. + + Args: + input (Tensor): Input tensor. + target (bool): Target label. + + Returns: + Tensor: wgan loss. + """ + return F.softplus(-input).mean() if target else F.softplus(input).mean() + + def get_target_label(self, input, target_is_real): + """Get target label. + + Args: + input (Tensor): Input tensor. + target_is_real (bool): Whether the target is real or fake. + + Returns: + (bool | Tensor): Target tensor. Return bool for wgan, otherwise, + return Tensor. + """ + + if self.gan_type in ['wgan', 'wgan_softplus']: + return target_is_real + target_val = (self.real_label_val if target_is_real else self.fake_label_val) + return input.new_ones(input.size()) * target_val + + def forward(self, input, target_is_real, is_disc=False): + """ + Args: + input (Tensor): The input for the loss module, i.e., the network + prediction. + target_is_real (bool): Whether the targe is real or fake. + is_disc (bool): Whether the loss for discriminators or not. + Default: False. + + Returns: + Tensor: GAN loss value. + """ + if self.gan_type == 'hinge': + if is_disc: # for discriminators in hinge-gan + input = -input if target_is_real else input + loss = self.loss(1 + input).mean() + else: # for generators in hinge-gan + loss = -input.mean() + else: # other gan types + target_label = self.get_target_label(input, target_is_real) + loss = self.loss(input, target_label) + + # loss_weight is always 1.0 for discriminators + return loss if is_disc else loss * self.loss_weight + + +def r1_penalty(real_pred, real_img): + """R1 regularization for discriminator. The core idea is to + penalize the gradient on real data alone: when the + generator distribution produces the true data distribution + and the discriminator is equal to 0 on the data manifold, the + gradient penalty ensures that the discriminator cannot create + a non-zero gradient orthogonal to the data manifold without + suffering a loss in the GAN game. + + Ref: + Eq. 9 in Which training methods for GANs do actually converge. + """ + grad_real = autograd.grad(outputs=real_pred.sum(), inputs=real_img, create_graph=True)[0] + grad_penalty = grad_real.pow(2).view(grad_real.shape[0], -1).sum(1).mean() + return grad_penalty + + +def g_path_regularize(fake_img, latents, mean_path_length, decay=0.01): + noise = torch.randn_like(fake_img) / math.sqrt(fake_img.shape[2] * fake_img.shape[3]) + grad = autograd.grad(outputs=(fake_img * noise).sum(), inputs=latents, create_graph=True)[0] + path_lengths = torch.sqrt(grad.pow(2).sum(2).mean(1)) + + path_mean = mean_path_length + decay * (path_lengths.mean() - mean_path_length) + + path_penalty = (path_lengths - path_mean).pow(2).mean() + + return path_penalty, path_lengths.detach().mean(), path_mean.detach() + + +def gradient_penalty_loss(discriminator, real_data, fake_data, weight=None): + """Calculate gradient penalty for wgan-gp. + + Args: + discriminator (nn.Module): Network for the discriminator. + real_data (Tensor): Real input data. + fake_data (Tensor): Fake input data. + weight (Tensor): Weight tensor. Default: None. + + Returns: + Tensor: A tensor for gradient penalty. + """ + + batch_size = real_data.size(0) + alpha = real_data.new_tensor(torch.rand(batch_size, 1, 1, 1)) + + # interpolate between real_data and fake_data + interpolates = alpha * real_data + (1. - alpha) * fake_data + interpolates = autograd.Variable(interpolates, requires_grad=True) + + disc_interpolates = discriminator(interpolates) + gradients = autograd.grad( + outputs=disc_interpolates, + inputs=interpolates, + grad_outputs=torch.ones_like(disc_interpolates), + create_graph=True, + retain_graph=True, + only_inputs=True)[0] + + if weight is not None: + gradients = gradients * weight + + gradients_penalty = ((gradients.norm(2, dim=1) - 1)**2).mean() + if weight is not None: + gradients_penalty /= torch.mean(weight) + + return gradients_penalty diff --git a/sd/stablediffusion/src/codeformer/basicsr/metrics/__init__.py b/sd/stablediffusion/src/codeformer/basicsr/metrics/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..19d55cc8321f124c918d78465b053aef67f13a33 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/metrics/__init__.py @@ -0,0 +1,19 @@ +from copy import deepcopy + +from basicsr.utils.registry import METRIC_REGISTRY +from .psnr_ssim import calculate_psnr, calculate_ssim + +__all__ = ['calculate_psnr', 'calculate_ssim'] + + +def calculate_metric(data, opt): + """Calculate metric from data and options. + + Args: + opt (dict): Configuration. It must constain: + type (str): Model type. + """ + opt = deepcopy(opt) + metric_type = opt.pop('type') + metric = METRIC_REGISTRY.get(metric_type)(**data, **opt) + return metric diff --git a/sd/stablediffusion/src/codeformer/basicsr/metrics/metric_util.py b/sd/stablediffusion/src/codeformer/basicsr/metrics/metric_util.py new file mode 100644 index 0000000000000000000000000000000000000000..4d18f0f7816431bed6af9d58319c6435bdf5c971 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/metrics/metric_util.py @@ -0,0 +1,45 @@ +import numpy as np + +from basicsr.utils.matlab_functions import bgr2ycbcr + + +def reorder_image(img, input_order='HWC'): + """Reorder images to 'HWC' order. + + If the input_order is (h, w), return (h, w, 1); + If the input_order is (c, h, w), return (h, w, c); + If the input_order is (h, w, c), return as it is. + + Args: + img (ndarray): Input image. + input_order (str): Whether the input order is 'HWC' or 'CHW'. + If the input image shape is (h, w), input_order will not have + effects. Default: 'HWC'. + + Returns: + ndarray: reordered image. + """ + + if input_order not in ['HWC', 'CHW']: + raise ValueError(f'Wrong input_order {input_order}. Supported input_orders are ' "'HWC' and 'CHW'") + if len(img.shape) == 2: + img = img[..., None] + if input_order == 'CHW': + img = img.transpose(1, 2, 0) + return img + + +def to_y_channel(img): + """Change to Y channel of YCbCr. + + Args: + img (ndarray): Images with range [0, 255]. + + Returns: + (ndarray): Images with range [0, 255] (float type) without round. + """ + img = img.astype(np.float32) / 255. + if img.ndim == 3 and img.shape[2] == 3: + img = bgr2ycbcr(img, y_only=True) + img = img[..., None] + return img * 255. diff --git a/sd/stablediffusion/src/codeformer/basicsr/metrics/psnr_ssim.py b/sd/stablediffusion/src/codeformer/basicsr/metrics/psnr_ssim.py new file mode 100644 index 0000000000000000000000000000000000000000..bbd950699c2495880236883861d9e199f900eae8 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/metrics/psnr_ssim.py @@ -0,0 +1,128 @@ +import cv2 +import numpy as np + +from basicsr.metrics.metric_util import reorder_image, to_y_channel +from basicsr.utils.registry import METRIC_REGISTRY + + +@METRIC_REGISTRY.register() +def calculate_psnr(img1, img2, crop_border, input_order='HWC', test_y_channel=False): + """Calculate PSNR (Peak Signal-to-Noise Ratio). + + Ref: https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio + + Args: + img1 (ndarray): Images with range [0, 255]. + img2 (ndarray): Images with range [0, 255]. + crop_border (int): Cropped pixels in each edge of an image. These + pixels are not involved in the PSNR calculation. + input_order (str): Whether the input order is 'HWC' or 'CHW'. + Default: 'HWC'. + test_y_channel (bool): Test on Y channel of YCbCr. Default: False. + + Returns: + float: psnr result. + """ + + assert img1.shape == img2.shape, (f'Image shapes are differnet: {img1.shape}, {img2.shape}.') + if input_order not in ['HWC', 'CHW']: + raise ValueError(f'Wrong input_order {input_order}. Supported input_orders are ' '"HWC" and "CHW"') + img1 = reorder_image(img1, input_order=input_order) + img2 = reorder_image(img2, input_order=input_order) + img1 = img1.astype(np.float64) + img2 = img2.astype(np.float64) + + if crop_border != 0: + img1 = img1[crop_border:-crop_border, crop_border:-crop_border, ...] + img2 = img2[crop_border:-crop_border, crop_border:-crop_border, ...] + + if test_y_channel: + img1 = to_y_channel(img1) + img2 = to_y_channel(img2) + + mse = np.mean((img1 - img2)**2) + if mse == 0: + return float('inf') + return 20. * np.log10(255. / np.sqrt(mse)) + + +def _ssim(img1, img2): + """Calculate SSIM (structural similarity) for one channel images. + + It is called by func:`calculate_ssim`. + + Args: + img1 (ndarray): Images with range [0, 255] with order 'HWC'. + img2 (ndarray): Images with range [0, 255] with order 'HWC'. + + Returns: + float: ssim result. + """ + + C1 = (0.01 * 255)**2 + C2 = (0.03 * 255)**2 + + img1 = img1.astype(np.float64) + img2 = img2.astype(np.float64) + kernel = cv2.getGaussianKernel(11, 1.5) + window = np.outer(kernel, kernel.transpose()) + + mu1 = cv2.filter2D(img1, -1, window)[5:-5, 5:-5] + mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5] + mu1_sq = mu1**2 + mu2_sq = mu2**2 + mu1_mu2 = mu1 * mu2 + sigma1_sq = cv2.filter2D(img1**2, -1, window)[5:-5, 5:-5] - mu1_sq + sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq + sigma12 = cv2.filter2D(img1 * img2, -1, window)[5:-5, 5:-5] - mu1_mu2 + + ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) * (sigma1_sq + sigma2_sq + C2)) + return ssim_map.mean() + + +@METRIC_REGISTRY.register() +def calculate_ssim(img1, img2, crop_border, input_order='HWC', test_y_channel=False): + """Calculate SSIM (structural similarity). + + Ref: + Image quality assessment: From error visibility to structural similarity + + The results are the same as that of the official released MATLAB code in + https://ece.uwaterloo.ca/~z70wang/research/ssim/. + + For three-channel images, SSIM is calculated for each channel and then + averaged. + + Args: + img1 (ndarray): Images with range [0, 255]. + img2 (ndarray): Images with range [0, 255]. + crop_border (int): Cropped pixels in each edge of an image. These + pixels are not involved in the SSIM calculation. + input_order (str): Whether the input order is 'HWC' or 'CHW'. + Default: 'HWC'. + test_y_channel (bool): Test on Y channel of YCbCr. Default: False. + + Returns: + float: ssim result. + """ + + assert img1.shape == img2.shape, (f'Image shapes are differnet: {img1.shape}, {img2.shape}.') + if input_order not in ['HWC', 'CHW']: + raise ValueError(f'Wrong input_order {input_order}. Supported input_orders are ' '"HWC" and "CHW"') + img1 = reorder_image(img1, input_order=input_order) + img2 = reorder_image(img2, input_order=input_order) + img1 = img1.astype(np.float64) + img2 = img2.astype(np.float64) + + if crop_border != 0: + img1 = img1[crop_border:-crop_border, crop_border:-crop_border, ...] + img2 = img2[crop_border:-crop_border, crop_border:-crop_border, ...] + + if test_y_channel: + img1 = to_y_channel(img1) + img2 = to_y_channel(img2) + + ssims = [] + for i in range(img1.shape[2]): + ssims.append(_ssim(img1[..., i], img2[..., i])) + return np.array(ssims).mean() diff --git a/sd/stablediffusion/src/codeformer/basicsr/models/__init__.py b/sd/stablediffusion/src/codeformer/basicsr/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..00bde45f003698a5b15d3517ae47b59ef1d86e0c --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/models/__init__.py @@ -0,0 +1,30 @@ +import importlib +from copy import deepcopy +from os import path as osp + +from basicsr.utils import get_root_logger, scandir +from basicsr.utils.registry import MODEL_REGISTRY + +__all__ = ['build_model'] + +# automatically scan and import model modules for registry +# scan all the files under the 'models' folder and collect files ending with +# '_model.py' +model_folder = osp.dirname(osp.abspath(__file__)) +model_filenames = [osp.splitext(osp.basename(v))[0] for v in scandir(model_folder) if v.endswith('_model.py')] +# import all the model modules +_model_modules = [importlib.import_module(f'basicsr.models.{file_name}') for file_name in model_filenames] + + +def build_model(opt): + """Build model from options. + + Args: + opt (dict): Configuration. It must constain: + model_type (str): Model type. + """ + opt = deepcopy(opt) + model = MODEL_REGISTRY.get(opt['model_type'])(opt) + logger = get_root_logger() + logger.info(f'Model [{model.__class__.__name__}] is created.') + return model diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/__init__.py b/sd/stablediffusion/src/codeformer/basicsr/ops/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/__init__.py b/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..32e3592f896d61b4127e09d0476381b9d55e32ff --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/__init__.py @@ -0,0 +1,7 @@ +from .deform_conv import (DeformConv, DeformConvPack, ModulatedDeformConv, ModulatedDeformConvPack, deform_conv, + modulated_deform_conv) + +__all__ = [ + 'DeformConv', 'DeformConvPack', 'ModulatedDeformConv', 'ModulatedDeformConvPack', 'deform_conv', + 'modulated_deform_conv' +] diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/deform_conv.py b/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/deform_conv.py new file mode 100644 index 0000000000000000000000000000000000000000..734154f9ed9447d585eae7df6886acb136f8a3cf --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/deform_conv.py @@ -0,0 +1,377 @@ +import math +import torch +from torch import nn as nn +from torch.autograd import Function +from torch.autograd.function import once_differentiable +from torch.nn import functional as F +from torch.nn.modules.utils import _pair, _single + +try: + from . import deform_conv_ext +except ImportError: + import os + BASICSR_JIT = os.getenv('BASICSR_JIT') + if BASICSR_JIT == 'True': + from torch.utils.cpp_extension import load + module_path = os.path.dirname(__file__) + deform_conv_ext = load( + 'deform_conv', + sources=[ + os.path.join(module_path, 'src', 'deform_conv_ext.cpp'), + os.path.join(module_path, 'src', 'deform_conv_cuda.cpp'), + os.path.join(module_path, 'src', 'deform_conv_cuda_kernel.cu'), + ], + ) + + +class DeformConvFunction(Function): + + @staticmethod + def forward(ctx, + input, + offset, + weight, + stride=1, + padding=0, + dilation=1, + groups=1, + deformable_groups=1, + im2col_step=64): + if input is not None and input.dim() != 4: + raise ValueError(f'Expected 4D tensor as input, got {input.dim()}' 'D tensor instead.') + ctx.stride = _pair(stride) + ctx.padding = _pair(padding) + ctx.dilation = _pair(dilation) + ctx.groups = groups + ctx.deformable_groups = deformable_groups + ctx.im2col_step = im2col_step + + ctx.save_for_backward(input, offset, weight) + + output = input.new_empty(DeformConvFunction._output_size(input, weight, ctx.padding, ctx.dilation, ctx.stride)) + + ctx.bufs_ = [input.new_empty(0), input.new_empty(0)] # columns, ones + + if not input.is_cuda: + raise NotImplementedError + else: + cur_im2col_step = min(ctx.im2col_step, input.shape[0]) + assert (input.shape[0] % cur_im2col_step) == 0, 'im2col step must divide batchsize' + deform_conv_ext.deform_conv_forward(input, weight, + offset, output, ctx.bufs_[0], ctx.bufs_[1], weight.size(3), + weight.size(2), ctx.stride[1], ctx.stride[0], ctx.padding[1], + ctx.padding[0], ctx.dilation[1], ctx.dilation[0], ctx.groups, + ctx.deformable_groups, cur_im2col_step) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + input, offset, weight = ctx.saved_tensors + + grad_input = grad_offset = grad_weight = None + + if not grad_output.is_cuda: + raise NotImplementedError + else: + cur_im2col_step = min(ctx.im2col_step, input.shape[0]) + assert (input.shape[0] % cur_im2col_step) == 0, 'im2col step must divide batchsize' + + if ctx.needs_input_grad[0] or ctx.needs_input_grad[1]: + grad_input = torch.zeros_like(input) + grad_offset = torch.zeros_like(offset) + deform_conv_ext.deform_conv_backward_input(input, offset, grad_output, grad_input, + grad_offset, weight, ctx.bufs_[0], weight.size(3), + weight.size(2), ctx.stride[1], ctx.stride[0], ctx.padding[1], + ctx.padding[0], ctx.dilation[1], ctx.dilation[0], ctx.groups, + ctx.deformable_groups, cur_im2col_step) + + if ctx.needs_input_grad[2]: + grad_weight = torch.zeros_like(weight) + deform_conv_ext.deform_conv_backward_parameters(input, offset, grad_output, grad_weight, + ctx.bufs_[0], ctx.bufs_[1], weight.size(3), + weight.size(2), ctx.stride[1], ctx.stride[0], + ctx.padding[1], ctx.padding[0], ctx.dilation[1], + ctx.dilation[0], ctx.groups, ctx.deformable_groups, 1, + cur_im2col_step) + + return (grad_input, grad_offset, grad_weight, None, None, None, None, None) + + @staticmethod + def _output_size(input, weight, padding, dilation, stride): + channels = weight.size(0) + output_size = (input.size(0), channels) + for d in range(input.dim() - 2): + in_size = input.size(d + 2) + pad = padding[d] + kernel = dilation[d] * (weight.size(d + 2) - 1) + 1 + stride_ = stride[d] + output_size += ((in_size + (2 * pad) - kernel) // stride_ + 1, ) + if not all(map(lambda s: s > 0, output_size)): + raise ValueError('convolution input is too small (output would be ' f'{"x".join(map(str, output_size))})') + return output_size + + +class ModulatedDeformConvFunction(Function): + + @staticmethod + def forward(ctx, + input, + offset, + mask, + weight, + bias=None, + stride=1, + padding=0, + dilation=1, + groups=1, + deformable_groups=1): + ctx.stride = stride + ctx.padding = padding + ctx.dilation = dilation + ctx.groups = groups + ctx.deformable_groups = deformable_groups + ctx.with_bias = bias is not None + if not ctx.with_bias: + bias = input.new_empty(1) # fake tensor + if not input.is_cuda: + raise NotImplementedError + if weight.requires_grad or mask.requires_grad or offset.requires_grad \ + or input.requires_grad: + ctx.save_for_backward(input, offset, mask, weight, bias) + output = input.new_empty(ModulatedDeformConvFunction._infer_shape(ctx, input, weight)) + ctx._bufs = [input.new_empty(0), input.new_empty(0)] + deform_conv_ext.modulated_deform_conv_forward(input, weight, bias, ctx._bufs[0], offset, mask, output, + ctx._bufs[1], weight.shape[2], weight.shape[3], ctx.stride, + ctx.stride, ctx.padding, ctx.padding, ctx.dilation, ctx.dilation, + ctx.groups, ctx.deformable_groups, ctx.with_bias) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + if not grad_output.is_cuda: + raise NotImplementedError + input, offset, mask, weight, bias = ctx.saved_tensors + grad_input = torch.zeros_like(input) + grad_offset = torch.zeros_like(offset) + grad_mask = torch.zeros_like(mask) + grad_weight = torch.zeros_like(weight) + grad_bias = torch.zeros_like(bias) + deform_conv_ext.modulated_deform_conv_backward(input, weight, bias, ctx._bufs[0], offset, mask, ctx._bufs[1], + grad_input, grad_weight, grad_bias, grad_offset, grad_mask, + grad_output, weight.shape[2], weight.shape[3], ctx.stride, + ctx.stride, ctx.padding, ctx.padding, ctx.dilation, ctx.dilation, + ctx.groups, ctx.deformable_groups, ctx.with_bias) + if not ctx.with_bias: + grad_bias = None + + return (grad_input, grad_offset, grad_mask, grad_weight, grad_bias, None, None, None, None, None) + + @staticmethod + def _infer_shape(ctx, input, weight): + n = input.size(0) + channels_out = weight.size(0) + height, width = input.shape[2:4] + kernel_h, kernel_w = weight.shape[2:4] + height_out = (height + 2 * ctx.padding - (ctx.dilation * (kernel_h - 1) + 1)) // ctx.stride + 1 + width_out = (width + 2 * ctx.padding - (ctx.dilation * (kernel_w - 1) + 1)) // ctx.stride + 1 + return n, channels_out, height_out, width_out + + +deform_conv = DeformConvFunction.apply +modulated_deform_conv = ModulatedDeformConvFunction.apply + + +class DeformConv(nn.Module): + + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + deformable_groups=1, + bias=False): + super(DeformConv, self).__init__() + + assert not bias + assert in_channels % groups == 0, \ + f'in_channels {in_channels} is not divisible by groups {groups}' + assert out_channels % groups == 0, \ + f'out_channels {out_channels} is not divisible ' \ + f'by groups {groups}' + + self.in_channels = in_channels + self.out_channels = out_channels + self.kernel_size = _pair(kernel_size) + self.stride = _pair(stride) + self.padding = _pair(padding) + self.dilation = _pair(dilation) + self.groups = groups + self.deformable_groups = deformable_groups + # enable compatibility with nn.Conv2d + self.transposed = False + self.output_padding = _single(0) + + self.weight = nn.Parameter(torch.Tensor(out_channels, in_channels // self.groups, *self.kernel_size)) + + self.reset_parameters() + + def reset_parameters(self): + n = self.in_channels + for k in self.kernel_size: + n *= k + stdv = 1. / math.sqrt(n) + self.weight.data.uniform_(-stdv, stdv) + + def forward(self, x, offset): + # To fix an assert error in deform_conv_cuda.cpp:128 + # input image is smaller than kernel + input_pad = (x.size(2) < self.kernel_size[0] or x.size(3) < self.kernel_size[1]) + if input_pad: + pad_h = max(self.kernel_size[0] - x.size(2), 0) + pad_w = max(self.kernel_size[1] - x.size(3), 0) + x = F.pad(x, (0, pad_w, 0, pad_h), 'constant', 0).contiguous() + offset = F.pad(offset, (0, pad_w, 0, pad_h), 'constant', 0).contiguous() + out = deform_conv(x, offset, self.weight, self.stride, self.padding, self.dilation, self.groups, + self.deformable_groups) + if input_pad: + out = out[:, :, :out.size(2) - pad_h, :out.size(3) - pad_w].contiguous() + return out + + +class DeformConvPack(DeformConv): + """A Deformable Conv Encapsulation that acts as normal Conv layers. + + Args: + in_channels (int): Same as nn.Conv2d. + out_channels (int): Same as nn.Conv2d. + kernel_size (int or tuple[int]): Same as nn.Conv2d. + stride (int or tuple[int]): Same as nn.Conv2d. + padding (int or tuple[int]): Same as nn.Conv2d. + dilation (int or tuple[int]): Same as nn.Conv2d. + groups (int): Same as nn.Conv2d. + bias (bool or str): If specified as `auto`, it will be decided by the + norm_cfg. Bias will be set as True if norm_cfg is None, otherwise + False. + """ + + _version = 2 + + def __init__(self, *args, **kwargs): + super(DeformConvPack, self).__init__(*args, **kwargs) + + self.conv_offset = nn.Conv2d( + self.in_channels, + self.deformable_groups * 2 * self.kernel_size[0] * self.kernel_size[1], + kernel_size=self.kernel_size, + stride=_pair(self.stride), + padding=_pair(self.padding), + dilation=_pair(self.dilation), + bias=True) + self.init_offset() + + def init_offset(self): + self.conv_offset.weight.data.zero_() + self.conv_offset.bias.data.zero_() + + def forward(self, x): + offset = self.conv_offset(x) + return deform_conv(x, offset, self.weight, self.stride, self.padding, self.dilation, self.groups, + self.deformable_groups) + + +class ModulatedDeformConv(nn.Module): + + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + deformable_groups=1, + bias=True): + super(ModulatedDeformConv, self).__init__() + self.in_channels = in_channels + self.out_channels = out_channels + self.kernel_size = _pair(kernel_size) + self.stride = stride + self.padding = padding + self.dilation = dilation + self.groups = groups + self.deformable_groups = deformable_groups + self.with_bias = bias + # enable compatibility with nn.Conv2d + self.transposed = False + self.output_padding = _single(0) + + self.weight = nn.Parameter(torch.Tensor(out_channels, in_channels // groups, *self.kernel_size)) + if bias: + self.bias = nn.Parameter(torch.Tensor(out_channels)) + else: + self.register_parameter('bias', None) + self.init_weights() + + def init_weights(self): + n = self.in_channels + for k in self.kernel_size: + n *= k + stdv = 1. / math.sqrt(n) + self.weight.data.uniform_(-stdv, stdv) + if self.bias is not None: + self.bias.data.zero_() + + def forward(self, x, offset, mask): + return modulated_deform_conv(x, offset, mask, self.weight, self.bias, self.stride, self.padding, self.dilation, + self.groups, self.deformable_groups) + + +class ModulatedDeformConvPack(ModulatedDeformConv): + """A ModulatedDeformable Conv Encapsulation that acts as normal Conv layers. + + Args: + in_channels (int): Same as nn.Conv2d. + out_channels (int): Same as nn.Conv2d. + kernel_size (int or tuple[int]): Same as nn.Conv2d. + stride (int or tuple[int]): Same as nn.Conv2d. + padding (int or tuple[int]): Same as nn.Conv2d. + dilation (int or tuple[int]): Same as nn.Conv2d. + groups (int): Same as nn.Conv2d. + bias (bool or str): If specified as `auto`, it will be decided by the + norm_cfg. Bias will be set as True if norm_cfg is None, otherwise + False. + """ + + _version = 2 + + def __init__(self, *args, **kwargs): + super(ModulatedDeformConvPack, self).__init__(*args, **kwargs) + + self.conv_offset = nn.Conv2d( + self.in_channels, + self.deformable_groups * 3 * self.kernel_size[0] * self.kernel_size[1], + kernel_size=self.kernel_size, + stride=_pair(self.stride), + padding=_pair(self.padding), + dilation=_pair(self.dilation), + bias=True) + self.init_weights() + + def init_weights(self): + super(ModulatedDeformConvPack, self).init_weights() + if hasattr(self, 'conv_offset'): + self.conv_offset.weight.data.zero_() + self.conv_offset.bias.data.zero_() + + def forward(self, x): + out = self.conv_offset(x) + o1, o2, mask = torch.chunk(out, 3, dim=1) + offset = torch.cat((o1, o2), dim=1) + mask = torch.sigmoid(mask) + return modulated_deform_conv(x, offset, mask, self.weight, self.bias, self.stride, self.padding, self.dilation, + self.groups, self.deformable_groups) diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/src/deform_conv_cuda.cpp b/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/src/deform_conv_cuda.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5d9424908ed2dbd4ac3cdb98d13e09287a4d2f2d --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/src/deform_conv_cuda.cpp @@ -0,0 +1,685 @@ +// modify from +// https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/blob/mmdetection/mmdet/ops/dcn/src/deform_conv_cuda.c + +#include +#include + +#include +#include + +void deformable_im2col(const at::Tensor data_im, const at::Tensor data_offset, + const int channels, const int height, const int width, + const int ksize_h, const int ksize_w, const int pad_h, + const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int parallel_imgs, const int deformable_group, + at::Tensor data_col); + +void deformable_col2im(const at::Tensor data_col, const at::Tensor data_offset, + const int channels, const int height, const int width, + const int ksize_h, const int ksize_w, const int pad_h, + const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int parallel_imgs, const int deformable_group, + at::Tensor grad_im); + +void deformable_col2im_coord( + const at::Tensor data_col, const at::Tensor data_im, + const at::Tensor data_offset, const int channels, const int height, + const int width, const int ksize_h, const int ksize_w, const int pad_h, + const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, const int parallel_imgs, + const int deformable_group, at::Tensor grad_offset); + +void modulated_deformable_im2col_cuda( + const at::Tensor data_im, const at::Tensor data_offset, + const at::Tensor data_mask, const int batch_size, const int channels, + const int height_im, const int width_im, const int height_col, + const int width_col, const int kernel_h, const int kenerl_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, const int deformable_group, + at::Tensor data_col); + +void modulated_deformable_col2im_cuda( + const at::Tensor data_col, const at::Tensor data_offset, + const at::Tensor data_mask, const int batch_size, const int channels, + const int height_im, const int width_im, const int height_col, + const int width_col, const int kernel_h, const int kenerl_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, const int deformable_group, + at::Tensor grad_im); + +void modulated_deformable_col2im_coord_cuda( + const at::Tensor data_col, const at::Tensor data_im, + const at::Tensor data_offset, const at::Tensor data_mask, + const int batch_size, const int channels, const int height_im, + const int width_im, const int height_col, const int width_col, + const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, + const int stride_h, const int stride_w, const int dilation_h, + const int dilation_w, const int deformable_group, at::Tensor grad_offset, + at::Tensor grad_mask); + +void shape_check(at::Tensor input, at::Tensor offset, at::Tensor *gradOutput, + at::Tensor weight, int kH, int kW, int dH, int dW, int padH, + int padW, int dilationH, int dilationW, int group, + int deformable_group) { + TORCH_CHECK(weight.ndimension() == 4, + "4D weight tensor (nOutputPlane,nInputPlane,kH,kW) expected, " + "but got: %s", + weight.ndimension()); + + TORCH_CHECK(weight.is_contiguous(), "weight tensor has to be contiguous"); + + TORCH_CHECK(kW > 0 && kH > 0, + "kernel size should be greater than zero, but got kH: %d kW: %d", kH, + kW); + + TORCH_CHECK((weight.size(2) == kH && weight.size(3) == kW), + "kernel size should be consistent with weight, ", + "but got kH: %d kW: %d weight.size(2): %d, weight.size(3): %d", kH, + kW, weight.size(2), weight.size(3)); + + TORCH_CHECK(dW > 0 && dH > 0, + "stride should be greater than zero, but got dH: %d dW: %d", dH, dW); + + TORCH_CHECK( + dilationW > 0 && dilationH > 0, + "dilation should be greater than 0, but got dilationH: %d dilationW: %d", + dilationH, dilationW); + + int ndim = input.ndimension(); + int dimf = 0; + int dimh = 1; + int dimw = 2; + + if (ndim == 4) { + dimf++; + dimh++; + dimw++; + } + + TORCH_CHECK(ndim == 3 || ndim == 4, "3D or 4D input tensor expected but got: %s", + ndim); + + long nInputPlane = weight.size(1) * group; + long inputHeight = input.size(dimh); + long inputWidth = input.size(dimw); + long nOutputPlane = weight.size(0); + long outputHeight = + (inputHeight + 2 * padH - (dilationH * (kH - 1) + 1)) / dH + 1; + long outputWidth = + (inputWidth + 2 * padW - (dilationW * (kW - 1) + 1)) / dW + 1; + + TORCH_CHECK(nInputPlane % deformable_group == 0, + "input channels must divide deformable group size"); + + if (outputWidth < 1 || outputHeight < 1) + AT_ERROR( + "Given input size: (%ld x %ld x %ld). " + "Calculated output size: (%ld x %ld x %ld). Output size is too small", + nInputPlane, inputHeight, inputWidth, nOutputPlane, outputHeight, + outputWidth); + + TORCH_CHECK(input.size(1) == nInputPlane, + "invalid number of input planes, expected: %d, but got: %d", + nInputPlane, input.size(1)); + + TORCH_CHECK((inputHeight >= kH && inputWidth >= kW), + "input image is smaller than kernel"); + + TORCH_CHECK((offset.size(2) == outputHeight && offset.size(3) == outputWidth), + "invalid spatial size of offset, expected height: %d width: %d, but " + "got height: %d width: %d", + outputHeight, outputWidth, offset.size(2), offset.size(3)); + + TORCH_CHECK((offset.size(1) == deformable_group * 2 * kH * kW), + "invalid number of channels of offset"); + + if (gradOutput != NULL) { + TORCH_CHECK(gradOutput->size(dimf) == nOutputPlane, + "invalid number of gradOutput planes, expected: %d, but got: %d", + nOutputPlane, gradOutput->size(dimf)); + + TORCH_CHECK((gradOutput->size(dimh) == outputHeight && + gradOutput->size(dimw) == outputWidth), + "invalid size of gradOutput, expected height: %d width: %d , but " + "got height: %d width: %d", + outputHeight, outputWidth, gradOutput->size(dimh), + gradOutput->size(dimw)); + } +} + +int deform_conv_forward_cuda(at::Tensor input, at::Tensor weight, + at::Tensor offset, at::Tensor output, + at::Tensor columns, at::Tensor ones, int kW, + int kH, int dW, int dH, int padW, int padH, + int dilationW, int dilationH, int group, + int deformable_group, int im2col_step) { + // todo: resize columns to include im2col: done + // todo: add im2col_step as input + // todo: add new output buffer and transpose it to output (or directly + // transpose output) todo: possibly change data indexing because of + // parallel_imgs + + shape_check(input, offset, NULL, weight, kH, kW, dH, dW, padH, padW, + dilationH, dilationW, group, deformable_group); + at::DeviceGuard guard(input.device()); + + input = input.contiguous(); + offset = offset.contiguous(); + weight = weight.contiguous(); + + int batch = 1; + if (input.ndimension() == 3) { + // Force batch + batch = 0; + input.unsqueeze_(0); + offset.unsqueeze_(0); + } + + // todo: assert batchsize dividable by im2col_step + + long batchSize = input.size(0); + long nInputPlane = input.size(1); + long inputHeight = input.size(2); + long inputWidth = input.size(3); + + long nOutputPlane = weight.size(0); + + long outputWidth = + (inputWidth + 2 * padW - (dilationW * (kW - 1) + 1)) / dW + 1; + long outputHeight = + (inputHeight + 2 * padH - (dilationH * (kH - 1) + 1)) / dH + 1; + + TORCH_CHECK((offset.size(0) == batchSize), "invalid batch size of offset"); + + output = output.view({batchSize / im2col_step, im2col_step, nOutputPlane, + outputHeight, outputWidth}); + columns = at::zeros( + {nInputPlane * kW * kH, im2col_step * outputHeight * outputWidth}, + input.options()); + + if (ones.ndimension() != 2 || + ones.size(0) * ones.size(1) < outputHeight * outputWidth) { + ones = at::ones({outputHeight, outputWidth}, input.options()); + } + + input = input.view({batchSize / im2col_step, im2col_step, nInputPlane, + inputHeight, inputWidth}); + offset = + offset.view({batchSize / im2col_step, im2col_step, + deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + + at::Tensor output_buffer = + at::zeros({batchSize / im2col_step, nOutputPlane, + im2col_step * outputHeight, outputWidth}, + output.options()); + + output_buffer = output_buffer.view( + {output_buffer.size(0), group, output_buffer.size(1) / group, + output_buffer.size(2), output_buffer.size(3)}); + + for (int elt = 0; elt < batchSize / im2col_step; elt++) { + deformable_im2col(input[elt], offset[elt], nInputPlane, inputHeight, + inputWidth, kH, kW, padH, padW, dH, dW, dilationH, + dilationW, im2col_step, deformable_group, columns); + + columns = columns.view({group, columns.size(0) / group, columns.size(1)}); + weight = weight.view({group, weight.size(0) / group, weight.size(1), + weight.size(2), weight.size(3)}); + + for (int g = 0; g < group; g++) { + output_buffer[elt][g] = output_buffer[elt][g] + .flatten(1) + .addmm_(weight[g].flatten(1), columns[g]) + .view_as(output_buffer[elt][g]); + } + } + + output_buffer = output_buffer.view( + {output_buffer.size(0), output_buffer.size(1) * output_buffer.size(2), + output_buffer.size(3), output_buffer.size(4)}); + + output_buffer = output_buffer.view({batchSize / im2col_step, nOutputPlane, + im2col_step, outputHeight, outputWidth}); + output_buffer.transpose_(1, 2); + output.copy_(output_buffer); + output = output.view({batchSize, nOutputPlane, outputHeight, outputWidth}); + + input = input.view({batchSize, nInputPlane, inputHeight, inputWidth}); + offset = offset.view( + {batchSize, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + + if (batch == 0) { + output = output.view({nOutputPlane, outputHeight, outputWidth}); + input = input.view({nInputPlane, inputHeight, inputWidth}); + offset = offset.view({offset.size(1), offset.size(2), offset.size(3)}); + } + + return 1; +} + +int deform_conv_backward_input_cuda(at::Tensor input, at::Tensor offset, + at::Tensor gradOutput, at::Tensor gradInput, + at::Tensor gradOffset, at::Tensor weight, + at::Tensor columns, int kW, int kH, int dW, + int dH, int padW, int padH, int dilationW, + int dilationH, int group, + int deformable_group, int im2col_step) { + shape_check(input, offset, &gradOutput, weight, kH, kW, dH, dW, padH, padW, + dilationH, dilationW, group, deformable_group); + at::DeviceGuard guard(input.device()); + + input = input.contiguous(); + offset = offset.contiguous(); + gradOutput = gradOutput.contiguous(); + weight = weight.contiguous(); + + int batch = 1; + + if (input.ndimension() == 3) { + // Force batch + batch = 0; + input = input.view({1, input.size(0), input.size(1), input.size(2)}); + offset = offset.view({1, offset.size(0), offset.size(1), offset.size(2)}); + gradOutput = gradOutput.view( + {1, gradOutput.size(0), gradOutput.size(1), gradOutput.size(2)}); + } + + long batchSize = input.size(0); + long nInputPlane = input.size(1); + long inputHeight = input.size(2); + long inputWidth = input.size(3); + + long nOutputPlane = weight.size(0); + + long outputWidth = + (inputWidth + 2 * padW - (dilationW * (kW - 1) + 1)) / dW + 1; + long outputHeight = + (inputHeight + 2 * padH - (dilationH * (kH - 1) + 1)) / dH + 1; + + TORCH_CHECK((offset.size(0) == batchSize), 3, "invalid batch size of offset"); + gradInput = gradInput.view({batchSize, nInputPlane, inputHeight, inputWidth}); + columns = at::zeros( + {nInputPlane * kW * kH, im2col_step * outputHeight * outputWidth}, + input.options()); + + // change order of grad output + gradOutput = gradOutput.view({batchSize / im2col_step, im2col_step, + nOutputPlane, outputHeight, outputWidth}); + gradOutput.transpose_(1, 2); + + gradInput = gradInput.view({batchSize / im2col_step, im2col_step, nInputPlane, + inputHeight, inputWidth}); + input = input.view({batchSize / im2col_step, im2col_step, nInputPlane, + inputHeight, inputWidth}); + gradOffset = gradOffset.view({batchSize / im2col_step, im2col_step, + deformable_group * 2 * kH * kW, outputHeight, + outputWidth}); + offset = + offset.view({batchSize / im2col_step, im2col_step, + deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + + for (int elt = 0; elt < batchSize / im2col_step; elt++) { + // divide into groups + columns = columns.view({group, columns.size(0) / group, columns.size(1)}); + weight = weight.view({group, weight.size(0) / group, weight.size(1), + weight.size(2), weight.size(3)}); + gradOutput = gradOutput.view( + {gradOutput.size(0), group, gradOutput.size(1) / group, + gradOutput.size(2), gradOutput.size(3), gradOutput.size(4)}); + + for (int g = 0; g < group; g++) { + columns[g] = columns[g].addmm_(weight[g].flatten(1).transpose(0, 1), + gradOutput[elt][g].flatten(1), 0.0f, 1.0f); + } + + columns = + columns.view({columns.size(0) * columns.size(1), columns.size(2)}); + gradOutput = gradOutput.view( + {gradOutput.size(0), gradOutput.size(1) * gradOutput.size(2), + gradOutput.size(3), gradOutput.size(4), gradOutput.size(5)}); + + deformable_col2im_coord(columns, input[elt], offset[elt], nInputPlane, + inputHeight, inputWidth, kH, kW, padH, padW, dH, dW, + dilationH, dilationW, im2col_step, deformable_group, + gradOffset[elt]); + + deformable_col2im(columns, offset[elt], nInputPlane, inputHeight, + inputWidth, kH, kW, padH, padW, dH, dW, dilationH, + dilationW, im2col_step, deformable_group, gradInput[elt]); + } + + gradOutput.transpose_(1, 2); + gradOutput = + gradOutput.view({batchSize, nOutputPlane, outputHeight, outputWidth}); + + gradInput = gradInput.view({batchSize, nInputPlane, inputHeight, inputWidth}); + input = input.view({batchSize, nInputPlane, inputHeight, inputWidth}); + gradOffset = gradOffset.view( + {batchSize, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + offset = offset.view( + {batchSize, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + + if (batch == 0) { + gradOutput = gradOutput.view({nOutputPlane, outputHeight, outputWidth}); + input = input.view({nInputPlane, inputHeight, inputWidth}); + gradInput = gradInput.view({nInputPlane, inputHeight, inputWidth}); + offset = offset.view({offset.size(1), offset.size(2), offset.size(3)}); + gradOffset = + gradOffset.view({offset.size(1), offset.size(2), offset.size(3)}); + } + + return 1; +} + +int deform_conv_backward_parameters_cuda( + at::Tensor input, at::Tensor offset, at::Tensor gradOutput, + at::Tensor gradWeight, // at::Tensor gradBias, + at::Tensor columns, at::Tensor ones, int kW, int kH, int dW, int dH, + int padW, int padH, int dilationW, int dilationH, int group, + int deformable_group, float scale, int im2col_step) { + // todo: transpose and reshape outGrad + // todo: reshape columns + // todo: add im2col_step as input + + shape_check(input, offset, &gradOutput, gradWeight, kH, kW, dH, dW, padH, + padW, dilationH, dilationW, group, deformable_group); + at::DeviceGuard guard(input.device()); + + input = input.contiguous(); + offset = offset.contiguous(); + gradOutput = gradOutput.contiguous(); + + int batch = 1; + + if (input.ndimension() == 3) { + // Force batch + batch = 0; + input = input.view( + at::IntList({1, input.size(0), input.size(1), input.size(2)})); + gradOutput = gradOutput.view( + {1, gradOutput.size(0), gradOutput.size(1), gradOutput.size(2)}); + } + + long batchSize = input.size(0); + long nInputPlane = input.size(1); + long inputHeight = input.size(2); + long inputWidth = input.size(3); + + long nOutputPlane = gradWeight.size(0); + + long outputWidth = + (inputWidth + 2 * padW - (dilationW * (kW - 1) + 1)) / dW + 1; + long outputHeight = + (inputHeight + 2 * padH - (dilationH * (kH - 1) + 1)) / dH + 1; + + TORCH_CHECK((offset.size(0) == batchSize), "invalid batch size of offset"); + + columns = at::zeros( + {nInputPlane * kW * kH, im2col_step * outputHeight * outputWidth}, + input.options()); + + gradOutput = gradOutput.view({batchSize / im2col_step, im2col_step, + nOutputPlane, outputHeight, outputWidth}); + gradOutput.transpose_(1, 2); + + at::Tensor gradOutputBuffer = at::zeros_like(gradOutput); + gradOutputBuffer = + gradOutputBuffer.view({batchSize / im2col_step, nOutputPlane, im2col_step, + outputHeight, outputWidth}); + gradOutputBuffer.copy_(gradOutput); + gradOutputBuffer = + gradOutputBuffer.view({batchSize / im2col_step, nOutputPlane, + im2col_step * outputHeight, outputWidth}); + + gradOutput.transpose_(1, 2); + gradOutput = + gradOutput.view({batchSize, nOutputPlane, outputHeight, outputWidth}); + + input = input.view({batchSize / im2col_step, im2col_step, nInputPlane, + inputHeight, inputWidth}); + offset = + offset.view({batchSize / im2col_step, im2col_step, + deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + + for (int elt = 0; elt < batchSize / im2col_step; elt++) { + deformable_im2col(input[elt], offset[elt], nInputPlane, inputHeight, + inputWidth, kH, kW, padH, padW, dH, dW, dilationH, + dilationW, im2col_step, deformable_group, columns); + + // divide into group + gradOutputBuffer = gradOutputBuffer.view( + {gradOutputBuffer.size(0), group, gradOutputBuffer.size(1) / group, + gradOutputBuffer.size(2), gradOutputBuffer.size(3)}); + columns = columns.view({group, columns.size(0) / group, columns.size(1)}); + gradWeight = + gradWeight.view({group, gradWeight.size(0) / group, gradWeight.size(1), + gradWeight.size(2), gradWeight.size(3)}); + + for (int g = 0; g < group; g++) { + gradWeight[g] = gradWeight[g] + .flatten(1) + .addmm_(gradOutputBuffer[elt][g].flatten(1), + columns[g].transpose(1, 0), 1.0, scale) + .view_as(gradWeight[g]); + } + gradOutputBuffer = gradOutputBuffer.view( + {gradOutputBuffer.size(0), + gradOutputBuffer.size(1) * gradOutputBuffer.size(2), + gradOutputBuffer.size(3), gradOutputBuffer.size(4)}); + columns = + columns.view({columns.size(0) * columns.size(1), columns.size(2)}); + gradWeight = gradWeight.view({gradWeight.size(0) * gradWeight.size(1), + gradWeight.size(2), gradWeight.size(3), + gradWeight.size(4)}); + } + + input = input.view({batchSize, nInputPlane, inputHeight, inputWidth}); + offset = offset.view( + {batchSize, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + + if (batch == 0) { + gradOutput = gradOutput.view({nOutputPlane, outputHeight, outputWidth}); + input = input.view({nInputPlane, inputHeight, inputWidth}); + } + + return 1; +} + +void modulated_deform_conv_cuda_forward( + at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, + at::Tensor offset, at::Tensor mask, at::Tensor output, at::Tensor columns, + int kernel_h, int kernel_w, const int stride_h, const int stride_w, + const int pad_h, const int pad_w, const int dilation_h, + const int dilation_w, const int group, const int deformable_group, + const bool with_bias) { + TORCH_CHECK(input.is_contiguous(), "input tensor has to be contiguous"); + TORCH_CHECK(weight.is_contiguous(), "weight tensor has to be contiguous"); + at::DeviceGuard guard(input.device()); + + const int batch = input.size(0); + const int channels = input.size(1); + const int height = input.size(2); + const int width = input.size(3); + + const int channels_out = weight.size(0); + const int channels_kernel = weight.size(1); + const int kernel_h_ = weight.size(2); + const int kernel_w_ = weight.size(3); + + if (kernel_h_ != kernel_h || kernel_w_ != kernel_w) + AT_ERROR("Input shape and kernel shape wont match: (%d x %d vs %d x %d).", + kernel_h_, kernel_w, kernel_h_, kernel_w_); + if (channels != channels_kernel * group) + AT_ERROR("Input shape and kernel channels wont match: (%d vs %d).", + channels, channels_kernel * group); + + const int height_out = + (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1; + const int width_out = + (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1; + + if (ones.ndimension() != 2 || + ones.size(0) * ones.size(1) < height_out * width_out) { + // Resize plane and fill with ones... + ones = at::ones({height_out, width_out}, input.options()); + } + + // resize output + output = output.view({batch, channels_out, height_out, width_out}).zero_(); + // resize temporary columns + columns = + at::zeros({channels * kernel_h * kernel_w, 1 * height_out * width_out}, + input.options()); + + output = output.view({output.size(0), group, output.size(1) / group, + output.size(2), output.size(3)}); + + for (int b = 0; b < batch; b++) { + modulated_deformable_im2col_cuda( + input[b], offset[b], mask[b], 1, channels, height, width, height_out, + width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, deformable_group, columns); + + // divide into group + weight = weight.view({group, weight.size(0) / group, weight.size(1), + weight.size(2), weight.size(3)}); + columns = columns.view({group, columns.size(0) / group, columns.size(1)}); + + for (int g = 0; g < group; g++) { + output[b][g] = output[b][g] + .flatten(1) + .addmm_(weight[g].flatten(1), columns[g]) + .view_as(output[b][g]); + } + + weight = weight.view({weight.size(0) * weight.size(1), weight.size(2), + weight.size(3), weight.size(4)}); + columns = + columns.view({columns.size(0) * columns.size(1), columns.size(2)}); + } + + output = output.view({output.size(0), output.size(1) * output.size(2), + output.size(3), output.size(4)}); + + if (with_bias) { + output += bias.view({1, bias.size(0), 1, 1}); + } +} + +void modulated_deform_conv_cuda_backward( + at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, + at::Tensor offset, at::Tensor mask, at::Tensor columns, + at::Tensor grad_input, at::Tensor grad_weight, at::Tensor grad_bias, + at::Tensor grad_offset, at::Tensor grad_mask, at::Tensor grad_output, + int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, + int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, + const bool with_bias) { + TORCH_CHECK(input.is_contiguous(), "input tensor has to be contiguous"); + TORCH_CHECK(weight.is_contiguous(), "weight tensor has to be contiguous"); + at::DeviceGuard guard(input.device()); + + const int batch = input.size(0); + const int channels = input.size(1); + const int height = input.size(2); + const int width = input.size(3); + + const int channels_kernel = weight.size(1); + const int kernel_h_ = weight.size(2); + const int kernel_w_ = weight.size(3); + if (kernel_h_ != kernel_h || kernel_w_ != kernel_w) + AT_ERROR("Input shape and kernel shape wont match: (%d x %d vs %d x %d).", + kernel_h_, kernel_w, kernel_h_, kernel_w_); + if (channels != channels_kernel * group) + AT_ERROR("Input shape and kernel channels wont match: (%d vs %d).", + channels, channels_kernel * group); + + const int height_out = + (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1; + const int width_out = + (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1; + + if (ones.ndimension() != 2 || + ones.size(0) * ones.size(1) < height_out * width_out) { + // Resize plane and fill with ones... + ones = at::ones({height_out, width_out}, input.options()); + } + + grad_input = grad_input.view({batch, channels, height, width}); + columns = at::zeros({channels * kernel_h * kernel_w, height_out * width_out}, + input.options()); + + grad_output = + grad_output.view({grad_output.size(0), group, grad_output.size(1) / group, + grad_output.size(2), grad_output.size(3)}); + + for (int b = 0; b < batch; b++) { + // divide int group + columns = columns.view({group, columns.size(0) / group, columns.size(1)}); + weight = weight.view({group, weight.size(0) / group, weight.size(1), + weight.size(2), weight.size(3)}); + + for (int g = 0; g < group; g++) { + columns[g].addmm_(weight[g].flatten(1).transpose(0, 1), + grad_output[b][g].flatten(1), 0.0f, 1.0f); + } + + columns = + columns.view({columns.size(0) * columns.size(1), columns.size(2)}); + weight = weight.view({weight.size(0) * weight.size(1), weight.size(2), + weight.size(3), weight.size(4)}); + + // gradient w.r.t. input coordinate data + modulated_deformable_col2im_coord_cuda( + columns, input[b], offset[b], mask[b], 1, channels, height, width, + height_out, width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, + stride_w, dilation_h, dilation_w, deformable_group, grad_offset[b], + grad_mask[b]); + // gradient w.r.t. input data + modulated_deformable_col2im_cuda( + columns, offset[b], mask[b], 1, channels, height, width, height_out, + width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, deformable_group, grad_input[b]); + + // gradient w.r.t. weight, dWeight should accumulate across the batch and + // group + modulated_deformable_im2col_cuda( + input[b], offset[b], mask[b], 1, channels, height, width, height_out, + width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, deformable_group, columns); + + columns = columns.view({group, columns.size(0) / group, columns.size(1)}); + grad_weight = grad_weight.view({group, grad_weight.size(0) / group, + grad_weight.size(1), grad_weight.size(2), + grad_weight.size(3)}); + if (with_bias) + grad_bias = grad_bias.view({group, grad_bias.size(0) / group}); + + for (int g = 0; g < group; g++) { + grad_weight[g] = + grad_weight[g] + .flatten(1) + .addmm_(grad_output[b][g].flatten(1), columns[g].transpose(0, 1)) + .view_as(grad_weight[g]); + if (with_bias) { + grad_bias[g] = + grad_bias[g] + .view({-1, 1}) + .addmm_(grad_output[b][g].flatten(1), ones.view({-1, 1})) + .view(-1); + } + } + + columns = + columns.view({columns.size(0) * columns.size(1), columns.size(2)}); + grad_weight = grad_weight.view({grad_weight.size(0) * grad_weight.size(1), + grad_weight.size(2), grad_weight.size(3), + grad_weight.size(4)}); + if (with_bias) + grad_bias = grad_bias.view({grad_bias.size(0) * grad_bias.size(1)}); + } + grad_output = grad_output.view({grad_output.size(0) * grad_output.size(1), + grad_output.size(2), grad_output.size(3), + grad_output.size(4)}); +} diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/src/deform_conv_cuda_kernel.cu b/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/src/deform_conv_cuda_kernel.cu new file mode 100644 index 0000000000000000000000000000000000000000..98752dccf8c58817ca1a952554dd3f33188a2d34 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/src/deform_conv_cuda_kernel.cu @@ -0,0 +1,867 @@ +/*! + ******************* BEGIN Caffe Copyright Notice and Disclaimer **************** + * + * COPYRIGHT + * + * All contributions by the University of California: + * Copyright (c) 2014-2017 The Regents of the University of California (Regents) + * All rights reserved. + * + * All other contributions: + * Copyright (c) 2014-2017, the respective contributors + * All rights reserved. + * + * Caffe uses a shared copyright model: each contributor holds copyright over + * their contributions to Caffe. The project versioning records all such + * contribution and copyright details. If a contributor wants to further mark + * their specific copyright on a particular contribution, they should indicate + * their copyright solely in the commit message of the change when it is + * committed. + * + * LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * CONTRIBUTION AGREEMENT + * + * By contributing to the BVLC/caffe repository through pull-request, comment, + * or otherwise, the contributor releases their content to the + * license and copyright terms herein. + * + ***************** END Caffe Copyright Notice and Disclaimer ******************** + * + * Copyright (c) 2018 Microsoft + * Licensed under The MIT License [see LICENSE for details] + * \file modulated_deformable_im2col.cuh + * \brief Function definitions of converting an image to + * column matrix based on kernel, padding, dilation, and offset. + * These functions are mainly used in deformable convolution operators. + * \ref: https://arxiv.org/abs/1703.06211 + * \author Yuwen Xiong, Haozhi Qi, Jifeng Dai, Xizhou Zhu, Han Hu, Dazhi Cheng + */ + +// modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/blob/mmdetection/mmdet/ops/dcn/src/deform_conv_cuda_kernel.cu + +#include +#include +#include +#include +#include +#include + +using namespace at; + +#define CUDA_KERNEL_LOOP(i, n) \ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ + i += blockDim.x * gridDim.x) + +const int CUDA_NUM_THREADS = 1024; +const int kMaxGridNum = 65535; + +inline int GET_BLOCKS(const int N) +{ + return std::min(kMaxGridNum, (N + CUDA_NUM_THREADS - 1) / CUDA_NUM_THREADS); +} + +template +__device__ scalar_t deformable_im2col_bilinear(const scalar_t *bottom_data, const int data_width, + const int height, const int width, scalar_t h, scalar_t w) +{ + + int h_low = floor(h); + int w_low = floor(w); + int h_high = h_low + 1; + int w_high = w_low + 1; + + scalar_t lh = h - h_low; + scalar_t lw = w - w_low; + scalar_t hh = 1 - lh, hw = 1 - lw; + + scalar_t v1 = 0; + if (h_low >= 0 && w_low >= 0) + v1 = bottom_data[h_low * data_width + w_low]; + scalar_t v2 = 0; + if (h_low >= 0 && w_high <= width - 1) + v2 = bottom_data[h_low * data_width + w_high]; + scalar_t v3 = 0; + if (h_high <= height - 1 && w_low >= 0) + v3 = bottom_data[h_high * data_width + w_low]; + scalar_t v4 = 0; + if (h_high <= height - 1 && w_high <= width - 1) + v4 = bottom_data[h_high * data_width + w_high]; + + scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; + + scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + return val; +} + +template +__device__ scalar_t get_gradient_weight(scalar_t argmax_h, scalar_t argmax_w, + const int h, const int w, const int height, const int width) +{ + + if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || argmax_w >= width) + { + //empty + return 0; + } + + int argmax_h_low = floor(argmax_h); + int argmax_w_low = floor(argmax_w); + int argmax_h_high = argmax_h_low + 1; + int argmax_w_high = argmax_w_low + 1; + + scalar_t weight = 0; + if (h == argmax_h_low && w == argmax_w_low) + weight = (h + 1 - argmax_h) * (w + 1 - argmax_w); + if (h == argmax_h_low && w == argmax_w_high) + weight = (h + 1 - argmax_h) * (argmax_w + 1 - w); + if (h == argmax_h_high && w == argmax_w_low) + weight = (argmax_h + 1 - h) * (w + 1 - argmax_w); + if (h == argmax_h_high && w == argmax_w_high) + weight = (argmax_h + 1 - h) * (argmax_w + 1 - w); + return weight; +} + +template +__device__ scalar_t get_coordinate_weight(scalar_t argmax_h, scalar_t argmax_w, + const int height, const int width, const scalar_t *im_data, + const int data_width, const int bp_dir) +{ + + if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || argmax_w >= width) + { + //empty + return 0; + } + + int argmax_h_low = floor(argmax_h); + int argmax_w_low = floor(argmax_w); + int argmax_h_high = argmax_h_low + 1; + int argmax_w_high = argmax_w_low + 1; + + scalar_t weight = 0; + + if (bp_dir == 0) + { + if (argmax_h_low >= 0 && argmax_w_low >= 0) + weight += -1 * (argmax_w_low + 1 - argmax_w) * im_data[argmax_h_low * data_width + argmax_w_low]; + if (argmax_h_low >= 0 && argmax_w_high <= width - 1) + weight += -1 * (argmax_w - argmax_w_low) * im_data[argmax_h_low * data_width + argmax_w_high]; + if (argmax_h_high <= height - 1 && argmax_w_low >= 0) + weight += (argmax_w_low + 1 - argmax_w) * im_data[argmax_h_high * data_width + argmax_w_low]; + if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) + weight += (argmax_w - argmax_w_low) * im_data[argmax_h_high * data_width + argmax_w_high]; + } + else if (bp_dir == 1) + { + if (argmax_h_low >= 0 && argmax_w_low >= 0) + weight += -1 * (argmax_h_low + 1 - argmax_h) * im_data[argmax_h_low * data_width + argmax_w_low]; + if (argmax_h_low >= 0 && argmax_w_high <= width - 1) + weight += (argmax_h_low + 1 - argmax_h) * im_data[argmax_h_low * data_width + argmax_w_high]; + if (argmax_h_high <= height - 1 && argmax_w_low >= 0) + weight += -1 * (argmax_h - argmax_h_low) * im_data[argmax_h_high * data_width + argmax_w_low]; + if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) + weight += (argmax_h - argmax_h_low) * im_data[argmax_h_high * data_width + argmax_w_high]; + } + + return weight; +} + +template +__global__ void deformable_im2col_gpu_kernel(const int n, const scalar_t *data_im, const scalar_t *data_offset, + const int height, const int width, const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, const int channel_per_deformable_group, + const int batch_size, const int num_channels, const int deformable_group, + const int height_col, const int width_col, + scalar_t *data_col) +{ + CUDA_KERNEL_LOOP(index, n) + { + // index index of output matrix + const int w_col = index % width_col; + const int h_col = (index / width_col) % height_col; + const int b_col = (index / width_col / height_col) % batch_size; + const int c_im = (index / width_col / height_col) / batch_size; + const int c_col = c_im * kernel_h * kernel_w; + + // compute deformable group index + const int deformable_group_index = c_im / channel_per_deformable_group; + + const int h_in = h_col * stride_h - pad_h; + const int w_in = w_col * stride_w - pad_w; + scalar_t *data_col_ptr = data_col + ((c_col * batch_size + b_col) * height_col + h_col) * width_col + w_col; + //const scalar_t* data_im_ptr = data_im + ((b_col * num_channels + c_im) * height + h_in) * width + w_in; + const scalar_t *data_im_ptr = data_im + (b_col * num_channels + c_im) * height * width; + const scalar_t *data_offset_ptr = data_offset + (b_col * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; + + for (int i = 0; i < kernel_h; ++i) + { + for (int j = 0; j < kernel_w; ++j) + { + const int data_offset_h_ptr = ((2 * (i * kernel_w + j)) * height_col + h_col) * width_col + w_col; + const int data_offset_w_ptr = ((2 * (i * kernel_w + j) + 1) * height_col + h_col) * width_col + w_col; + const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; + const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; + scalar_t val = static_cast(0); + const scalar_t h_im = h_in + i * dilation_h + offset_h; + const scalar_t w_im = w_in + j * dilation_w + offset_w; + if (h_im > -1 && w_im > -1 && h_im < height && w_im < width) + { + //const scalar_t map_h = i * dilation_h + offset_h; + //const scalar_t map_w = j * dilation_w + offset_w; + //const int cur_height = height - h_in; + //const int cur_width = width - w_in; + //val = deformable_im2col_bilinear(data_im_ptr, width, cur_height, cur_width, map_h, map_w); + val = deformable_im2col_bilinear(data_im_ptr, width, height, width, h_im, w_im); + } + *data_col_ptr = val; + data_col_ptr += batch_size * height_col * width_col; + } + } + } +} + +void deformable_im2col( + const at::Tensor data_im, const at::Tensor data_offset, const int channels, + const int height, const int width, const int ksize_h, const int ksize_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, const int parallel_imgs, + const int deformable_group, at::Tensor data_col) +{ + // num_axes should be smaller than block size + // todo: check parallel_imgs is correctly passed in + int height_col = (height + 2 * pad_h - (dilation_h * (ksize_h - 1) + 1)) / stride_h + 1; + int width_col = (width + 2 * pad_w - (dilation_w * (ksize_w - 1) + 1)) / stride_w + 1; + int num_kernels = channels * height_col * width_col * parallel_imgs; + int channel_per_deformable_group = channels / deformable_group; + + AT_DISPATCH_FLOATING_TYPES_AND_HALF( + data_im.scalar_type(), "deformable_im2col_gpu", ([&] { + const scalar_t *data_im_ = data_im.data_ptr(); + const scalar_t *data_offset_ = data_offset.data_ptr(); + scalar_t *data_col_ = data_col.data_ptr(); + + deformable_im2col_gpu_kernel<<>>( + num_kernels, data_im_, data_offset_, height, width, ksize_h, ksize_w, + pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, + channel_per_deformable_group, parallel_imgs, channels, deformable_group, + height_col, width_col, data_col_); + })); + + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in deformable_im2col: %s\n", cudaGetErrorString(err)); + } +} + +template +__global__ void deformable_col2im_gpu_kernel( + const int n, const scalar_t *data_col, const scalar_t *data_offset, + const int channels, const int height, const int width, + const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, + const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int channel_per_deformable_group, + const int batch_size, const int deformable_group, + const int height_col, const int width_col, + scalar_t *grad_im) +{ + CUDA_KERNEL_LOOP(index, n) + { + const int j = (index / width_col / height_col / batch_size) % kernel_w; + const int i = (index / width_col / height_col / batch_size / kernel_w) % kernel_h; + const int c = index / width_col / height_col / batch_size / kernel_w / kernel_h; + // compute the start and end of the output + + const int deformable_group_index = c / channel_per_deformable_group; + + int w_out = index % width_col; + int h_out = (index / width_col) % height_col; + int b = (index / width_col / height_col) % batch_size; + int w_in = w_out * stride_w - pad_w; + int h_in = h_out * stride_h - pad_h; + + const scalar_t *data_offset_ptr = data_offset + (b * deformable_group + deformable_group_index) * + 2 * kernel_h * kernel_w * height_col * width_col; + const int data_offset_h_ptr = ((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out; + const int data_offset_w_ptr = ((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out; + const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; + const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; + const scalar_t cur_inv_h_data = h_in + i * dilation_h + offset_h; + const scalar_t cur_inv_w_data = w_in + j * dilation_w + offset_w; + + const scalar_t cur_top_grad = data_col[index]; + const int cur_h = (int)cur_inv_h_data; + const int cur_w = (int)cur_inv_w_data; + for (int dy = -2; dy <= 2; dy++) + { + for (int dx = -2; dx <= 2; dx++) + { + if (cur_h + dy >= 0 && cur_h + dy < height && + cur_w + dx >= 0 && cur_w + dx < width && + abs(cur_inv_h_data - (cur_h + dy)) < 1 && + abs(cur_inv_w_data - (cur_w + dx)) < 1) + { + int cur_bottom_grad_pos = ((b * channels + c) * height + cur_h + dy) * width + cur_w + dx; + scalar_t weight = get_gradient_weight(cur_inv_h_data, cur_inv_w_data, cur_h + dy, cur_w + dx, height, width); + atomicAdd(grad_im + cur_bottom_grad_pos, weight * cur_top_grad); + } + } + } + } +} + +void deformable_col2im( + const at::Tensor data_col, const at::Tensor data_offset, const int channels, + const int height, const int width, const int ksize_h, + const int ksize_w, const int pad_h, const int pad_w, + const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int parallel_imgs, const int deformable_group, + at::Tensor grad_im) +{ + + // todo: make sure parallel_imgs is passed in correctly + int height_col = (height + 2 * pad_h - (dilation_h * (ksize_h - 1) + 1)) / stride_h + 1; + int width_col = (width + 2 * pad_w - (dilation_w * (ksize_w - 1) + 1)) / stride_w + 1; + int num_kernels = channels * ksize_h * ksize_w * height_col * width_col * parallel_imgs; + int channel_per_deformable_group = channels / deformable_group; + + AT_DISPATCH_FLOATING_TYPES_AND_HALF( + data_col.scalar_type(), "deformable_col2im_gpu", ([&] { + const scalar_t *data_col_ = data_col.data_ptr(); + const scalar_t *data_offset_ = data_offset.data_ptr(); + scalar_t *grad_im_ = grad_im.data_ptr(); + + deformable_col2im_gpu_kernel<<>>( + num_kernels, data_col_, data_offset_, channels, height, width, ksize_h, + ksize_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, channel_per_deformable_group, + parallel_imgs, deformable_group, height_col, width_col, grad_im_); + })); + + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in deformable_col2im: %s\n", cudaGetErrorString(err)); + } +} + +template +__global__ void deformable_col2im_coord_gpu_kernel(const int n, const scalar_t *data_col, + const scalar_t *data_im, const scalar_t *data_offset, + const int channels, const int height, const int width, + const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, + const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int channel_per_deformable_group, + const int batch_size, const int offset_channels, const int deformable_group, + const int height_col, const int width_col, scalar_t *grad_offset) +{ + CUDA_KERNEL_LOOP(index, n) + { + scalar_t val = 0; + int w = index % width_col; + int h = (index / width_col) % height_col; + int c = (index / width_col / height_col) % offset_channels; + int b = (index / width_col / height_col) / offset_channels; + // compute the start and end of the output + + const int deformable_group_index = c / (2 * kernel_h * kernel_w); + const int col_step = kernel_h * kernel_w; + int cnt = 0; + const scalar_t *data_col_ptr = data_col + deformable_group_index * channel_per_deformable_group * + batch_size * width_col * height_col; + const scalar_t *data_im_ptr = data_im + (b * deformable_group + deformable_group_index) * + channel_per_deformable_group / kernel_h / kernel_w * height * width; + const scalar_t *data_offset_ptr = data_offset + (b * deformable_group + deformable_group_index) * 2 * + kernel_h * kernel_w * height_col * width_col; + + const int offset_c = c - deformable_group_index * 2 * kernel_h * kernel_w; + + for (int col_c = (offset_c / 2); col_c < channel_per_deformable_group; col_c += col_step) + { + const int col_pos = (((col_c * batch_size + b) * height_col) + h) * width_col + w; + const int bp_dir = offset_c % 2; + + int j = (col_pos / width_col / height_col / batch_size) % kernel_w; + int i = (col_pos / width_col / height_col / batch_size / kernel_w) % kernel_h; + int w_out = col_pos % width_col; + int h_out = (col_pos / width_col) % height_col; + int w_in = w_out * stride_w - pad_w; + int h_in = h_out * stride_h - pad_h; + const int data_offset_h_ptr = (((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out); + const int data_offset_w_ptr = (((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out); + const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; + const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; + scalar_t inv_h = h_in + i * dilation_h + offset_h; + scalar_t inv_w = w_in + j * dilation_w + offset_w; + if (inv_h <= -1 || inv_w <= -1 || inv_h >= height || inv_w >= width) + { + inv_h = inv_w = -2; + } + const scalar_t weight = get_coordinate_weight( + inv_h, inv_w, + height, width, data_im_ptr + cnt * height * width, width, bp_dir); + val += weight * data_col_ptr[col_pos]; + cnt += 1; + } + + grad_offset[index] = val; + } +} + +void deformable_col2im_coord( + const at::Tensor data_col, const at::Tensor data_im, const at::Tensor data_offset, + const int channels, const int height, const int width, const int ksize_h, + const int ksize_w, const int pad_h, const int pad_w, const int stride_h, + const int stride_w, const int dilation_h, const int dilation_w, + const int parallel_imgs, const int deformable_group, at::Tensor grad_offset) +{ + + int height_col = (height + 2 * pad_h - (dilation_h * (ksize_h - 1) + 1)) / stride_h + 1; + int width_col = (width + 2 * pad_w - (dilation_w * (ksize_w - 1) + 1)) / stride_w + 1; + int num_kernels = height_col * width_col * 2 * ksize_h * ksize_w * deformable_group * parallel_imgs; + int channel_per_deformable_group = channels * ksize_h * ksize_w / deformable_group; + + AT_DISPATCH_FLOATING_TYPES_AND_HALF( + data_col.scalar_type(), "deformable_col2im_coord_gpu", ([&] { + const scalar_t *data_col_ = data_col.data_ptr(); + const scalar_t *data_im_ = data_im.data_ptr(); + const scalar_t *data_offset_ = data_offset.data_ptr(); + scalar_t *grad_offset_ = grad_offset.data_ptr(); + + deformable_col2im_coord_gpu_kernel<<>>( + num_kernels, data_col_, data_im_, data_offset_, channels, height, width, + ksize_h, ksize_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, channel_per_deformable_group, + parallel_imgs, 2 * ksize_h * ksize_w * deformable_group, deformable_group, + height_col, width_col, grad_offset_); + })); +} + +template +__device__ scalar_t dmcn_im2col_bilinear(const scalar_t *bottom_data, const int data_width, + const int height, const int width, scalar_t h, scalar_t w) +{ + int h_low = floor(h); + int w_low = floor(w); + int h_high = h_low + 1; + int w_high = w_low + 1; + + scalar_t lh = h - h_low; + scalar_t lw = w - w_low; + scalar_t hh = 1 - lh, hw = 1 - lw; + + scalar_t v1 = 0; + if (h_low >= 0 && w_low >= 0) + v1 = bottom_data[h_low * data_width + w_low]; + scalar_t v2 = 0; + if (h_low >= 0 && w_high <= width - 1) + v2 = bottom_data[h_low * data_width + w_high]; + scalar_t v3 = 0; + if (h_high <= height - 1 && w_low >= 0) + v3 = bottom_data[h_high * data_width + w_low]; + scalar_t v4 = 0; + if (h_high <= height - 1 && w_high <= width - 1) + v4 = bottom_data[h_high * data_width + w_high]; + + scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; + + scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + return val; +} + +template +__device__ scalar_t dmcn_get_gradient_weight(scalar_t argmax_h, scalar_t argmax_w, + const int h, const int w, const int height, const int width) +{ + if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || argmax_w >= width) + { + //empty + return 0; + } + + int argmax_h_low = floor(argmax_h); + int argmax_w_low = floor(argmax_w); + int argmax_h_high = argmax_h_low + 1; + int argmax_w_high = argmax_w_low + 1; + + scalar_t weight = 0; + if (h == argmax_h_low && w == argmax_w_low) + weight = (h + 1 - argmax_h) * (w + 1 - argmax_w); + if (h == argmax_h_low && w == argmax_w_high) + weight = (h + 1 - argmax_h) * (argmax_w + 1 - w); + if (h == argmax_h_high && w == argmax_w_low) + weight = (argmax_h + 1 - h) * (w + 1 - argmax_w); + if (h == argmax_h_high && w == argmax_w_high) + weight = (argmax_h + 1 - h) * (argmax_w + 1 - w); + return weight; +} + +template +__device__ scalar_t dmcn_get_coordinate_weight(scalar_t argmax_h, scalar_t argmax_w, + const int height, const int width, const scalar_t *im_data, + const int data_width, const int bp_dir) +{ + if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || argmax_w >= width) + { + //empty + return 0; + } + + int argmax_h_low = floor(argmax_h); + int argmax_w_low = floor(argmax_w); + int argmax_h_high = argmax_h_low + 1; + int argmax_w_high = argmax_w_low + 1; + + scalar_t weight = 0; + + if (bp_dir == 0) + { + if (argmax_h_low >= 0 && argmax_w_low >= 0) + weight += -1 * (argmax_w_low + 1 - argmax_w) * im_data[argmax_h_low * data_width + argmax_w_low]; + if (argmax_h_low >= 0 && argmax_w_high <= width - 1) + weight += -1 * (argmax_w - argmax_w_low) * im_data[argmax_h_low * data_width + argmax_w_high]; + if (argmax_h_high <= height - 1 && argmax_w_low >= 0) + weight += (argmax_w_low + 1 - argmax_w) * im_data[argmax_h_high * data_width + argmax_w_low]; + if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) + weight += (argmax_w - argmax_w_low) * im_data[argmax_h_high * data_width + argmax_w_high]; + } + else if (bp_dir == 1) + { + if (argmax_h_low >= 0 && argmax_w_low >= 0) + weight += -1 * (argmax_h_low + 1 - argmax_h) * im_data[argmax_h_low * data_width + argmax_w_low]; + if (argmax_h_low >= 0 && argmax_w_high <= width - 1) + weight += (argmax_h_low + 1 - argmax_h) * im_data[argmax_h_low * data_width + argmax_w_high]; + if (argmax_h_high <= height - 1 && argmax_w_low >= 0) + weight += -1 * (argmax_h - argmax_h_low) * im_data[argmax_h_high * data_width + argmax_w_low]; + if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) + weight += (argmax_h - argmax_h_low) * im_data[argmax_h_high * data_width + argmax_w_high]; + } + + return weight; +} + +template +__global__ void modulated_deformable_im2col_gpu_kernel(const int n, + const scalar_t *data_im, const scalar_t *data_offset, const scalar_t *data_mask, + const int height, const int width, const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, + const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int channel_per_deformable_group, + const int batch_size, const int num_channels, const int deformable_group, + const int height_col, const int width_col, + scalar_t *data_col) +{ + CUDA_KERNEL_LOOP(index, n) + { + // index index of output matrix + const int w_col = index % width_col; + const int h_col = (index / width_col) % height_col; + const int b_col = (index / width_col / height_col) % batch_size; + const int c_im = (index / width_col / height_col) / batch_size; + const int c_col = c_im * kernel_h * kernel_w; + + // compute deformable group index + const int deformable_group_index = c_im / channel_per_deformable_group; + + const int h_in = h_col * stride_h - pad_h; + const int w_in = w_col * stride_w - pad_w; + + scalar_t *data_col_ptr = data_col + ((c_col * batch_size + b_col) * height_col + h_col) * width_col + w_col; + //const float* data_im_ptr = data_im + ((b_col * num_channels + c_im) * height + h_in) * width + w_in; + const scalar_t *data_im_ptr = data_im + (b_col * num_channels + c_im) * height * width; + const scalar_t *data_offset_ptr = data_offset + (b_col * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; + + const scalar_t *data_mask_ptr = data_mask + (b_col * deformable_group + deformable_group_index) * kernel_h * kernel_w * height_col * width_col; + + for (int i = 0; i < kernel_h; ++i) + { + for (int j = 0; j < kernel_w; ++j) + { + const int data_offset_h_ptr = ((2 * (i * kernel_w + j)) * height_col + h_col) * width_col + w_col; + const int data_offset_w_ptr = ((2 * (i * kernel_w + j) + 1) * height_col + h_col) * width_col + w_col; + const int data_mask_hw_ptr = ((i * kernel_w + j) * height_col + h_col) * width_col + w_col; + const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; + const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; + const scalar_t mask = data_mask_ptr[data_mask_hw_ptr]; + scalar_t val = static_cast(0); + const scalar_t h_im = h_in + i * dilation_h + offset_h; + const scalar_t w_im = w_in + j * dilation_w + offset_w; + //if (h_im >= 0 && w_im >= 0 && h_im < height && w_im < width) { + if (h_im > -1 && w_im > -1 && h_im < height && w_im < width) + { + //const float map_h = i * dilation_h + offset_h; + //const float map_w = j * dilation_w + offset_w; + //const int cur_height = height - h_in; + //const int cur_width = width - w_in; + //val = dmcn_im2col_bilinear(data_im_ptr, width, cur_height, cur_width, map_h, map_w); + val = dmcn_im2col_bilinear(data_im_ptr, width, height, width, h_im, w_im); + } + *data_col_ptr = val * mask; + data_col_ptr += batch_size * height_col * width_col; + //data_col_ptr += height_col * width_col; + } + } + } +} + +template +__global__ void modulated_deformable_col2im_gpu_kernel(const int n, + const scalar_t *data_col, const scalar_t *data_offset, const scalar_t *data_mask, + const int channels, const int height, const int width, + const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, + const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int channel_per_deformable_group, + const int batch_size, const int deformable_group, + const int height_col, const int width_col, + scalar_t *grad_im) +{ + CUDA_KERNEL_LOOP(index, n) + { + const int j = (index / width_col / height_col / batch_size) % kernel_w; + const int i = (index / width_col / height_col / batch_size / kernel_w) % kernel_h; + const int c = index / width_col / height_col / batch_size / kernel_w / kernel_h; + // compute the start and end of the output + + const int deformable_group_index = c / channel_per_deformable_group; + + int w_out = index % width_col; + int h_out = (index / width_col) % height_col; + int b = (index / width_col / height_col) % batch_size; + int w_in = w_out * stride_w - pad_w; + int h_in = h_out * stride_h - pad_h; + + const scalar_t *data_offset_ptr = data_offset + (b * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; + const scalar_t *data_mask_ptr = data_mask + (b * deformable_group + deformable_group_index) * kernel_h * kernel_w * height_col * width_col; + const int data_offset_h_ptr = ((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out; + const int data_offset_w_ptr = ((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out; + const int data_mask_hw_ptr = ((i * kernel_w + j) * height_col + h_out) * width_col + w_out; + const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; + const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; + const scalar_t mask = data_mask_ptr[data_mask_hw_ptr]; + const scalar_t cur_inv_h_data = h_in + i * dilation_h + offset_h; + const scalar_t cur_inv_w_data = w_in + j * dilation_w + offset_w; + + const scalar_t cur_top_grad = data_col[index] * mask; + const int cur_h = (int)cur_inv_h_data; + const int cur_w = (int)cur_inv_w_data; + for (int dy = -2; dy <= 2; dy++) + { + for (int dx = -2; dx <= 2; dx++) + { + if (cur_h + dy >= 0 && cur_h + dy < height && + cur_w + dx >= 0 && cur_w + dx < width && + abs(cur_inv_h_data - (cur_h + dy)) < 1 && + abs(cur_inv_w_data - (cur_w + dx)) < 1) + { + int cur_bottom_grad_pos = ((b * channels + c) * height + cur_h + dy) * width + cur_w + dx; + scalar_t weight = dmcn_get_gradient_weight(cur_inv_h_data, cur_inv_w_data, cur_h + dy, cur_w + dx, height, width); + atomicAdd(grad_im + cur_bottom_grad_pos, weight * cur_top_grad); + } + } + } + } +} + +template +__global__ void modulated_deformable_col2im_coord_gpu_kernel(const int n, + const scalar_t *data_col, const scalar_t *data_im, + const scalar_t *data_offset, const scalar_t *data_mask, + const int channels, const int height, const int width, + const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, + const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int channel_per_deformable_group, + const int batch_size, const int offset_channels, const int deformable_group, + const int height_col, const int width_col, + scalar_t *grad_offset, scalar_t *grad_mask) +{ + CUDA_KERNEL_LOOP(index, n) + { + scalar_t val = 0, mval = 0; + int w = index % width_col; + int h = (index / width_col) % height_col; + int c = (index / width_col / height_col) % offset_channels; + int b = (index / width_col / height_col) / offset_channels; + // compute the start and end of the output + + const int deformable_group_index = c / (2 * kernel_h * kernel_w); + const int col_step = kernel_h * kernel_w; + int cnt = 0; + const scalar_t *data_col_ptr = data_col + deformable_group_index * channel_per_deformable_group * batch_size * width_col * height_col; + const scalar_t *data_im_ptr = data_im + (b * deformable_group + deformable_group_index) * channel_per_deformable_group / kernel_h / kernel_w * height * width; + const scalar_t *data_offset_ptr = data_offset + (b * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; + const scalar_t *data_mask_ptr = data_mask + (b * deformable_group + deformable_group_index) * kernel_h * kernel_w * height_col * width_col; + + const int offset_c = c - deformable_group_index * 2 * kernel_h * kernel_w; + + for (int col_c = (offset_c / 2); col_c < channel_per_deformable_group; col_c += col_step) + { + const int col_pos = (((col_c * batch_size + b) * height_col) + h) * width_col + w; + const int bp_dir = offset_c % 2; + + int j = (col_pos / width_col / height_col / batch_size) % kernel_w; + int i = (col_pos / width_col / height_col / batch_size / kernel_w) % kernel_h; + int w_out = col_pos % width_col; + int h_out = (col_pos / width_col) % height_col; + int w_in = w_out * stride_w - pad_w; + int h_in = h_out * stride_h - pad_h; + const int data_offset_h_ptr = (((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out); + const int data_offset_w_ptr = (((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out); + const int data_mask_hw_ptr = (((i * kernel_w + j) * height_col + h_out) * width_col + w_out); + const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; + const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; + const scalar_t mask = data_mask_ptr[data_mask_hw_ptr]; + scalar_t inv_h = h_in + i * dilation_h + offset_h; + scalar_t inv_w = w_in + j * dilation_w + offset_w; + if (inv_h <= -1 || inv_w <= -1 || inv_h >= height || inv_w >= width) + { + inv_h = inv_w = -2; + } + else + { + mval += data_col_ptr[col_pos] * dmcn_im2col_bilinear(data_im_ptr + cnt * height * width, width, height, width, inv_h, inv_w); + } + const scalar_t weight = dmcn_get_coordinate_weight( + inv_h, inv_w, + height, width, data_im_ptr + cnt * height * width, width, bp_dir); + val += weight * data_col_ptr[col_pos] * mask; + cnt += 1; + } + // KERNEL_ASSIGN(grad_offset[index], offset_req, val); + grad_offset[index] = val; + if (offset_c % 2 == 0) + // KERNEL_ASSIGN(grad_mask[(((b * deformable_group + deformable_group_index) * kernel_h * kernel_w + offset_c / 2) * height_col + h) * width_col + w], mask_req, mval); + grad_mask[(((b * deformable_group + deformable_group_index) * kernel_h * kernel_w + offset_c / 2) * height_col + h) * width_col + w] = mval; + } +} + +void modulated_deformable_im2col_cuda( + const at::Tensor data_im, const at::Tensor data_offset, const at::Tensor data_mask, + const int batch_size, const int channels, const int height_im, const int width_im, + const int height_col, const int width_col, const int kernel_h, const int kenerl_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int deformable_group, at::Tensor data_col) +{ + // num_axes should be smaller than block size + const int channel_per_deformable_group = channels / deformable_group; + const int num_kernels = channels * batch_size * height_col * width_col; + + AT_DISPATCH_FLOATING_TYPES_AND_HALF( + data_im.scalar_type(), "modulated_deformable_im2col_gpu", ([&] { + const scalar_t *data_im_ = data_im.data_ptr(); + const scalar_t *data_offset_ = data_offset.data_ptr(); + const scalar_t *data_mask_ = data_mask.data_ptr(); + scalar_t *data_col_ = data_col.data_ptr(); + + modulated_deformable_im2col_gpu_kernel<<>>( + num_kernels, data_im_, data_offset_, data_mask_, height_im, width_im, kernel_h, kenerl_w, + pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, channel_per_deformable_group, + batch_size, channels, deformable_group, height_col, width_col, data_col_); + })); + + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in modulated_deformable_im2col_cuda: %s\n", cudaGetErrorString(err)); + } +} + +void modulated_deformable_col2im_cuda( + const at::Tensor data_col, const at::Tensor data_offset, const at::Tensor data_mask, + const int batch_size, const int channels, const int height_im, const int width_im, + const int height_col, const int width_col, const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int deformable_group, at::Tensor grad_im) +{ + + const int channel_per_deformable_group = channels / deformable_group; + const int num_kernels = channels * kernel_h * kernel_w * batch_size * height_col * width_col; + + AT_DISPATCH_FLOATING_TYPES_AND_HALF( + data_col.scalar_type(), "modulated_deformable_col2im_gpu", ([&] { + const scalar_t *data_col_ = data_col.data_ptr(); + const scalar_t *data_offset_ = data_offset.data_ptr(); + const scalar_t *data_mask_ = data_mask.data_ptr(); + scalar_t *grad_im_ = grad_im.data_ptr(); + + modulated_deformable_col2im_gpu_kernel<<>>( + num_kernels, data_col_, data_offset_, data_mask_, channels, height_im, width_im, + kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, channel_per_deformable_group, + batch_size, deformable_group, height_col, width_col, grad_im_); + })); + + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in modulated_deformable_col2im_cuda: %s\n", cudaGetErrorString(err)); + } +} + +void modulated_deformable_col2im_coord_cuda( + const at::Tensor data_col, const at::Tensor data_im, const at::Tensor data_offset, const at::Tensor data_mask, + const int batch_size, const int channels, const int height_im, const int width_im, + const int height_col, const int width_col, const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int deformable_group, + at::Tensor grad_offset, at::Tensor grad_mask) +{ + const int num_kernels = batch_size * height_col * width_col * 2 * kernel_h * kernel_w * deformable_group; + const int channel_per_deformable_group = channels * kernel_h * kernel_w / deformable_group; + + AT_DISPATCH_FLOATING_TYPES_AND_HALF( + data_col.scalar_type(), "modulated_deformable_col2im_coord_gpu", ([&] { + const scalar_t *data_col_ = data_col.data_ptr(); + const scalar_t *data_im_ = data_im.data_ptr(); + const scalar_t *data_offset_ = data_offset.data_ptr(); + const scalar_t *data_mask_ = data_mask.data_ptr(); + scalar_t *grad_offset_ = grad_offset.data_ptr(); + scalar_t *grad_mask_ = grad_mask.data_ptr(); + + modulated_deformable_col2im_coord_gpu_kernel<<>>( + num_kernels, data_col_, data_im_, data_offset_, data_mask_, channels, height_im, width_im, + kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, channel_per_deformable_group, + batch_size, 2 * kernel_h * kernel_w * deformable_group, deformable_group, height_col, width_col, + grad_offset_, grad_mask_); + })); + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in modulated_deformable_col2im_coord_cuda: %s\n", cudaGetErrorString(err)); + } +} diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/src/deform_conv_ext.cpp b/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/src/deform_conv_ext.cpp new file mode 100644 index 0000000000000000000000000000000000000000..41c6df6f721bd95a525fd6a03dd9882e863de042 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/dcn/src/deform_conv_ext.cpp @@ -0,0 +1,164 @@ +// modify from +// https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/blob/mmdetection/mmdet/ops/dcn/src/deform_conv_cuda.c + +#include +#include + +#include +#include + +#define WITH_CUDA // always use cuda +#ifdef WITH_CUDA +int deform_conv_forward_cuda(at::Tensor input, at::Tensor weight, + at::Tensor offset, at::Tensor output, + at::Tensor columns, at::Tensor ones, int kW, + int kH, int dW, int dH, int padW, int padH, + int dilationW, int dilationH, int group, + int deformable_group, int im2col_step); + +int deform_conv_backward_input_cuda(at::Tensor input, at::Tensor offset, + at::Tensor gradOutput, at::Tensor gradInput, + at::Tensor gradOffset, at::Tensor weight, + at::Tensor columns, int kW, int kH, int dW, + int dH, int padW, int padH, int dilationW, + int dilationH, int group, + int deformable_group, int im2col_step); + +int deform_conv_backward_parameters_cuda( + at::Tensor input, at::Tensor offset, at::Tensor gradOutput, + at::Tensor gradWeight, // at::Tensor gradBias, + at::Tensor columns, at::Tensor ones, int kW, int kH, int dW, int dH, + int padW, int padH, int dilationW, int dilationH, int group, + int deformable_group, float scale, int im2col_step); + +void modulated_deform_conv_cuda_forward( + at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, + at::Tensor offset, at::Tensor mask, at::Tensor output, at::Tensor columns, + int kernel_h, int kernel_w, const int stride_h, const int stride_w, + const int pad_h, const int pad_w, const int dilation_h, + const int dilation_w, const int group, const int deformable_group, + const bool with_bias); + +void modulated_deform_conv_cuda_backward( + at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, + at::Tensor offset, at::Tensor mask, at::Tensor columns, + at::Tensor grad_input, at::Tensor grad_weight, at::Tensor grad_bias, + at::Tensor grad_offset, at::Tensor grad_mask, at::Tensor grad_output, + int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, + int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, + const bool with_bias); +#endif + +int deform_conv_forward(at::Tensor input, at::Tensor weight, + at::Tensor offset, at::Tensor output, + at::Tensor columns, at::Tensor ones, int kW, + int kH, int dW, int dH, int padW, int padH, + int dilationW, int dilationH, int group, + int deformable_group, int im2col_step) { + if (input.device().is_cuda()) { +#ifdef WITH_CUDA + return deform_conv_forward_cuda(input, weight, offset, output, columns, + ones, kW, kH, dW, dH, padW, padH, dilationW, dilationH, group, + deformable_group, im2col_step); +#else + AT_ERROR("deform conv is not compiled with GPU support"); +#endif + } + AT_ERROR("deform conv is not implemented on CPU"); +} + +int deform_conv_backward_input(at::Tensor input, at::Tensor offset, + at::Tensor gradOutput, at::Tensor gradInput, + at::Tensor gradOffset, at::Tensor weight, + at::Tensor columns, int kW, int kH, int dW, + int dH, int padW, int padH, int dilationW, + int dilationH, int group, + int deformable_group, int im2col_step) { + if (input.device().is_cuda()) { +#ifdef WITH_CUDA + return deform_conv_backward_input_cuda(input, offset, gradOutput, + gradInput, gradOffset, weight, columns, kW, kH, dW, dH, padW, padH, + dilationW, dilationH, group, deformable_group, im2col_step); +#else + AT_ERROR("deform conv is not compiled with GPU support"); +#endif + } + AT_ERROR("deform conv is not implemented on CPU"); +} + +int deform_conv_backward_parameters( + at::Tensor input, at::Tensor offset, at::Tensor gradOutput, + at::Tensor gradWeight, // at::Tensor gradBias, + at::Tensor columns, at::Tensor ones, int kW, int kH, int dW, int dH, + int padW, int padH, int dilationW, int dilationH, int group, + int deformable_group, float scale, int im2col_step) { + if (input.device().is_cuda()) { +#ifdef WITH_CUDA + return deform_conv_backward_parameters_cuda(input, offset, gradOutput, + gradWeight, columns, ones, kW, kH, dW, dH, padW, padH, dilationW, + dilationH, group, deformable_group, scale, im2col_step); +#else + AT_ERROR("deform conv is not compiled with GPU support"); +#endif + } + AT_ERROR("deform conv is not implemented on CPU"); +} + +void modulated_deform_conv_forward( + at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, + at::Tensor offset, at::Tensor mask, at::Tensor output, at::Tensor columns, + int kernel_h, int kernel_w, const int stride_h, const int stride_w, + const int pad_h, const int pad_w, const int dilation_h, + const int dilation_w, const int group, const int deformable_group, + const bool with_bias) { + if (input.device().is_cuda()) { +#ifdef WITH_CUDA + return modulated_deform_conv_cuda_forward(input, weight, bias, ones, + offset, mask, output, columns, kernel_h, kernel_w, stride_h, + stride_w, pad_h, pad_w, dilation_h, dilation_w, group, + deformable_group, with_bias); +#else + AT_ERROR("modulated deform conv is not compiled with GPU support"); +#endif + } + AT_ERROR("modulated deform conv is not implemented on CPU"); +} + +void modulated_deform_conv_backward( + at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, + at::Tensor offset, at::Tensor mask, at::Tensor columns, + at::Tensor grad_input, at::Tensor grad_weight, at::Tensor grad_bias, + at::Tensor grad_offset, at::Tensor grad_mask, at::Tensor grad_output, + int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, + int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, + const bool with_bias) { + if (input.device().is_cuda()) { +#ifdef WITH_CUDA + return modulated_deform_conv_cuda_backward(input, weight, bias, ones, + offset, mask, columns, grad_input, grad_weight, grad_bias, grad_offset, + grad_mask, grad_output, kernel_h, kernel_w, stride_h, stride_w, + pad_h, pad_w, dilation_h, dilation_w, group, deformable_group, + with_bias); +#else + AT_ERROR("modulated deform conv is not compiled with GPU support"); +#endif + } + AT_ERROR("modulated deform conv is not implemented on CPU"); +} + + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("deform_conv_forward", &deform_conv_forward, + "deform forward"); + m.def("deform_conv_backward_input", &deform_conv_backward_input, + "deform_conv_backward_input"); + m.def("deform_conv_backward_parameters", + &deform_conv_backward_parameters, + "deform_conv_backward_parameters"); + m.def("modulated_deform_conv_forward", + &modulated_deform_conv_forward, + "modulated deform conv forward"); + m.def("modulated_deform_conv_backward", + &modulated_deform_conv_backward, + "modulated deform conv backward"); +} diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/fused_act/__init__.py b/sd/stablediffusion/src/codeformer/basicsr/ops/fused_act/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..241dc0754fae7d88dbbd9a02e665ca30a73c7422 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/fused_act/__init__.py @@ -0,0 +1,3 @@ +from .fused_act import FusedLeakyReLU, fused_leaky_relu + +__all__ = ['FusedLeakyReLU', 'fused_leaky_relu'] diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/fused_act/fused_act.py b/sd/stablediffusion/src/codeformer/basicsr/ops/fused_act/fused_act.py new file mode 100644 index 0000000000000000000000000000000000000000..588f815e596ab0fc83ab0f9d21426c22ec5ed7c3 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/fused_act/fused_act.py @@ -0,0 +1,89 @@ +# modify from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/fused_act.py # noqa:E501 + +import torch +from torch import nn +from torch.autograd import Function + +try: + from . import fused_act_ext +except ImportError: + import os + BASICSR_JIT = os.getenv('BASICSR_JIT') + if BASICSR_JIT == 'True': + from torch.utils.cpp_extension import load + module_path = os.path.dirname(__file__) + fused_act_ext = load( + 'fused', + sources=[ + os.path.join(module_path, 'src', 'fused_bias_act.cpp'), + os.path.join(module_path, 'src', 'fused_bias_act_kernel.cu'), + ], + ) + + +class FusedLeakyReLUFunctionBackward(Function): + + @staticmethod + def forward(ctx, grad_output, out, negative_slope, scale): + ctx.save_for_backward(out) + ctx.negative_slope = negative_slope + ctx.scale = scale + + empty = grad_output.new_empty(0) + + grad_input = fused_act_ext.fused_bias_act(grad_output, empty, out, 3, 1, negative_slope, scale) + + dim = [0] + + if grad_input.ndim > 2: + dim += list(range(2, grad_input.ndim)) + + grad_bias = grad_input.sum(dim).detach() + + return grad_input, grad_bias + + @staticmethod + def backward(ctx, gradgrad_input, gradgrad_bias): + out, = ctx.saved_tensors + gradgrad_out = fused_act_ext.fused_bias_act(gradgrad_input, gradgrad_bias, out, 3, 1, ctx.negative_slope, + ctx.scale) + + return gradgrad_out, None, None, None + + +class FusedLeakyReLUFunction(Function): + + @staticmethod + def forward(ctx, input, bias, negative_slope, scale): + empty = input.new_empty(0) + out = fused_act_ext.fused_bias_act(input, bias, empty, 3, 0, negative_slope, scale) + ctx.save_for_backward(out) + ctx.negative_slope = negative_slope + ctx.scale = scale + + return out + + @staticmethod + def backward(ctx, grad_output): + out, = ctx.saved_tensors + + grad_input, grad_bias = FusedLeakyReLUFunctionBackward.apply(grad_output, out, ctx.negative_slope, ctx.scale) + + return grad_input, grad_bias, None, None + + +class FusedLeakyReLU(nn.Module): + + def __init__(self, channel, negative_slope=0.2, scale=2**0.5): + super().__init__() + + self.bias = nn.Parameter(torch.zeros(channel)) + self.negative_slope = negative_slope + self.scale = scale + + def forward(self, input): + return fused_leaky_relu(input, self.bias, self.negative_slope, self.scale) + + +def fused_leaky_relu(input, bias, negative_slope=0.2, scale=2**0.5): + return FusedLeakyReLUFunction.apply(input, bias, negative_slope, scale) diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/fused_act/src/fused_bias_act.cpp b/sd/stablediffusion/src/codeformer/basicsr/ops/fused_act/src/fused_bias_act.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85ed0a79fb9c75f83470ac834090f03608d998ee --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/fused_act/src/fused_bias_act.cpp @@ -0,0 +1,26 @@ +// from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/fused_bias_act.cpp +#include + + +torch::Tensor fused_bias_act_op(const torch::Tensor& input, + const torch::Tensor& bias, + const torch::Tensor& refer, + int act, int grad, float alpha, float scale); + +#define CHECK_CUDA(x) TORCH_CHECK(x.type().is_cuda(), #x " must be a CUDA tensor") +#define CHECK_CONTIGUOUS(x) TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") +#define CHECK_INPUT(x) CHECK_CUDA(x); CHECK_CONTIGUOUS(x) + +torch::Tensor fused_bias_act(const torch::Tensor& input, + const torch::Tensor& bias, + const torch::Tensor& refer, + int act, int grad, float alpha, float scale) { + CHECK_CUDA(input); + CHECK_CUDA(bias); + + return fused_bias_act_op(input, bias, refer, act, grad, alpha, scale); +} + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("fused_bias_act", &fused_bias_act, "fused bias act (CUDA)"); +} diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/fused_act/src/fused_bias_act_kernel.cu b/sd/stablediffusion/src/codeformer/basicsr/ops/fused_act/src/fused_bias_act_kernel.cu new file mode 100644 index 0000000000000000000000000000000000000000..54c7ff53ce8306db2b3c582ec7fa6696a38b4df0 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/fused_act/src/fused_bias_act_kernel.cu @@ -0,0 +1,100 @@ +// from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/fused_bias_act_kernel.cu +// Copyright (c) 2019, NVIDIA Corporation. All rights reserved. +// +// This work is made available under the Nvidia Source Code License-NC. +// To view a copy of this license, visit +// https://nvlabs.github.io/stylegan2/license.html + +#include + +#include +#include +#include +#include + +#include +#include + + +template +static __global__ void fused_bias_act_kernel(scalar_t* out, const scalar_t* p_x, const scalar_t* p_b, const scalar_t* p_ref, + int act, int grad, scalar_t alpha, scalar_t scale, int loop_x, int size_x, int step_b, int size_b, int use_bias, int use_ref) { + int xi = blockIdx.x * loop_x * blockDim.x + threadIdx.x; + + scalar_t zero = 0.0; + + for (int loop_idx = 0; loop_idx < loop_x && xi < size_x; loop_idx++, xi += blockDim.x) { + scalar_t x = p_x[xi]; + + if (use_bias) { + x += p_b[(xi / step_b) % size_b]; + } + + scalar_t ref = use_ref ? p_ref[xi] : zero; + + scalar_t y; + + switch (act * 10 + grad) { + default: + case 10: y = x; break; + case 11: y = x; break; + case 12: y = 0.0; break; + + case 30: y = (x > 0.0) ? x : x * alpha; break; + case 31: y = (ref > 0.0) ? x : x * alpha; break; + case 32: y = 0.0; break; + } + + out[xi] = y * scale; + } +} + + +torch::Tensor fused_bias_act_op(const torch::Tensor& input, const torch::Tensor& bias, const torch::Tensor& refer, + int act, int grad, float alpha, float scale) { + int curDevice = -1; + cudaGetDevice(&curDevice); + cudaStream_t stream = at::cuda::getCurrentCUDAStream(curDevice); + + auto x = input.contiguous(); + auto b = bias.contiguous(); + auto ref = refer.contiguous(); + + int use_bias = b.numel() ? 1 : 0; + int use_ref = ref.numel() ? 1 : 0; + + int size_x = x.numel(); + int size_b = b.numel(); + int step_b = 1; + + for (int i = 1 + 1; i < x.dim(); i++) { + step_b *= x.size(i); + } + + int loop_x = 4; + int block_size = 4 * 32; + int grid_size = (size_x - 1) / (loop_x * block_size) + 1; + + auto y = torch::empty_like(x); + + AT_DISPATCH_FLOATING_TYPES_AND_HALF(x.scalar_type(), "fused_bias_act_kernel", [&] { + fused_bias_act_kernel<<>>( + y.data_ptr(), + x.data_ptr(), + b.data_ptr(), + ref.data_ptr(), + act, + grad, + alpha, + scale, + loop_x, + size_x, + step_b, + size_b, + use_bias, + use_ref + ); + }); + + return y; +} diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/upfirdn2d/__init__.py b/sd/stablediffusion/src/codeformer/basicsr/ops/upfirdn2d/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..397e85bea063e97fc4c12ad4d3e15669b69290bd --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/upfirdn2d/__init__.py @@ -0,0 +1,3 @@ +from .upfirdn2d import upfirdn2d + +__all__ = ['upfirdn2d'] diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d.cpp b/sd/stablediffusion/src/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d.cpp new file mode 100644 index 0000000000000000000000000000000000000000..43d0b6783a5b512b55815a291fcac2bebeea31e0 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d.cpp @@ -0,0 +1,24 @@ +// from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/upfirdn2d.cpp +#include + + +torch::Tensor upfirdn2d_op(const torch::Tensor& input, const torch::Tensor& kernel, + int up_x, int up_y, int down_x, int down_y, + int pad_x0, int pad_x1, int pad_y0, int pad_y1); + +#define CHECK_CUDA(x) TORCH_CHECK(x.type().is_cuda(), #x " must be a CUDA tensor") +#define CHECK_CONTIGUOUS(x) TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") +#define CHECK_INPUT(x) CHECK_CUDA(x); CHECK_CONTIGUOUS(x) + +torch::Tensor upfirdn2d(const torch::Tensor& input, const torch::Tensor& kernel, + int up_x, int up_y, int down_x, int down_y, + int pad_x0, int pad_x1, int pad_y0, int pad_y1) { + CHECK_CUDA(input); + CHECK_CUDA(kernel); + + return upfirdn2d_op(input, kernel, up_x, up_y, down_x, down_y, pad_x0, pad_x1, pad_y0, pad_y1); +} + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("upfirdn2d", &upfirdn2d, "upfirdn2d (CUDA)"); +} diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d_kernel.cu b/sd/stablediffusion/src/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d_kernel.cu new file mode 100644 index 0000000000000000000000000000000000000000..8870063bae4468deab2e721f0978fe9facfb01b1 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d_kernel.cu @@ -0,0 +1,370 @@ +// from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/upfirdn2d_kernel.cu +// Copyright (c) 2019, NVIDIA Corporation. All rights reserved. +// +// This work is made available under the Nvidia Source Code License-NC. +// To view a copy of this license, visit +// https://nvlabs.github.io/stylegan2/license.html + +#include + +#include +#include +#include +#include + +#include +#include + +static __host__ __device__ __forceinline__ int floor_div(int a, int b) { + int c = a / b; + + if (c * b > a) { + c--; + } + + return c; +} + +struct UpFirDn2DKernelParams { + int up_x; + int up_y; + int down_x; + int down_y; + int pad_x0; + int pad_x1; + int pad_y0; + int pad_y1; + + int major_dim; + int in_h; + int in_w; + int minor_dim; + int kernel_h; + int kernel_w; + int out_h; + int out_w; + int loop_major; + int loop_x; +}; + +template +__global__ void upfirdn2d_kernel_large(scalar_t *out, const scalar_t *input, + const scalar_t *kernel, + const UpFirDn2DKernelParams p) { + int minor_idx = blockIdx.x * blockDim.x + threadIdx.x; + int out_y = minor_idx / p.minor_dim; + minor_idx -= out_y * p.minor_dim; + int out_x_base = blockIdx.y * p.loop_x * blockDim.y + threadIdx.y; + int major_idx_base = blockIdx.z * p.loop_major; + + if (out_x_base >= p.out_w || out_y >= p.out_h || + major_idx_base >= p.major_dim) { + return; + } + + int mid_y = out_y * p.down_y + p.up_y - 1 - p.pad_y0; + int in_y = min(max(floor_div(mid_y, p.up_y), 0), p.in_h); + int h = min(max(floor_div(mid_y + p.kernel_h, p.up_y), 0), p.in_h) - in_y; + int kernel_y = mid_y + p.kernel_h - (in_y + 1) * p.up_y; + + for (int loop_major = 0, major_idx = major_idx_base; + loop_major < p.loop_major && major_idx < p.major_dim; + loop_major++, major_idx++) { + for (int loop_x = 0, out_x = out_x_base; + loop_x < p.loop_x && out_x < p.out_w; loop_x++, out_x += blockDim.y) { + int mid_x = out_x * p.down_x + p.up_x - 1 - p.pad_x0; + int in_x = min(max(floor_div(mid_x, p.up_x), 0), p.in_w); + int w = min(max(floor_div(mid_x + p.kernel_w, p.up_x), 0), p.in_w) - in_x; + int kernel_x = mid_x + p.kernel_w - (in_x + 1) * p.up_x; + + const scalar_t *x_p = + &input[((major_idx * p.in_h + in_y) * p.in_w + in_x) * p.minor_dim + + minor_idx]; + const scalar_t *k_p = &kernel[kernel_y * p.kernel_w + kernel_x]; + int x_px = p.minor_dim; + int k_px = -p.up_x; + int x_py = p.in_w * p.minor_dim; + int k_py = -p.up_y * p.kernel_w; + + scalar_t v = 0.0f; + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + v += static_cast(*x_p) * static_cast(*k_p); + x_p += x_px; + k_p += k_px; + } + + x_p += x_py - w * x_px; + k_p += k_py - w * k_px; + } + + out[((major_idx * p.out_h + out_y) * p.out_w + out_x) * p.minor_dim + + minor_idx] = v; + } + } +} + +template +__global__ void upfirdn2d_kernel(scalar_t *out, const scalar_t *input, + const scalar_t *kernel, + const UpFirDn2DKernelParams p) { + const int tile_in_h = ((tile_out_h - 1) * down_y + kernel_h - 1) / up_y + 1; + const int tile_in_w = ((tile_out_w - 1) * down_x + kernel_w - 1) / up_x + 1; + + __shared__ volatile float sk[kernel_h][kernel_w]; + __shared__ volatile float sx[tile_in_h][tile_in_w]; + + int minor_idx = blockIdx.x; + int tile_out_y = minor_idx / p.minor_dim; + minor_idx -= tile_out_y * p.minor_dim; + tile_out_y *= tile_out_h; + int tile_out_x_base = blockIdx.y * p.loop_x * tile_out_w; + int major_idx_base = blockIdx.z * p.loop_major; + + if (tile_out_x_base >= p.out_w | tile_out_y >= p.out_h | + major_idx_base >= p.major_dim) { + return; + } + + for (int tap_idx = threadIdx.x; tap_idx < kernel_h * kernel_w; + tap_idx += blockDim.x) { + int ky = tap_idx / kernel_w; + int kx = tap_idx - ky * kernel_w; + scalar_t v = 0.0; + + if (kx < p.kernel_w & ky < p.kernel_h) { + v = kernel[(p.kernel_h - 1 - ky) * p.kernel_w + (p.kernel_w - 1 - kx)]; + } + + sk[ky][kx] = v; + } + + for (int loop_major = 0, major_idx = major_idx_base; + loop_major < p.loop_major & major_idx < p.major_dim; + loop_major++, major_idx++) { + for (int loop_x = 0, tile_out_x = tile_out_x_base; + loop_x < p.loop_x & tile_out_x < p.out_w; + loop_x++, tile_out_x += tile_out_w) { + int tile_mid_x = tile_out_x * down_x + up_x - 1 - p.pad_x0; + int tile_mid_y = tile_out_y * down_y + up_y - 1 - p.pad_y0; + int tile_in_x = floor_div(tile_mid_x, up_x); + int tile_in_y = floor_div(tile_mid_y, up_y); + + __syncthreads(); + + for (int in_idx = threadIdx.x; in_idx < tile_in_h * tile_in_w; + in_idx += blockDim.x) { + int rel_in_y = in_idx / tile_in_w; + int rel_in_x = in_idx - rel_in_y * tile_in_w; + int in_x = rel_in_x + tile_in_x; + int in_y = rel_in_y + tile_in_y; + + scalar_t v = 0.0; + + if (in_x >= 0 & in_y >= 0 & in_x < p.in_w & in_y < p.in_h) { + v = input[((major_idx * p.in_h + in_y) * p.in_w + in_x) * + p.minor_dim + + minor_idx]; + } + + sx[rel_in_y][rel_in_x] = v; + } + + __syncthreads(); + for (int out_idx = threadIdx.x; out_idx < tile_out_h * tile_out_w; + out_idx += blockDim.x) { + int rel_out_y = out_idx / tile_out_w; + int rel_out_x = out_idx - rel_out_y * tile_out_w; + int out_x = rel_out_x + tile_out_x; + int out_y = rel_out_y + tile_out_y; + + int mid_x = tile_mid_x + rel_out_x * down_x; + int mid_y = tile_mid_y + rel_out_y * down_y; + int in_x = floor_div(mid_x, up_x); + int in_y = floor_div(mid_y, up_y); + int rel_in_x = in_x - tile_in_x; + int rel_in_y = in_y - tile_in_y; + int kernel_x = (in_x + 1) * up_x - mid_x - 1; + int kernel_y = (in_y + 1) * up_y - mid_y - 1; + + scalar_t v = 0.0; + +#pragma unroll + for (int y = 0; y < kernel_h / up_y; y++) +#pragma unroll + for (int x = 0; x < kernel_w / up_x; x++) + v += sx[rel_in_y + y][rel_in_x + x] * + sk[kernel_y + y * up_y][kernel_x + x * up_x]; + + if (out_x < p.out_w & out_y < p.out_h) { + out[((major_idx * p.out_h + out_y) * p.out_w + out_x) * p.minor_dim + + minor_idx] = v; + } + } + } + } +} + +torch::Tensor upfirdn2d_op(const torch::Tensor &input, + const torch::Tensor &kernel, int up_x, int up_y, + int down_x, int down_y, int pad_x0, int pad_x1, + int pad_y0, int pad_y1) { + int curDevice = -1; + cudaGetDevice(&curDevice); + cudaStream_t stream = at::cuda::getCurrentCUDAStream(curDevice); + + UpFirDn2DKernelParams p; + + auto x = input.contiguous(); + auto k = kernel.contiguous(); + + p.major_dim = x.size(0); + p.in_h = x.size(1); + p.in_w = x.size(2); + p.minor_dim = x.size(3); + p.kernel_h = k.size(0); + p.kernel_w = k.size(1); + p.up_x = up_x; + p.up_y = up_y; + p.down_x = down_x; + p.down_y = down_y; + p.pad_x0 = pad_x0; + p.pad_x1 = pad_x1; + p.pad_y0 = pad_y0; + p.pad_y1 = pad_y1; + + p.out_h = (p.in_h * p.up_y + p.pad_y0 + p.pad_y1 - p.kernel_h + p.down_y) / + p.down_y; + p.out_w = (p.in_w * p.up_x + p.pad_x0 + p.pad_x1 - p.kernel_w + p.down_x) / + p.down_x; + + auto out = + at::empty({p.major_dim, p.out_h, p.out_w, p.minor_dim}, x.options()); + + int mode = -1; + + int tile_out_h = -1; + int tile_out_w = -1; + + if (p.up_x == 1 && p.up_y == 1 && p.down_x == 1 && p.down_y == 1 && + p.kernel_h <= 4 && p.kernel_w <= 4) { + mode = 1; + tile_out_h = 16; + tile_out_w = 64; + } + + if (p.up_x == 1 && p.up_y == 1 && p.down_x == 1 && p.down_y == 1 && + p.kernel_h <= 3 && p.kernel_w <= 3) { + mode = 2; + tile_out_h = 16; + tile_out_w = 64; + } + + if (p.up_x == 2 && p.up_y == 2 && p.down_x == 1 && p.down_y == 1 && + p.kernel_h <= 4 && p.kernel_w <= 4) { + mode = 3; + tile_out_h = 16; + tile_out_w = 64; + } + + if (p.up_x == 2 && p.up_y == 2 && p.down_x == 1 && p.down_y == 1 && + p.kernel_h <= 2 && p.kernel_w <= 2) { + mode = 4; + tile_out_h = 16; + tile_out_w = 64; + } + + if (p.up_x == 1 && p.up_y == 1 && p.down_x == 2 && p.down_y == 2 && + p.kernel_h <= 4 && p.kernel_w <= 4) { + mode = 5; + tile_out_h = 8; + tile_out_w = 32; + } + + if (p.up_x == 1 && p.up_y == 1 && p.down_x == 2 && p.down_y == 2 && + p.kernel_h <= 2 && p.kernel_w <= 2) { + mode = 6; + tile_out_h = 8; + tile_out_w = 32; + } + + dim3 block_size; + dim3 grid_size; + + if (tile_out_h > 0 && tile_out_w > 0) { + p.loop_major = (p.major_dim - 1) / 16384 + 1; + p.loop_x = 1; + block_size = dim3(32 * 8, 1, 1); + grid_size = dim3(((p.out_h - 1) / tile_out_h + 1) * p.minor_dim, + (p.out_w - 1) / (p.loop_x * tile_out_w) + 1, + (p.major_dim - 1) / p.loop_major + 1); + } else { + p.loop_major = (p.major_dim - 1) / 16384 + 1; + p.loop_x = 4; + block_size = dim3(4, 32, 1); + grid_size = dim3((p.out_h * p.minor_dim - 1) / block_size.x + 1, + (p.out_w - 1) / (p.loop_x * block_size.y) + 1, + (p.major_dim - 1) / p.loop_major + 1); + } + + AT_DISPATCH_FLOATING_TYPES_AND_HALF(x.scalar_type(), "upfirdn2d_cuda", [&] { + switch (mode) { + case 1: + upfirdn2d_kernel + <<>>(out.data_ptr(), + x.data_ptr(), + k.data_ptr(), p); + + break; + + case 2: + upfirdn2d_kernel + <<>>(out.data_ptr(), + x.data_ptr(), + k.data_ptr(), p); + + break; + + case 3: + upfirdn2d_kernel + <<>>(out.data_ptr(), + x.data_ptr(), + k.data_ptr(), p); + + break; + + case 4: + upfirdn2d_kernel + <<>>(out.data_ptr(), + x.data_ptr(), + k.data_ptr(), p); + + break; + + case 5: + upfirdn2d_kernel + <<>>(out.data_ptr(), + x.data_ptr(), + k.data_ptr(), p); + + break; + + case 6: + upfirdn2d_kernel + <<>>(out.data_ptr(), + x.data_ptr(), + k.data_ptr(), p); + + break; + + default: + upfirdn2d_kernel_large<<>>( + out.data_ptr(), x.data_ptr(), + k.data_ptr(), p); + } + }); + + return out; +} diff --git a/sd/stablediffusion/src/codeformer/basicsr/ops/upfirdn2d/upfirdn2d.py b/sd/stablediffusion/src/codeformer/basicsr/ops/upfirdn2d/upfirdn2d.py new file mode 100644 index 0000000000000000000000000000000000000000..667f96e1ded35d48f163f37e21d1ed8ff191aac3 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/ops/upfirdn2d/upfirdn2d.py @@ -0,0 +1,186 @@ +# modify from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/upfirdn2d.py # noqa:E501 + +import torch +from torch.autograd import Function +from torch.nn import functional as F + +try: + from . import upfirdn2d_ext +except ImportError: + import os + BASICSR_JIT = os.getenv('BASICSR_JIT') + if BASICSR_JIT == 'True': + from torch.utils.cpp_extension import load + module_path = os.path.dirname(__file__) + upfirdn2d_ext = load( + 'upfirdn2d', + sources=[ + os.path.join(module_path, 'src', 'upfirdn2d.cpp'), + os.path.join(module_path, 'src', 'upfirdn2d_kernel.cu'), + ], + ) + + +class UpFirDn2dBackward(Function): + + @staticmethod + def forward(ctx, grad_output, kernel, grad_kernel, up, down, pad, g_pad, in_size, out_size): + + up_x, up_y = up + down_x, down_y = down + g_pad_x0, g_pad_x1, g_pad_y0, g_pad_y1 = g_pad + + grad_output = grad_output.reshape(-1, out_size[0], out_size[1], 1) + + grad_input = upfirdn2d_ext.upfirdn2d( + grad_output, + grad_kernel, + down_x, + down_y, + up_x, + up_y, + g_pad_x0, + g_pad_x1, + g_pad_y0, + g_pad_y1, + ) + grad_input = grad_input.view(in_size[0], in_size[1], in_size[2], in_size[3]) + + ctx.save_for_backward(kernel) + + pad_x0, pad_x1, pad_y0, pad_y1 = pad + + ctx.up_x = up_x + ctx.up_y = up_y + ctx.down_x = down_x + ctx.down_y = down_y + ctx.pad_x0 = pad_x0 + ctx.pad_x1 = pad_x1 + ctx.pad_y0 = pad_y0 + ctx.pad_y1 = pad_y1 + ctx.in_size = in_size + ctx.out_size = out_size + + return grad_input + + @staticmethod + def backward(ctx, gradgrad_input): + kernel, = ctx.saved_tensors + + gradgrad_input = gradgrad_input.reshape(-1, ctx.in_size[2], ctx.in_size[3], 1) + + gradgrad_out = upfirdn2d_ext.upfirdn2d( + gradgrad_input, + kernel, + ctx.up_x, + ctx.up_y, + ctx.down_x, + ctx.down_y, + ctx.pad_x0, + ctx.pad_x1, + ctx.pad_y0, + ctx.pad_y1, + ) + # gradgrad_out = gradgrad_out.view(ctx.in_size[0], ctx.out_size[0], + # ctx.out_size[1], ctx.in_size[3]) + gradgrad_out = gradgrad_out.view(ctx.in_size[0], ctx.in_size[1], ctx.out_size[0], ctx.out_size[1]) + + return gradgrad_out, None, None, None, None, None, None, None, None + + +class UpFirDn2d(Function): + + @staticmethod + def forward(ctx, input, kernel, up, down, pad): + up_x, up_y = up + down_x, down_y = down + pad_x0, pad_x1, pad_y0, pad_y1 = pad + + kernel_h, kernel_w = kernel.shape + batch, channel, in_h, in_w = input.shape + ctx.in_size = input.shape + + input = input.reshape(-1, in_h, in_w, 1) + + ctx.save_for_backward(kernel, torch.flip(kernel, [0, 1])) + + out_h = (in_h * up_y + pad_y0 + pad_y1 - kernel_h) // down_y + 1 + out_w = (in_w * up_x + pad_x0 + pad_x1 - kernel_w) // down_x + 1 + ctx.out_size = (out_h, out_w) + + ctx.up = (up_x, up_y) + ctx.down = (down_x, down_y) + ctx.pad = (pad_x0, pad_x1, pad_y0, pad_y1) + + g_pad_x0 = kernel_w - pad_x0 - 1 + g_pad_y0 = kernel_h - pad_y0 - 1 + g_pad_x1 = in_w * up_x - out_w * down_x + pad_x0 - up_x + 1 + g_pad_y1 = in_h * up_y - out_h * down_y + pad_y0 - up_y + 1 + + ctx.g_pad = (g_pad_x0, g_pad_x1, g_pad_y0, g_pad_y1) + + out = upfirdn2d_ext.upfirdn2d(input, kernel, up_x, up_y, down_x, down_y, pad_x0, pad_x1, pad_y0, pad_y1) + # out = out.view(major, out_h, out_w, minor) + out = out.view(-1, channel, out_h, out_w) + + return out + + @staticmethod + def backward(ctx, grad_output): + kernel, grad_kernel = ctx.saved_tensors + + grad_input = UpFirDn2dBackward.apply( + grad_output, + kernel, + grad_kernel, + ctx.up, + ctx.down, + ctx.pad, + ctx.g_pad, + ctx.in_size, + ctx.out_size, + ) + + return grad_input, None, None, None, None + + +def upfirdn2d(input, kernel, up=1, down=1, pad=(0, 0)): + if input.device.type == 'cpu': + out = upfirdn2d_native(input, kernel, up, up, down, down, pad[0], pad[1], pad[0], pad[1]) + else: + out = UpFirDn2d.apply(input, kernel, (up, up), (down, down), (pad[0], pad[1], pad[0], pad[1])) + + return out + + +def upfirdn2d_native(input, kernel, up_x, up_y, down_x, down_y, pad_x0, pad_x1, pad_y0, pad_y1): + _, channel, in_h, in_w = input.shape + input = input.reshape(-1, in_h, in_w, 1) + + _, in_h, in_w, minor = input.shape + kernel_h, kernel_w = kernel.shape + + out = input.view(-1, in_h, 1, in_w, 1, minor) + out = F.pad(out, [0, 0, 0, up_x - 1, 0, 0, 0, up_y - 1]) + out = out.view(-1, in_h * up_y, in_w * up_x, minor) + + out = F.pad(out, [0, 0, max(pad_x0, 0), max(pad_x1, 0), max(pad_y0, 0), max(pad_y1, 0)]) + out = out[:, max(-pad_y0, 0):out.shape[1] - max(-pad_y1, 0), max(-pad_x0, 0):out.shape[2] - max(-pad_x1, 0), :, ] + + out = out.permute(0, 3, 1, 2) + out = out.reshape([-1, 1, in_h * up_y + pad_y0 + pad_y1, in_w * up_x + pad_x0 + pad_x1]) + w = torch.flip(kernel, [0, 1]).view(1, 1, kernel_h, kernel_w) + out = F.conv2d(out, w) + out = out.reshape( + -1, + minor, + in_h * up_y + pad_y0 + pad_y1 - kernel_h + 1, + in_w * up_x + pad_x0 + pad_x1 - kernel_w + 1, + ) + out = out.permute(0, 2, 3, 1) + out = out[:, ::down_y, ::down_x, :] + + out_h = (in_h * up_y + pad_y0 + pad_y1 - kernel_h) // down_y + 1 + out_w = (in_w * up_x + pad_x0 + pad_x1 - kernel_w) // down_x + 1 + + return out.view(-1, channel, out_h, out_w) diff --git a/sd/stablediffusion/src/codeformer/basicsr/setup.py b/sd/stablediffusion/src/codeformer/basicsr/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..382a2aa1006e581eaf31dbb3155d4b0ba3b31140 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/setup.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python + +from setuptools import find_packages, setup + +import os +import subprocess +import sys +import time +import torch +from torch.utils.cpp_extension import BuildExtension, CppExtension, CUDAExtension + +version_file = './basicsr/version.py' + + +def readme(): + with open('README.md', encoding='utf-8') as f: + content = f.read() + return content + + +def get_git_hash(): + + def _minimal_ext_cmd(cmd): + # construct minimal environment + env = {} + for k in ['SYSTEMROOT', 'PATH', 'HOME']: + v = os.environ.get(k) + if v is not None: + env[k] = v + # LANGUAGE is used on win32 + env['LANGUAGE'] = 'C' + env['LANG'] = 'C' + env['LC_ALL'] = 'C' + out = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=env).communicate()[0] + return out + + try: + out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) + sha = out.strip().decode('ascii') + except OSError: + sha = 'unknown' + + return sha + + +def get_hash(): + if os.path.exists('.git'): + sha = get_git_hash()[:7] + elif os.path.exists(version_file): + try: + from version import __version__ + sha = __version__.split('+')[-1] + except ImportError: + raise ImportError('Unable to get git version') + else: + sha = 'unknown' + + return sha + + +def write_version_py(): + content = """# GENERATED VERSION FILE +# TIME: {} +__version__ = '{}' +__gitsha__ = '{}' +version_info = ({}) +""" + sha = get_hash() + with open('./basicsr/VERSION', 'r') as f: + SHORT_VERSION = f.read().strip() + VERSION_INFO = ', '.join([x if x.isdigit() else f'"{x}"' for x in SHORT_VERSION.split('.')]) + + version_file_str = content.format(time.asctime(), SHORT_VERSION, sha, VERSION_INFO) + with open(version_file, 'w') as f: + f.write(version_file_str) + + +def get_version(): + with open(version_file, 'r') as f: + exec(compile(f.read(), version_file, 'exec')) + return locals()['__version__'] + + +def make_cuda_ext(name, module, sources, sources_cuda=None): + if sources_cuda is None: + sources_cuda = [] + define_macros = [] + extra_compile_args = {'cxx': []} + + if torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1': + define_macros += [('WITH_CUDA', None)] + extension = CUDAExtension + extra_compile_args['nvcc'] = [ + '-D__CUDA_NO_HALF_OPERATORS__', + '-D__CUDA_NO_HALF_CONVERSIONS__', + '-D__CUDA_NO_HALF2_OPERATORS__', + ] + sources += sources_cuda + else: + print(f'Compiling {name} without CUDA') + extension = CppExtension + + return extension( + name=f'{module}.{name}', + sources=[os.path.join(*module.split('.'), p) for p in sources], + define_macros=define_macros, + extra_compile_args=extra_compile_args) + + +def get_requirements(filename='requirements.txt'): + with open(os.path.join('.', filename), 'r') as f: + requires = [line.replace('\n', '') for line in f.readlines()] + return requires + + +if __name__ == '__main__': + if '--cuda_ext' in sys.argv: + ext_modules = [ + make_cuda_ext( + name='deform_conv_ext', + module='ops.dcn', + sources=['src/deform_conv_ext.cpp'], + sources_cuda=['src/deform_conv_cuda.cpp', 'src/deform_conv_cuda_kernel.cu']), + make_cuda_ext( + name='fused_act_ext', + module='ops.fused_act', + sources=['src/fused_bias_act.cpp'], + sources_cuda=['src/fused_bias_act_kernel.cu']), + make_cuda_ext( + name='upfirdn2d_ext', + module='ops.upfirdn2d', + sources=['src/upfirdn2d.cpp'], + sources_cuda=['src/upfirdn2d_kernel.cu']), + ] + sys.argv.remove('--cuda_ext') + else: + ext_modules = [] + + write_version_py() + setup( + name='basicsr', + version=get_version(), + description='Open Source Image and Video Super-Resolution Toolbox', + long_description=readme(), + long_description_content_type='text/markdown', + author='Xintao Wang', + author_email='xintao.wang@outlook.com', + keywords='computer vision, restoration, super resolution', + url='https://github.com/xinntao/BasicSR', + include_package_data=True, + packages=find_packages(exclude=('options', 'datasets', 'experiments', 'results', 'tb_logger', 'wandb')), + classifiers=[ + 'Development Status :: 4 - Beta', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: OS Independent', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + ], + license='Apache License 2.0', + setup_requires=['cython', 'numpy'], + install_requires=get_requirements(), + ext_modules=ext_modules, + cmdclass={'build_ext': BuildExtension}, + zip_safe=False) diff --git a/sd/stablediffusion/src/codeformer/basicsr/train.py b/sd/stablediffusion/src/codeformer/basicsr/train.py new file mode 100644 index 0000000000000000000000000000000000000000..a01c0dfccdb8b02283100ec5b792c33afaf22f5e --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/train.py @@ -0,0 +1,225 @@ +import argparse +import datetime +import logging +import math +import copy +import random +import time +import torch +from os import path as osp + +from basicsr.data import build_dataloader, build_dataset +from basicsr.data.data_sampler import EnlargedSampler +from basicsr.data.prefetch_dataloader import CPUPrefetcher, CUDAPrefetcher +from basicsr.models import build_model +from basicsr.utils import (MessageLogger, check_resume, get_env_info, get_root_logger, init_tb_logger, + init_wandb_logger, make_exp_dirs, mkdir_and_rename, set_random_seed) +from basicsr.utils.dist_util import get_dist_info, init_dist +from basicsr.utils.options import dict2str, parse + +import warnings +# ignore UserWarning: Detected call of `lr_scheduler.step()` before `optimizer.step()`. +warnings.filterwarnings("ignore", category=UserWarning) + +def parse_options(root_path, is_train=True): + parser = argparse.ArgumentParser() + parser.add_argument('-opt', type=str, required=True, help='Path to option YAML file.') + parser.add_argument('--launcher', choices=['none', 'pytorch', 'slurm'], default='none', help='job launcher') + parser.add_argument('--local_rank', type=int, default=0) + args = parser.parse_args() + opt = parse(args.opt, root_path, is_train=is_train) + + # distributed settings + if args.launcher == 'none': + opt['dist'] = False + print('Disable distributed.', flush=True) + else: + opt['dist'] = True + if args.launcher == 'slurm' and 'dist_params' in opt: + init_dist(args.launcher, **opt['dist_params']) + else: + init_dist(args.launcher) + + opt['rank'], opt['world_size'] = get_dist_info() + + # random seed + seed = opt.get('manual_seed') + if seed is None: + seed = random.randint(1, 10000) + opt['manual_seed'] = seed + set_random_seed(seed + opt['rank']) + + return opt + + +def init_loggers(opt): + log_file = osp.join(opt['path']['log'], f"train_{opt['name']}.log") + logger = get_root_logger(logger_name='basicsr', log_level=logging.INFO, log_file=log_file) + logger.info(get_env_info()) + logger.info(dict2str(opt)) + + # initialize wandb logger before tensorboard logger to allow proper sync: + if (opt['logger'].get('wandb') is not None) and (opt['logger']['wandb'].get('project') is not None): + assert opt['logger'].get('use_tb_logger') is True, ('should turn on tensorboard when using wandb') + init_wandb_logger(opt) + tb_logger = None + if opt['logger'].get('use_tb_logger'): + tb_logger = init_tb_logger(log_dir=osp.join('tb_logger', opt['name'])) + return logger, tb_logger + + +def create_train_val_dataloader(opt, logger): + # create train and val dataloaders + train_loader, val_loader = None, None + for phase, dataset_opt in opt['datasets'].items(): + if phase == 'train': + dataset_enlarge_ratio = dataset_opt.get('dataset_enlarge_ratio', 1) + train_set = build_dataset(dataset_opt) + train_sampler = EnlargedSampler(train_set, opt['world_size'], opt['rank'], dataset_enlarge_ratio) + train_loader = build_dataloader( + train_set, + dataset_opt, + num_gpu=opt['num_gpu'], + dist=opt['dist'], + sampler=train_sampler, + seed=opt['manual_seed']) + + num_iter_per_epoch = math.ceil( + len(train_set) * dataset_enlarge_ratio / (dataset_opt['batch_size_per_gpu'] * opt['world_size'])) + total_iters = int(opt['train']['total_iter']) + total_epochs = math.ceil(total_iters / (num_iter_per_epoch)) + logger.info('Training statistics:' + f'\n\tNumber of train images: {len(train_set)}' + f'\n\tDataset enlarge ratio: {dataset_enlarge_ratio}' + f'\n\tBatch size per gpu: {dataset_opt["batch_size_per_gpu"]}' + f'\n\tWorld size (gpu number): {opt["world_size"]}' + f'\n\tRequire iter number per epoch: {num_iter_per_epoch}' + f'\n\tTotal epochs: {total_epochs}; iters: {total_iters}.') + + elif phase == 'val': + val_set = build_dataset(dataset_opt) + val_loader = build_dataloader( + val_set, dataset_opt, num_gpu=opt['num_gpu'], dist=opt['dist'], sampler=None, seed=opt['manual_seed']) + logger.info(f'Number of val images/folders in {dataset_opt["name"]}: ' f'{len(val_set)}') + else: + raise ValueError(f'Dataset phase {phase} is not recognized.') + + return train_loader, train_sampler, val_loader, total_epochs, total_iters + + +def train_pipeline(root_path): + # parse options, set distributed setting, set ramdom seed + opt = parse_options(root_path, is_train=True) + + torch.backends.cudnn.benchmark = True + # torch.backends.cudnn.deterministic = True + + # load resume states if necessary + if opt['path'].get('resume_state'): + device_id = torch.cuda.current_device() + resume_state = torch.load( + opt['path']['resume_state'], map_location=lambda storage, loc: storage.cuda(device_id)) + else: + resume_state = None + + # mkdir for experiments and logger + if resume_state is None: + make_exp_dirs(opt) + if opt['logger'].get('use_tb_logger') and opt['rank'] == 0: + mkdir_and_rename(osp.join('tb_logger', opt['name'])) + + # initialize loggers + logger, tb_logger = init_loggers(opt) + + # create train and validation dataloaders + result = create_train_val_dataloader(opt, logger) + train_loader, train_sampler, val_loader, total_epochs, total_iters = result + + # create model + if resume_state: # resume training + check_resume(opt, resume_state['iter']) + model = build_model(opt) + model.resume_training(resume_state) # handle optimizers and schedulers + logger.info(f"Resuming training from epoch: {resume_state['epoch']}, " f"iter: {resume_state['iter']}.") + start_epoch = resume_state['epoch'] + current_iter = resume_state['iter'] + else: + model = build_model(opt) + start_epoch = 0 + current_iter = 0 + + # create message logger (formatted outputs) + msg_logger = MessageLogger(opt, current_iter, tb_logger) + + # dataloader prefetcher + prefetch_mode = opt['datasets']['train'].get('prefetch_mode') + if prefetch_mode is None or prefetch_mode == 'cpu': + prefetcher = CPUPrefetcher(train_loader) + elif prefetch_mode == 'cuda': + prefetcher = CUDAPrefetcher(train_loader, opt) + logger.info(f'Use {prefetch_mode} prefetch dataloader') + if opt['datasets']['train'].get('pin_memory') is not True: + raise ValueError('Please set pin_memory=True for CUDAPrefetcher.') + else: + raise ValueError(f'Wrong prefetch_mode {prefetch_mode}.' "Supported ones are: None, 'cuda', 'cpu'.") + + # training + logger.info(f'Start training from epoch: {start_epoch}, iter: {current_iter+1}') + data_time, iter_time = time.time(), time.time() + start_time = time.time() + + for epoch in range(start_epoch, total_epochs + 1): + train_sampler.set_epoch(epoch) + prefetcher.reset() + train_data = prefetcher.next() + + while train_data is not None: + data_time = time.time() - data_time + + current_iter += 1 + if current_iter > total_iters: + break + # update learning rate + model.update_learning_rate(current_iter, warmup_iter=opt['train'].get('warmup_iter', -1)) + # training + model.feed_data(train_data) + model.optimize_parameters(current_iter) + iter_time = time.time() - iter_time + # log + if current_iter % opt['logger']['print_freq'] == 0: + log_vars = {'epoch': epoch, 'iter': current_iter} + log_vars.update({'lrs': model.get_current_learning_rate()}) + log_vars.update({'time': iter_time, 'data_time': data_time}) + log_vars.update(model.get_current_log()) + msg_logger(log_vars) + + # save models and training states + if current_iter % opt['logger']['save_checkpoint_freq'] == 0: + logger.info('Saving models and training states.') + model.save(epoch, current_iter) + + # validation + if opt.get('val') is not None and opt['datasets'].get('val') is not None \ + and (current_iter % opt['val']['val_freq'] == 0): + model.validation(val_loader, current_iter, tb_logger, opt['val']['save_img']) + + data_time = time.time() + iter_time = time.time() + train_data = prefetcher.next() + # end of iter + + # end of epoch + + consumed_time = str(datetime.timedelta(seconds=int(time.time() - start_time))) + logger.info(f'End of training. Time consumed: {consumed_time}') + logger.info('Save the latest model.') + model.save(epoch=-1, current_iter=-1) # -1 stands for the latest + if opt.get('val') is not None and opt['datasets'].get('val'): + model.validation(val_loader, current_iter, tb_logger, opt['val']['save_img']) + if tb_logger: + tb_logger.close() + + +if __name__ == '__main__': + root_path = osp.abspath(osp.join(__file__, osp.pardir, osp.pardir)) + train_pipeline(root_path) diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/__init__.py b/sd/stablediffusion/src/codeformer/basicsr/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5fcc1d540462712387523d1e326d1dfc2bcfbf32 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/__init__.py @@ -0,0 +1,29 @@ +from .file_client import FileClient +from .img_util import crop_border, imfrombytes, img2tensor, imwrite, tensor2img +from .logger import MessageLogger, get_env_info, get_root_logger, init_tb_logger, init_wandb_logger +from .misc import check_resume, get_time_str, make_exp_dirs, mkdir_and_rename, scandir, set_random_seed, sizeof_fmt + +__all__ = [ + # file_client.py + 'FileClient', + # img_util.py + 'img2tensor', + 'tensor2img', + 'imfrombytes', + 'imwrite', + 'crop_border', + # logger.py + 'MessageLogger', + 'init_tb_logger', + 'init_wandb_logger', + 'get_root_logger', + 'get_env_info', + # misc.py + 'set_random_seed', + 'get_time_str', + 'mkdir_and_rename', + 'make_exp_dirs', + 'scandir', + 'check_resume', + 'sizeof_fmt' +] diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/dist_util.py b/sd/stablediffusion/src/codeformer/basicsr/utils/dist_util.py new file mode 100644 index 0000000000000000000000000000000000000000..0fab887b2cb1ce8533d2e8fdee72ae0c24f68fd0 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/dist_util.py @@ -0,0 +1,82 @@ +# Modified from https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/dist_utils.py # noqa: E501 +import functools +import os +import subprocess +import torch +import torch.distributed as dist +import torch.multiprocessing as mp + + +def init_dist(launcher, backend='nccl', **kwargs): + if mp.get_start_method(allow_none=True) is None: + mp.set_start_method('spawn') + if launcher == 'pytorch': + _init_dist_pytorch(backend, **kwargs) + elif launcher == 'slurm': + _init_dist_slurm(backend, **kwargs) + else: + raise ValueError(f'Invalid launcher type: {launcher}') + + +def _init_dist_pytorch(backend, **kwargs): + rank = int(os.environ['RANK']) + num_gpus = torch.cuda.device_count() + torch.cuda.set_device(rank % num_gpus) + dist.init_process_group(backend=backend, **kwargs) + + +def _init_dist_slurm(backend, port=None): + """Initialize slurm distributed training environment. + + If argument ``port`` is not specified, then the master port will be system + environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system + environment variable, then a default port ``29500`` will be used. + + Args: + backend (str): Backend of torch.distributed. + port (int, optional): Master port. Defaults to None. + """ + proc_id = int(os.environ['SLURM_PROCID']) + ntasks = int(os.environ['SLURM_NTASKS']) + node_list = os.environ['SLURM_NODELIST'] + num_gpus = torch.cuda.device_count() + torch.cuda.set_device(proc_id % num_gpus) + addr = subprocess.getoutput(f'scontrol show hostname {node_list} | head -n1') + # specify master port + if port is not None: + os.environ['MASTER_PORT'] = str(port) + elif 'MASTER_PORT' in os.environ: + pass # use MASTER_PORT in the environment variable + else: + # 29500 is torch.distributed default port + os.environ['MASTER_PORT'] = '29500' + os.environ['MASTER_ADDR'] = addr + os.environ['WORLD_SIZE'] = str(ntasks) + os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) + os.environ['RANK'] = str(proc_id) + dist.init_process_group(backend=backend) + + +def get_dist_info(): + if dist.is_available(): + initialized = dist.is_initialized() + else: + initialized = False + if initialized: + rank = dist.get_rank() + world_size = dist.get_world_size() + else: + rank = 0 + world_size = 1 + return rank, world_size + + +def master_only(func): + + @functools.wraps(func) + def wrapper(*args, **kwargs): + rank, _ = get_dist_info() + if rank == 0: + return func(*args, **kwargs) + + return wrapper diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/download_util.py b/sd/stablediffusion/src/codeformer/basicsr/utils/download_util.py new file mode 100644 index 0000000000000000000000000000000000000000..2a267915743ee3f3232bc8fe992466b52468979a --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/download_util.py @@ -0,0 +1,95 @@ +import math +import os +import requests +from torch.hub import download_url_to_file, get_dir +from tqdm import tqdm +from urllib.parse import urlparse + +from .misc import sizeof_fmt + + +def download_file_from_google_drive(file_id, save_path): + """Download files from google drive. + Ref: + https://stackoverflow.com/questions/25010369/wget-curl-large-file-from-google-drive # noqa E501 + Args: + file_id (str): File id. + save_path (str): Save path. + """ + + session = requests.Session() + URL = 'https://docs.google.com/uc?export=download' + params = {'id': file_id} + + response = session.get(URL, params=params, stream=True) + token = get_confirm_token(response) + if token: + params['confirm'] = token + response = session.get(URL, params=params, stream=True) + + # get file size + response_file_size = session.get(URL, params=params, stream=True, headers={'Range': 'bytes=0-2'}) + print(response_file_size) + if 'Content-Range' in response_file_size.headers: + file_size = int(response_file_size.headers['Content-Range'].split('/')[1]) + else: + file_size = None + + save_response_content(response, save_path, file_size) + + +def get_confirm_token(response): + for key, value in response.cookies.items(): + if key.startswith('download_warning'): + return value + return None + + +def save_response_content(response, destination, file_size=None, chunk_size=32768): + if file_size is not None: + pbar = tqdm(total=math.ceil(file_size / chunk_size), unit='chunk') + + readable_file_size = sizeof_fmt(file_size) + else: + pbar = None + + with open(destination, 'wb') as f: + downloaded_size = 0 + for chunk in response.iter_content(chunk_size): + downloaded_size += chunk_size + if pbar is not None: + pbar.update(1) + pbar.set_description(f'Download {sizeof_fmt(downloaded_size)} / {readable_file_size}') + if chunk: # filter out keep-alive new chunks + f.write(chunk) + if pbar is not None: + pbar.close() + + +def load_file_from_url(url, model_dir=None, progress=True, file_name=None): + """Load file form http url, will download models if necessary. + Ref:https://github.com/1adrianb/face-alignment/blob/master/face_alignment/utils.py + Args: + url (str): URL to be downloaded. + model_dir (str): The path to save the downloaded model. Should be a full path. If None, use pytorch hub_dir. + Default: None. + progress (bool): Whether to show the download progress. Default: True. + file_name (str): The downloaded file name. If None, use the file name in the url. Default: None. + Returns: + str: The path to the downloaded file. + """ + if model_dir is None: # use the pytorch hub_dir + hub_dir = get_dir() + model_dir = os.path.join(hub_dir, 'checkpoints') + + os.makedirs(model_dir, exist_ok=True) + + parts = urlparse(url) + filename = os.path.basename(parts.path) + if file_name is not None: + filename = file_name + cached_file = os.path.abspath(os.path.join(model_dir, filename)) + if not os.path.exists(cached_file): + print(f'Downloading: "{url}" to {cached_file}\n') + download_url_to_file(url, cached_file, hash_prefix=None, progress=progress) + return cached_file \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/file_client.py b/sd/stablediffusion/src/codeformer/basicsr/utils/file_client.py new file mode 100644 index 0000000000000000000000000000000000000000..7f38d9796da3899048924f2f803d1088927966b0 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/file_client.py @@ -0,0 +1,167 @@ +# Modified from https://github.com/open-mmlab/mmcv/blob/master/mmcv/fileio/file_client.py # noqa: E501 +from abc import ABCMeta, abstractmethod + + +class BaseStorageBackend(metaclass=ABCMeta): + """Abstract class of storage backends. + + All backends need to implement two apis: ``get()`` and ``get_text()``. + ``get()`` reads the file as a byte stream and ``get_text()`` reads the file + as texts. + """ + + @abstractmethod + def get(self, filepath): + pass + + @abstractmethod + def get_text(self, filepath): + pass + + +class MemcachedBackend(BaseStorageBackend): + """Memcached storage backend. + + Attributes: + server_list_cfg (str): Config file for memcached server list. + client_cfg (str): Config file for memcached client. + sys_path (str | None): Additional path to be appended to `sys.path`. + Default: None. + """ + + def __init__(self, server_list_cfg, client_cfg, sys_path=None): + if sys_path is not None: + import sys + sys.path.append(sys_path) + try: + import mc + except ImportError: + raise ImportError('Please install memcached to enable MemcachedBackend.') + + self.server_list_cfg = server_list_cfg + self.client_cfg = client_cfg + self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, self.client_cfg) + # mc.pyvector servers as a point which points to a memory cache + self._mc_buffer = mc.pyvector() + + def get(self, filepath): + filepath = str(filepath) + import mc + self._client.Get(filepath, self._mc_buffer) + value_buf = mc.ConvertBuffer(self._mc_buffer) + return value_buf + + def get_text(self, filepath): + raise NotImplementedError + + +class HardDiskBackend(BaseStorageBackend): + """Raw hard disks storage backend.""" + + def get(self, filepath): + filepath = str(filepath) + with open(filepath, 'rb') as f: + value_buf = f.read() + return value_buf + + def get_text(self, filepath): + filepath = str(filepath) + with open(filepath, 'r') as f: + value_buf = f.read() + return value_buf + + +class LmdbBackend(BaseStorageBackend): + """Lmdb storage backend. + + Args: + db_paths (str | list[str]): Lmdb database paths. + client_keys (str | list[str]): Lmdb client keys. Default: 'default'. + readonly (bool, optional): Lmdb environment parameter. If True, + disallow any write operations. Default: True. + lock (bool, optional): Lmdb environment parameter. If False, when + concurrent access occurs, do not lock the database. Default: False. + readahead (bool, optional): Lmdb environment parameter. If False, + disable the OS filesystem readahead mechanism, which may improve + random read performance when a database is larger than RAM. + Default: False. + + Attributes: + db_paths (list): Lmdb database path. + _client (list): A list of several lmdb envs. + """ + + def __init__(self, db_paths, client_keys='default', readonly=True, lock=False, readahead=False, **kwargs): + try: + import lmdb + except ImportError: + raise ImportError('Please install lmdb to enable LmdbBackend.') + + if isinstance(client_keys, str): + client_keys = [client_keys] + + if isinstance(db_paths, list): + self.db_paths = [str(v) for v in db_paths] + elif isinstance(db_paths, str): + self.db_paths = [str(db_paths)] + assert len(client_keys) == len(self.db_paths), ('client_keys and db_paths should have the same length, ' + f'but received {len(client_keys)} and {len(self.db_paths)}.') + + self._client = {} + for client, path in zip(client_keys, self.db_paths): + self._client[client] = lmdb.open(path, readonly=readonly, lock=lock, readahead=readahead, **kwargs) + + def get(self, filepath, client_key): + """Get values according to the filepath from one lmdb named client_key. + + Args: + filepath (str | obj:`Path`): Here, filepath is the lmdb key. + client_key (str): Used for distinguishing differnet lmdb envs. + """ + filepath = str(filepath) + assert client_key in self._client, (f'client_key {client_key} is not ' 'in lmdb clients.') + client = self._client[client_key] + with client.begin(write=False) as txn: + value_buf = txn.get(filepath.encode('ascii')) + return value_buf + + def get_text(self, filepath): + raise NotImplementedError + + +class FileClient(object): + """A general file client to access files in different backend. + + The client loads a file or text in a specified backend from its path + and return it as a binary file. it can also register other backend + accessor with a given name and backend class. + + Attributes: + backend (str): The storage backend type. Options are "disk", + "memcached" and "lmdb". + client (:obj:`BaseStorageBackend`): The backend object. + """ + + _backends = { + 'disk': HardDiskBackend, + 'memcached': MemcachedBackend, + 'lmdb': LmdbBackend, + } + + def __init__(self, backend='disk', **kwargs): + if backend not in self._backends: + raise ValueError(f'Backend {backend} is not supported. Currently supported ones' + f' are {list(self._backends.keys())}') + self.backend = backend + self.client = self._backends[backend](**kwargs) + + def get(self, filepath, client_key='default'): + # client_key is used only for lmdb, where different fileclients have + # different lmdb environments. + if self.backend == 'lmdb': + return self.client.get(filepath, client_key) + else: + return self.client.get(filepath) + + def get_text(self, filepath): + return self.client.get_text(filepath) diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/img_util.py b/sd/stablediffusion/src/codeformer/basicsr/utils/img_util.py new file mode 100644 index 0000000000000000000000000000000000000000..5aba82ce08eefaeb3e56ea5a3a09c342ae513522 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/img_util.py @@ -0,0 +1,171 @@ +import cv2 +import math +import numpy as np +import os +import torch +from torchvision.utils import make_grid + + +def img2tensor(imgs, bgr2rgb=True, float32=True): + """Numpy array to tensor. + + Args: + imgs (list[ndarray] | ndarray): Input images. + bgr2rgb (bool): Whether to change bgr to rgb. + float32 (bool): Whether to change to float32. + + Returns: + list[tensor] | tensor: Tensor images. If returned results only have + one element, just return tensor. + """ + + def _totensor(img, bgr2rgb, float32): + if img.shape[2] == 3 and bgr2rgb: + if img.dtype == 'float64': + img = img.astype('float32') + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + img = torch.from_numpy(img.transpose(2, 0, 1)) + if float32: + img = img.float() + return img + + if isinstance(imgs, list): + return [_totensor(img, bgr2rgb, float32) for img in imgs] + else: + return _totensor(imgs, bgr2rgb, float32) + + +def tensor2img(tensor, rgb2bgr=True, out_type=np.uint8, min_max=(0, 1)): + """Convert torch Tensors into image numpy arrays. + + After clamping to [min, max], values will be normalized to [0, 1]. + + Args: + tensor (Tensor or list[Tensor]): Accept shapes: + 1) 4D mini-batch Tensor of shape (B x 3/1 x H x W); + 2) 3D Tensor of shape (3/1 x H x W); + 3) 2D Tensor of shape (H x W). + Tensor channel should be in RGB order. + rgb2bgr (bool): Whether to change rgb to bgr. + out_type (numpy type): output types. If ``np.uint8``, transform outputs + to uint8 type with range [0, 255]; otherwise, float type with + range [0, 1]. Default: ``np.uint8``. + min_max (tuple[int]): min and max values for clamp. + + Returns: + (Tensor or list): 3D ndarray of shape (H x W x C) OR 2D ndarray of + shape (H x W). The channel order is BGR. + """ + if not (torch.is_tensor(tensor) or (isinstance(tensor, list) and all(torch.is_tensor(t) for t in tensor))): + raise TypeError(f'tensor or list of tensors expected, got {type(tensor)}') + + if torch.is_tensor(tensor): + tensor = [tensor] + result = [] + for _tensor in tensor: + _tensor = _tensor.squeeze(0).float().detach().cpu().clamp_(*min_max) + _tensor = (_tensor - min_max[0]) / (min_max[1] - min_max[0]) + + n_dim = _tensor.dim() + if n_dim == 4: + img_np = make_grid(_tensor, nrow=int(math.sqrt(_tensor.size(0))), normalize=False).numpy() + img_np = img_np.transpose(1, 2, 0) + if rgb2bgr: + img_np = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR) + elif n_dim == 3: + img_np = _tensor.numpy() + img_np = img_np.transpose(1, 2, 0) + if img_np.shape[2] == 1: # gray image + img_np = np.squeeze(img_np, axis=2) + else: + if rgb2bgr: + img_np = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR) + elif n_dim == 2: + img_np = _tensor.numpy() + else: + raise TypeError('Only support 4D, 3D or 2D tensor. ' f'But received with dimension: {n_dim}') + if out_type == np.uint8: + # Unlike MATLAB, numpy.unit8() WILL NOT round by default. + img_np = (img_np * 255.0).round() + img_np = img_np.astype(out_type) + result.append(img_np) + if len(result) == 1: + result = result[0] + return result + + +def tensor2img_fast(tensor, rgb2bgr=True, min_max=(0, 1)): + """This implementation is slightly faster than tensor2img. + It now only supports torch tensor with shape (1, c, h, w). + + Args: + tensor (Tensor): Now only support torch tensor with (1, c, h, w). + rgb2bgr (bool): Whether to change rgb to bgr. Default: True. + min_max (tuple[int]): min and max values for clamp. + """ + output = tensor.squeeze(0).detach().clamp_(*min_max).permute(1, 2, 0) + output = (output - min_max[0]) / (min_max[1] - min_max[0]) * 255 + output = output.type(torch.uint8).cpu().numpy() + if rgb2bgr: + output = cv2.cvtColor(output, cv2.COLOR_RGB2BGR) + return output + + +def imfrombytes(content, flag='color', float32=False): + """Read an image from bytes. + + Args: + content (bytes): Image bytes got from files or other streams. + flag (str): Flags specifying the color type of a loaded image, + candidates are `color`, `grayscale` and `unchanged`. + float32 (bool): Whether to change to float32., If True, will also norm + to [0, 1]. Default: False. + + Returns: + ndarray: Loaded image array. + """ + img_np = np.frombuffer(content, np.uint8) + imread_flags = {'color': cv2.IMREAD_COLOR, 'grayscale': cv2.IMREAD_GRAYSCALE, 'unchanged': cv2.IMREAD_UNCHANGED} + img = cv2.imdecode(img_np, imread_flags[flag]) + if float32: + img = img.astype(np.float32) / 255. + return img + + +def imwrite(img, file_path, params=None, auto_mkdir=True): + """Write image to file. + + Args: + img (ndarray): Image array to be written. + file_path (str): Image file path. + params (None or list): Same as opencv's :func:`imwrite` interface. + auto_mkdir (bool): If the parent folder of `file_path` does not exist, + whether to create it automatically. + + Returns: + bool: Successful or not. + """ + if auto_mkdir: + dir_name = os.path.abspath(os.path.dirname(file_path)) + os.makedirs(dir_name, exist_ok=True) + return cv2.imwrite(file_path, img, params) + + +def crop_border(imgs, crop_border): + """Crop borders of images. + + Args: + imgs (list[ndarray] | ndarray): Images with shape (h, w, c). + crop_border (int): Crop border for each end of height and weight. + + Returns: + list[ndarray]: Cropped images. + """ + if crop_border == 0: + return imgs + else: + if isinstance(imgs, list): + return [v[crop_border:-crop_border, crop_border:-crop_border, ...] for v in imgs] + else: + return imgs[crop_border:-crop_border, crop_border:-crop_border, ...] + \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/lmdb_util.py b/sd/stablediffusion/src/codeformer/basicsr/utils/lmdb_util.py new file mode 100644 index 0000000000000000000000000000000000000000..e0a10f60ffca2e36ac5f5564aafd70e79d06a723 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/lmdb_util.py @@ -0,0 +1,196 @@ +import cv2 +import lmdb +import sys +from multiprocessing import Pool +from os import path as osp +from tqdm import tqdm + + +def make_lmdb_from_imgs(data_path, + lmdb_path, + img_path_list, + keys, + batch=5000, + compress_level=1, + multiprocessing_read=False, + n_thread=40, + map_size=None): + """Make lmdb from images. + + Contents of lmdb. The file structure is: + example.lmdb + ├── data.mdb + ├── lock.mdb + ├── meta_info.txt + + The data.mdb and lock.mdb are standard lmdb files and you can refer to + https://lmdb.readthedocs.io/en/release/ for more details. + + The meta_info.txt is a specified txt file to record the meta information + of our datasets. It will be automatically created when preparing + datasets by our provided dataset tools. + Each line in the txt file records 1)image name (with extension), + 2)image shape, and 3)compression level, separated by a white space. + + For example, the meta information could be: + `000_00000000.png (720,1280,3) 1`, which means: + 1) image name (with extension): 000_00000000.png; + 2) image shape: (720,1280,3); + 3) compression level: 1 + + We use the image name without extension as the lmdb key. + + If `multiprocessing_read` is True, it will read all the images to memory + using multiprocessing. Thus, your server needs to have enough memory. + + Args: + data_path (str): Data path for reading images. + lmdb_path (str): Lmdb save path. + img_path_list (str): Image path list. + keys (str): Used for lmdb keys. + batch (int): After processing batch images, lmdb commits. + Default: 5000. + compress_level (int): Compress level when encoding images. Default: 1. + multiprocessing_read (bool): Whether use multiprocessing to read all + the images to memory. Default: False. + n_thread (int): For multiprocessing. + map_size (int | None): Map size for lmdb env. If None, use the + estimated size from images. Default: None + """ + + assert len(img_path_list) == len(keys), ('img_path_list and keys should have the same length, ' + f'but got {len(img_path_list)} and {len(keys)}') + print(f'Create lmdb for {data_path}, save to {lmdb_path}...') + print(f'Totoal images: {len(img_path_list)}') + if not lmdb_path.endswith('.lmdb'): + raise ValueError("lmdb_path must end with '.lmdb'.") + if osp.exists(lmdb_path): + print(f'Folder {lmdb_path} already exists. Exit.') + sys.exit(1) + + if multiprocessing_read: + # read all the images to memory (multiprocessing) + dataset = {} # use dict to keep the order for multiprocessing + shapes = {} + print(f'Read images with multiprocessing, #thread: {n_thread} ...') + pbar = tqdm(total=len(img_path_list), unit='image') + + def callback(arg): + """get the image data and update pbar.""" + key, dataset[key], shapes[key] = arg + pbar.update(1) + pbar.set_description(f'Read {key}') + + pool = Pool(n_thread) + for path, key in zip(img_path_list, keys): + pool.apply_async(read_img_worker, args=(osp.join(data_path, path), key, compress_level), callback=callback) + pool.close() + pool.join() + pbar.close() + print(f'Finish reading {len(img_path_list)} images.') + + # create lmdb environment + if map_size is None: + # obtain data size for one image + img = cv2.imread(osp.join(data_path, img_path_list[0]), cv2.IMREAD_UNCHANGED) + _, img_byte = cv2.imencode('.png', img, [cv2.IMWRITE_PNG_COMPRESSION, compress_level]) + data_size_per_img = img_byte.nbytes + print('Data size per image is: ', data_size_per_img) + data_size = data_size_per_img * len(img_path_list) + map_size = data_size * 10 + + env = lmdb.open(lmdb_path, map_size=map_size) + + # write data to lmdb + pbar = tqdm(total=len(img_path_list), unit='chunk') + txn = env.begin(write=True) + txt_file = open(osp.join(lmdb_path, 'meta_info.txt'), 'w') + for idx, (path, key) in enumerate(zip(img_path_list, keys)): + pbar.update(1) + pbar.set_description(f'Write {key}') + key_byte = key.encode('ascii') + if multiprocessing_read: + img_byte = dataset[key] + h, w, c = shapes[key] + else: + _, img_byte, img_shape = read_img_worker(osp.join(data_path, path), key, compress_level) + h, w, c = img_shape + + txn.put(key_byte, img_byte) + # write meta information + txt_file.write(f'{key}.png ({h},{w},{c}) {compress_level}\n') + if idx % batch == 0: + txn.commit() + txn = env.begin(write=True) + pbar.close() + txn.commit() + env.close() + txt_file.close() + print('\nFinish writing lmdb.') + + +def read_img_worker(path, key, compress_level): + """Read image worker. + + Args: + path (str): Image path. + key (str): Image key. + compress_level (int): Compress level when encoding images. + + Returns: + str: Image key. + byte: Image byte. + tuple[int]: Image shape. + """ + + img = cv2.imread(path, cv2.IMREAD_UNCHANGED) + if img.ndim == 2: + h, w = img.shape + c = 1 + else: + h, w, c = img.shape + _, img_byte = cv2.imencode('.png', img, [cv2.IMWRITE_PNG_COMPRESSION, compress_level]) + return (key, img_byte, (h, w, c)) + + +class LmdbMaker(): + """LMDB Maker. + + Args: + lmdb_path (str): Lmdb save path. + map_size (int): Map size for lmdb env. Default: 1024 ** 4, 1TB. + batch (int): After processing batch images, lmdb commits. + Default: 5000. + compress_level (int): Compress level when encoding images. Default: 1. + """ + + def __init__(self, lmdb_path, map_size=1024**4, batch=5000, compress_level=1): + if not lmdb_path.endswith('.lmdb'): + raise ValueError("lmdb_path must end with '.lmdb'.") + if osp.exists(lmdb_path): + print(f'Folder {lmdb_path} already exists. Exit.') + sys.exit(1) + + self.lmdb_path = lmdb_path + self.batch = batch + self.compress_level = compress_level + self.env = lmdb.open(lmdb_path, map_size=map_size) + self.txn = self.env.begin(write=True) + self.txt_file = open(osp.join(lmdb_path, 'meta_info.txt'), 'w') + self.counter = 0 + + def put(self, img_byte, key, img_shape): + self.counter += 1 + key_byte = key.encode('ascii') + self.txn.put(key_byte, img_byte) + # write meta information + h, w, c = img_shape + self.txt_file.write(f'{key}.png ({h},{w},{c}) {self.compress_level}\n') + if self.counter % self.batch == 0: + self.txn.commit() + self.txn = self.env.begin(write=True) + + def close(self): + self.txn.commit() + self.env.close() + self.txt_file.close() diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/logger.py b/sd/stablediffusion/src/codeformer/basicsr/utils/logger.py new file mode 100644 index 0000000000000000000000000000000000000000..9714bf59c30fc82de24c1ee58d9118d0864b3572 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/logger.py @@ -0,0 +1,169 @@ +import datetime +import logging +import time + +from .dist_util import get_dist_info, master_only + +initialized_logger = {} + + +class MessageLogger(): + """Message logger for printing. + Args: + opt (dict): Config. It contains the following keys: + name (str): Exp name. + logger (dict): Contains 'print_freq' (str) for logger interval. + train (dict): Contains 'total_iter' (int) for total iters. + use_tb_logger (bool): Use tensorboard logger. + start_iter (int): Start iter. Default: 1. + tb_logger (obj:`tb_logger`): Tensorboard logger. Default: None. + """ + + def __init__(self, opt, start_iter=1, tb_logger=None): + self.exp_name = opt['name'] + self.interval = opt['logger']['print_freq'] + self.start_iter = start_iter + self.max_iters = opt['train']['total_iter'] + self.use_tb_logger = opt['logger']['use_tb_logger'] + self.tb_logger = tb_logger + self.start_time = time.time() + self.logger = get_root_logger() + + @master_only + def __call__(self, log_vars): + """Format logging message. + Args: + log_vars (dict): It contains the following keys: + epoch (int): Epoch number. + iter (int): Current iter. + lrs (list): List for learning rates. + time (float): Iter time. + data_time (float): Data time for each iter. + """ + # epoch, iter, learning rates + epoch = log_vars.pop('epoch') + current_iter = log_vars.pop('iter') + lrs = log_vars.pop('lrs') + + message = (f'[{self.exp_name[:5]}..][epoch:{epoch:3d}, ' f'iter:{current_iter:8,d}, lr:(') + for v in lrs: + message += f'{v:.3e},' + message += ')] ' + + # time and estimated time + if 'time' in log_vars.keys(): + iter_time = log_vars.pop('time') + data_time = log_vars.pop('data_time') + + total_time = time.time() - self.start_time + time_sec_avg = total_time / (current_iter - self.start_iter + 1) + eta_sec = time_sec_avg * (self.max_iters - current_iter - 1) + eta_str = str(datetime.timedelta(seconds=int(eta_sec))) + message += f'[eta: {eta_str}, ' + message += f'time (data): {iter_time:.3f} ({data_time:.3f})] ' + + # other items, especially losses + for k, v in log_vars.items(): + message += f'{k}: {v:.4e} ' + # tensorboard logger + if self.use_tb_logger: + if k.startswith('l_'): + self.tb_logger.add_scalar(f'losses/{k}', v, current_iter) + else: + self.tb_logger.add_scalar(k, v, current_iter) + self.logger.info(message) + + +@master_only +def init_tb_logger(log_dir): + from torch.utils.tensorboard import SummaryWriter + tb_logger = SummaryWriter(log_dir=log_dir) + return tb_logger + + +@master_only +def init_wandb_logger(opt): + """We now only use wandb to sync tensorboard log.""" + import wandb + logger = logging.getLogger('basicsr') + + project = opt['logger']['wandb']['project'] + resume_id = opt['logger']['wandb'].get('resume_id') + if resume_id: + wandb_id = resume_id + resume = 'allow' + logger.warning(f'Resume wandb logger with id={wandb_id}.') + else: + wandb_id = wandb.util.generate_id() + resume = 'never' + + wandb.init(id=wandb_id, resume=resume, name=opt['name'], config=opt, project=project, sync_tensorboard=True) + + logger.info(f'Use wandb logger with id={wandb_id}; project={project}.') + + +def get_root_logger(logger_name='basicsr', log_level=logging.INFO, log_file=None): + """Get the root logger. + The logger will be initialized if it has not been initialized. By default a + StreamHandler will be added. If `log_file` is specified, a FileHandler will + also be added. + Args: + logger_name (str): root logger name. Default: 'basicsr'. + log_file (str | None): The log filename. If specified, a FileHandler + will be added to the root logger. + log_level (int): The root logger level. Note that only the process of + rank 0 is affected, while other processes will set the level to + "Error" and be silent most of the time. + Returns: + logging.Logger: The root logger. + """ + logger = logging.getLogger(logger_name) + # if the logger has been initialized, just return it + if logger_name in initialized_logger: + return logger + + format_str = '%(asctime)s %(levelname)s: %(message)s' + stream_handler = logging.StreamHandler() + stream_handler.setFormatter(logging.Formatter(format_str)) + logger.addHandler(stream_handler) + logger.propagate = False + rank, _ = get_dist_info() + if rank != 0: + logger.setLevel('ERROR') + elif log_file is not None: + logger.setLevel(log_level) + # add file handler + # file_handler = logging.FileHandler(log_file, 'w') + file_handler = logging.FileHandler(log_file, 'a') #Shangchen: keep the previous log + file_handler.setFormatter(logging.Formatter(format_str)) + file_handler.setLevel(log_level) + logger.addHandler(file_handler) + initialized_logger[logger_name] = True + return logger + + +def get_env_info(): + """Get environment information. + Currently, only log the software version. + """ + import torch + import torchvision + + from basicsr.version import __version__ + msg = r""" + ____ _ _____ ____ + / __ ) ____ _ _____ (_)_____/ ___/ / __ \ + / __ |/ __ `// ___// // ___/\__ \ / /_/ / + / /_/ // /_/ /(__ )/ // /__ ___/ // _, _/ + /_____/ \__,_//____//_/ \___//____//_/ |_| + ______ __ __ __ __ + / ____/____ ____ ____/ / / / __ __ _____ / /__ / / + / / __ / __ \ / __ \ / __ / / / / / / // ___// //_/ / / + / /_/ // /_/ // /_/ // /_/ / / /___/ /_/ // /__ / /< /_/ + \____/ \____/ \____/ \____/ /_____/\____/ \___//_/|_| (_) + """ + msg += ('\nVersion Information: ' + f'\n\tBasicSR: {__version__}' + f'\n\tPyTorch: {torch.__version__}' + f'\n\tTorchVision: {torchvision.__version__}') + return msg \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/matlab_functions.py b/sd/stablediffusion/src/codeformer/basicsr/utils/matlab_functions.py new file mode 100644 index 0000000000000000000000000000000000000000..c6ce1004a2c9f8521505c4b5889d3c24a909c70d --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/matlab_functions.py @@ -0,0 +1,347 @@ +import math +import numpy as np +import torch + + +def cubic(x): + """cubic function used for calculate_weights_indices.""" + absx = torch.abs(x) + absx2 = absx**2 + absx3 = absx**3 + return (1.5 * absx3 - 2.5 * absx2 + 1) * ( + (absx <= 1).type_as(absx)) + (-0.5 * absx3 + 2.5 * absx2 - 4 * absx + 2) * (((absx > 1) * + (absx <= 2)).type_as(absx)) + + +def calculate_weights_indices(in_length, out_length, scale, kernel, kernel_width, antialiasing): + """Calculate weights and indices, used for imresize function. + + Args: + in_length (int): Input length. + out_length (int): Output length. + scale (float): Scale factor. + kernel_width (int): Kernel width. + antialisaing (bool): Whether to apply anti-aliasing when downsampling. + """ + + if (scale < 1) and antialiasing: + # Use a modified kernel (larger kernel width) to simultaneously + # interpolate and antialias + kernel_width = kernel_width / scale + + # Output-space coordinates + x = torch.linspace(1, out_length, out_length) + + # Input-space coordinates. Calculate the inverse mapping such that 0.5 + # in output space maps to 0.5 in input space, and 0.5 + scale in output + # space maps to 1.5 in input space. + u = x / scale + 0.5 * (1 - 1 / scale) + + # What is the left-most pixel that can be involved in the computation? + left = torch.floor(u - kernel_width / 2) + + # What is the maximum number of pixels that can be involved in the + # computation? Note: it's OK to use an extra pixel here; if the + # corresponding weights are all zero, it will be eliminated at the end + # of this function. + p = math.ceil(kernel_width) + 2 + + # The indices of the input pixels involved in computing the k-th output + # pixel are in row k of the indices matrix. + indices = left.view(out_length, 1).expand(out_length, p) + torch.linspace(0, p - 1, p).view(1, p).expand( + out_length, p) + + # The weights used to compute the k-th output pixel are in row k of the + # weights matrix. + distance_to_center = u.view(out_length, 1).expand(out_length, p) - indices + + # apply cubic kernel + if (scale < 1) and antialiasing: + weights = scale * cubic(distance_to_center * scale) + else: + weights = cubic(distance_to_center) + + # Normalize the weights matrix so that each row sums to 1. + weights_sum = torch.sum(weights, 1).view(out_length, 1) + weights = weights / weights_sum.expand(out_length, p) + + # If a column in weights is all zero, get rid of it. only consider the + # first and last column. + weights_zero_tmp = torch.sum((weights == 0), 0) + if not math.isclose(weights_zero_tmp[0], 0, rel_tol=1e-6): + indices = indices.narrow(1, 1, p - 2) + weights = weights.narrow(1, 1, p - 2) + if not math.isclose(weights_zero_tmp[-1], 0, rel_tol=1e-6): + indices = indices.narrow(1, 0, p - 2) + weights = weights.narrow(1, 0, p - 2) + weights = weights.contiguous() + indices = indices.contiguous() + sym_len_s = -indices.min() + 1 + sym_len_e = indices.max() - in_length + indices = indices + sym_len_s - 1 + return weights, indices, int(sym_len_s), int(sym_len_e) + + +@torch.no_grad() +def imresize(img, scale, antialiasing=True): + """imresize function same as MATLAB. + + It now only supports bicubic. + The same scale applies for both height and width. + + Args: + img (Tensor | Numpy array): + Tensor: Input image with shape (c, h, w), [0, 1] range. + Numpy: Input image with shape (h, w, c), [0, 1] range. + scale (float): Scale factor. The same scale applies for both height + and width. + antialisaing (bool): Whether to apply anti-aliasing when downsampling. + Default: True. + + Returns: + Tensor: Output image with shape (c, h, w), [0, 1] range, w/o round. + """ + if type(img).__module__ == np.__name__: # numpy type + numpy_type = True + img = torch.from_numpy(img.transpose(2, 0, 1)).float() + else: + numpy_type = False + + in_c, in_h, in_w = img.size() + out_h, out_w = math.ceil(in_h * scale), math.ceil(in_w * scale) + kernel_width = 4 + kernel = 'cubic' + + # get weights and indices + weights_h, indices_h, sym_len_hs, sym_len_he = calculate_weights_indices(in_h, out_h, scale, kernel, kernel_width, + antialiasing) + weights_w, indices_w, sym_len_ws, sym_len_we = calculate_weights_indices(in_w, out_w, scale, kernel, kernel_width, + antialiasing) + # process H dimension + # symmetric copying + img_aug = torch.FloatTensor(in_c, in_h + sym_len_hs + sym_len_he, in_w) + img_aug.narrow(1, sym_len_hs, in_h).copy_(img) + + sym_patch = img[:, :sym_len_hs, :] + inv_idx = torch.arange(sym_patch.size(1) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(1, inv_idx) + img_aug.narrow(1, 0, sym_len_hs).copy_(sym_patch_inv) + + sym_patch = img[:, -sym_len_he:, :] + inv_idx = torch.arange(sym_patch.size(1) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(1, inv_idx) + img_aug.narrow(1, sym_len_hs + in_h, sym_len_he).copy_(sym_patch_inv) + + out_1 = torch.FloatTensor(in_c, out_h, in_w) + kernel_width = weights_h.size(1) + for i in range(out_h): + idx = int(indices_h[i][0]) + for j in range(in_c): + out_1[j, i, :] = img_aug[j, idx:idx + kernel_width, :].transpose(0, 1).mv(weights_h[i]) + + # process W dimension + # symmetric copying + out_1_aug = torch.FloatTensor(in_c, out_h, in_w + sym_len_ws + sym_len_we) + out_1_aug.narrow(2, sym_len_ws, in_w).copy_(out_1) + + sym_patch = out_1[:, :, :sym_len_ws] + inv_idx = torch.arange(sym_patch.size(2) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(2, inv_idx) + out_1_aug.narrow(2, 0, sym_len_ws).copy_(sym_patch_inv) + + sym_patch = out_1[:, :, -sym_len_we:] + inv_idx = torch.arange(sym_patch.size(2) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(2, inv_idx) + out_1_aug.narrow(2, sym_len_ws + in_w, sym_len_we).copy_(sym_patch_inv) + + out_2 = torch.FloatTensor(in_c, out_h, out_w) + kernel_width = weights_w.size(1) + for i in range(out_w): + idx = int(indices_w[i][0]) + for j in range(in_c): + out_2[j, :, i] = out_1_aug[j, :, idx:idx + kernel_width].mv(weights_w[i]) + + if numpy_type: + out_2 = out_2.numpy().transpose(1, 2, 0) + return out_2 + + +def rgb2ycbcr(img, y_only=False): + """Convert a RGB image to YCbCr image. + + This function produces the same results as Matlab's `rgb2ycbcr` function. + It implements the ITU-R BT.601 conversion for standard-definition + television. See more details in + https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. + + It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. + In OpenCV, it implements a JPEG conversion. See more details in + https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + y_only (bool): Whether to only return Y channel. Default: False. + + Returns: + ndarray: The converted YCbCr image. The output image has the same type + and range as input image. + """ + img_type = img.dtype + img = _convert_input_type_range(img) + if y_only: + out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 + else: + out_img = np.matmul( + img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], [24.966, 112.0, -18.214]]) + [16, 128, 128] + out_img = _convert_output_type_range(out_img, img_type) + return out_img + + +def bgr2ycbcr(img, y_only=False): + """Convert a BGR image to YCbCr image. + + The bgr version of rgb2ycbcr. + It implements the ITU-R BT.601 conversion for standard-definition + television. See more details in + https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. + + It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. + In OpenCV, it implements a JPEG conversion. See more details in + https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + y_only (bool): Whether to only return Y channel. Default: False. + + Returns: + ndarray: The converted YCbCr image. The output image has the same type + and range as input image. + """ + img_type = img.dtype + img = _convert_input_type_range(img) + if y_only: + out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 + else: + out_img = np.matmul( + img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], [65.481, -37.797, 112.0]]) + [16, 128, 128] + out_img = _convert_output_type_range(out_img, img_type) + return out_img + + +def ycbcr2rgb(img): + """Convert a YCbCr image to RGB image. + + This function produces the same results as Matlab's ycbcr2rgb function. + It implements the ITU-R BT.601 conversion for standard-definition + television. See more details in + https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. + + It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. + In OpenCV, it implements a JPEG conversion. See more details in + https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + + Returns: + ndarray: The converted RGB image. The output image has the same type + and range as input image. + """ + img_type = img.dtype + img = _convert_input_type_range(img) * 255 + out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], [0, -0.00153632, 0.00791071], + [0.00625893, -0.00318811, 0]]) * 255.0 + [-222.921, 135.576, -276.836] # noqa: E126 + out_img = _convert_output_type_range(out_img, img_type) + return out_img + + +def ycbcr2bgr(img): + """Convert a YCbCr image to BGR image. + + The bgr version of ycbcr2rgb. + It implements the ITU-R BT.601 conversion for standard-definition + television. See more details in + https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. + + It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. + In OpenCV, it implements a JPEG conversion. See more details in + https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + + Returns: + ndarray: The converted BGR image. The output image has the same type + and range as input image. + """ + img_type = img.dtype + img = _convert_input_type_range(img) * 255 + out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], [0.00791071, -0.00153632, 0], + [0, -0.00318811, 0.00625893]]) * 255.0 + [-276.836, 135.576, -222.921] # noqa: E126 + out_img = _convert_output_type_range(out_img, img_type) + return out_img + + +def _convert_input_type_range(img): + """Convert the type and range of the input image. + + It converts the input image to np.float32 type and range of [0, 1]. + It is mainly used for pre-processing the input image in colorspace + convertion functions such as rgb2ycbcr and ycbcr2rgb. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + + Returns: + (ndarray): The converted image with type of np.float32 and range of + [0, 1]. + """ + img_type = img.dtype + img = img.astype(np.float32) + if img_type == np.float32: + pass + elif img_type == np.uint8: + img /= 255. + else: + raise TypeError('The img type should be np.float32 or np.uint8, ' f'but got {img_type}') + return img + + +def _convert_output_type_range(img, dst_type): + """Convert the type and range of the image according to dst_type. + + It converts the image to desired type and range. If `dst_type` is np.uint8, + images will be converted to np.uint8 type with range [0, 255]. If + `dst_type` is np.float32, it converts the image to np.float32 type with + range [0, 1]. + It is mainly used for post-processing images in colorspace convertion + functions such as rgb2ycbcr and ycbcr2rgb. + + Args: + img (ndarray): The image to be converted with np.float32 type and + range [0, 255]. + dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it + converts the image to np.uint8 type with range [0, 255]. If + dst_type is np.float32, it converts the image to np.float32 type + with range [0, 1]. + + Returns: + (ndarray): The converted image with desired type and range. + """ + if dst_type not in (np.uint8, np.float32): + raise TypeError('The dst_type should be np.float32 or np.uint8, ' f'but got {dst_type}') + if dst_type == np.uint8: + img = img.round() + else: + img /= 255. + return img.astype(dst_type) diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/misc.py b/sd/stablediffusion/src/codeformer/basicsr/utils/misc.py new file mode 100644 index 0000000000000000000000000000000000000000..3b444ff3b950e38f43a5451d1330ff1b65951a9e --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/misc.py @@ -0,0 +1,134 @@ +import numpy as np +import os +import random +import time +import torch +from os import path as osp + +from .dist_util import master_only +from .logger import get_root_logger + + +def set_random_seed(seed): + """Set random seeds.""" + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + + +def get_time_str(): + return time.strftime('%Y%m%d_%H%M%S', time.localtime()) + + +def mkdir_and_rename(path): + """mkdirs. If path exists, rename it with timestamp and create a new one. + + Args: + path (str): Folder path. + """ + if osp.exists(path): + new_name = path + '_archived_' + get_time_str() + print(f'Path already exists. Rename it to {new_name}', flush=True) + os.rename(path, new_name) + os.makedirs(path, exist_ok=True) + + +@master_only +def make_exp_dirs(opt): + """Make dirs for experiments.""" + path_opt = opt['path'].copy() + if opt['is_train']: + mkdir_and_rename(path_opt.pop('experiments_root')) + else: + mkdir_and_rename(path_opt.pop('results_root')) + for key, path in path_opt.items(): + if ('strict_load' not in key) and ('pretrain_network' not in key) and ('resume' not in key): + os.makedirs(path, exist_ok=True) + + +def scandir(dir_path, suffix=None, recursive=False, full_path=False): + """Scan a directory to find the interested files. + + Args: + dir_path (str): Path of the directory. + suffix (str | tuple(str), optional): File suffix that we are + interested in. Default: None. + recursive (bool, optional): If set to True, recursively scan the + directory. Default: False. + full_path (bool, optional): If set to True, include the dir_path. + Default: False. + + Returns: + A generator for all the interested files with relative pathes. + """ + + if (suffix is not None) and not isinstance(suffix, (str, tuple)): + raise TypeError('"suffix" must be a string or tuple of strings') + + root = dir_path + + def _scandir(dir_path, suffix, recursive): + for entry in os.scandir(dir_path): + if not entry.name.startswith('.') and entry.is_file(): + if full_path: + return_path = entry.path + else: + return_path = osp.relpath(entry.path, root) + + if suffix is None: + yield return_path + elif return_path.endswith(suffix): + yield return_path + else: + if recursive: + yield from _scandir(entry.path, suffix=suffix, recursive=recursive) + else: + continue + + return _scandir(dir_path, suffix=suffix, recursive=recursive) + + +def check_resume(opt, resume_iter): + """Check resume states and pretrain_network paths. + + Args: + opt (dict): Options. + resume_iter (int): Resume iteration. + """ + logger = get_root_logger() + if opt['path']['resume_state']: + # get all the networks + networks = [key for key in opt.keys() if key.startswith('network_')] + flag_pretrain = False + for network in networks: + if opt['path'].get(f'pretrain_{network}') is not None: + flag_pretrain = True + if flag_pretrain: + logger.warning('pretrain_network path will be ignored during resuming.') + # set pretrained model paths + for network in networks: + name = f'pretrain_{network}' + basename = network.replace('network_', '') + if opt['path'].get('ignore_resume_networks') is None or (basename + not in opt['path']['ignore_resume_networks']): + opt['path'][name] = osp.join(opt['path']['models'], f'net_{basename}_{resume_iter}.pth') + logger.info(f"Set {name} to {opt['path'][name]}") + + +def sizeof_fmt(size, suffix='B'): + """Get human readable file size. + + Args: + size (int): File size. + suffix (str): Suffix. Default: 'B'. + + Return: + str: Formated file siz. + """ + for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']: + if abs(size) < 1024.0: + return f'{size:3.1f} {unit}{suffix}' + size /= 1024.0 + return f'{size:3.1f} Y{suffix}' diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/options.py b/sd/stablediffusion/src/codeformer/basicsr/utils/options.py new file mode 100644 index 0000000000000000000000000000000000000000..db490e4aa52e26fde31959fd74c2cef3af2ecf76 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/options.py @@ -0,0 +1,108 @@ +import yaml +import time +from collections import OrderedDict +from os import path as osp +from basicsr.utils.misc import get_time_str + +def ordered_yaml(): + """Support OrderedDict for yaml. + + Returns: + yaml Loader and Dumper. + """ + try: + from yaml import CDumper as Dumper + from yaml import CLoader as Loader + except ImportError: + from yaml import Dumper, Loader + + _mapping_tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG + + def dict_representer(dumper, data): + return dumper.represent_dict(data.items()) + + def dict_constructor(loader, node): + return OrderedDict(loader.construct_pairs(node)) + + Dumper.add_representer(OrderedDict, dict_representer) + Loader.add_constructor(_mapping_tag, dict_constructor) + return Loader, Dumper + + +def parse(opt_path, root_path, is_train=True): + """Parse option file. + + Args: + opt_path (str): Option file path. + is_train (str): Indicate whether in training or not. Default: True. + + Returns: + (dict): Options. + """ + with open(opt_path, mode='r') as f: + Loader, _ = ordered_yaml() + opt = yaml.load(f, Loader=Loader) + + opt['is_train'] = is_train + + # opt['name'] = f"{get_time_str()}_{opt['name']}" + if opt['path'].get('resume_state', None): # Shangchen added + resume_state_path = opt['path'].get('resume_state') + opt['name'] = resume_state_path.split("/")[-3] + else: + opt['name'] = f"{get_time_str()}_{opt['name']}" + + + # datasets + for phase, dataset in opt['datasets'].items(): + # for several datasets, e.g., test_1, test_2 + phase = phase.split('_')[0] + dataset['phase'] = phase + if 'scale' in opt: + dataset['scale'] = opt['scale'] + if dataset.get('dataroot_gt') is not None: + dataset['dataroot_gt'] = osp.expanduser(dataset['dataroot_gt']) + if dataset.get('dataroot_lq') is not None: + dataset['dataroot_lq'] = osp.expanduser(dataset['dataroot_lq']) + + # paths + for key, val in opt['path'].items(): + if (val is not None) and ('resume_state' in key or 'pretrain_network' in key): + opt['path'][key] = osp.expanduser(val) + + if is_train: + experiments_root = osp.join(root_path, 'experiments', opt['name']) + opt['path']['experiments_root'] = experiments_root + opt['path']['models'] = osp.join(experiments_root, 'models') + opt['path']['training_states'] = osp.join(experiments_root, 'training_states') + opt['path']['log'] = experiments_root + opt['path']['visualization'] = osp.join(experiments_root, 'visualization') + + else: # test + results_root = osp.join(root_path, 'results', opt['name']) + opt['path']['results_root'] = results_root + opt['path']['log'] = results_root + opt['path']['visualization'] = osp.join(results_root, 'visualization') + + return opt + + +def dict2str(opt, indent_level=1): + """dict to string for printing options. + + Args: + opt (dict): Option dict. + indent_level (int): Indent level. Default: 1. + + Return: + (str): Option string for printing. + """ + msg = '\n' + for k, v in opt.items(): + if isinstance(v, dict): + msg += ' ' * (indent_level * 2) + k + ':[' + msg += dict2str(v, indent_level + 1) + msg += ' ' * (indent_level * 2) + ']\n' + else: + msg += ' ' * (indent_level * 2) + k + ': ' + str(v) + '\n' + return msg diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/realesrgan_utils.py b/sd/stablediffusion/src/codeformer/basicsr/utils/realesrgan_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..6b7a8b460272d7160eb9199c12ef6c52d5278f5b --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/realesrgan_utils.py @@ -0,0 +1,299 @@ +import cv2 +import math +import numpy as np +import os +import queue +import threading +import torch +from basicsr.utils.download_util import load_file_from_url +from torch.nn import functional as F + +# ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +class RealESRGANer(): + """A helper class for upsampling images with RealESRGAN. + + Args: + scale (int): Upsampling scale factor used in the networks. It is usually 2 or 4. + model_path (str): The path to the pretrained model. It can be urls (will first download it automatically). + model (nn.Module): The defined network. Default: None. + tile (int): As too large images result in the out of GPU memory issue, so this tile option will first crop + input images into tiles, and then process each of them. Finally, they will be merged into one image. + 0 denotes for do not use tile. Default: 0. + tile_pad (int): The pad size for each tile, to remove border artifacts. Default: 10. + pre_pad (int): Pad the input images to avoid border artifacts. Default: 10. + half (float): Whether to use half precision during inference. Default: False. + """ + + def __init__(self, + scale, + model_path, + model=None, + tile=0, + tile_pad=10, + pre_pad=10, + half=False, + device=None, + gpu_id=None): + self.scale = scale + self.tile_size = tile + self.tile_pad = tile_pad + self.pre_pad = pre_pad + self.mod_scale = None + self.half = half + + # initialize model + if gpu_id: + self.device = torch.device( + f'cuda:{gpu_id}' if torch.cuda.is_available() else 'cpu') if device is None else device + else: + self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') if device is None else device + # if the model_path starts with https, it will first download models to the folder: realesrgan/weights + if model_path.startswith('https://'): + model_path = load_file_from_url( + url=model_path, model_dir=os.path.join('weights/realesrgan'), progress=True, file_name=None) + loadnet = torch.load(model_path, map_location=torch.device('cpu')) + # prefer to use params_ema + if 'params_ema' in loadnet: + keyname = 'params_ema' + else: + keyname = 'params' + model.load_state_dict(loadnet[keyname], strict=True) + model.eval() + self.model = model.to(self.device) + if self.half: + self.model = self.model.half() + + def pre_process(self, img): + """Pre-process, such as pre-pad and mod pad, so that the images can be divisible + """ + img = torch.from_numpy(np.transpose(img, (2, 0, 1))).float() + self.img = img.unsqueeze(0).to(self.device) + if self.half: + self.img = self.img.half() + + # pre_pad + if self.pre_pad != 0: + self.img = F.pad(self.img, (0, self.pre_pad, 0, self.pre_pad), 'reflect') + # mod pad for divisible borders + if self.scale == 2: + self.mod_scale = 2 + elif self.scale == 1: + self.mod_scale = 4 + if self.mod_scale is not None: + self.mod_pad_h, self.mod_pad_w = 0, 0 + _, _, h, w = self.img.size() + if (h % self.mod_scale != 0): + self.mod_pad_h = (self.mod_scale - h % self.mod_scale) + if (w % self.mod_scale != 0): + self.mod_pad_w = (self.mod_scale - w % self.mod_scale) + self.img = F.pad(self.img, (0, self.mod_pad_w, 0, self.mod_pad_h), 'reflect') + + def process(self): + # model inference + self.output = self.model(self.img) + + def tile_process(self): + """It will first crop input images to tiles, and then process each tile. + Finally, all the processed tiles are merged into one images. + + Modified from: https://github.com/ata4/esrgan-launcher + """ + batch, channel, height, width = self.img.shape + output_height = height * self.scale + output_width = width * self.scale + output_shape = (batch, channel, output_height, output_width) + + # start with black image + self.output = self.img.new_zeros(output_shape) + tiles_x = math.ceil(width / self.tile_size) + tiles_y = math.ceil(height / self.tile_size) + + # loop over all tiles + for y in range(tiles_y): + for x in range(tiles_x): + # extract tile from input image + ofs_x = x * self.tile_size + ofs_y = y * self.tile_size + # input tile area on total image + input_start_x = ofs_x + input_end_x = min(ofs_x + self.tile_size, width) + input_start_y = ofs_y + input_end_y = min(ofs_y + self.tile_size, height) + + # input tile area on total image with padding + input_start_x_pad = max(input_start_x - self.tile_pad, 0) + input_end_x_pad = min(input_end_x + self.tile_pad, width) + input_start_y_pad = max(input_start_y - self.tile_pad, 0) + input_end_y_pad = min(input_end_y + self.tile_pad, height) + + # input tile dimensions + input_tile_width = input_end_x - input_start_x + input_tile_height = input_end_y - input_start_y + tile_idx = y * tiles_x + x + 1 + input_tile = self.img[:, :, input_start_y_pad:input_end_y_pad, input_start_x_pad:input_end_x_pad] + + # upscale tile + try: + with torch.no_grad(): + output_tile = self.model(input_tile) + except RuntimeError as error: + print('Error', error) + # print(f'\tTile {tile_idx}/{tiles_x * tiles_y}') + + # output tile area on total image + output_start_x = input_start_x * self.scale + output_end_x = input_end_x * self.scale + output_start_y = input_start_y * self.scale + output_end_y = input_end_y * self.scale + + # output tile area without padding + output_start_x_tile = (input_start_x - input_start_x_pad) * self.scale + output_end_x_tile = output_start_x_tile + input_tile_width * self.scale + output_start_y_tile = (input_start_y - input_start_y_pad) * self.scale + output_end_y_tile = output_start_y_tile + input_tile_height * self.scale + + # put tile into output image + self.output[:, :, output_start_y:output_end_y, + output_start_x:output_end_x] = output_tile[:, :, output_start_y_tile:output_end_y_tile, + output_start_x_tile:output_end_x_tile] + + def post_process(self): + # remove extra pad + if self.mod_scale is not None: + _, _, h, w = self.output.size() + self.output = self.output[:, :, 0:h - self.mod_pad_h * self.scale, 0:w - self.mod_pad_w * self.scale] + # remove prepad + if self.pre_pad != 0: + _, _, h, w = self.output.size() + self.output = self.output[:, :, 0:h - self.pre_pad * self.scale, 0:w - self.pre_pad * self.scale] + return self.output + + @torch.no_grad() + def enhance(self, img, outscale=None, alpha_upsampler='realesrgan'): + h_input, w_input = img.shape[0:2] + # img: numpy + img = img.astype(np.float32) + if np.max(img) > 256: # 16-bit image + max_range = 65535 + print('\tInput is a 16-bit image') + else: + max_range = 255 + img = img / max_range + if len(img.shape) == 2: # gray image + img_mode = 'L' + img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) + elif img.shape[2] == 4: # RGBA image with alpha channel + img_mode = 'RGBA' + alpha = img[:, :, 3] + img = img[:, :, 0:3] + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + if alpha_upsampler == 'realesrgan': + alpha = cv2.cvtColor(alpha, cv2.COLOR_GRAY2RGB) + else: + img_mode = 'RGB' + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # ------------------- process image (without the alpha channel) ------------------- # + try: + with torch.no_grad(): + self.pre_process(img) + if self.tile_size > 0: + self.tile_process() + else: + self.process() + output_img_t = self.post_process() + output_img = output_img_t.data.squeeze().float().cpu().clamp_(0, 1).numpy() + output_img = np.transpose(output_img[[2, 1, 0], :, :], (1, 2, 0)) + if img_mode == 'L': + output_img = cv2.cvtColor(output_img, cv2.COLOR_BGR2GRAY) + del output_img_t + torch.cuda.empty_cache() + except RuntimeError as error: + print(f"Failed inference for RealESRGAN: {error}") + + # ------------------- process the alpha channel if necessary ------------------- # + if img_mode == 'RGBA': + if alpha_upsampler == 'realesrgan': + self.pre_process(alpha) + if self.tile_size > 0: + self.tile_process() + else: + self.process() + output_alpha = self.post_process() + output_alpha = output_alpha.data.squeeze().float().cpu().clamp_(0, 1).numpy() + output_alpha = np.transpose(output_alpha[[2, 1, 0], :, :], (1, 2, 0)) + output_alpha = cv2.cvtColor(output_alpha, cv2.COLOR_BGR2GRAY) + else: # use the cv2 resize for alpha channel + h, w = alpha.shape[0:2] + output_alpha = cv2.resize(alpha, (w * self.scale, h * self.scale), interpolation=cv2.INTER_LINEAR) + + # merge the alpha channel + output_img = cv2.cvtColor(output_img, cv2.COLOR_BGR2BGRA) + output_img[:, :, 3] = output_alpha + + # ------------------------------ return ------------------------------ # + if max_range == 65535: # 16-bit image + output = (output_img * 65535.0).round().astype(np.uint16) + else: + output = (output_img * 255.0).round().astype(np.uint8) + + if outscale is not None and outscale != float(self.scale): + output = cv2.resize( + output, ( + int(w_input * outscale), + int(h_input * outscale), + ), interpolation=cv2.INTER_LANCZOS4) + + return output, img_mode + + +class PrefetchReader(threading.Thread): + """Prefetch images. + + Args: + img_list (list[str]): A image list of image paths to be read. + num_prefetch_queue (int): Number of prefetch queue. + """ + + def __init__(self, img_list, num_prefetch_queue): + super().__init__() + self.que = queue.Queue(num_prefetch_queue) + self.img_list = img_list + + def run(self): + for img_path in self.img_list: + img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED) + self.que.put(img) + + self.que.put(None) + + def __next__(self): + next_item = self.que.get() + if next_item is None: + raise StopIteration + return next_item + + def __iter__(self): + return self + + +class IOConsumer(threading.Thread): + + def __init__(self, opt, que, qid): + super().__init__() + self._queue = que + self.qid = qid + self.opt = opt + + def run(self): + while True: + msg = self._queue.get() + if isinstance(msg, str) and msg == 'quit': + break + + output = msg['output'] + save_path = msg['save_path'] + cv2.imwrite(save_path, output) + print(f'IO worker {self.qid} is done.') \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/registry.py b/sd/stablediffusion/src/codeformer/basicsr/utils/registry.py new file mode 100644 index 0000000000000000000000000000000000000000..655753b3b9cbd0cfe73fe93a77cf1fcc3db6d827 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/registry.py @@ -0,0 +1,82 @@ +# Modified from: https://github.com/facebookresearch/fvcore/blob/master/fvcore/common/registry.py # noqa: E501 + + +class Registry(): + """ + The registry that provides name -> object mapping, to support third-party + users' custom modules. + + To create a registry (e.g. a backbone registry): + + .. code-block:: python + + BACKBONE_REGISTRY = Registry('BACKBONE') + + To register an object: + + .. code-block:: python + + @BACKBONE_REGISTRY.register() + class MyBackbone(): + ... + + Or: + + .. code-block:: python + + BACKBONE_REGISTRY.register(MyBackbone) + """ + + def __init__(self, name): + """ + Args: + name (str): the name of this registry + """ + self._name = name + self._obj_map = {} + + def _do_register(self, name, obj): + assert (name not in self._obj_map), (f"An object named '{name}' was already registered " + f"in '{self._name}' registry!") + self._obj_map[name] = obj + + def register(self, obj=None): + """ + Register the given object under the the name `obj.__name__`. + Can be used as either a decorator or not. + See docstring of this class for usage. + """ + if obj is None: + # used as a decorator + def deco(func_or_class): + name = func_or_class.__name__ + self._do_register(name, func_or_class) + return func_or_class + + return deco + + # used as a function call + name = obj.__name__ + self._do_register(name, obj) + + def get(self, name): + ret = self._obj_map.get(name) + if ret is None: + raise KeyError(f"No object named '{name}' found in '{self._name}' registry!") + return ret + + def __contains__(self, name): + return name in self._obj_map + + def __iter__(self): + return iter(self._obj_map.items()) + + def keys(self): + return self._obj_map.keys() + + +DATASET_REGISTRY = Registry('dataset') +ARCH_REGISTRY = Registry('arch') +MODEL_REGISTRY = Registry('model') +LOSS_REGISTRY = Registry('loss') +METRIC_REGISTRY = Registry('metric') diff --git a/sd/stablediffusion/src/codeformer/basicsr/utils/video_util.py b/sd/stablediffusion/src/codeformer/basicsr/utils/video_util.py new file mode 100644 index 0000000000000000000000000000000000000000..f37e6046e4d71abf45ff284864fae89d83fddcbc --- /dev/null +++ b/sd/stablediffusion/src/codeformer/basicsr/utils/video_util.py @@ -0,0 +1,119 @@ +''' +The code is modified from the Real-ESRGAN: +https://github.com/xinntao/Real-ESRGAN/blob/master/inference_realesrgan_video.py + +''' +import cv2 +import sys +import numpy as np + +try: + import ffmpeg +except ImportError: + import pip + pip.main(['install', '--user', 'ffmpeg-python']) + import ffmpeg + +def get_video_meta_info(video_path): + ret = {} + probe = ffmpeg.probe(video_path) + video_streams = [stream for stream in probe['streams'] if stream['codec_type'] == 'video'] + has_audio = any(stream['codec_type'] == 'audio' for stream in probe['streams']) + ret['width'] = video_streams[0]['width'] + ret['height'] = video_streams[0]['height'] + ret['fps'] = eval(video_streams[0]['avg_frame_rate']) + ret['audio'] = ffmpeg.input(video_path).audio if has_audio else None + ret['nb_frames'] = int(video_streams[0]['nb_frames']) + return ret + +class VideoReader: + def __init__(self, video_path): + self.paths = [] # for image&folder type + self.audio = None + self.stream_reader = ( + ffmpeg.input(video_path).output('pipe:', format='rawvideo', pix_fmt='bgr24', + loglevel='error').run_async( + pipe_stdin=True, pipe_stdout=True, cmd='ffmpeg')) + meta = get_video_meta_info(video_path) + self.width = meta['width'] + self.height = meta['height'] + self.input_fps = meta['fps'] + self.audio = meta['audio'] + self.nb_frames = meta['nb_frames'] + + self.idx = 0 + + def get_resolution(self): + return self.height, self.width + + def get_fps(self): + if self.input_fps is not None: + return self.input_fps + return 24 + + def get_audio(self): + return self.audio + + def __len__(self): + return self.nb_frames + + def get_frame_from_stream(self): + img_bytes = self.stream_reader.stdout.read(self.width * self.height * 3) # 3 bytes for one pixel + if not img_bytes: + return None + img = np.frombuffer(img_bytes, np.uint8).reshape([self.height, self.width, 3]) + return img + + def get_frame_from_list(self): + if self.idx >= self.nb_frames: + return None + img = cv2.imread(self.paths[self.idx]) + self.idx += 1 + return img + + def get_frame(self): + return self.get_frame_from_stream() + + + def close(self): + self.stream_reader.stdin.close() + self.stream_reader.wait() + + +class VideoWriter: + def __init__(self, video_save_path, height, width, fps, audio): + if height > 2160: + print('You are generating video that is larger than 4K, which will be very slow due to IO speed.', + 'We highly recommend to decrease the outscale(aka, -s).') + if audio is not None: + self.stream_writer = ( + ffmpeg.input('pipe:', format='rawvideo', pix_fmt='bgr24', s=f'{width}x{height}', + framerate=fps).output( + audio, + video_save_path, + pix_fmt='yuv420p', + vcodec='libx264', + loglevel='error', + acodec='copy').overwrite_output().run_async( + pipe_stdin=True, pipe_stdout=True, cmd='ffmpeg')) + else: + self.stream_writer = ( + ffmpeg.input('pipe:', format='rawvideo', pix_fmt='bgr24', s=f'{width}x{height}', + framerate=fps).output( + video_save_path, pix_fmt='yuv420p', vcodec='libx264', + loglevel='error').overwrite_output().run_async( + pipe_stdin=True, pipe_stdout=True, cmd='ffmpeg')) + + def write_frame(self, frame): + try: + frame = frame.astype(np.uint8).tobytes() + self.stream_writer.stdin.write(frame) + except BrokenPipeError: + print('Please re-install ffmpeg and libx264 by running\n', + '\t$ conda install -c conda-forge ffmpeg\n', + '\t$ conda install -c conda-forge x264') + sys.exit(0) + + def close(self): + self.stream_writer.stdin.close() + self.stream_writer.wait() \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/__init__.py b/sd/stablediffusion/src/codeformer/facelib/detection/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5d1f8fc21542d7aba1f5aa32fc9367135e0b5035 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/__init__.py @@ -0,0 +1,100 @@ +import os +import torch +from torch import nn +from copy import deepcopy + +from facelib.utils import load_file_from_url +from facelib.utils import download_pretrained_models +from facelib.detection.yolov5face.models.common import Conv + +from .retinaface.retinaface import RetinaFace +from .yolov5face.face_detector import YoloDetector + + +def init_detection_model(model_name, half=False, device='cuda'): + if 'retinaface' in model_name: + model = init_retinaface_model(model_name, half, device) + elif 'YOLOv5' in model_name: + model = init_yolov5face_model(model_name, device) + else: + raise NotImplementedError(f'{model_name} is not implemented.') + + return model + + +def init_retinaface_model(model_name, half=False, device='cuda'): + if model_name == 'retinaface_resnet50': + model = RetinaFace(network_name='resnet50', half=half) + model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/detection_Resnet50_Final.pth' + elif model_name == 'retinaface_mobile0.25': + model = RetinaFace(network_name='mobile0.25', half=half) + model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/detection_mobilenet0.25_Final.pth' + else: + raise NotImplementedError(f'{model_name} is not implemented.') + + model_path = load_file_from_url(url=model_url, model_dir='weights/facelib', progress=True, file_name=None) + load_net = torch.load(model_path, map_location=lambda storage, loc: storage) + # remove unnecessary 'module.' + for k, v in deepcopy(load_net).items(): + if k.startswith('module.'): + load_net[k[7:]] = v + load_net.pop(k) + model.load_state_dict(load_net, strict=True) + model.eval() + model = model.to(device) + + return model + + +def init_yolov5face_model(model_name, device='cuda'): + if model_name == 'YOLOv5l': + model = YoloDetector(config_name='facelib/detection/yolov5face/models/yolov5l.yaml', device=device) + model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/yolov5l-face.pth' + elif model_name == 'YOLOv5n': + model = YoloDetector(config_name='facelib/detection/yolov5face/models/yolov5n.yaml', device=device) + model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/yolov5n-face.pth' + else: + raise NotImplementedError(f'{model_name} is not implemented.') + + model_path = load_file_from_url(url=model_url, model_dir='weights/facelib', progress=True, file_name=None) + load_net = torch.load(model_path, map_location=lambda storage, loc: storage) + model.detector.load_state_dict(load_net, strict=True) + model.detector.eval() + model.detector = model.detector.to(device).float() + + for m in model.detector.modules(): + if type(m) in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU]: + m.inplace = True # pytorch 1.7.0 compatibility + elif isinstance(m, Conv): + m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility + + return model + + +# Download from Google Drive +# def init_yolov5face_model(model_name, device='cuda'): +# if model_name == 'YOLOv5l': +# model = YoloDetector(config_name='facelib/detection/yolov5face/models/yolov5l.yaml', device=device) +# f_id = {'yolov5l-face.pth': '131578zMA6B2x8VQHyHfa6GEPtulMCNzV'} +# elif model_name == 'YOLOv5n': +# model = YoloDetector(config_name='facelib/detection/yolov5face/models/yolov5n.yaml', device=device) +# f_id = {'yolov5n-face.pth': '1fhcpFvWZqghpGXjYPIne2sw1Fy4yhw6o'} +# else: +# raise NotImplementedError(f'{model_name} is not implemented.') + +# model_path = os.path.join('weights/facelib', list(f_id.keys())[0]) +# if not os.path.exists(model_path): +# download_pretrained_models(file_ids=f_id, save_path_root='weights/facelib') + +# load_net = torch.load(model_path, map_location=lambda storage, loc: storage) +# model.detector.load_state_dict(load_net, strict=True) +# model.detector.eval() +# model.detector = model.detector.to(device).float() + +# for m in model.detector.modules(): +# if type(m) in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU]: +# m.inplace = True # pytorch 1.7.0 compatibility +# elif isinstance(m, Conv): +# m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility + +# return model \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd167ccbef1429560f75042f5efb85dd1760cdcf Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/__pycache__/align_trans.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/__pycache__/align_trans.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a45596c3f746c26696cc3bb33d5ad2774b5f2ccf Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/__pycache__/align_trans.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/__pycache__/matlab_cp2tform.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/__pycache__/matlab_cp2tform.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b60997b36891ca1693170b089c15eb289b988b87 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/__pycache__/matlab_cp2tform.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/align_trans.py b/sd/stablediffusion/src/codeformer/facelib/detection/align_trans.py new file mode 100644 index 0000000000000000000000000000000000000000..07f1eb365462c2ec5bbac6d1854c786b6fd6be90 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/align_trans.py @@ -0,0 +1,219 @@ +import cv2 +import numpy as np + +from .matlab_cp2tform import get_similarity_transform_for_cv2 + +# reference facial points, a list of coordinates (x,y) +REFERENCE_FACIAL_POINTS = [[30.29459953, 51.69630051], [65.53179932, 51.50139999], [48.02519989, 71.73660278], + [33.54930115, 92.3655014], [62.72990036, 92.20410156]] + +DEFAULT_CROP_SIZE = (96, 112) + + +class FaceWarpException(Exception): + + def __str__(self): + return 'In File {}:{}'.format(__file__, super.__str__(self)) + + +def get_reference_facial_points(output_size=None, inner_padding_factor=0.0, outer_padding=(0, 0), default_square=False): + """ + Function: + ---------- + get reference 5 key points according to crop settings: + 0. Set default crop_size: + if default_square: + crop_size = (112, 112) + else: + crop_size = (96, 112) + 1. Pad the crop_size by inner_padding_factor in each side; + 2. Resize crop_size into (output_size - outer_padding*2), + pad into output_size with outer_padding; + 3. Output reference_5point; + Parameters: + ---------- + @output_size: (w, h) or None + size of aligned face image + @inner_padding_factor: (w_factor, h_factor) + padding factor for inner (w, h) + @outer_padding: (w_pad, h_pad) + each row is a pair of coordinates (x, y) + @default_square: True or False + if True: + default crop_size = (112, 112) + else: + default crop_size = (96, 112); + !!! make sure, if output_size is not None: + (output_size - outer_padding) + = some_scale * (default crop_size * (1.0 + + inner_padding_factor)) + Returns: + ---------- + @reference_5point: 5x2 np.array + each row is a pair of transformed coordinates (x, y) + """ + + tmp_5pts = np.array(REFERENCE_FACIAL_POINTS) + tmp_crop_size = np.array(DEFAULT_CROP_SIZE) + + # 0) make the inner region a square + if default_square: + size_diff = max(tmp_crop_size) - tmp_crop_size + tmp_5pts += size_diff / 2 + tmp_crop_size += size_diff + + if (output_size and output_size[0] == tmp_crop_size[0] and output_size[1] == tmp_crop_size[1]): + + return tmp_5pts + + if (inner_padding_factor == 0 and outer_padding == (0, 0)): + if output_size is None: + return tmp_5pts + else: + raise FaceWarpException('No paddings to do, output_size must be None or {}'.format(tmp_crop_size)) + + # check output size + if not (0 <= inner_padding_factor <= 1.0): + raise FaceWarpException('Not (0 <= inner_padding_factor <= 1.0)') + + if ((inner_padding_factor > 0 or outer_padding[0] > 0 or outer_padding[1] > 0) and output_size is None): + output_size = tmp_crop_size * \ + (1 + inner_padding_factor * 2).astype(np.int32) + output_size += np.array(outer_padding) + if not (outer_padding[0] < output_size[0] and outer_padding[1] < output_size[1]): + raise FaceWarpException('Not (outer_padding[0] < output_size[0] and outer_padding[1] < output_size[1])') + + # 1) pad the inner region according inner_padding_factor + if inner_padding_factor > 0: + size_diff = tmp_crop_size * inner_padding_factor * 2 + tmp_5pts += size_diff / 2 + tmp_crop_size += np.round(size_diff).astype(np.int32) + + # 2) resize the padded inner region + size_bf_outer_pad = np.array(output_size) - np.array(outer_padding) * 2 + + if size_bf_outer_pad[0] * tmp_crop_size[1] != size_bf_outer_pad[1] * tmp_crop_size[0]: + raise FaceWarpException('Must have (output_size - outer_padding)' + '= some_scale * (crop_size * (1.0 + inner_padding_factor)') + + scale_factor = size_bf_outer_pad[0].astype(np.float32) / tmp_crop_size[0] + tmp_5pts = tmp_5pts * scale_factor + # size_diff = tmp_crop_size * (scale_factor - min(scale_factor)) + # tmp_5pts = tmp_5pts + size_diff / 2 + tmp_crop_size = size_bf_outer_pad + + # 3) add outer_padding to make output_size + reference_5point = tmp_5pts + np.array(outer_padding) + tmp_crop_size = output_size + + return reference_5point + + +def get_affine_transform_matrix(src_pts, dst_pts): + """ + Function: + ---------- + get affine transform matrix 'tfm' from src_pts to dst_pts + Parameters: + ---------- + @src_pts: Kx2 np.array + source points matrix, each row is a pair of coordinates (x, y) + @dst_pts: Kx2 np.array + destination points matrix, each row is a pair of coordinates (x, y) + Returns: + ---------- + @tfm: 2x3 np.array + transform matrix from src_pts to dst_pts + """ + + tfm = np.float32([[1, 0, 0], [0, 1, 0]]) + n_pts = src_pts.shape[0] + ones = np.ones((n_pts, 1), src_pts.dtype) + src_pts_ = np.hstack([src_pts, ones]) + dst_pts_ = np.hstack([dst_pts, ones]) + + A, res, rank, s = np.linalg.lstsq(src_pts_, dst_pts_) + + if rank == 3: + tfm = np.float32([[A[0, 0], A[1, 0], A[2, 0]], [A[0, 1], A[1, 1], A[2, 1]]]) + elif rank == 2: + tfm = np.float32([[A[0, 0], A[1, 0], 0], [A[0, 1], A[1, 1], 0]]) + + return tfm + + +def warp_and_crop_face(src_img, facial_pts, reference_pts=None, crop_size=(96, 112), align_type='smilarity'): + """ + Function: + ---------- + apply affine transform 'trans' to uv + Parameters: + ---------- + @src_img: 3x3 np.array + input image + @facial_pts: could be + 1)a list of K coordinates (x,y) + or + 2) Kx2 or 2xK np.array + each row or col is a pair of coordinates (x, y) + @reference_pts: could be + 1) a list of K coordinates (x,y) + or + 2) Kx2 or 2xK np.array + each row or col is a pair of coordinates (x, y) + or + 3) None + if None, use default reference facial points + @crop_size: (w, h) + output face image size + @align_type: transform type, could be one of + 1) 'similarity': use similarity transform + 2) 'cv2_affine': use the first 3 points to do affine transform, + by calling cv2.getAffineTransform() + 3) 'affine': use all points to do affine transform + Returns: + ---------- + @face_img: output face image with size (w, h) = @crop_size + """ + + if reference_pts is None: + if crop_size[0] == 96 and crop_size[1] == 112: + reference_pts = REFERENCE_FACIAL_POINTS + else: + default_square = False + inner_padding_factor = 0 + outer_padding = (0, 0) + output_size = crop_size + + reference_pts = get_reference_facial_points(output_size, inner_padding_factor, outer_padding, + default_square) + + ref_pts = np.float32(reference_pts) + ref_pts_shp = ref_pts.shape + if max(ref_pts_shp) < 3 or min(ref_pts_shp) != 2: + raise FaceWarpException('reference_pts.shape must be (K,2) or (2,K) and K>2') + + if ref_pts_shp[0] == 2: + ref_pts = ref_pts.T + + src_pts = np.float32(facial_pts) + src_pts_shp = src_pts.shape + if max(src_pts_shp) < 3 or min(src_pts_shp) != 2: + raise FaceWarpException('facial_pts.shape must be (K,2) or (2,K) and K>2') + + if src_pts_shp[0] == 2: + src_pts = src_pts.T + + if src_pts.shape != ref_pts.shape: + raise FaceWarpException('facial_pts and reference_pts must have the same shape') + + if align_type == 'cv2_affine': + tfm = cv2.getAffineTransform(src_pts[0:3], ref_pts[0:3]) + elif align_type == 'affine': + tfm = get_affine_transform_matrix(src_pts, ref_pts) + else: + tfm = get_similarity_transform_for_cv2(src_pts, ref_pts) + + face_img = cv2.warpAffine(src_img, tfm, (crop_size[0], crop_size[1])) + + return face_img diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/matlab_cp2tform.py b/sd/stablediffusion/src/codeformer/facelib/detection/matlab_cp2tform.py new file mode 100644 index 0000000000000000000000000000000000000000..b2a8b54a91709c71437e15c68d3be9a9b0a20a34 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/matlab_cp2tform.py @@ -0,0 +1,317 @@ +import numpy as np +from numpy.linalg import inv, lstsq +from numpy.linalg import matrix_rank as rank +from numpy.linalg import norm + + +class MatlabCp2tormException(Exception): + + def __str__(self): + return 'In File {}:{}'.format(__file__, super.__str__(self)) + + +def tformfwd(trans, uv): + """ + Function: + ---------- + apply affine transform 'trans' to uv + + Parameters: + ---------- + @trans: 3x3 np.array + transform matrix + @uv: Kx2 np.array + each row is a pair of coordinates (x, y) + + Returns: + ---------- + @xy: Kx2 np.array + each row is a pair of transformed coordinates (x, y) + """ + uv = np.hstack((uv, np.ones((uv.shape[0], 1)))) + xy = np.dot(uv, trans) + xy = xy[:, 0:-1] + return xy + + +def tforminv(trans, uv): + """ + Function: + ---------- + apply the inverse of affine transform 'trans' to uv + + Parameters: + ---------- + @trans: 3x3 np.array + transform matrix + @uv: Kx2 np.array + each row is a pair of coordinates (x, y) + + Returns: + ---------- + @xy: Kx2 np.array + each row is a pair of inverse-transformed coordinates (x, y) + """ + Tinv = inv(trans) + xy = tformfwd(Tinv, uv) + return xy + + +def findNonreflectiveSimilarity(uv, xy, options=None): + options = {'K': 2} + + K = options['K'] + M = xy.shape[0] + x = xy[:, 0].reshape((-1, 1)) # use reshape to keep a column vector + y = xy[:, 1].reshape((-1, 1)) # use reshape to keep a column vector + + tmp1 = np.hstack((x, y, np.ones((M, 1)), np.zeros((M, 1)))) + tmp2 = np.hstack((y, -x, np.zeros((M, 1)), np.ones((M, 1)))) + X = np.vstack((tmp1, tmp2)) + + u = uv[:, 0].reshape((-1, 1)) # use reshape to keep a column vector + v = uv[:, 1].reshape((-1, 1)) # use reshape to keep a column vector + U = np.vstack((u, v)) + + # We know that X * r = U + if rank(X) >= 2 * K: + r, _, _, _ = lstsq(X, U, rcond=-1) + r = np.squeeze(r) + else: + raise Exception('cp2tform:twoUniquePointsReq') + sc = r[0] + ss = r[1] + tx = r[2] + ty = r[3] + + Tinv = np.array([[sc, -ss, 0], [ss, sc, 0], [tx, ty, 1]]) + T = inv(Tinv) + T[:, 2] = np.array([0, 0, 1]) + + return T, Tinv + + +def findSimilarity(uv, xy, options=None): + options = {'K': 2} + + # uv = np.array(uv) + # xy = np.array(xy) + + # Solve for trans1 + trans1, trans1_inv = findNonreflectiveSimilarity(uv, xy, options) + + # Solve for trans2 + + # manually reflect the xy data across the Y-axis + xyR = xy + xyR[:, 0] = -1 * xyR[:, 0] + + trans2r, trans2r_inv = findNonreflectiveSimilarity(uv, xyR, options) + + # manually reflect the tform to undo the reflection done on xyR + TreflectY = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, 1]]) + + trans2 = np.dot(trans2r, TreflectY) + + # Figure out if trans1 or trans2 is better + xy1 = tformfwd(trans1, uv) + norm1 = norm(xy1 - xy) + + xy2 = tformfwd(trans2, uv) + norm2 = norm(xy2 - xy) + + if norm1 <= norm2: + return trans1, trans1_inv + else: + trans2_inv = inv(trans2) + return trans2, trans2_inv + + +def get_similarity_transform(src_pts, dst_pts, reflective=True): + """ + Function: + ---------- + Find Similarity Transform Matrix 'trans': + u = src_pts[:, 0] + v = src_pts[:, 1] + x = dst_pts[:, 0] + y = dst_pts[:, 1] + [x, y, 1] = [u, v, 1] * trans + + Parameters: + ---------- + @src_pts: Kx2 np.array + source points, each row is a pair of coordinates (x, y) + @dst_pts: Kx2 np.array + destination points, each row is a pair of transformed + coordinates (x, y) + @reflective: True or False + if True: + use reflective similarity transform + else: + use non-reflective similarity transform + + Returns: + ---------- + @trans: 3x3 np.array + transform matrix from uv to xy + trans_inv: 3x3 np.array + inverse of trans, transform matrix from xy to uv + """ + + if reflective: + trans, trans_inv = findSimilarity(src_pts, dst_pts) + else: + trans, trans_inv = findNonreflectiveSimilarity(src_pts, dst_pts) + + return trans, trans_inv + + +def cvt_tform_mat_for_cv2(trans): + """ + Function: + ---------- + Convert Transform Matrix 'trans' into 'cv2_trans' which could be + directly used by cv2.warpAffine(): + u = src_pts[:, 0] + v = src_pts[:, 1] + x = dst_pts[:, 0] + y = dst_pts[:, 1] + [x, y].T = cv_trans * [u, v, 1].T + + Parameters: + ---------- + @trans: 3x3 np.array + transform matrix from uv to xy + + Returns: + ---------- + @cv2_trans: 2x3 np.array + transform matrix from src_pts to dst_pts, could be directly used + for cv2.warpAffine() + """ + cv2_trans = trans[:, 0:2].T + + return cv2_trans + + +def get_similarity_transform_for_cv2(src_pts, dst_pts, reflective=True): + """ + Function: + ---------- + Find Similarity Transform Matrix 'cv2_trans' which could be + directly used by cv2.warpAffine(): + u = src_pts[:, 0] + v = src_pts[:, 1] + x = dst_pts[:, 0] + y = dst_pts[:, 1] + [x, y].T = cv_trans * [u, v, 1].T + + Parameters: + ---------- + @src_pts: Kx2 np.array + source points, each row is a pair of coordinates (x, y) + @dst_pts: Kx2 np.array + destination points, each row is a pair of transformed + coordinates (x, y) + reflective: True or False + if True: + use reflective similarity transform + else: + use non-reflective similarity transform + + Returns: + ---------- + @cv2_trans: 2x3 np.array + transform matrix from src_pts to dst_pts, could be directly used + for cv2.warpAffine() + """ + trans, trans_inv = get_similarity_transform(src_pts, dst_pts, reflective) + cv2_trans = cvt_tform_mat_for_cv2(trans) + + return cv2_trans + + +if __name__ == '__main__': + """ + u = [0, 6, -2] + v = [0, 3, 5] + x = [-1, 0, 4] + y = [-1, -10, 4] + + # In Matlab, run: + # + # uv = [u'; v']; + # xy = [x'; y']; + # tform_sim=cp2tform(uv,xy,'similarity'); + # + # trans = tform_sim.tdata.T + # ans = + # -0.0764 -1.6190 0 + # 1.6190 -0.0764 0 + # -3.2156 0.0290 1.0000 + # trans_inv = tform_sim.tdata.Tinv + # ans = + # + # -0.0291 0.6163 0 + # -0.6163 -0.0291 0 + # -0.0756 1.9826 1.0000 + # xy_m=tformfwd(tform_sim, u,v) + # + # xy_m = + # + # -3.2156 0.0290 + # 1.1833 -9.9143 + # 5.0323 2.8853 + # uv_m=tforminv(tform_sim, x,y) + # + # uv_m = + # + # 0.5698 1.3953 + # 6.0872 2.2733 + # -2.6570 4.3314 + """ + u = [0, 6, -2] + v = [0, 3, 5] + x = [-1, 0, 4] + y = [-1, -10, 4] + + uv = np.array((u, v)).T + xy = np.array((x, y)).T + + print('\n--->uv:') + print(uv) + print('\n--->xy:') + print(xy) + + trans, trans_inv = get_similarity_transform(uv, xy) + + print('\n--->trans matrix:') + print(trans) + + print('\n--->trans_inv matrix:') + print(trans_inv) + + print('\n---> apply transform to uv') + print('\nxy_m = uv_augmented * trans') + uv_aug = np.hstack((uv, np.ones((uv.shape[0], 1)))) + xy_m = np.dot(uv_aug, trans) + print(xy_m) + + print('\nxy_m = tformfwd(trans, uv)') + xy_m = tformfwd(trans, uv) + print(xy_m) + + print('\n---> apply inverse transform to xy') + print('\nuv_m = xy_augmented * trans_inv') + xy_aug = np.hstack((xy, np.ones((xy.shape[0], 1)))) + uv_m = np.dot(xy_aug, trans_inv) + print(uv_m) + + print('\nuv_m = tformfwd(trans_inv, xy)') + uv_m = tformfwd(trans_inv, xy) + print(uv_m) + + uv_m = tforminv(trans, xy) + print('\nuv_m = tforminv(trans, xy)') + print(uv_m) diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/__pycache__/retinaface.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/__pycache__/retinaface.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8777fee898ca14db6fbd8068f1125bd92ed4d6a3 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/__pycache__/retinaface.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/__pycache__/retinaface_net.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/__pycache__/retinaface_net.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..04af71e958a0753852b83f077b1b21d4b7adf99e Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/__pycache__/retinaface_net.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/__pycache__/retinaface_utils.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/__pycache__/retinaface_utils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96f04bcfbce5e05d1263360f16587bedf873d897 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/__pycache__/retinaface_utils.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/retinaface.py b/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/retinaface.py new file mode 100644 index 0000000000000000000000000000000000000000..02593556d88a90232bbe55a062875f4af4520621 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/retinaface.py @@ -0,0 +1,370 @@ +import cv2 +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +from PIL import Image +from torchvision.models._utils import IntermediateLayerGetter as IntermediateLayerGetter + +from facelib.detection.align_trans import get_reference_facial_points, warp_and_crop_face +from facelib.detection.retinaface.retinaface_net import FPN, SSH, MobileNetV1, make_bbox_head, make_class_head, make_landmark_head +from facelib.detection.retinaface.retinaface_utils import (PriorBox, batched_decode, batched_decode_landm, decode, decode_landm, + py_cpu_nms) + +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + + +def generate_config(network_name): + + cfg_mnet = { + 'name': 'mobilenet0.25', + 'min_sizes': [[16, 32], [64, 128], [256, 512]], + 'steps': [8, 16, 32], + 'variance': [0.1, 0.2], + 'clip': False, + 'loc_weight': 2.0, + 'gpu_train': True, + 'batch_size': 32, + 'ngpu': 1, + 'epoch': 250, + 'decay1': 190, + 'decay2': 220, + 'image_size': 640, + 'return_layers': { + 'stage1': 1, + 'stage2': 2, + 'stage3': 3 + }, + 'in_channel': 32, + 'out_channel': 64 + } + + cfg_re50 = { + 'name': 'Resnet50', + 'min_sizes': [[16, 32], [64, 128], [256, 512]], + 'steps': [8, 16, 32], + 'variance': [0.1, 0.2], + 'clip': False, + 'loc_weight': 2.0, + 'gpu_train': True, + 'batch_size': 24, + 'ngpu': 4, + 'epoch': 100, + 'decay1': 70, + 'decay2': 90, + 'image_size': 840, + 'return_layers': { + 'layer2': 1, + 'layer3': 2, + 'layer4': 3 + }, + 'in_channel': 256, + 'out_channel': 256 + } + + if network_name == 'mobile0.25': + return cfg_mnet + elif network_name == 'resnet50': + return cfg_re50 + else: + raise NotImplementedError(f'network_name={network_name}') + + +class RetinaFace(nn.Module): + + def __init__(self, network_name='resnet50', half=False, phase='test'): + super(RetinaFace, self).__init__() + self.half_inference = half + cfg = generate_config(network_name) + self.backbone = cfg['name'] + + self.model_name = f'retinaface_{network_name}' + self.cfg = cfg + self.phase = phase + self.target_size, self.max_size = 1600, 2150 + self.resize, self.scale, self.scale1 = 1., None, None + self.mean_tensor = torch.tensor([[[[104.]], [[117.]], [[123.]]]]).to(device) + self.reference = get_reference_facial_points(default_square=True) + # Build network. + backbone = None + if cfg['name'] == 'mobilenet0.25': + backbone = MobileNetV1() + self.body = IntermediateLayerGetter(backbone, cfg['return_layers']) + elif cfg['name'] == 'Resnet50': + import torchvision.models as models + backbone = models.resnet50(pretrained=False) + self.body = IntermediateLayerGetter(backbone, cfg['return_layers']) + + in_channels_stage2 = cfg['in_channel'] + in_channels_list = [ + in_channels_stage2 * 2, + in_channels_stage2 * 4, + in_channels_stage2 * 8, + ] + + out_channels = cfg['out_channel'] + self.fpn = FPN(in_channels_list, out_channels) + self.ssh1 = SSH(out_channels, out_channels) + self.ssh2 = SSH(out_channels, out_channels) + self.ssh3 = SSH(out_channels, out_channels) + + self.ClassHead = make_class_head(fpn_num=3, inchannels=cfg['out_channel']) + self.BboxHead = make_bbox_head(fpn_num=3, inchannels=cfg['out_channel']) + self.LandmarkHead = make_landmark_head(fpn_num=3, inchannels=cfg['out_channel']) + + self.to(device) + self.eval() + if self.half_inference: + self.half() + + def forward(self, inputs): + out = self.body(inputs) + + if self.backbone == 'mobilenet0.25' or self.backbone == 'Resnet50': + out = list(out.values()) + # FPN + fpn = self.fpn(out) + + # SSH + feature1 = self.ssh1(fpn[0]) + feature2 = self.ssh2(fpn[1]) + feature3 = self.ssh3(fpn[2]) + features = [feature1, feature2, feature3] + + bbox_regressions = torch.cat([self.BboxHead[i](feature) for i, feature in enumerate(features)], dim=1) + classifications = torch.cat([self.ClassHead[i](feature) for i, feature in enumerate(features)], dim=1) + tmp = [self.LandmarkHead[i](feature) for i, feature in enumerate(features)] + ldm_regressions = (torch.cat(tmp, dim=1)) + + if self.phase == 'train': + output = (bbox_regressions, classifications, ldm_regressions) + else: + output = (bbox_regressions, F.softmax(classifications, dim=-1), ldm_regressions) + return output + + def __detect_faces(self, inputs): + # get scale + height, width = inputs.shape[2:] + self.scale = torch.tensor([width, height, width, height], dtype=torch.float32).to(device) + tmp = [width, height, width, height, width, height, width, height, width, height] + self.scale1 = torch.tensor(tmp, dtype=torch.float32).to(device) + + # forawrd + inputs = inputs.to(device) + if self.half_inference: + inputs = inputs.half() + loc, conf, landmarks = self(inputs) + + # get priorbox + priorbox = PriorBox(self.cfg, image_size=inputs.shape[2:]) + priors = priorbox.forward().to(device) + + return loc, conf, landmarks, priors + + # single image detection + def transform(self, image, use_origin_size): + # convert to opencv format + if isinstance(image, Image.Image): + image = cv2.cvtColor(np.asarray(image), cv2.COLOR_RGB2BGR) + image = image.astype(np.float32) + + # testing scale + im_size_min = np.min(image.shape[0:2]) + im_size_max = np.max(image.shape[0:2]) + resize = float(self.target_size) / float(im_size_min) + + # prevent bigger axis from being more than max_size + if np.round(resize * im_size_max) > self.max_size: + resize = float(self.max_size) / float(im_size_max) + resize = 1 if use_origin_size else resize + + # resize + if resize != 1: + image = cv2.resize(image, None, None, fx=resize, fy=resize, interpolation=cv2.INTER_LINEAR) + + # convert to torch.tensor format + # image -= (104, 117, 123) + image = image.transpose(2, 0, 1) + image = torch.from_numpy(image).unsqueeze(0) + + return image, resize + + def detect_faces( + self, + image, + conf_threshold=0.8, + nms_threshold=0.4, + use_origin_size=True, + ): + """ + Params: + imgs: BGR image + """ + image, self.resize = self.transform(image, use_origin_size) + image = image.to(device) + if self.half_inference: + image = image.half() + image = image - self.mean_tensor + + loc, conf, landmarks, priors = self.__detect_faces(image) + + boxes = decode(loc.data.squeeze(0), priors.data, self.cfg['variance']) + boxes = boxes * self.scale / self.resize + boxes = boxes.cpu().numpy() + + scores = conf.squeeze(0).data.cpu().numpy()[:, 1] + + landmarks = decode_landm(landmarks.squeeze(0), priors, self.cfg['variance']) + landmarks = landmarks * self.scale1 / self.resize + landmarks = landmarks.cpu().numpy() + + # ignore low scores + inds = np.where(scores > conf_threshold)[0] + boxes, landmarks, scores = boxes[inds], landmarks[inds], scores[inds] + + # sort + order = scores.argsort()[::-1] + boxes, landmarks, scores = boxes[order], landmarks[order], scores[order] + + # do NMS + bounding_boxes = np.hstack((boxes, scores[:, np.newaxis])).astype(np.float32, copy=False) + keep = py_cpu_nms(bounding_boxes, nms_threshold) + bounding_boxes, landmarks = bounding_boxes[keep, :], landmarks[keep] + # self.t['forward_pass'].toc() + # print(self.t['forward_pass'].average_time) + # import sys + # sys.stdout.flush() + return np.concatenate((bounding_boxes, landmarks), axis=1) + + def __align_multi(self, image, boxes, landmarks, limit=None): + + if len(boxes) < 1: + return [], [] + + if limit: + boxes = boxes[:limit] + landmarks = landmarks[:limit] + + faces = [] + for landmark in landmarks: + facial5points = [[landmark[2 * j], landmark[2 * j + 1]] for j in range(5)] + + warped_face = warp_and_crop_face(np.array(image), facial5points, self.reference, crop_size=(112, 112)) + faces.append(warped_face) + + return np.concatenate((boxes, landmarks), axis=1), faces + + def align_multi(self, img, conf_threshold=0.8, limit=None): + + rlt = self.detect_faces(img, conf_threshold=conf_threshold) + boxes, landmarks = rlt[:, 0:5], rlt[:, 5:] + + return self.__align_multi(img, boxes, landmarks, limit) + + # batched detection + def batched_transform(self, frames, use_origin_size): + """ + Arguments: + frames: a list of PIL.Image, or torch.Tensor(shape=[n, h, w, c], + type=np.float32, BGR format). + use_origin_size: whether to use origin size. + """ + from_PIL = True if isinstance(frames[0], Image.Image) else False + + # convert to opencv format + if from_PIL: + frames = [cv2.cvtColor(np.asarray(frame), cv2.COLOR_RGB2BGR) for frame in frames] + frames = np.asarray(frames, dtype=np.float32) + + # testing scale + im_size_min = np.min(frames[0].shape[0:2]) + im_size_max = np.max(frames[0].shape[0:2]) + resize = float(self.target_size) / float(im_size_min) + + # prevent bigger axis from being more than max_size + if np.round(resize * im_size_max) > self.max_size: + resize = float(self.max_size) / float(im_size_max) + resize = 1 if use_origin_size else resize + + # resize + if resize != 1: + if not from_PIL: + frames = F.interpolate(frames, scale_factor=resize) + else: + frames = [ + cv2.resize(frame, None, None, fx=resize, fy=resize, interpolation=cv2.INTER_LINEAR) + for frame in frames + ] + + # convert to torch.tensor format + if not from_PIL: + frames = frames.transpose(1, 2).transpose(1, 3).contiguous() + else: + frames = frames.transpose((0, 3, 1, 2)) + frames = torch.from_numpy(frames) + + return frames, resize + + def batched_detect_faces(self, frames, conf_threshold=0.8, nms_threshold=0.4, use_origin_size=True): + """ + Arguments: + frames: a list of PIL.Image, or np.array(shape=[n, h, w, c], + type=np.uint8, BGR format). + conf_threshold: confidence threshold. + nms_threshold: nms threshold. + use_origin_size: whether to use origin size. + Returns: + final_bounding_boxes: list of np.array ([n_boxes, 5], + type=np.float32). + final_landmarks: list of np.array ([n_boxes, 10], type=np.float32). + """ + # self.t['forward_pass'].tic() + frames, self.resize = self.batched_transform(frames, use_origin_size) + frames = frames.to(device) + frames = frames - self.mean_tensor + + b_loc, b_conf, b_landmarks, priors = self.__detect_faces(frames) + + final_bounding_boxes, final_landmarks = [], [] + + # decode + priors = priors.unsqueeze(0) + b_loc = batched_decode(b_loc, priors, self.cfg['variance']) * self.scale / self.resize + b_landmarks = batched_decode_landm(b_landmarks, priors, self.cfg['variance']) * self.scale1 / self.resize + b_conf = b_conf[:, :, 1] + + # index for selection + b_indice = b_conf > conf_threshold + + # concat + b_loc_and_conf = torch.cat((b_loc, b_conf.unsqueeze(-1)), dim=2).float() + + for pred, landm, inds in zip(b_loc_and_conf, b_landmarks, b_indice): + + # ignore low scores + pred, landm = pred[inds, :], landm[inds, :] + if pred.shape[0] == 0: + final_bounding_boxes.append(np.array([], dtype=np.float32)) + final_landmarks.append(np.array([], dtype=np.float32)) + continue + + # sort + # order = score.argsort(descending=True) + # box, landm, score = box[order], landm[order], score[order] + + # to CPU + bounding_boxes, landm = pred.cpu().numpy(), landm.cpu().numpy() + + # NMS + keep = py_cpu_nms(bounding_boxes, nms_threshold) + bounding_boxes, landmarks = bounding_boxes[keep, :], landm[keep] + + # append + final_bounding_boxes.append(bounding_boxes) + final_landmarks.append(landmarks) + # self.t['forward_pass'].toc(average=True) + # self.batch_time += self.t['forward_pass'].diff + # self.total_frame += len(frames) + # print(self.batch_time / self.total_frame) + + return final_bounding_boxes, final_landmarks diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/retinaface_net.py b/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/retinaface_net.py new file mode 100644 index 0000000000000000000000000000000000000000..ab6aa82d3e9055a838f1f9076b12f05fdfc154d0 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/retinaface_net.py @@ -0,0 +1,196 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + + +def conv_bn(inp, oup, stride=1, leaky=0): + return nn.Sequential( + nn.Conv2d(inp, oup, 3, stride, 1, bias=False), nn.BatchNorm2d(oup), + nn.LeakyReLU(negative_slope=leaky, inplace=True)) + + +def conv_bn_no_relu(inp, oup, stride): + return nn.Sequential( + nn.Conv2d(inp, oup, 3, stride, 1, bias=False), + nn.BatchNorm2d(oup), + ) + + +def conv_bn1X1(inp, oup, stride, leaky=0): + return nn.Sequential( + nn.Conv2d(inp, oup, 1, stride, padding=0, bias=False), nn.BatchNorm2d(oup), + nn.LeakyReLU(negative_slope=leaky, inplace=True)) + + +def conv_dw(inp, oup, stride, leaky=0.1): + return nn.Sequential( + nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False), + nn.BatchNorm2d(inp), + nn.LeakyReLU(negative_slope=leaky, inplace=True), + nn.Conv2d(inp, oup, 1, 1, 0, bias=False), + nn.BatchNorm2d(oup), + nn.LeakyReLU(negative_slope=leaky, inplace=True), + ) + + +class SSH(nn.Module): + + def __init__(self, in_channel, out_channel): + super(SSH, self).__init__() + assert out_channel % 4 == 0 + leaky = 0 + if (out_channel <= 64): + leaky = 0.1 + self.conv3X3 = conv_bn_no_relu(in_channel, out_channel // 2, stride=1) + + self.conv5X5_1 = conv_bn(in_channel, out_channel // 4, stride=1, leaky=leaky) + self.conv5X5_2 = conv_bn_no_relu(out_channel // 4, out_channel // 4, stride=1) + + self.conv7X7_2 = conv_bn(out_channel // 4, out_channel // 4, stride=1, leaky=leaky) + self.conv7x7_3 = conv_bn_no_relu(out_channel // 4, out_channel // 4, stride=1) + + def forward(self, input): + conv3X3 = self.conv3X3(input) + + conv5X5_1 = self.conv5X5_1(input) + conv5X5 = self.conv5X5_2(conv5X5_1) + + conv7X7_2 = self.conv7X7_2(conv5X5_1) + conv7X7 = self.conv7x7_3(conv7X7_2) + + out = torch.cat([conv3X3, conv5X5, conv7X7], dim=1) + out = F.relu(out) + return out + + +class FPN(nn.Module): + + def __init__(self, in_channels_list, out_channels): + super(FPN, self).__init__() + leaky = 0 + if (out_channels <= 64): + leaky = 0.1 + self.output1 = conv_bn1X1(in_channels_list[0], out_channels, stride=1, leaky=leaky) + self.output2 = conv_bn1X1(in_channels_list[1], out_channels, stride=1, leaky=leaky) + self.output3 = conv_bn1X1(in_channels_list[2], out_channels, stride=1, leaky=leaky) + + self.merge1 = conv_bn(out_channels, out_channels, leaky=leaky) + self.merge2 = conv_bn(out_channels, out_channels, leaky=leaky) + + def forward(self, input): + # names = list(input.keys()) + # input = list(input.values()) + + output1 = self.output1(input[0]) + output2 = self.output2(input[1]) + output3 = self.output3(input[2]) + + up3 = F.interpolate(output3, size=[output2.size(2), output2.size(3)], mode='nearest') + output2 = output2 + up3 + output2 = self.merge2(output2) + + up2 = F.interpolate(output2, size=[output1.size(2), output1.size(3)], mode='nearest') + output1 = output1 + up2 + output1 = self.merge1(output1) + + out = [output1, output2, output3] + return out + + +class MobileNetV1(nn.Module): + + def __init__(self): + super(MobileNetV1, self).__init__() + self.stage1 = nn.Sequential( + conv_bn(3, 8, 2, leaky=0.1), # 3 + conv_dw(8, 16, 1), # 7 + conv_dw(16, 32, 2), # 11 + conv_dw(32, 32, 1), # 19 + conv_dw(32, 64, 2), # 27 + conv_dw(64, 64, 1), # 43 + ) + self.stage2 = nn.Sequential( + conv_dw(64, 128, 2), # 43 + 16 = 59 + conv_dw(128, 128, 1), # 59 + 32 = 91 + conv_dw(128, 128, 1), # 91 + 32 = 123 + conv_dw(128, 128, 1), # 123 + 32 = 155 + conv_dw(128, 128, 1), # 155 + 32 = 187 + conv_dw(128, 128, 1), # 187 + 32 = 219 + ) + self.stage3 = nn.Sequential( + conv_dw(128, 256, 2), # 219 +3 2 = 241 + conv_dw(256, 256, 1), # 241 + 64 = 301 + ) + self.avg = nn.AdaptiveAvgPool2d((1, 1)) + self.fc = nn.Linear(256, 1000) + + def forward(self, x): + x = self.stage1(x) + x = self.stage2(x) + x = self.stage3(x) + x = self.avg(x) + # x = self.model(x) + x = x.view(-1, 256) + x = self.fc(x) + return x + + +class ClassHead(nn.Module): + + def __init__(self, inchannels=512, num_anchors=3): + super(ClassHead, self).__init__() + self.num_anchors = num_anchors + self.conv1x1 = nn.Conv2d(inchannels, self.num_anchors * 2, kernel_size=(1, 1), stride=1, padding=0) + + def forward(self, x): + out = self.conv1x1(x) + out = out.permute(0, 2, 3, 1).contiguous() + + return out.view(out.shape[0], -1, 2) + + +class BboxHead(nn.Module): + + def __init__(self, inchannels=512, num_anchors=3): + super(BboxHead, self).__init__() + self.conv1x1 = nn.Conv2d(inchannels, num_anchors * 4, kernel_size=(1, 1), stride=1, padding=0) + + def forward(self, x): + out = self.conv1x1(x) + out = out.permute(0, 2, 3, 1).contiguous() + + return out.view(out.shape[0], -1, 4) + + +class LandmarkHead(nn.Module): + + def __init__(self, inchannels=512, num_anchors=3): + super(LandmarkHead, self).__init__() + self.conv1x1 = nn.Conv2d(inchannels, num_anchors * 10, kernel_size=(1, 1), stride=1, padding=0) + + def forward(self, x): + out = self.conv1x1(x) + out = out.permute(0, 2, 3, 1).contiguous() + + return out.view(out.shape[0], -1, 10) + + +def make_class_head(fpn_num=3, inchannels=64, anchor_num=2): + classhead = nn.ModuleList() + for i in range(fpn_num): + classhead.append(ClassHead(inchannels, anchor_num)) + return classhead + + +def make_bbox_head(fpn_num=3, inchannels=64, anchor_num=2): + bboxhead = nn.ModuleList() + for i in range(fpn_num): + bboxhead.append(BboxHead(inchannels, anchor_num)) + return bboxhead + + +def make_landmark_head(fpn_num=3, inchannels=64, anchor_num=2): + landmarkhead = nn.ModuleList() + for i in range(fpn_num): + landmarkhead.append(LandmarkHead(inchannels, anchor_num)) + return landmarkhead diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/retinaface_utils.py b/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/retinaface_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..8c357757741c6d9bd7ce4d8ce740fefd51850fbf --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/retinaface/retinaface_utils.py @@ -0,0 +1,421 @@ +import numpy as np +import torch +import torchvision +from itertools import product as product +from math import ceil + + +class PriorBox(object): + + def __init__(self, cfg, image_size=None, phase='train'): + super(PriorBox, self).__init__() + self.min_sizes = cfg['min_sizes'] + self.steps = cfg['steps'] + self.clip = cfg['clip'] + self.image_size = image_size + self.feature_maps = [[ceil(self.image_size[0] / step), ceil(self.image_size[1] / step)] for step in self.steps] + self.name = 's' + + def forward(self): + anchors = [] + for k, f in enumerate(self.feature_maps): + min_sizes = self.min_sizes[k] + for i, j in product(range(f[0]), range(f[1])): + for min_size in min_sizes: + s_kx = min_size / self.image_size[1] + s_ky = min_size / self.image_size[0] + dense_cx = [x * self.steps[k] / self.image_size[1] for x in [j + 0.5]] + dense_cy = [y * self.steps[k] / self.image_size[0] for y in [i + 0.5]] + for cy, cx in product(dense_cy, dense_cx): + anchors += [cx, cy, s_kx, s_ky] + + # back to torch land + output = torch.Tensor(anchors).view(-1, 4) + if self.clip: + output.clamp_(max=1, min=0) + return output + + +def py_cpu_nms(dets, thresh): + """Pure Python NMS baseline.""" + keep = torchvision.ops.nms( + boxes=torch.Tensor(dets[:, :4]), + scores=torch.Tensor(dets[:, 4]), + iou_threshold=thresh, + ) + + return list(keep) + + +def point_form(boxes): + """ Convert prior_boxes to (xmin, ymin, xmax, ymax) + representation for comparison to point form ground truth data. + Args: + boxes: (tensor) center-size default boxes from priorbox layers. + Return: + boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes. + """ + return torch.cat( + ( + boxes[:, :2] - boxes[:, 2:] / 2, # xmin, ymin + boxes[:, :2] + boxes[:, 2:] / 2), + 1) # xmax, ymax + + +def center_size(boxes): + """ Convert prior_boxes to (cx, cy, w, h) + representation for comparison to center-size form ground truth data. + Args: + boxes: (tensor) point_form boxes + Return: + boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes. + """ + return torch.cat( + (boxes[:, 2:] + boxes[:, :2]) / 2, # cx, cy + boxes[:, 2:] - boxes[:, :2], + 1) # w, h + + +def intersect(box_a, box_b): + """ We resize both tensors to [A,B,2] without new malloc: + [A,2] -> [A,1,2] -> [A,B,2] + [B,2] -> [1,B,2] -> [A,B,2] + Then we compute the area of intersect between box_a and box_b. + Args: + box_a: (tensor) bounding boxes, Shape: [A,4]. + box_b: (tensor) bounding boxes, Shape: [B,4]. + Return: + (tensor) intersection area, Shape: [A,B]. + """ + A = box_a.size(0) + B = box_b.size(0) + max_xy = torch.min(box_a[:, 2:].unsqueeze(1).expand(A, B, 2), box_b[:, 2:].unsqueeze(0).expand(A, B, 2)) + min_xy = torch.max(box_a[:, :2].unsqueeze(1).expand(A, B, 2), box_b[:, :2].unsqueeze(0).expand(A, B, 2)) + inter = torch.clamp((max_xy - min_xy), min=0) + return inter[:, :, 0] * inter[:, :, 1] + + +def jaccard(box_a, box_b): + """Compute the jaccard overlap of two sets of boxes. The jaccard overlap + is simply the intersection over union of two boxes. Here we operate on + ground truth boxes and default boxes. + E.g.: + A ∩ B / A ∪ B = A ∩ B / (area(A) + area(B) - A ∩ B) + Args: + box_a: (tensor) Ground truth bounding boxes, Shape: [num_objects,4] + box_b: (tensor) Prior boxes from priorbox layers, Shape: [num_priors,4] + Return: + jaccard overlap: (tensor) Shape: [box_a.size(0), box_b.size(0)] + """ + inter = intersect(box_a, box_b) + area_a = ((box_a[:, 2] - box_a[:, 0]) * (box_a[:, 3] - box_a[:, 1])).unsqueeze(1).expand_as(inter) # [A,B] + area_b = ((box_b[:, 2] - box_b[:, 0]) * (box_b[:, 3] - box_b[:, 1])).unsqueeze(0).expand_as(inter) # [A,B] + union = area_a + area_b - inter + return inter / union # [A,B] + + +def matrix_iou(a, b): + """ + return iou of a and b, numpy version for data augenmentation + """ + lt = np.maximum(a[:, np.newaxis, :2], b[:, :2]) + rb = np.minimum(a[:, np.newaxis, 2:], b[:, 2:]) + + area_i = np.prod(rb - lt, axis=2) * (lt < rb).all(axis=2) + area_a = np.prod(a[:, 2:] - a[:, :2], axis=1) + area_b = np.prod(b[:, 2:] - b[:, :2], axis=1) + return area_i / (area_a[:, np.newaxis] + area_b - area_i) + + +def matrix_iof(a, b): + """ + return iof of a and b, numpy version for data augenmentation + """ + lt = np.maximum(a[:, np.newaxis, :2], b[:, :2]) + rb = np.minimum(a[:, np.newaxis, 2:], b[:, 2:]) + + area_i = np.prod(rb - lt, axis=2) * (lt < rb).all(axis=2) + area_a = np.prod(a[:, 2:] - a[:, :2], axis=1) + return area_i / np.maximum(area_a[:, np.newaxis], 1) + + +def match(threshold, truths, priors, variances, labels, landms, loc_t, conf_t, landm_t, idx): + """Match each prior box with the ground truth box of the highest jaccard + overlap, encode the bounding boxes, then return the matched indices + corresponding to both confidence and location preds. + Args: + threshold: (float) The overlap threshold used when matching boxes. + truths: (tensor) Ground truth boxes, Shape: [num_obj, 4]. + priors: (tensor) Prior boxes from priorbox layers, Shape: [n_priors,4]. + variances: (tensor) Variances corresponding to each prior coord, + Shape: [num_priors, 4]. + labels: (tensor) All the class labels for the image, Shape: [num_obj]. + landms: (tensor) Ground truth landms, Shape [num_obj, 10]. + loc_t: (tensor) Tensor to be filled w/ encoded location targets. + conf_t: (tensor) Tensor to be filled w/ matched indices for conf preds. + landm_t: (tensor) Tensor to be filled w/ encoded landm targets. + idx: (int) current batch index + Return: + The matched indices corresponding to 1)location 2)confidence + 3)landm preds. + """ + # jaccard index + overlaps = jaccard(truths, point_form(priors)) + # (Bipartite Matching) + # [1,num_objects] best prior for each ground truth + best_prior_overlap, best_prior_idx = overlaps.max(1, keepdim=True) + + # ignore hard gt + valid_gt_idx = best_prior_overlap[:, 0] >= 0.2 + best_prior_idx_filter = best_prior_idx[valid_gt_idx, :] + if best_prior_idx_filter.shape[0] <= 0: + loc_t[idx] = 0 + conf_t[idx] = 0 + return + + # [1,num_priors] best ground truth for each prior + best_truth_overlap, best_truth_idx = overlaps.max(0, keepdim=True) + best_truth_idx.squeeze_(0) + best_truth_overlap.squeeze_(0) + best_prior_idx.squeeze_(1) + best_prior_idx_filter.squeeze_(1) + best_prior_overlap.squeeze_(1) + best_truth_overlap.index_fill_(0, best_prior_idx_filter, 2) # ensure best prior + # TODO refactor: index best_prior_idx with long tensor + # ensure every gt matches with its prior of max overlap + for j in range(best_prior_idx.size(0)): # 判别此anchor是预测哪一个boxes + best_truth_idx[best_prior_idx[j]] = j + matches = truths[best_truth_idx] # Shape: [num_priors,4] 此处为每一个anchor对应的bbox取出来 + conf = labels[best_truth_idx] # Shape: [num_priors] 此处为每一个anchor对应的label取出来 + conf[best_truth_overlap < threshold] = 0 # label as background overlap<0.35的全部作为负样本 + loc = encode(matches, priors, variances) + + matches_landm = landms[best_truth_idx] + landm = encode_landm(matches_landm, priors, variances) + loc_t[idx] = loc # [num_priors,4] encoded offsets to learn + conf_t[idx] = conf # [num_priors] top class label for each prior + landm_t[idx] = landm + + +def encode(matched, priors, variances): + """Encode the variances from the priorbox layers into the ground truth boxes + we have matched (based on jaccard overlap) with the prior boxes. + Args: + matched: (tensor) Coords of ground truth for each prior in point-form + Shape: [num_priors, 4]. + priors: (tensor) Prior boxes in center-offset form + Shape: [num_priors,4]. + variances: (list[float]) Variances of priorboxes + Return: + encoded boxes (tensor), Shape: [num_priors, 4] + """ + + # dist b/t match center and prior's center + g_cxcy = (matched[:, :2] + matched[:, 2:]) / 2 - priors[:, :2] + # encode variance + g_cxcy /= (variances[0] * priors[:, 2:]) + # match wh / prior wh + g_wh = (matched[:, 2:] - matched[:, :2]) / priors[:, 2:] + g_wh = torch.log(g_wh) / variances[1] + # return target for smooth_l1_loss + return torch.cat([g_cxcy, g_wh], 1) # [num_priors,4] + + +def encode_landm(matched, priors, variances): + """Encode the variances from the priorbox layers into the ground truth boxes + we have matched (based on jaccard overlap) with the prior boxes. + Args: + matched: (tensor) Coords of ground truth for each prior in point-form + Shape: [num_priors, 10]. + priors: (tensor) Prior boxes in center-offset form + Shape: [num_priors,4]. + variances: (list[float]) Variances of priorboxes + Return: + encoded landm (tensor), Shape: [num_priors, 10] + """ + + # dist b/t match center and prior's center + matched = torch.reshape(matched, (matched.size(0), 5, 2)) + priors_cx = priors[:, 0].unsqueeze(1).expand(matched.size(0), 5).unsqueeze(2) + priors_cy = priors[:, 1].unsqueeze(1).expand(matched.size(0), 5).unsqueeze(2) + priors_w = priors[:, 2].unsqueeze(1).expand(matched.size(0), 5).unsqueeze(2) + priors_h = priors[:, 3].unsqueeze(1).expand(matched.size(0), 5).unsqueeze(2) + priors = torch.cat([priors_cx, priors_cy, priors_w, priors_h], dim=2) + g_cxcy = matched[:, :, :2] - priors[:, :, :2] + # encode variance + g_cxcy /= (variances[0] * priors[:, :, 2:]) + # g_cxcy /= priors[:, :, 2:] + g_cxcy = g_cxcy.reshape(g_cxcy.size(0), -1) + # return target for smooth_l1_loss + return g_cxcy + + +# Adapted from https://github.com/Hakuyume/chainer-ssd +def decode(loc, priors, variances): + """Decode locations from predictions using priors to undo + the encoding we did for offset regression at train time. + Args: + loc (tensor): location predictions for loc layers, + Shape: [num_priors,4] + priors (tensor): Prior boxes in center-offset form. + Shape: [num_priors,4]. + variances: (list[float]) Variances of priorboxes + Return: + decoded bounding box predictions + """ + + boxes = torch.cat((priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:], + priors[:, 2:] * torch.exp(loc[:, 2:] * variances[1])), 1) + boxes[:, :2] -= boxes[:, 2:] / 2 + boxes[:, 2:] += boxes[:, :2] + return boxes + + +def decode_landm(pre, priors, variances): + """Decode landm from predictions using priors to undo + the encoding we did for offset regression at train time. + Args: + pre (tensor): landm predictions for loc layers, + Shape: [num_priors,10] + priors (tensor): Prior boxes in center-offset form. + Shape: [num_priors,4]. + variances: (list[float]) Variances of priorboxes + Return: + decoded landm predictions + """ + tmp = ( + priors[:, :2] + pre[:, :2] * variances[0] * priors[:, 2:], + priors[:, :2] + pre[:, 2:4] * variances[0] * priors[:, 2:], + priors[:, :2] + pre[:, 4:6] * variances[0] * priors[:, 2:], + priors[:, :2] + pre[:, 6:8] * variances[0] * priors[:, 2:], + priors[:, :2] + pre[:, 8:10] * variances[0] * priors[:, 2:], + ) + landms = torch.cat(tmp, dim=1) + return landms + + +def batched_decode(b_loc, priors, variances): + """Decode locations from predictions using priors to undo + the encoding we did for offset regression at train time. + Args: + b_loc (tensor): location predictions for loc layers, + Shape: [num_batches,num_priors,4] + priors (tensor): Prior boxes in center-offset form. + Shape: [1,num_priors,4]. + variances: (list[float]) Variances of priorboxes + Return: + decoded bounding box predictions + """ + boxes = ( + priors[:, :, :2] + b_loc[:, :, :2] * variances[0] * priors[:, :, 2:], + priors[:, :, 2:] * torch.exp(b_loc[:, :, 2:] * variances[1]), + ) + boxes = torch.cat(boxes, dim=2) + + boxes[:, :, :2] -= boxes[:, :, 2:] / 2 + boxes[:, :, 2:] += boxes[:, :, :2] + return boxes + + +def batched_decode_landm(pre, priors, variances): + """Decode landm from predictions using priors to undo + the encoding we did for offset regression at train time. + Args: + pre (tensor): landm predictions for loc layers, + Shape: [num_batches,num_priors,10] + priors (tensor): Prior boxes in center-offset form. + Shape: [1,num_priors,4]. + variances: (list[float]) Variances of priorboxes + Return: + decoded landm predictions + """ + landms = ( + priors[:, :, :2] + pre[:, :, :2] * variances[0] * priors[:, :, 2:], + priors[:, :, :2] + pre[:, :, 2:4] * variances[0] * priors[:, :, 2:], + priors[:, :, :2] + pre[:, :, 4:6] * variances[0] * priors[:, :, 2:], + priors[:, :, :2] + pre[:, :, 6:8] * variances[0] * priors[:, :, 2:], + priors[:, :, :2] + pre[:, :, 8:10] * variances[0] * priors[:, :, 2:], + ) + landms = torch.cat(landms, dim=2) + return landms + + +def log_sum_exp(x): + """Utility function for computing log_sum_exp while determining + This will be used to determine unaveraged confidence loss across + all examples in a batch. + Args: + x (Variable(tensor)): conf_preds from conf layers + """ + x_max = x.data.max() + return torch.log(torch.sum(torch.exp(x - x_max), 1, keepdim=True)) + x_max + + +# Original author: Francisco Massa: +# https://github.com/fmassa/object-detection.torch +# Ported to PyTorch by Max deGroot (02/01/2017) +def nms(boxes, scores, overlap=0.5, top_k=200): + """Apply non-maximum suppression at test time to avoid detecting too many + overlapping bounding boxes for a given object. + Args: + boxes: (tensor) The location preds for the img, Shape: [num_priors,4]. + scores: (tensor) The class predscores for the img, Shape:[num_priors]. + overlap: (float) The overlap thresh for suppressing unnecessary boxes. + top_k: (int) The Maximum number of box preds to consider. + Return: + The indices of the kept boxes with respect to num_priors. + """ + + keep = torch.Tensor(scores.size(0)).fill_(0).long() + if boxes.numel() == 0: + return keep + x1 = boxes[:, 0] + y1 = boxes[:, 1] + x2 = boxes[:, 2] + y2 = boxes[:, 3] + area = torch.mul(x2 - x1, y2 - y1) + v, idx = scores.sort(0) # sort in ascending order + # I = I[v >= 0.01] + idx = idx[-top_k:] # indices of the top-k largest vals + xx1 = boxes.new() + yy1 = boxes.new() + xx2 = boxes.new() + yy2 = boxes.new() + w = boxes.new() + h = boxes.new() + + # keep = torch.Tensor() + count = 0 + while idx.numel() > 0: + i = idx[-1] # index of current largest val + # keep.append(i) + keep[count] = i + count += 1 + if idx.size(0) == 1: + break + idx = idx[:-1] # remove kept element from view + # load bboxes of next highest vals + torch.index_select(x1, 0, idx, out=xx1) + torch.index_select(y1, 0, idx, out=yy1) + torch.index_select(x2, 0, idx, out=xx2) + torch.index_select(y2, 0, idx, out=yy2) + # store element-wise max with next highest score + xx1 = torch.clamp(xx1, min=x1[i]) + yy1 = torch.clamp(yy1, min=y1[i]) + xx2 = torch.clamp(xx2, max=x2[i]) + yy2 = torch.clamp(yy2, max=y2[i]) + w.resize_as_(xx2) + h.resize_as_(yy2) + w = xx2 - xx1 + h = yy2 - yy1 + # check sizes of xx1 and xx2.. after each iteration + w = torch.clamp(w, min=0.0) + h = torch.clamp(h, min=0.0) + inter = w * h + # IoU = i / (area(a) + area(b) - i) + rem_areas = torch.index_select(area, 0, idx) # load remaining areas) + union = (rem_areas - inter) + area[i] + IoU = inter / union # store result in iou + # keep only elements with an IoU <= overlap + idx = idx[IoU.le(overlap)] + return keep, count diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/__init__.py b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1476e34a31791a704c3e9fb50170721749c0d50 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/__pycache__/face_detector.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/__pycache__/face_detector.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..94e50550275f60b37c01047deb9105a13e48fbdc Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/__pycache__/face_detector.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/face_detector.py b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/face_detector.py new file mode 100644 index 0000000000000000000000000000000000000000..79fdba0c9bd65fba5664e55ef2899c71965d9a56 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/face_detector.py @@ -0,0 +1,142 @@ +import copy +import os +from pathlib import Path + +import cv2 +import numpy as np +import torch +from torch import nn + +from facelib.detection.yolov5face.models.common import Conv +from facelib.detection.yolov5face.models.yolo import Model +from facelib.detection.yolov5face.utils.datasets import letterbox +from facelib.detection.yolov5face.utils.general import ( + check_img_size, + non_max_suppression_face, + scale_coords, + scale_coords_landmarks, +) + +IS_HIGH_VERSION = tuple(map(int, torch.__version__.split('+')[0].split('.')[:2])) >= (1, 9, 0) + + +def isListempty(inList): + if isinstance(inList, list): # Is a list + return all(map(isListempty, inList)) + return False # Not a list + +class YoloDetector: + def __init__( + self, + config_name, + min_face=10, + target_size=None, + device='cuda', + ): + """ + config_name: name of .yaml config with network configuration from models/ folder. + min_face : minimal face size in pixels. + target_size : target size of smaller image axis (choose lower for faster work). e.g. 480, 720, 1080. + None for original resolution. + """ + self._class_path = Path(__file__).parent.absolute() + self.target_size = target_size + self.min_face = min_face + self.detector = Model(cfg=config_name) + self.device = device + + + def _preprocess(self, imgs): + """ + Preprocessing image before passing through the network. Resize and conversion to torch tensor. + """ + pp_imgs = [] + for img in imgs: + h0, w0 = img.shape[:2] # orig hw + if self.target_size: + r = self.target_size / min(h0, w0) # resize image to img_size + if r < 1: + img = cv2.resize(img, (int(w0 * r), int(h0 * r)), interpolation=cv2.INTER_LINEAR) + + imgsz = check_img_size(max(img.shape[:2]), s=self.detector.stride.max()) # check img_size + img = letterbox(img, new_shape=imgsz)[0] + pp_imgs.append(img) + pp_imgs = np.array(pp_imgs) + pp_imgs = pp_imgs.transpose(0, 3, 1, 2) + pp_imgs = torch.from_numpy(pp_imgs).to(self.device) + pp_imgs = pp_imgs.float() # uint8 to fp16/32 + return pp_imgs / 255.0 # 0 - 255 to 0.0 - 1.0 + + def _postprocess(self, imgs, origimgs, pred, conf_thres, iou_thres): + """ + Postprocessing of raw pytorch model output. + Returns: + bboxes: list of arrays with 4 coordinates of bounding boxes with format x1,y1,x2,y2. + points: list of arrays with coordinates of 5 facial keypoints (eyes, nose, lips corners). + """ + bboxes = [[] for _ in range(len(origimgs))] + landmarks = [[] for _ in range(len(origimgs))] + + pred = non_max_suppression_face(pred, conf_thres, iou_thres) + + for image_id, origimg in enumerate(origimgs): + img_shape = origimg.shape + image_height, image_width = img_shape[:2] + gn = torch.tensor(img_shape)[[1, 0, 1, 0]] # normalization gain whwh + gn_lks = torch.tensor(img_shape)[[1, 0, 1, 0, 1, 0, 1, 0, 1, 0]] # normalization gain landmarks + det = pred[image_id].cpu() + scale_coords(imgs[image_id].shape[1:], det[:, :4], img_shape).round() + scale_coords_landmarks(imgs[image_id].shape[1:], det[:, 5:15], img_shape).round() + + for j in range(det.size()[0]): + box = (det[j, :4].view(1, 4) / gn).view(-1).tolist() + box = list( + map(int, [box[0] * image_width, box[1] * image_height, box[2] * image_width, box[3] * image_height]) + ) + if box[3] - box[1] < self.min_face: + continue + lm = (det[j, 5:15].view(1, 10) / gn_lks).view(-1).tolist() + lm = list(map(int, [i * image_width if j % 2 == 0 else i * image_height for j, i in enumerate(lm)])) + lm = [lm[i : i + 2] for i in range(0, len(lm), 2)] + bboxes[image_id].append(box) + landmarks[image_id].append(lm) + return bboxes, landmarks + + def detect_faces(self, imgs, conf_thres=0.7, iou_thres=0.5): + """ + Get bbox coordinates and keypoints of faces on original image. + Params: + imgs: image or list of images to detect faces on with BGR order (convert to RGB order for inference) + conf_thres: confidence threshold for each prediction + iou_thres: threshold for NMS (filter of intersecting bboxes) + Returns: + bboxes: list of arrays with 4 coordinates of bounding boxes with format x1,y1,x2,y2. + points: list of arrays with coordinates of 5 facial keypoints (eyes, nose, lips corners). + """ + # Pass input images through face detector + images = imgs if isinstance(imgs, list) else [imgs] + images = [cv2.cvtColor(img, cv2.COLOR_BGR2RGB) for img in images] + origimgs = copy.deepcopy(images) + + images = self._preprocess(images) + + if IS_HIGH_VERSION: + with torch.inference_mode(): # for pytorch>=1.9 + pred = self.detector(images)[0] + else: + with torch.no_grad(): # for pytorch<1.9 + pred = self.detector(images)[0] + + bboxes, points = self._postprocess(images, origimgs, pred, conf_thres, iou_thres) + + # return bboxes, points + if not isListempty(points): + bboxes = np.array(bboxes).reshape(-1,4) + points = np.array(points).reshape(-1,10) + padding = bboxes[:,0].reshape(-1,1) + return np.concatenate((bboxes, padding, points), axis=1) + else: + return None + + def __call__(self, *args): + return self.predict(*args) diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__init__.py b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..86630169690f4715c017653743f759ad5ad221aa Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__pycache__/common.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__pycache__/common.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61a545c37325182668e8870f03cc73cf6af0e0c9 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__pycache__/common.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__pycache__/experimental.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__pycache__/experimental.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f0ae24bf7e64fb660ea76291584c7160cc70e15c Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__pycache__/experimental.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__pycache__/yolo.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__pycache__/yolo.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d51031089e74d9454c8bea28deb4c0c5835591a1 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/__pycache__/yolo.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/common.py b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/common.py new file mode 100644 index 0000000000000000000000000000000000000000..497a00444c4c59725001993a63fe4617e9d323c8 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/common.py @@ -0,0 +1,299 @@ +# This file contains modules common to various models + +import math + +import numpy as np +import torch +from torch import nn + +from facelib.detection.yolov5face.utils.datasets import letterbox +from facelib.detection.yolov5face.utils.general import ( + make_divisible, + non_max_suppression, + scale_coords, + xyxy2xywh, +) + + +def autopad(k, p=None): # kernel, padding + # Pad to 'same' + if p is None: + p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad + return p + + +def channel_shuffle(x, groups): + batchsize, num_channels, height, width = x.data.size() + channels_per_group = torch.div(num_channels, groups, rounding_mode="trunc") + + # reshape + x = x.view(batchsize, groups, channels_per_group, height, width) + x = torch.transpose(x, 1, 2).contiguous() + + # flatten + return x.view(batchsize, -1, height, width) + + +def DWConv(c1, c2, k=1, s=1, act=True): + # Depthwise convolution + return Conv(c1, c2, k, s, g=math.gcd(c1, c2), act=act) + + +class Conv(nn.Module): + # Standard convolution + def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups + super().__init__() + self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) + self.bn = nn.BatchNorm2d(c2) + self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) + + def forward(self, x): + return self.act(self.bn(self.conv(x))) + + def fuseforward(self, x): + return self.act(self.conv(x)) + + +class StemBlock(nn.Module): + def __init__(self, c1, c2, k=3, s=2, p=None, g=1, act=True): + super().__init__() + self.stem_1 = Conv(c1, c2, k, s, p, g, act) + self.stem_2a = Conv(c2, c2 // 2, 1, 1, 0) + self.stem_2b = Conv(c2 // 2, c2, 3, 2, 1) + self.stem_2p = nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True) + self.stem_3 = Conv(c2 * 2, c2, 1, 1, 0) + + def forward(self, x): + stem_1_out = self.stem_1(x) + stem_2a_out = self.stem_2a(stem_1_out) + stem_2b_out = self.stem_2b(stem_2a_out) + stem_2p_out = self.stem_2p(stem_1_out) + return self.stem_3(torch.cat((stem_2b_out, stem_2p_out), 1)) + + +class Bottleneck(nn.Module): + # Standard bottleneck + def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion + super().__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c_, c2, 3, 1, g=g) + self.add = shortcut and c1 == c2 + + def forward(self, x): + return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) + + +class BottleneckCSP(nn.Module): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False) + self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False) + self.cv4 = Conv(2 * c_, c2, 1, 1) + self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3) + self.act = nn.LeakyReLU(0.1, inplace=True) + self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n))) + + def forward(self, x): + y1 = self.cv3(self.m(self.cv1(x))) + y2 = self.cv2(x) + return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1)))) + + +class C3(nn.Module): + # CSP Bottleneck with 3 convolutions + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c1, c_, 1, 1) + self.cv3 = Conv(2 * c_, c2, 1) # act=FReLU(c2) + self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n))) + + def forward(self, x): + return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1)) + + +class ShuffleV2Block(nn.Module): + def __init__(self, inp, oup, stride): + super().__init__() + + if not 1 <= stride <= 3: + raise ValueError("illegal stride value") + self.stride = stride + + branch_features = oup // 2 + + if self.stride > 1: + self.branch1 = nn.Sequential( + self.depthwise_conv(inp, inp, kernel_size=3, stride=self.stride, padding=1), + nn.BatchNorm2d(inp), + nn.Conv2d(inp, branch_features, kernel_size=1, stride=1, padding=0, bias=False), + nn.BatchNorm2d(branch_features), + nn.SiLU(), + ) + else: + self.branch1 = nn.Sequential() + + self.branch2 = nn.Sequential( + nn.Conv2d( + inp if (self.stride > 1) else branch_features, + branch_features, + kernel_size=1, + stride=1, + padding=0, + bias=False, + ), + nn.BatchNorm2d(branch_features), + nn.SiLU(), + self.depthwise_conv(branch_features, branch_features, kernel_size=3, stride=self.stride, padding=1), + nn.BatchNorm2d(branch_features), + nn.Conv2d(branch_features, branch_features, kernel_size=1, stride=1, padding=0, bias=False), + nn.BatchNorm2d(branch_features), + nn.SiLU(), + ) + + @staticmethod + def depthwise_conv(i, o, kernel_size, stride=1, padding=0, bias=False): + return nn.Conv2d(i, o, kernel_size, stride, padding, bias=bias, groups=i) + + def forward(self, x): + if self.stride == 1: + x1, x2 = x.chunk(2, dim=1) + out = torch.cat((x1, self.branch2(x2)), dim=1) + else: + out = torch.cat((self.branch1(x), self.branch2(x)), dim=1) + out = channel_shuffle(out, 2) + return out + + +class SPP(nn.Module): + # Spatial pyramid pooling layer used in YOLOv3-SPP + def __init__(self, c1, c2, k=(5, 9, 13)): + super().__init__() + c_ = c1 // 2 # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1) + self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k]) + + def forward(self, x): + x = self.cv1(x) + return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1)) + + +class Focus(nn.Module): + # Focus wh information into c-space + def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups + super().__init__() + self.conv = Conv(c1 * 4, c2, k, s, p, g, act) + + def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2) + return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1)) + + +class Concat(nn.Module): + # Concatenate a list of tensors along dimension + def __init__(self, dimension=1): + super().__init__() + self.d = dimension + + def forward(self, x): + return torch.cat(x, self.d) + + +class NMS(nn.Module): + # Non-Maximum Suppression (NMS) module + conf = 0.25 # confidence threshold + iou = 0.45 # IoU threshold + classes = None # (optional list) filter by class + + def forward(self, x): + return non_max_suppression(x[0], conf_thres=self.conf, iou_thres=self.iou, classes=self.classes) + + +class AutoShape(nn.Module): + # input-robust model wrapper for passing cv2/np/PIL/torch inputs. Includes preprocessing, inference and NMS + img_size = 640 # inference size (pixels) + conf = 0.25 # NMS confidence threshold + iou = 0.45 # NMS IoU threshold + classes = None # (optional list) filter by class + + def __init__(self, model): + super().__init__() + self.model = model.eval() + + def autoshape(self): + print("autoShape already enabled, skipping... ") # model already converted to model.autoshape() + return self + + def forward(self, imgs, size=640, augment=False, profile=False): + # Inference from various sources. For height=720, width=1280, RGB images example inputs are: + # OpenCV: = cv2.imread('image.jpg')[:,:,::-1] # HWC BGR to RGB x(720,1280,3) + # PIL: = Image.open('image.jpg') # HWC x(720,1280,3) + # numpy: = np.zeros((720,1280,3)) # HWC + # torch: = torch.zeros(16,3,720,1280) # BCHW + # multiple: = [Image.open('image1.jpg'), Image.open('image2.jpg'), ...] # list of images + + p = next(self.model.parameters()) # for device and type + if isinstance(imgs, torch.Tensor): # torch + return self.model(imgs.to(p.device).type_as(p), augment, profile) # inference + + # Pre-process + n, imgs = (len(imgs), imgs) if isinstance(imgs, list) else (1, [imgs]) # number of images, list of images + shape0, shape1 = [], [] # image and inference shapes + for i, im in enumerate(imgs): + im = np.array(im) # to numpy + if im.shape[0] < 5: # image in CHW + im = im.transpose((1, 2, 0)) # reverse dataloader .transpose(2, 0, 1) + im = im[:, :, :3] if im.ndim == 3 else np.tile(im[:, :, None], 3) # enforce 3ch input + s = im.shape[:2] # HWC + shape0.append(s) # image shape + g = size / max(s) # gain + shape1.append([y * g for y in s]) + imgs[i] = im # update + shape1 = [make_divisible(x, int(self.stride.max())) for x in np.stack(shape1, 0).max(0)] # inference shape + x = [letterbox(im, new_shape=shape1, auto=False)[0] for im in imgs] # pad + x = np.stack(x, 0) if n > 1 else x[0][None] # stack + x = np.ascontiguousarray(x.transpose((0, 3, 1, 2))) # BHWC to BCHW + x = torch.from_numpy(x).to(p.device).type_as(p) / 255.0 # uint8 to fp16/32 + + # Inference + with torch.no_grad(): + y = self.model(x, augment, profile)[0] # forward + y = non_max_suppression(y, conf_thres=self.conf, iou_thres=self.iou, classes=self.classes) # NMS + + # Post-process + for i in range(n): + scale_coords(shape1, y[i][:, :4], shape0[i]) + + return Detections(imgs, y, self.names) + + +class Detections: + # detections class for YOLOv5 inference results + def __init__(self, imgs, pred, names=None): + super().__init__() + d = pred[0].device # device + gn = [torch.tensor([*(im.shape[i] for i in [1, 0, 1, 0]), 1.0, 1.0], device=d) for im in imgs] # normalizations + self.imgs = imgs # list of images as numpy arrays + self.pred = pred # list of tensors pred[0] = (xyxy, conf, cls) + self.names = names # class names + self.xyxy = pred # xyxy pixels + self.xywh = [xyxy2xywh(x) for x in pred] # xywh pixels + self.xyxyn = [x / g for x, g in zip(self.xyxy, gn)] # xyxy normalized + self.xywhn = [x / g for x, g in zip(self.xywh, gn)] # xywh normalized + self.n = len(self.pred) + + def __len__(self): + return self.n + + def tolist(self): + # return a list of Detections objects, i.e. 'for result in results.tolist():' + x = [Detections([self.imgs[i]], [self.pred[i]], self.names) for i in range(self.n)] + for d in x: + for k in ["imgs", "pred", "xyxy", "xyxyn", "xywh", "xywhn"]: + setattr(d, k, getattr(d, k)[0]) # pop out of list + return x diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/experimental.py b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/experimental.py new file mode 100644 index 0000000000000000000000000000000000000000..37ba4c4420789c92dc0e2aaeb3d5b64859ec728c --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/experimental.py @@ -0,0 +1,45 @@ +# # This file contains experimental modules + +import numpy as np +import torch +from torch import nn + +from facelib.detection.yolov5face.models.common import Conv + + +class CrossConv(nn.Module): + # Cross Convolution Downsample + def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False): + # ch_in, ch_out, kernel, stride, groups, expansion, shortcut + super().__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, (1, k), (1, s)) + self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g) + self.add = shortcut and c1 == c2 + + def forward(self, x): + return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) + + +class MixConv2d(nn.Module): + # Mixed Depthwise Conv https://arxiv.org/abs/1907.09595 + def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True): + super().__init__() + groups = len(k) + if equal_ch: # equal c_ per group + i = torch.linspace(0, groups - 1e-6, c2).floor() # c2 indices + c_ = [(i == g).sum() for g in range(groups)] # intermediate channels + else: # equal weight.numel() per group + b = [c2] + [0] * groups + a = np.eye(groups + 1, groups, k=-1) + a -= np.roll(a, 1, axis=1) + a *= np.array(k) ** 2 + a[0] = 1 + c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b + + self.m = nn.ModuleList([nn.Conv2d(c1, int(c_[g]), k[g], s, k[g] // 2, bias=False) for g in range(groups)]) + self.bn = nn.BatchNorm2d(c2) + self.act = nn.LeakyReLU(0.1, inplace=True) + + def forward(self, x): + return x + self.act(self.bn(torch.cat([m(x) for m in self.m], 1))) diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/yolo.py b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/yolo.py new file mode 100644 index 0000000000000000000000000000000000000000..70845d972f0bcfd3632fcbac096b23e1b4d4d779 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/yolo.py @@ -0,0 +1,235 @@ +import math +from copy import deepcopy +from pathlib import Path + +import torch +import yaml # for torch hub +from torch import nn + +from facelib.detection.yolov5face.models.common import ( + C3, + NMS, + SPP, + AutoShape, + Bottleneck, + BottleneckCSP, + Concat, + Conv, + DWConv, + Focus, + ShuffleV2Block, + StemBlock, +) +from facelib.detection.yolov5face.models.experimental import CrossConv, MixConv2d +from facelib.detection.yolov5face.utils.autoanchor import check_anchor_order +from facelib.detection.yolov5face.utils.general import make_divisible +from facelib.detection.yolov5face.utils.torch_utils import copy_attr, fuse_conv_and_bn + + +class Detect(nn.Module): + stride = None # strides computed during build + export = False # onnx export + + def __init__(self, nc=80, anchors=(), ch=()): # detection layer + super().__init__() + self.nc = nc # number of classes + self.no = nc + 5 + 10 # number of outputs per anchor + + self.nl = len(anchors) # number of detection layers + self.na = len(anchors[0]) // 2 # number of anchors + self.grid = [torch.zeros(1)] * self.nl # init grid + a = torch.tensor(anchors).float().view(self.nl, -1, 2) + self.register_buffer("anchors", a) # shape(nl,na,2) + self.register_buffer("anchor_grid", a.clone().view(self.nl, 1, -1, 1, 1, 2)) # shape(nl,1,na,1,1,2) + self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv + + def forward(self, x): + z = [] # inference output + if self.export: + for i in range(self.nl): + x[i] = self.m[i](x[i]) + return x + for i in range(self.nl): + x[i] = self.m[i](x[i]) # conv + bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) + x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() + + if not self.training: # inference + if self.grid[i].shape[2:4] != x[i].shape[2:4]: + self.grid[i] = self._make_grid(nx, ny).to(x[i].device) + + y = torch.full_like(x[i], 0) + y[..., [0, 1, 2, 3, 4, 15]] = x[i][..., [0, 1, 2, 3, 4, 15]].sigmoid() + y[..., 5:15] = x[i][..., 5:15] + + y[..., 0:2] = (y[..., 0:2] * 2.0 - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i] # xy + y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh + + y[..., 5:7] = ( + y[..., 5:7] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i] + ) # landmark x1 y1 + y[..., 7:9] = ( + y[..., 7:9] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i] + ) # landmark x2 y2 + y[..., 9:11] = ( + y[..., 9:11] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i] + ) # landmark x3 y3 + y[..., 11:13] = ( + y[..., 11:13] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i] + ) # landmark x4 y4 + y[..., 13:15] = ( + y[..., 13:15] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i] + ) # landmark x5 y5 + + z.append(y.view(bs, -1, self.no)) + + return x if self.training else (torch.cat(z, 1), x) + + @staticmethod + def _make_grid(nx=20, ny=20): + # yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)], indexing="ij") # for pytorch>=1.10 + yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)]) + return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float() + + +class Model(nn.Module): + def __init__(self, cfg="yolov5s.yaml", ch=3, nc=None): # model, input channels, number of classes + super().__init__() + self.yaml_file = Path(cfg).name + with Path(cfg).open(encoding="utf8") as f: + self.yaml = yaml.safe_load(f) # model dict + + # Define model + ch = self.yaml["ch"] = self.yaml.get("ch", ch) # input channels + if nc and nc != self.yaml["nc"]: + self.yaml["nc"] = nc # override yaml value + + self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch]) # model, savelist + self.names = [str(i) for i in range(self.yaml["nc"])] # default names + + # Build strides, anchors + m = self.model[-1] # Detect() + if isinstance(m, Detect): + s = 128 # 2x min stride + m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))]) # forward + m.anchors /= m.stride.view(-1, 1, 1) + check_anchor_order(m) + self.stride = m.stride + self._initialize_biases() # only run once + + def forward(self, x): + return self.forward_once(x) # single-scale inference, train + + def forward_once(self, x): + y = [] # outputs + for m in self.model: + if m.f != -1: # if not from previous layer + x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers + + x = m(x) # run + y.append(x if m.i in self.save else None) # save output + + return x + + def _initialize_biases(self, cf=None): # initialize biases into Detect(), cf is class frequency + # https://arxiv.org/abs/1708.02002 section 3.3 + m = self.model[-1] # Detect() module + for mi, s in zip(m.m, m.stride): # from + b = mi.bias.view(m.na, -1) # conv.bias(255) to (3,85) + b.data[:, 4] += math.log(8 / (640 / s) ** 2) # obj (8 objects per 640 image) + b.data[:, 5:] += math.log(0.6 / (m.nc - 0.99)) if cf is None else torch.log(cf / cf.sum()) # cls + mi.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + + def _print_biases(self): + m = self.model[-1] # Detect() module + for mi in m.m: # from + b = mi.bias.detach().view(m.na, -1).T # conv.bias(255) to (3,85) + print(("%6g Conv2d.bias:" + "%10.3g" * 6) % (mi.weight.shape[1], *b[:5].mean(1).tolist(), b[5:].mean())) + + def fuse(self): # fuse model Conv2d() + BatchNorm2d() layers + print("Fusing layers... ") + for m in self.model.modules(): + if isinstance(m, Conv) and hasattr(m, "bn"): + m.conv = fuse_conv_and_bn(m.conv, m.bn) # update conv + delattr(m, "bn") # remove batchnorm + m.forward = m.fuseforward # update forward + elif type(m) is nn.Upsample: + m.recompute_scale_factor = None # torch 1.11.0 compatibility + return self + + def nms(self, mode=True): # add or remove NMS module + present = isinstance(self.model[-1], NMS) # last layer is NMS + if mode and not present: + print("Adding NMS... ") + m = NMS() # module + m.f = -1 # from + m.i = self.model[-1].i + 1 # index + self.model.add_module(name=str(m.i), module=m) # add + self.eval() + elif not mode and present: + print("Removing NMS... ") + self.model = self.model[:-1] # remove + return self + + def autoshape(self): # add autoShape module + print("Adding autoShape... ") + m = AutoShape(self) # wrap model + copy_attr(m, self, include=("yaml", "nc", "hyp", "names", "stride"), exclude=()) # copy attributes + return m + + +def parse_model(d, ch): # model_dict, input_channels(3) + anchors, nc, gd, gw = d["anchors"], d["nc"], d["depth_multiple"], d["width_multiple"] + na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors # number of anchors + no = na * (nc + 5) # number of outputs = anchors * (classes + 5) + + layers, save, c2 = [], [], ch[-1] # layers, savelist, ch out + for i, (f, n, m, args) in enumerate(d["backbone"] + d["head"]): # from, number, module, args + m = eval(m) if isinstance(m, str) else m # eval strings + for j, a in enumerate(args): + try: + args[j] = eval(a) if isinstance(a, str) else a # eval strings + except: + pass + + n = max(round(n * gd), 1) if n > 1 else n # depth gain + if m in [ + Conv, + Bottleneck, + SPP, + DWConv, + MixConv2d, + Focus, + CrossConv, + BottleneckCSP, + C3, + ShuffleV2Block, + StemBlock, + ]: + c1, c2 = ch[f], args[0] + + c2 = make_divisible(c2 * gw, 8) if c2 != no else c2 + + args = [c1, c2, *args[1:]] + if m in [BottleneckCSP, C3]: + args.insert(2, n) + n = 1 + elif m is nn.BatchNorm2d: + args = [ch[f]] + elif m is Concat: + c2 = sum(ch[-1 if x == -1 else x + 1] for x in f) + elif m is Detect: + args.append([ch[x + 1] for x in f]) + if isinstance(args[1], int): # number of anchors + args[1] = [list(range(args[1] * 2))] * len(f) + else: + c2 = ch[f] + + m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args) # module + t = str(m)[8:-2].replace("__main__.", "") # module type + np = sum(x.numel() for x in m_.parameters()) # number params + m_.i, m_.f, m_.type, m_.np = i, f, t, np # attach index, 'from' index, type, number params + save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist + layers.append(m_) + ch.append(c2) + return nn.Sequential(*layers), sorted(save) diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/yolov5l.yaml b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/yolov5l.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0532b0e22fa7f59349b178146ffddcfdb368aba6 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/yolov5l.yaml @@ -0,0 +1,47 @@ +# parameters +nc: 1 # number of classes +depth_multiple: 1.0 # model depth multiple +width_multiple: 1.0 # layer channel multiple + +# anchors +anchors: + - [4,5, 8,10, 13,16] # P3/8 + - [23,29, 43,55, 73,105] # P4/16 + - [146,217, 231,300, 335,433] # P5/32 + +# YOLOv5 backbone +backbone: + # [from, number, module, args] + [[-1, 1, StemBlock, [64, 3, 2]], # 0-P1/2 + [-1, 3, C3, [128]], + [-1, 1, Conv, [256, 3, 2]], # 2-P3/8 + [-1, 9, C3, [256]], + [-1, 1, Conv, [512, 3, 2]], # 4-P4/16 + [-1, 9, C3, [512]], + [-1, 1, Conv, [1024, 3, 2]], # 6-P5/32 + [-1, 1, SPP, [1024, [3,5,7]]], + [-1, 3, C3, [1024, False]], # 8 + ] + +# YOLOv5 head +head: + [[-1, 1, Conv, [512, 1, 1]], + [-1, 1, nn.Upsample, [None, 2, 'nearest']], + [[-1, 5], 1, Concat, [1]], # cat backbone P4 + [-1, 3, C3, [512, False]], # 12 + + [-1, 1, Conv, [256, 1, 1]], + [-1, 1, nn.Upsample, [None, 2, 'nearest']], + [[-1, 3], 1, Concat, [1]], # cat backbone P3 + [-1, 3, C3, [256, False]], # 16 (P3/8-small) + + [-1, 1, Conv, [256, 3, 2]], + [[-1, 13], 1, Concat, [1]], # cat head P4 + [-1, 3, C3, [512, False]], # 19 (P4/16-medium) + + [-1, 1, Conv, [512, 3, 2]], + [[-1, 9], 1, Concat, [1]], # cat head P5 + [-1, 3, C3, [1024, False]], # 22 (P5/32-large) + + [[16, 19, 22], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) + ] \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/yolov5n.yaml b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/yolov5n.yaml new file mode 100644 index 0000000000000000000000000000000000000000..caba6bed674aa2213b110f19e04eb352ffbeaf1e --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/models/yolov5n.yaml @@ -0,0 +1,45 @@ +# parameters +nc: 1 # number of classes +depth_multiple: 1.0 # model depth multiple +width_multiple: 1.0 # layer channel multiple + +# anchors +anchors: + - [4,5, 8,10, 13,16] # P3/8 + - [23,29, 43,55, 73,105] # P4/16 + - [146,217, 231,300, 335,433] # P5/32 + +# YOLOv5 backbone +backbone: + # [from, number, module, args] + [[-1, 1, StemBlock, [32, 3, 2]], # 0-P2/4 + [-1, 1, ShuffleV2Block, [128, 2]], # 1-P3/8 + [-1, 3, ShuffleV2Block, [128, 1]], # 2 + [-1, 1, ShuffleV2Block, [256, 2]], # 3-P4/16 + [-1, 7, ShuffleV2Block, [256, 1]], # 4 + [-1, 1, ShuffleV2Block, [512, 2]], # 5-P5/32 + [-1, 3, ShuffleV2Block, [512, 1]], # 6 + ] + +# YOLOv5 head +head: + [[-1, 1, Conv, [128, 1, 1]], + [-1, 1, nn.Upsample, [None, 2, 'nearest']], + [[-1, 4], 1, Concat, [1]], # cat backbone P4 + [-1, 1, C3, [128, False]], # 10 + + [-1, 1, Conv, [128, 1, 1]], + [-1, 1, nn.Upsample, [None, 2, 'nearest']], + [[-1, 2], 1, Concat, [1]], # cat backbone P3 + [-1, 1, C3, [128, False]], # 14 (P3/8-small) + + [-1, 1, Conv, [128, 3, 2]], + [[-1, 11], 1, Concat, [1]], # cat head P4 + [-1, 1, C3, [128, False]], # 17 (P4/16-medium) + + [-1, 1, Conv, [128, 3, 2]], + [[-1, 7], 1, Concat, [1]], # cat head P5 + [-1, 1, C3, [128, False]], # 20 (P5/32-large) + + [[14, 17, 20], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) + ] diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__init__.py b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61176c18ad2d6026972fea9c45b2117b6507229f Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/autoanchor.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/autoanchor.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66376af3df27cdf034265e1e9ec9aa81fa56a6e7 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/autoanchor.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/datasets.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/datasets.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be661918a578499816224a4213f7edc867f5f450 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/datasets.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/general.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/general.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..94c1a5dee60b215e17df6685533940500550bf22 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/general.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/torch_utils.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/torch_utils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..090ca3053527c62c94c0c83bd32b378a61a84521 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/__pycache__/torch_utils.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/autoanchor.py b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/autoanchor.py new file mode 100644 index 0000000000000000000000000000000000000000..a4eba3e94888709be7d2a7c7499fbcc1808b4a88 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/autoanchor.py @@ -0,0 +1,12 @@ +# Auto-anchor utils + + +def check_anchor_order(m): + # Check anchor order against stride order for YOLOv5 Detect() module m, and correct if necessary + a = m.anchor_grid.prod(-1).view(-1) # anchor area + da = a[-1] - a[0] # delta a + ds = m.stride[-1] - m.stride[0] # delta s + if da.sign() != ds.sign(): # same order + print("Reversing anchor order") + m.anchors[:] = m.anchors.flip(0) + m.anchor_grid[:] = m.anchor_grid.flip(0) diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/datasets.py b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/datasets.py new file mode 100644 index 0000000000000000000000000000000000000000..e672b136f56fd6b05038e24377908361a54fe519 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/datasets.py @@ -0,0 +1,35 @@ +import cv2 +import numpy as np + + +def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scale_fill=False, scaleup=True): + # Resize image to a 32-pixel-multiple rectangle https://github.com/ultralytics/yolov3/issues/232 + shape = img.shape[:2] # current shape [height, width] + if isinstance(new_shape, int): + new_shape = (new_shape, new_shape) + + # Scale ratio (new / old) + r = min(new_shape[0] / shape[0], new_shape[1] / shape[1]) + if not scaleup: # only scale down, do not scale up (for better test mAP) + r = min(r, 1.0) + + # Compute padding + ratio = r, r # width, height ratios + new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r)) + dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding + if auto: # minimum rectangle + dw, dh = np.mod(dw, 64), np.mod(dh, 64) # wh padding + elif scale_fill: # stretch + dw, dh = 0.0, 0.0 + new_unpad = (new_shape[1], new_shape[0]) + ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios + + dw /= 2 # divide padding into 2 sides + dh /= 2 + + if shape[::-1] != new_unpad: # resize + img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR) + top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) + left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) + img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border + return img, ratio, (dw, dh) diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/extract_ckpt.py b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/extract_ckpt.py new file mode 100644 index 0000000000000000000000000000000000000000..4b8b631348f2d0cdea4e5a3594bb59f3e8f34a0f --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/extract_ckpt.py @@ -0,0 +1,5 @@ +import torch +import sys +sys.path.insert(0,'./facelib/detection/yolov5face') +model = torch.load('facelib/detection/yolov5face/yolov5n-face.pt', map_location='cpu')['model'] +torch.save(model.state_dict(),'weights/facelib/yolov5n-face.pth') \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/general.py b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/general.py new file mode 100644 index 0000000000000000000000000000000000000000..1c8e14f56a107ec3a4269c382cfc5168ad780ffc --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/general.py @@ -0,0 +1,271 @@ +import math +import time + +import numpy as np +import torch +import torchvision + + +def check_img_size(img_size, s=32): + # Verify img_size is a multiple of stride s + new_size = make_divisible(img_size, int(s)) # ceil gs-multiple + # if new_size != img_size: + # print(f"WARNING: --img-size {img_size:g} must be multiple of max stride {s:g}, updating to {new_size:g}") + return new_size + + +def make_divisible(x, divisor): + # Returns x evenly divisible by divisor + return math.ceil(x / divisor) * divisor + + +def xyxy2xywh(x): + # Convert nx4 boxes from [x1, y1, x2, y2] to [x, y, w, h] where xy1=top-left, xy2=bottom-right + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[:, 0] = (x[:, 0] + x[:, 2]) / 2 # x center + y[:, 1] = (x[:, 1] + x[:, 3]) / 2 # y center + y[:, 2] = x[:, 2] - x[:, 0] # width + y[:, 3] = x[:, 3] - x[:, 1] # height + return y + + +def xywh2xyxy(x): + # Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x + y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y + y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x + y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y + return y + + +def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None): + # Rescale coords (xyxy) from img1_shape to img0_shape + if ratio_pad is None: # calculate from img0_shape + gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new + pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding + else: + gain = ratio_pad[0][0] + pad = ratio_pad[1] + + coords[:, [0, 2]] -= pad[0] # x padding + coords[:, [1, 3]] -= pad[1] # y padding + coords[:, :4] /= gain + clip_coords(coords, img0_shape) + return coords + + +def clip_coords(boxes, img_shape): + # Clip bounding xyxy bounding boxes to image shape (height, width) + boxes[:, 0].clamp_(0, img_shape[1]) # x1 + boxes[:, 1].clamp_(0, img_shape[0]) # y1 + boxes[:, 2].clamp_(0, img_shape[1]) # x2 + boxes[:, 3].clamp_(0, img_shape[0]) # y2 + + +def box_iou(box1, box2): + # https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.py + """ + Return intersection-over-union (Jaccard index) of boxes. + Both sets of boxes are expected to be in (x1, y1, x2, y2) format. + Arguments: + box1 (Tensor[N, 4]) + box2 (Tensor[M, 4]) + Returns: + iou (Tensor[N, M]): the NxM matrix containing the pairwise + IoU values for every element in boxes1 and boxes2 + """ + + def box_area(box): + return (box[2] - box[0]) * (box[3] - box[1]) + + area1 = box_area(box1.T) + area2 = box_area(box2.T) + + inter = (torch.min(box1[:, None, 2:], box2[:, 2:]) - torch.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2) + return inter / (area1[:, None] + area2 - inter) + + +def non_max_suppression_face(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, labels=()): + """Performs Non-Maximum Suppression (NMS) on inference results + Returns: + detections with shape: nx6 (x1, y1, x2, y2, conf, cls) + """ + + nc = prediction.shape[2] - 15 # number of classes + xc = prediction[..., 4] > conf_thres # candidates + + # Settings + # (pixels) maximum box width and height + max_wh = 4096 + time_limit = 10.0 # seconds to quit after + redundant = True # require redundant detections + multi_label = nc > 1 # multiple labels per box (adds 0.5ms/img) + merge = False # use merge-NMS + + t = time.time() + output = [torch.zeros((0, 16), device=prediction.device)] * prediction.shape[0] + for xi, x in enumerate(prediction): # image index, image inference + # Apply constraints + x = x[xc[xi]] # confidence + + # Cat apriori labels if autolabelling + if labels and len(labels[xi]): + label = labels[xi] + v = torch.zeros((len(label), nc + 15), device=x.device) + v[:, :4] = label[:, 1:5] # box + v[:, 4] = 1.0 # conf + v[range(len(label)), label[:, 0].long() + 15] = 1.0 # cls + x = torch.cat((x, v), 0) + + # If none remain process next image + if not x.shape[0]: + continue + + # Compute conf + x[:, 15:] *= x[:, 4:5] # conf = obj_conf * cls_conf + + # Box (center x, center y, width, height) to (x1, y1, x2, y2) + box = xywh2xyxy(x[:, :4]) + + # Detections matrix nx6 (xyxy, conf, landmarks, cls) + if multi_label: + i, j = (x[:, 15:] > conf_thres).nonzero(as_tuple=False).T + x = torch.cat((box[i], x[i, j + 15, None], x[:, 5:15], j[:, None].float()), 1) + else: # best class only + conf, j = x[:, 15:].max(1, keepdim=True) + x = torch.cat((box, conf, x[:, 5:15], j.float()), 1)[conf.view(-1) > conf_thres] + + # Filter by class + if classes is not None: + x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)] + + # If none remain process next image + n = x.shape[0] # number of boxes + if not n: + continue + + # Batched NMS + c = x[:, 15:16] * (0 if agnostic else max_wh) # classes + boxes, scores = x[:, :4] + c, x[:, 4] # boxes (offset by class), scores + i = torchvision.ops.nms(boxes, scores, iou_thres) # NMS + + if merge and (1 < n < 3e3): # Merge NMS (boxes merged using weighted mean) + # update boxes as boxes(i,4) = weights(i,n) * boxes(n,4) + iou = box_iou(boxes[i], boxes) > iou_thres # iou matrix + weights = iou * scores[None] # box weights + x[i, :4] = torch.mm(weights, x[:, :4]).float() / weights.sum(1, keepdim=True) # merged boxes + if redundant: + i = i[iou.sum(1) > 1] # require redundancy + + output[xi] = x[i] + if (time.time() - t) > time_limit: + break # time limit exceeded + + return output + + +def non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, labels=()): + """Performs Non-Maximum Suppression (NMS) on inference results + + Returns: + detections with shape: nx6 (x1, y1, x2, y2, conf, cls) + """ + + nc = prediction.shape[2] - 5 # number of classes + xc = prediction[..., 4] > conf_thres # candidates + + # Settings + # (pixels) maximum box width and height + max_wh = 4096 + time_limit = 10.0 # seconds to quit after + redundant = True # require redundant detections + multi_label = nc > 1 # multiple labels per box (adds 0.5ms/img) + merge = False # use merge-NMS + + t = time.time() + output = [torch.zeros((0, 6), device=prediction.device)] * prediction.shape[0] + for xi, x in enumerate(prediction): # image index, image inference + x = x[xc[xi]] # confidence + + # Cat apriori labels if autolabelling + if labels and len(labels[xi]): + label_id = labels[xi] + v = torch.zeros((len(label_id), nc + 5), device=x.device) + v[:, :4] = label_id[:, 1:5] # box + v[:, 4] = 1.0 # conf + v[range(len(label_id)), label_id[:, 0].long() + 5] = 1.0 # cls + x = torch.cat((x, v), 0) + + # If none remain process next image + if not x.shape[0]: + continue + + # Compute conf + x[:, 5:] *= x[:, 4:5] # conf = obj_conf * cls_conf + + # Box (center x, center y, width, height) to (x1, y1, x2, y2) + box = xywh2xyxy(x[:, :4]) + + # Detections matrix nx6 (xyxy, conf, cls) + if multi_label: + i, j = (x[:, 5:] > conf_thres).nonzero(as_tuple=False).T + x = torch.cat((box[i], x[i, j + 5, None], j[:, None].float()), 1) + else: # best class only + conf, j = x[:, 5:].max(1, keepdim=True) + x = torch.cat((box, conf, j.float()), 1)[conf.view(-1) > conf_thres] + + # Filter by class + if classes is not None: + x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)] + + # Check shape + n = x.shape[0] # number of boxes + if not n: # no boxes + continue + + x = x[x[:, 4].argsort(descending=True)] # sort by confidence + + # Batched NMS + c = x[:, 5:6] * (0 if agnostic else max_wh) # classes + boxes, scores = x[:, :4] + c, x[:, 4] # boxes (offset by class), scores + i = torchvision.ops.nms(boxes, scores, iou_thres) # NMS + if merge and (1 < n < 3e3): # Merge NMS (boxes merged using weighted mean) + # update boxes as boxes(i,4) = weights(i,n) * boxes(n,4) + iou = box_iou(boxes[i], boxes) > iou_thres # iou matrix + weights = iou * scores[None] # box weights + x[i, :4] = torch.mm(weights, x[:, :4]).float() / weights.sum(1, keepdim=True) # merged boxes + if redundant: + i = i[iou.sum(1) > 1] # require redundancy + + output[xi] = x[i] + if (time.time() - t) > time_limit: + print(f"WARNING: NMS time limit {time_limit}s exceeded") + break # time limit exceeded + + return output + + +def scale_coords_landmarks(img1_shape, coords, img0_shape, ratio_pad=None): + # Rescale coords (xyxy) from img1_shape to img0_shape + if ratio_pad is None: # calculate from img0_shape + gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new + pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding + else: + gain = ratio_pad[0][0] + pad = ratio_pad[1] + + coords[:, [0, 2, 4, 6, 8]] -= pad[0] # x padding + coords[:, [1, 3, 5, 7, 9]] -= pad[1] # y padding + coords[:, :10] /= gain + coords[:, 0].clamp_(0, img0_shape[1]) # x1 + coords[:, 1].clamp_(0, img0_shape[0]) # y1 + coords[:, 2].clamp_(0, img0_shape[1]) # x2 + coords[:, 3].clamp_(0, img0_shape[0]) # y2 + coords[:, 4].clamp_(0, img0_shape[1]) # x3 + coords[:, 5].clamp_(0, img0_shape[0]) # y3 + coords[:, 6].clamp_(0, img0_shape[1]) # x4 + coords[:, 7].clamp_(0, img0_shape[0]) # y4 + coords[:, 8].clamp_(0, img0_shape[1]) # x5 + coords[:, 9].clamp_(0, img0_shape[0]) # y5 + return coords diff --git a/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/torch_utils.py b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/torch_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..af2d06587b2d07b2eab199a8484380fde1de5c3c --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/detection/yolov5face/utils/torch_utils.py @@ -0,0 +1,40 @@ +import torch +from torch import nn + + +def fuse_conv_and_bn(conv, bn): + # Fuse convolution and batchnorm layers https://tehnokv.com/posts/fusing-batchnorm-and-conv/ + fusedconv = ( + nn.Conv2d( + conv.in_channels, + conv.out_channels, + kernel_size=conv.kernel_size, + stride=conv.stride, + padding=conv.padding, + groups=conv.groups, + bias=True, + ) + .requires_grad_(False) + .to(conv.weight.device) + ) + + # prepare filters + w_conv = conv.weight.clone().view(conv.out_channels, -1) + w_bn = torch.diag(bn.weight.div(torch.sqrt(bn.eps + bn.running_var))) + fusedconv.weight.copy_(torch.mm(w_bn, w_conv).view(fusedconv.weight.size())) + + # prepare spatial bias + b_conv = torch.zeros(conv.weight.size(0), device=conv.weight.device) if conv.bias is None else conv.bias + b_bn = bn.bias - bn.weight.mul(bn.running_mean).div(torch.sqrt(bn.running_var + bn.eps)) + fusedconv.bias.copy_(torch.mm(w_bn, b_conv.reshape(-1, 1)).reshape(-1) + b_bn) + + return fusedconv + + +def copy_attr(a, b, include=(), exclude=()): + # Copy attributes from b to a, options to only include [...] and to exclude [...] + for k, v in b.__dict__.items(): + if (include and k not in include) or k.startswith("_") or k in exclude: + continue + + setattr(a, k, v) diff --git a/sd/stablediffusion/src/codeformer/facelib/parsing/__init__.py b/sd/stablediffusion/src/codeformer/facelib/parsing/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..72656e4b5f61df8cd0838588b0c6488fcc886e16 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/parsing/__init__.py @@ -0,0 +1,23 @@ +import torch + +from facelib.utils import load_file_from_url +from .bisenet import BiSeNet +from .parsenet import ParseNet + + +def init_parsing_model(model_name='bisenet', half=False, device='cuda'): + if model_name == 'bisenet': + model = BiSeNet(num_class=19) + model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/parsing_bisenet.pth' + elif model_name == 'parsenet': + model = ParseNet(in_size=512, out_size=512, parsing_ch=19) + model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/parsing_parsenet.pth' + else: + raise NotImplementedError(f'{model_name} is not implemented.') + + model_path = load_file_from_url(url=model_url, model_dir='weights/facelib', progress=True, file_name=None) + load_net = torch.load(model_path, map_location=lambda storage, loc: storage) + model.load_state_dict(load_net, strict=True) + model.eval() + model = model.to(device) + return model diff --git a/sd/stablediffusion/src/codeformer/facelib/parsing/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/parsing/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a4218ee5b1f68ac84b9a4895c9b8529cdd0f16c6 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/parsing/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/parsing/__pycache__/bisenet.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/parsing/__pycache__/bisenet.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b99e8371e0e9755c291d9e88af6b2b2548848211 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/parsing/__pycache__/bisenet.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/parsing/__pycache__/parsenet.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/parsing/__pycache__/parsenet.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..676f8790b67a29c8819ad907f186156a4eabd784 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/parsing/__pycache__/parsenet.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/parsing/__pycache__/resnet.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/parsing/__pycache__/resnet.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca4f39f0bd6d418806654a9ba580e6a336febba4 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/parsing/__pycache__/resnet.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/parsing/bisenet.py b/sd/stablediffusion/src/codeformer/facelib/parsing/bisenet.py new file mode 100644 index 0000000000000000000000000000000000000000..3898cab76ae5876459cd4899c54cafa14234971d --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/parsing/bisenet.py @@ -0,0 +1,140 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +from .resnet import ResNet18 + + +class ConvBNReLU(nn.Module): + + def __init__(self, in_chan, out_chan, ks=3, stride=1, padding=1): + super(ConvBNReLU, self).__init__() + self.conv = nn.Conv2d(in_chan, out_chan, kernel_size=ks, stride=stride, padding=padding, bias=False) + self.bn = nn.BatchNorm2d(out_chan) + + def forward(self, x): + x = self.conv(x) + x = F.relu(self.bn(x)) + return x + + +class BiSeNetOutput(nn.Module): + + def __init__(self, in_chan, mid_chan, num_class): + super(BiSeNetOutput, self).__init__() + self.conv = ConvBNReLU(in_chan, mid_chan, ks=3, stride=1, padding=1) + self.conv_out = nn.Conv2d(mid_chan, num_class, kernel_size=1, bias=False) + + def forward(self, x): + feat = self.conv(x) + out = self.conv_out(feat) + return out, feat + + +class AttentionRefinementModule(nn.Module): + + def __init__(self, in_chan, out_chan): + super(AttentionRefinementModule, self).__init__() + self.conv = ConvBNReLU(in_chan, out_chan, ks=3, stride=1, padding=1) + self.conv_atten = nn.Conv2d(out_chan, out_chan, kernel_size=1, bias=False) + self.bn_atten = nn.BatchNorm2d(out_chan) + self.sigmoid_atten = nn.Sigmoid() + + def forward(self, x): + feat = self.conv(x) + atten = F.avg_pool2d(feat, feat.size()[2:]) + atten = self.conv_atten(atten) + atten = self.bn_atten(atten) + atten = self.sigmoid_atten(atten) + out = torch.mul(feat, atten) + return out + + +class ContextPath(nn.Module): + + def __init__(self): + super(ContextPath, self).__init__() + self.resnet = ResNet18() + self.arm16 = AttentionRefinementModule(256, 128) + self.arm32 = AttentionRefinementModule(512, 128) + self.conv_head32 = ConvBNReLU(128, 128, ks=3, stride=1, padding=1) + self.conv_head16 = ConvBNReLU(128, 128, ks=3, stride=1, padding=1) + self.conv_avg = ConvBNReLU(512, 128, ks=1, stride=1, padding=0) + + def forward(self, x): + feat8, feat16, feat32 = self.resnet(x) + h8, w8 = feat8.size()[2:] + h16, w16 = feat16.size()[2:] + h32, w32 = feat32.size()[2:] + + avg = F.avg_pool2d(feat32, feat32.size()[2:]) + avg = self.conv_avg(avg) + avg_up = F.interpolate(avg, (h32, w32), mode='nearest') + + feat32_arm = self.arm32(feat32) + feat32_sum = feat32_arm + avg_up + feat32_up = F.interpolate(feat32_sum, (h16, w16), mode='nearest') + feat32_up = self.conv_head32(feat32_up) + + feat16_arm = self.arm16(feat16) + feat16_sum = feat16_arm + feat32_up + feat16_up = F.interpolate(feat16_sum, (h8, w8), mode='nearest') + feat16_up = self.conv_head16(feat16_up) + + return feat8, feat16_up, feat32_up # x8, x8, x16 + + +class FeatureFusionModule(nn.Module): + + def __init__(self, in_chan, out_chan): + super(FeatureFusionModule, self).__init__() + self.convblk = ConvBNReLU(in_chan, out_chan, ks=1, stride=1, padding=0) + self.conv1 = nn.Conv2d(out_chan, out_chan // 4, kernel_size=1, stride=1, padding=0, bias=False) + self.conv2 = nn.Conv2d(out_chan // 4, out_chan, kernel_size=1, stride=1, padding=0, bias=False) + self.relu = nn.ReLU(inplace=True) + self.sigmoid = nn.Sigmoid() + + def forward(self, fsp, fcp): + fcat = torch.cat([fsp, fcp], dim=1) + feat = self.convblk(fcat) + atten = F.avg_pool2d(feat, feat.size()[2:]) + atten = self.conv1(atten) + atten = self.relu(atten) + atten = self.conv2(atten) + atten = self.sigmoid(atten) + feat_atten = torch.mul(feat, atten) + feat_out = feat_atten + feat + return feat_out + + +class BiSeNet(nn.Module): + + def __init__(self, num_class): + super(BiSeNet, self).__init__() + self.cp = ContextPath() + self.ffm = FeatureFusionModule(256, 256) + self.conv_out = BiSeNetOutput(256, 256, num_class) + self.conv_out16 = BiSeNetOutput(128, 64, num_class) + self.conv_out32 = BiSeNetOutput(128, 64, num_class) + + def forward(self, x, return_feat=False): + h, w = x.size()[2:] + feat_res8, feat_cp8, feat_cp16 = self.cp(x) # return res3b1 feature + feat_sp = feat_res8 # replace spatial path feature with res3b1 feature + feat_fuse = self.ffm(feat_sp, feat_cp8) + + out, feat = self.conv_out(feat_fuse) + out16, feat16 = self.conv_out16(feat_cp8) + out32, feat32 = self.conv_out32(feat_cp16) + + out = F.interpolate(out, (h, w), mode='bilinear', align_corners=True) + out16 = F.interpolate(out16, (h, w), mode='bilinear', align_corners=True) + out32 = F.interpolate(out32, (h, w), mode='bilinear', align_corners=True) + + if return_feat: + feat = F.interpolate(feat, (h, w), mode='bilinear', align_corners=True) + feat16 = F.interpolate(feat16, (h, w), mode='bilinear', align_corners=True) + feat32 = F.interpolate(feat32, (h, w), mode='bilinear', align_corners=True) + return out, out16, out32, feat, feat16, feat32 + else: + return out, out16, out32 diff --git a/sd/stablediffusion/src/codeformer/facelib/parsing/parsenet.py b/sd/stablediffusion/src/codeformer/facelib/parsing/parsenet.py new file mode 100644 index 0000000000000000000000000000000000000000..e178ebe43a1ef666aaea0bc0faf629485c22a24f --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/parsing/parsenet.py @@ -0,0 +1,194 @@ +"""Modified from https://github.com/chaofengc/PSFRGAN +""" +import numpy as np +import torch.nn as nn +from torch.nn import functional as F + + +class NormLayer(nn.Module): + """Normalization Layers. + + Args: + channels: input channels, for batch norm and instance norm. + input_size: input shape without batch size, for layer norm. + """ + + def __init__(self, channels, normalize_shape=None, norm_type='bn'): + super(NormLayer, self).__init__() + norm_type = norm_type.lower() + self.norm_type = norm_type + if norm_type == 'bn': + self.norm = nn.BatchNorm2d(channels, affine=True) + elif norm_type == 'in': + self.norm = nn.InstanceNorm2d(channels, affine=False) + elif norm_type == 'gn': + self.norm = nn.GroupNorm(32, channels, affine=True) + elif norm_type == 'pixel': + self.norm = lambda x: F.normalize(x, p=2, dim=1) + elif norm_type == 'layer': + self.norm = nn.LayerNorm(normalize_shape) + elif norm_type == 'none': + self.norm = lambda x: x * 1.0 + else: + assert 1 == 0, f'Norm type {norm_type} not support.' + + def forward(self, x, ref=None): + if self.norm_type == 'spade': + return self.norm(x, ref) + else: + return self.norm(x) + + +class ReluLayer(nn.Module): + """Relu Layer. + + Args: + relu type: type of relu layer, candidates are + - ReLU + - LeakyReLU: default relu slope 0.2 + - PRelu + - SELU + - none: direct pass + """ + + def __init__(self, channels, relu_type='relu'): + super(ReluLayer, self).__init__() + relu_type = relu_type.lower() + if relu_type == 'relu': + self.func = nn.ReLU(True) + elif relu_type == 'leakyrelu': + self.func = nn.LeakyReLU(0.2, inplace=True) + elif relu_type == 'prelu': + self.func = nn.PReLU(channels) + elif relu_type == 'selu': + self.func = nn.SELU(True) + elif relu_type == 'none': + self.func = lambda x: x * 1.0 + else: + assert 1 == 0, f'Relu type {relu_type} not support.' + + def forward(self, x): + return self.func(x) + + +class ConvLayer(nn.Module): + + def __init__(self, + in_channels, + out_channels, + kernel_size=3, + scale='none', + norm_type='none', + relu_type='none', + use_pad=True, + bias=True): + super(ConvLayer, self).__init__() + self.use_pad = use_pad + self.norm_type = norm_type + if norm_type in ['bn']: + bias = False + + stride = 2 if scale == 'down' else 1 + + self.scale_func = lambda x: x + if scale == 'up': + self.scale_func = lambda x: nn.functional.interpolate(x, scale_factor=2, mode='nearest') + + self.reflection_pad = nn.ReflectionPad2d(int(np.ceil((kernel_size - 1.) / 2))) + self.conv2d = nn.Conv2d(in_channels, out_channels, kernel_size, stride, bias=bias) + + self.relu = ReluLayer(out_channels, relu_type) + self.norm = NormLayer(out_channels, norm_type=norm_type) + + def forward(self, x): + out = self.scale_func(x) + if self.use_pad: + out = self.reflection_pad(out) + out = self.conv2d(out) + out = self.norm(out) + out = self.relu(out) + return out + + +class ResidualBlock(nn.Module): + """ + Residual block recommended in: http://torch.ch/blog/2016/02/04/resnets.html + """ + + def __init__(self, c_in, c_out, relu_type='prelu', norm_type='bn', scale='none'): + super(ResidualBlock, self).__init__() + + if scale == 'none' and c_in == c_out: + self.shortcut_func = lambda x: x + else: + self.shortcut_func = ConvLayer(c_in, c_out, 3, scale) + + scale_config_dict = {'down': ['none', 'down'], 'up': ['up', 'none'], 'none': ['none', 'none']} + scale_conf = scale_config_dict[scale] + + self.conv1 = ConvLayer(c_in, c_out, 3, scale_conf[0], norm_type=norm_type, relu_type=relu_type) + self.conv2 = ConvLayer(c_out, c_out, 3, scale_conf[1], norm_type=norm_type, relu_type='none') + + def forward(self, x): + identity = self.shortcut_func(x) + + res = self.conv1(x) + res = self.conv2(res) + return identity + res + + +class ParseNet(nn.Module): + + def __init__(self, + in_size=128, + out_size=128, + min_feat_size=32, + base_ch=64, + parsing_ch=19, + res_depth=10, + relu_type='LeakyReLU', + norm_type='bn', + ch_range=[32, 256]): + super().__init__() + self.res_depth = res_depth + act_args = {'norm_type': norm_type, 'relu_type': relu_type} + min_ch, max_ch = ch_range + + ch_clip = lambda x: max(min_ch, min(x, max_ch)) # noqa: E731 + min_feat_size = min(in_size, min_feat_size) + + down_steps = int(np.log2(in_size // min_feat_size)) + up_steps = int(np.log2(out_size // min_feat_size)) + + # =============== define encoder-body-decoder ==================== + self.encoder = [] + self.encoder.append(ConvLayer(3, base_ch, 3, 1)) + head_ch = base_ch + for i in range(down_steps): + cin, cout = ch_clip(head_ch), ch_clip(head_ch * 2) + self.encoder.append(ResidualBlock(cin, cout, scale='down', **act_args)) + head_ch = head_ch * 2 + + self.body = [] + for i in range(res_depth): + self.body.append(ResidualBlock(ch_clip(head_ch), ch_clip(head_ch), **act_args)) + + self.decoder = [] + for i in range(up_steps): + cin, cout = ch_clip(head_ch), ch_clip(head_ch // 2) + self.decoder.append(ResidualBlock(cin, cout, scale='up', **act_args)) + head_ch = head_ch // 2 + + self.encoder = nn.Sequential(*self.encoder) + self.body = nn.Sequential(*self.body) + self.decoder = nn.Sequential(*self.decoder) + self.out_img_conv = ConvLayer(ch_clip(head_ch), 3) + self.out_mask_conv = ConvLayer(ch_clip(head_ch), parsing_ch) + + def forward(self, x): + feat = self.encoder(x) + x = feat + self.body(feat) + x = self.decoder(x) + out_img = self.out_img_conv(x) + out_mask = self.out_mask_conv(x) + return out_mask, out_img diff --git a/sd/stablediffusion/src/codeformer/facelib/parsing/resnet.py b/sd/stablediffusion/src/codeformer/facelib/parsing/resnet.py new file mode 100644 index 0000000000000000000000000000000000000000..fec8e82cf64469fb51be21ad5130217052addbda --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/parsing/resnet.py @@ -0,0 +1,69 @@ +import torch.nn as nn +import torch.nn.functional as F + + +def conv3x3(in_planes, out_planes, stride=1): + """3x3 convolution with padding""" + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False) + + +class BasicBlock(nn.Module): + + def __init__(self, in_chan, out_chan, stride=1): + super(BasicBlock, self).__init__() + self.conv1 = conv3x3(in_chan, out_chan, stride) + self.bn1 = nn.BatchNorm2d(out_chan) + self.conv2 = conv3x3(out_chan, out_chan) + self.bn2 = nn.BatchNorm2d(out_chan) + self.relu = nn.ReLU(inplace=True) + self.downsample = None + if in_chan != out_chan or stride != 1: + self.downsample = nn.Sequential( + nn.Conv2d(in_chan, out_chan, kernel_size=1, stride=stride, bias=False), + nn.BatchNorm2d(out_chan), + ) + + def forward(self, x): + residual = self.conv1(x) + residual = F.relu(self.bn1(residual)) + residual = self.conv2(residual) + residual = self.bn2(residual) + + shortcut = x + if self.downsample is not None: + shortcut = self.downsample(x) + + out = shortcut + residual + out = self.relu(out) + return out + + +def create_layer_basic(in_chan, out_chan, bnum, stride=1): + layers = [BasicBlock(in_chan, out_chan, stride=stride)] + for i in range(bnum - 1): + layers.append(BasicBlock(out_chan, out_chan, stride=1)) + return nn.Sequential(*layers) + + +class ResNet18(nn.Module): + + def __init__(self): + super(ResNet18, self).__init__() + self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False) + self.bn1 = nn.BatchNorm2d(64) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + self.layer1 = create_layer_basic(64, 64, bnum=2, stride=1) + self.layer2 = create_layer_basic(64, 128, bnum=2, stride=2) + self.layer3 = create_layer_basic(128, 256, bnum=2, stride=2) + self.layer4 = create_layer_basic(256, 512, bnum=2, stride=2) + + def forward(self, x): + x = self.conv1(x) + x = F.relu(self.bn1(x)) + x = self.maxpool(x) + + x = self.layer1(x) + feat8 = self.layer2(x) # 1/8 + feat16 = self.layer3(feat8) # 1/16 + feat32 = self.layer4(feat16) # 1/32 + return feat8, feat16, feat32 diff --git a/sd/stablediffusion/src/codeformer/facelib/utils/__init__.py b/sd/stablediffusion/src/codeformer/facelib/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f03b1c2bafcd7759cb7e8722a0c6715f201a46dc --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/utils/__init__.py @@ -0,0 +1,7 @@ +from .face_utils import align_crop_face_landmarks, compute_increased_bbox, get_valid_bboxes, paste_face_back +from .misc import img2tensor, load_file_from_url, download_pretrained_models, scandir + +__all__ = [ + 'align_crop_face_landmarks', 'compute_increased_bbox', 'get_valid_bboxes', 'load_file_from_url', + 'download_pretrained_models', 'paste_face_back', 'img2tensor', 'scandir' +] diff --git a/sd/stablediffusion/src/codeformer/facelib/utils/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/utils/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b84b0d21327cd8bb7caf0d1d35d4ac5bd08963f1 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/utils/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/utils/__pycache__/face_restoration_helper.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/utils/__pycache__/face_restoration_helper.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5c4ce788c7397af4cb678a0aea575393e766f84 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/utils/__pycache__/face_restoration_helper.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/utils/__pycache__/face_utils.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/utils/__pycache__/face_utils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c07e771326cce6bfae7b5e7a8b6cf207f2193249 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/utils/__pycache__/face_utils.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/utils/__pycache__/misc.cpython-39.pyc b/sd/stablediffusion/src/codeformer/facelib/utils/__pycache__/misc.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc379057be1b572caf0b9746009c9296d2121e99 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/facelib/utils/__pycache__/misc.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/codeformer/facelib/utils/face_restoration_helper.py b/sd/stablediffusion/src/codeformer/facelib/utils/face_restoration_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..5d3fb8f3b95ed9959610e64f6d7373ea8a56ece8 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/utils/face_restoration_helper.py @@ -0,0 +1,460 @@ +import cv2 +import numpy as np +import os +import torch +from torchvision.transforms.functional import normalize + +from facelib.detection import init_detection_model +from facelib.parsing import init_parsing_model +from facelib.utils.misc import img2tensor, imwrite, is_gray, bgr2gray + + +def get_largest_face(det_faces, h, w): + + def get_location(val, length): + if val < 0: + return 0 + elif val > length: + return length + else: + return val + + face_areas = [] + for det_face in det_faces: + left = get_location(det_face[0], w) + right = get_location(det_face[2], w) + top = get_location(det_face[1], h) + bottom = get_location(det_face[3], h) + face_area = (right - left) * (bottom - top) + face_areas.append(face_area) + largest_idx = face_areas.index(max(face_areas)) + return det_faces[largest_idx], largest_idx + + +def get_center_face(det_faces, h=0, w=0, center=None): + if center is not None: + center = np.array(center) + else: + center = np.array([w / 2, h / 2]) + center_dist = [] + for det_face in det_faces: + face_center = np.array([(det_face[0] + det_face[2]) / 2, (det_face[1] + det_face[3]) / 2]) + dist = np.linalg.norm(face_center - center) + center_dist.append(dist) + center_idx = center_dist.index(min(center_dist)) + return det_faces[center_idx], center_idx + + +class FaceRestoreHelper(object): + """Helper for the face restoration pipeline (base class).""" + + def __init__(self, + upscale_factor, + face_size=512, + crop_ratio=(1, 1), + det_model='retinaface_resnet50', + save_ext='png', + template_3points=False, + pad_blur=False, + use_parse=False, + device=None): + self.template_3points = template_3points # improve robustness + self.upscale_factor = int(upscale_factor) + # the cropped face ratio based on the square face + self.crop_ratio = crop_ratio # (h, w) + assert (self.crop_ratio[0] >= 1 and self.crop_ratio[1] >= 1), 'crop ration only supports >=1' + self.face_size = (int(face_size * self.crop_ratio[1]), int(face_size * self.crop_ratio[0])) + + if self.template_3points: + self.face_template = np.array([[192, 240], [319, 240], [257, 371]]) + else: + # standard 5 landmarks for FFHQ faces with 512 x 512 + # facexlib + self.face_template = np.array([[192.98138, 239.94708], [318.90277, 240.1936], [256.63416, 314.01935], + [201.26117, 371.41043], [313.08905, 371.15118]]) + + # dlib: left_eye: 36:41 right_eye: 42:47 nose: 30,32,33,34 left mouth corner: 48 right mouth corner: 54 + # self.face_template = np.array([[193.65928, 242.98541], [318.32558, 243.06108], [255.67984, 328.82894], + # [198.22603, 372.82502], [313.91018, 372.75659]]) + + + self.face_template = self.face_template * (face_size / 512.0) + if self.crop_ratio[0] > 1: + self.face_template[:, 1] += face_size * (self.crop_ratio[0] - 1) / 2 + if self.crop_ratio[1] > 1: + self.face_template[:, 0] += face_size * (self.crop_ratio[1] - 1) / 2 + self.save_ext = save_ext + self.pad_blur = pad_blur + if self.pad_blur is True: + self.template_3points = False + + self.all_landmarks_5 = [] + self.det_faces = [] + self.affine_matrices = [] + self.inverse_affine_matrices = [] + self.cropped_faces = [] + self.restored_faces = [] + self.pad_input_imgs = [] + + if device is None: + self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + else: + self.device = device + + # init face detection model + self.face_det = init_detection_model(det_model, half=False, device=self.device) + + # init face parsing model + self.use_parse = use_parse + self.face_parse = init_parsing_model(model_name='parsenet', device=self.device) + + def set_upscale_factor(self, upscale_factor): + self.upscale_factor = upscale_factor + + def read_image(self, img): + """img can be image path or cv2 loaded image.""" + # self.input_img is Numpy array, (h, w, c), BGR, uint8, [0, 255] + if isinstance(img, str): + img = cv2.imread(img) + + if np.max(img) > 256: # 16-bit image + img = img / 65535 * 255 + if len(img.shape) == 2: # gray image + img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) + elif img.shape[2] == 4: # BGRA image with alpha channel + img = img[:, :, 0:3] + + self.input_img = img + self.is_gray = is_gray(img, threshold=5) + if self.is_gray: + print('Grayscale input: True') + + if min(self.input_img.shape[:2])<512: + f = 512.0/min(self.input_img.shape[:2]) + self.input_img = cv2.resize(self.input_img, (0,0), fx=f, fy=f, interpolation=cv2.INTER_LINEAR) + + def get_face_landmarks_5(self, + only_keep_largest=False, + only_center_face=False, + resize=None, + blur_ratio=0.01, + eye_dist_threshold=None): + if resize is None: + scale = 1 + input_img = self.input_img + else: + h, w = self.input_img.shape[0:2] + scale = resize / min(h, w) + scale = max(1, scale) # always scale up + h, w = int(h * scale), int(w * scale) + interp = cv2.INTER_AREA if scale < 1 else cv2.INTER_LINEAR + input_img = cv2.resize(self.input_img, (w, h), interpolation=interp) + + with torch.no_grad(): + bboxes = self.face_det.detect_faces(input_img) + + if bboxes is None or bboxes.shape[0] == 0: + return 0 + else: + bboxes = bboxes / scale + + for bbox in bboxes: + # remove faces with too small eye distance: side faces or too small faces + eye_dist = np.linalg.norm([bbox[6] - bbox[8], bbox[7] - bbox[9]]) + if eye_dist_threshold is not None and (eye_dist < eye_dist_threshold): + continue + + if self.template_3points: + landmark = np.array([[bbox[i], bbox[i + 1]] for i in range(5, 11, 2)]) + else: + landmark = np.array([[bbox[i], bbox[i + 1]] for i in range(5, 15, 2)]) + self.all_landmarks_5.append(landmark) + self.det_faces.append(bbox[0:5]) + + if len(self.det_faces) == 0: + return 0 + if only_keep_largest: + h, w, _ = self.input_img.shape + self.det_faces, largest_idx = get_largest_face(self.det_faces, h, w) + self.all_landmarks_5 = [self.all_landmarks_5[largest_idx]] + elif only_center_face: + h, w, _ = self.input_img.shape + self.det_faces, center_idx = get_center_face(self.det_faces, h, w) + self.all_landmarks_5 = [self.all_landmarks_5[center_idx]] + + # pad blurry images + if self.pad_blur: + self.pad_input_imgs = [] + for landmarks in self.all_landmarks_5: + # get landmarks + eye_left = landmarks[0, :] + eye_right = landmarks[1, :] + eye_avg = (eye_left + eye_right) * 0.5 + mouth_avg = (landmarks[3, :] + landmarks[4, :]) * 0.5 + eye_to_eye = eye_right - eye_left + eye_to_mouth = mouth_avg - eye_avg + + # Get the oriented crop rectangle + # x: half width of the oriented crop rectangle + x = eye_to_eye - np.flipud(eye_to_mouth) * [-1, 1] + # - np.flipud(eye_to_mouth) * [-1, 1]: rotate 90 clockwise + # norm with the hypotenuse: get the direction + x /= np.hypot(*x) # get the hypotenuse of a right triangle + rect_scale = 1.5 + x *= max(np.hypot(*eye_to_eye) * 2.0 * rect_scale, np.hypot(*eye_to_mouth) * 1.8 * rect_scale) + # y: half height of the oriented crop rectangle + y = np.flipud(x) * [-1, 1] + + # c: center + c = eye_avg + eye_to_mouth * 0.1 + # quad: (left_top, left_bottom, right_bottom, right_top) + quad = np.stack([c - x - y, c - x + y, c + x + y, c + x - y]) + # qsize: side length of the square + qsize = np.hypot(*x) * 2 + border = max(int(np.rint(qsize * 0.1)), 3) + + # get pad + # pad: (width_left, height_top, width_right, height_bottom) + pad = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), int(np.ceil(max(quad[:, 0]))), + int(np.ceil(max(quad[:, 1])))) + pad = [ + max(-pad[0] + border, 1), + max(-pad[1] + border, 1), + max(pad[2] - self.input_img.shape[0] + border, 1), + max(pad[3] - self.input_img.shape[1] + border, 1) + ] + + if max(pad) > 1: + # pad image + pad_img = np.pad(self.input_img, ((pad[1], pad[3]), (pad[0], pad[2]), (0, 0)), 'reflect') + # modify landmark coords + landmarks[:, 0] += pad[0] + landmarks[:, 1] += pad[1] + # blur pad images + h, w, _ = pad_img.shape + y, x, _ = np.ogrid[:h, :w, :1] + mask = np.maximum(1.0 - np.minimum(np.float32(x) / pad[0], + np.float32(w - 1 - x) / pad[2]), + 1.0 - np.minimum(np.float32(y) / pad[1], + np.float32(h - 1 - y) / pad[3])) + blur = int(qsize * blur_ratio) + if blur % 2 == 0: + blur += 1 + blur_img = cv2.boxFilter(pad_img, 0, ksize=(blur, blur)) + # blur_img = cv2.GaussianBlur(pad_img, (blur, blur), 0) + + pad_img = pad_img.astype('float32') + pad_img += (blur_img - pad_img) * np.clip(mask * 3.0 + 1.0, 0.0, 1.0) + pad_img += (np.median(pad_img, axis=(0, 1)) - pad_img) * np.clip(mask, 0.0, 1.0) + pad_img = np.clip(pad_img, 0, 255) # float32, [0, 255] + self.pad_input_imgs.append(pad_img) + else: + self.pad_input_imgs.append(np.copy(self.input_img)) + + return len(self.all_landmarks_5) + + def align_warp_face(self, save_cropped_path=None, border_mode='constant'): + """Align and warp faces with face template. + """ + if self.pad_blur: + assert len(self.pad_input_imgs) == len( + self.all_landmarks_5), f'Mismatched samples: {len(self.pad_input_imgs)} and {len(self.all_landmarks_5)}' + for idx, landmark in enumerate(self.all_landmarks_5): + # use 5 landmarks to get affine matrix + # use cv2.LMEDS method for the equivalence to skimage transform + # ref: https://blog.csdn.net/yichxi/article/details/115827338 + affine_matrix = cv2.estimateAffinePartial2D(landmark, self.face_template, method=cv2.LMEDS)[0] + self.affine_matrices.append(affine_matrix) + # warp and crop faces + if border_mode == 'constant': + border_mode = cv2.BORDER_CONSTANT + elif border_mode == 'reflect101': + border_mode = cv2.BORDER_REFLECT101 + elif border_mode == 'reflect': + border_mode = cv2.BORDER_REFLECT + if self.pad_blur: + input_img = self.pad_input_imgs[idx] + else: + input_img = self.input_img + cropped_face = cv2.warpAffine( + input_img, affine_matrix, self.face_size, borderMode=border_mode, borderValue=(135, 133, 132)) # gray + self.cropped_faces.append(cropped_face) + # save the cropped face + if save_cropped_path is not None: + path = os.path.splitext(save_cropped_path)[0] + save_path = f'{path}_{idx:02d}.{self.save_ext}' + imwrite(cropped_face, save_path) + + def get_inverse_affine(self, save_inverse_affine_path=None): + """Get inverse affine matrix.""" + for idx, affine_matrix in enumerate(self.affine_matrices): + inverse_affine = cv2.invertAffineTransform(affine_matrix) + inverse_affine *= self.upscale_factor + self.inverse_affine_matrices.append(inverse_affine) + # save inverse affine matrices + if save_inverse_affine_path is not None: + path, _ = os.path.splitext(save_inverse_affine_path) + save_path = f'{path}_{idx:02d}.pth' + torch.save(inverse_affine, save_path) + + + def add_restored_face(self, face): + if self.is_gray: + face = bgr2gray(face) # convert img into grayscale + self.restored_faces.append(face) + + + def paste_faces_to_input_image(self, save_path=None, upsample_img=None, draw_box=False, face_upsampler=None): + h, w, _ = self.input_img.shape + h_up, w_up = int(h * self.upscale_factor), int(w * self.upscale_factor) + + if upsample_img is None: + # simply resize the background + # upsample_img = cv2.resize(self.input_img, (w_up, h_up), interpolation=cv2.INTER_LANCZOS4) + upsample_img = cv2.resize(self.input_img, (w_up, h_up), interpolation=cv2.INTER_LINEAR) + else: + upsample_img = cv2.resize(upsample_img, (w_up, h_up), interpolation=cv2.INTER_LANCZOS4) + + assert len(self.restored_faces) == len( + self.inverse_affine_matrices), ('length of restored_faces and affine_matrices are different.') + + inv_mask_borders = [] + for restored_face, inverse_affine in zip(self.restored_faces, self.inverse_affine_matrices): + if face_upsampler is not None: + restored_face = face_upsampler.enhance(restored_face, outscale=self.upscale_factor)[0] + inverse_affine /= self.upscale_factor + inverse_affine[:, 2] *= self.upscale_factor + face_size = (self.face_size[0]*self.upscale_factor, self.face_size[1]*self.upscale_factor) + else: + # Add an offset to inverse affine matrix, for more precise back alignment + if self.upscale_factor > 1: + extra_offset = 0.5 * self.upscale_factor + else: + extra_offset = 0 + inverse_affine[:, 2] += extra_offset + face_size = self.face_size + inv_restored = cv2.warpAffine(restored_face, inverse_affine, (w_up, h_up)) + + # if draw_box or not self.use_parse: # use square parse maps + # mask = np.ones(face_size, dtype=np.float32) + # inv_mask = cv2.warpAffine(mask, inverse_affine, (w_up, h_up)) + # # remove the black borders + # inv_mask_erosion = cv2.erode( + # inv_mask, np.ones((int(2 * self.upscale_factor), int(2 * self.upscale_factor)), np.uint8)) + # pasted_face = inv_mask_erosion[:, :, None] * inv_restored + # total_face_area = np.sum(inv_mask_erosion) # // 3 + # # add border + # if draw_box: + # h, w = face_size + # mask_border = np.ones((h, w, 3), dtype=np.float32) + # border = int(1400/np.sqrt(total_face_area)) + # mask_border[border:h-border, border:w-border,:] = 0 + # inv_mask_border = cv2.warpAffine(mask_border, inverse_affine, (w_up, h_up)) + # inv_mask_borders.append(inv_mask_border) + # if not self.use_parse: + # # compute the fusion edge based on the area of face + # w_edge = int(total_face_area**0.5) // 20 + # erosion_radius = w_edge * 2 + # inv_mask_center = cv2.erode(inv_mask_erosion, np.ones((erosion_radius, erosion_radius), np.uint8)) + # blur_size = w_edge * 2 + # inv_soft_mask = cv2.GaussianBlur(inv_mask_center, (blur_size + 1, blur_size + 1), 0) + # if len(upsample_img.shape) == 2: # upsample_img is gray image + # upsample_img = upsample_img[:, :, None] + # inv_soft_mask = inv_soft_mask[:, :, None] + + # always use square mask + mask = np.ones(face_size, dtype=np.float32) + inv_mask = cv2.warpAffine(mask, inverse_affine, (w_up, h_up)) + # remove the black borders + inv_mask_erosion = cv2.erode( + inv_mask, np.ones((int(2 * self.upscale_factor), int(2 * self.upscale_factor)), np.uint8)) + pasted_face = inv_mask_erosion[:, :, None] * inv_restored + total_face_area = np.sum(inv_mask_erosion) # // 3 + # add border + if draw_box: + h, w = face_size + mask_border = np.ones((h, w, 3), dtype=np.float32) + border = int(1400/np.sqrt(total_face_area)) + mask_border[border:h-border, border:w-border,:] = 0 + inv_mask_border = cv2.warpAffine(mask_border, inverse_affine, (w_up, h_up)) + inv_mask_borders.append(inv_mask_border) + # compute the fusion edge based on the area of face + w_edge = int(total_face_area**0.5) // 20 + erosion_radius = w_edge * 2 + inv_mask_center = cv2.erode(inv_mask_erosion, np.ones((erosion_radius, erosion_radius), np.uint8)) + blur_size = w_edge * 2 + inv_soft_mask = cv2.GaussianBlur(inv_mask_center, (blur_size + 1, blur_size + 1), 0) + if len(upsample_img.shape) == 2: # upsample_img is gray image + upsample_img = upsample_img[:, :, None] + inv_soft_mask = inv_soft_mask[:, :, None] + + # parse mask + if self.use_parse: + # inference + face_input = cv2.resize(restored_face, (512, 512), interpolation=cv2.INTER_LINEAR) + face_input = img2tensor(face_input.astype('float32') / 255., bgr2rgb=True, float32=True) + normalize(face_input, (0.5, 0.5, 0.5), (0.5, 0.5, 0.5), inplace=True) + face_input = torch.unsqueeze(face_input, 0).to(self.device) + with torch.no_grad(): + out = self.face_parse(face_input)[0] + out = out.argmax(dim=1).squeeze().cpu().numpy() + + parse_mask = np.zeros(out.shape) + MASK_COLORMAP = [0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 0, 0, 0] + for idx, color in enumerate(MASK_COLORMAP): + parse_mask[out == idx] = color + # blur the mask + parse_mask = cv2.GaussianBlur(parse_mask, (101, 101), 11) + parse_mask = cv2.GaussianBlur(parse_mask, (101, 101), 11) + # remove the black borders + thres = 10 + parse_mask[:thres, :] = 0 + parse_mask[-thres:, :] = 0 + parse_mask[:, :thres] = 0 + parse_mask[:, -thres:] = 0 + parse_mask = parse_mask / 255. + + parse_mask = cv2.resize(parse_mask, face_size) + parse_mask = cv2.warpAffine(parse_mask, inverse_affine, (w_up, h_up), flags=3) + inv_soft_parse_mask = parse_mask[:, :, None] + # pasted_face = inv_restored + fuse_mask = (inv_soft_parse_mask 256: # 16-bit image + upsample_img = upsample_img.astype(np.uint16) + else: + upsample_img = upsample_img.astype(np.uint8) + + # draw bounding box + if draw_box: + # upsample_input_img = cv2.resize(input_img, (w_up, h_up)) + img_color = np.ones([*upsample_img.shape], dtype=np.float32) + img_color[:,:,0] = 0 + img_color[:,:,1] = 255 + img_color[:,:,2] = 0 + for inv_mask_border in inv_mask_borders: + upsample_img = inv_mask_border * img_color + (1 - inv_mask_border) * upsample_img + # upsample_input_img = inv_mask_border * img_color + (1 - inv_mask_border) * upsample_input_img + + if save_path is not None: + path = os.path.splitext(save_path)[0] + save_path = f'{path}.{self.save_ext}' + imwrite(upsample_img, save_path) + return upsample_img + + def clean_all(self): + self.all_landmarks_5 = [] + self.restored_faces = [] + self.affine_matrices = [] + self.cropped_faces = [] + self.inverse_affine_matrices = [] + self.det_faces = [] + self.pad_input_imgs = [] \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/facelib/utils/face_utils.py b/sd/stablediffusion/src/codeformer/facelib/utils/face_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..f1474a2a4419b6b62fab8a919ef805b802556464 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/utils/face_utils.py @@ -0,0 +1,248 @@ +import cv2 +import numpy as np +import torch + + +def compute_increased_bbox(bbox, increase_area, preserve_aspect=True): + left, top, right, bot = bbox + width = right - left + height = bot - top + + if preserve_aspect: + width_increase = max(increase_area, ((1 + 2 * increase_area) * height - width) / (2 * width)) + height_increase = max(increase_area, ((1 + 2 * increase_area) * width - height) / (2 * height)) + else: + width_increase = height_increase = increase_area + left = int(left - width_increase * width) + top = int(top - height_increase * height) + right = int(right + width_increase * width) + bot = int(bot + height_increase * height) + return (left, top, right, bot) + + +def get_valid_bboxes(bboxes, h, w): + left = max(bboxes[0], 0) + top = max(bboxes[1], 0) + right = min(bboxes[2], w) + bottom = min(bboxes[3], h) + return (left, top, right, bottom) + + +def align_crop_face_landmarks(img, + landmarks, + output_size, + transform_size=None, + enable_padding=True, + return_inverse_affine=False, + shrink_ratio=(1, 1)): + """Align and crop face with landmarks. + + The output_size and transform_size are based on width. The height is + adjusted based on shrink_ratio_h/shring_ration_w. + + Modified from: + https://github.com/NVlabs/ffhq-dataset/blob/master/download_ffhq.py + + Args: + img (Numpy array): Input image. + landmarks (Numpy array): 5 or 68 or 98 landmarks. + output_size (int): Output face size. + transform_size (ing): Transform size. Usually the four time of + output_size. + enable_padding (float): Default: True. + shrink_ratio (float | tuple[float] | list[float]): Shring the whole + face for height and width (crop larger area). Default: (1, 1). + + Returns: + (Numpy array): Cropped face. + """ + lm_type = 'retinaface_5' # Options: dlib_5, retinaface_5 + + if isinstance(shrink_ratio, (float, int)): + shrink_ratio = (shrink_ratio, shrink_ratio) + if transform_size is None: + transform_size = output_size * 4 + + # Parse landmarks + lm = np.array(landmarks) + if lm.shape[0] == 5 and lm_type == 'retinaface_5': + eye_left = lm[0] + eye_right = lm[1] + mouth_avg = (lm[3] + lm[4]) * 0.5 + elif lm.shape[0] == 5 and lm_type == 'dlib_5': + lm_eye_left = lm[2:4] + lm_eye_right = lm[0:2] + eye_left = np.mean(lm_eye_left, axis=0) + eye_right = np.mean(lm_eye_right, axis=0) + mouth_avg = lm[4] + elif lm.shape[0] == 68: + lm_eye_left = lm[36:42] + lm_eye_right = lm[42:48] + eye_left = np.mean(lm_eye_left, axis=0) + eye_right = np.mean(lm_eye_right, axis=0) + mouth_avg = (lm[48] + lm[54]) * 0.5 + elif lm.shape[0] == 98: + lm_eye_left = lm[60:68] + lm_eye_right = lm[68:76] + eye_left = np.mean(lm_eye_left, axis=0) + eye_right = np.mean(lm_eye_right, axis=0) + mouth_avg = (lm[76] + lm[82]) * 0.5 + + eye_avg = (eye_left + eye_right) * 0.5 + eye_to_eye = eye_right - eye_left + eye_to_mouth = mouth_avg - eye_avg + + # Get the oriented crop rectangle + # x: half width of the oriented crop rectangle + x = eye_to_eye - np.flipud(eye_to_mouth) * [-1, 1] + # - np.flipud(eye_to_mouth) * [-1, 1]: rotate 90 clockwise + # norm with the hypotenuse: get the direction + x /= np.hypot(*x) # get the hypotenuse of a right triangle + rect_scale = 1 # TODO: you can edit it to get larger rect + x *= max(np.hypot(*eye_to_eye) * 2.0 * rect_scale, np.hypot(*eye_to_mouth) * 1.8 * rect_scale) + # y: half height of the oriented crop rectangle + y = np.flipud(x) * [-1, 1] + + x *= shrink_ratio[1] # width + y *= shrink_ratio[0] # height + + # c: center + c = eye_avg + eye_to_mouth * 0.1 + # quad: (left_top, left_bottom, right_bottom, right_top) + quad = np.stack([c - x - y, c - x + y, c + x + y, c + x - y]) + # qsize: side length of the square + qsize = np.hypot(*x) * 2 + + quad_ori = np.copy(quad) + # Shrink, for large face + # TODO: do we really need shrink + shrink = int(np.floor(qsize / output_size * 0.5)) + if shrink > 1: + h, w = img.shape[0:2] + rsize = (int(np.rint(float(w) / shrink)), int(np.rint(float(h) / shrink))) + img = cv2.resize(img, rsize, interpolation=cv2.INTER_AREA) + quad /= shrink + qsize /= shrink + + # Crop + h, w = img.shape[0:2] + border = max(int(np.rint(qsize * 0.1)), 3) + crop = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), int(np.ceil(max(quad[:, 0]))), + int(np.ceil(max(quad[:, 1])))) + crop = (max(crop[0] - border, 0), max(crop[1] - border, 0), min(crop[2] + border, w), min(crop[3] + border, h)) + if crop[2] - crop[0] < w or crop[3] - crop[1] < h: + img = img[crop[1]:crop[3], crop[0]:crop[2], :] + quad -= crop[0:2] + + # Pad + # pad: (width_left, height_top, width_right, height_bottom) + h, w = img.shape[0:2] + pad = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), int(np.ceil(max(quad[:, 0]))), + int(np.ceil(max(quad[:, 1])))) + pad = (max(-pad[0] + border, 0), max(-pad[1] + border, 0), max(pad[2] - w + border, 0), max(pad[3] - h + border, 0)) + if enable_padding and max(pad) > border - 4: + pad = np.maximum(pad, int(np.rint(qsize * 0.3))) + img = np.pad(img, ((pad[1], pad[3]), (pad[0], pad[2]), (0, 0)), 'reflect') + h, w = img.shape[0:2] + y, x, _ = np.ogrid[:h, :w, :1] + mask = np.maximum(1.0 - np.minimum(np.float32(x) / pad[0], + np.float32(w - 1 - x) / pad[2]), + 1.0 - np.minimum(np.float32(y) / pad[1], + np.float32(h - 1 - y) / pad[3])) + blur = int(qsize * 0.02) + if blur % 2 == 0: + blur += 1 + blur_img = cv2.boxFilter(img, 0, ksize=(blur, blur)) + + img = img.astype('float32') + img += (blur_img - img) * np.clip(mask * 3.0 + 1.0, 0.0, 1.0) + img += (np.median(img, axis=(0, 1)) - img) * np.clip(mask, 0.0, 1.0) + img = np.clip(img, 0, 255) # float32, [0, 255] + quad += pad[:2] + + # Transform use cv2 + h_ratio = shrink_ratio[0] / shrink_ratio[1] + dst_h, dst_w = int(transform_size * h_ratio), transform_size + template = np.array([[0, 0], [0, dst_h], [dst_w, dst_h], [dst_w, 0]]) + # use cv2.LMEDS method for the equivalence to skimage transform + # ref: https://blog.csdn.net/yichxi/article/details/115827338 + affine_matrix = cv2.estimateAffinePartial2D(quad, template, method=cv2.LMEDS)[0] + cropped_face = cv2.warpAffine( + img, affine_matrix, (dst_w, dst_h), borderMode=cv2.BORDER_CONSTANT, borderValue=(135, 133, 132)) # gray + + if output_size < transform_size: + cropped_face = cv2.resize( + cropped_face, (output_size, int(output_size * h_ratio)), interpolation=cv2.INTER_LINEAR) + + if return_inverse_affine: + dst_h, dst_w = int(output_size * h_ratio), output_size + template = np.array([[0, 0], [0, dst_h], [dst_w, dst_h], [dst_w, 0]]) + # use cv2.LMEDS method for the equivalence to skimage transform + # ref: https://blog.csdn.net/yichxi/article/details/115827338 + affine_matrix = cv2.estimateAffinePartial2D( + quad_ori, np.array([[0, 0], [0, output_size], [dst_w, dst_h], [dst_w, 0]]), method=cv2.LMEDS)[0] + inverse_affine = cv2.invertAffineTransform(affine_matrix) + else: + inverse_affine = None + return cropped_face, inverse_affine + + +def paste_face_back(img, face, inverse_affine): + h, w = img.shape[0:2] + face_h, face_w = face.shape[0:2] + inv_restored = cv2.warpAffine(face, inverse_affine, (w, h)) + mask = np.ones((face_h, face_w, 3), dtype=np.float32) + inv_mask = cv2.warpAffine(mask, inverse_affine, (w, h)) + # remove the black borders + inv_mask_erosion = cv2.erode(inv_mask, np.ones((2, 2), np.uint8)) + inv_restored_remove_border = inv_mask_erosion * inv_restored + total_face_area = np.sum(inv_mask_erosion) // 3 + # compute the fusion edge based on the area of face + w_edge = int(total_face_area**0.5) // 20 + erosion_radius = w_edge * 2 + inv_mask_center = cv2.erode(inv_mask_erosion, np.ones((erosion_radius, erosion_radius), np.uint8)) + blur_size = w_edge * 2 + inv_soft_mask = cv2.GaussianBlur(inv_mask_center, (blur_size + 1, blur_size + 1), 0) + img = inv_soft_mask * inv_restored_remove_border + (1 - inv_soft_mask) * img + # float32, [0, 255] + return img + + +if __name__ == '__main__': + import os + + from facelib.detection import init_detection_model + from facelib.utils.face_restoration_helper import get_largest_face + + img_path = '/home/wxt/datasets/ffhq/ffhq_wild/00009.png' + img_name = os.splitext(os.path.basename(img_path))[0] + + # initialize model + det_net = init_detection_model('retinaface_resnet50', half=False) + img_ori = cv2.imread(img_path) + h, w = img_ori.shape[0:2] + # if larger than 800, scale it + scale = max(h / 800, w / 800) + if scale > 1: + img = cv2.resize(img_ori, (int(w / scale), int(h / scale)), interpolation=cv2.INTER_LINEAR) + + with torch.no_grad(): + bboxes = det_net.detect_faces(img, 0.97) + if scale > 1: + bboxes *= scale # the score is incorrect + bboxes = get_largest_face(bboxes, h, w)[0] + + landmarks = np.array([[bboxes[i], bboxes[i + 1]] for i in range(5, 15, 2)]) + + cropped_face, inverse_affine = align_crop_face_landmarks( + img_ori, + landmarks, + output_size=512, + transform_size=None, + enable_padding=True, + return_inverse_affine=True, + shrink_ratio=(1, 1)) + + cv2.imwrite(f'tmp/{img_name}_cropeed_face.png', cropped_face) + img = paste_face_back(img_ori, cropped_face, inverse_affine) + cv2.imwrite(f'tmp/{img_name}_back.png', img) diff --git a/sd/stablediffusion/src/codeformer/facelib/utils/misc.py b/sd/stablediffusion/src/codeformer/facelib/utils/misc.py new file mode 100644 index 0000000000000000000000000000000000000000..7f5c95506c83fc6ed2155f0c7614559572dabd28 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/facelib/utils/misc.py @@ -0,0 +1,174 @@ +import cv2 +import os +import os.path as osp +import numpy as np +from PIL import Image +import torch +from torch.hub import download_url_to_file, get_dir +from urllib.parse import urlparse +# from basicsr.utils.download_util import download_file_from_google_drive + +ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + +def download_pretrained_models(file_ids, save_path_root): + import gdown + + os.makedirs(save_path_root, exist_ok=True) + + for file_name, file_id in file_ids.items(): + file_url = 'https://drive.google.com/uc?id='+file_id + save_path = osp.abspath(osp.join(save_path_root, file_name)) + if osp.exists(save_path): + user_response = input(f'{file_name} already exist. Do you want to cover it? Y/N\n') + if user_response.lower() == 'y': + print(f'Covering {file_name} to {save_path}') + gdown.download(file_url, save_path, quiet=False) + # download_file_from_google_drive(file_id, save_path) + elif user_response.lower() == 'n': + print(f'Skipping {file_name}') + else: + raise ValueError('Wrong input. Only accepts Y/N.') + else: + print(f'Downloading {file_name} to {save_path}') + gdown.download(file_url, save_path, quiet=False) + # download_file_from_google_drive(file_id, save_path) + + +def imwrite(img, file_path, params=None, auto_mkdir=True): + """Write image to file. + + Args: + img (ndarray): Image array to be written. + file_path (str): Image file path. + params (None or list): Same as opencv's :func:`imwrite` interface. + auto_mkdir (bool): If the parent folder of `file_path` does not exist, + whether to create it automatically. + + Returns: + bool: Successful or not. + """ + if auto_mkdir: + dir_name = os.path.abspath(os.path.dirname(file_path)) + os.makedirs(dir_name, exist_ok=True) + return cv2.imwrite(file_path, img, params) + + +def img2tensor(imgs, bgr2rgb=True, float32=True): + """Numpy array to tensor. + + Args: + imgs (list[ndarray] | ndarray): Input images. + bgr2rgb (bool): Whether to change bgr to rgb. + float32 (bool): Whether to change to float32. + + Returns: + list[tensor] | tensor: Tensor images. If returned results only have + one element, just return tensor. + """ + + def _totensor(img, bgr2rgb, float32): + if img.shape[2] == 3 and bgr2rgb: + if img.dtype == 'float64': + img = img.astype('float32') + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + img = torch.from_numpy(img.transpose(2, 0, 1)) + if float32: + img = img.float() + return img + + if isinstance(imgs, list): + return [_totensor(img, bgr2rgb, float32) for img in imgs] + else: + return _totensor(imgs, bgr2rgb, float32) + + +def load_file_from_url(url, model_dir=None, progress=True, file_name=None): + """Ref:https://github.com/1adrianb/face-alignment/blob/master/face_alignment/utils.py + """ + if model_dir is None: + hub_dir = get_dir() + model_dir = os.path.join(hub_dir, 'checkpoints') + + os.makedirs(os.path.join(ROOT_DIR, model_dir), exist_ok=True) + + parts = urlparse(url) + filename = os.path.basename(parts.path) + if file_name is not None: + filename = file_name + cached_file = os.path.abspath(os.path.join(ROOT_DIR, model_dir, filename)) + if not os.path.exists(cached_file): + print(f'Downloading: "{url}" to {cached_file}\n') + download_url_to_file(url, cached_file, hash_prefix=None, progress=progress) + return cached_file + + +def scandir(dir_path, suffix=None, recursive=False, full_path=False): + """Scan a directory to find the interested files. + Args: + dir_path (str): Path of the directory. + suffix (str | tuple(str), optional): File suffix that we are + interested in. Default: None. + recursive (bool, optional): If set to True, recursively scan the + directory. Default: False. + full_path (bool, optional): If set to True, include the dir_path. + Default: False. + Returns: + A generator for all the interested files with relative paths. + """ + + if (suffix is not None) and not isinstance(suffix, (str, tuple)): + raise TypeError('"suffix" must be a string or tuple of strings') + + root = dir_path + + def _scandir(dir_path, suffix, recursive): + for entry in os.scandir(dir_path): + if not entry.name.startswith('.') and entry.is_file(): + if full_path: + return_path = entry.path + else: + return_path = osp.relpath(entry.path, root) + + if suffix is None: + yield return_path + elif return_path.endswith(suffix): + yield return_path + else: + if recursive: + yield from _scandir(entry.path, suffix=suffix, recursive=recursive) + else: + continue + + return _scandir(dir_path, suffix=suffix, recursive=recursive) + + +def is_gray(img, threshold=10): + img = Image.fromarray(img) + if len(img.getbands()) == 1: + return True + img1 = np.asarray(img.getchannel(channel=0), dtype=np.int16) + img2 = np.asarray(img.getchannel(channel=1), dtype=np.int16) + img3 = np.asarray(img.getchannel(channel=2), dtype=np.int16) + diff1 = (img1 - img2).var() + diff2 = (img2 - img3).var() + diff3 = (img3 - img1).var() + diff_sum = (diff1 + diff2 + diff3) / 3.0 + if diff_sum <= threshold: + return True + else: + return False + +def rgb2gray(img, out_channel=3): + r, g, b = img[:,:,0], img[:,:,1], img[:,:,2] + gray = 0.2989 * r + 0.5870 * g + 0.1140 * b + if out_channel == 3: + gray = gray[:,:,np.newaxis].repeat(3, axis=2) + return gray + +def bgr2gray(img, out_channel=3): + b, g, r = img[:,:,0], img[:,:,1], img[:,:,2] + gray = 0.2989 * r + 0.5870 * g + 0.1140 * b + if out_channel == 3: + gray = gray[:,:,np.newaxis].repeat(3, axis=2) + return gray diff --git a/sd/stablediffusion/src/codeformer/inference_codeformer.py b/sd/stablediffusion/src/codeformer/inference_codeformer.py new file mode 100644 index 0000000000000000000000000000000000000000..126caa4a57a687924d8a2268b2a1e0375e2d180d --- /dev/null +++ b/sd/stablediffusion/src/codeformer/inference_codeformer.py @@ -0,0 +1,266 @@ +import os +import cv2 +import argparse +import glob +import torch +from torchvision.transforms.functional import normalize +from basicsr.utils import imwrite, img2tensor, tensor2img +from basicsr.utils.download_util import load_file_from_url +from facelib.utils.face_restoration_helper import FaceRestoreHelper +from facelib.utils.misc import is_gray +import torch.nn.functional as F + +from basicsr.utils.registry import ARCH_REGISTRY + +pretrain_model_url = { + 'restoration': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth', +} + +def set_realesrgan(): + from basicsr.archs.rrdbnet_arch import RRDBNet + from basicsr.utils.realesrgan_utils import RealESRGANer + + cuda_is_available = torch.cuda.is_available() + half = True if cuda_is_available else False + model = RRDBNet( + num_in_ch=3, + num_out_ch=3, + num_feat=64, + num_block=23, + num_grow_ch=32, + scale=2, + ) + upsampler = RealESRGANer( + scale=2, + model_path="https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/RealESRGAN_x2plus.pth", + model=model, + tile=args.bg_tile, + tile_pad=40, + pre_pad=0, + half=half, # need to set False in CPU mode + ) + + if not cuda_is_available: # CPU + import warnings + warnings.warn('Running on CPU now! Make sure your PyTorch version matches your CUDA.' + 'The unoptimized RealESRGAN is slow on CPU. ' + 'If you want to disable it, please remove `--bg_upsampler` and `--face_upsample` in command.', + category=RuntimeWarning) + return upsampler + +if __name__ == '__main__': + device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + parser = argparse.ArgumentParser() + + parser.add_argument('-i', '--input_path', type=str, default='./inputs/whole_imgs', + help='Input image, video or folder. Default: inputs/whole_imgs') + parser.add_argument('-o', '--output_path', type=str, default=None, + help='Output folder. Default: results/_') + parser.add_argument('-w', '--fidelity_weight', type=float, default=0.5, + help='Balance the quality and fidelity. Default: 0.5') + parser.add_argument('-s', '--upscale', type=int, default=2, + help='The final upsampling scale of the image. Default: 2') + parser.add_argument('--has_aligned', action='store_true', help='Input are cropped and aligned faces. Default: False') + parser.add_argument('--only_center_face', action='store_true', help='Only restore the center face. Default: False') + parser.add_argument('--draw_box', action='store_true', help='Draw the bounding box for the detected faces. Default: False') + # large det_model: 'YOLOv5l', 'retinaface_resnet50' + # small det_model: 'YOLOv5n', 'retinaface_mobile0.25' + parser.add_argument('--detection_model', type=str, default='retinaface_resnet50', + help='Face detector. Optional: retinaface_resnet50, retinaface_mobile0.25, YOLOv5l, YOLOv5n. \ + Default: retinaface_resnet50') + parser.add_argument('--bg_upsampler', type=str, default='None', help='Background upsampler. Optional: realesrgan') + parser.add_argument('--face_upsample', action='store_true', help='Face upsampler after enhancement. Default: False') + parser.add_argument('--bg_tile', type=int, default=400, help='Tile size for background sampler. Default: 400') + parser.add_argument('--suffix', type=str, default=None, help='Suffix of the restored faces. Default: None') + parser.add_argument('--save_video_fps', type=float, default=None, help='Frame rate for saving video. Default: None') + + args = parser.parse_args() + + # ------------------------ input & output ------------------------ + w = args.fidelity_weight + input_video = False + if args.input_path.endswith(('jpg', 'png')): # input single img path + input_img_list = [args.input_path] + result_root = f'results/test_img_{w}' + elif args.input_path.endswith(('mp4', 'mov', 'avi')): # input video path + from basicsr.utils.video_util import VideoReader, VideoWriter + input_img_list = [] + vidreader = VideoReader(args.input_path) + image = vidreader.get_frame() + while image is not None: + input_img_list.append(image) + image = vidreader.get_frame() + audio = vidreader.get_audio() + fps = vidreader.get_fps() if args.save_video_fps is None else args.save_video_fps + video_name = os.path.basename(args.input_path)[:-4] + result_root = f'results/{video_name}_{w}' + input_video = True + vidreader.close() + else: # input img folder + if args.input_path.endswith('/'): # solve when path ends with / + args.input_path = args.input_path[:-1] + # scan all the jpg and png images + input_img_list = sorted(glob.glob(os.path.join(args.input_path, '*.[jp][pn]g'))) + result_root = f'results/{os.path.basename(args.input_path)}_{w}' + + if not args.output_path is None: # set output path + result_root = args.output_path + + test_img_num = len(input_img_list) + # ------------------ set up background upsampler ------------------ + if args.bg_upsampler == 'realesrgan': + bg_upsampler = set_realesrgan() + else: + bg_upsampler = None + + # ------------------ set up face upsampler ------------------ + if args.face_upsample: + face_upsampler = None + # if bg_upsampler is not None: + # face_upsampler = bg_upsampler + # else: + # face_upsampler = set_realesrgan() + else: + face_upsampler = None + + # ------------------ set up CodeFormer restorer ------------------- + net = ARCH_REGISTRY.get('CodeFormer')(dim_embd=512, codebook_size=1024, n_head=8, n_layers=9, + connect_list=['32', '64', '128', '256']).to(device) + + # ckpt_path = 'weights/CodeFormer/codeformer.pth' + ckpt_path = load_file_from_url(url=pretrain_model_url['restoration'], + model_dir='weights/CodeFormer', progress=True, file_name=None) + checkpoint = torch.load(ckpt_path)['params_ema'] + net.load_state_dict(checkpoint) + net.eval() + + # ------------------ set up FaceRestoreHelper ------------------- + # large det_model: 'YOLOv5l', 'retinaface_resnet50' + # small det_model: 'YOLOv5n', 'retinaface_mobile0.25' + if not args.has_aligned: + print(f'Face detection model: {args.detection_model}') + if bg_upsampler is not None: + print(f'Background upsampling: True, Face upsampling: {args.face_upsample}') + else: + print(f'Background upsampling: False, Face upsampling: {args.face_upsample}') + + face_helper = FaceRestoreHelper( + args.upscale, + face_size=512, + crop_ratio=(1, 1), + det_model = args.detection_model, + save_ext='png', + use_parse=True, + device=device) + + # -------------------- start to processing --------------------- + for i, img_path in enumerate(input_img_list): + # clean all the intermediate results to process the next image + face_helper.clean_all() + + if isinstance(img_path, str): + img_name = os.path.basename(img_path) + basename, ext = os.path.splitext(img_name) + print(f'[{i+1}/{test_img_num}] Processing: {img_name}') + img = cv2.imread(img_path, cv2.IMREAD_COLOR) + else: # for video processing + basename = str(i).zfill(6) + img_name = f'{video_name}_{basename}' if input_video else basename + print(f'[{i+1}/{test_img_num}] Processing: {img_name}') + img = img_path + + if args.has_aligned: + # the input faces are already cropped and aligned + img = cv2.resize(img, (512, 512), interpolation=cv2.INTER_LINEAR) + face_helper.is_gray = is_gray(img, threshold=5) + if face_helper.is_gray: + print('Grayscale input: True') + face_helper.cropped_faces = [img] + else: + face_helper.read_image(img) + # get face landmarks for each face + num_det_faces = face_helper.get_face_landmarks_5( + only_center_face=args.only_center_face, resize=640, eye_dist_threshold=5) + print(f'\tdetect {num_det_faces} faces') + # align and warp each face + face_helper.align_warp_face() + + # face restoration for each cropped face + for idx, cropped_face in enumerate(face_helper.cropped_faces): + # prepare data + cropped_face_t = img2tensor(cropped_face / 255., bgr2rgb=True, float32=True) + normalize(cropped_face_t, (0.5, 0.5, 0.5), (0.5, 0.5, 0.5), inplace=True) + cropped_face_t = cropped_face_t.unsqueeze(0).to(device) + + try: + with torch.no_grad(): + output = net(cropped_face_t, w=w, adain=True)[0] + restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1)) + del output + torch.cuda.empty_cache() + except Exception as error: + print(f'\tFailed inference for CodeFormer: {error}') + restored_face = tensor2img(cropped_face_t, rgb2bgr=True, min_max=(-1, 1)) + + restored_face = restored_face.astype('uint8') + face_helper.add_restored_face(restored_face) + + # paste_back + if not args.has_aligned: + # upsample the background + if bg_upsampler is not None: + # Now only support RealESRGAN for upsampling background + bg_img = bg_upsampler.enhance(img, outscale=args.upscale)[0] + else: + bg_img = None + face_helper.get_inverse_affine(None) + # paste each restored face to the input image + if args.face_upsample and face_upsampler is not None: + restored_img = face_helper.paste_faces_to_input_image(upsample_img=bg_img, draw_box=args.draw_box, face_upsampler=face_upsampler) + else: + restored_img = face_helper.paste_faces_to_input_image(upsample_img=bg_img, draw_box=args.draw_box) + + # save faces + for idx, (cropped_face, restored_face) in enumerate(zip(face_helper.cropped_faces, face_helper.restored_faces)): + # save cropped face + if not args.has_aligned: + save_crop_path = os.path.join(result_root, 'cropped_faces', f'{basename}_{idx:02d}.png') + imwrite(cropped_face, save_crop_path) + # save restored face + if args.has_aligned: + save_face_name = f'{basename}.png' + else: + save_face_name = f'{basename}_{idx:02d}.png' + if args.suffix is not None: + save_face_name = f'{save_face_name[:-4]}_{args.suffix}.png' + save_restore_path = os.path.join(result_root, 'restored_faces', save_face_name) + imwrite(restored_face, save_restore_path) + + # save restored img + if not args.has_aligned and restored_img is not None: + if args.suffix is not None: + basename = f'{basename}_{args.suffix}' + save_restore_path = os.path.join(result_root, 'final_results', f'{basename}.png') + imwrite(restored_img, save_restore_path) + + # save enhanced video + if input_video: + print('Video Saving...') + # load images + video_frames = [] + img_list = sorted(glob.glob(os.path.join(result_root, 'final_results', '*.[jp][pn]g'))) + for img_path in img_list: + img = cv2.imread(img_path) + video_frames.append(img) + # write images to video + height, width = video_frames[0].shape[:2] + if args.suffix is not None: + video_name = f'{video_name}_{args.suffix}.png' + save_restore_path = os.path.join(result_root, f'{video_name}.mp4') + vidwriter = VideoWriter(save_restore_path, height, width, fps, audio) + + for f in video_frames: + vidwriter.write_frame(f) + vidwriter.close() + + print(f'\nAll results are saved in {result_root}') diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0143.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0143.png new file mode 100644 index 0000000000000000000000000000000000000000..065b3f001f43e7767308d3c2d2e5496cbe3660c7 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0143.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0240.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0240.png new file mode 100644 index 0000000000000000000000000000000000000000..7a117017a27defc68c875f97322fdf2850c0e50b Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0240.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0342.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0342.png new file mode 100644 index 0000000000000000000000000000000000000000..8f5aeeae1886ee3ec89dff52d30bcd2a8ad0ad4f Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0342.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0345.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0345.png new file mode 100644 index 0000000000000000000000000000000000000000..b8f71b6d8e437c1ffef045182ca0fcfb2f058e5c Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0345.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0368.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0368.png new file mode 100644 index 0000000000000000000000000000000000000000..262778a98196bd3f19f71ad49a9c81db417eae8c Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0368.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0412.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0412.png new file mode 100644 index 0000000000000000000000000000000000000000..c4a63b13df67ec8392c14daa204f167d2c664c98 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0412.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0444.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0444.png new file mode 100644 index 0000000000000000000000000000000000000000..9028dd0e3b65286a79e2a7538b5fb38b0f7a92ff Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0444.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0478.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0478.png new file mode 100644 index 0000000000000000000000000000000000000000..f0924061b574a9e2124c686db8e37995ebed850c Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0478.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0500.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0500.png new file mode 100644 index 0000000000000000000000000000000000000000..7a7146b473ba7cbec6eb65e7db6891ad1ceb62b7 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0500.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0599.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0599.png new file mode 100644 index 0000000000000000000000000000000000000000..ff26ccdaf1ef441e1366fe2d1b4fa7ea1b8f13a2 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0599.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0717.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0717.png new file mode 100644 index 0000000000000000000000000000000000000000..9342b5e55fd7d1698936628a7684d0c7ca2d8349 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0717.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0720.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0720.png new file mode 100644 index 0000000000000000000000000000000000000000..af384dce912c081073e4e5fed381dd2385159567 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0720.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0729.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0729.png new file mode 100644 index 0000000000000000000000000000000000000000..4f70f46e134775659d5ff40098acbafe6c1111c4 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0729.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0763.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0763.png new file mode 100644 index 0000000000000000000000000000000000000000..1263df7b03de9947281263ac79aaa7e8a1806860 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0763.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0770.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0770.png new file mode 100644 index 0000000000000000000000000000000000000000..40a64e832d7701c1bfcdc29e89eb1989487d009b Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0770.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0777.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0777.png new file mode 100644 index 0000000000000000000000000000000000000000..c72cb26ff64b7e06767e0252caa6c3f3f0538d27 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0777.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0885.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0885.png new file mode 100644 index 0000000000000000000000000000000000000000..f3ea2632f7671749c15981a966289fafb1765aa3 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0885.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0934.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0934.png new file mode 100644 index 0000000000000000000000000000000000000000..bf82c2d3a36260d589687d21325700e9cf48a889 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/0934.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/Solvay_conference_1927_0018.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/Solvay_conference_1927_0018.png new file mode 100644 index 0000000000000000000000000000000000000000..0f79547a80af7684e5f3b8bef8160ad9f1c85773 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/Solvay_conference_1927_0018.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/cropped_faces/Solvay_conference_1927_2_16.png b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/Solvay_conference_1927_2_16.png new file mode 100644 index 0000000000000000000000000000000000000000..f75b9602f3a8b2210fc459b22d9a67011404709e Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/cropped_faces/Solvay_conference_1927_2_16.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/whole_imgs/00.jpg b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/00.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d6e323e56d782d566a024a1d25282996904ffebd Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/00.jpg differ diff --git a/sd/stablediffusion/src/codeformer/inputs/whole_imgs/01.jpg b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/01.jpg new file mode 100644 index 0000000000000000000000000000000000000000..485fc6a51a066a15ff8164150afef4028a304242 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/01.jpg differ diff --git a/sd/stablediffusion/src/codeformer/inputs/whole_imgs/02.png b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/02.png new file mode 100644 index 0000000000000000000000000000000000000000..378e7b159223e49e9967de53d0c121558b9a56ef Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/02.png differ diff --git a/sd/stablediffusion/src/codeformer/inputs/whole_imgs/03.jpg b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/03.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b6c8428105e821801c07e6d4c8dbbc3c72814a58 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/03.jpg differ diff --git a/sd/stablediffusion/src/codeformer/inputs/whole_imgs/04.jpg b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/04.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bb94681a7ff03ef57e0951a6c1fbfe6329571950 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/04.jpg differ diff --git a/sd/stablediffusion/src/codeformer/inputs/whole_imgs/05.jpg b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/05.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4dc33735f9ad7c02b591810cc38c033f21be6c81 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/05.jpg differ diff --git a/sd/stablediffusion/src/codeformer/inputs/whole_imgs/06.png b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/06.png new file mode 100644 index 0000000000000000000000000000000000000000..49c2fff2c655aaaddcd38920a063307c7ecc5152 Binary files /dev/null and b/sd/stablediffusion/src/codeformer/inputs/whole_imgs/06.png differ diff --git a/sd/stablediffusion/src/codeformer/requirements.txt b/sd/stablediffusion/src/codeformer/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..15b5a93df0d422ddc3e7e44e72d19c0d6fec5290 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/requirements.txt @@ -0,0 +1,18 @@ +addict +future +lmdb +numpy +opencv-python +Pillow +pyyaml +requests +scikit-image +scipy +tb-nightly +torch>=1.7.1 +torchvision +tqdm +yapf +lpips +gdown # supports downloading the large file from Google Drive +ffmpeg-python \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/scripts/crop_align_face.py b/sd/stablediffusion/src/codeformer/scripts/crop_align_face.py new file mode 100644 index 0000000000000000000000000000000000000000..31e66266ac0e5f818fa18b6409993151086bbc8b --- /dev/null +++ b/sd/stablediffusion/src/codeformer/scripts/crop_align_face.py @@ -0,0 +1,192 @@ +""" +brief: face alignment with FFHQ method (https://github.com/NVlabs/ffhq-dataset) +author: lzhbrian (https://lzhbrian.me) +link: https://gist.github.com/lzhbrian/bde87ab23b499dd02ba4f588258f57d5 +date: 2020.1.5 +note: code is heavily borrowed from + https://github.com/NVlabs/ffhq-dataset + http://dlib.net/face_landmark_detection.py.html +requirements: + conda install Pillow numpy scipy + conda install -c conda-forge dlib + # download face landmark model from: + # http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2 +""" + +import cv2 +import dlib +import glob +import numpy as np +import os +import PIL +import PIL.Image +import scipy +import scipy.ndimage +import sys +import argparse + +# download model from: http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2 +predictor = dlib.shape_predictor('weights/dlib/shape_predictor_68_face_landmarks-fbdc2cb8.dat') + + +def get_landmark(filepath, only_keep_largest=True): + """get landmark with dlib + :return: np.array shape=(68, 2) + """ + detector = dlib.get_frontal_face_detector() + + img = dlib.load_rgb_image(filepath) + dets = detector(img, 1) + + # Shangchen modified + print("Number of faces detected: {}".format(len(dets))) + if only_keep_largest: + print('Detect several faces and only keep the largest.') + face_areas = [] + for k, d in enumerate(dets): + face_area = (d.right() - d.left()) * (d.bottom() - d.top()) + face_areas.append(face_area) + + largest_idx = face_areas.index(max(face_areas)) + d = dets[largest_idx] + shape = predictor(img, d) + print("Part 0: {}, Part 1: {} ...".format( + shape.part(0), shape.part(1))) + else: + for k, d in enumerate(dets): + print("Detection {}: Left: {} Top: {} Right: {} Bottom: {}".format( + k, d.left(), d.top(), d.right(), d.bottom())) + # Get the landmarks/parts for the face in box d. + shape = predictor(img, d) + print("Part 0: {}, Part 1: {} ...".format( + shape.part(0), shape.part(1))) + + t = list(shape.parts()) + a = [] + for tt in t: + a.append([tt.x, tt.y]) + lm = np.array(a) + # lm is a shape=(68,2) np.array + return lm + +def align_face(filepath, out_path): + """ + :param filepath: str + :return: PIL Image + """ + try: + lm = get_landmark(filepath) + except: + print('No landmark ...') + return + + lm_chin = lm[0:17] # left-right + lm_eyebrow_left = lm[17:22] # left-right + lm_eyebrow_right = lm[22:27] # left-right + lm_nose = lm[27:31] # top-down + lm_nostrils = lm[31:36] # top-down + lm_eye_left = lm[36:42] # left-clockwise + lm_eye_right = lm[42:48] # left-clockwise + lm_mouth_outer = lm[48:60] # left-clockwise + lm_mouth_inner = lm[60:68] # left-clockwise + + # Calculate auxiliary vectors. + eye_left = np.mean(lm_eye_left, axis=0) + eye_right = np.mean(lm_eye_right, axis=0) + eye_avg = (eye_left + eye_right) * 0.5 + eye_to_eye = eye_right - eye_left + mouth_left = lm_mouth_outer[0] + mouth_right = lm_mouth_outer[6] + mouth_avg = (mouth_left + mouth_right) * 0.5 + eye_to_mouth = mouth_avg - eye_avg + + # Choose oriented crop rectangle. + x = eye_to_eye - np.flipud(eye_to_mouth) * [-1, 1] + x /= np.hypot(*x) + x *= max(np.hypot(*eye_to_eye) * 2.0, np.hypot(*eye_to_mouth) * 1.8) + y = np.flipud(x) * [-1, 1] + c = eye_avg + eye_to_mouth * 0.1 + quad = np.stack([c - x - y, c - x + y, c + x + y, c + x - y]) + qsize = np.hypot(*x) * 2 + + # read image + img = PIL.Image.open(filepath) + + output_size = 512 + transform_size = 4096 + enable_padding = False + + # Shrink. + shrink = int(np.floor(qsize / output_size * 0.5)) + if shrink > 1: + rsize = (int(np.rint(float(img.size[0]) / shrink)), + int(np.rint(float(img.size[1]) / shrink))) + img = img.resize(rsize, PIL.Image.ANTIALIAS) + quad /= shrink + qsize /= shrink + + # Crop. + border = max(int(np.rint(qsize * 0.1)), 3) + crop = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), + int(np.ceil(max(quad[:, 0]))), int(np.ceil(max(quad[:, 1])))) + crop = (max(crop[0] - border, 0), max(crop[1] - border, 0), + min(crop[2] + border, + img.size[0]), min(crop[3] + border, img.size[1])) + if crop[2] - crop[0] < img.size[0] or crop[3] - crop[1] < img.size[1]: + img = img.crop(crop) + quad -= crop[0:2] + + # Pad. + pad = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), + int(np.ceil(max(quad[:, 0]))), int(np.ceil(max(quad[:, 1])))) + pad = (max(-pad[0] + border, + 0), max(-pad[1] + border, + 0), max(pad[2] - img.size[0] + border, + 0), max(pad[3] - img.size[1] + border, 0)) + if enable_padding and max(pad) > border - 4: + pad = np.maximum(pad, int(np.rint(qsize * 0.3))) + img = np.pad( + np.float32(img), ((pad[1], pad[3]), (pad[0], pad[2]), (0, 0)), + 'reflect') + h, w, _ = img.shape + y, x, _ = np.ogrid[:h, :w, :1] + mask = np.maximum( + 1.0 - + np.minimum(np.float32(x) / pad[0], + np.float32(w - 1 - x) / pad[2]), 1.0 - + np.minimum(np.float32(y) / pad[1], + np.float32(h - 1 - y) / pad[3])) + blur = qsize * 0.02 + img += (scipy.ndimage.gaussian_filter(img, [blur, blur, 0]) - + img) * np.clip(mask * 3.0 + 1.0, 0.0, 1.0) + img += (np.median(img, axis=(0, 1)) - img) * np.clip(mask, 0.0, 1.0) + img = PIL.Image.fromarray( + np.uint8(np.clip(np.rint(img), 0, 255)), 'RGB') + quad += pad[:2] + + img = img.transform((transform_size, transform_size), PIL.Image.QUAD, + (quad + 0.5).flatten(), PIL.Image.BILINEAR) + + if output_size < transform_size: + img = img.resize((output_size, output_size), PIL.Image.ANTIALIAS) + + # Save aligned image. + print('saveing: ', out_path) + img.save(out_path) + + return img, np.max(quad[:, 0]) - np.min(quad[:, 0]) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--in_dir', type=str, default='./inputs/whole_imgs') + parser.add_argument('--out_dir', type=str, default='./inputs/cropped_faces') + args = parser.parse_args() + + img_list = sorted(glob.glob(f'{args.in_dir}/*.png')) + img_list = sorted(img_list) + + for in_path in img_list: + out_path = os.path.join(args.out_dir, in_path.split("/")[-1]) + out_path = out_path.replace('.jpg', '.png') + size_ = align_face(in_path, out_path) \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/scripts/download_pretrained_models.py b/sd/stablediffusion/src/codeformer/scripts/download_pretrained_models.py new file mode 100644 index 0000000000000000000000000000000000000000..daa6e8ca14ea91c89a318e85d9f182eb7d1bf025 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/scripts/download_pretrained_models.py @@ -0,0 +1,40 @@ +import argparse +import os +from os import path as osp + +from basicsr.utils.download_util import load_file_from_url + + +def download_pretrained_models(method, file_urls): + save_path_root = f'./weights/{method}' + os.makedirs(save_path_root, exist_ok=True) + + for file_name, file_url in file_urls.items(): + save_path = load_file_from_url(url=file_url, model_dir=save_path_root, progress=True, file_name=file_name) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + + parser.add_argument( + 'method', + type=str, + help=("Options: 'CodeFormer' 'facelib'. Set to 'all' to download all the models.")) + args = parser.parse_args() + + file_urls = { + 'CodeFormer': { + 'codeformer.pth': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth' + }, + 'facelib': { + # 'yolov5l-face.pth': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/yolov5l-face.pth', + 'detection_Resnet50_Final.pth': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/detection_Resnet50_Final.pth', + 'parsing_parsenet.pth': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/parsing_parsenet.pth' + } + } + + if args.method == 'all': + for method in file_urls.keys(): + download_pretrained_models(method, file_urls[method]) + else: + download_pretrained_models(args.method, file_urls[args.method]) \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/scripts/download_pretrained_models_from_gdrive.py b/sd/stablediffusion/src/codeformer/scripts/download_pretrained_models_from_gdrive.py new file mode 100644 index 0000000000000000000000000000000000000000..7df5be6fc260394ee9bbd0a7ae377e2ca657fe83 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/scripts/download_pretrained_models_from_gdrive.py @@ -0,0 +1,60 @@ +import argparse +import os +from os import path as osp + +# from basicsr.utils.download_util import download_file_from_google_drive +import gdown + + +def download_pretrained_models(method, file_ids): + save_path_root = f'./weights/{method}' + os.makedirs(save_path_root, exist_ok=True) + + for file_name, file_id in file_ids.items(): + file_url = 'https://drive.google.com/uc?id='+file_id + save_path = osp.abspath(osp.join(save_path_root, file_name)) + if osp.exists(save_path): + user_response = input(f'{file_name} already exist. Do you want to cover it? Y/N\n') + if user_response.lower() == 'y': + print(f'Covering {file_name} to {save_path}') + gdown.download(file_url, save_path, quiet=False) + # download_file_from_google_drive(file_id, save_path) + elif user_response.lower() == 'n': + print(f'Skipping {file_name}') + else: + raise ValueError('Wrong input. Only accepts Y/N.') + else: + print(f'Downloading {file_name} to {save_path}') + gdown.download(file_url, save_path, quiet=False) + # download_file_from_google_drive(file_id, save_path) + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + + parser.add_argument( + 'method', + type=str, + help=("Options: 'CodeFormer' 'facelib'. Set to 'all' to download all the models.")) + args = parser.parse_args() + + # file name: file id + # 'dlib': { + # 'mmod_human_face_detector-4cb19393.dat': '1qD-OqY8M6j4PWUP_FtqfwUPFPRMu6ubX', + # 'shape_predictor_5_face_landmarks-c4b1e980.dat': '1vF3WBUApw4662v9Pw6wke3uk1qxnmLdg', + # 'shape_predictor_68_face_landmarks-fbdc2cb8.dat': '1tJyIVdCHaU6IDMDx86BZCxLGZfsWB8yq' + # } + file_ids = { + 'CodeFormer': { + 'codeformer.pth': '1v_E_vZvP-dQPF55Kc5SRCjaKTQXDz-JB' + }, + 'facelib': { + 'yolov5l-face.pth': '131578zMA6B2x8VQHyHfa6GEPtulMCNzV', + 'parsing_parsenet.pth': '16pkohyZZ8ViHGBk3QtVqxLZKzdo466bK' + } + } + + if args.method == 'all': + for method in file_ids.keys(): + download_pretrained_models(method, file_ids[method]) + else: + download_pretrained_models(args.method, file_ids[args.method]) \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/web-demos/hugging_face/app.py b/sd/stablediffusion/src/codeformer/web-demos/hugging_face/app.py new file mode 100644 index 0000000000000000000000000000000000000000..7da0fc9479dcc28d3b3183980c80154885137571 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/web-demos/hugging_face/app.py @@ -0,0 +1,280 @@ +""" +This file is used for deploying hugging face demo: +https://huggingface.co/spaces/sczhou/CodeFormer +""" + +import sys +sys.path.append('CodeFormer') +import os +import cv2 +import torch +import torch.nn.functional as F +import gradio as gr + +from torchvision.transforms.functional import normalize + +from basicsr.utils import imwrite, img2tensor, tensor2img +from basicsr.utils.download_util import load_file_from_url +from facelib.utils.face_restoration_helper import FaceRestoreHelper +from facelib.utils.misc import is_gray +from basicsr.archs.rrdbnet_arch import RRDBNet +from basicsr.utils.realesrgan_utils import RealESRGANer + +from basicsr.utils.registry import ARCH_REGISTRY + + +os.system("pip freeze") + +pretrain_model_url = { + 'codeformer': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth', + 'detection': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/detection_Resnet50_Final.pth', + 'parsing': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/parsing_parsenet.pth', + 'realesrgan': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/RealESRGAN_x2plus.pth' +} +# download weights +if not os.path.exists('CodeFormer/weights/CodeFormer/codeformer.pth'): + load_file_from_url(url=pretrain_model_url['codeformer'], model_dir='CodeFormer/weights/CodeFormer', progress=True, file_name=None) +if not os.path.exists('CodeFormer/weights/facelib/detection_Resnet50_Final.pth'): + load_file_from_url(url=pretrain_model_url['detection'], model_dir='CodeFormer/weights/facelib', progress=True, file_name=None) +if not os.path.exists('CodeFormer/weights/facelib/parsing_parsenet.pth'): + load_file_from_url(url=pretrain_model_url['parsing'], model_dir='CodeFormer/weights/facelib', progress=True, file_name=None) +if not os.path.exists('CodeFormer/weights/realesrgan/RealESRGAN_x2plus.pth'): + load_file_from_url(url=pretrain_model_url['realesrgan'], model_dir='CodeFormer/weights/realesrgan', progress=True, file_name=None) + +# download images +torch.hub.download_url_to_file( + 'https://replicate.com/api/models/sczhou/codeformer/files/fa3fe3d1-76b0-4ca8-ac0d-0a925cb0ff54/06.png', + '01.png') +torch.hub.download_url_to_file( + 'https://replicate.com/api/models/sczhou/codeformer/files/a1daba8e-af14-4b00-86a4-69cec9619b53/04.jpg', + '02.jpg') +torch.hub.download_url_to_file( + 'https://replicate.com/api/models/sczhou/codeformer/files/542d64f9-1712-4de7-85f7-3863009a7c3d/03.jpg', + '03.jpg') +torch.hub.download_url_to_file( + 'https://replicate.com/api/models/sczhou/codeformer/files/a11098b0-a18a-4c02-a19a-9a7045d68426/010.jpg', + '04.jpg') +torch.hub.download_url_to_file( + 'https://replicate.com/api/models/sczhou/codeformer/files/7cf19c2c-e0cf-4712-9af8-cf5bdbb8d0ee/012.jpg', + '05.jpg') + +def imread(img_path): + img = cv2.imread(img_path) + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + return img + +# set enhancer with RealESRGAN +def set_realesrgan(): + half = True if torch.cuda.is_available() else False + model = RRDBNet( + num_in_ch=3, + num_out_ch=3, + num_feat=64, + num_block=23, + num_grow_ch=32, + scale=2, + ) + upsampler = RealESRGANer( + scale=2, + model_path="CodeFormer/weights/realesrgan/RealESRGAN_x2plus.pth", + model=model, + tile=400, + tile_pad=40, + pre_pad=0, + half=half, + ) + return upsampler + +upsampler = set_realesrgan() +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') +codeformer_net = ARCH_REGISTRY.get("CodeFormer")( + dim_embd=512, + codebook_size=1024, + n_head=8, + n_layers=9, + connect_list=["32", "64", "128", "256"], +).to(device) +ckpt_path = "CodeFormer/weights/CodeFormer/codeformer.pth" +checkpoint = torch.load(ckpt_path)["params_ema"] +codeformer_net.load_state_dict(checkpoint) +codeformer_net.eval() + +os.makedirs('output', exist_ok=True) + +def inference(image, background_enhance, face_upsample, upscale, codeformer_fidelity): + """Run a single prediction on the model""" + try: # global try + # take the default setting for the demo + has_aligned = False + only_center_face = False + draw_box = False + detection_model = "retinaface_resnet50" + print('Inp:', image, background_enhance, face_upsample, upscale, codeformer_fidelity) + + img = cv2.imread(str(image), cv2.IMREAD_COLOR) + print('\timage size:', img.shape) + + upscale = int(upscale) # convert type to int + if upscale > 4: # avoid memory exceeded due to too large upscale + upscale = 4 + if upscale > 2 and max(img.shape[:2])>1000: # avoid memory exceeded due to too large img resolution + upscale = 2 + if max(img.shape[:2]) > 1500: # avoid memory exceeded due to too large img resolution + upscale = 1 + background_enhance = False + face_upsample = False + + face_helper = FaceRestoreHelper( + upscale, + face_size=512, + crop_ratio=(1, 1), + det_model=detection_model, + save_ext="png", + use_parse=True, + device=device, + ) + bg_upsampler = upsampler if background_enhance else None + face_upsampler = upsampler if face_upsample else None + + if has_aligned: + # the input faces are already cropped and aligned + img = cv2.resize(img, (512, 512), interpolation=cv2.INTER_LINEAR) + face_helper.is_gray = is_gray(img, threshold=5) + if face_helper.is_gray: + print('\tgrayscale input: True') + face_helper.cropped_faces = [img] + else: + face_helper.read_image(img) + # get face landmarks for each face + num_det_faces = face_helper.get_face_landmarks_5( + only_center_face=only_center_face, resize=640, eye_dist_threshold=5 + ) + print(f'\tdetect {num_det_faces} faces') + # align and warp each face + face_helper.align_warp_face() + + # face restoration for each cropped face + for idx, cropped_face in enumerate(face_helper.cropped_faces): + # prepare data + cropped_face_t = img2tensor( + cropped_face / 255.0, bgr2rgb=True, float32=True + ) + normalize(cropped_face_t, (0.5, 0.5, 0.5), (0.5, 0.5, 0.5), inplace=True) + cropped_face_t = cropped_face_t.unsqueeze(0).to(device) + + try: + with torch.no_grad(): + output = codeformer_net( + cropped_face_t, w=codeformer_fidelity, adain=True + )[0] + restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1)) + del output + torch.cuda.empty_cache() + except RuntimeError as error: + print(f"Failed inference for CodeFormer: {error}") + restored_face = tensor2img( + cropped_face_t, rgb2bgr=True, min_max=(-1, 1) + ) + + restored_face = restored_face.astype("uint8") + face_helper.add_restored_face(restored_face) + + # paste_back + if not has_aligned: + # upsample the background + if bg_upsampler is not None: + # Now only support RealESRGAN for upsampling background + bg_img = bg_upsampler.enhance(img, outscale=upscale)[0] + else: + bg_img = None + face_helper.get_inverse_affine(None) + # paste each restored face to the input image + if face_upsample and face_upsampler is not None: + restored_img = face_helper.paste_faces_to_input_image( + upsample_img=bg_img, + draw_box=draw_box, + face_upsampler=face_upsampler, + ) + else: + restored_img = face_helper.paste_faces_to_input_image( + upsample_img=bg_img, draw_box=draw_box + ) + + # save restored img + save_path = f'output/out.png' + imwrite(restored_img, str(save_path)) + + restored_img = cv2.cvtColor(restored_img, cv2.COLOR_BGR2RGB) + return restored_img, save_path + except Exception as error: + print('Global exception', error) + return None, None + + +title = "CodeFormer: Robust Face Restoration and Enhancement Network" +description = r"""
    CodeFormer logo
    +Official Gradio demo for Towards Robust Blind Face Restoration with Codebook Lookup Transformer (NeurIPS 2022).
    +🔥 CodeFormer is a robust face restoration algorithm for old photos or AI-generated faces.
    +🤗 Try CodeFormer for improved stable-diffusion generation!
    +""" +article = r""" +If CodeFormer is helpful, please help to ⭐ the Github Repo. Thanks! +[![GitHub Stars](https://img.shields.io/github/stars/sczhou/CodeFormer?style=social)](https://github.com/sczhou/CodeFormer) + +--- + +📝 **Citation** + +If our work is useful for your research, please consider citing: +```bibtex +@inproceedings{zhou2022codeformer, + author = {Zhou, Shangchen and Chan, Kelvin C.K. and Li, Chongyi and Loy, Chen Change}, + title = {Towards Robust Blind Face Restoration with Codebook Lookup TransFormer}, + booktitle = {NeurIPS}, + year = {2022} +} +``` + +📋 **License** + +This project is licensed under S-Lab License 1.0. +Redistribution and use for non-commercial purposes should follow this license. + +📧 **Contact** + +If you have any questions, please feel free to reach me out at shangchenzhou@gmail.com. + +
    + 🤗 Find Me: + Twitter Follow + Github Follow +
    + +
    visitors
    +""" + +demo = gr.Interface( + inference, [ + gr.inputs.Image(type="filepath", label="Input"), + gr.inputs.Checkbox(default=True, label="Background_Enhance"), + gr.inputs.Checkbox(default=True, label="Face_Upsample"), + gr.inputs.Number(default=2, label="Rescaling_Factor (up to 4)"), + gr.Slider(0, 1, value=0.5, step=0.01, label='Codeformer_Fidelity (0 for better quality, 1 for better identity)') + ], [ + gr.outputs.Image(type="numpy", label="Output"), + gr.outputs.File(label="Download the output") + ], + title=title, + description=description, + article=article, + examples=[ + ['01.png', True, True, 2, 0.7], + ['02.jpg', True, True, 2, 0.7], + ['03.jpg', True, True, 2, 0.7], + ['04.jpg', True, True, 2, 0.1], + ['05.jpg', True, True, 2, 0.1] + ] + ) + +demo.queue(concurrency_count=2) +demo.launch() \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/web-demos/replicate/cog.yaml b/sd/stablediffusion/src/codeformer/web-demos/replicate/cog.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3f458969086e462ca4bdf888703a38e51016a9b1 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/web-demos/replicate/cog.yaml @@ -0,0 +1,30 @@ +""" +This file is used for deploying replicate demo: +https://replicate.com/sczhou/codeformer +""" + +build: + gpu: true + cuda: "11.3" + python_version: "3.8" + system_packages: + - "libgl1-mesa-glx" + - "libglib2.0-0" + python_packages: + - "ipython==8.4.0" + - "future==0.18.2" + - "lmdb==1.3.0" + - "scikit-image==0.19.3" + - "torch==1.11.0 --extra-index-url=https://download.pytorch.org/whl/cu113" + - "torchvision==0.12.0 --extra-index-url=https://download.pytorch.org/whl/cu113" + - "scipy==1.9.0" + - "gdown==4.5.1" + - "pyyaml==6.0" + - "tb-nightly==2.11.0a20220906" + - "tqdm==4.64.1" + - "yapf==0.32.0" + - "lpips==0.1.4" + - "Pillow==9.2.0" + - "opencv-python==4.6.0.66" + +predict: "predict.py:Predictor" diff --git a/sd/stablediffusion/src/codeformer/web-demos/replicate/predict.py b/sd/stablediffusion/src/codeformer/web-demos/replicate/predict.py new file mode 100644 index 0000000000000000000000000000000000000000..61935e9e7c72988b25b9fe911392aaeba5da633c --- /dev/null +++ b/sd/stablediffusion/src/codeformer/web-demos/replicate/predict.py @@ -0,0 +1,189 @@ +""" +This file is used for deploying replicate demo: +https://replicate.com/sczhou/codeformer +running: cog predict -i image=@inputs/whole_imgs/04.jpg -i codeformer_fidelity=0.5 -i upscale=2 +push: cog push r8.im/sczhou/codeformer +""" + +import tempfile +import cv2 +import torch +from torchvision.transforms.functional import normalize +try: + from cog import BasePredictor, Input, Path +except Exception: + print('please install cog package') + +from basicsr.utils import imwrite, img2tensor, tensor2img +from basicsr.archs.rrdbnet_arch import RRDBNet +from basicsr.utils.realesrgan_utils import RealESRGANer +from basicsr.utils.registry import ARCH_REGISTRY +from facelib.utils.face_restoration_helper import FaceRestoreHelper + + +class Predictor(BasePredictor): + def setup(self): + """Load the model into memory to make running multiple predictions efficient""" + self.device = "cuda:0" + self.upsampler = set_realesrgan() + self.net = ARCH_REGISTRY.get("CodeFormer")( + dim_embd=512, + codebook_size=1024, + n_head=8, + n_layers=9, + connect_list=["32", "64", "128", "256"], + ).to(self.device) + ckpt_path = "weights/CodeFormer/codeformer.pth" + checkpoint = torch.load(ckpt_path)[ + "params_ema" + ] # update file permission if cannot load + self.net.load_state_dict(checkpoint) + self.net.eval() + + def predict( + self, + image: Path = Input(description="Input image"), + codeformer_fidelity: float = Input( + default=0.5, + ge=0, + le=1, + description="Balance the quality (lower number) and fidelity (higher number).", + ), + background_enhance: bool = Input( + description="Enhance background image with Real-ESRGAN", default=True + ), + face_upsample: bool = Input( + description="Upsample restored faces for high-resolution AI-created images", + default=True, + ), + upscale: int = Input( + description="The final upsampling scale of the image", + default=2, + ), + ) -> Path: + """Run a single prediction on the model""" + + # take the default setting for the demo + has_aligned = False + only_center_face = False + draw_box = False + detection_model = "retinaface_resnet50" + + self.face_helper = FaceRestoreHelper( + upscale, + face_size=512, + crop_ratio=(1, 1), + det_model=detection_model, + save_ext="png", + use_parse=True, + device=self.device, + ) + + bg_upsampler = self.upsampler if background_enhance else None + face_upsampler = self.upsampler if face_upsample else None + + img = cv2.imread(str(image), cv2.IMREAD_COLOR) + + if has_aligned: + # the input faces are already cropped and aligned + img = cv2.resize(img, (512, 512), interpolation=cv2.INTER_LINEAR) + self.face_helper.cropped_faces = [img] + else: + self.face_helper.read_image(img) + # get face landmarks for each face + num_det_faces = self.face_helper.get_face_landmarks_5( + only_center_face=only_center_face, resize=640, eye_dist_threshold=5 + ) + print(f"\tdetect {num_det_faces} faces") + # align and warp each face + self.face_helper.align_warp_face() + + # face restoration for each cropped face + for idx, cropped_face in enumerate(self.face_helper.cropped_faces): + # prepare data + cropped_face_t = img2tensor( + cropped_face / 255.0, bgr2rgb=True, float32=True + ) + normalize(cropped_face_t, (0.5, 0.5, 0.5), (0.5, 0.5, 0.5), inplace=True) + cropped_face_t = cropped_face_t.unsqueeze(0).to(self.device) + + try: + with torch.no_grad(): + output = self.net( + cropped_face_t, w=codeformer_fidelity, adain=True + )[0] + restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1)) + del output + torch.cuda.empty_cache() + except Exception as error: + print(f"\tFailed inference for CodeFormer: {error}") + restored_face = tensor2img( + cropped_face_t, rgb2bgr=True, min_max=(-1, 1) + ) + + restored_face = restored_face.astype("uint8") + self.face_helper.add_restored_face(restored_face) + + # paste_back + if not has_aligned: + # upsample the background + if bg_upsampler is not None: + # Now only support RealESRGAN for upsampling background + bg_img = bg_upsampler.enhance(img, outscale=upscale)[0] + else: + bg_img = None + self.face_helper.get_inverse_affine(None) + # paste each restored face to the input image + if face_upsample and face_upsampler is not None: + restored_img = self.face_helper.paste_faces_to_input_image( + upsample_img=bg_img, + draw_box=draw_box, + face_upsampler=face_upsampler, + ) + else: + restored_img = self.face_helper.paste_faces_to_input_image( + upsample_img=bg_img, draw_box=draw_box + ) + + # save restored img + out_path = Path(tempfile.mkdtemp()) / 'output.png' + imwrite(restored_img, str(out_path)) + + return out_path + + +def imread(img_path): + img = cv2.imread(img_path) + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + return img + + +def set_realesrgan(): + if not torch.cuda.is_available(): # CPU + import warnings + + warnings.warn( + "The unoptimized RealESRGAN is slow on CPU. We do not use it. " + "If you really want to use it, please modify the corresponding codes.", + category=RuntimeWarning, + ) + upsampler = None + else: + model = RRDBNet( + num_in_ch=3, + num_out_ch=3, + num_feat=64, + num_block=23, + num_grow_ch=32, + scale=2, + ) + upsampler = RealESRGANer( + scale=2, + model_path="./weights/realesrgan/RealESRGAN_x2plus.pth", + model=model, + tile=400, + tile_pad=40, + pre_pad=0, + half=True, + ) + return upsampler diff --git a/sd/stablediffusion/src/codeformer/weights/CodeFormer/.gitkeep b/sd/stablediffusion/src/codeformer/weights/CodeFormer/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/src/codeformer/weights/README.md b/sd/stablediffusion/src/codeformer/weights/README.md new file mode 100644 index 0000000000000000000000000000000000000000..67ad334bd672eeb9f82813cd54e8885331bbb2f2 --- /dev/null +++ b/sd/stablediffusion/src/codeformer/weights/README.md @@ -0,0 +1,3 @@ +# Weights + +Put the downloaded pre-trained models to this folder. \ No newline at end of file diff --git a/sd/stablediffusion/src/codeformer/weights/facelib/.gitkeep b/sd/stablediffusion/src/codeformer/weights/facelib/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sd/stablediffusion/src/k-diffusion/.github/workflows/python-publish.yml b/sd/stablediffusion/src/k-diffusion/.github/workflows/python-publish.yml new file mode 100644 index 0000000000000000000000000000000000000000..9638b6b4575cb6544bf45429ad86899262a50762 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/.github/workflows/python-publish.yml @@ -0,0 +1,37 @@ +name: Release + +on: + push: + branches: + - master +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-ecosystem/action-regex-match@v2 + id: regex-match + with: + text: ${{ github.event.head_commit.message }} + regex: '^Release ([^ ]+)' + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.8' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Release + if: ${{ steps.regex-match.outputs.match != '' }} + uses: softprops/action-gh-release@v1 + with: + tag_name: v${{ steps.regex-match.outputs.group1 }} + - name: Build and publish + if: ${{ steps.regex-match.outputs.match != '' }} + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* diff --git a/sd/stablediffusion/src/k-diffusion/.gitignore b/sd/stablediffusion/src/k-diffusion/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e647ac957b0cfb9b4d10af4829e84f0f1ca0b2f0 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/.gitignore @@ -0,0 +1,10 @@ +venv* +__pycache__ +.ipynb_checkpoints +*.pth +*.egg-info +data +*_demo_*.png +wandb/* +*.csv +.env \ No newline at end of file diff --git a/sd/stablediffusion/src/k-diffusion/LICENSE b/sd/stablediffusion/src/k-diffusion/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..37a42365ab6670d39334fc9b650975c819fee3d1 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2022 Katherine Crowson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/sd/stablediffusion/src/k-diffusion/README.md b/sd/stablediffusion/src/k-diffusion/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4f7c92f9a947da69dedecd8cc1149b3713ab2e80 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/README.md @@ -0,0 +1,61 @@ +# k-diffusion + +An implementation of [Elucidating the Design Space of Diffusion-Based Generative Models](https://arxiv.org/abs/2206.00364) (Karras et al., 2022) for PyTorch. The patching method in [Improving Diffusion Model Efficiency Through Patching](https://arxiv.org/abs/2207.04316) is implemented as well. + +## Installation + +`k-diffusion` can be installed via PyPI (`pip install k-diffusion`) but it will not include training and inference scripts, only library code that others can depend on. To run the training and inference scripts, clone this repository and run `pip install -e `. + +## Training: + +To train models: + +```sh +$ ./train.py --config CONFIG_FILE --name RUN_NAME +``` + +For instance, to train a model on MNIST: + +```sh +$ ./train.py --config configs/config_mnist.json --name RUN_NAME +``` + +The configuration file allows you to specify the dataset type. Currently supported types are `"imagefolder"` (finds all images in that folder and its subfolders, recursively), `"cifar10"` (CIFAR-10), and `"mnist"` (MNIST). `"huggingface"` [Hugging Face Datasets](https://huggingface.co/docs/datasets/index) is also supported. + +Multi-GPU and multi-node training is supported with [Hugging Face Accelerate](https://huggingface.co/docs/accelerate/index). You can configure Accelerate by running: + +```sh +$ accelerate config +``` + +on all nodes, then running: + +```sh +$ accelerate launch train.py --config CONFIG_FILE --name RUN_NAME +``` + +on all nodes. + +## Enhancements/additional features: + +- k-diffusion supports an experimental model output type, an isotropic Gaussian, which seems to have a lower gradient noise scale and to train faster than Karras et al. (2022) diffusion models. + +- k-diffusion has wrappers for [v-diffusion-pytorch](https://github.com/crowsonkb/v-diffusion-pytorch), [OpenAI diffusion](https://github.com/openai/guided-diffusion), and [CompVis diffusion](https://github.com/CompVis/latent-diffusion) models allowing them to be used with its samplers and ODE/SDE. + +- k-diffusion models support progressive growing. + +- k-diffusion implements [DPM-Solver](https://arxiv.org/abs/2206.00927), which produces higher quality samples at the same number of function evalutions as Karras Algorithm 2, as well as supporting adaptive step size control. [DPM-Solver++(2S) and (2M)](https://arxiv.org/abs/2211.01095) are implemented now too for improved quality with low numbers of steps. + +- k-diffusion supports [CLIP](https://openai.com/blog/clip/) guided sampling from unconditional diffusion models (see `sample_clip_guided.py`). + +- k-diffusion supports log likelihood calculation (not a variational lower bound) for native models and all wrapped models. + +- k-diffusion can calculate, during training, the [FID](https://papers.nips.cc/paper/2017/file/8a1d694707eb0fefe65871369074926d-Paper.pdf) and [KID](https://arxiv.org/abs/1801.01401) vs the training set. + +- k-diffusion can calculate, during training, the gradient noise scale (1 / SNR), from _An Empirical Model of Large-Batch Training_, https://arxiv.org/abs/1812.06162). + +## To do: + +- Anything except unconditional image diffusion models + +- Latent diffusion diff --git a/sd/stablediffusion/src/k-diffusion/configs/config_32x32_small.json b/sd/stablediffusion/src/k-diffusion/configs/config_32x32_small.json new file mode 100644 index 0000000000000000000000000000000000000000..28bacb6274ff278185031fd49db0ea39d93b1299 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/configs/config_32x32_small.json @@ -0,0 +1,46 @@ +{ + "model": { + "type": "image_v1", + "input_channels": 3, + "input_size": [32, 32], + "patch_size": 1, + "mapping_out": 256, + "depths": [2, 4, 4], + "channels": [128, 256, 512], + "self_attn_depths": [false, true, true], + "has_variance": true, + "dropout_rate": 0.05, + "augment_wrapper": true, + "augment_prob": 0.12, + "sigma_data": 0.5, + "sigma_min": 1e-2, + "sigma_max": 80, + "sigma_sample_density": { + "type": "lognormal", + "mean": -1.2, + "std": 1.2 + } + }, + "dataset": { + "type": "imagefolder", + "location": "/path/to/dataset" + }, + "optimizer": { + "type": "adamw", + "lr": 1e-4, + "betas": [0.95, 0.999], + "eps": 1e-6, + "weight_decay": 1e-3 + }, + "lr_sched": { + "type": "inverse", + "inv_gamma": 20000.0, + "power": 1.0, + "warmup": 0.99 + }, + "ema_sched": { + "type": "inverse", + "power": 0.6667, + "max_value": 0.9999 + } +} diff --git a/sd/stablediffusion/src/k-diffusion/configs/config_32x32_small_butterflies.json b/sd/stablediffusion/src/k-diffusion/configs/config_32x32_small_butterflies.json new file mode 100644 index 0000000000000000000000000000000000000000..de02c8e2e3f7b21c3dc305eb2fd8eeb144b489cf --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/configs/config_32x32_small_butterflies.json @@ -0,0 +1,47 @@ +{ + "model": { + "type": "image_v1", + "input_channels": 3, + "input_size": [32, 32], + "patch_size": 1, + "mapping_out": 256, + "depths": [2, 4, 4], + "channels": [128, 256, 512], + "self_attn_depths": [false, true, true], + "has_variance": true, + "dropout_rate": 0.05, + "augment_wrapper": true, + "augment_prob": 0.12, + "sigma_data": 0.5, + "sigma_min": 1e-2, + "sigma_max": 80, + "sigma_sample_density": { + "type": "lognormal", + "mean": -1.2, + "std": 1.2 + } + }, + "dataset": { + "type": "huggingface", + "location": "huggan/smithsonian_butterflies_subset", + "image_key": "image" + }, + "optimizer": { + "type": "adamw", + "lr": 1e-4, + "betas": [0.95, 0.999], + "eps": 1e-6, + "weight_decay": 1e-3 + }, + "lr_sched": { + "type": "inverse", + "inv_gamma": 20000.0, + "power": 1.0, + "warmup": 0.99 + }, + "ema_sched": { + "type": "inverse", + "power": 0.6667, + "max_value": 0.9999 + } +} diff --git a/sd/stablediffusion/src/k-diffusion/configs/config_cifar10.json b/sd/stablediffusion/src/k-diffusion/configs/config_cifar10.json new file mode 100644 index 0000000000000000000000000000000000000000..f719fd7faf4a88152e50cb2da4487178a82483ae --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/configs/config_cifar10.json @@ -0,0 +1,46 @@ +{ + "model": { + "type": "image_v1", + "input_channels": 3, + "input_size": [32, 32], + "patch_size": 1, + "mapping_out": 256, + "depths": [2, 4, 4], + "channels": [128, 256, 512], + "self_attn_depths": [false, true, true], + "has_variance": true, + "dropout_rate": 0.05, + "augment_wrapper": true, + "augment_prob": 0.12, + "sigma_data": 0.5, + "sigma_min": 1e-2, + "sigma_max": 80, + "sigma_sample_density": { + "type": "lognormal", + "mean": -1.2, + "std": 1.2 + } + }, + "dataset": { + "type": "cifar10", + "location": "data" + }, + "optimizer": { + "type": "adamw", + "lr": 1e-4, + "betas": [0.95, 0.999], + "eps": 1e-6, + "weight_decay": 1e-3 + }, + "lr_sched": { + "type": "inverse", + "inv_gamma": 20000.0, + "power": 1.0, + "warmup": 0.99 + }, + "ema_sched": { + "type": "inverse", + "power": 0.6667, + "max_value": 0.9999 + } +} diff --git a/sd/stablediffusion/src/k-diffusion/configs/config_mnist.json b/sd/stablediffusion/src/k-diffusion/configs/config_mnist.json new file mode 100644 index 0000000000000000000000000000000000000000..c698913b0aa5db455b5191477b2c86b4edec88b0 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/configs/config_mnist.json @@ -0,0 +1,46 @@ +{ + "model": { + "type": "image_v1", + "input_channels": 1, + "input_size": [28, 28], + "patch_size": 1, + "mapping_out": 256, + "depths": [2, 4, 4], + "channels": [128, 128, 256], + "self_attn_depths": [false, false, true], + "has_variance": true, + "dropout_rate": 0.05, + "augment_wrapper": true, + "augment_prob": 0.12, + "sigma_data": 0.6162, + "sigma_min": 1e-2, + "sigma_max": 80, + "sigma_sample_density": { + "type": "lognormal", + "mean": -1.2, + "std": 1.2 + } + }, + "dataset": { + "type": "mnist", + "location": "data" + }, + "optimizer": { + "type": "adamw", + "lr": 2e-4, + "betas": [0.95, 0.999], + "eps": 1e-6, + "weight_decay": 1e-3 + }, + "lr_sched": { + "type": "inverse", + "inv_gamma": 20000.0, + "power": 1.0, + "warmup": 0.99 + }, + "ema_sched": { + "type": "inverse", + "power": 0.6667, + "max_value": 0.9999 + } +} diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/__init__.py b/sd/stablediffusion/src/k-diffusion/k_diffusion/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5de9decab9fef99f2dd152f16b82b5806508ffdf --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/k_diffusion/__init__.py @@ -0,0 +1,2 @@ +from . import augmentation, config, evaluation, external, gns, layers, models, sampling, utils +from .layers import Denoiser diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..161ec8f0b886ff93f013634ca866c30def66d596 Binary files /dev/null and b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/augmentation.cpython-39.pyc b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/augmentation.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0036721f6128f2fb6a65168c78619ee495f35069 Binary files /dev/null and b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/augmentation.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/config.cpython-39.pyc b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/config.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8280e1e146657845612163da53f07e35775b522c Binary files /dev/null and b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/config.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/evaluation.cpython-39.pyc b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/evaluation.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..397334c48ea60af658e2e6f78fd5fb39e97aa5b8 Binary files /dev/null and b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/evaluation.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/external.cpython-39.pyc b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/external.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..396c0f877b8ea0d9918336cd00941773a7445b30 Binary files /dev/null and b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/external.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/gns.cpython-39.pyc b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/gns.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7559a169af1eaaaf844fab4f047154ed08aea119 Binary files /dev/null and b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/gns.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/layers.cpython-39.pyc b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/layers.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d763da5b65e9a4d628f5c8b4af336d0907bd1be8 Binary files /dev/null and b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/layers.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/sampling.cpython-39.pyc b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/sampling.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1befd13c0839c93b91147ccb5db5fae68e2a8d86 Binary files /dev/null and b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/sampling.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/utils.cpython-39.pyc b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/utils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..149b7b22b6c1a9c676b2211f28d1cdd25b07f5c8 Binary files /dev/null and b/sd/stablediffusion/src/k-diffusion/k_diffusion/__pycache__/utils.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/augmentation.py b/sd/stablediffusion/src/k-diffusion/k_diffusion/augmentation.py new file mode 100644 index 0000000000000000000000000000000000000000..7dd17c686300c8ecba7fac134aa54f01619c3d46 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/k_diffusion/augmentation.py @@ -0,0 +1,105 @@ +from functools import reduce +import math +import operator + +import numpy as np +from skimage import transform +import torch +from torch import nn + + +def translate2d(tx, ty): + mat = [[1, 0, tx], + [0, 1, ty], + [0, 0, 1]] + return torch.tensor(mat, dtype=torch.float32) + + +def scale2d(sx, sy): + mat = [[sx, 0, 0], + [ 0, sy, 0], + [ 0, 0, 1]] + return torch.tensor(mat, dtype=torch.float32) + + +def rotate2d(theta): + mat = [[torch.cos(theta), torch.sin(-theta), 0], + [torch.sin(theta), torch.cos(theta), 0], + [ 0, 0, 1]] + return torch.tensor(mat, dtype=torch.float32) + + +class KarrasAugmentationPipeline: + def __init__(self, a_prob=0.12, a_scale=2**0.2, a_aniso=2**0.2, a_trans=1/8): + self.a_prob = a_prob + self.a_scale = a_scale + self.a_aniso = a_aniso + self.a_trans = a_trans + + def __call__(self, image): + h, w = image.size + mats = [translate2d(h / 2 - 0.5, w / 2 - 0.5)] + + # x-flip + a0 = torch.randint(2, []).float() + mats.append(scale2d(1 - 2 * a0, 1)) + # y-flip + do = (torch.rand([]) < self.a_prob).float() + a1 = torch.randint(2, []).float() * do + mats.append(scale2d(1, 1 - 2 * a1)) + # scaling + do = (torch.rand([]) < self.a_prob).float() + a2 = torch.randn([]) * do + mats.append(scale2d(self.a_scale ** a2, self.a_scale ** a2)) + # rotation + do = (torch.rand([]) < self.a_prob).float() + a3 = (torch.rand([]) * 2 * math.pi - math.pi) * do + mats.append(rotate2d(-a3)) + # anisotropy + do = (torch.rand([]) < self.a_prob).float() + a4 = (torch.rand([]) * 2 * math.pi - math.pi) * do + a5 = torch.randn([]) * do + mats.append(rotate2d(a4)) + mats.append(scale2d(self.a_aniso ** a5, self.a_aniso ** -a5)) + mats.append(rotate2d(-a4)) + # translation + do = (torch.rand([]) < self.a_prob).float() + a6 = torch.randn([]) * do + a7 = torch.randn([]) * do + mats.append(translate2d(self.a_trans * w * a6, self.a_trans * h * a7)) + + # form the transformation matrix and conditioning vector + mats.append(translate2d(-h / 2 + 0.5, -w / 2 + 0.5)) + mat = reduce(operator.matmul, mats) + cond = torch.stack([a0, a1, a2, a3.cos() - 1, a3.sin(), a5 * a4.cos(), a5 * a4.sin(), a6, a7]) + + # apply the transformation + image_orig = np.array(image, dtype=np.float32) / 255 + if image_orig.ndim == 2: + image_orig = image_orig[..., None] + tf = transform.AffineTransform(mat.numpy()) + image = transform.warp(image_orig, tf.inverse, order=3, mode='reflect', cval=0.5, clip=False, preserve_range=True) + image_orig = torch.as_tensor(image_orig).movedim(2, 0) * 2 - 1 + image = torch.as_tensor(image).movedim(2, 0) * 2 - 1 + return image, image_orig, cond + + +class KarrasAugmentWrapper(nn.Module): + def __init__(self, model): + super().__init__() + self.inner_model = model + + def forward(self, input, sigma, aug_cond=None, mapping_cond=None, **kwargs): + if aug_cond is None: + aug_cond = input.new_zeros([input.shape[0], 9]) + if mapping_cond is None: + mapping_cond = aug_cond + else: + mapping_cond = torch.cat([aug_cond, mapping_cond], dim=1) + return self.inner_model(input, sigma, mapping_cond=mapping_cond, **kwargs) + + def set_skip_stages(self, skip_stages): + return self.inner_model.set_skip_stages(skip_stages) + + def set_patch_size(self, patch_size): + return self.inner_model.set_patch_size(patch_size) diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/config.py b/sd/stablediffusion/src/k-diffusion/k_diffusion/config.py new file mode 100644 index 0000000000000000000000000000000000000000..4b504d6d74b2fbdf92be6aa6f84955832f8c701a --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/k_diffusion/config.py @@ -0,0 +1,110 @@ +from functools import partial +import json +import math +import warnings + +from jsonmerge import merge + +from . import augmentation, layers, models, utils + + +def load_config(file): + defaults = { + 'model': { + 'sigma_data': 1., + 'patch_size': 1, + 'dropout_rate': 0., + 'augment_wrapper': True, + 'augment_prob': 0., + 'mapping_cond_dim': 0, + 'unet_cond_dim': 0, + 'cross_cond_dim': 0, + 'cross_attn_depths': None, + 'skip_stages': 0, + 'has_variance': False, + }, + 'dataset': { + 'type': 'imagefolder', + }, + 'optimizer': { + 'type': 'adamw', + 'lr': 1e-4, + 'betas': [0.95, 0.999], + 'eps': 1e-6, + 'weight_decay': 1e-3, + }, + 'lr_sched': { + 'type': 'inverse', + 'inv_gamma': 20000., + 'power': 1., + 'warmup': 0.99, + }, + 'ema_sched': { + 'type': 'inverse', + 'power': 0.6667, + 'max_value': 0.9999 + }, + } + config = json.load(file) + return merge(defaults, config) + + +def make_model(config): + config = config['model'] + assert config['type'] == 'image_v1' + model = models.ImageDenoiserModelV1( + config['input_channels'], + config['mapping_out'], + config['depths'], + config['channels'], + config['self_attn_depths'], + config['cross_attn_depths'], + patch_size=config['patch_size'], + dropout_rate=config['dropout_rate'], + mapping_cond_dim=config['mapping_cond_dim'] + (9 if config['augment_wrapper'] else 0), + unet_cond_dim=config['unet_cond_dim'], + cross_cond_dim=config['cross_cond_dim'], + skip_stages=config['skip_stages'], + has_variance=config['has_variance'], + ) + if config['augment_wrapper']: + model = augmentation.KarrasAugmentWrapper(model) + return model + + +def make_denoiser_wrapper(config): + config = config['model'] + sigma_data = config.get('sigma_data', 1.) + has_variance = config.get('has_variance', False) + if not has_variance: + return partial(layers.Denoiser, sigma_data=sigma_data) + return partial(layers.DenoiserWithVariance, sigma_data=sigma_data) + + +def make_sample_density(config): + sd_config = config['sigma_sample_density'] + sigma_data = config['sigma_data'] + if sd_config['type'] == 'lognormal': + loc = sd_config['mean'] if 'mean' in sd_config else sd_config['loc'] + scale = sd_config['std'] if 'std' in sd_config else sd_config['scale'] + return partial(utils.rand_log_normal, loc=loc, scale=scale) + if sd_config['type'] == 'loglogistic': + loc = sd_config['loc'] if 'loc' in sd_config else math.log(sigma_data) + scale = sd_config['scale'] if 'scale' in sd_config else 0.5 + min_value = sd_config['min_value'] if 'min_value' in sd_config else 0. + max_value = sd_config['max_value'] if 'max_value' in sd_config else float('inf') + return partial(utils.rand_log_logistic, loc=loc, scale=scale, min_value=min_value, max_value=max_value) + if sd_config['type'] == 'loguniform': + min_value = sd_config['min_value'] if 'min_value' in sd_config else config['sigma_min'] + max_value = sd_config['max_value'] if 'max_value' in sd_config else config['sigma_max'] + return partial(utils.rand_log_uniform, min_value=min_value, max_value=max_value) + if sd_config['type'] == 'v-diffusion': + min_value = sd_config['min_value'] if 'min_value' in sd_config else 0. + max_value = sd_config['max_value'] if 'max_value' in sd_config else float('inf') + return partial(utils.rand_v_diffusion, sigma_data=sigma_data, min_value=min_value, max_value=max_value) + if sd_config['type'] == 'split-lognormal': + loc = sd_config['mean'] if 'mean' in sd_config else sd_config['loc'] + scale_1 = sd_config['std_1'] if 'std_1' in sd_config else sd_config['scale_1'] + scale_2 = sd_config['std_2'] if 'std_2' in sd_config else sd_config['scale_2'] + return partial(utils.rand_split_log_normal, loc=loc, scale_1=scale_1, scale_2=scale_2) + raise ValueError('Unknown sample density type') diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/evaluation.py b/sd/stablediffusion/src/k-diffusion/k_diffusion/evaluation.py new file mode 100644 index 0000000000000000000000000000000000000000..2c34bbf1656854d9cf233b7620b684e44b30de82 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/k_diffusion/evaluation.py @@ -0,0 +1,134 @@ +import math +import os +from pathlib import Path + +from cleanfid.inception_torchscript import InceptionV3W +import clip +from resize_right import resize +import torch +from torch import nn +from torch.nn import functional as F +from torchvision import transforms +from tqdm.auto import trange + +from . import utils + + +class InceptionV3FeatureExtractor(nn.Module): + def __init__(self, device='cpu'): + super().__init__() + path = Path(os.environ.get('XDG_CACHE_HOME', Path.home() / '.cache')) / 'k-diffusion' + url = 'https://nvlabs-fi-cdn.nvidia.com/stylegan2-ada-pytorch/pretrained/metrics/inception-2015-12-05.pt' + digest = 'f58cb9b6ec323ed63459aa4fb441fe750cfe39fafad6da5cb504a16f19e958f4' + utils.download_file(path / 'inception-2015-12-05.pt', url, digest) + self.model = InceptionV3W(str(path), resize_inside=False).to(device) + self.size = (299, 299) + + def forward(self, x): + if x.shape[2:4] != self.size: + x = resize(x, out_shape=self.size, pad_mode='reflect') + if x.shape[1] == 1: + x = torch.cat([x] * 3, dim=1) + x = (x * 127.5 + 127.5).clamp(0, 255) + return self.model(x) + + +class CLIPFeatureExtractor(nn.Module): + def __init__(self, name='ViT-L/14@336px', device='cpu'): + super().__init__() + self.model = clip.load(name, device=device)[0].eval().requires_grad_(False) + self.normalize = transforms.Normalize(mean=(0.48145466, 0.4578275, 0.40821073), + std=(0.26862954, 0.26130258, 0.27577711)) + self.size = (self.model.visual.input_resolution, self.model.visual.input_resolution) + + def forward(self, x): + if x.shape[2:4] != self.size: + x = resize(x.add(1).div(2), out_shape=self.size, pad_mode='reflect').clamp(0, 1) + x = self.normalize(x) + x = self.model.encode_image(x).float() + x = F.normalize(x) * x.shape[1] ** 0.5 + return x + + +def compute_features(accelerator, sample_fn, extractor_fn, n, batch_size): + n_per_proc = math.ceil(n / accelerator.num_processes) + feats_all = [] + try: + for i in trange(0, n_per_proc, batch_size, disable=not accelerator.is_main_process): + cur_batch_size = min(n - i, batch_size) + samples = sample_fn(cur_batch_size)[:cur_batch_size] + feats_all.append(accelerator.gather(extractor_fn(samples))) + except StopIteration: + pass + return torch.cat(feats_all)[:n] + + +def polynomial_kernel(x, y): + d = x.shape[-1] + dot = x @ y.transpose(-2, -1) + return (dot / d + 1) ** 3 + + +def squared_mmd(x, y, kernel=polynomial_kernel): + m = x.shape[-2] + n = y.shape[-2] + kxx = kernel(x, x) + kyy = kernel(y, y) + kxy = kernel(x, y) + kxx_sum = kxx.sum([-1, -2]) - kxx.diagonal(dim1=-1, dim2=-2).sum(-1) + kyy_sum = kyy.sum([-1, -2]) - kyy.diagonal(dim1=-1, dim2=-2).sum(-1) + kxy_sum = kxy.sum([-1, -2]) + term_1 = kxx_sum / m / (m - 1) + term_2 = kyy_sum / n / (n - 1) + term_3 = kxy_sum * 2 / m / n + return term_1 + term_2 - term_3 + + +@utils.tf32_mode(matmul=False) +def kid(x, y, max_size=5000): + x_size, y_size = x.shape[0], y.shape[0] + n_partitions = math.ceil(max(x_size / max_size, y_size / max_size)) + total_mmd = x.new_zeros([]) + for i in range(n_partitions): + cur_x = x[round(i * x_size / n_partitions):round((i + 1) * x_size / n_partitions)] + cur_y = y[round(i * y_size / n_partitions):round((i + 1) * y_size / n_partitions)] + total_mmd = total_mmd + squared_mmd(cur_x, cur_y) + return total_mmd / n_partitions + + +class _MatrixSquareRootEig(torch.autograd.Function): + @staticmethod + def forward(ctx, a): + vals, vecs = torch.linalg.eigh(a) + ctx.save_for_backward(vals, vecs) + return vecs @ vals.abs().sqrt().diag_embed() @ vecs.transpose(-2, -1) + + @staticmethod + def backward(ctx, grad_output): + vals, vecs = ctx.saved_tensors + d = vals.abs().sqrt().unsqueeze(-1).repeat_interleave(vals.shape[-1], -1) + vecs_t = vecs.transpose(-2, -1) + return vecs @ (vecs_t @ grad_output @ vecs / (d + d.transpose(-2, -1))) @ vecs_t + + +def sqrtm_eig(a): + if a.ndim < 2: + raise RuntimeError('tensor of matrices must have at least 2 dimensions') + if a.shape[-2] != a.shape[-1]: + raise RuntimeError('tensor must be batches of square matrices') + return _MatrixSquareRootEig.apply(a) + + +@utils.tf32_mode(matmul=False) +def fid(x, y, eps=1e-8): + x_mean = x.mean(dim=0) + y_mean = y.mean(dim=0) + mean_term = (x_mean - y_mean).pow(2).sum() + x_cov = torch.cov(x.T) + y_cov = torch.cov(y.T) + eps_eye = torch.eye(x_cov.shape[0], device=x_cov.device, dtype=x_cov.dtype) * eps + x_cov = x_cov + eps_eye + y_cov = y_cov + eps_eye + x_cov_sqrt = sqrtm_eig(x_cov) + cov_term = torch.trace(x_cov + y_cov - 2 * sqrtm_eig(x_cov_sqrt @ y_cov @ x_cov_sqrt)) + return mean_term + cov_term diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/external.py b/sd/stablediffusion/src/k-diffusion/k_diffusion/external.py new file mode 100644 index 0000000000000000000000000000000000000000..79b51cec41c52f054775f26c26cf63414d588aef --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/k_diffusion/external.py @@ -0,0 +1,177 @@ +import math + +import torch +from torch import nn + +from . import sampling, utils + + +class VDenoiser(nn.Module): + """A v-diffusion-pytorch model wrapper for k-diffusion.""" + + def __init__(self, inner_model): + super().__init__() + self.inner_model = inner_model + self.sigma_data = 1. + + def get_scalings(self, sigma): + c_skip = self.sigma_data ** 2 / (sigma ** 2 + self.sigma_data ** 2) + c_out = -sigma * self.sigma_data / (sigma ** 2 + self.sigma_data ** 2) ** 0.5 + c_in = 1 / (sigma ** 2 + self.sigma_data ** 2) ** 0.5 + return c_skip, c_out, c_in + + def sigma_to_t(self, sigma): + return sigma.atan() / math.pi * 2 + + def t_to_sigma(self, t): + return (t * math.pi / 2).tan() + + def loss(self, input, noise, sigma, **kwargs): + c_skip, c_out, c_in = [utils.append_dims(x, input.ndim) for x in self.get_scalings(sigma)] + noised_input = input + noise * utils.append_dims(sigma, input.ndim) + model_output = self.inner_model(noised_input * c_in, self.sigma_to_t(sigma), **kwargs) + target = (input - c_skip * noised_input) / c_out + return (model_output - target).pow(2).flatten(1).mean(1) + + def forward(self, input, sigma, **kwargs): + c_skip, c_out, c_in = [utils.append_dims(x, input.ndim) for x in self.get_scalings(sigma)] + return self.inner_model(input * c_in, self.sigma_to_t(sigma), **kwargs) * c_out + input * c_skip + + +class DiscreteSchedule(nn.Module): + """A mapping between continuous noise levels (sigmas) and a list of discrete noise + levels.""" + + def __init__(self, sigmas, quantize): + super().__init__() + self.register_buffer('sigmas', sigmas) + self.register_buffer('log_sigmas', sigmas.log()) + self.quantize = quantize + + @property + def sigma_min(self): + return self.sigmas[0] + + @property + def sigma_max(self): + return self.sigmas[-1] + + def get_sigmas(self, n=None): + if n is None: + return sampling.append_zero(self.sigmas.flip(0)) + t_max = len(self.sigmas) - 1 + t = torch.linspace(t_max, 0, n, device=self.sigmas.device) + return sampling.append_zero(self.t_to_sigma(t)) + + def sigma_to_t(self, sigma, quantize=None): + quantize = self.quantize if quantize is None else quantize + log_sigma = sigma.log() + dists = log_sigma - self.log_sigmas[:, None] + if quantize: + return dists.abs().argmin(dim=0).view(sigma.shape) + low_idx = dists.ge(0).cumsum(dim=0).argmax(dim=0).clamp(max=self.log_sigmas.shape[0] - 2) + high_idx = low_idx + 1 + low, high = self.log_sigmas[low_idx], self.log_sigmas[high_idx] + w = (low - log_sigma) / (low - high) + w = w.clamp(0, 1) + t = (1 - w) * low_idx + w * high_idx + return t.view(sigma.shape) + + def t_to_sigma(self, t): + t = t.float() + low_idx, high_idx, w = t.floor().long(), t.ceil().long(), t.frac() + log_sigma = (1 - w) * self.log_sigmas[low_idx] + w * self.log_sigmas[high_idx] + return log_sigma.exp() + + +class DiscreteEpsDDPMDenoiser(DiscreteSchedule): + """A wrapper for discrete schedule DDPM models that output eps (the predicted + noise).""" + + def __init__(self, model, alphas_cumprod, quantize): + super().__init__(((1 - alphas_cumprod) / alphas_cumprod) ** 0.5, quantize) + self.inner_model = model + self.sigma_data = 1. + + def get_scalings(self, sigma): + c_out = -sigma + c_in = 1 / (sigma ** 2 + self.sigma_data ** 2) ** 0.5 + return c_out, c_in + + def get_eps(self, *args, **kwargs): + return self.inner_model(*args, **kwargs) + + def loss(self, input, noise, sigma, **kwargs): + c_out, c_in = [utils.append_dims(x, input.ndim) for x in self.get_scalings(sigma)] + noised_input = input + noise * utils.append_dims(sigma, input.ndim) + eps = self.get_eps(noised_input * c_in, self.sigma_to_t(sigma), **kwargs) + return (eps - noise).pow(2).flatten(1).mean(1) + + def forward(self, input, sigma, **kwargs): + c_out, c_in = [utils.append_dims(x, input.ndim) for x in self.get_scalings(sigma)] + eps = self.get_eps(input * c_in, self.sigma_to_t(sigma), **kwargs) + return input + eps * c_out + + +class OpenAIDenoiser(DiscreteEpsDDPMDenoiser): + """A wrapper for OpenAI diffusion models.""" + + def __init__(self, model, diffusion, quantize=False, has_learned_sigmas=True, device='cpu'): + alphas_cumprod = torch.tensor(diffusion.alphas_cumprod, device=device, dtype=torch.float32) + super().__init__(model, alphas_cumprod, quantize=quantize) + self.has_learned_sigmas = has_learned_sigmas + + def get_eps(self, *args, **kwargs): + model_output = self.inner_model(*args, **kwargs) + if self.has_learned_sigmas: + return model_output.chunk(2, dim=1)[0] + return model_output + + +class CompVisDenoiser(DiscreteEpsDDPMDenoiser): + """A wrapper for CompVis diffusion models.""" + + def __init__(self, model, quantize=False, device='cpu'): + super().__init__(model, model.alphas_cumprod, quantize=quantize) + + def get_eps(self, *args, **kwargs): + return self.inner_model.apply_model(*args, **kwargs) + + +class DiscreteVDDPMDenoiser(DiscreteSchedule): + """A wrapper for discrete schedule DDPM models that output v.""" + + def __init__(self, model, alphas_cumprod, quantize): + super().__init__(((1 - alphas_cumprod) / alphas_cumprod) ** 0.5, quantize) + self.inner_model = model + self.sigma_data = 1. + + def get_scalings(self, sigma): + c_skip = self.sigma_data ** 2 / (sigma ** 2 + self.sigma_data ** 2) + c_out = -sigma * self.sigma_data / (sigma ** 2 + self.sigma_data ** 2) ** 0.5 + c_in = 1 / (sigma ** 2 + self.sigma_data ** 2) ** 0.5 + return c_skip, c_out, c_in + + def get_v(self, *args, **kwargs): + return self.inner_model(*args, **kwargs) + + def loss(self, input, noise, sigma, **kwargs): + c_skip, c_out, c_in = [utils.append_dims(x, input.ndim) for x in self.get_scalings(sigma)] + noised_input = input + noise * utils.append_dims(sigma, input.ndim) + model_output = self.get_v(noised_input * c_in, self.sigma_to_t(sigma), **kwargs) + target = (input - c_skip * noised_input) / c_out + return (model_output - target).pow(2).flatten(1).mean(1) + + def forward(self, input, sigma, **kwargs): + c_skip, c_out, c_in = [utils.append_dims(x, input.ndim) for x in self.get_scalings(sigma)] + return self.get_v(input * c_in, self.sigma_to_t(sigma), **kwargs) * c_out + input * c_skip + + +class CompVisVDenoiser(DiscreteVDDPMDenoiser): + """A wrapper for CompVis diffusion models that output v.""" + + def __init__(self, model, quantize=False, device='cpu'): + super().__init__(model, model.alphas_cumprod, quantize=quantize) + + def get_v(self, x, t, cond, **kwargs): + return self.inner_model.apply_model(x, t, cond) diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/gns.py b/sd/stablediffusion/src/k-diffusion/k_diffusion/gns.py new file mode 100644 index 0000000000000000000000000000000000000000..dcb7b8d8a9aeae38a7f961c63f66cca4ef90a9e7 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/k_diffusion/gns.py @@ -0,0 +1,99 @@ +import torch +from torch import nn + + +class DDPGradientStatsHook: + def __init__(self, ddp_module): + try: + ddp_module.register_comm_hook(self, self._hook_fn) + except AttributeError: + raise ValueError('DDPGradientStatsHook does not support non-DDP wrapped modules') + self._clear_state() + + def _clear_state(self): + self.bucket_sq_norms_small_batch = [] + self.bucket_sq_norms_large_batch = [] + + @staticmethod + def _hook_fn(self, bucket): + buf = bucket.buffer() + self.bucket_sq_norms_small_batch.append(buf.pow(2).sum()) + fut = torch.distributed.all_reduce(buf, op=torch.distributed.ReduceOp.AVG, async_op=True).get_future() + def callback(fut): + buf = fut.value()[0] + self.bucket_sq_norms_large_batch.append(buf.pow(2).sum()) + return buf + return fut.then(callback) + + def get_stats(self): + sq_norm_small_batch = sum(self.bucket_sq_norms_small_batch) + sq_norm_large_batch = sum(self.bucket_sq_norms_large_batch) + self._clear_state() + stats = torch.stack([sq_norm_small_batch, sq_norm_large_batch]) + torch.distributed.all_reduce(stats, op=torch.distributed.ReduceOp.AVG) + return stats[0].item(), stats[1].item() + + +class GradientNoiseScale: + """Calculates the gradient noise scale (1 / SNR), or critical batch size, + from _An Empirical Model of Large-Batch Training_, + https://arxiv.org/abs/1812.06162). + + Args: + beta (float): The decay factor for the exponential moving averages used to + calculate the gradient noise scale. + Default: 0.9998 + eps (float): Added for numerical stability. + Default: 1e-8 + """ + + def __init__(self, beta=0.9998, eps=1e-8): + self.beta = beta + self.eps = eps + self.ema_sq_norm = 0. + self.ema_var = 0. + self.beta_cumprod = 1. + self.gradient_noise_scale = float('nan') + + def state_dict(self): + """Returns the state of the object as a :class:`dict`.""" + return dict(self.__dict__.items()) + + def load_state_dict(self, state_dict): + """Loads the object's state. + Args: + state_dict (dict): object state. Should be an object returned + from a call to :meth:`state_dict`. + """ + self.__dict__.update(state_dict) + + def update(self, sq_norm_small_batch, sq_norm_large_batch, n_small_batch, n_large_batch): + """Updates the state with a new batch's gradient statistics, and returns the + current gradient noise scale. + + Args: + sq_norm_small_batch (float): The mean of the squared 2-norms of microbatch or + per sample gradients. + sq_norm_large_batch (float): The squared 2-norm of the mean of the microbatch or + per sample gradients. + n_small_batch (int): The batch size of the individual microbatch or per sample + gradients (1 if per sample). + n_large_batch (int): The total batch size of the mean of the microbatch or + per sample gradients. + """ + est_sq_norm = (n_large_batch * sq_norm_large_batch - n_small_batch * sq_norm_small_batch) / (n_large_batch - n_small_batch) + est_var = (sq_norm_small_batch - sq_norm_large_batch) / (1 / n_small_batch - 1 / n_large_batch) + self.ema_sq_norm = self.beta * self.ema_sq_norm + (1 - self.beta) * est_sq_norm + self.ema_var = self.beta * self.ema_var + (1 - self.beta) * est_var + self.beta_cumprod *= self.beta + self.gradient_noise_scale = max(self.ema_var, self.eps) / max(self.ema_sq_norm, self.eps) + return self.gradient_noise_scale + + def get_gns(self): + """Returns the current gradient noise scale.""" + return self.gradient_noise_scale + + def get_stats(self): + """Returns the current (debiased) estimates of the squared mean gradient + and gradient variance.""" + return self.ema_sq_norm / (1 - self.beta_cumprod), self.ema_var / (1 - self.beta_cumprod) diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/layers.py b/sd/stablediffusion/src/k-diffusion/k_diffusion/layers.py new file mode 100644 index 0000000000000000000000000000000000000000..cdeba0ad68f584261bd88de608e843a350489544 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/k_diffusion/layers.py @@ -0,0 +1,246 @@ +import math + +from einops import rearrange, repeat +import torch +from torch import nn +from torch.nn import functional as F + +from . import utils + +# Karras et al. preconditioned denoiser + +class Denoiser(nn.Module): + """A Karras et al. preconditioner for denoising diffusion models.""" + + def __init__(self, inner_model, sigma_data=1.): + super().__init__() + self.inner_model = inner_model + self.sigma_data = sigma_data + + def get_scalings(self, sigma): + c_skip = self.sigma_data ** 2 / (sigma ** 2 + self.sigma_data ** 2) + c_out = sigma * self.sigma_data / (sigma ** 2 + self.sigma_data ** 2) ** 0.5 + c_in = 1 / (sigma ** 2 + self.sigma_data ** 2) ** 0.5 + return c_skip, c_out, c_in + + def loss(self, input, noise, sigma, **kwargs): + c_skip, c_out, c_in = [utils.append_dims(x, input.ndim) for x in self.get_scalings(sigma)] + noised_input = input + noise * utils.append_dims(sigma, input.ndim) + model_output = self.inner_model(noised_input * c_in, sigma, **kwargs) + target = (input - c_skip * noised_input) / c_out + return (model_output - target).pow(2).flatten(1).mean(1) + + def forward(self, input, sigma, **kwargs): + c_skip, c_out, c_in = [utils.append_dims(x, input.ndim) for x in self.get_scalings(sigma)] + return self.inner_model(input * c_in, sigma, **kwargs) * c_out + input * c_skip + + +class DenoiserWithVariance(Denoiser): + def loss(self, input, noise, sigma, **kwargs): + c_skip, c_out, c_in = [utils.append_dims(x, input.ndim) for x in self.get_scalings(sigma)] + noised_input = input + noise * utils.append_dims(sigma, input.ndim) + model_output, logvar = self.inner_model(noised_input * c_in, sigma, return_variance=True, **kwargs) + logvar = utils.append_dims(logvar, model_output.ndim) + target = (input - c_skip * noised_input) / c_out + losses = ((model_output - target) ** 2 / logvar.exp() + logvar) / 2 + return losses.flatten(1).mean(1) + + +# Residual blocks + +class ResidualBlock(nn.Module): + def __init__(self, *main, skip=None): + super().__init__() + self.main = nn.Sequential(*main) + self.skip = skip if skip else nn.Identity() + + def forward(self, input): + return self.main(input) + self.skip(input) + + +# Noise level (and other) conditioning + +class ConditionedModule(nn.Module): + pass + + +class UnconditionedModule(ConditionedModule): + def __init__(self, module): + super().__init__() + self.module = module + + def forward(self, input, cond=None): + return self.module(input) + + +class ConditionedSequential(nn.Sequential, ConditionedModule): + def forward(self, input, cond): + for module in self: + if isinstance(module, ConditionedModule): + input = module(input, cond) + else: + input = module(input) + return input + + +class ConditionedResidualBlock(ConditionedModule): + def __init__(self, *main, skip=None): + super().__init__() + self.main = ConditionedSequential(*main) + self.skip = skip if skip else nn.Identity() + + def forward(self, input, cond): + skip = self.skip(input, cond) if isinstance(self.skip, ConditionedModule) else self.skip(input) + return self.main(input, cond) + skip + + +class AdaGN(ConditionedModule): + def __init__(self, feats_in, c_out, num_groups, eps=1e-5, cond_key='cond'): + super().__init__() + self.num_groups = num_groups + self.eps = eps + self.cond_key = cond_key + self.mapper = nn.Linear(feats_in, c_out * 2) + + def forward(self, input, cond): + weight, bias = self.mapper(cond[self.cond_key]).chunk(2, dim=-1) + input = F.group_norm(input, self.num_groups, eps=self.eps) + return torch.addcmul(utils.append_dims(bias, input.ndim), input, utils.append_dims(weight, input.ndim) + 1) + + +# Attention + +class SelfAttention2d(ConditionedModule): + def __init__(self, c_in, n_head, norm, dropout_rate=0.): + super().__init__() + assert c_in % n_head == 0 + self.norm_in = norm(c_in) + self.n_head = n_head + self.qkv_proj = nn.Conv2d(c_in, c_in * 3, 1) + self.out_proj = nn.Conv2d(c_in, c_in, 1) + self.dropout = nn.Dropout(dropout_rate) + + def forward(self, input, cond): + n, c, h, w = input.shape + qkv = self.qkv_proj(self.norm_in(input, cond)) + qkv = qkv.view([n, self.n_head * 3, c // self.n_head, h * w]).transpose(2, 3) + q, k, v = qkv.chunk(3, dim=1) + scale = k.shape[3] ** -0.25 + att = ((q * scale) @ (k.transpose(2, 3) * scale)).softmax(3) + att = self.dropout(att) + y = (att @ v).transpose(2, 3).contiguous().view([n, c, h, w]) + return input + self.out_proj(y) + + +class CrossAttention2d(ConditionedModule): + def __init__(self, c_dec, c_enc, n_head, norm_dec, dropout_rate=0., + cond_key='cross', cond_key_padding='cross_padding'): + super().__init__() + assert c_dec % n_head == 0 + self.cond_key = cond_key + self.cond_key_padding = cond_key_padding + self.norm_enc = nn.LayerNorm(c_enc) + self.norm_dec = norm_dec(c_dec) + self.n_head = n_head + self.q_proj = nn.Conv2d(c_dec, c_dec, 1) + self.kv_proj = nn.Linear(c_enc, c_dec * 2) + self.out_proj = nn.Conv2d(c_dec, c_dec, 1) + self.dropout = nn.Dropout(dropout_rate) + + def forward(self, input, cond): + n, c, h, w = input.shape + q = self.q_proj(self.norm_dec(input, cond)) + q = q.view([n, self.n_head, c // self.n_head, h * w]).transpose(2, 3) + kv = self.kv_proj(self.norm_enc(cond[self.cond_key])) + kv = kv.view([n, -1, self.n_head * 2, c // self.n_head]).transpose(1, 2) + k, v = kv.chunk(2, dim=1) + scale = k.shape[3] ** -0.25 + att = ((q * scale) @ (k.transpose(2, 3) * scale)) + att = att - (cond[self.cond_key_padding][:, None, None, :]) * 10000 + att = att.softmax(3) + att = self.dropout(att) + y = (att @ v).transpose(2, 3) + y = y.contiguous().view([n, c, h, w]) + return input + self.out_proj(y) + + +# Downsampling/upsampling + +_kernels = { + 'linear': + [1 / 8, 3 / 8, 3 / 8, 1 / 8], + 'cubic': + [-0.01171875, -0.03515625, 0.11328125, 0.43359375, + 0.43359375, 0.11328125, -0.03515625, -0.01171875], + 'lanczos3': + [0.003689131001010537, 0.015056144446134567, -0.03399861603975296, + -0.066637322306633, 0.13550527393817902, 0.44638532400131226, + 0.44638532400131226, 0.13550527393817902, -0.066637322306633, + -0.03399861603975296, 0.015056144446134567, 0.003689131001010537] +} +_kernels['bilinear'] = _kernels['linear'] +_kernels['bicubic'] = _kernels['cubic'] + + +class Downsample2d(nn.Module): + def __init__(self, kernel='linear', pad_mode='reflect'): + super().__init__() + self.pad_mode = pad_mode + kernel_1d = torch.tensor([_kernels[kernel]]) + self.pad = kernel_1d.shape[1] // 2 - 1 + self.register_buffer('kernel', kernel_1d.T @ kernel_1d) + + def forward(self, x): + x = F.pad(x, (self.pad,) * 4, self.pad_mode) + weight = x.new_zeros([x.shape[1], x.shape[1], self.kernel.shape[0], self.kernel.shape[1]]) + indices = torch.arange(x.shape[1], device=x.device) + weight[indices, indices] = self.kernel.to(weight) + return F.conv2d(x, weight, stride=2) + + +class Upsample2d(nn.Module): + def __init__(self, kernel='linear', pad_mode='reflect'): + super().__init__() + self.pad_mode = pad_mode + kernel_1d = torch.tensor([_kernels[kernel]]) * 2 + self.pad = kernel_1d.shape[1] // 2 - 1 + self.register_buffer('kernel', kernel_1d.T @ kernel_1d) + + def forward(self, x): + x = F.pad(x, ((self.pad + 1) // 2,) * 4, self.pad_mode) + weight = x.new_zeros([x.shape[1], x.shape[1], self.kernel.shape[0], self.kernel.shape[1]]) + indices = torch.arange(x.shape[1], device=x.device) + weight[indices, indices] = self.kernel.to(weight) + return F.conv_transpose2d(x, weight, stride=2, padding=self.pad * 2 + 1) + + +# Embeddings + +class FourierFeatures(nn.Module): + def __init__(self, in_features, out_features, std=1.): + super().__init__() + assert out_features % 2 == 0 + self.register_buffer('weight', torch.randn([out_features // 2, in_features]) * std) + + def forward(self, input): + f = 2 * math.pi * input @ self.weight.T + return torch.cat([f.cos(), f.sin()], dim=-1) + + +# U-Nets + +class UNet(ConditionedModule): + def __init__(self, d_blocks, u_blocks, skip_stages=0): + super().__init__() + self.d_blocks = nn.ModuleList(d_blocks) + self.u_blocks = nn.ModuleList(u_blocks) + self.skip_stages = skip_stages + + def forward(self, input, cond): + skips = [] + for block in self.d_blocks[self.skip_stages:]: + input = block(input, cond) + skips.append(input) + for i, (block, skip) in enumerate(zip(self.u_blocks, reversed(skips))): + input = block(input, cond, skip if i > 0 else None) + return input diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/models/__init__.py b/sd/stablediffusion/src/k-diffusion/k_diffusion/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..82608ff1de6137b31eeaf8de6814df6a7e35606a --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/k_diffusion/models/__init__.py @@ -0,0 +1 @@ +from .image_v1 import ImageDenoiserModelV1 diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/models/__pycache__/__init__.cpython-39.pyc b/sd/stablediffusion/src/k-diffusion/k_diffusion/models/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..296b0e84bdd5b12a2ce78bc86ebc7f33be7de3fe Binary files /dev/null and b/sd/stablediffusion/src/k-diffusion/k_diffusion/models/__pycache__/__init__.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/models/__pycache__/image_v1.cpython-39.pyc b/sd/stablediffusion/src/k-diffusion/k_diffusion/models/__pycache__/image_v1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..747b1a89a94ebc620a21f3394700fa16d273d305 Binary files /dev/null and b/sd/stablediffusion/src/k-diffusion/k_diffusion/models/__pycache__/image_v1.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/models/image_v1.py b/sd/stablediffusion/src/k-diffusion/k_diffusion/models/image_v1.py new file mode 100644 index 0000000000000000000000000000000000000000..9ffd5f2c4d6c9d086107d5fac67452419696c723 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/k_diffusion/models/image_v1.py @@ -0,0 +1,156 @@ +import math + +import torch +from torch import nn +from torch.nn import functional as F + +from .. import layers, utils + + +def orthogonal_(module): + nn.init.orthogonal_(module.weight) + return module + + +class ResConvBlock(layers.ConditionedResidualBlock): + def __init__(self, feats_in, c_in, c_mid, c_out, group_size=32, dropout_rate=0.): + skip = None if c_in == c_out else orthogonal_(nn.Conv2d(c_in, c_out, 1, bias=False)) + super().__init__( + layers.AdaGN(feats_in, c_in, max(1, c_in // group_size)), + nn.GELU(), + nn.Conv2d(c_in, c_mid, 3, padding=1), + nn.Dropout2d(dropout_rate, inplace=True), + layers.AdaGN(feats_in, c_mid, max(1, c_mid // group_size)), + nn.GELU(), + nn.Conv2d(c_mid, c_out, 3, padding=1), + nn.Dropout2d(dropout_rate, inplace=True), + skip=skip) + + +class DBlock(layers.ConditionedSequential): + def __init__(self, n_layers, feats_in, c_in, c_mid, c_out, group_size=32, head_size=64, dropout_rate=0., downsample=False, self_attn=False, cross_attn=False, c_enc=0): + modules = [nn.Identity()] + for i in range(n_layers): + my_c_in = c_in if i == 0 else c_mid + my_c_out = c_mid if i < n_layers - 1 else c_out + modules.append(ResConvBlock(feats_in, my_c_in, c_mid, my_c_out, group_size, dropout_rate)) + if self_attn: + norm = lambda c_in: layers.AdaGN(feats_in, c_in, max(1, my_c_out // group_size)) + modules.append(layers.SelfAttention2d(my_c_out, max(1, my_c_out // head_size), norm, dropout_rate)) + if cross_attn: + norm = lambda c_in: layers.AdaGN(feats_in, c_in, max(1, my_c_out // group_size)) + modules.append(layers.CrossAttention2d(my_c_out, c_enc, max(1, my_c_out // head_size), norm, dropout_rate)) + super().__init__(*modules) + self.set_downsample(downsample) + + def set_downsample(self, downsample): + self[0] = layers.Downsample2d() if downsample else nn.Identity() + return self + + +class UBlock(layers.ConditionedSequential): + def __init__(self, n_layers, feats_in, c_in, c_mid, c_out, group_size=32, head_size=64, dropout_rate=0., upsample=False, self_attn=False, cross_attn=False, c_enc=0): + modules = [] + for i in range(n_layers): + my_c_in = c_in if i == 0 else c_mid + my_c_out = c_mid if i < n_layers - 1 else c_out + modules.append(ResConvBlock(feats_in, my_c_in, c_mid, my_c_out, group_size, dropout_rate)) + if self_attn: + norm = lambda c_in: layers.AdaGN(feats_in, c_in, max(1, my_c_out // group_size)) + modules.append(layers.SelfAttention2d(my_c_out, max(1, my_c_out // head_size), norm, dropout_rate)) + if cross_attn: + norm = lambda c_in: layers.AdaGN(feats_in, c_in, max(1, my_c_out // group_size)) + modules.append(layers.CrossAttention2d(my_c_out, c_enc, max(1, my_c_out // head_size), norm, dropout_rate)) + modules.append(nn.Identity()) + super().__init__(*modules) + self.set_upsample(upsample) + + def forward(self, input, cond, skip=None): + if skip is not None: + input = torch.cat([input, skip], dim=1) + return super().forward(input, cond) + + def set_upsample(self, upsample): + self[-1] = layers.Upsample2d() if upsample else nn.Identity() + return self + + +class MappingNet(nn.Sequential): + def __init__(self, feats_in, feats_out, n_layers=2): + layers = [] + for i in range(n_layers): + layers.append(orthogonal_(nn.Linear(feats_in if i == 0 else feats_out, feats_out))) + layers.append(nn.GELU()) + super().__init__(*layers) + + +class ImageDenoiserModelV1(nn.Module): + def __init__(self, c_in, feats_in, depths, channels, self_attn_depths, cross_attn_depths=None, mapping_cond_dim=0, unet_cond_dim=0, cross_cond_dim=0, dropout_rate=0., patch_size=1, skip_stages=0, has_variance=False): + super().__init__() + self.c_in = c_in + self.channels = channels + self.unet_cond_dim = unet_cond_dim + self.patch_size = patch_size + self.has_variance = has_variance + self.timestep_embed = layers.FourierFeatures(1, feats_in) + if mapping_cond_dim > 0: + self.mapping_cond = nn.Linear(mapping_cond_dim, feats_in, bias=False) + self.mapping = MappingNet(feats_in, feats_in) + self.proj_in = nn.Conv2d((c_in + unet_cond_dim) * self.patch_size ** 2, channels[max(0, skip_stages - 1)], 1) + self.proj_out = nn.Conv2d(channels[max(0, skip_stages - 1)], c_in * self.patch_size ** 2 + (1 if self.has_variance else 0), 1) + nn.init.zeros_(self.proj_out.weight) + nn.init.zeros_(self.proj_out.bias) + if cross_cond_dim == 0: + cross_attn_depths = [False] * len(self_attn_depths) + d_blocks, u_blocks = [], [] + for i in range(len(depths)): + my_c_in = channels[max(0, i - 1)] + d_blocks.append(DBlock(depths[i], feats_in, my_c_in, channels[i], channels[i], downsample=i > skip_stages, self_attn=self_attn_depths[i], cross_attn=cross_attn_depths[i], c_enc=cross_cond_dim, dropout_rate=dropout_rate)) + for i in range(len(depths)): + my_c_in = channels[i] * 2 if i < len(depths) - 1 else channels[i] + my_c_out = channels[max(0, i - 1)] + u_blocks.append(UBlock(depths[i], feats_in, my_c_in, channels[i], my_c_out, upsample=i > skip_stages, self_attn=self_attn_depths[i], cross_attn=cross_attn_depths[i], c_enc=cross_cond_dim, dropout_rate=dropout_rate)) + self.u_net = layers.UNet(d_blocks, reversed(u_blocks), skip_stages=skip_stages) + + def forward(self, input, sigma, mapping_cond=None, unet_cond=None, cross_cond=None, cross_cond_padding=None, return_variance=False): + c_noise = sigma.log() / 4 + timestep_embed = self.timestep_embed(utils.append_dims(c_noise, 2)) + mapping_cond_embed = torch.zeros_like(timestep_embed) if mapping_cond is None else self.mapping_cond(mapping_cond) + mapping_out = self.mapping(timestep_embed + mapping_cond_embed) + cond = {'cond': mapping_out} + if unet_cond is not None: + input = torch.cat([input, unet_cond], dim=1) + if cross_cond is not None: + cond['cross'] = cross_cond + cond['cross_padding'] = cross_cond_padding + if self.patch_size > 1: + input = F.pixel_unshuffle(input, self.patch_size) + input = self.proj_in(input) + input = self.u_net(input, cond) + input = self.proj_out(input) + if self.has_variance: + input, logvar = input[:, :-1], input[:, -1].flatten(1).mean(1) + if self.patch_size > 1: + input = F.pixel_shuffle(input, self.patch_size) + if self.has_variance and return_variance: + return input, logvar + return input + + def set_skip_stages(self, skip_stages): + self.proj_in = nn.Conv2d(self.proj_in.in_channels, self.channels[max(0, skip_stages - 1)], 1) + self.proj_out = nn.Conv2d(self.channels[max(0, skip_stages - 1)], self.proj_out.out_channels, 1) + nn.init.zeros_(self.proj_out.weight) + nn.init.zeros_(self.proj_out.bias) + self.u_net.skip_stages = skip_stages + for i, block in enumerate(self.u_net.d_blocks): + block.set_downsample(i > skip_stages) + for i, block in enumerate(reversed(self.u_net.u_blocks)): + block.set_upsample(i > skip_stages) + return self + + def set_patch_size(self, patch_size): + self.patch_size = patch_size + self.proj_in = nn.Conv2d((self.c_in + self.unet_cond_dim) * self.patch_size ** 2, self.channels[max(0, self.u_net.skip_stages - 1)], 1) + self.proj_out = nn.Conv2d(self.channels[max(0, self.u_net.skip_stages - 1)], self.c_in * self.patch_size ** 2 + (1 if self.has_variance else 0), 1) + nn.init.zeros_(self.proj_out.weight) + nn.init.zeros_(self.proj_out.bias) diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/sampling.py b/sd/stablediffusion/src/k-diffusion/k_diffusion/sampling.py new file mode 100644 index 0000000000000000000000000000000000000000..f050f88e43bf5d0073cddbbb9f085f7137835fd1 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/k_diffusion/sampling.py @@ -0,0 +1,607 @@ +import math + +from scipy import integrate +import torch +from torch import nn +from torchdiffeq import odeint +import torchsde +from tqdm.auto import trange, tqdm + +from . import utils + + +def append_zero(x): + return torch.cat([x, x.new_zeros([1])]) + + +def get_sigmas_karras(n, sigma_min, sigma_max, rho=7., device='cpu'): + """Constructs the noise schedule of Karras et al. (2022).""" + ramp = torch.linspace(0, 1, n) + min_inv_rho = sigma_min ** (1 / rho) + max_inv_rho = sigma_max ** (1 / rho) + sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho)) ** rho + return append_zero(sigmas).to(device) + + +def get_sigmas_exponential(n, sigma_min, sigma_max, device='cpu'): + """Constructs an exponential noise schedule.""" + sigmas = torch.linspace(math.log(sigma_max), math.log(sigma_min), n, device=device).exp() + return append_zero(sigmas) + + +def get_sigmas_polyexponential(n, sigma_min, sigma_max, rho=1., device='cpu'): + """Constructs an polynomial in log sigma noise schedule.""" + ramp = torch.linspace(1, 0, n, device=device) ** rho + sigmas = torch.exp(ramp * (math.log(sigma_max) - math.log(sigma_min)) + math.log(sigma_min)) + return append_zero(sigmas) + + +def get_sigmas_vp(n, beta_d=19.9, beta_min=0.1, eps_s=1e-3, device='cpu'): + """Constructs a continuous VP noise schedule.""" + t = torch.linspace(1, eps_s, n, device=device) + sigmas = torch.sqrt(torch.exp(beta_d * t ** 2 / 2 + beta_min * t) - 1) + return append_zero(sigmas) + + +def to_d(x, sigma, denoised): + """Converts a denoiser output to a Karras ODE derivative.""" + return (x - denoised) / utils.append_dims(sigma, x.ndim) + + +def get_ancestral_step(sigma_from, sigma_to, eta=1.): + """Calculates the noise level (sigma_down) to step down to and the amount + of noise to add (sigma_up) when doing an ancestral sampling step.""" + if not eta: + return sigma_to, 0. + sigma_up = min(sigma_to, eta * (sigma_to ** 2 * (sigma_from ** 2 - sigma_to ** 2) / sigma_from ** 2) ** 0.5) + sigma_down = (sigma_to ** 2 - sigma_up ** 2) ** 0.5 + return sigma_down, sigma_up + + +def default_noise_sampler(x): + return lambda sigma, sigma_next: torch.randn_like(x) + + +class BatchedBrownianTree: + """A wrapper around torchsde.BrownianTree that enables batches of entropy.""" + + def __init__(self, x, t0, t1, seed=None, **kwargs): + t0, t1, self.sign = self.sort(t0, t1) + w0 = kwargs.get('w0', torch.zeros_like(x)) + if seed is None: + seed = torch.randint(0, 2 ** 63 - 1, []).item() + self.batched = True + try: + assert len(seed) == x.shape[0] + w0 = w0[0] + except TypeError: + seed = [seed] + self.batched = False + self.trees = [torchsde.BrownianTree(t0, w0, t1, entropy=s, **kwargs) for s in seed] + + @staticmethod + def sort(a, b): + return (a, b, 1) if a < b else (b, a, -1) + + def __call__(self, t0, t1): + t0, t1, sign = self.sort(t0, t1) + w = torch.stack([tree(t0, t1) for tree in self.trees]) * (self.sign * sign) + return w if self.batched else w[0] + + +class BrownianTreeNoiseSampler: + """A noise sampler backed by a torchsde.BrownianTree. + + Args: + x (Tensor): The tensor whose shape, device and dtype to use to generate + random samples. + sigma_min (float): The low end of the valid interval. + sigma_max (float): The high end of the valid interval. + seed (int or List[int]): The random seed. If a list of seeds is + supplied instead of a single integer, then the noise sampler will + use one BrownianTree per batch item, each with its own seed. + transform (callable): A function that maps sigma to the sampler's + internal timestep. + """ + + def __init__(self, x, sigma_min, sigma_max, seed=None, transform=lambda x: x): + self.transform = transform + t0, t1 = self.transform(torch.as_tensor(sigma_min)), self.transform(torch.as_tensor(sigma_max)) + self.tree = BatchedBrownianTree(x, t0, t1, seed) + + def __call__(self, sigma, sigma_next): + t0, t1 = self.transform(torch.as_tensor(sigma)), self.transform(torch.as_tensor(sigma_next)) + return self.tree(t0, t1) / (t1 - t0).abs().sqrt() + + +@torch.no_grad() +def sample_euler(model, x, sigmas, extra_args=None, callback=None, disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'), s_noise=1.): + """Implements Algorithm 2 (Euler steps) from Karras et al. (2022).""" + extra_args = {} if extra_args is None else extra_args + s_in = x.new_ones([x.shape[0]]) + for i in trange(len(sigmas) - 1, disable=disable): + gamma = min(s_churn / (len(sigmas) - 1), 2 ** 0.5 - 1) if s_tmin <= sigmas[i] <= s_tmax else 0. + eps = torch.randn_like(x) * s_noise + sigma_hat = sigmas[i] * (gamma + 1) + if gamma > 0: + x = x + eps * (sigma_hat ** 2 - sigmas[i] ** 2) ** 0.5 + denoised = model(x, sigma_hat * s_in, **extra_args) + d = to_d(x, sigma_hat, denoised) + if callback is not None: + callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigma_hat, 'denoised': denoised}) + dt = sigmas[i + 1] - sigma_hat + # Euler method + x = x + d * dt + return x + + +@torch.no_grad() +def sample_euler_ancestral(model, x, sigmas, extra_args=None, callback=None, disable=None, eta=1., s_noise=1., noise_sampler=None): + """Ancestral sampling with Euler method steps.""" + extra_args = {} if extra_args is None else extra_args + noise_sampler = default_noise_sampler(x) if noise_sampler is None else noise_sampler + s_in = x.new_ones([x.shape[0]]) + for i in trange(len(sigmas) - 1, disable=disable): + denoised = model(x, sigmas[i] * s_in, **extra_args) + sigma_down, sigma_up = get_ancestral_step(sigmas[i], sigmas[i + 1], eta=eta) + if callback is not None: + callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised}) + d = to_d(x, sigmas[i], denoised) + # Euler method + dt = sigma_down - sigmas[i] + x = x + d * dt + if sigmas[i + 1] > 0: + x = x + noise_sampler(sigmas[i], sigmas[i + 1]) * s_noise * sigma_up + return x + + +@torch.no_grad() +def sample_heun(model, x, sigmas, extra_args=None, callback=None, disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'), s_noise=1.): + """Implements Algorithm 2 (Heun steps) from Karras et al. (2022).""" + extra_args = {} if extra_args is None else extra_args + s_in = x.new_ones([x.shape[0]]) + for i in trange(len(sigmas) - 1, disable=disable): + gamma = min(s_churn / (len(sigmas) - 1), 2 ** 0.5 - 1) if s_tmin <= sigmas[i] <= s_tmax else 0. + eps = torch.randn_like(x) * s_noise + sigma_hat = sigmas[i] * (gamma + 1) + if gamma > 0: + x = x + eps * (sigma_hat ** 2 - sigmas[i] ** 2) ** 0.5 + denoised = model(x, sigma_hat * s_in, **extra_args) + d = to_d(x, sigma_hat, denoised) + if callback is not None: + callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigma_hat, 'denoised': denoised}) + dt = sigmas[i + 1] - sigma_hat + if sigmas[i + 1] == 0: + # Euler method + x = x + d * dt + else: + # Heun's method + x_2 = x + d * dt + denoised_2 = model(x_2, sigmas[i + 1] * s_in, **extra_args) + d_2 = to_d(x_2, sigmas[i + 1], denoised_2) + d_prime = (d + d_2) / 2 + x = x + d_prime * dt + return x + + +@torch.no_grad() +def sample_dpm_2(model, x, sigmas, extra_args=None, callback=None, disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'), s_noise=1.): + """A sampler inspired by DPM-Solver-2 and Algorithm 2 from Karras et al. (2022).""" + extra_args = {} if extra_args is None else extra_args + s_in = x.new_ones([x.shape[0]]) + for i in trange(len(sigmas) - 1, disable=disable): + gamma = min(s_churn / (len(sigmas) - 1), 2 ** 0.5 - 1) if s_tmin <= sigmas[i] <= s_tmax else 0. + eps = torch.randn_like(x) * s_noise + sigma_hat = sigmas[i] * (gamma + 1) + if gamma > 0: + x = x + eps * (sigma_hat ** 2 - sigmas[i] ** 2) ** 0.5 + denoised = model(x, sigma_hat * s_in, **extra_args) + d = to_d(x, sigma_hat, denoised) + if callback is not None: + callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigma_hat, 'denoised': denoised}) + if sigmas[i + 1] == 0: + # Euler method + dt = sigmas[i + 1] - sigma_hat + x = x + d * dt + else: + # DPM-Solver-2 + sigma_mid = sigma_hat.log().lerp(sigmas[i + 1].log(), 0.5).exp() + dt_1 = sigma_mid - sigma_hat + dt_2 = sigmas[i + 1] - sigma_hat + x_2 = x + d * dt_1 + denoised_2 = model(x_2, sigma_mid * s_in, **extra_args) + d_2 = to_d(x_2, sigma_mid, denoised_2) + x = x + d_2 * dt_2 + return x + + +@torch.no_grad() +def sample_dpm_2_ancestral(model, x, sigmas, extra_args=None, callback=None, disable=None, eta=1., s_noise=1., noise_sampler=None): + """Ancestral sampling with DPM-Solver second-order steps.""" + extra_args = {} if extra_args is None else extra_args + noise_sampler = default_noise_sampler(x) if noise_sampler is None else noise_sampler + s_in = x.new_ones([x.shape[0]]) + for i in trange(len(sigmas) - 1, disable=disable): + denoised = model(x, sigmas[i] * s_in, **extra_args) + sigma_down, sigma_up = get_ancestral_step(sigmas[i], sigmas[i + 1], eta=eta) + if callback is not None: + callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised}) + d = to_d(x, sigmas[i], denoised) + if sigma_down == 0: + # Euler method + dt = sigma_down - sigmas[i] + x = x + d * dt + else: + # DPM-Solver-2 + sigma_mid = sigmas[i].log().lerp(sigma_down.log(), 0.5).exp() + dt_1 = sigma_mid - sigmas[i] + dt_2 = sigma_down - sigmas[i] + x_2 = x + d * dt_1 + denoised_2 = model(x_2, sigma_mid * s_in, **extra_args) + d_2 = to_d(x_2, sigma_mid, denoised_2) + x = x + d_2 * dt_2 + x = x + noise_sampler(sigmas[i], sigmas[i + 1]) * s_noise * sigma_up + return x + + +def linear_multistep_coeff(order, t, i, j): + if order - 1 > i: + raise ValueError(f'Order {order} too high for step {i}') + def fn(tau): + prod = 1. + for k in range(order): + if j == k: + continue + prod *= (tau - t[i - k]) / (t[i - j] - t[i - k]) + return prod + return integrate.quad(fn, t[i], t[i + 1], epsrel=1e-4)[0] + + +@torch.no_grad() +def sample_lms(model, x, sigmas, extra_args=None, callback=None, disable=None, order=4): + extra_args = {} if extra_args is None else extra_args + s_in = x.new_ones([x.shape[0]]) + sigmas_cpu = sigmas.detach().cpu().numpy() + ds = [] + for i in trange(len(sigmas) - 1, disable=disable): + denoised = model(x, sigmas[i] * s_in, **extra_args) + d = to_d(x, sigmas[i], denoised) + ds.append(d) + if len(ds) > order: + ds.pop(0) + if callback is not None: + callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised}) + cur_order = min(i + 1, order) + coeffs = [linear_multistep_coeff(cur_order, sigmas_cpu, i, j) for j in range(cur_order)] + x = x + sum(coeff * d for coeff, d in zip(coeffs, reversed(ds))) + return x + + +@torch.no_grad() +def log_likelihood(model, x, sigma_min, sigma_max, extra_args=None, atol=1e-4, rtol=1e-4): + extra_args = {} if extra_args is None else extra_args + s_in = x.new_ones([x.shape[0]]) + v = torch.randint_like(x, 2) * 2 - 1 + fevals = 0 + def ode_fn(sigma, x): + nonlocal fevals + with torch.enable_grad(): + x = x[0].detach().requires_grad_() + denoised = model(x, sigma * s_in, **extra_args) + d = to_d(x, sigma, denoised) + fevals += 1 + grad = torch.autograd.grad((d * v).sum(), x)[0] + d_ll = (v * grad).flatten(1).sum(1) + return d.detach(), d_ll + x_min = x, x.new_zeros([x.shape[0]]) + t = x.new_tensor([sigma_min, sigma_max]) + sol = odeint(ode_fn, x_min, t, atol=atol, rtol=rtol, method='dopri5') + latent, delta_ll = sol[0][-1], sol[1][-1] + ll_prior = torch.distributions.Normal(0, sigma_max).log_prob(latent).flatten(1).sum(1) + return ll_prior + delta_ll, {'fevals': fevals} + + +class PIDStepSizeController: + """A PID controller for ODE adaptive step size control.""" + def __init__(self, h, pcoeff, icoeff, dcoeff, order=1, accept_safety=0.81, eps=1e-8): + self.h = h + self.b1 = (pcoeff + icoeff + dcoeff) / order + self.b2 = -(pcoeff + 2 * dcoeff) / order + self.b3 = dcoeff / order + self.accept_safety = accept_safety + self.eps = eps + self.errs = [] + + def limiter(self, x): + return 1 + math.atan(x - 1) + + def propose_step(self, error): + inv_error = 1 / (float(error) + self.eps) + if not self.errs: + self.errs = [inv_error, inv_error, inv_error] + self.errs[0] = inv_error + factor = self.errs[0] ** self.b1 * self.errs[1] ** self.b2 * self.errs[2] ** self.b3 + factor = self.limiter(factor) + accept = factor >= self.accept_safety + if accept: + self.errs[2] = self.errs[1] + self.errs[1] = self.errs[0] + self.h *= factor + return accept + + +class DPMSolver(nn.Module): + """DPM-Solver. See https://arxiv.org/abs/2206.00927.""" + + def __init__(self, model, extra_args=None, eps_callback=None, info_callback=None): + super().__init__() + self.model = model + self.extra_args = {} if extra_args is None else extra_args + self.eps_callback = eps_callback + self.info_callback = info_callback + + def t(self, sigma): + return -sigma.log() + + def sigma(self, t): + return t.neg().exp() + + def eps(self, eps_cache, key, x, t, *args, **kwargs): + if key in eps_cache: + return eps_cache[key], eps_cache + sigma = self.sigma(t) * x.new_ones([x.shape[0]]) + eps = (x - self.model(x, sigma, *args, **self.extra_args, **kwargs)) / self.sigma(t) + if self.eps_callback is not None: + self.eps_callback() + return eps, {key: eps, **eps_cache} + + def dpm_solver_1_step(self, x, t, t_next, eps_cache=None): + eps_cache = {} if eps_cache is None else eps_cache + h = t_next - t + eps, eps_cache = self.eps(eps_cache, 'eps', x, t) + x_1 = x - self.sigma(t_next) * h.expm1() * eps + return x_1, eps_cache + + def dpm_solver_2_step(self, x, t, t_next, r1=1 / 2, eps_cache=None): + eps_cache = {} if eps_cache is None else eps_cache + h = t_next - t + eps, eps_cache = self.eps(eps_cache, 'eps', x, t) + s1 = t + r1 * h + u1 = x - self.sigma(s1) * (r1 * h).expm1() * eps + eps_r1, eps_cache = self.eps(eps_cache, 'eps_r1', u1, s1) + x_2 = x - self.sigma(t_next) * h.expm1() * eps - self.sigma(t_next) / (2 * r1) * h.expm1() * (eps_r1 - eps) + return x_2, eps_cache + + def dpm_solver_3_step(self, x, t, t_next, r1=1 / 3, r2=2 / 3, eps_cache=None): + eps_cache = {} if eps_cache is None else eps_cache + h = t_next - t + eps, eps_cache = self.eps(eps_cache, 'eps', x, t) + s1 = t + r1 * h + s2 = t + r2 * h + u1 = x - self.sigma(s1) * (r1 * h).expm1() * eps + eps_r1, eps_cache = self.eps(eps_cache, 'eps_r1', u1, s1) + u2 = x - self.sigma(s2) * (r2 * h).expm1() * eps - self.sigma(s2) * (r2 / r1) * ((r2 * h).expm1() / (r2 * h) - 1) * (eps_r1 - eps) + eps_r2, eps_cache = self.eps(eps_cache, 'eps_r2', u2, s2) + x_3 = x - self.sigma(t_next) * h.expm1() * eps - self.sigma(t_next) / r2 * (h.expm1() / h - 1) * (eps_r2 - eps) + return x_3, eps_cache + + def dpm_solver_fast(self, x, t_start, t_end, nfe, eta=0., s_noise=1., noise_sampler=None): + noise_sampler = default_noise_sampler(x) if noise_sampler is None else noise_sampler + if not t_end > t_start and eta: + raise ValueError('eta must be 0 for reverse sampling') + + m = math.floor(nfe / 3) + 1 + ts = torch.linspace(t_start, t_end, m + 1, device=x.device) + + if nfe % 3 == 0: + orders = [3] * (m - 2) + [2, 1] + else: + orders = [3] * (m - 1) + [nfe % 3] + + for i in range(len(orders)): + eps_cache = {} + t, t_next = ts[i], ts[i + 1] + if eta: + sd, su = get_ancestral_step(self.sigma(t), self.sigma(t_next), eta) + t_next_ = torch.minimum(t_end, self.t(sd)) + su = (self.sigma(t_next) ** 2 - self.sigma(t_next_) ** 2) ** 0.5 + else: + t_next_, su = t_next, 0. + + eps, eps_cache = self.eps(eps_cache, 'eps', x, t) + denoised = x - self.sigma(t) * eps + if self.info_callback is not None: + self.info_callback({'x': x, 'i': i, 't': ts[i], 't_up': t, 'denoised': denoised}) + + if orders[i] == 1: + x, eps_cache = self.dpm_solver_1_step(x, t, t_next_, eps_cache=eps_cache) + elif orders[i] == 2: + x, eps_cache = self.dpm_solver_2_step(x, t, t_next_, eps_cache=eps_cache) + else: + x, eps_cache = self.dpm_solver_3_step(x, t, t_next_, eps_cache=eps_cache) + + x = x + su * s_noise * noise_sampler(self.sigma(t), self.sigma(t_next)) + + return x + + def dpm_solver_adaptive(self, x, t_start, t_end, order=3, rtol=0.05, atol=0.0078, h_init=0.05, pcoeff=0., icoeff=1., dcoeff=0., accept_safety=0.81, eta=0., s_noise=1., noise_sampler=None): + noise_sampler = default_noise_sampler(x) if noise_sampler is None else noise_sampler + if order not in {2, 3}: + raise ValueError('order should be 2 or 3') + forward = t_end > t_start + if not forward and eta: + raise ValueError('eta must be 0 for reverse sampling') + h_init = abs(h_init) * (1 if forward else -1) + atol = torch.tensor(atol) + rtol = torch.tensor(rtol) + s = t_start + x_prev = x + accept = True + pid = PIDStepSizeController(h_init, pcoeff, icoeff, dcoeff, 1.5 if eta else order, accept_safety) + info = {'steps': 0, 'nfe': 0, 'n_accept': 0, 'n_reject': 0} + + while s < t_end - 1e-5 if forward else s > t_end + 1e-5: + eps_cache = {} + t = torch.minimum(t_end, s + pid.h) if forward else torch.maximum(t_end, s + pid.h) + if eta: + sd, su = get_ancestral_step(self.sigma(s), self.sigma(t), eta) + t_ = torch.minimum(t_end, self.t(sd)) + su = (self.sigma(t) ** 2 - self.sigma(t_) ** 2) ** 0.5 + else: + t_, su = t, 0. + + eps, eps_cache = self.eps(eps_cache, 'eps', x, s) + denoised = x - self.sigma(s) * eps + + if order == 2: + x_low, eps_cache = self.dpm_solver_1_step(x, s, t_, eps_cache=eps_cache) + x_high, eps_cache = self.dpm_solver_2_step(x, s, t_, eps_cache=eps_cache) + else: + x_low, eps_cache = self.dpm_solver_2_step(x, s, t_, r1=1 / 3, eps_cache=eps_cache) + x_high, eps_cache = self.dpm_solver_3_step(x, s, t_, eps_cache=eps_cache) + delta = torch.maximum(atol, rtol * torch.maximum(x_low.abs(), x_prev.abs())) + error = torch.linalg.norm((x_low - x_high) / delta) / x.numel() ** 0.5 + accept = pid.propose_step(error) + if accept: + x_prev = x_low + x = x_high + su * s_noise * noise_sampler(self.sigma(s), self.sigma(t)) + s = t + info['n_accept'] += 1 + else: + info['n_reject'] += 1 + info['nfe'] += order + info['steps'] += 1 + + if self.info_callback is not None: + self.info_callback({'x': x, 'i': info['steps'] - 1, 't': s, 't_up': s, 'denoised': denoised, 'error': error, 'h': pid.h, **info}) + + return x, info + + +@torch.no_grad() +def sample_dpm_fast(model, x, sigma_min, sigma_max, n, extra_args=None, callback=None, disable=None, eta=0., s_noise=1., noise_sampler=None): + """DPM-Solver-Fast (fixed step size). See https://arxiv.org/abs/2206.00927.""" + if sigma_min <= 0 or sigma_max <= 0: + raise ValueError('sigma_min and sigma_max must not be 0') + with tqdm(total=n, disable=disable) as pbar: + dpm_solver = DPMSolver(model, extra_args, eps_callback=pbar.update) + if callback is not None: + dpm_solver.info_callback = lambda info: callback({'sigma': dpm_solver.sigma(info['t']), 'sigma_hat': dpm_solver.sigma(info['t_up']), **info}) + return dpm_solver.dpm_solver_fast(x, dpm_solver.t(torch.tensor(sigma_max)), dpm_solver.t(torch.tensor(sigma_min)), n, eta, s_noise, noise_sampler) + + +@torch.no_grad() +def sample_dpm_adaptive(model, x, sigma_min, sigma_max, extra_args=None, callback=None, disable=None, order=3, rtol=0.05, atol=0.0078, h_init=0.05, pcoeff=0., icoeff=1., dcoeff=0., accept_safety=0.81, eta=0., s_noise=1., noise_sampler=None, return_info=False): + """DPM-Solver-12 and 23 (adaptive step size). See https://arxiv.org/abs/2206.00927.""" + if sigma_min <= 0 or sigma_max <= 0: + raise ValueError('sigma_min and sigma_max must not be 0') + with tqdm(disable=disable) as pbar: + dpm_solver = DPMSolver(model, extra_args, eps_callback=pbar.update) + if callback is not None: + dpm_solver.info_callback = lambda info: callback({'sigma': dpm_solver.sigma(info['t']), 'sigma_hat': dpm_solver.sigma(info['t_up']), **info}) + x, info = dpm_solver.dpm_solver_adaptive(x, dpm_solver.t(torch.tensor(sigma_max)), dpm_solver.t(torch.tensor(sigma_min)), order, rtol, atol, h_init, pcoeff, icoeff, dcoeff, accept_safety, eta, s_noise, noise_sampler) + if return_info: + return x, info + return x + + +@torch.no_grad() +def sample_dpmpp_2s_ancestral(model, x, sigmas, extra_args=None, callback=None, disable=None, eta=1., s_noise=1., noise_sampler=None): + """Ancestral sampling with DPM-Solver++(2S) second-order steps.""" + extra_args = {} if extra_args is None else extra_args + noise_sampler = default_noise_sampler(x) if noise_sampler is None else noise_sampler + s_in = x.new_ones([x.shape[0]]) + sigma_fn = lambda t: t.neg().exp() + t_fn = lambda sigma: sigma.log().neg() + + for i in trange(len(sigmas) - 1, disable=disable): + denoised = model(x, sigmas[i] * s_in, **extra_args) + sigma_down, sigma_up = get_ancestral_step(sigmas[i], sigmas[i + 1], eta=eta) + if callback is not None: + callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised}) + if sigma_down == 0: + # Euler method + d = to_d(x, sigmas[i], denoised) + dt = sigma_down - sigmas[i] + x = x + d * dt + else: + # DPM-Solver++(2S) + t, t_next = t_fn(sigmas[i]), t_fn(sigma_down) + r = 1 / 2 + h = t_next - t + s = t + r * h + x_2 = (sigma_fn(s) / sigma_fn(t)) * x - (-h * r).expm1() * denoised + denoised_2 = model(x_2, sigma_fn(s) * s_in, **extra_args) + x = (sigma_fn(t_next) / sigma_fn(t)) * x - (-h).expm1() * denoised_2 + # Noise addition + if sigmas[i + 1] > 0: + x = x + noise_sampler(sigmas[i], sigmas[i + 1]) * s_noise * sigma_up + return x + + +@torch.no_grad() +def sample_dpmpp_sde(model, x, sigmas, extra_args=None, callback=None, disable=None, eta=1., s_noise=1., noise_sampler=None, r=1 / 2): + """DPM-Solver++ (stochastic).""" + sigma_min, sigma_max = sigmas[sigmas > 0].min(), sigmas.max() + noise_sampler = BrownianTreeNoiseSampler(x, sigma_min, sigma_max) if noise_sampler is None else noise_sampler + extra_args = {} if extra_args is None else extra_args + s_in = x.new_ones([x.shape[0]]) + sigma_fn = lambda t: t.neg().exp() + t_fn = lambda sigma: sigma.log().neg() + + for i in trange(len(sigmas) - 1, disable=disable): + denoised = model(x, sigmas[i] * s_in, **extra_args) + if callback is not None: + callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised}) + if sigmas[i + 1] == 0: + # Euler method + d = to_d(x, sigmas[i], denoised) + dt = sigmas[i + 1] - sigmas[i] + x = x + d * dt + else: + # DPM-Solver++ + t, t_next = t_fn(sigmas[i]), t_fn(sigmas[i + 1]) + h = t_next - t + s = t + h * r + fac = 1 / (2 * r) + + # Step 1 + sd, su = get_ancestral_step(sigma_fn(t), sigma_fn(s), eta) + s_ = t_fn(sd) + x_2 = (sigma_fn(s_) / sigma_fn(t)) * x - (t - s_).expm1() * denoised + x_2 = x_2 + noise_sampler(sigma_fn(t), sigma_fn(s)) * s_noise * su + denoised_2 = model(x_2, sigma_fn(s) * s_in, **extra_args) + + # Step 2 + sd, su = get_ancestral_step(sigma_fn(t), sigma_fn(t_next), eta) + t_next_ = t_fn(sd) + denoised_d = (1 - fac) * denoised + fac * denoised_2 + x = (sigma_fn(t_next_) / sigma_fn(t)) * x - (t - t_next_).expm1() * denoised_d + x = x + noise_sampler(sigma_fn(t), sigma_fn(t_next)) * s_noise * su + return x + + +@torch.no_grad() +def sample_dpmpp_2m(model, x, sigmas, extra_args=None, callback=None, disable=None): + """DPM-Solver++(2M).""" + extra_args = {} if extra_args is None else extra_args + s_in = x.new_ones([x.shape[0]]) + sigma_fn = lambda t: t.neg().exp() + t_fn = lambda sigma: sigma.log().neg() + old_denoised = None + + for i in trange(len(sigmas) - 1, disable=disable): + denoised = model(x, sigmas[i] * s_in, **extra_args) + if callback is not None: + callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised}) + t, t_next = t_fn(sigmas[i]), t_fn(sigmas[i + 1]) + h = t_next - t + if old_denoised is None or sigmas[i + 1] == 0: + x = (sigma_fn(t_next) / sigma_fn(t)) * x - (-h).expm1() * denoised + else: + h_last = t - t_fn(sigmas[i - 1]) + r = h_last / h + denoised_d = (1 + 1 / (2 * r)) * denoised - (1 / (2 * r)) * old_denoised + x = (sigma_fn(t_next) / sigma_fn(t)) * x - (-h).expm1() * denoised_d + old_denoised = denoised + return x diff --git a/sd/stablediffusion/src/k-diffusion/k_diffusion/utils.py b/sd/stablediffusion/src/k-diffusion/k_diffusion/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..9afedb99276d55d5b923a04ffb62d403c9dfae93 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/k_diffusion/utils.py @@ -0,0 +1,329 @@ +from contextlib import contextmanager +import hashlib +import math +from pathlib import Path +import shutil +import urllib +import warnings + +from PIL import Image +import torch +from torch import nn, optim +from torch.utils import data +from torchvision.transforms import functional as TF + + +def from_pil_image(x): + """Converts from a PIL image to a tensor.""" + x = TF.to_tensor(x) + if x.ndim == 2: + x = x[..., None] + return x * 2 - 1 + + +def to_pil_image(x): + """Converts from a tensor to a PIL image.""" + if x.ndim == 4: + assert x.shape[0] == 1 + x = x[0] + if x.shape[0] == 1: + x = x[0] + return TF.to_pil_image((x.clamp(-1, 1) + 1) / 2) + + +def hf_datasets_augs_helper(examples, transform, image_key, mode='RGB'): + """Apply passed in transforms for HuggingFace Datasets.""" + images = [transform(image.convert(mode)) for image in examples[image_key]] + return {image_key: images} + + +def append_dims(x, target_dims): + """Appends dimensions to the end of a tensor until it has target_dims dimensions.""" + dims_to_append = target_dims - x.ndim + if dims_to_append < 0: + raise ValueError(f'input has {x.ndim} dims but target_dims is {target_dims}, which is less') + return x[(...,) + (None,) * dims_to_append] + + +def n_params(module): + """Returns the number of trainable parameters in a module.""" + return sum(p.numel() for p in module.parameters()) + + +def download_file(path, url, digest=None): + """Downloads a file if it does not exist, optionally checking its SHA-256 hash.""" + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + if not path.exists(): + with urllib.request.urlopen(url) as response, open(path, 'wb') as f: + shutil.copyfileobj(response, f) + if digest is not None: + file_digest = hashlib.sha256(open(path, 'rb').read()).hexdigest() + if digest != file_digest: + raise OSError(f'hash of {path} (url: {url}) failed to validate') + return path + + +@contextmanager +def train_mode(model, mode=True): + """A context manager that places a model into training mode and restores + the previous mode on exit.""" + modes = [module.training for module in model.modules()] + try: + yield model.train(mode) + finally: + for i, module in enumerate(model.modules()): + module.training = modes[i] + + +def eval_mode(model): + """A context manager that places a model into evaluation mode and restores + the previous mode on exit.""" + return train_mode(model, False) + + +@torch.no_grad() +def ema_update(model, averaged_model, decay): + """Incorporates updated model parameters into an exponential moving averaged + version of a model. It should be called after each optimizer step.""" + model_params = dict(model.named_parameters()) + averaged_params = dict(averaged_model.named_parameters()) + assert model_params.keys() == averaged_params.keys() + + for name, param in model_params.items(): + averaged_params[name].mul_(decay).add_(param, alpha=1 - decay) + + model_buffers = dict(model.named_buffers()) + averaged_buffers = dict(averaged_model.named_buffers()) + assert model_buffers.keys() == averaged_buffers.keys() + + for name, buf in model_buffers.items(): + averaged_buffers[name].copy_(buf) + + +class EMAWarmup: + """Implements an EMA warmup using an inverse decay schedule. + If inv_gamma=1 and power=1, implements a simple average. inv_gamma=1, power=2/3 are + good values for models you plan to train for a million or more steps (reaches decay + factor 0.999 at 31.6K steps, 0.9999 at 1M steps), inv_gamma=1, power=3/4 for models + you plan to train for less (reaches decay factor 0.999 at 10K steps, 0.9999 at + 215.4k steps). + Args: + inv_gamma (float): Inverse multiplicative factor of EMA warmup. Default: 1. + power (float): Exponential factor of EMA warmup. Default: 1. + min_value (float): The minimum EMA decay rate. Default: 0. + max_value (float): The maximum EMA decay rate. Default: 1. + start_at (int): The epoch to start averaging at. Default: 0. + last_epoch (int): The index of last epoch. Default: 0. + """ + + def __init__(self, inv_gamma=1., power=1., min_value=0., max_value=1., start_at=0, + last_epoch=0): + self.inv_gamma = inv_gamma + self.power = power + self.min_value = min_value + self.max_value = max_value + self.start_at = start_at + self.last_epoch = last_epoch + + def state_dict(self): + """Returns the state of the class as a :class:`dict`.""" + return dict(self.__dict__.items()) + + def load_state_dict(self, state_dict): + """Loads the class's state. + Args: + state_dict (dict): scaler state. Should be an object returned + from a call to :meth:`state_dict`. + """ + self.__dict__.update(state_dict) + + def get_value(self): + """Gets the current EMA decay rate.""" + epoch = max(0, self.last_epoch - self.start_at) + value = 1 - (1 + epoch / self.inv_gamma) ** -self.power + return 0. if epoch < 0 else min(self.max_value, max(self.min_value, value)) + + def step(self): + """Updates the step count.""" + self.last_epoch += 1 + + +class InverseLR(optim.lr_scheduler._LRScheduler): + """Implements an inverse decay learning rate schedule with an optional exponential + warmup. When last_epoch=-1, sets initial lr as lr. + inv_gamma is the number of steps/epochs required for the learning rate to decay to + (1 / 2)**power of its original value. + Args: + optimizer (Optimizer): Wrapped optimizer. + inv_gamma (float): Inverse multiplicative factor of learning rate decay. Default: 1. + power (float): Exponential factor of learning rate decay. Default: 1. + warmup (float): Exponential warmup factor (0 <= warmup < 1, 0 to disable) + Default: 0. + min_lr (float): The minimum learning rate. Default: 0. + last_epoch (int): The index of last epoch. Default: -1. + verbose (bool): If ``True``, prints a message to stdout for + each update. Default: ``False``. + """ + + def __init__(self, optimizer, inv_gamma=1., power=1., warmup=0., min_lr=0., + last_epoch=-1, verbose=False): + self.inv_gamma = inv_gamma + self.power = power + if not 0. <= warmup < 1: + raise ValueError('Invalid value for warmup') + self.warmup = warmup + self.min_lr = min_lr + super().__init__(optimizer, last_epoch, verbose) + + def get_lr(self): + if not self._get_lr_called_within_step: + warnings.warn("To get the last learning rate computed by the scheduler, " + "please use `get_last_lr()`.") + + return self._get_closed_form_lr() + + def _get_closed_form_lr(self): + warmup = 1 - self.warmup ** (self.last_epoch + 1) + lr_mult = (1 + self.last_epoch / self.inv_gamma) ** -self.power + return [warmup * max(self.min_lr, base_lr * lr_mult) + for base_lr in self.base_lrs] + + +class ExponentialLR(optim.lr_scheduler._LRScheduler): + """Implements an exponential learning rate schedule with an optional exponential + warmup. When last_epoch=-1, sets initial lr as lr. Decays the learning rate + continuously by decay (default 0.5) every num_steps steps. + Args: + optimizer (Optimizer): Wrapped optimizer. + num_steps (float): The number of steps to decay the learning rate by decay in. + decay (float): The factor by which to decay the learning rate every num_steps + steps. Default: 0.5. + warmup (float): Exponential warmup factor (0 <= warmup < 1, 0 to disable) + Default: 0. + min_lr (float): The minimum learning rate. Default: 0. + last_epoch (int): The index of last epoch. Default: -1. + verbose (bool): If ``True``, prints a message to stdout for + each update. Default: ``False``. + """ + + def __init__(self, optimizer, num_steps, decay=0.5, warmup=0., min_lr=0., + last_epoch=-1, verbose=False): + self.num_steps = num_steps + self.decay = decay + if not 0. <= warmup < 1: + raise ValueError('Invalid value for warmup') + self.warmup = warmup + self.min_lr = min_lr + super().__init__(optimizer, last_epoch, verbose) + + def get_lr(self): + if not self._get_lr_called_within_step: + warnings.warn("To get the last learning rate computed by the scheduler, " + "please use `get_last_lr()`.") + + return self._get_closed_form_lr() + + def _get_closed_form_lr(self): + warmup = 1 - self.warmup ** (self.last_epoch + 1) + lr_mult = (self.decay ** (1 / self.num_steps)) ** self.last_epoch + return [warmup * max(self.min_lr, base_lr * lr_mult) + for base_lr in self.base_lrs] + + +def rand_log_normal(shape, loc=0., scale=1., device='cpu', dtype=torch.float32): + """Draws samples from an lognormal distribution.""" + return (torch.randn(shape, device=device, dtype=dtype) * scale + loc).exp() + + +def rand_log_logistic(shape, loc=0., scale=1., min_value=0., max_value=float('inf'), device='cpu', dtype=torch.float32): + """Draws samples from an optionally truncated log-logistic distribution.""" + min_value = torch.as_tensor(min_value, device=device, dtype=torch.float64) + max_value = torch.as_tensor(max_value, device=device, dtype=torch.float64) + min_cdf = min_value.log().sub(loc).div(scale).sigmoid() + max_cdf = max_value.log().sub(loc).div(scale).sigmoid() + u = torch.rand(shape, device=device, dtype=torch.float64) * (max_cdf - min_cdf) + min_cdf + return u.logit().mul(scale).add(loc).exp().to(dtype) + + +def rand_log_uniform(shape, min_value, max_value, device='cpu', dtype=torch.float32): + """Draws samples from an log-uniform distribution.""" + min_value = math.log(min_value) + max_value = math.log(max_value) + return (torch.rand(shape, device=device, dtype=dtype) * (max_value - min_value) + min_value).exp() + + +def rand_v_diffusion(shape, sigma_data=1., min_value=0., max_value=float('inf'), device='cpu', dtype=torch.float32): + """Draws samples from a truncated v-diffusion training timestep distribution.""" + min_cdf = math.atan(min_value / sigma_data) * 2 / math.pi + max_cdf = math.atan(max_value / sigma_data) * 2 / math.pi + u = torch.rand(shape, device=device, dtype=dtype) * (max_cdf - min_cdf) + min_cdf + return torch.tan(u * math.pi / 2) * sigma_data + + +def rand_split_log_normal(shape, loc, scale_1, scale_2, device='cpu', dtype=torch.float32): + """Draws samples from a split lognormal distribution.""" + n = torch.randn(shape, device=device, dtype=dtype).abs() + u = torch.rand(shape, device=device, dtype=dtype) + n_left = n * -scale_1 + loc + n_right = n * scale_2 + loc + ratio = scale_1 / (scale_1 + scale_2) + return torch.where(u < ratio, n_left, n_right).exp() + + +class FolderOfImages(data.Dataset): + """Recursively finds all images in a directory. It does not support + classes/targets.""" + + IMG_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif', '.tiff', '.webp'} + + def __init__(self, root, transform=None): + super().__init__() + self.root = Path(root) + self.transform = nn.Identity() if transform is None else transform + self.paths = sorted(path for path in self.root.rglob('*') if path.suffix.lower() in self.IMG_EXTENSIONS) + + def __repr__(self): + return f'FolderOfImages(root="{self.root}", len: {len(self)})' + + def __len__(self): + return len(self.paths) + + def __getitem__(self, key): + path = self.paths[key] + with open(path, 'rb') as f: + image = Image.open(f).convert('RGB') + image = self.transform(image) + return image, + + +class CSVLogger: + def __init__(self, filename, columns): + self.filename = Path(filename) + self.columns = columns + if self.filename.exists(): + self.file = open(self.filename, 'a') + else: + self.file = open(self.filename, 'w') + self.write(*self.columns) + + def write(self, *args): + print(*args, sep=',', file=self.file, flush=True) + + +@contextmanager +def tf32_mode(cudnn=None, matmul=None): + """A context manager that sets whether TF32 is allowed on cuDNN or matmul.""" + cudnn_old = torch.backends.cudnn.allow_tf32 + matmul_old = torch.backends.cuda.matmul.allow_tf32 + try: + if cudnn is not None: + torch.backends.cudnn.allow_tf32 = cudnn + if matmul is not None: + torch.backends.cuda.matmul.allow_tf32 = matmul + yield + finally: + if cudnn is not None: + torch.backends.cudnn.allow_tf32 = cudnn_old + if matmul is not None: + torch.backends.cuda.matmul.allow_tf32 = matmul_old diff --git a/sd/stablediffusion/src/k-diffusion/make_grid.py b/sd/stablediffusion/src/k-diffusion/make_grid.py new file mode 100644 index 0000000000000000000000000000000000000000..0c6616843cac1a69fdb94df804822cf07b533543 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/make_grid.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +"""Assembles images into a grid.""" + +import argparse +import math +import sys + +from PIL import Image + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument('images', type=str, nargs='+', metavar='image', + help='the input images') + p.add_argument('--output', '-o', type=str, default='out.png', + help='the output image') + p.add_argument('--nrow', type=int, + help='the number of images per row') + args = p.parse_args() + + images = [Image.open(image) for image in args.images] + mode = images[0].mode + size = images[0].size + for image, name in zip(images, args.images): + if image.mode != mode: + print(f'Error: Image {name} had mode {image.mode}, expected {mode}', file=sys.stderr) + sys.exit(1) + if image.size != size: + print(f'Error: Image {name} had size {image.size}, expected {size}', file=sys.stderr) + sys.exit(1) + + n = len(images) + x = args.nrow if args.nrow else math.ceil(n**0.5) + y = math.ceil(n / x) + + output = Image.new(mode, (size[0] * x, size[1] * y)) + for i, image in enumerate(images): + cur_x, cur_y = i % x, i // x + output.paste(image, (size[0] * cur_x, size[1] * cur_y)) + + output.save(args.output) + + +if __name__ == '__main__': + main() diff --git a/sd/stablediffusion/src/k-diffusion/pyproject.toml b/sd/stablediffusion/src/k-diffusion/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..fed528d4a7a148fd0bf0b0198a6461f8c91b87e9 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" diff --git a/sd/stablediffusion/src/k-diffusion/requirements.txt b/sd/stablediffusion/src/k-diffusion/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..497553b0c7d493cf11d0278f5855ff296ad5e02a --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/requirements.txt @@ -0,0 +1,16 @@ +accelerate +clean-fid +clip-anytorch +einops +jsonmerge +kornia +Pillow +resize-right +scikit-image +scipy +torch +torchdiffeq +torchsde +torchvision +tqdm +wandb diff --git a/sd/stablediffusion/src/k-diffusion/sample.py b/sd/stablediffusion/src/k-diffusion/sample.py new file mode 100644 index 0000000000000000000000000000000000000000..21e0dc3c9ca055f7de73b7df7aa2841025187c18 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/sample.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 + +"""Samples from k-diffusion models.""" + +import argparse +import math + +import accelerate +import torch +from tqdm import trange, tqdm + +import k_diffusion as K + + +def main(): + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + p.add_argument('--batch-size', type=int, default=64, + help='the batch size') + p.add_argument('--checkpoint', type=str, required=True, + help='the checkpoint to use') + p.add_argument('--config', type=str, required=True, + help='the model config') + p.add_argument('-n', type=int, default=64, + help='the number of images to sample') + p.add_argument('--prefix', type=str, default='out', + help='the output prefix') + p.add_argument('--steps', type=int, default=50, + help='the number of denoising steps') + args = p.parse_args() + + config = K.config.load_config(open(args.config)) + model_config = config['model'] + # TODO: allow non-square input sizes + assert len(model_config['input_size']) == 2 and model_config['input_size'][0] == model_config['input_size'][1] + size = model_config['input_size'] + + accelerator = accelerate.Accelerator() + device = accelerator.device + print('Using device:', device, flush=True) + + inner_model = K.config.make_model(config).eval().requires_grad_(False).to(device) + inner_model.load_state_dict(torch.load(args.checkpoint, map_location='cpu')['model_ema']) + accelerator.print('Parameters:', K.utils.n_params(inner_model)) + model = K.Denoiser(inner_model, sigma_data=model_config['sigma_data']) + + sigma_min = model_config['sigma_min'] + sigma_max = model_config['sigma_max'] + + @torch.no_grad() + @K.utils.eval_mode(model) + def run(): + if accelerator.is_local_main_process: + tqdm.write('Sampling...') + sigmas = K.sampling.get_sigmas_karras(args.steps, sigma_min, sigma_max, rho=7., device=device) + def sample_fn(n): + x = torch.randn([n, model_config['input_channels'], size[0], size[1]], device=device) * sigma_max + x_0 = K.sampling.sample_lms(model, x, sigmas, disable=not accelerator.is_local_main_process) + return x_0 + x_0 = K.evaluation.compute_features(accelerator, sample_fn, lambda x: x, args.n, args.batch_size) + if accelerator.is_main_process: + for i, out in enumerate(x_0): + filename = f'{args.prefix}_{i:05}.png' + K.utils.to_pil_image(out).save(filename) + + try: + run() + except KeyboardInterrupt: + pass + + +if __name__ == '__main__': + main() diff --git a/sd/stablediffusion/src/k-diffusion/sample_clip_guided.py b/sd/stablediffusion/src/k-diffusion/sample_clip_guided.py new file mode 100644 index 0000000000000000000000000000000000000000..592350196fbbac8479563be5be9e138248d94c86 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/sample_clip_guided.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 + +"""CLIP guided sampling from k-diffusion models.""" + +import argparse +import math + +import accelerate +import clip +from kornia import augmentation as KA +from resize_right import resize +import torch +from torch.nn import functional as F +from torchvision import transforms +from tqdm import trange, tqdm + +import k_diffusion as K + + +def spherical_dist_loss(x, y): + x = F.normalize(x, dim=-1) + y = F.normalize(y, dim=-1) + return (x - y).norm(dim=-1).div(2).arcsin().pow(2).mul(2) + + +def make_cond_model_fn(model, cond_fn): + def model_fn(x, sigma, **kwargs): + with torch.enable_grad(): + x = x.detach().requires_grad_() + denoised = model(x, sigma, **kwargs) + cond_grad = cond_fn(x, sigma, denoised=denoised, **kwargs).detach() + cond_denoised = denoised.detach() + cond_grad * K.utils.append_dims(sigma**2, x.ndim) + return cond_denoised + return model_fn + + +def make_static_thresh_model_fn(model, value=1.): + def model_fn(x, sigma, **kwargs): + return model(x, sigma, **kwargs).clamp(-value, value) + return model_fn + + +def main(): + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + p.add_argument('prompt', type=str, + default='the prompt to use') + p.add_argument('--batch-size', type=int, default=16, + help='the batch size') + p.add_argument('--checkpoint', type=str, required=True, + help='the checkpoint to use') + p.add_argument('--clip-guidance-scale', '-cgs', type=float, default=500., + help='the CLIP guidance scale') + p.add_argument('--clip-model', type=str, default='ViT-B/16', choices=clip.available_models(), + help='the CLIP model to use') + p.add_argument('--config', type=str, required=True, + help='the model config') + p.add_argument('-n', type=int, default=64, + help='the number of images to sample') + p.add_argument('--prefix', type=str, default='out', + help='the output prefix') + p.add_argument('--steps', type=int, default=100, + help='the number of denoising steps') + args = p.parse_args() + + config = K.config.load_config(open(args.config)) + model_config = config['model'] + # TODO: allow non-square input sizes + assert len(model_config['input_size']) == 2 and model_config['input_size'][0] == model_config['input_size'][1] + size = model_config['input_size'] + + accelerator = accelerate.Accelerator() + device = accelerator.device + print('Using device:', device, flush=True) + + inner_model = K.config.make_model(config).eval().requires_grad_(False).to(device) + inner_model.load_state_dict(torch.load(args.checkpoint, map_location='cpu')['model_ema']) + accelerator.print('Parameters:', K.utils.n_params(inner_model)) + model = K.Denoiser(inner_model, sigma_data=model_config['sigma_data']) + + sigma_min = model_config['sigma_min'] + sigma_max = model_config['sigma_max'] + + clip_model = clip.load(args.clip_model, device=device)[0].eval().requires_grad_(False) + clip_normalize = transforms.Normalize(mean=(0.48145466, 0.4578275, 0.40821073), + std=(0.26862954, 0.26130258, 0.27577711)) + clip_size = (clip_model.visual.input_resolution, clip_model.visual.input_resolution) + aug = KA.RandomAffine(0, (1/14, 1/14), p=1, padding_mode='border') + + def get_image_embed(x): + if x.shape[2:4] != clip_size: + x = resize(x, out_shape=clip_size, pad_mode='reflect') + x = clip_normalize(x) + x = clip_model.encode_image(x).float() + return F.normalize(x) + + target_embed = F.normalize(clip_model.encode_text(clip.tokenize(args.prompt, truncate=True).to(device)).float()) + + def cond_fn(x, t, denoised): + image_embed = get_image_embed(aug(denoised.add(1).div(2))) + loss = spherical_dist_loss(image_embed, target_embed).sum() * args.clip_guidance_scale + grad = -torch.autograd.grad(loss, x)[0] + return grad + + model_fn = make_cond_model_fn(model, cond_fn) + model_fn = make_static_thresh_model_fn(model_fn) + + @torch.no_grad() + @K.utils.eval_mode(model) + def run(): + if accelerator.is_local_main_process: + tqdm.write('Sampling...') + sigmas = K.sampling.get_sigmas_karras(args.steps, sigma_min, sigma_max, rho=7., device=device) + def sample_fn(n): + x = torch.randn([n, model_config['input_channels'], size[0], size[1]], device=device) * sigmas[0] + x_0 = K.sampling.sample_dpmpp_2s_ancestral(model_fn, x, sigmas, eta=1., disable=not accelerator.is_local_main_process) + return x_0 + x_0 = K.evaluation.compute_features(accelerator, sample_fn, lambda x: x, args.n, args.batch_size) + if accelerator.is_main_process: + for i, out in enumerate(x_0): + filename = f'{args.prefix}_{i:05}.png' + K.utils.to_pil_image(out).save(filename) + + try: + run() + except KeyboardInterrupt: + pass + + +if __name__ == '__main__': + main() diff --git a/sd/stablediffusion/src/k-diffusion/setup.cfg b/sd/stablediffusion/src/k-diffusion/setup.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e1ac99d3112d104b6167a1a5de6712a431e98703 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/setup.cfg @@ -0,0 +1,30 @@ +[metadata] +name = k-diffusion +version = 0.0.12 +author = Katherine Crowson +author_email = crowsonkb@gmail.com +url = https://github.com/crowsonkb/k-diffusion +description = Karras et al. (2022) diffusion models for PyTorch +long_description = file: README.md +long_description_content_type = text/markdown +license = MIT + +[options] +packages = find: +install_requires = + accelerate + clean-fid + clip-anytorch + einops + jsonmerge + kornia + Pillow + resize-right + scikit-image + scipy + torch + torchdiffeq + torchsde + torchvision + tqdm + wandb diff --git a/sd/stablediffusion/src/k-diffusion/setup.py b/sd/stablediffusion/src/k-diffusion/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..0ae4555937eb30e6632281a2326726826a41fe88 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/setup.py @@ -0,0 +1,5 @@ +from setuptools import setup + + +if __name__ == '__main__': + setup() diff --git a/sd/stablediffusion/src/k-diffusion/train.py b/sd/stablediffusion/src/k-diffusion/train.py new file mode 100644 index 0000000000000000000000000000000000000000..dbfbeb9c6f523a9b8ce03cb353f81146b6f95051 --- /dev/null +++ b/sd/stablediffusion/src/k-diffusion/train.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python3 + +"""Trains Karras et al. (2022) diffusion models.""" + +import argparse +from copy import deepcopy +from functools import partial +import math +import json +from pathlib import Path + +import accelerate +import torch +from torch import nn, optim +from torch import multiprocessing as mp +from torch.utils import data +from torchvision import datasets, transforms, utils +from tqdm.auto import trange, tqdm + +import k_diffusion as K + + +def main(): + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + p.add_argument('--batch-size', type=int, default=64, + help='the batch size') + p.add_argument('--config', type=str, required=True, + help='the configuration file') + p.add_argument('--demo-every', type=int, default=500, + help='save a demo grid every this many steps') + p.add_argument('--evaluate-every', type=int, default=10000, + help='save a demo grid every this many steps') + p.add_argument('--evaluate-n', type=int, default=2000, + help='the number of samples to draw to evaluate') + p.add_argument('--gns', action='store_true', + help='measure the gradient noise scale (DDP only)') + p.add_argument('--grad-accum-steps', type=int, default=1, + help='the number of gradient accumulation steps') + p.add_argument('--grow', type=str, + help='the checkpoint to grow from') + p.add_argument('--grow-config', type=str, + help='the configuration file of the model to grow from') + p.add_argument('--lr', type=float, + help='the learning rate') + p.add_argument('--name', type=str, default='model', + help='the name of the run') + p.add_argument('--num-workers', type=int, default=8, + help='the number of data loader workers') + p.add_argument('--resume', type=str, + help='the checkpoint to resume from') + p.add_argument('--sample-n', type=int, default=64, + help='the number of images to sample for demo grids') + p.add_argument('--save-every', type=int, default=10000, + help='save every this many steps') + p.add_argument('--seed', type=int, + help='the random seed') + p.add_argument('--start-method', type=str, default='spawn', + choices=['fork', 'forkserver', 'spawn'], + help='the multiprocessing start method') + p.add_argument('--wandb-entity', type=str, + help='the wandb entity name') + p.add_argument('--wandb-group', type=str, + help='the wandb group name') + p.add_argument('--wandb-project', type=str, + help='the wandb project name (specify this to enable wandb)') + p.add_argument('--wandb-save-model', action='store_true', + help='save model to wandb') + args = p.parse_args() + + mp.set_start_method(args.start_method) + torch.backends.cuda.matmul.allow_tf32 = True + + config = K.config.load_config(open(args.config)) + model_config = config['model'] + dataset_config = config['dataset'] + opt_config = config['optimizer'] + sched_config = config['lr_sched'] + ema_sched_config = config['ema_sched'] + + # TODO: allow non-square input sizes + assert len(model_config['input_size']) == 2 and model_config['input_size'][0] == model_config['input_size'][1] + size = model_config['input_size'] + + ddp_kwargs = accelerate.DistributedDataParallelKwargs(find_unused_parameters=model_config['skip_stages'] > 0) + accelerator = accelerate.Accelerator(kwargs_handlers=[ddp_kwargs], gradient_accumulation_steps=args.grad_accum_steps) + device = accelerator.device + print(f'Process {accelerator.process_index} using device: {device}', flush=True) + + if args.seed is not None: + seeds = torch.randint(-2 ** 63, 2 ** 63 - 1, [accelerator.num_processes], generator=torch.Generator().manual_seed(args.seed)) + torch.manual_seed(seeds[accelerator.process_index]) + + inner_model = K.config.make_model(config) + if accelerator.is_main_process: + print('Parameters:', K.utils.n_params(inner_model)) + + # If logging to wandb, initialize the run + use_wandb = accelerator.is_main_process and args.wandb_project + if use_wandb: + import wandb + log_config = vars(args) + log_config['config'] = config + log_config['parameters'] = K.utils.n_params(inner_model) + wandb.init(project=args.wandb_project, entity=args.wandb_entity, group=args.wandb_group, config=log_config, save_code=True) + + if opt_config['type'] == 'adamw': + opt = optim.AdamW(inner_model.parameters(), + lr=opt_config['lr'] if args.lr is None else args.lr, + betas=tuple(opt_config['betas']), + eps=opt_config['eps'], + weight_decay=opt_config['weight_decay']) + elif opt_config['type'] == 'sgd': + opt = optim.SGD(inner_model.parameters(), + lr=opt_config['lr'] if args.lr is None else args.lr, + momentum=opt_config.get('momentum', 0.), + nesterov=opt_config.get('nesterov', False), + weight_decay=opt_config.get('weight_decay', 0.)) + else: + raise ValueError('Invalid optimizer type') + + if sched_config['type'] == 'inverse': + sched = K.utils.InverseLR(opt, + inv_gamma=sched_config['inv_gamma'], + power=sched_config['power'], + warmup=sched_config['warmup']) + elif sched_config['type'] == 'exponential': + sched = K.utils.ExponentialLR(opt, + num_steps=sched_config['num_steps'], + decay=sched_config['decay'], + warmup=sched_config['warmup']) + else: + raise ValueError('Invalid schedule type') + + assert ema_sched_config['type'] == 'inverse' + ema_sched = K.utils.EMAWarmup(power=ema_sched_config['power'], + max_value=ema_sched_config['max_value']) + + tf = transforms.Compose([ + transforms.Resize(size[0], interpolation=transforms.InterpolationMode.LANCZOS), + transforms.CenterCrop(size[0]), + K.augmentation.KarrasAugmentationPipeline(model_config['augment_prob']), + ]) + + if dataset_config['type'] == 'imagefolder': + train_set = K.utils.FolderOfImages(dataset_config['location'], transform=tf) + elif dataset_config['type'] == 'cifar10': + train_set = datasets.CIFAR10(dataset_config['location'], train=True, download=True, transform=tf) + elif dataset_config['type'] == 'mnist': + train_set = datasets.MNIST(dataset_config['location'], train=True, download=True, transform=tf) + elif dataset_config['type'] == 'huggingface': + from datasets import load_dataset + train_set = load_dataset(dataset_config['location']) + train_set.set_transform(partial(K.utils.hf_datasets_augs_helper, transform=tf, image_key=dataset_config['image_key'])) + train_set = train_set['train'] + else: + raise ValueError('Invalid dataset type') + + if accelerator.is_main_process: + try: + print('Number of items in dataset:', len(train_set)) + except TypeError: + pass + + image_key = dataset_config.get('image_key', 0) + + train_dl = data.DataLoader(train_set, args.batch_size, shuffle=True, drop_last=True, + num_workers=args.num_workers, persistent_workers=True) + + if args.grow: + if not args.grow_config: + raise ValueError('--grow requires --grow-config') + ckpt = torch.load(args.grow, map_location='cpu') + old_config = K.config.load_config(open(args.grow_config)) + old_inner_model = K.config.make_model(old_config) + old_inner_model.load_state_dict(ckpt['model_ema']) + if old_config['model']['skip_stages'] != model_config['skip_stages']: + old_inner_model.set_skip_stages(model_config['skip_stages']) + if old_config['model']['patch_size'] != model_config['patch_size']: + old_inner_model.set_patch_size(model_config['patch_size']) + inner_model.load_state_dict(old_inner_model.state_dict()) + del ckpt, old_inner_model + + inner_model, opt, train_dl = accelerator.prepare(inner_model, opt, train_dl) + if use_wandb: + wandb.watch(inner_model) + if args.gns: + gns_stats_hook = K.gns.DDPGradientStatsHook(inner_model) + gns_stats = K.gns.GradientNoiseScale() + else: + gns_stats = None + sigma_min = model_config['sigma_min'] + sigma_max = model_config['sigma_max'] + sample_density = K.config.make_sample_density(model_config) + + model = K.config.make_denoiser_wrapper(config)(inner_model) + model_ema = deepcopy(model) + + state_path = Path(f'{args.name}_state.json') + + if state_path.exists() or args.resume: + if args.resume: + ckpt_path = args.resume + if not args.resume: + state = json.load(open(state_path)) + ckpt_path = state['latest_checkpoint'] + if accelerator.is_main_process: + print(f'Resuming from {ckpt_path}...') + ckpt = torch.load(ckpt_path, map_location='cpu') + accelerator.unwrap_model(model.inner_model).load_state_dict(ckpt['model']) + accelerator.unwrap_model(model_ema.inner_model).load_state_dict(ckpt['model_ema']) + opt.load_state_dict(ckpt['opt']) + sched.load_state_dict(ckpt['sched']) + ema_sched.load_state_dict(ckpt['ema_sched']) + epoch = ckpt['epoch'] + 1 + step = ckpt['step'] + 1 + if args.gns and ckpt.get('gns_stats', None) is not None: + gns_stats.load_state_dict(ckpt['gns_stats']) + + del ckpt + else: + epoch = 0 + step = 0 + + evaluate_enabled = args.evaluate_every > 0 and args.evaluate_n > 0 + if evaluate_enabled: + extractor = K.evaluation.InceptionV3FeatureExtractor(device=device) + train_iter = iter(train_dl) + if accelerator.is_main_process: + print('Computing features for reals...') + reals_features = K.evaluation.compute_features(accelerator, lambda x: next(train_iter)[image_key][1], extractor, args.evaluate_n, args.batch_size) + if accelerator.is_main_process: + metrics_log = K.utils.CSVLogger(f'{args.name}_metrics.csv', ['step', 'fid', 'kid']) + del train_iter + + @torch.no_grad() + @K.utils.eval_mode(model_ema) + def demo(): + if accelerator.is_main_process: + tqdm.write('Sampling...') + filename = f'{args.name}_demo_{step:08}.png' + n_per_proc = math.ceil(args.sample_n / accelerator.num_processes) + x = torch.randn([n_per_proc, model_config['input_channels'], size[0], size[1]], device=device) * sigma_max + sigmas = K.sampling.get_sigmas_karras(50, sigma_min, sigma_max, rho=7., device=device) + x_0 = K.sampling.sample_dpmpp_2m(model_ema, x, sigmas, disable=not accelerator.is_main_process) + x_0 = accelerator.gather(x_0)[:args.sample_n] + if accelerator.is_main_process: + grid = utils.make_grid(x_0, nrow=math.ceil(args.sample_n ** 0.5), padding=0) + K.utils.to_pil_image(grid).save(filename) + if use_wandb: + wandb.log({'demo_grid': wandb.Image(filename)}, step=step) + + @torch.no_grad() + @K.utils.eval_mode(model_ema) + def evaluate(): + if not evaluate_enabled: + return + if accelerator.is_main_process: + tqdm.write('Evaluating...') + sigmas = K.sampling.get_sigmas_karras(50, sigma_min, sigma_max, rho=7., device=device) + def sample_fn(n): + x = torch.randn([n, model_config['input_channels'], size[0], size[1]], device=device) * sigma_max + x_0 = K.sampling.sample_dpmpp_2m(model_ema, x, sigmas, disable=True) + return x_0 + fakes_features = K.evaluation.compute_features(accelerator, sample_fn, extractor, args.evaluate_n, args.batch_size) + if accelerator.is_main_process: + fid = K.evaluation.fid(fakes_features, reals_features) + kid = K.evaluation.kid(fakes_features, reals_features) + print(f'FID: {fid.item():g}, KID: {kid.item():g}') + if accelerator.is_main_process: + metrics_log.write(step, fid.item(), kid.item()) + if use_wandb: + wandb.log({'FID': fid.item(), 'KID': kid.item()}, step=step) + + def save(): + accelerator.wait_for_everyone() + filename = f'{args.name}_{step:08}.pth' + if accelerator.is_main_process: + tqdm.write(f'Saving to {filename}...') + obj = { + 'model': accelerator.unwrap_model(model.inner_model).state_dict(), + 'model_ema': accelerator.unwrap_model(model_ema.inner_model).state_dict(), + 'opt': opt.state_dict(), + 'sched': sched.state_dict(), + 'ema_sched': ema_sched.state_dict(), + 'epoch': epoch, + 'step': step, + 'gns_stats': gns_stats.state_dict() if gns_stats is not None else None, + } + accelerator.save(obj, filename) + if accelerator.is_main_process: + state_obj = {'latest_checkpoint': filename} + json.dump(state_obj, open(state_path, 'w')) + if args.wandb_save_model and use_wandb: + wandb.save(filename) + + try: + while True: + for batch in tqdm(train_dl, disable=not accelerator.is_main_process): + with accelerator.accumulate(model): + reals, _, aug_cond = batch[image_key] + noise = torch.randn_like(reals) + sigma = sample_density([reals.shape[0]], device=device) + losses = model.loss(reals, noise, sigma, aug_cond=aug_cond) + losses_all = accelerator.gather(losses) + loss = losses_all.mean() + accelerator.backward(losses.mean()) + if args.gns: + sq_norm_small_batch, sq_norm_large_batch = gns_stats_hook.get_stats() + gns_stats.update(sq_norm_small_batch, sq_norm_large_batch, reals.shape[0], reals.shape[0] * accelerator.num_processes) + opt.step() + sched.step() + opt.zero_grad() + if accelerator.sync_gradients: + ema_decay = ema_sched.get_value() + K.utils.ema_update(model, model_ema, ema_decay) + ema_sched.step() + + if accelerator.is_main_process: + if step % 25 == 0: + if args.gns: + tqdm.write(f'Epoch: {epoch}, step: {step}, loss: {loss.item():g}, gns: {gns_stats.get_gns():g}') + else: + tqdm.write(f'Epoch: {epoch}, step: {step}, loss: {loss.item():g}') + + if use_wandb: + log_dict = { + 'epoch': epoch, + 'loss': loss.item(), + 'lr': sched.get_last_lr()[0], + 'ema_decay': ema_decay, + } + if args.gns: + log_dict['gradient_noise_scale'] = gns_stats.get_gns() + wandb.log(log_dict, step=step) + + if step % args.demo_every == 0: + demo() + + if evaluate_enabled and step > 0 and step % args.evaluate_every == 0: + evaluate() + + if step > 0 and step % args.save_every == 0: + save() + + step += 1 + epoch += 1 + except KeyboardInterrupt: + pass + + +if __name__ == '__main__': + main() diff --git a/sd/stablediffusion/src/taming-transformers/License.txt b/sd/stablediffusion/src/taming-transformers/License.txt new file mode 100644 index 0000000000000000000000000000000000000000..57fb4153bafcd64b60377ba0ba2c79b7530efc1e --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/License.txt @@ -0,0 +1,19 @@ +Copyright (c) 2020 Patrick Esser and Robin Rombach and Björn Ommer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE./ diff --git a/sd/stablediffusion/src/taming-transformers/README.md b/sd/stablediffusion/src/taming-transformers/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d295fbf75703e6cd285330432785b8cdea072ba7 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/README.md @@ -0,0 +1,410 @@ +# Taming Transformers for High-Resolution Image Synthesis +##### CVPR 2021 (Oral) +![teaser](assets/mountain.jpeg) + +[**Taming Transformers for High-Resolution Image Synthesis**](https://compvis.github.io/taming-transformers/)
    +[Patrick Esser](https://github.com/pesser)\*, +[Robin Rombach](https://github.com/rromb)\*, +[Björn Ommer](https://hci.iwr.uni-heidelberg.de/Staff/bommer)
    +\* equal contribution + +**tl;dr** We combine the efficiancy of convolutional approaches with the expressivity of transformers by introducing a convolutional VQGAN, which learns a codebook of context-rich visual parts, whose composition is modeled with an autoregressive transformer. + +![teaser](assets/teaser.png) +[arXiv](https://arxiv.org/abs/2012.09841) | [BibTeX](#bibtex) | [Project Page](https://compvis.github.io/taming-transformers/) + + +### News +#### 2022 +- More pretrained VQGANs (e.g. a f8-model with only 256 codebook entries) are available in our new work on [Latent Diffusion Models](https://github.com/CompVis/latent-diffusion). +- Added scene synthesis models as proposed in the paper [High-Resolution Complex Scene Synthesis with Transformers](https://arxiv.org/abs/2105.06458), see [this section](#scene-image-synthesis). +#### 2021 +- Thanks to [rom1504](https://github.com/rom1504) it is now easy to [train a VQGAN on your own datasets](#training-on-custom-data). +- Included a bugfix for the quantizer. For backward compatibility it is + disabled by default (which corresponds to always training with `beta=1.0`). + Use `legacy=False` in the quantizer config to enable it. + Thanks [richcmwang](https://github.com/richcmwang) and [wcshin-git](https://github.com/wcshin-git)! +- Our paper received an update: See https://arxiv.org/abs/2012.09841v3 and the corresponding changelog. +- Added a pretrained, [1.4B transformer model](https://k00.fr/s511rwcv) trained for class-conditional ImageNet synthesis, which obtains state-of-the-art FID scores among autoregressive approaches and outperforms BigGAN. +- Added pretrained, unconditional models on [FFHQ](https://k00.fr/yndvfu95) and [CelebA-HQ](https://k00.fr/2xkmielf). +- Added accelerated sampling via caching of keys/values in the self-attention operation, used in `scripts/sample_fast.py`. +- Added a checkpoint of a [VQGAN](https://heibox.uni-heidelberg.de/d/2e5662443a6b4307b470/) trained with f8 compression and Gumbel-Quantization. + See also our updated [reconstruction notebook](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/reconstruction_usage.ipynb). +- We added a [colab notebook](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/reconstruction_usage.ipynb) which compares two VQGANs and OpenAI's [DALL-E](https://github.com/openai/DALL-E). See also [this section](#more-resources). +- We now include an overview of pretrained models in [Tab.1](#overview-of-pretrained-models). We added models for [COCO](#coco) and [ADE20k](#ade20k). +- The streamlit demo now supports image completions. +- We now include a couple of examples from the D-RIN dataset so you can run the + [D-RIN demo](#d-rin) without preparing the dataset first. +- You can now jump right into sampling with our [Colab quickstart notebook](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/taming-transformers.ipynb). + +## Requirements +A suitable [conda](https://conda.io/) environment named `taming` can be created +and activated with: + +``` +conda env create -f environment.yaml +conda activate taming +``` +## Overview of pretrained models +The following table provides an overview of all models that are currently available. +FID scores were evaluated using [torch-fidelity](https://github.com/toshas/torch-fidelity). +For reference, we also include a link to the recently released autoencoder of the [DALL-E](https://github.com/openai/DALL-E) model. +See the corresponding [colab +notebook](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/reconstruction_usage.ipynb) +for a comparison and discussion of reconstruction capabilities. + +| Dataset | FID vs train | FID vs val | Link | Samples (256x256) | Comments +| ------------- | ------------- | ------------- |------------- | ------------- |------------- | +| FFHQ (f=16) | 9.6 | -- | [ffhq_transformer](https://k00.fr/yndvfu95) | [ffhq_samples](https://k00.fr/j626x093) | +| CelebA-HQ (f=16) | 10.2 | -- | [celebahq_transformer](https://k00.fr/2xkmielf) | [celebahq_samples](https://k00.fr/j626x093) | +| ADE20K (f=16) | -- | 35.5 | [ade20k_transformer](https://k00.fr/ot46cksa) | [ade20k_samples.zip](https://heibox.uni-heidelberg.de/f/70bb78cbaf844501b8fb/) [2k] | evaluated on val split (2k images) +| COCO-Stuff (f=16) | -- | 20.4 | [coco_transformer](https://k00.fr/2zz6i2ce) | [coco_samples.zip](https://heibox.uni-heidelberg.de/f/a395a9be612f4a7a8054/) [5k] | evaluated on val split (5k images) +| ImageNet (cIN) (f=16) | 15.98/15.78/6.59/5.88/5.20 | -- | [cin_transformer](https://k00.fr/s511rwcv) | [cin_samples](https://k00.fr/j626x093) | different decoding hyperparameters | +| | | | || | +| FacesHQ (f=16) | -- | -- | [faceshq_transformer](https://k00.fr/qqfl2do8) +| S-FLCKR (f=16) | -- | -- | [sflckr](https://heibox.uni-heidelberg.de/d/73487ab6e5314cb5adba/) +| D-RIN (f=16) | -- | -- | [drin_transformer](https://k00.fr/39jcugc5) +| | | | | || | +| VQGAN ImageNet (f=16), 1024 | 10.54 | 7.94 | [vqgan_imagenet_f16_1024](https://heibox.uni-heidelberg.de/d/8088892a516d4e3baf92/) | [reconstructions](https://k00.fr/j626x093) | Reconstruction-FIDs. +| VQGAN ImageNet (f=16), 16384 | 7.41 | 4.98 |[vqgan_imagenet_f16_16384](https://heibox.uni-heidelberg.de/d/a7530b09fed84f80a887/) | [reconstructions](https://k00.fr/j626x093) | Reconstruction-FIDs. +| VQGAN OpenImages (f=8), 256 | -- | 1.49 |https://ommer-lab.com/files/latent-diffusion/vq-f8-n256.zip | --- | Reconstruction-FIDs. Available via [latent diffusion](https://github.com/CompVis/latent-diffusion). +| VQGAN OpenImages (f=8), 16384 | -- | 1.14 |https://ommer-lab.com/files/latent-diffusion/vq-f8.zip | --- | Reconstruction-FIDs. Available via [latent diffusion](https://github.com/CompVis/latent-diffusion) +| VQGAN OpenImages (f=8), 8192, GumbelQuantization | 3.24 | 1.49 |[vqgan_gumbel_f8](https://heibox.uni-heidelberg.de/d/2e5662443a6b4307b470/) | --- | Reconstruction-FIDs. +| | | | | || | +| DALL-E dVAE (f=8), 8192, GumbelQuantization | 33.88 | 32.01 | https://github.com/openai/DALL-E | [reconstructions](https://k00.fr/j626x093) | Reconstruction-FIDs. + + +## Running pretrained models + +The commands below will start a streamlit demo which supports sampling at +different resolutions and image completions. To run a non-interactive version +of the sampling process, replace `streamlit run scripts/sample_conditional.py --` +by `python scripts/make_samples.py --outdir ` and +keep the remaining command line arguments. + +To sample from unconditional or class-conditional models, +run `python scripts/sample_fast.py -r `. +We describe below how to use this script to sample from the ImageNet, FFHQ, and CelebA-HQ models, +respectively. + +### S-FLCKR +![teaser](assets/sunset_and_ocean.jpg) + +You can also [run this model in a Colab +notebook](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/taming-transformers.ipynb), +which includes all necessary steps to start sampling. + +Download the +[2020-11-09T13-31-51_sflckr](https://heibox.uni-heidelberg.de/d/73487ab6e5314cb5adba/) +folder and place it into `logs`. Then, run +``` +streamlit run scripts/sample_conditional.py -- -r logs/2020-11-09T13-31-51_sflckr/ +``` + +### ImageNet +![teaser](assets/imagenet.png) + +Download the [2021-04-03T19-39-50_cin_transformer](https://k00.fr/s511rwcv) +folder and place it into logs. Sampling from the class-conditional ImageNet +model does not require any data preparation. To produce 50 samples for each of +the 1000 classes of ImageNet, with k=600 for top-k sampling, p=0.92 for nucleus +sampling and temperature t=1.0, run + +``` +python scripts/sample_fast.py -r logs/2021-04-03T19-39-50_cin_transformer/ -n 50 -k 600 -t 1.0 -p 0.92 --batch_size 25 +``` + +To restrict the model to certain classes, provide them via the `--classes` argument, separated by +commas. For example, to sample 50 *ostriches*, *border collies* and *whiskey jugs*, run + +``` +python scripts/sample_fast.py -r logs/2021-04-03T19-39-50_cin_transformer/ -n 50 -k 600 -t 1.0 -p 0.92 --batch_size 25 --classes 9,232,901 +``` +We recommended to experiment with the autoregressive decoding parameters (top-k, top-p and temperature) for best results. + +### FFHQ/CelebA-HQ + +Download the [2021-04-23T18-19-01_ffhq_transformer](https://k00.fr/yndvfu95) and +[2021-04-23T18-11-19_celebahq_transformer](https://k00.fr/2xkmielf) +folders and place them into logs. +Again, sampling from these unconditional models does not require any data preparation. +To produce 50000 samples, with k=250 for top-k sampling, +p=1.0 for nucleus sampling and temperature t=1.0, run + +``` +python scripts/sample_fast.py -r logs/2021-04-23T18-19-01_ffhq_transformer/ +``` +for FFHQ and + +``` +python scripts/sample_fast.py -r logs/2021-04-23T18-11-19_celebahq_transformer/ +``` +to sample from the CelebA-HQ model. +For both models it can be advantageous to vary the top-k/top-p parameters for sampling. + +### FacesHQ +![teaser](assets/faceshq.jpg) + +Download [2020-11-13T21-41-45_faceshq_transformer](https://k00.fr/qqfl2do8) and +place it into `logs`. Follow the data preparation steps for +[CelebA-HQ](#celeba-hq) and [FFHQ](#ffhq). Run +``` +streamlit run scripts/sample_conditional.py -- -r logs/2020-11-13T21-41-45_faceshq_transformer/ +``` + +### D-RIN +![teaser](assets/drin.jpg) + +Download [2020-11-20T12-54-32_drin_transformer](https://k00.fr/39jcugc5) and +place it into `logs`. To run the demo on a couple of example depth maps +included in the repository, run + +``` +streamlit run scripts/sample_conditional.py -- -r logs/2020-11-20T12-54-32_drin_transformer/ --ignore_base_data data="{target: main.DataModuleFromConfig, params: {batch_size: 1, validation: {target: taming.data.imagenet.DRINExamples}}}" +``` + +To run the demo on the complete validation set, first follow the data preparation steps for +[ImageNet](#imagenet) and then run +``` +streamlit run scripts/sample_conditional.py -- -r logs/2020-11-20T12-54-32_drin_transformer/ +``` + +### COCO +Download [2021-01-20T16-04-20_coco_transformer](https://k00.fr/2zz6i2ce) and +place it into `logs`. To run the demo on a couple of example segmentation maps +included in the repository, run + +``` +streamlit run scripts/sample_conditional.py -- -r logs/2021-01-20T16-04-20_coco_transformer/ --ignore_base_data data="{target: main.DataModuleFromConfig, params: {batch_size: 1, validation: {target: taming.data.coco.Examples}}}" +``` + +### ADE20k +Download [2020-11-20T21-45-44_ade20k_transformer](https://k00.fr/ot46cksa) and +place it into `logs`. To run the demo on a couple of example segmentation maps +included in the repository, run + +``` +streamlit run scripts/sample_conditional.py -- -r logs/2020-11-20T21-45-44_ade20k_transformer/ --ignore_base_data data="{target: main.DataModuleFromConfig, params: {batch_size: 1, validation: {target: taming.data.ade20k.Examples}}}" +``` + +## Scene Image Synthesis +![teaser](assets/scene_images_samples.svg) +Scene image generation based on bounding box conditionals as done in our CVPR2021 AI4CC workshop paper [High-Resolution Complex Scene Synthesis with Transformers](https://arxiv.org/abs/2105.06458) (see talk on [workshop page](https://visual.cs.brown.edu/workshops/aicc2021/#awards)). Supporting the datasets COCO and Open Images. + +### Training +Download first-stage models [COCO-8k-VQGAN](https://heibox.uni-heidelberg.de/f/78dea9589974474c97c1/) for COCO or [COCO/Open-Images-8k-VQGAN](https://heibox.uni-heidelberg.de/f/461d9a9f4fcf48ab84f4/) for Open Images. +Change `ckpt_path` in `data/coco_scene_images_transformer.yaml` and `data/open_images_scene_images_transformer.yaml` to point to the downloaded first-stage models. +Download the full COCO/OI datasets and adapt `data_path` in the same files, unless working with the 100 files provided for training and validation suits your needs already. + +Code can be run with +`python main.py --base configs/coco_scene_images_transformer.yaml -t True --gpus 0,` +or +`python main.py --base configs/open_images_scene_images_transformer.yaml -t True --gpus 0,` + +### Sampling +Train a model as described above or download a pre-trained model: + - [Open Images 1 billion parameter model](https://drive.google.com/file/d/1FEK-Z7hyWJBvFWQF50pzSK9y1W_CJEig/view?usp=sharing) available that trained 100 epochs. On 256x256 pixels, FID 41.48±0.21, SceneFID 14.60±0.15, Inception Score 18.47±0.27. The model was trained with 2d crops of images and is thus well-prepared for the task of generating high-resolution images, e.g. 512x512. + - [Open Images distilled version of the above model with 125 million parameters](https://drive.google.com/file/d/1xf89g0mc78J3d8Bx5YhbK4tNRNlOoYaO) allows for sampling on smaller GPUs (4 GB is enough for sampling 256x256 px images). Model was trained for 60 epochs with 10% soft loss, 90% hard loss. On 256x256 pixels, FID 43.07±0.40, SceneFID 15.93±0.19, Inception Score 17.23±0.11. + - [COCO 30 epochs](https://heibox.uni-heidelberg.de/f/0d0b2594e9074c7e9a33/) + - [COCO 60 epochs](https://drive.google.com/file/d/1bInd49g2YulTJBjU32Awyt5qnzxxG5U9/) (find model statistics for both COCO versions in `assets/coco_scene_images_training.svg`) + +When downloading a pre-trained model, remember to change `ckpt_path` in `configs/*project.yaml` to point to your downloaded first-stage model (see ->Training). + +Scene image generation can be run with +`python scripts/make_scene_samples.py --outdir=/some/outdir -r /path/to/pretrained/model --resolution=512,512` + + +## Training on custom data + +Training on your own dataset can be beneficial to get better tokens and hence better images for your domain. +Those are the steps to follow to make this work: +1. install the repo with `conda env create -f environment.yaml`, `conda activate taming` and `pip install -e .` +1. put your .jpg files in a folder `your_folder` +2. create 2 text files a `xx_train.txt` and `xx_test.txt` that point to the files in your training and test set respectively (for example `find $(pwd)/your_folder -name "*.jpg" > train.txt`) +3. adapt `configs/custom_vqgan.yaml` to point to these 2 files +4. run `python main.py --base configs/custom_vqgan.yaml -t True --gpus 0,1` to + train on two GPUs. Use `--gpus 0,` (with a trailing comma) to train on a single GPU. + +## Data Preparation + +### ImageNet +The code will try to download (through [Academic +Torrents](http://academictorrents.com/)) and prepare ImageNet the first time it +is used. However, since ImageNet is quite large, this requires a lot of disk +space and time. If you already have ImageNet on your disk, you can speed things +up by putting the data into +`${XDG_CACHE}/autoencoders/data/ILSVRC2012_{split}/data/` (which defaults to +`~/.cache/autoencoders/data/ILSVRC2012_{split}/data/`), where `{split}` is one +of `train`/`validation`. It should have the following structure: + +``` +${XDG_CACHE}/autoencoders/data/ILSVRC2012_{split}/data/ +├── n01440764 +│ ├── n01440764_10026.JPEG +│ ├── n01440764_10027.JPEG +│ ├── ... +├── n01443537 +│ ├── n01443537_10007.JPEG +│ ├── n01443537_10014.JPEG +│ ├── ... +├── ... +``` + +If you haven't extracted the data, you can also place +`ILSVRC2012_img_train.tar`/`ILSVRC2012_img_val.tar` (or symlinks to them) into +`${XDG_CACHE}/autoencoders/data/ILSVRC2012_train/` / +`${XDG_CACHE}/autoencoders/data/ILSVRC2012_validation/`, which will then be +extracted into above structure without downloading it again. Note that this +will only happen if neither a folder +`${XDG_CACHE}/autoencoders/data/ILSVRC2012_{split}/data/` nor a file +`${XDG_CACHE}/autoencoders/data/ILSVRC2012_{split}/.ready` exist. Remove them +if you want to force running the dataset preparation again. + +You will then need to prepare the depth data using +[MiDaS](https://github.com/intel-isl/MiDaS). Create a symlink +`data/imagenet_depth` pointing to a folder with two subfolders `train` and +`val`, each mirroring the structure of the corresponding ImageNet folder +described above and containing a `png` file for each of ImageNet's `JPEG` +files. The `png` encodes `float32` depth values obtained from MiDaS as RGBA +images. We provide the script `scripts/extract_depth.py` to generate this data. +**Please note** that this script uses [MiDaS via PyTorch +Hub](https://pytorch.org/hub/intelisl_midas_v2/). When we prepared the data, +the hub provided the [MiDaS +v2.0](https://github.com/intel-isl/MiDaS/releases/tag/v2) version, but now it +provides a v2.1 version. We haven't tested our models with depth maps obtained +via v2.1 and if you want to make sure that things work as expected, you must +adjust the script to make sure it explicitly uses +[v2.0](https://github.com/intel-isl/MiDaS/releases/tag/v2)! + +### CelebA-HQ +Create a symlink `data/celebahq` pointing to a folder containing the `.npy` +files of CelebA-HQ (instructions to obtain them can be found in the [PGGAN +repository](https://github.com/tkarras/progressive_growing_of_gans)). + +### FFHQ +Create a symlink `data/ffhq` pointing to the `images1024x1024` folder obtained +from the [FFHQ repository](https://github.com/NVlabs/ffhq-dataset). + +### S-FLCKR +Unfortunately, we are not allowed to distribute the images we collected for the +S-FLCKR dataset and can therefore only give a description how it was produced. +There are many resources on [collecting images from the +web](https://github.com/adrianmrit/flickrdatasets) to get started. +We collected sufficiently large images from [flickr](https://www.flickr.com) +(see `data/flickr_tags.txt` for a full list of tags used to find images) +and various [subreddits](https://www.reddit.com/r/sfwpornnetwork/wiki/network) +(see `data/subreddits.txt` for all subreddits that were used). +Overall, we collected 107625 images, and split them randomly into 96861 +training images and 10764 validation images. We then obtained segmentation +masks for each image using [DeepLab v2](https://arxiv.org/abs/1606.00915) +trained on [COCO-Stuff](https://arxiv.org/abs/1612.03716). We used a [PyTorch +reimplementation](https://github.com/kazuto1011/deeplab-pytorch) and include an +example script for this process in `scripts/extract_segmentation.py`. + +### COCO +Create a symlink `data/coco` containing the images from the 2017 split in +`train2017` and `val2017`, and their annotations in `annotations`. Files can be +obtained from the [COCO webpage](https://cocodataset.org/). In addition, we use +the [Stuff+thing PNG-style annotations on COCO 2017 +trainval](http://calvin.inf.ed.ac.uk/wp-content/uploads/data/cocostuffdataset/stuffthingmaps_trainval2017.zip) +annotations from [COCO-Stuff](https://github.com/nightrome/cocostuff), which +should be placed under `data/cocostuffthings`. + +### ADE20k +Create a symlink `data/ade20k_root` containing the contents of +[ADEChallengeData2016.zip](http://data.csail.mit.edu/places/ADEchallenge/ADEChallengeData2016.zip) +from the [MIT Scene Parsing Benchmark](http://sceneparsing.csail.mit.edu/). + +## Training models + +### FacesHQ + +Train a VQGAN with +``` +python main.py --base configs/faceshq_vqgan.yaml -t True --gpus 0, +``` + +Then, adjust the checkpoint path of the config key +`model.params.first_stage_config.params.ckpt_path` in +`configs/faceshq_transformer.yaml` (or download +[2020-11-09T13-33-36_faceshq_vqgan](https://k00.fr/uxy5usa9) and place into `logs`, which +corresponds to the preconfigured checkpoint path), then run +``` +python main.py --base configs/faceshq_transformer.yaml -t True --gpus 0, +``` + +### D-RIN + +Train a VQGAN on ImageNet with +``` +python main.py --base configs/imagenet_vqgan.yaml -t True --gpus 0, +``` + +or download a pretrained one from [2020-09-23T17-56-33_imagenet_vqgan](https://k00.fr/u0j2dtac) +and place under `logs`. If you trained your own, adjust the path in the config +key `model.params.first_stage_config.params.ckpt_path` of +`configs/drin_transformer.yaml`. + +Train a VQGAN on Depth Maps of ImageNet with +``` +python main.py --base configs/imagenetdepth_vqgan.yaml -t True --gpus 0, +``` + +or download a pretrained one from [2020-11-03T15-34-24_imagenetdepth_vqgan](https://k00.fr/55rlxs6i) +and place under `logs`. If you trained your own, adjust the path in the config +key `model.params.cond_stage_config.params.ckpt_path` of +`configs/drin_transformer.yaml`. + +To train the transformer, run +``` +python main.py --base configs/drin_transformer.yaml -t True --gpus 0, +``` + +## More Resources +### Comparing Different First Stage Models +The reconstruction and compression capabilities of different fist stage models can be analyzed in this [colab notebook](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/reconstruction_usage.ipynb). +In particular, the notebook compares two VQGANs with a downsampling factor of f=16 for each and codebook dimensionality of 1024 and 16384, +a VQGAN with f=8 and 8192 codebook entries and the discrete autoencoder of OpenAI's [DALL-E](https://github.com/openai/DALL-E) (which has f=8 and 8192 +codebook entries). +![firststages1](assets/first_stage_squirrels.png) +![firststages2](assets/first_stage_mushrooms.png) + +### Other +- A [video summary](https://www.youtube.com/watch?v=o7dqGcLDf0A&feature=emb_imp_woyt) by [Two Minute Papers](https://www.youtube.com/channel/UCbfYPyITQ-7l4upoX8nvctg). +- A [video summary](https://www.youtube.com/watch?v=-wDSDtIAyWQ) by [Gradient Dude](https://www.youtube.com/c/GradientDude/about). +- A [weights and biases report summarizing the paper](https://wandb.ai/ayush-thakur/taming-transformer/reports/-Overview-Taming-Transformers-for-High-Resolution-Image-Synthesis---Vmlldzo0NjEyMTY) +by [ayulockin](https://github.com/ayulockin). +- A [video summary](https://www.youtube.com/watch?v=JfUTd8fjtX8&feature=emb_imp_woyt) by [What's AI](https://www.youtube.com/channel/UCUzGQrN-lyyc0BWTYoJM_Sg). +- Take a look at [ak9250's notebook](https://github.com/ak9250/taming-transformers/blob/master/tamingtransformerscolab.ipynb) if you want to run the streamlit demos on Colab. + +### Text-to-Image Optimization via CLIP +VQGAN has been successfully used as an image generator guided by the [CLIP](https://github.com/openai/CLIP) model, both for pure image generation +from scratch and image-to-image translation. We recommend the following notebooks/videos/resources: + + - [Advadnouns](https://twitter.com/advadnoun/status/1389316507134357506) Patreon and corresponding LatentVision notebooks: https://www.patreon.com/patronizeme + - The [notebook]( https://colab.research.google.com/drive/1L8oL-vLJXVcRzCFbPwOoMkPKJ8-aYdPN) of [Rivers Have Wings](https://twitter.com/RiversHaveWings). + - A [video](https://www.youtube.com/watch?v=90QDe6DQXF4&t=12s) explanation by [Dot CSV](https://www.youtube.com/channel/UCy5znSnfMsDwaLlROnZ7Qbg) (in Spanish, but English subtitles are available) + +![txt2img](assets/birddrawnbyachild.png) + +Text prompt: *'A bird drawn by a child'* + +## Shout-outs +Thanks to everyone who makes their code and models available. In particular, + +- The architecture of our VQGAN is inspired by [Denoising Diffusion Probabilistic Models](https://github.com/hojonathanho/diffusion) +- The very hackable transformer implementation [minGPT](https://github.com/karpathy/minGPT) +- The good ol' [PatchGAN](https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix) and [Learned Perceptual Similarity (LPIPS)](https://github.com/richzhang/PerceptualSimilarity) + +## BibTeX + +``` +@misc{esser2020taming, + title={Taming Transformers for High-Resolution Image Synthesis}, + author={Patrick Esser and Robin Rombach and Björn Ommer}, + year={2020}, + eprint={2012.09841}, + archivePrefix={arXiv}, + primaryClass={cs.CV} +} +``` diff --git a/sd/stablediffusion/src/taming-transformers/assets/birddrawnbyachild.png b/sd/stablediffusion/src/taming-transformers/assets/birddrawnbyachild.png new file mode 100644 index 0000000000000000000000000000000000000000..dec439d6f7dbf49e1d4aab94a4e5f32d52275f11 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/assets/birddrawnbyachild.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:165778bb85e86f8aaaed38eee4d33f62ab1ef237d890229cfa2e0685f5064127 +size 1607893 diff --git a/sd/stablediffusion/src/taming-transformers/assets/coco_scene_images_training.svg b/sd/stablediffusion/src/taming-transformers/assets/coco_scene_images_training.svg new file mode 100644 index 0000000000000000000000000000000000000000..6793487237678a6e8f32f7421cec24af547e571c --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/assets/coco_scene_images_training.svg @@ -0,0 +1,2574 @@ + + + + + + + + + + 2021-10-07T13:59:09.893851 + image/svg+xml + + + Matplotlib v3.3.3, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NLL loss + Inception score + FID + SceneFID + diff --git a/sd/stablediffusion/src/taming-transformers/assets/drin.jpg b/sd/stablediffusion/src/taming-transformers/assets/drin.jpg new file mode 100644 index 0000000000000000000000000000000000000000..77ca85c8a3a68102050064e89716436d125080ba Binary files /dev/null and b/sd/stablediffusion/src/taming-transformers/assets/drin.jpg differ diff --git a/sd/stablediffusion/src/taming-transformers/assets/faceshq.jpg b/sd/stablediffusion/src/taming-transformers/assets/faceshq.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7a8177fb3f434323cf79041a372445d4bb3b9e45 Binary files /dev/null and b/sd/stablediffusion/src/taming-transformers/assets/faceshq.jpg differ diff --git a/sd/stablediffusion/src/taming-transformers/assets/first_stage_mushrooms.png b/sd/stablediffusion/src/taming-transformers/assets/first_stage_mushrooms.png new file mode 100644 index 0000000000000000000000000000000000000000..c3a521079c3bc996eb2226598bb4a67b141c04f8 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/assets/first_stage_mushrooms.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:425218621d5e01ea30c9e51fa0969ad36c22063a405dc6f6ccb6dd8db64000a0 +size 1353846 diff --git a/sd/stablediffusion/src/taming-transformers/assets/first_stage_squirrels.png b/sd/stablediffusion/src/taming-transformers/assets/first_stage_squirrels.png new file mode 100644 index 0000000000000000000000000000000000000000..a1883fcee4aca3ee39d213587bcfa037caee3fd4 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/assets/first_stage_squirrels.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5f234ee1566d6c537339a7110a1a1df088d527812097c19ac61f01b335cd6ae +size 1424657 diff --git a/sd/stablediffusion/src/taming-transformers/assets/imagenet.png b/sd/stablediffusion/src/taming-transformers/assets/imagenet.png new file mode 100644 index 0000000000000000000000000000000000000000..fa831a2fd75344f96eefdba3e97de2d0dd56126f --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/assets/imagenet.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2057d65399435ba17f265ad7ff421a9aabfb6051dec00bec5a37383dfccb2e54 +size 1029071 diff --git a/sd/stablediffusion/src/taming-transformers/assets/lake_in_the_mountains.png b/sd/stablediffusion/src/taming-transformers/assets/lake_in_the_mountains.png new file mode 100644 index 0000000000000000000000000000000000000000..a66ea43c8f1146229eb18d8ebb9f67054438bc7c Binary files /dev/null and b/sd/stablediffusion/src/taming-transformers/assets/lake_in_the_mountains.png differ diff --git a/sd/stablediffusion/src/taming-transformers/assets/mountain.jpeg b/sd/stablediffusion/src/taming-transformers/assets/mountain.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..3df63a15199a91367067b587e0141c1c52ad778b Binary files /dev/null and b/sd/stablediffusion/src/taming-transformers/assets/mountain.jpeg differ diff --git a/sd/stablediffusion/src/taming-transformers/assets/scene_images_samples.svg b/sd/stablediffusion/src/taming-transformers/assets/scene_images_samples.svg new file mode 100644 index 0000000000000000000000000000000000000000..f646d3bdb60b3bb83f97edbed265b0ad8fb062ec --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/assets/scene_images_samples.svg @@ -0,0 +1,453 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Open Images + COCO + diff --git a/sd/stablediffusion/src/taming-transformers/assets/stormy.jpeg b/sd/stablediffusion/src/taming-transformers/assets/stormy.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..335edab19ea17e31f318540390f4575c4ff27bf4 Binary files /dev/null and b/sd/stablediffusion/src/taming-transformers/assets/stormy.jpeg differ diff --git a/sd/stablediffusion/src/taming-transformers/assets/sunset_and_ocean.jpg b/sd/stablediffusion/src/taming-transformers/assets/sunset_and_ocean.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5294e8f7b889b70d0dec79cd6775878754922838 Binary files /dev/null and b/sd/stablediffusion/src/taming-transformers/assets/sunset_and_ocean.jpg differ diff --git a/sd/stablediffusion/src/taming-transformers/assets/teaser.png b/sd/stablediffusion/src/taming-transformers/assets/teaser.png new file mode 100644 index 0000000000000000000000000000000000000000..c485153154e2aa0fd9f08773263ba13a4139868b Binary files /dev/null and b/sd/stablediffusion/src/taming-transformers/assets/teaser.png differ diff --git a/sd/stablediffusion/src/taming-transformers/configs/coco_cond_stage.yaml b/sd/stablediffusion/src/taming-transformers/configs/coco_cond_stage.yaml new file mode 100644 index 0000000000000000000000000000000000000000..18a3dde147455c281a3687b1a0b42bbbc3fb2725 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/configs/coco_cond_stage.yaml @@ -0,0 +1,49 @@ +model: + base_learning_rate: 4.5e-06 + target: taming.models.vqgan.VQSegmentationModel + params: + embed_dim: 256 + n_embed: 1024 + image_key: "segmentation" + n_labels: 183 + ddconfig: + double_z: false + z_channels: 256 + resolution: 256 + in_channels: 183 + out_ch: 183 + ch: 128 + ch_mult: + - 1 + - 1 + - 2 + - 2 + - 4 + num_res_blocks: 2 + attn_resolutions: + - 16 + dropout: 0.0 + + lossconfig: + target: taming.modules.losses.segmentation.BCELossWithQuant + params: + codebook_weight: 1.0 + +data: + target: main.DataModuleFromConfig + params: + batch_size: 12 + train: + target: taming.data.coco.CocoImagesAndCaptionsTrain + params: + size: 296 + crop_size: 256 + onehot_segmentation: true + use_stuffthing: true + validation: + target: taming.data.coco.CocoImagesAndCaptionsValidation + params: + size: 256 + crop_size: 256 + onehot_segmentation: true + use_stuffthing: true diff --git a/sd/stablediffusion/src/taming-transformers/configs/coco_scene_images_transformer.yaml b/sd/stablediffusion/src/taming-transformers/configs/coco_scene_images_transformer.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a03078de708182cc175f139078f8455ca3ec8a09 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/configs/coco_scene_images_transformer.yaml @@ -0,0 +1,80 @@ +model: + base_learning_rate: 4.5e-06 + target: taming.models.cond_transformer.Net2NetTransformer + params: + cond_stage_key: objects_bbox + transformer_config: + target: taming.modules.transformer.mingpt.GPT + params: + vocab_size: 8192 + block_size: 348 # = 256 + 92 = dim(vqgan_latent_space,16x16) + dim(conditional_builder.embedding_dim) + n_layer: 40 + n_head: 16 + n_embd: 1408 + embd_pdrop: 0.1 + resid_pdrop: 0.1 + attn_pdrop: 0.1 + first_stage_config: + target: taming.models.vqgan.VQModel + params: + ckpt_path: /path/to/coco_epoch117.ckpt # https://heibox.uni-heidelberg.de/f/78dea9589974474c97c1/ + embed_dim: 256 + n_embed: 8192 + ddconfig: + double_z: false + z_channels: 256 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 1 + - 2 + - 2 + - 4 + num_res_blocks: 2 + attn_resolutions: + - 16 + dropout: 0.0 + lossconfig: + target: taming.modules.losses.DummyLoss + cond_stage_config: + target: taming.models.dummy_cond_stage.DummyCondStage + params: + conditional_key: objects_bbox + +data: + target: main.DataModuleFromConfig + params: + batch_size: 6 + train: + target: taming.data.annotated_objects_coco.AnnotatedObjectsCoco + params: + data_path: data/coco_annotations_100 # substitute with path to full dataset + split: train + keys: [image, objects_bbox, file_name, annotations] + no_tokens: 8192 + target_image_size: 256 + min_object_area: 0.00001 + min_objects_per_image: 2 + max_objects_per_image: 30 + crop_method: random-1d + random_flip: true + use_group_parameter: true + encode_crop: true + validation: + target: taming.data.annotated_objects_coco.AnnotatedObjectsCoco + params: + data_path: data/coco_annotations_100 # substitute with path to full dataset + split: validation + keys: [image, objects_bbox, file_name, annotations] + no_tokens: 8192 + target_image_size: 256 + min_object_area: 0.00001 + min_objects_per_image: 2 + max_objects_per_image: 30 + crop_method: center + random_flip: false + use_group_parameter: true + encode_crop: true diff --git a/sd/stablediffusion/src/taming-transformers/configs/custom_vqgan.yaml b/sd/stablediffusion/src/taming-transformers/configs/custom_vqgan.yaml new file mode 100644 index 0000000000000000000000000000000000000000..908687f38325dfe49430692d668733a8e1598375 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/configs/custom_vqgan.yaml @@ -0,0 +1,43 @@ +model: + base_learning_rate: 4.5e-6 + target: taming.models.vqgan.VQModel + params: + embed_dim: 256 + n_embed: 1024 + ddconfig: + double_z: False + z_channels: 256 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: [ 1,1,2,2,4] # num_down = len(ch_mult)-1 + num_res_blocks: 2 + attn_resolutions: [16] + dropout: 0.0 + + lossconfig: + target: taming.modules.losses.vqperceptual.VQLPIPSWithDiscriminator + params: + disc_conditional: False + disc_in_channels: 3 + disc_start: 10000 + disc_weight: 0.8 + codebook_weight: 1.0 + +data: + target: main.DataModuleFromConfig + params: + batch_size: 5 + num_workers: 8 + train: + target: taming.data.custom.CustomTrain + params: + training_images_list_file: some/training.txt + size: 256 + validation: + target: taming.data.custom.CustomTest + params: + test_images_list_file: some/test.txt + size: 256 + diff --git a/sd/stablediffusion/src/taming-transformers/configs/drin_transformer.yaml b/sd/stablediffusion/src/taming-transformers/configs/drin_transformer.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bead4567d2dcc3d0f1a7b8eec823df4b427cab07 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/configs/drin_transformer.yaml @@ -0,0 +1,77 @@ +model: + base_learning_rate: 4.5e-06 + target: taming.models.cond_transformer.Net2NetTransformer + params: + cond_stage_key: depth + transformer_config: + target: taming.modules.transformer.mingpt.GPT + params: + vocab_size: 1024 + block_size: 512 + n_layer: 24 + n_head: 16 + n_embd: 1024 + first_stage_config: + target: taming.models.vqgan.VQModel + params: + ckpt_path: logs/2020-09-23T17-56-33_imagenet_vqgan/checkpoints/last.ckpt + embed_dim: 256 + n_embed: 1024 + ddconfig: + double_z: false + z_channels: 256 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 1 + - 2 + - 2 + - 4 + num_res_blocks: 2 + attn_resolutions: + - 16 + dropout: 0.0 + lossconfig: + target: taming.modules.losses.DummyLoss + cond_stage_config: + target: taming.models.vqgan.VQModel + params: + ckpt_path: logs/2020-11-03T15-34-24_imagenetdepth_vqgan/checkpoints/last.ckpt + embed_dim: 256 + n_embed: 1024 + ddconfig: + double_z: false + z_channels: 256 + resolution: 256 + in_channels: 1 + out_ch: 1 + ch: 128 + ch_mult: + - 1 + - 1 + - 2 + - 2 + - 4 + num_res_blocks: 2 + attn_resolutions: + - 16 + dropout: 0.0 + lossconfig: + target: taming.modules.losses.DummyLoss + +data: + target: main.DataModuleFromConfig + params: + batch_size: 2 + num_workers: 8 + train: + target: taming.data.imagenet.RINTrainWithDepth + params: + size: 256 + validation: + target: taming.data.imagenet.RINValidationWithDepth + params: + size: 256 diff --git a/sd/stablediffusion/src/taming-transformers/configs/faceshq_transformer.yaml b/sd/stablediffusion/src/taming-transformers/configs/faceshq_transformer.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b93391f9c9c41d63d28dd38dbf83615552642db3 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/configs/faceshq_transformer.yaml @@ -0,0 +1,61 @@ +model: + base_learning_rate: 4.5e-06 + target: taming.models.cond_transformer.Net2NetTransformer + params: + cond_stage_key: coord + transformer_config: + target: taming.modules.transformer.mingpt.GPT + params: + vocab_size: 1024 + block_size: 512 + n_layer: 24 + n_head: 16 + n_embd: 1024 + first_stage_config: + target: taming.models.vqgan.VQModel + params: + ckpt_path: logs/2020-11-09T13-33-36_faceshq_vqgan/checkpoints/last.ckpt + embed_dim: 256 + n_embed: 1024 + ddconfig: + double_z: false + z_channels: 256 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 1 + - 2 + - 2 + - 4 + num_res_blocks: 2 + attn_resolutions: + - 16 + dropout: 0.0 + lossconfig: + target: taming.modules.losses.DummyLoss + cond_stage_config: + target: taming.modules.misc.coord.CoordStage + params: + n_embed: 1024 + down_factor: 16 + +data: + target: main.DataModuleFromConfig + params: + batch_size: 2 + num_workers: 8 + train: + target: taming.data.faceshq.FacesHQTrain + params: + size: 256 + crop_size: 256 + coord: True + validation: + target: taming.data.faceshq.FacesHQValidation + params: + size: 256 + crop_size: 256 + coord: True diff --git a/sd/stablediffusion/src/taming-transformers/configs/faceshq_vqgan.yaml b/sd/stablediffusion/src/taming-transformers/configs/faceshq_vqgan.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3960f784551bfd9caddb5b084fc592c6eed6483b --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/configs/faceshq_vqgan.yaml @@ -0,0 +1,42 @@ +model: + base_learning_rate: 4.5e-6 + target: taming.models.vqgan.VQModel + params: + embed_dim: 256 + n_embed: 1024 + ddconfig: + double_z: False + z_channels: 256 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: [ 1,1,2,2,4] # num_down = len(ch_mult)-1 + num_res_blocks: 2 + attn_resolutions: [16] + dropout: 0.0 + + lossconfig: + target: taming.modules.losses.vqperceptual.VQLPIPSWithDiscriminator + params: + disc_conditional: False + disc_in_channels: 3 + disc_start: 30001 + disc_weight: 0.8 + codebook_weight: 1.0 + +data: + target: main.DataModuleFromConfig + params: + batch_size: 3 + num_workers: 8 + train: + target: taming.data.faceshq.FacesHQTrain + params: + size: 256 + crop_size: 256 + validation: + target: taming.data.faceshq.FacesHQValidation + params: + size: 256 + crop_size: 256 diff --git a/sd/stablediffusion/src/taming-transformers/configs/imagenet_vqgan.yaml b/sd/stablediffusion/src/taming-transformers/configs/imagenet_vqgan.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f6dc21ff6de9a26474fa18a0d496a4a0b9bb0837 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/configs/imagenet_vqgan.yaml @@ -0,0 +1,42 @@ +model: + base_learning_rate: 4.5e-6 + target: taming.models.vqgan.VQModel + params: + embed_dim: 256 + n_embed: 1024 + ddconfig: + double_z: False + z_channels: 256 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: [ 1,1,2,2,4] # num_down = len(ch_mult)-1 + num_res_blocks: 2 + attn_resolutions: [16] + dropout: 0.0 + + lossconfig: + target: taming.modules.losses.vqperceptual.VQLPIPSWithDiscriminator + params: + disc_conditional: False + disc_in_channels: 3 + disc_start: 250001 + disc_weight: 0.8 + codebook_weight: 1.0 + +data: + target: main.DataModuleFromConfig + params: + batch_size: 12 + num_workers: 24 + train: + target: taming.data.imagenet.ImageNetTrain + params: + config: + size: 256 + validation: + target: taming.data.imagenet.ImageNetValidation + params: + config: + size: 256 diff --git a/sd/stablediffusion/src/taming-transformers/configs/imagenetdepth_vqgan.yaml b/sd/stablediffusion/src/taming-transformers/configs/imagenetdepth_vqgan.yaml new file mode 100644 index 0000000000000000000000000000000000000000..88d2f34f1c0661e350899cf4229cdf60697baf0d --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/configs/imagenetdepth_vqgan.yaml @@ -0,0 +1,41 @@ +model: + base_learning_rate: 4.5e-6 + target: taming.models.vqgan.VQModel + params: + embed_dim: 256 + n_embed: 1024 + image_key: depth + ddconfig: + double_z: False + z_channels: 256 + resolution: 256 + in_channels: 1 + out_ch: 1 + ch: 128 + ch_mult: [ 1,1,2,2,4] # num_down = len(ch_mult)-1 + num_res_blocks: 2 + attn_resolutions: [16] + dropout: 0.0 + + lossconfig: + target: taming.modules.losses.vqperceptual.VQLPIPSWithDiscriminator + params: + disc_conditional: False + disc_in_channels: 1 + disc_start: 50001 + disc_weight: 0.75 + codebook_weight: 1.0 + +data: + target: main.DataModuleFromConfig + params: + batch_size: 3 + num_workers: 8 + train: + target: taming.data.imagenet.ImageNetTrainWithDepth + params: + size: 256 + validation: + target: taming.data.imagenet.ImageNetValidationWithDepth + params: + size: 256 diff --git a/sd/stablediffusion/src/taming-transformers/configs/open_images_scene_images_transformer.yaml b/sd/stablediffusion/src/taming-transformers/configs/open_images_scene_images_transformer.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f4e41e0d67d3e3eb17509862e063e4d626b06d4b --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/configs/open_images_scene_images_transformer.yaml @@ -0,0 +1,86 @@ +model: + base_learning_rate: 4.5e-06 + target: taming.models.cond_transformer.Net2NetTransformer + params: + cond_stage_key: objects_bbox + transformer_config: + target: taming.modules.transformer.mingpt.GPT + params: + vocab_size: 8192 + block_size: 348 # = 256 + 92 = dim(vqgan_latent_space,16x16) + dim(conditional_builder.embedding_dim) + n_layer: 36 + n_head: 16 + n_embd: 1536 + embd_pdrop: 0.1 + resid_pdrop: 0.1 + attn_pdrop: 0.1 + first_stage_config: + target: taming.models.vqgan.VQModel + params: + ckpt_path: /path/to/coco_oi_epoch12.ckpt # https://heibox.uni-heidelberg.de/f/461d9a9f4fcf48ab84f4/ + embed_dim: 256 + n_embed: 8192 + ddconfig: + double_z: false + z_channels: 256 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 1 + - 2 + - 2 + - 4 + num_res_blocks: 2 + attn_resolutions: + - 16 + dropout: 0.0 + lossconfig: + target: taming.modules.losses.DummyLoss + cond_stage_config: + target: taming.models.dummy_cond_stage.DummyCondStage + params: + conditional_key: objects_bbox + +data: + target: main.DataModuleFromConfig + params: + batch_size: 6 + train: + target: taming.data.annotated_objects_open_images.AnnotatedObjectsOpenImages + params: + data_path: data/open_images_annotations_100 # substitute with path to full dataset + split: train + keys: [image, objects_bbox, file_name, annotations] + no_tokens: 8192 + target_image_size: 256 + category_allow_list_target: taming.data.open_images_helper.top_300_classes_plus_coco_compatibility + category_mapping_target: taming.data.open_images_helper.open_images_unify_categories_for_coco + min_object_area: 0.0001 + min_objects_per_image: 2 + max_objects_per_image: 30 + crop_method: random-2d + random_flip: true + use_group_parameter: true + use_additional_parameters: true + encode_crop: true + validation: + target: taming.data.annotated_objects_open_images.AnnotatedObjectsOpenImages + params: + data_path: data/open_images_annotations_100 # substitute with path to full dataset + split: validation + keys: [image, objects_bbox, file_name, annotations] + no_tokens: 8192 + target_image_size: 256 + category_allow_list_target: taming.data.open_images_helper.top_300_classes_plus_coco_compatibility + category_mapping_target: taming.data.open_images_helper.open_images_unify_categories_for_coco + min_object_area: 0.0001 + min_objects_per_image: 2 + max_objects_per_image: 30 + crop_method: center + random_flip: false + use_group_parameter: true + use_additional_parameters: true + encode_crop: true diff --git a/sd/stablediffusion/src/taming-transformers/configs/sflckr_cond_stage.yaml b/sd/stablediffusion/src/taming-transformers/configs/sflckr_cond_stage.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d48b50a700c4b44098ce4f3c752d5a4d7158f8a9 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/configs/sflckr_cond_stage.yaml @@ -0,0 +1,43 @@ +model: + base_learning_rate: 4.5e-06 + target: taming.models.vqgan.VQSegmentationModel + params: + embed_dim: 256 + n_embed: 1024 + image_key: "segmentation" + n_labels: 182 + ddconfig: + double_z: false + z_channels: 256 + resolution: 256 + in_channels: 182 + out_ch: 182 + ch: 128 + ch_mult: + - 1 + - 1 + - 2 + - 2 + - 4 + num_res_blocks: 2 + attn_resolutions: + - 16 + dropout: 0.0 + + lossconfig: + target: taming.modules.losses.segmentation.BCELossWithQuant + params: + codebook_weight: 1.0 + +data: + target: cutlit.DataModuleFromConfig + params: + batch_size: 12 + train: + target: taming.data.sflckr.Examples # adjust + params: + size: 256 + validation: + target: taming.data.sflckr.Examples # adjust + params: + size: 256 diff --git a/sd/stablediffusion/src/taming-transformers/environment.yaml b/sd/stablediffusion/src/taming-transformers/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3fbba586e55dbe64184d006319fad969805ef16f --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/environment.yaml @@ -0,0 +1,25 @@ +name: taming +channels: + - pytorch + - defaults +dependencies: + - python=3.8.5 + - pip=20.3 + - cudatoolkit=10.2 + - pytorch=1.7.0 + - torchvision=0.8.1 + - numpy=1.19.2 + - pip: + - albumentations==0.4.3 + - opencv-python==4.1.2.30 + - pudb==2019.2 + - imageio==2.9.0 + - imageio-ffmpeg==0.4.2 + - pytorch-lightning==1.0.8 + - omegaconf==2.0.0 + - test-tube>=0.7.5 + - streamlit>=0.73.1 + - einops==0.3.0 + - more-itertools>=8.0.0 + - transformers==4.3.1 + - -e . diff --git a/sd/stablediffusion/src/taming-transformers/main.py b/sd/stablediffusion/src/taming-transformers/main.py new file mode 100644 index 0000000000000000000000000000000000000000..5a992a65d1465457f2685ec9f5f63f316d9e3164 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/main.py @@ -0,0 +1,585 @@ +import argparse, os, sys, datetime, glob, importlib +from omegaconf import OmegaConf +import numpy as np +from PIL import Image +import torch +import torchvision +from torch.utils.data import random_split, DataLoader, Dataset +import pytorch_lightning as pl +from pytorch_lightning import seed_everything +from pytorch_lightning.trainer import Trainer +from pytorch_lightning.callbacks import ModelCheckpoint, Callback, LearningRateMonitor +from pytorch_lightning.utilities import rank_zero_only + +from taming.data.utils import custom_collate + + +def get_obj_from_str(string, reload=False): + module, cls = string.rsplit(".", 1) + if reload: + module_imp = importlib.import_module(module) + importlib.reload(module_imp) + return getattr(importlib.import_module(module, package=None), cls) + + +def get_parser(**parser_kwargs): + def str2bool(v): + if isinstance(v, bool): + return v + if v.lower() in ("yes", "true", "t", "y", "1"): + return True + elif v.lower() in ("no", "false", "f", "n", "0"): + return False + else: + raise argparse.ArgumentTypeError("Boolean value expected.") + + parser = argparse.ArgumentParser(**parser_kwargs) + parser.add_argument( + "-n", + "--name", + type=str, + const=True, + default="", + nargs="?", + help="postfix for logdir", + ) + parser.add_argument( + "-r", + "--resume", + type=str, + const=True, + default="", + nargs="?", + help="resume from logdir or checkpoint in logdir", + ) + parser.add_argument( + "-b", + "--base", + nargs="*", + metavar="base_config.yaml", + help="paths to base configs. Loaded from left-to-right. " + "Parameters can be overwritten or added with command-line options of the form `--key value`.", + default=list(), + ) + parser.add_argument( + "-t", + "--train", + type=str2bool, + const=True, + default=False, + nargs="?", + help="train", + ) + parser.add_argument( + "--no-test", + type=str2bool, + const=True, + default=False, + nargs="?", + help="disable test", + ) + parser.add_argument("-p", "--project", help="name of new or path to existing project") + parser.add_argument( + "-d", + "--debug", + type=str2bool, + nargs="?", + const=True, + default=False, + help="enable post-mortem debugging", + ) + parser.add_argument( + "-s", + "--seed", + type=int, + default=23, + help="seed for seed_everything", + ) + parser.add_argument( + "-f", + "--postfix", + type=str, + default="", + help="post-postfix for default name", + ) + + return parser + + +def nondefault_trainer_args(opt): + parser = argparse.ArgumentParser() + parser = Trainer.add_argparse_args(parser) + args = parser.parse_args([]) + return sorted(k for k in vars(args) if getattr(opt, k) != getattr(args, k)) + + +def instantiate_from_config(config): + if not "target" in config: + raise KeyError("Expected key `target` to instantiate.") + return get_obj_from_str(config["target"])(**config.get("params", dict())) + + +class WrappedDataset(Dataset): + """Wraps an arbitrary object with __len__ and __getitem__ into a pytorch dataset""" + def __init__(self, dataset): + self.data = dataset + + def __len__(self): + return len(self.data) + + def __getitem__(self, idx): + return self.data[idx] + + +class DataModuleFromConfig(pl.LightningDataModule): + def __init__(self, batch_size, train=None, validation=None, test=None, + wrap=False, num_workers=None): + super().__init__() + self.batch_size = batch_size + self.dataset_configs = dict() + self.num_workers = num_workers if num_workers is not None else batch_size*2 + if train is not None: + self.dataset_configs["train"] = train + self.train_dataloader = self._train_dataloader + if validation is not None: + self.dataset_configs["validation"] = validation + self.val_dataloader = self._val_dataloader + if test is not None: + self.dataset_configs["test"] = test + self.test_dataloader = self._test_dataloader + self.wrap = wrap + + def prepare_data(self): + for data_cfg in self.dataset_configs.values(): + instantiate_from_config(data_cfg) + + def setup(self, stage=None): + self.datasets = dict( + (k, instantiate_from_config(self.dataset_configs[k])) + for k in self.dataset_configs) + if self.wrap: + for k in self.datasets: + self.datasets[k] = WrappedDataset(self.datasets[k]) + + def _train_dataloader(self): + return DataLoader(self.datasets["train"], batch_size=self.batch_size, + num_workers=self.num_workers, shuffle=True, collate_fn=custom_collate) + + def _val_dataloader(self): + return DataLoader(self.datasets["validation"], + batch_size=self.batch_size, + num_workers=self.num_workers, collate_fn=custom_collate) + + def _test_dataloader(self): + return DataLoader(self.datasets["test"], batch_size=self.batch_size, + num_workers=self.num_workers, collate_fn=custom_collate) + + +class SetupCallback(Callback): + def __init__(self, resume, now, logdir, ckptdir, cfgdir, config, lightning_config): + super().__init__() + self.resume = resume + self.now = now + self.logdir = logdir + self.ckptdir = ckptdir + self.cfgdir = cfgdir + self.config = config + self.lightning_config = lightning_config + + def on_pretrain_routine_start(self, trainer, pl_module): + if trainer.global_rank == 0: + # Create logdirs and save configs + os.makedirs(self.logdir, exist_ok=True) + os.makedirs(self.ckptdir, exist_ok=True) + os.makedirs(self.cfgdir, exist_ok=True) + + print("Project config") + print(self.config.pretty()) + OmegaConf.save(self.config, + os.path.join(self.cfgdir, "{}-project.yaml".format(self.now))) + + print("Lightning config") + print(self.lightning_config.pretty()) + OmegaConf.save(OmegaConf.create({"lightning": self.lightning_config}), + os.path.join(self.cfgdir, "{}-lightning.yaml".format(self.now))) + + else: + # ModelCheckpoint callback created log directory --- remove it + if not self.resume and os.path.exists(self.logdir): + dst, name = os.path.split(self.logdir) + dst = os.path.join(dst, "child_runs", name) + os.makedirs(os.path.split(dst)[0], exist_ok=True) + try: + os.rename(self.logdir, dst) + except FileNotFoundError: + pass + + +class ImageLogger(Callback): + def __init__(self, batch_frequency, max_images, clamp=True, increase_log_steps=True): + super().__init__() + self.batch_freq = batch_frequency + self.max_images = max_images + self.logger_log_images = { + pl.loggers.WandbLogger: self._wandb, + pl.loggers.TestTubeLogger: self._testtube, + } + self.log_steps = [2 ** n for n in range(int(np.log2(self.batch_freq)) + 1)] + if not increase_log_steps: + self.log_steps = [self.batch_freq] + self.clamp = clamp + + @rank_zero_only + def _wandb(self, pl_module, images, batch_idx, split): + raise ValueError("No way wandb") + grids = dict() + for k in images: + grid = torchvision.utils.make_grid(images[k]) + grids[f"{split}/{k}"] = wandb.Image(grid) + pl_module.logger.experiment.log(grids) + + @rank_zero_only + def _testtube(self, pl_module, images, batch_idx, split): + for k in images: + grid = torchvision.utils.make_grid(images[k]) + grid = (grid+1.0)/2.0 # -1,1 -> 0,1; c,h,w + + tag = f"{split}/{k}" + pl_module.logger.experiment.add_image( + tag, grid, + global_step=pl_module.global_step) + + @rank_zero_only + def log_local(self, save_dir, split, images, + global_step, current_epoch, batch_idx): + root = os.path.join(save_dir, "images", split) + for k in images: + grid = torchvision.utils.make_grid(images[k], nrow=4) + + grid = (grid+1.0)/2.0 # -1,1 -> 0,1; c,h,w + grid = grid.transpose(0,1).transpose(1,2).squeeze(-1) + grid = grid.numpy() + grid = (grid*255).astype(np.uint8) + filename = "{}_gs-{:06}_e-{:06}_b-{:06}.png".format( + k, + global_step, + current_epoch, + batch_idx) + path = os.path.join(root, filename) + os.makedirs(os.path.split(path)[0], exist_ok=True) + Image.fromarray(grid).save(path) + + def log_img(self, pl_module, batch, batch_idx, split="train"): + if (self.check_frequency(batch_idx) and # batch_idx % self.batch_freq == 0 + hasattr(pl_module, "log_images") and + callable(pl_module.log_images) and + self.max_images > 0): + logger = type(pl_module.logger) + + is_train = pl_module.training + if is_train: + pl_module.eval() + + with torch.no_grad(): + images = pl_module.log_images(batch, split=split, pl_module=pl_module) + + for k in images: + N = min(images[k].shape[0], self.max_images) + images[k] = images[k][:N] + if isinstance(images[k], torch.Tensor): + images[k] = images[k].detach().cpu() + if self.clamp: + images[k] = torch.clamp(images[k], -1., 1.) + + self.log_local(pl_module.logger.save_dir, split, images, + pl_module.global_step, pl_module.current_epoch, batch_idx) + + logger_log_images = self.logger_log_images.get(logger, lambda *args, **kwargs: None) + logger_log_images(pl_module, images, pl_module.global_step, split) + + if is_train: + pl_module.train() + + def check_frequency(self, batch_idx): + if (batch_idx % self.batch_freq) == 0 or (batch_idx in self.log_steps): + try: + self.log_steps.pop(0) + except IndexError: + pass + return True + return False + + def on_train_batch_end(self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx): + self.log_img(pl_module, batch, batch_idx, split="train") + + def on_validation_batch_end(self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx): + self.log_img(pl_module, batch, batch_idx, split="val") + + + +if __name__ == "__main__": + # custom parser to specify config files, train, test and debug mode, + # postfix, resume. + # `--key value` arguments are interpreted as arguments to the trainer. + # `nested.key=value` arguments are interpreted as config parameters. + # configs are merged from left-to-right followed by command line parameters. + + # model: + # base_learning_rate: float + # target: path to lightning module + # params: + # key: value + # data: + # target: main.DataModuleFromConfig + # params: + # batch_size: int + # wrap: bool + # train: + # target: path to train dataset + # params: + # key: value + # validation: + # target: path to validation dataset + # params: + # key: value + # test: + # target: path to test dataset + # params: + # key: value + # lightning: (optional, has sane defaults and can be specified on cmdline) + # trainer: + # additional arguments to trainer + # logger: + # logger to instantiate + # modelcheckpoint: + # modelcheckpoint to instantiate + # callbacks: + # callback1: + # target: importpath + # params: + # key: value + + now = datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S") + + # add cwd for convenience and to make classes in this file available when + # running as `python main.py` + # (in particular `main.DataModuleFromConfig`) + sys.path.append(os.getcwd()) + + parser = get_parser() + parser = Trainer.add_argparse_args(parser) + + opt, unknown = parser.parse_known_args() + if opt.name and opt.resume: + raise ValueError( + "-n/--name and -r/--resume cannot be specified both." + "If you want to resume training in a new log folder, " + "use -n/--name in combination with --resume_from_checkpoint" + ) + if opt.resume: + if not os.path.exists(opt.resume): + raise ValueError("Cannot find {}".format(opt.resume)) + if os.path.isfile(opt.resume): + paths = opt.resume.split("/") + idx = len(paths)-paths[::-1].index("logs")+1 + logdir = "/".join(paths[:idx]) + ckpt = opt.resume + else: + assert os.path.isdir(opt.resume), opt.resume + logdir = opt.resume.rstrip("/") + ckpt = os.path.join(logdir, "checkpoints", "last.ckpt") + + opt.resume_from_checkpoint = ckpt + base_configs = sorted(glob.glob(os.path.join(logdir, "configs/*.yaml"))) + opt.base = base_configs+opt.base + _tmp = logdir.split("/") + nowname = _tmp[_tmp.index("logs")+1] + else: + if opt.name: + name = "_"+opt.name + elif opt.base: + cfg_fname = os.path.split(opt.base[0])[-1] + cfg_name = os.path.splitext(cfg_fname)[0] + name = "_"+cfg_name + else: + name = "" + nowname = now+name+opt.postfix + logdir = os.path.join("logs", nowname) + + ckptdir = os.path.join(logdir, "checkpoints") + cfgdir = os.path.join(logdir, "configs") + seed_everything(opt.seed) + + try: + # init and save configs + configs = [OmegaConf.load(cfg) for cfg in opt.base] + cli = OmegaConf.from_dotlist(unknown) + config = OmegaConf.merge(*configs, cli) + lightning_config = config.pop("lightning", OmegaConf.create()) + # merge trainer cli with config + trainer_config = lightning_config.get("trainer", OmegaConf.create()) + # default to ddp + trainer_config["distributed_backend"] = "ddp" + for k in nondefault_trainer_args(opt): + trainer_config[k] = getattr(opt, k) + if not "gpus" in trainer_config: + del trainer_config["distributed_backend"] + cpu = True + else: + gpuinfo = trainer_config["gpus"] + print(f"Running on GPUs {gpuinfo}") + cpu = False + trainer_opt = argparse.Namespace(**trainer_config) + lightning_config.trainer = trainer_config + + # model + model = instantiate_from_config(config.model) + + # trainer and callbacks + trainer_kwargs = dict() + + # default logger configs + # NOTE wandb < 0.10.0 interferes with shutdown + # wandb >= 0.10.0 seems to fix it but still interferes with pudb + # debugging (wrongly sized pudb ui) + # thus prefer testtube for now + default_logger_cfgs = { + "wandb": { + "target": "pytorch_lightning.loggers.WandbLogger", + "params": { + "name": nowname, + "save_dir": logdir, + "offline": opt.debug, + "id": nowname, + } + }, + "testtube": { + "target": "pytorch_lightning.loggers.TestTubeLogger", + "params": { + "name": "testtube", + "save_dir": logdir, + } + }, + } + default_logger_cfg = default_logger_cfgs["testtube"] + logger_cfg = lightning_config.logger or OmegaConf.create() + logger_cfg = OmegaConf.merge(default_logger_cfg, logger_cfg) + trainer_kwargs["logger"] = instantiate_from_config(logger_cfg) + + # modelcheckpoint - use TrainResult/EvalResult(checkpoint_on=metric) to + # specify which metric is used to determine best models + default_modelckpt_cfg = { + "target": "pytorch_lightning.callbacks.ModelCheckpoint", + "params": { + "dirpath": ckptdir, + "filename": "{epoch:06}", + "verbose": True, + "save_last": True, + } + } + if hasattr(model, "monitor"): + print(f"Monitoring {model.monitor} as checkpoint metric.") + default_modelckpt_cfg["params"]["monitor"] = model.monitor + default_modelckpt_cfg["params"]["save_top_k"] = 3 + + modelckpt_cfg = lightning_config.modelcheckpoint or OmegaConf.create() + modelckpt_cfg = OmegaConf.merge(default_modelckpt_cfg, modelckpt_cfg) + trainer_kwargs["checkpoint_callback"] = instantiate_from_config(modelckpt_cfg) + + # add callback which sets up log directory + default_callbacks_cfg = { + "setup_callback": { + "target": "main.SetupCallback", + "params": { + "resume": opt.resume, + "now": now, + "logdir": logdir, + "ckptdir": ckptdir, + "cfgdir": cfgdir, + "config": config, + "lightning_config": lightning_config, + } + }, + "image_logger": { + "target": "main.ImageLogger", + "params": { + "batch_frequency": 750, + "max_images": 4, + "clamp": True + } + }, + "learning_rate_logger": { + "target": "main.LearningRateMonitor", + "params": { + "logging_interval": "step", + #"log_momentum": True + } + }, + } + callbacks_cfg = lightning_config.callbacks or OmegaConf.create() + callbacks_cfg = OmegaConf.merge(default_callbacks_cfg, callbacks_cfg) + trainer_kwargs["callbacks"] = [instantiate_from_config(callbacks_cfg[k]) for k in callbacks_cfg] + + trainer = Trainer.from_argparse_args(trainer_opt, **trainer_kwargs) + + # data + data = instantiate_from_config(config.data) + # NOTE according to https://pytorch-lightning.readthedocs.io/en/latest/datamodules.html + # calling these ourselves should not be necessary but it is. + # lightning still takes care of proper multiprocessing though + data.prepare_data() + data.setup() + + # configure learning rate + bs, base_lr = config.data.params.batch_size, config.model.base_learning_rate + if not cpu: + ngpu = len(lightning_config.trainer.gpus.strip(",").split(',')) + else: + ngpu = 1 + accumulate_grad_batches = lightning_config.trainer.accumulate_grad_batches or 1 + print(f"accumulate_grad_batches = {accumulate_grad_batches}") + lightning_config.trainer.accumulate_grad_batches = accumulate_grad_batches + model.learning_rate = accumulate_grad_batches * ngpu * bs * base_lr + print("Setting learning rate to {:.2e} = {} (accumulate_grad_batches) * {} (num_gpus) * {} (batchsize) * {:.2e} (base_lr)".format( + model.learning_rate, accumulate_grad_batches, ngpu, bs, base_lr)) + + # allow checkpointing via USR1 + def melk(*args, **kwargs): + # run all checkpoint hooks + if trainer.global_rank == 0: + print("Summoning checkpoint.") + ckpt_path = os.path.join(ckptdir, "last.ckpt") + trainer.save_checkpoint(ckpt_path) + + def divein(*args, **kwargs): + if trainer.global_rank == 0: + import pudb; pudb.set_trace() + + import signal + signal.signal(signal.SIGUSR1, melk) + signal.signal(signal.SIGUSR2, divein) + + # run + if opt.train: + try: + trainer.fit(model, data) + except Exception: + melk() + raise + if not opt.no_test and not trainer.interrupted: + trainer.test(model, data) + except Exception: + if opt.debug and trainer.global_rank==0: + try: + import pudb as debugger + except ImportError: + import pdb as debugger + debugger.post_mortem() + raise + finally: + # move newly created debug project to debug_runs + if opt.debug and not opt.resume and trainer.global_rank==0: + dst, name = os.path.split(logdir) + dst = os.path.join(dst, "debug_runs", name) + os.makedirs(os.path.split(dst)[0], exist_ok=True) + os.rename(logdir, dst) diff --git a/sd/stablediffusion/src/taming-transformers/scripts/extract_depth.py b/sd/stablediffusion/src/taming-transformers/scripts/extract_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..d6aa0d80c63a3e580fa28e0f2c7af4e9ae003b64 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/scripts/extract_depth.py @@ -0,0 +1,112 @@ +import os +import torch +import numpy as np +from tqdm import trange +from PIL import Image + + +def get_state(gpu): + import torch + midas = torch.hub.load("intel-isl/MiDaS", "MiDaS") + if gpu: + midas.cuda() + midas.eval() + + midas_transforms = torch.hub.load("intel-isl/MiDaS", "transforms") + transform = midas_transforms.default_transform + + state = {"model": midas, + "transform": transform} + return state + + +def depth_to_rgba(x): + assert x.dtype == np.float32 + assert len(x.shape) == 2 + y = x.copy() + y.dtype = np.uint8 + y = y.reshape(x.shape+(4,)) + return np.ascontiguousarray(y) + + +def rgba_to_depth(x): + assert x.dtype == np.uint8 + assert len(x.shape) == 3 and x.shape[2] == 4 + y = x.copy() + y.dtype = np.float32 + y = y.reshape(x.shape[:2]) + return np.ascontiguousarray(y) + + +def run(x, state): + model = state["model"] + transform = state["transform"] + hw = x.shape[:2] + with torch.no_grad(): + prediction = model(transform((x + 1.0) * 127.5).cuda()) + prediction = torch.nn.functional.interpolate( + prediction.unsqueeze(1), + size=hw, + mode="bicubic", + align_corners=False, + ).squeeze() + output = prediction.cpu().numpy() + return output + + +def get_filename(relpath, level=-2): + # save class folder structure and filename: + fn = relpath.split(os.sep)[level:] + folder = fn[-2] + file = fn[-1].split('.')[0] + return folder, file + + +def save_depth(dataset, path, debug=False): + os.makedirs(path) + N = len(dset) + if debug: + N = 10 + state = get_state(gpu=True) + for idx in trange(N, desc="Data"): + ex = dataset[idx] + image, relpath = ex["image"], ex["relpath"] + folder, filename = get_filename(relpath) + # prepare + folderabspath = os.path.join(path, folder) + os.makedirs(folderabspath, exist_ok=True) + savepath = os.path.join(folderabspath, filename) + # run model + xout = run(image, state) + I = depth_to_rgba(xout) + Image.fromarray(I).save("{}.png".format(savepath)) + + +if __name__ == "__main__": + from taming.data.imagenet import ImageNetTrain, ImageNetValidation + out = "data/imagenet_depth" + if not os.path.exists(out): + print("Please create a folder or symlink '{}' to extract depth data ".format(out) + + "(be prepared that the output size will be larger than ImageNet itself).") + exit(1) + + # go + dset = ImageNetValidation() + abspath = os.path.join(out, "val") + if os.path.exists(abspath): + print("{} exists - not doing anything.".format(abspath)) + else: + print("preparing {}".format(abspath)) + save_depth(dset, abspath) + print("done with validation split") + + dset = ImageNetTrain() + abspath = os.path.join(out, "train") + if os.path.exists(abspath): + print("{} exists - not doing anything.".format(abspath)) + else: + print("preparing {}".format(abspath)) + save_depth(dset, abspath) + print("done with train split") + + print("done done.") diff --git a/sd/stablediffusion/src/taming-transformers/scripts/extract_segmentation.py b/sd/stablediffusion/src/taming-transformers/scripts/extract_segmentation.py new file mode 100644 index 0000000000000000000000000000000000000000..235b3c4b4575981b7533ce18bceaff97e05b55f9 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/scripts/extract_segmentation.py @@ -0,0 +1,130 @@ +import sys, os +import numpy as np +import scipy +import torch +import torch.nn as nn +from scipy import ndimage +from tqdm import tqdm, trange +from PIL import Image +import torch.hub +import torchvision +import torch.nn.functional as F + +# download deeplabv2_resnet101_msc-cocostuff164k-100000.pth from +# https://github.com/kazuto1011/deeplab-pytorch/releases/download/v1.0/deeplabv2_resnet101_msc-cocostuff164k-100000.pth +# and put the path here +CKPT_PATH = "TODO" + +rescale = lambda x: (x + 1.) / 2. + +def rescale_bgr(x): + x = (x+1)*127.5 + x = torch.flip(x, dims=[0]) + return x + + +class COCOStuffSegmenter(nn.Module): + def __init__(self, config): + super().__init__() + self.config = config + self.n_labels = 182 + model = torch.hub.load("kazuto1011/deeplab-pytorch", "deeplabv2_resnet101", n_classes=self.n_labels) + ckpt_path = CKPT_PATH + model.load_state_dict(torch.load(ckpt_path)) + self.model = model + + normalize = torchvision.transforms.Normalize(mean=self.mean, std=self.std) + self.image_transform = torchvision.transforms.Compose([ + torchvision.transforms.Lambda(lambda image: torch.stack( + [normalize(rescale_bgr(x)) for x in image])) + ]) + + def forward(self, x, upsample=None): + x = self._pre_process(x) + x = self.model(x) + if upsample is not None: + x = torch.nn.functional.upsample_bilinear(x, size=upsample) + return x + + def _pre_process(self, x): + x = self.image_transform(x) + return x + + @property + def mean(self): + # bgr + return [104.008, 116.669, 122.675] + + @property + def std(self): + return [1.0, 1.0, 1.0] + + @property + def input_size(self): + return [3, 224, 224] + + +def run_model(img, model): + model = model.eval() + with torch.no_grad(): + segmentation = model(img, upsample=(img.shape[2], img.shape[3])) + segmentation = torch.argmax(segmentation, dim=1, keepdim=True) + return segmentation.detach().cpu() + + +def get_input(batch, k): + x = batch[k] + if len(x.shape) == 3: + x = x[..., None] + x = x.permute(0, 3, 1, 2).to(memory_format=torch.contiguous_format) + return x.float() + + +def save_segmentation(segmentation, path): + # --> class label to uint8, save as png + os.makedirs(os.path.dirname(path), exist_ok=True) + assert len(segmentation.shape)==4 + assert segmentation.shape[0]==1 + for seg in segmentation: + seg = seg.permute(1,2,0).numpy().squeeze().astype(np.uint8) + seg = Image.fromarray(seg) + seg.save(path) + + +def iterate_dataset(dataloader, destpath, model): + os.makedirs(destpath, exist_ok=True) + num_processed = 0 + for i, batch in tqdm(enumerate(dataloader), desc="Data"): + try: + img = get_input(batch, "image") + img = img.cuda() + seg = run_model(img, model) + + path = batch["relative_file_path_"][0] + path = os.path.splitext(path)[0] + + path = os.path.join(destpath, path + ".png") + save_segmentation(seg, path) + num_processed += 1 + except Exception as e: + print(e) + print("but anyhow..") + + print("Processed {} files. Bye.".format(num_processed)) + + +from taming.data.sflckr import Examples +from torch.utils.data import DataLoader + +if __name__ == "__main__": + dest = sys.argv[1] + batchsize = 1 + print("Running with batch-size {}, saving to {}...".format(batchsize, dest)) + + model = COCOStuffSegmenter({}).cuda() + print("Instantiated model.") + + dataset = Examples() + dloader = DataLoader(dataset, batch_size=batchsize) + iterate_dataset(dataloader=dloader, destpath=dest, model=model) + print("done.") diff --git a/sd/stablediffusion/src/taming-transformers/scripts/extract_submodel.py b/sd/stablediffusion/src/taming-transformers/scripts/extract_submodel.py new file mode 100644 index 0000000000000000000000000000000000000000..559bc5e04281a7cf833a82e3cd48627b20f1a76d --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/scripts/extract_submodel.py @@ -0,0 +1,17 @@ +import torch +import sys + +if __name__ == "__main__": + inpath = sys.argv[1] + outpath = sys.argv[2] + submodel = "cond_stage_model" + if len(sys.argv) > 3: + submodel = sys.argv[3] + + print("Extracting {} from {} to {}.".format(submodel, inpath, outpath)) + + sd = torch.load(inpath, map_location="cpu") + new_sd = {"state_dict": dict((k.split(".", 1)[-1],v) + for k,v in sd["state_dict"].items() + if k.startswith("cond_stage_model"))} + torch.save(new_sd, outpath) diff --git a/sd/stablediffusion/src/taming-transformers/scripts/make_samples.py b/sd/stablediffusion/src/taming-transformers/scripts/make_samples.py new file mode 100644 index 0000000000000000000000000000000000000000..5e4d6995cd41cc07b4e8861cb941c6052b0f5517 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/scripts/make_samples.py @@ -0,0 +1,292 @@ +import argparse, os, sys, glob, math, time +import torch +import numpy as np +from omegaconf import OmegaConf +from PIL import Image +from main import instantiate_from_config, DataModuleFromConfig +from torch.utils.data import DataLoader +from torch.utils.data.dataloader import default_collate +from tqdm import trange + + +def save_image(x, path): + c,h,w = x.shape + assert c==3 + x = ((x.detach().cpu().numpy().transpose(1,2,0)+1.0)*127.5).clip(0,255).astype(np.uint8) + Image.fromarray(x).save(path) + + +@torch.no_grad() +def run_conditional(model, dsets, outdir, top_k, temperature, batch_size=1): + if len(dsets.datasets) > 1: + split = sorted(dsets.datasets.keys())[0] + dset = dsets.datasets[split] + else: + dset = next(iter(dsets.datasets.values())) + print("Dataset: ", dset.__class__.__name__) + for start_idx in trange(0,len(dset)-batch_size+1,batch_size): + indices = list(range(start_idx, start_idx+batch_size)) + example = default_collate([dset[i] for i in indices]) + + x = model.get_input("image", example).to(model.device) + for i in range(x.shape[0]): + save_image(x[i], os.path.join(outdir, "originals", + "{:06}.png".format(indices[i]))) + + cond_key = model.cond_stage_key + c = model.get_input(cond_key, example).to(model.device) + + scale_factor = 1.0 + quant_z, z_indices = model.encode_to_z(x) + quant_c, c_indices = model.encode_to_c(c) + + cshape = quant_z.shape + + xrec = model.first_stage_model.decode(quant_z) + for i in range(xrec.shape[0]): + save_image(xrec[i], os.path.join(outdir, "reconstructions", + "{:06}.png".format(indices[i]))) + + if cond_key == "segmentation": + # get image from segmentation mask + num_classes = c.shape[1] + c = torch.argmax(c, dim=1, keepdim=True) + c = torch.nn.functional.one_hot(c, num_classes=num_classes) + c = c.squeeze(1).permute(0, 3, 1, 2).float() + c = model.cond_stage_model.to_rgb(c) + + idx = z_indices + + half_sample = False + if half_sample: + start = idx.shape[1]//2 + else: + start = 0 + + idx[:,start:] = 0 + idx = idx.reshape(cshape[0],cshape[2],cshape[3]) + start_i = start//cshape[3] + start_j = start %cshape[3] + + cidx = c_indices + cidx = cidx.reshape(quant_c.shape[0],quant_c.shape[2],quant_c.shape[3]) + + sample = True + + for i in range(start_i,cshape[2]-0): + if i <= 8: + local_i = i + elif cshape[2]-i < 8: + local_i = 16-(cshape[2]-i) + else: + local_i = 8 + for j in range(start_j,cshape[3]-0): + if j <= 8: + local_j = j + elif cshape[3]-j < 8: + local_j = 16-(cshape[3]-j) + else: + local_j = 8 + + i_start = i-local_i + i_end = i_start+16 + j_start = j-local_j + j_end = j_start+16 + patch = idx[:,i_start:i_end,j_start:j_end] + patch = patch.reshape(patch.shape[0],-1) + cpatch = cidx[:, i_start:i_end, j_start:j_end] + cpatch = cpatch.reshape(cpatch.shape[0], -1) + patch = torch.cat((cpatch, patch), dim=1) + logits,_ = model.transformer(patch[:,:-1]) + logits = logits[:, -256:, :] + logits = logits.reshape(cshape[0],16,16,-1) + logits = logits[:,local_i,local_j,:] + + logits = logits/temperature + + if top_k is not None: + logits = model.top_k_logits(logits, top_k) + # apply softmax to convert to probabilities + probs = torch.nn.functional.softmax(logits, dim=-1) + # sample from the distribution or take the most likely + if sample: + ix = torch.multinomial(probs, num_samples=1) + else: + _, ix = torch.topk(probs, k=1, dim=-1) + idx[:,i,j] = ix + + xsample = model.decode_to_img(idx[:,:cshape[2],:cshape[3]], cshape) + for i in range(xsample.shape[0]): + save_image(xsample[i], os.path.join(outdir, "samples", + "{:06}.png".format(indices[i]))) + + +def get_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + "-r", + "--resume", + type=str, + nargs="?", + help="load from logdir or checkpoint in logdir", + ) + parser.add_argument( + "-b", + "--base", + nargs="*", + metavar="base_config.yaml", + help="paths to base configs. Loaded from left-to-right. " + "Parameters can be overwritten or added with command-line options of the form `--key value`.", + default=list(), + ) + parser.add_argument( + "-c", + "--config", + nargs="?", + metavar="single_config.yaml", + help="path to single config. If specified, base configs will be ignored " + "(except for the last one if left unspecified).", + const=True, + default="", + ) + parser.add_argument( + "--ignore_base_data", + action="store_true", + help="Ignore data specification from base configs. Useful if you want " + "to specify a custom datasets on the command line.", + ) + parser.add_argument( + "--outdir", + required=True, + type=str, + help="Where to write outputs to.", + ) + parser.add_argument( + "--top_k", + type=int, + default=100, + help="Sample from among top-k predictions.", + ) + parser.add_argument( + "--temperature", + type=float, + default=1.0, + help="Sampling temperature.", + ) + return parser + + +def load_model_from_config(config, sd, gpu=True, eval_mode=True): + if "ckpt_path" in config.params: + print("Deleting the restore-ckpt path from the config...") + config.params.ckpt_path = None + if "downsample_cond_size" in config.params: + print("Deleting downsample-cond-size from the config and setting factor=0.5 instead...") + config.params.downsample_cond_size = -1 + config.params["downsample_cond_factor"] = 0.5 + try: + if "ckpt_path" in config.params.first_stage_config.params: + config.params.first_stage_config.params.ckpt_path = None + print("Deleting the first-stage restore-ckpt path from the config...") + if "ckpt_path" in config.params.cond_stage_config.params: + config.params.cond_stage_config.params.ckpt_path = None + print("Deleting the cond-stage restore-ckpt path from the config...") + except: + pass + + model = instantiate_from_config(config) + if sd is not None: + missing, unexpected = model.load_state_dict(sd, strict=False) + print(f"Missing Keys in State Dict: {missing}") + print(f"Unexpected Keys in State Dict: {unexpected}") + if gpu: + model.cuda() + if eval_mode: + model.eval() + return {"model": model} + + +def get_data(config): + # get data + data = instantiate_from_config(config.data) + data.prepare_data() + data.setup() + return data + + +def load_model_and_dset(config, ckpt, gpu, eval_mode): + # get data + dsets = get_data(config) # calls data.config ... + + # now load the specified checkpoint + if ckpt: + pl_sd = torch.load(ckpt, map_location="cpu") + global_step = pl_sd["global_step"] + else: + pl_sd = {"state_dict": None} + global_step = None + model = load_model_from_config(config.model, + pl_sd["state_dict"], + gpu=gpu, + eval_mode=eval_mode)["model"] + return dsets, model, global_step + + +if __name__ == "__main__": + sys.path.append(os.getcwd()) + + parser = get_parser() + + opt, unknown = parser.parse_known_args() + + ckpt = None + if opt.resume: + if not os.path.exists(opt.resume): + raise ValueError("Cannot find {}".format(opt.resume)) + if os.path.isfile(opt.resume): + paths = opt.resume.split("/") + try: + idx = len(paths)-paths[::-1].index("logs")+1 + except ValueError: + idx = -2 # take a guess: path/to/logdir/checkpoints/model.ckpt + logdir = "/".join(paths[:idx]) + ckpt = opt.resume + else: + assert os.path.isdir(opt.resume), opt.resume + logdir = opt.resume.rstrip("/") + ckpt = os.path.join(logdir, "checkpoints", "last.ckpt") + print(f"logdir:{logdir}") + base_configs = sorted(glob.glob(os.path.join(logdir, "configs/*-project.yaml"))) + opt.base = base_configs+opt.base + + if opt.config: + if type(opt.config) == str: + opt.base = [opt.config] + else: + opt.base = [opt.base[-1]] + + configs = [OmegaConf.load(cfg) for cfg in opt.base] + cli = OmegaConf.from_dotlist(unknown) + if opt.ignore_base_data: + for config in configs: + if hasattr(config, "data"): del config["data"] + config = OmegaConf.merge(*configs, cli) + + print(ckpt) + gpu = True + eval_mode = True + show_config = False + if show_config: + print(OmegaConf.to_container(config)) + + dsets, model, global_step = load_model_and_dset(config, ckpt, gpu, eval_mode) + print(f"Global step: {global_step}") + + outdir = os.path.join(opt.outdir, "{:06}_{}_{}".format(global_step, + opt.top_k, + opt.temperature)) + os.makedirs(outdir, exist_ok=True) + print("Writing samples to ", outdir) + for k in ["originals", "reconstructions", "samples"]: + os.makedirs(os.path.join(outdir, k), exist_ok=True) + run_conditional(model, dsets, outdir, opt.top_k, opt.temperature) diff --git a/sd/stablediffusion/src/taming-transformers/scripts/make_scene_samples.py b/sd/stablediffusion/src/taming-transformers/scripts/make_scene_samples.py new file mode 100644 index 0000000000000000000000000000000000000000..c096b98460874be0acbe5b85464593fbad4bedd0 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/scripts/make_scene_samples.py @@ -0,0 +1,198 @@ +import glob +import os +import sys +from itertools import product +from pathlib import Path +from typing import Literal, List, Optional, Tuple + +import numpy as np +import torch +from omegaconf import OmegaConf +from pytorch_lightning import seed_everything +from torch import Tensor +from torchvision.utils import save_image +from tqdm import tqdm + +from scripts.make_samples import get_parser, load_model_and_dset +from taming.data.conditional_builder.objects_center_points import ObjectsCenterPointsConditionalBuilder +from taming.data.helper_types import BoundingBox, Annotation +from taming.data.annotated_objects_dataset import AnnotatedObjectsDataset +from taming.models.cond_transformer import Net2NetTransformer + +seed_everything(42424242) +device: Literal['cuda', 'cpu'] = 'cuda' +first_stage_factor = 16 +trained_on_res = 256 + + +def _helper(coord: int, coord_max: int, coord_window: int) -> (int, int): + assert 0 <= coord < coord_max + coord_desired_center = (coord_window - 1) // 2 + return np.clip(coord - coord_desired_center, 0, coord_max - coord_window) + + +def get_crop_coordinates(x: int, y: int) -> BoundingBox: + WIDTH, HEIGHT = desired_z_shape[1], desired_z_shape[0] + x0 = _helper(x, WIDTH, first_stage_factor) / WIDTH + y0 = _helper(y, HEIGHT, first_stage_factor) / HEIGHT + w = first_stage_factor / WIDTH + h = first_stage_factor / HEIGHT + return x0, y0, w, h + + +def get_z_indices_crop_out(z_indices: Tensor, predict_x: int, predict_y: int) -> Tensor: + WIDTH, HEIGHT = desired_z_shape[1], desired_z_shape[0] + x0 = _helper(predict_x, WIDTH, first_stage_factor) + y0 = _helper(predict_y, HEIGHT, first_stage_factor) + no_images = z_indices.shape[0] + cut_out_1 = z_indices[:, y0:predict_y, x0:x0+first_stage_factor].reshape((no_images, -1)) + cut_out_2 = z_indices[:, predict_y, x0:predict_x] + return torch.cat((cut_out_1, cut_out_2), dim=1) + + +@torch.no_grad() +def sample(model: Net2NetTransformer, annotations: List[Annotation], dataset: AnnotatedObjectsDataset, + conditional_builder: ObjectsCenterPointsConditionalBuilder, no_samples: int, + temperature: float, top_k: int) -> Tensor: + x_max, y_max = desired_z_shape[1], desired_z_shape[0] + + annotations = [a._replace(category_no=dataset.get_category_number(a.category_id)) for a in annotations] + + recompute_conditional = any((desired_resolution[0] > trained_on_res, desired_resolution[1] > trained_on_res)) + if not recompute_conditional: + crop_coordinates = get_crop_coordinates(0, 0) + conditional_indices = conditional_builder.build(annotations, crop_coordinates) + c_indices = conditional_indices.to(device).repeat(no_samples, 1) + z_indices = torch.zeros((no_samples, 0), device=device).long() + output_indices = model.sample(z_indices, c_indices, steps=x_max*y_max, temperature=temperature, + sample=True, top_k=top_k) + else: + output_indices = torch.zeros((no_samples, y_max, x_max), device=device).long() + for predict_y, predict_x in tqdm(product(range(y_max), range(x_max)), desc='sampling_image', total=x_max*y_max): + crop_coordinates = get_crop_coordinates(predict_x, predict_y) + z_indices = get_z_indices_crop_out(output_indices, predict_x, predict_y) + conditional_indices = conditional_builder.build(annotations, crop_coordinates) + c_indices = conditional_indices.to(device).repeat(no_samples, 1) + new_index = model.sample(z_indices, c_indices, steps=1, temperature=temperature, sample=True, top_k=top_k) + output_indices[:, predict_y, predict_x] = new_index[:, -1] + z_shape = ( + no_samples, + model.first_stage_model.quantize.e_dim, # codebook embed_dim + desired_z_shape[0], # z_height + desired_z_shape[1] # z_width + ) + x_sample = model.decode_to_img(output_indices, z_shape) * 0.5 + 0.5 + x_sample = x_sample.to('cpu') + + plotter = conditional_builder.plot + figure_size = (x_sample.shape[2], x_sample.shape[3]) + scene_graph = conditional_builder.build(annotations, (0., 0., 1., 1.)) + plot = plotter(scene_graph, dataset.get_textual_label_for_category_no, figure_size) + return torch.cat((x_sample, plot.unsqueeze(0))) + + +def get_resolution(resolution_str: str) -> (Tuple[int, int], Tuple[int, int]): + if not resolution_str.count(',') == 1: + raise ValueError("Give resolution as in 'height,width'") + res_h, res_w = resolution_str.split(',') + res_h = max(int(res_h), trained_on_res) + res_w = max(int(res_w), trained_on_res) + z_h = int(round(res_h/first_stage_factor)) + z_w = int(round(res_w/first_stage_factor)) + return (z_h, z_w), (z_h*first_stage_factor, z_w*first_stage_factor) + + +def add_arg_to_parser(parser): + parser.add_argument( + "-R", + "--resolution", + type=str, + default='256,256', + help=f"give resolution in multiples of {first_stage_factor}, default is '256,256'", + ) + parser.add_argument( + "-C", + "--conditional", + type=str, + default='objects_bbox', + help=f"objects_bbox or objects_center_points", + ) + parser.add_argument( + "-N", + "--n_samples_per_layout", + type=int, + default=4, + help=f"how many samples to generate per layout", + ) + return parser + + +if __name__ == "__main__": + sys.path.append(os.getcwd()) + + parser = get_parser() + parser = add_arg_to_parser(parser) + + opt, unknown = parser.parse_known_args() + + ckpt = None + if opt.resume: + if not os.path.exists(opt.resume): + raise ValueError("Cannot find {}".format(opt.resume)) + if os.path.isfile(opt.resume): + paths = opt.resume.split("/") + try: + idx = len(paths)-paths[::-1].index("logs")+1 + except ValueError: + idx = -2 # take a guess: path/to/logdir/checkpoints/model.ckpt + logdir = "/".join(paths[:idx]) + ckpt = opt.resume + else: + assert os.path.isdir(opt.resume), opt.resume + logdir = opt.resume.rstrip("/") + ckpt = os.path.join(logdir, "checkpoints", "last.ckpt") + print(f"logdir:{logdir}") + base_configs = sorted(glob.glob(os.path.join(logdir, "configs/*-project.yaml"))) + opt.base = base_configs+opt.base + + if opt.config: + if type(opt.config) == str: + opt.base = [opt.config] + else: + opt.base = [opt.base[-1]] + + configs = [OmegaConf.load(cfg) for cfg in opt.base] + cli = OmegaConf.from_dotlist(unknown) + if opt.ignore_base_data: + for config in configs: + if hasattr(config, "data"): + del config["data"] + config = OmegaConf.merge(*configs, cli) + desired_z_shape, desired_resolution = get_resolution(opt.resolution) + conditional = opt.conditional + + print(ckpt) + gpu = True + eval_mode = True + show_config = False + if show_config: + print(OmegaConf.to_container(config)) + + dsets, model, global_step = load_model_and_dset(config, ckpt, gpu, eval_mode) + print(f"Global step: {global_step}") + + data_loader = dsets.val_dataloader() + print(dsets.datasets["validation"].conditional_builders) + conditional_builder = dsets.datasets["validation"].conditional_builders[conditional] + + outdir = Path(opt.outdir).joinpath(f"{global_step:06}_{opt.top_k}_{opt.temperature}") + outdir.mkdir(exist_ok=True, parents=True) + print("Writing samples to ", outdir) + + p_bar_1 = tqdm(enumerate(iter(data_loader)), desc='batch', total=len(data_loader)) + for batch_no, batch in p_bar_1: + save_img: Optional[Tensor] = None + for i, annotations in tqdm(enumerate(batch['annotations']), desc='within_batch', total=data_loader.batch_size): + imgs = sample(model, annotations, dsets.datasets["validation"], conditional_builder, + opt.n_samples_per_layout, opt.temperature, opt.top_k) + save_image(imgs, outdir.joinpath(f'{batch_no:04}_{i:02}.png'), n_row=opt.n_samples_per_layout+1) diff --git a/sd/stablediffusion/src/taming-transformers/scripts/reconstruction_usage.ipynb b/sd/stablediffusion/src/taming-transformers/scripts/reconstruction_usage.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..35ca2a43cfaa05c59b82f4f7b181799882dc6998 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/scripts/reconstruction_usage.ipynb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:389aa20c5070678317cbf562755277833feec6d85a2e7f26ee92138241cc9197 +size 13700393 diff --git a/sd/stablediffusion/src/taming-transformers/scripts/sample_conditional.py b/sd/stablediffusion/src/taming-transformers/scripts/sample_conditional.py new file mode 100644 index 0000000000000000000000000000000000000000..174cf2af07c1a1ca4e6c35fc0e4f8d6e53591b56 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/scripts/sample_conditional.py @@ -0,0 +1,355 @@ +import argparse, os, sys, glob, math, time +import torch +import numpy as np +from omegaconf import OmegaConf +import streamlit as st +from streamlit import caching +from PIL import Image +from main import instantiate_from_config, DataModuleFromConfig +from torch.utils.data import DataLoader +from torch.utils.data.dataloader import default_collate + + +rescale = lambda x: (x + 1.) / 2. + + +def bchw_to_st(x): + return rescale(x.detach().cpu().numpy().transpose(0,2,3,1)) + +def save_img(xstart, fname): + I = (xstart.clip(0,1)[0]*255).astype(np.uint8) + Image.fromarray(I).save(fname) + + + +def get_interactive_image(resize=False): + image = st.file_uploader("Input", type=["jpg", "JPEG", "png"]) + if image is not None: + image = Image.open(image) + if not image.mode == "RGB": + image = image.convert("RGB") + image = np.array(image).astype(np.uint8) + print("upload image shape: {}".format(image.shape)) + img = Image.fromarray(image) + if resize: + img = img.resize((256, 256)) + image = np.array(img) + return image + + +def single_image_to_torch(x, permute=True): + assert x is not None, "Please provide an image through the upload function" + x = np.array(x) + x = torch.FloatTensor(x/255.*2. - 1.)[None,...] + if permute: + x = x.permute(0, 3, 1, 2) + return x + + +def pad_to_M(x, M): + hp = math.ceil(x.shape[2]/M)*M-x.shape[2] + wp = math.ceil(x.shape[3]/M)*M-x.shape[3] + x = torch.nn.functional.pad(x, (0,wp,0,hp,0,0,0,0)) + return x + +@torch.no_grad() +def run_conditional(model, dsets): + if len(dsets.datasets) > 1: + split = st.sidebar.radio("Split", sorted(dsets.datasets.keys())) + dset = dsets.datasets[split] + else: + dset = next(iter(dsets.datasets.values())) + batch_size = 1 + start_index = st.sidebar.number_input("Example Index (Size: {})".format(len(dset)), value=0, + min_value=0, + max_value=len(dset)-batch_size) + indices = list(range(start_index, start_index+batch_size)) + + example = default_collate([dset[i] for i in indices]) + + x = model.get_input("image", example).to(model.device) + + cond_key = model.cond_stage_key + c = model.get_input(cond_key, example).to(model.device) + + scale_factor = st.sidebar.slider("Scale Factor", min_value=0.5, max_value=4.0, step=0.25, value=1.00) + if scale_factor != 1.0: + x = torch.nn.functional.interpolate(x, scale_factor=scale_factor, mode="bicubic") + c = torch.nn.functional.interpolate(c, scale_factor=scale_factor, mode="bicubic") + + quant_z, z_indices = model.encode_to_z(x) + quant_c, c_indices = model.encode_to_c(c) + + cshape = quant_z.shape + + xrec = model.first_stage_model.decode(quant_z) + st.write("image: {}".format(x.shape)) + st.image(bchw_to_st(x), clamp=True, output_format="PNG") + st.write("image reconstruction: {}".format(xrec.shape)) + st.image(bchw_to_st(xrec), clamp=True, output_format="PNG") + + if cond_key == "segmentation": + # get image from segmentation mask + num_classes = c.shape[1] + c = torch.argmax(c, dim=1, keepdim=True) + c = torch.nn.functional.one_hot(c, num_classes=num_classes) + c = c.squeeze(1).permute(0, 3, 1, 2).float() + c = model.cond_stage_model.to_rgb(c) + + st.write(f"{cond_key}: {tuple(c.shape)}") + st.image(bchw_to_st(c), clamp=True, output_format="PNG") + + idx = z_indices + + half_sample = st.sidebar.checkbox("Image Completion", value=False) + if half_sample: + start = idx.shape[1]//2 + else: + start = 0 + + idx[:,start:] = 0 + idx = idx.reshape(cshape[0],cshape[2],cshape[3]) + start_i = start//cshape[3] + start_j = start %cshape[3] + + if not half_sample and quant_z.shape == quant_c.shape: + st.info("Setting idx to c_indices") + idx = c_indices.clone().reshape(cshape[0],cshape[2],cshape[3]) + + cidx = c_indices + cidx = cidx.reshape(quant_c.shape[0],quant_c.shape[2],quant_c.shape[3]) + + xstart = model.decode_to_img(idx[:,:cshape[2],:cshape[3]], cshape) + st.image(bchw_to_st(xstart), clamp=True, output_format="PNG") + + temperature = st.number_input("Temperature", value=1.0) + top_k = st.number_input("Top k", value=100) + sample = st.checkbox("Sample", value=True) + update_every = st.number_input("Update every", value=75) + + st.text(f"Sampling shape ({cshape[2]},{cshape[3]})") + + animate = st.checkbox("animate") + if animate: + import imageio + outvid = "sampling.mp4" + writer = imageio.get_writer(outvid, fps=25) + elapsed_t = st.empty() + info = st.empty() + st.text("Sampled") + if st.button("Sample"): + output = st.empty() + start_t = time.time() + for i in range(start_i,cshape[2]-0): + if i <= 8: + local_i = i + elif cshape[2]-i < 8: + local_i = 16-(cshape[2]-i) + else: + local_i = 8 + for j in range(start_j,cshape[3]-0): + if j <= 8: + local_j = j + elif cshape[3]-j < 8: + local_j = 16-(cshape[3]-j) + else: + local_j = 8 + + i_start = i-local_i + i_end = i_start+16 + j_start = j-local_j + j_end = j_start+16 + elapsed_t.text(f"Time: {time.time() - start_t} seconds") + info.text(f"Step: ({i},{j}) | Local: ({local_i},{local_j}) | Crop: ({i_start}:{i_end},{j_start}:{j_end})") + patch = idx[:,i_start:i_end,j_start:j_end] + patch = patch.reshape(patch.shape[0],-1) + cpatch = cidx[:, i_start:i_end, j_start:j_end] + cpatch = cpatch.reshape(cpatch.shape[0], -1) + patch = torch.cat((cpatch, patch), dim=1) + logits,_ = model.transformer(patch[:,:-1]) + logits = logits[:, -256:, :] + logits = logits.reshape(cshape[0],16,16,-1) + logits = logits[:,local_i,local_j,:] + + logits = logits/temperature + + if top_k is not None: + logits = model.top_k_logits(logits, top_k) + # apply softmax to convert to probabilities + probs = torch.nn.functional.softmax(logits, dim=-1) + # sample from the distribution or take the most likely + if sample: + ix = torch.multinomial(probs, num_samples=1) + else: + _, ix = torch.topk(probs, k=1, dim=-1) + idx[:,i,j] = ix + + if (i*cshape[3]+j)%update_every==0: + xstart = model.decode_to_img(idx[:, :cshape[2], :cshape[3]], cshape,) + + xstart = bchw_to_st(xstart) + output.image(xstart, clamp=True, output_format="PNG") + + if animate: + writer.append_data((xstart[0]*255).clip(0, 255).astype(np.uint8)) + + xstart = model.decode_to_img(idx[:,:cshape[2],:cshape[3]], cshape) + xstart = bchw_to_st(xstart) + output.image(xstart, clamp=True, output_format="PNG") + #save_img(xstart, "full_res_sample.png") + if animate: + writer.close() + st.video(outvid) + + +def get_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + "-r", + "--resume", + type=str, + nargs="?", + help="load from logdir or checkpoint in logdir", + ) + parser.add_argument( + "-b", + "--base", + nargs="*", + metavar="base_config.yaml", + help="paths to base configs. Loaded from left-to-right. " + "Parameters can be overwritten or added with command-line options of the form `--key value`.", + default=list(), + ) + parser.add_argument( + "-c", + "--config", + nargs="?", + metavar="single_config.yaml", + help="path to single config. If specified, base configs will be ignored " + "(except for the last one if left unspecified).", + const=True, + default="", + ) + parser.add_argument( + "--ignore_base_data", + action="store_true", + help="Ignore data specification from base configs. Useful if you want " + "to specify a custom datasets on the command line.", + ) + return parser + + +def load_model_from_config(config, sd, gpu=True, eval_mode=True): + if "ckpt_path" in config.params: + st.warning("Deleting the restore-ckpt path from the config...") + config.params.ckpt_path = None + if "downsample_cond_size" in config.params: + st.warning("Deleting downsample-cond-size from the config and setting factor=0.5 instead...") + config.params.downsample_cond_size = -1 + config.params["downsample_cond_factor"] = 0.5 + try: + if "ckpt_path" in config.params.first_stage_config.params: + config.params.first_stage_config.params.ckpt_path = None + st.warning("Deleting the first-stage restore-ckpt path from the config...") + if "ckpt_path" in config.params.cond_stage_config.params: + config.params.cond_stage_config.params.ckpt_path = None + st.warning("Deleting the cond-stage restore-ckpt path from the config...") + except: + pass + + model = instantiate_from_config(config) + if sd is not None: + missing, unexpected = model.load_state_dict(sd, strict=False) + st.info(f"Missing Keys in State Dict: {missing}") + st.info(f"Unexpected Keys in State Dict: {unexpected}") + if gpu: + model.cuda() + if eval_mode: + model.eval() + return {"model": model} + + +def get_data(config): + # get data + data = instantiate_from_config(config.data) + data.prepare_data() + data.setup() + return data + + +@st.cache(allow_output_mutation=True, suppress_st_warning=True) +def load_model_and_dset(config, ckpt, gpu, eval_mode): + # get data + dsets = get_data(config) # calls data.config ... + + # now load the specified checkpoint + if ckpt: + pl_sd = torch.load(ckpt, map_location="cpu") + global_step = pl_sd["global_step"] + else: + pl_sd = {"state_dict": None} + global_step = None + model = load_model_from_config(config.model, + pl_sd["state_dict"], + gpu=gpu, + eval_mode=eval_mode)["model"] + return dsets, model, global_step + + +if __name__ == "__main__": + sys.path.append(os.getcwd()) + + parser = get_parser() + + opt, unknown = parser.parse_known_args() + + ckpt = None + if opt.resume: + if not os.path.exists(opt.resume): + raise ValueError("Cannot find {}".format(opt.resume)) + if os.path.isfile(opt.resume): + paths = opt.resume.split("/") + try: + idx = len(paths)-paths[::-1].index("logs")+1 + except ValueError: + idx = -2 # take a guess: path/to/logdir/checkpoints/model.ckpt + logdir = "/".join(paths[:idx]) + ckpt = opt.resume + else: + assert os.path.isdir(opt.resume), opt.resume + logdir = opt.resume.rstrip("/") + ckpt = os.path.join(logdir, "checkpoints", "last.ckpt") + print(f"logdir:{logdir}") + base_configs = sorted(glob.glob(os.path.join(logdir, "configs/*-project.yaml"))) + opt.base = base_configs+opt.base + + if opt.config: + if type(opt.config) == str: + opt.base = [opt.config] + else: + opt.base = [opt.base[-1]] + + configs = [OmegaConf.load(cfg) for cfg in opt.base] + cli = OmegaConf.from_dotlist(unknown) + if opt.ignore_base_data: + for config in configs: + if hasattr(config, "data"): del config["data"] + config = OmegaConf.merge(*configs, cli) + + st.sidebar.text(ckpt) + gs = st.sidebar.empty() + gs.text(f"Global step: ?") + st.sidebar.text("Options") + #gpu = st.sidebar.checkbox("GPU", value=True) + gpu = True + #eval_mode = st.sidebar.checkbox("Eval Mode", value=True) + eval_mode = True + #show_config = st.sidebar.checkbox("Show Config", value=False) + show_config = False + if show_config: + st.info("Checkpoint: {}".format(ckpt)) + st.json(OmegaConf.to_container(config)) + + dsets, model, global_step = load_model_and_dset(config, ckpt, gpu, eval_mode) + gs.text(f"Global step: {global_step}") + run_conditional(model, dsets) diff --git a/sd/stablediffusion/src/taming-transformers/scripts/sample_fast.py b/sd/stablediffusion/src/taming-transformers/scripts/sample_fast.py new file mode 100644 index 0000000000000000000000000000000000000000..ff546c7dcbe459807ac3b70f834ccc1082fe8b4e --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/scripts/sample_fast.py @@ -0,0 +1,260 @@ +import argparse, os, sys, glob +import torch +import time +import numpy as np +from omegaconf import OmegaConf +from PIL import Image +from tqdm import tqdm, trange +from einops import repeat + +from main import instantiate_from_config +from taming.modules.transformer.mingpt import sample_with_past + + +rescale = lambda x: (x + 1.) / 2. + + +def chw_to_pillow(x): + return Image.fromarray((255*rescale(x.detach().cpu().numpy().transpose(1,2,0))).clip(0,255).astype(np.uint8)) + + +@torch.no_grad() +def sample_classconditional(model, batch_size, class_label, steps=256, temperature=None, top_k=None, callback=None, + dim_z=256, h=16, w=16, verbose_time=False, top_p=None): + log = dict() + assert type(class_label) == int, f'expecting type int but type is {type(class_label)}' + qzshape = [batch_size, dim_z, h, w] + assert not model.be_unconditional, 'Expecting a class-conditional Net2NetTransformer.' + c_indices = repeat(torch.tensor([class_label]), '1 -> b 1', b=batch_size).to(model.device) # class token + t1 = time.time() + index_sample = sample_with_past(c_indices, model.transformer, steps=steps, + sample_logits=True, top_k=top_k, callback=callback, + temperature=temperature, top_p=top_p) + if verbose_time: + sampling_time = time.time() - t1 + print(f"Full sampling takes about {sampling_time:.2f} seconds.") + x_sample = model.decode_to_img(index_sample, qzshape) + log["samples"] = x_sample + log["class_label"] = c_indices + return log + + +@torch.no_grad() +def sample_unconditional(model, batch_size, steps=256, temperature=None, top_k=None, top_p=None, callback=None, + dim_z=256, h=16, w=16, verbose_time=False): + log = dict() + qzshape = [batch_size, dim_z, h, w] + assert model.be_unconditional, 'Expecting an unconditional model.' + c_indices = repeat(torch.tensor([model.sos_token]), '1 -> b 1', b=batch_size).to(model.device) # sos token + t1 = time.time() + index_sample = sample_with_past(c_indices, model.transformer, steps=steps, + sample_logits=True, top_k=top_k, callback=callback, + temperature=temperature, top_p=top_p) + if verbose_time: + sampling_time = time.time() - t1 + print(f"Full sampling takes about {sampling_time:.2f} seconds.") + x_sample = model.decode_to_img(index_sample, qzshape) + log["samples"] = x_sample + return log + + +@torch.no_grad() +def run(logdir, model, batch_size, temperature, top_k, unconditional=True, num_samples=50000, + given_classes=None, top_p=None): + batches = [batch_size for _ in range(num_samples//batch_size)] + [num_samples % batch_size] + if not unconditional: + assert given_classes is not None + print("Running in pure class-conditional sampling mode. I will produce " + f"{num_samples} samples for each of the {len(given_classes)} classes, " + f"i.e. {num_samples*len(given_classes)} in total.") + for class_label in tqdm(given_classes, desc="Classes"): + for n, bs in tqdm(enumerate(batches), desc="Sampling Class"): + if bs == 0: break + logs = sample_classconditional(model, batch_size=bs, class_label=class_label, + temperature=temperature, top_k=top_k, top_p=top_p) + save_from_logs(logs, logdir, base_count=n * batch_size, cond_key=logs["class_label"]) + else: + print(f"Running in unconditional sampling mode, producing {num_samples} samples.") + for n, bs in tqdm(enumerate(batches), desc="Sampling"): + if bs == 0: break + logs = sample_unconditional(model, batch_size=bs, temperature=temperature, top_k=top_k, top_p=top_p) + save_from_logs(logs, logdir, base_count=n * batch_size) + + +def save_from_logs(logs, logdir, base_count, key="samples", cond_key=None): + xx = logs[key] + for i, x in enumerate(xx): + x = chw_to_pillow(x) + count = base_count + i + if cond_key is None: + x.save(os.path.join(logdir, f"{count:06}.png")) + else: + condlabel = cond_key[i] + if type(condlabel) == torch.Tensor: condlabel = condlabel.item() + os.makedirs(os.path.join(logdir, str(condlabel)), exist_ok=True) + x.save(os.path.join(logdir, str(condlabel), f"{count:06}.png")) + + +def get_parser(): + def str2bool(v): + if isinstance(v, bool): + return v + if v.lower() in ("yes", "true", "t", "y", "1"): + return True + elif v.lower() in ("no", "false", "f", "n", "0"): + return False + else: + raise argparse.ArgumentTypeError("Boolean value expected.") + + parser = argparse.ArgumentParser() + parser.add_argument( + "-r", + "--resume", + type=str, + nargs="?", + help="load from logdir or checkpoint in logdir", + ) + parser.add_argument( + "-o", + "--outdir", + type=str, + nargs="?", + help="path where the samples will be logged to.", + default="" + ) + parser.add_argument( + "-b", + "--base", + nargs="*", + metavar="base_config.yaml", + help="paths to base configs. Loaded from left-to-right. " + "Parameters can be overwritten or added with command-line options of the form `--key value`.", + default=list(), + ) + parser.add_argument( + "-n", + "--num_samples", + type=int, + nargs="?", + help="num_samples to draw", + default=50000 + ) + parser.add_argument( + "--batch_size", + type=int, + nargs="?", + help="the batch size", + default=25 + ) + parser.add_argument( + "-k", + "--top_k", + type=int, + nargs="?", + help="top-k value to sample with", + default=250, + ) + parser.add_argument( + "-t", + "--temperature", + type=float, + nargs="?", + help="temperature value to sample with", + default=1.0 + ) + parser.add_argument( + "-p", + "--top_p", + type=float, + nargs="?", + help="top-p value to sample with", + default=1.0 + ) + parser.add_argument( + "--classes", + type=str, + nargs="?", + help="specify comma-separated classes to sample from. Uses 1000 classes per default.", + default="imagenet" + ) + return parser + + +def load_model_from_config(config, sd, gpu=True, eval_mode=True): + model = instantiate_from_config(config) + if sd is not None: + model.load_state_dict(sd) + if gpu: + model.cuda() + if eval_mode: + model.eval() + return {"model": model} + + +def load_model(config, ckpt, gpu, eval_mode): + # load the specified checkpoint + if ckpt: + pl_sd = torch.load(ckpt, map_location="cpu") + global_step = pl_sd["global_step"] + print(f"loaded model from global step {global_step}.") + else: + pl_sd = {"state_dict": None} + global_step = None + model = load_model_from_config(config.model, pl_sd["state_dict"], gpu=gpu, eval_mode=eval_mode)["model"] + return model, global_step + + +if __name__ == "__main__": + sys.path.append(os.getcwd()) + parser = get_parser() + + opt, unknown = parser.parse_known_args() + assert opt.resume + + ckpt = None + + if not os.path.exists(opt.resume): + raise ValueError("Cannot find {}".format(opt.resume)) + if os.path.isfile(opt.resume): + paths = opt.resume.split("/") + try: + idx = len(paths)-paths[::-1].index("logs")+1 + except ValueError: + idx = -2 # take a guess: path/to/logdir/checkpoints/model.ckpt + logdir = "/".join(paths[:idx]) + ckpt = opt.resume + else: + assert os.path.isdir(opt.resume), opt.resume + logdir = opt.resume.rstrip("/") + ckpt = os.path.join(logdir, "checkpoints", "last.ckpt") + + base_configs = sorted(glob.glob(os.path.join(logdir, "configs/*-project.yaml"))) + opt.base = base_configs+opt.base + + configs = [OmegaConf.load(cfg) for cfg in opt.base] + cli = OmegaConf.from_dotlist(unknown) + config = OmegaConf.merge(*configs, cli) + + model, global_step = load_model(config, ckpt, gpu=True, eval_mode=True) + + if opt.outdir: + print(f"Switching logdir from '{logdir}' to '{opt.outdir}'") + logdir = opt.outdir + + if opt.classes == "imagenet": + given_classes = [i for i in range(1000)] + else: + cls_str = opt.classes + assert not cls_str.endswith(","), 'class string should not end with a ","' + given_classes = [int(c) for c in cls_str.split(",")] + + logdir = os.path.join(logdir, "samples", f"top_k_{opt.top_k}_temp_{opt.temperature:.2f}_top_p_{opt.top_p}", + f"{global_step}") + + print(f"Logging to {logdir}") + os.makedirs(logdir, exist_ok=True) + + run(logdir, model, opt.batch_size, opt.temperature, opt.top_k, unconditional=model.be_unconditional, + given_classes=given_classes, num_samples=opt.num_samples, top_p=opt.top_p) + + print("done.") diff --git a/sd/stablediffusion/src/taming-transformers/scripts/taming-transformers.ipynb b/sd/stablediffusion/src/taming-transformers/scripts/taming-transformers.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..05f096c600cad909fed42368c9c03658b8a0489b --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/scripts/taming-transformers.ipynb @@ -0,0 +1,659 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "taming-transformers.ipynb", + "provenance": [], + "collapsed_sections": [], + "toc_visible": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "2U0NA9HrrZey" + }, + "source": [ + "# Taming Transformers\n", + "\n", + "This notebook is a minimal working example to generate landscape images as in [Taming Transformers for High-Resolution Image Synthesis](https://github.com/CompVis/taming-transformers). **tl;dr** We combine the efficiancy of convolutional approaches with the expressivity of transformers by introducing a convolutional VQGAN, which learns a codebook of context-rich visual parts, whose composition is modeled with an autoregressive transformer." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4hdc6YonrvoC" + }, + "source": [ + "## Setup\n", + "The setup code in this section was written to be [run in a Colab environment](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/taming-transformers.ipynb). For a full, local setup, we recommend the provided [conda environment](https://github.com/CompVis/taming-transformers/blob/master/environment.yaml), as [described in the readme](https://github.com/CompVis/taming-transformers#requirements). This will also allow you to run a streamlit based demo.\n", + "\n", + "Here, we first clone the repository and download a model checkpoint and config." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Wwj8j_l201aF", + "outputId": "833f54a6-0620-4dc1-dd8c-69ab01433d17" + }, + "source": [ + "!git clone https://github.com/CompVis/taming-transformers\n", + "%cd taming-transformers\n", + "!mkdir -p logs/2020-11-09T13-31-51_sflckr/checkpoints\n", + "!wget 'https://heibox.uni-heidelberg.de/d/73487ab6e5314cb5adba/files/?p=%2Fcheckpoints%2Flast.ckpt&dl=1' -O 'logs/2020-11-09T13-31-51_sflckr/checkpoints/last.ckpt'\n", + "!mkdir logs/2020-11-09T13-31-51_sflckr/configs\n", + "!wget 'https://heibox.uni-heidelberg.de/d/73487ab6e5314cb5adba/files/?p=%2Fconfigs%2F2020-11-09T13-31-51-project.yaml&dl=1' -O 'logs/2020-11-09T13-31-51_sflckr/configs/2020-11-09T13-31-51-project.yaml'" + ], + "execution_count": 2, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Cloning into 'taming-transformers'...\n", + "remote: Enumerating objects: 287, done.\u001B[K\n", + "remote: Counting objects: 100% (287/287), done.\u001B[K\n", + "remote: Compressing objects: 100% (232/232), done.\u001B[K\n", + "remote: Total 287 (delta 47), reused 285 (delta 45), pack-reused 0\u001B[K\n", + "Receiving objects: 100% (287/287), 50.50 MiB | 47.84 MiB/s, done.\n", + "Resolving deltas: 100% (47/47), done.\n", + "/content/taming-transformers\n", + "--2021-01-03 21:17:37-- https://heibox.uni-heidelberg.de/d/73487ab6e5314cb5adba/files/?p=%2Fcheckpoints%2Flast.ckpt&dl=1\n", + "Resolving heibox.uni-heidelberg.de (heibox.uni-heidelberg.de)... 129.206.7.113\n", + "Connecting to heibox.uni-heidelberg.de (heibox.uni-heidelberg.de)|129.206.7.113|:443... connected.\n", + "HTTP request sent, awaiting response... 302 Found\n", + "Location: https://heibox.uni-heidelberg.de/seafhttp/files/0fa273f3-83b9-41a5-a7b6-49d825c6a892/last.ckpt [following]\n", + "--2021-01-03 21:17:38-- https://heibox.uni-heidelberg.de/seafhttp/files/0fa273f3-83b9-41a5-a7b6-49d825c6a892/last.ckpt\n", + "Reusing existing connection to heibox.uni-heidelberg.de:443.\n", + "HTTP request sent, awaiting response... 200 OK\n", + "Length: 4263525412 (4.0G) [application/octet-stream]\n", + "Saving to: ‘logs/2020-11-09T13-31-51_sflckr/checkpoints/last.ckpt’\n", + "\n", + "logs/2020-11-09T13- 100%[===================>] 3.97G 10.0MB/s in 6m 50s \n", + "\n", + "2021-01-03 21:24:28 (9.91 MB/s) - ‘logs/2020-11-09T13-31-51_sflckr/checkpoints/last.ckpt’ saved [4263525412/4263525412]\n", + "\n", + "--2021-01-03 21:24:28-- https://heibox.uni-heidelberg.de/d/73487ab6e5314cb5adba/files/?p=%2Fconfigs%2F2020-11-09T13-31-51-project.yaml&dl=1\n", + "Resolving heibox.uni-heidelberg.de (heibox.uni-heidelberg.de)... 129.206.7.113\n", + "Connecting to heibox.uni-heidelberg.de (heibox.uni-heidelberg.de)|129.206.7.113|:443... connected.\n", + "HTTP request sent, awaiting response... 302 Found\n", + "Location: https://heibox.uni-heidelberg.de/seafhttp/files/7f8d75ed-f754-4341-8027-f5e2c8eb906a/2020-11-09T13-31-51-project.yaml [following]\n", + "--2021-01-03 21:24:29-- https://heibox.uni-heidelberg.de/seafhttp/files/7f8d75ed-f754-4341-8027-f5e2c8eb906a/2020-11-09T13-31-51-project.yaml\n", + "Reusing existing connection to heibox.uni-heidelberg.de:443.\n", + "HTTP request sent, awaiting response... 200 OK\n", + "Length: 1603 (1.6K) [application/octet-stream]\n", + "Saving to: ‘logs/2020-11-09T13-31-51_sflckr/configs/2020-11-09T13-31-51-project.yaml’\n", + "\n", + "logs/2020-11-09T13- 100%[===================>] 1.57K --.-KB/s in 0.1s \n", + "\n", + "2021-01-03 21:24:29 (12.8 KB/s) - ‘logs/2020-11-09T13-31-51_sflckr/configs/2020-11-09T13-31-51-project.yaml’ saved [1603/1603]\n", + "\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eeBqWgMQDjZb" + }, + "source": [ + "Next, we install minimal required dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hzQNmIuT_0uF", + "outputId": "e6e1ce0f-bce9-4ebe-a852-95003ca7b630" + }, + "source": [ + "%pip install omegaconf>=2.0.0 pytorch-lightning>=1.0.8 einops transformers\n", + "import sys\n", + "sys.path.append(\".\")" + ], + "execution_count": 3, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Collecting omegaconf==2.0.0\n", + " Downloading https://files.pythonhosted.org/packages/3d/95/ebd73361f9c6e94bd0f3b19ffe31c24e833834c022f1c0328ac71b2d6c90/omegaconf-2.0.0-py3-none-any.whl\n", + "Collecting pytorch-lightning==1.0.8\n", + "\u001B[?25l Downloading https://files.pythonhosted.org/packages/2e/92/078c5524c875c274ded8a0317ef31a2bb86d02c5c74089ab754d0f12b29c/pytorch_lightning-1.0.8-py3-none-any.whl (561kB)\n", + "\u001B[K |████████████████████████████████| 563kB 17.7MB/s \n", + "\u001B[?25hRequirement already satisfied: typing-extensions in /usr/local/lib/python3.6/dist-packages (from omegaconf==2.0.0) (3.7.4.3)\n", + "Requirement already satisfied: dataclasses; python_version == \"3.6\" in /usr/local/lib/python3.6/dist-packages (from omegaconf==2.0.0) (0.8)\n", + "Requirement already satisfied: PyYAML in /usr/local/lib/python3.6/dist-packages (from omegaconf==2.0.0) (3.13)\n", + "Requirement already satisfied: tensorboard>=2.2.0 in /usr/local/lib/python3.6/dist-packages (from pytorch-lightning==1.0.8) (2.4.0)\n", + "Requirement already satisfied: torch>=1.3 in /usr/local/lib/python3.6/dist-packages (from pytorch-lightning==1.0.8) (1.7.0+cu101)\n", + "Requirement already satisfied: tqdm>=4.41.0 in /usr/local/lib/python3.6/dist-packages (from pytorch-lightning==1.0.8) (4.41.1)\n", + "Requirement already satisfied: numpy>=1.16.4 in /usr/local/lib/python3.6/dist-packages (from pytorch-lightning==1.0.8) (1.19.4)\n", + "Collecting future>=0.17.1\n", + "\u001B[?25l Downloading https://files.pythonhosted.org/packages/45/0b/38b06fd9b92dc2b68d58b75f900e97884c45bedd2ff83203d933cf5851c9/future-0.18.2.tar.gz (829kB)\n", + "\u001B[K |████████████████████████████████| 829kB 51.0MB/s \n", + "\u001B[?25hCollecting fsspec>=0.8.0\n", + "\u001B[?25l Downloading https://files.pythonhosted.org/packages/ec/80/72ac0982cc833945fada4b76c52f0f65435ba4d53bc9317d1c70b5f7e7d5/fsspec-0.8.5-py3-none-any.whl (98kB)\n", + "\u001B[K |████████████████████████████████| 102kB 15.7MB/s \n", + "\u001B[?25hRequirement already satisfied: absl-py>=0.4 in /usr/local/lib/python3.6/dist-packages (from tensorboard>=2.2.0->pytorch-lightning==1.0.8) (0.10.0)\n", + "Requirement already satisfied: grpcio>=1.24.3 in /usr/local/lib/python3.6/dist-packages (from tensorboard>=2.2.0->pytorch-lightning==1.0.8) (1.32.0)\n", + "Requirement already satisfied: setuptools>=41.0.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard>=2.2.0->pytorch-lightning==1.0.8) (51.0.0)\n", + "Requirement already satisfied: tensorboard-plugin-wit>=1.6.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard>=2.2.0->pytorch-lightning==1.0.8) (1.7.0)\n", + "Requirement already satisfied: requests<3,>=2.21.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard>=2.2.0->pytorch-lightning==1.0.8) (2.23.0)\n", + "Requirement already satisfied: wheel>=0.26; python_version >= \"3\" in /usr/local/lib/python3.6/dist-packages (from tensorboard>=2.2.0->pytorch-lightning==1.0.8) (0.36.2)\n", + "Requirement already satisfied: google-auth<2,>=1.6.3 in /usr/local/lib/python3.6/dist-packages (from tensorboard>=2.2.0->pytorch-lightning==1.0.8) (1.17.2)\n", + "Requirement already satisfied: google-auth-oauthlib<0.5,>=0.4.1 in /usr/local/lib/python3.6/dist-packages (from tensorboard>=2.2.0->pytorch-lightning==1.0.8) (0.4.2)\n", + "Requirement already satisfied: protobuf>=3.6.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard>=2.2.0->pytorch-lightning==1.0.8) (3.12.4)\n", + "Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard>=2.2.0->pytorch-lightning==1.0.8) (1.15.0)\n", + "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.6/dist-packages (from tensorboard>=2.2.0->pytorch-lightning==1.0.8) (3.3.3)\n", + "Requirement already satisfied: werkzeug>=0.11.15 in /usr/local/lib/python3.6/dist-packages (from tensorboard>=2.2.0->pytorch-lightning==1.0.8) (1.0.1)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard>=2.2.0->pytorch-lightning==1.0.8) (2020.12.5)\n", + "Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard>=2.2.0->pytorch-lightning==1.0.8) (3.0.4)\n", + "Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard>=2.2.0->pytorch-lightning==1.0.8) (1.24.3)\n", + "Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard>=2.2.0->pytorch-lightning==1.0.8) (2.10)\n", + "Requirement already satisfied: cachetools<5.0,>=2.0.0 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard>=2.2.0->pytorch-lightning==1.0.8) (4.2.0)\n", + "Requirement already satisfied: rsa<5,>=3.1.4; python_version >= \"3\" in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard>=2.2.0->pytorch-lightning==1.0.8) (4.6)\n", + "Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard>=2.2.0->pytorch-lightning==1.0.8) (0.2.8)\n", + "Requirement already satisfied: requests-oauthlib>=0.7.0 in /usr/local/lib/python3.6/dist-packages (from google-auth-oauthlib<0.5,>=0.4.1->tensorboard>=2.2.0->pytorch-lightning==1.0.8) (1.3.0)\n", + "Requirement already satisfied: importlib-metadata; python_version < \"3.8\" in /usr/local/lib/python3.6/dist-packages (from markdown>=2.6.8->tensorboard>=2.2.0->pytorch-lightning==1.0.8) (3.3.0)\n", + "Requirement already satisfied: pyasn1>=0.1.3 in /usr/local/lib/python3.6/dist-packages (from rsa<5,>=3.1.4; python_version >= \"3\"->google-auth<2,>=1.6.3->tensorboard>=2.2.0->pytorch-lightning==1.0.8) (0.4.8)\n", + "Requirement already satisfied: oauthlib>=3.0.0 in /usr/local/lib/python3.6/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard>=2.2.0->pytorch-lightning==1.0.8) (3.1.0)\n", + "Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.6/dist-packages (from importlib-metadata; python_version < \"3.8\"->markdown>=2.6.8->tensorboard>=2.2.0->pytorch-lightning==1.0.8) (3.4.0)\n", + "Building wheels for collected packages: future\n", + " Building wheel for future (setup.py) ... \u001B[?25l\u001B[?25hdone\n", + " Created wheel for future: filename=future-0.18.2-cp36-none-any.whl size=491057 sha256=77114a99b4b6e1a15924fdf57670f539d17f33b42f8ff0e951f27d6712e10848\n", + " Stored in directory: /root/.cache/pip/wheels/8b/99/a0/81daf51dcd359a9377b110a8a886b3895921802d2fc1b2397e\n", + "Successfully built future\n", + "\u001B[31mERROR: pytorch-lightning 1.0.8 has requirement PyYAML>=5.1, but you'll have pyyaml 3.13 which is incompatible.\u001B[0m\n", + "Installing collected packages: omegaconf, future, fsspec, pytorch-lightning\n", + " Found existing installation: future 0.16.0\n", + " Uninstalling future-0.16.0:\n", + " Successfully uninstalled future-0.16.0\n", + "Successfully installed fsspec-0.8.5 future-0.18.2 omegaconf-2.0.0 pytorch-lightning-1.0.8\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5gaAQZXTxFxD" + }, + "source": [ + "## Loading the model\n", + "\n", + "We load and print the config." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hUOUJaTj02Bq", + "outputId": "6671562d-f7f6-4407-ee40-22e1b83421e3" + }, + "source": [ + "from omegaconf import OmegaConf\n", + "config_path = \"logs/2020-11-09T13-31-51_sflckr/configs/2020-11-09T13-31-51-project.yaml\"\n", + "config = OmegaConf.load(config_path)\n", + "import yaml\n", + "print(yaml.dump(OmegaConf.to_container(config)))" + ], + "execution_count": 2, + "outputs": [ + { + "output_type": "stream", + "text": [ + "data:\n", + " params:\n", + " batch_size: 1\n", + " validation: {target: taming.data.sflckr.Examples}\n", + " target: main.DataModuleFromConfig\n", + "model:\n", + " base_learning_rate: 4.5e-06\n", + " params:\n", + " cond_stage_config:\n", + " params:\n", + " ddconfig:\n", + " attn_resolutions: [16]\n", + " ch: 128\n", + " ch_mult: [1, 1, 2, 2, 4]\n", + " double_z: false\n", + " dropout: 0.0\n", + " in_channels: 182\n", + " num_res_blocks: 2\n", + " out_ch: 182\n", + " resolution: 256\n", + " z_channels: 256\n", + " embed_dim: 256\n", + " image_key: segmentation\n", + " lossconfig: {target: taming.modules.losses.DummyLoss}\n", + " n_embed: 1024\n", + " target: taming.models.vqgan.VQModel\n", + " cond_stage_key: segmentation\n", + " first_stage_config:\n", + " params:\n", + " ddconfig:\n", + " attn_resolutions: [16]\n", + " ch: 128\n", + " ch_mult: [1, 1, 2, 2, 4]\n", + " double_z: false\n", + " dropout: 0.0\n", + " in_channels: 3\n", + " num_res_blocks: 2\n", + " out_ch: 3\n", + " resolution: 256\n", + " z_channels: 256\n", + " embed_dim: 256\n", + " lossconfig: {target: taming.modules.losses.DummyLoss}\n", + " n_embed: 1024\n", + " target: taming.models.vqgan.VQModel\n", + " first_stage_key: image\n", + " transformer_config:\n", + " params: {block_size: 512, n_embd: 1024, n_head: 16, n_layer: 24, vocab_size: 1024}\n", + " target: taming.modules.transformer.mingpt.GPT\n", + " target: taming.models.cond_transformer.Net2NetTransformer\n", + "\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FzQqNgiLEJ9J" + }, + "source": [ + "Instantiate the model." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "sWvDDVoz3RB4", + "outputId": "08462e1d-77da-4f9d-d3e9-43bdd2be74b8" + }, + "source": [ + "from taming.models.cond_transformer import Net2NetTransformer\n", + "model = Net2NetTransformer(**config.model.params)" + ], + "execution_count": 3, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Working with z of shape (1, 256, 16, 16) = 65536 dimensions.\n", + "Working with z of shape (1, 256, 16, 16) = 65536 dimensions.\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "njAiY_aqENwV" + }, + "source": [ + "Load the checkpoint." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "QABLRpVsDhba" + }, + "source": [ + "import torch\n", + "ckpt_path = \"logs/2020-11-09T13-31-51_sflckr/checkpoints/last.ckpt\"\n", + "sd = torch.load(ckpt_path, map_location=\"cpu\")[\"state_dict\"]\n", + "missing, unexpected = model.load_state_dict(sd, strict=False)" + ], + "execution_count": 4, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "iYN9cREY-r3N", + "outputId": "0954af07-f4b9-474a-d6bf-45355f2b360d" + }, + "source": [ + "model.cuda().eval()\n", + "torch.set_grad_enabled(False)" + ], + "execution_count": 5, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 5 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tTusbqk2y0u3" + }, + "source": [ + "## Load example data\n", + "\n", + "Load an example segmentation and visualize." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "8LiAOU6C-vTP" + }, + "source": [ + "from PIL import Image\n", + "import numpy as np\n", + "segmentation_path = \"data/sflckr_segmentations/norway/25735082181_999927fe5a_b.png\"\n", + "segmentation = Image.open(segmentation_path)\n", + "segmentation = np.array(segmentation)\n", + "segmentation = np.eye(182)[segmentation]\n", + "segmentation = torch.tensor(segmentation.transpose(2,0,1)[None]).to(dtype=torch.float32, device=model.device)" + ], + "execution_count": 27, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oCNy13FGPMy6" + }, + "source": [ + "Visualize" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 700 + }, + "id": "eZFxCkQ5McKG", + "outputId": "367a3774-3fcc-405c-c643-4477aad91a2d" + }, + "source": [ + "def show_segmentation(s):\n", + " s = s.detach().cpu().numpy().transpose(0,2,3,1)[0,:,:,None,:]\n", + " colorize = np.random.RandomState(1).randn(1,1,s.shape[-1],3)\n", + " colorize = colorize / colorize.sum(axis=2, keepdims=True)\n", + " s = s@colorize\n", + " s = s[...,0,:]\n", + " s = ((s+1.0)*127.5).clip(0,255).astype(np.uint8)\n", + " s = Image.fromarray(s)\n", + " display(s)\n", + "\n", + "show_segmentation(segmentation)" + ], + "execution_count": 28, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": { + "tags": [] + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hNBdHGbNTrfM" + }, + "source": [ + "Our model also employs a VQGAN for the conditioning information, i.e. the segmentation in this example. Let's autoencode the segmentation map. Encoding returns both the quantized code and its representation in terms of indices of a learned codebook." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 724 + }, + "id": "y_HS2hCCORLr", + "outputId": "770227a3-533e-4e77-a017-8f6a9b919940" + }, + "source": [ + "c_code, c_indices = model.encode_to_c(segmentation)\n", + "print(\"c_code\", c_code.shape, c_code.dtype)\n", + "print(\"c_indices\", c_indices.shape, c_indices.dtype)\n", + "assert c_code.shape[2]*c_code.shape[3] == c_indices.shape[0]\n", + "segmentation_rec = model.cond_stage_model.decode(c_code)\n", + "show_segmentation(torch.softmax(segmentation_rec, dim=1))" + ], + "execution_count": 29, + "outputs": [ + { + "output_type": "stream", + "text": [ + "c_code torch.Size([1, 256, 42, 64]) torch.float32\n", + "c_indices torch.Size([1, 2688]) torch.int64\n" + ], + "name": "stdout" + }, + { + "output_type": "display_data", + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": { + "tags": [] + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pp-OsC1RXAQ9" + }, + "source": [ + "Let's sample indices corresponding to codes from the image VQGAN given the segmentation code. We init randomly and take a look." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 689 + }, + "id": "VTfao3jJSCfW", + "outputId": "1a3320ff-a389-4759-ced3-5dee58bf7a29" + }, + "source": [ + "def show_image(s):\n", + " s = s.detach().cpu().numpy().transpose(0,2,3,1)[0]\n", + " s = ((s+1.0)*127.5).clip(0,255).astype(np.uint8)\n", + " s = Image.fromarray(s)\n", + " display(s)\n", + "\n", + "codebook_size = config.model.params.first_stage_config.params.embed_dim\n", + "z_indices_shape = c_indices.shape\n", + "z_code_shape = c_code.shape\n", + "z_indices = torch.randint(codebook_size, z_indices_shape, device=model.device)\n", + "x_sample = model.decode_to_img(z_indices, z_code_shape)\n", + "show_image(x_sample)" + ], + "execution_count": 30, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "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ma1mlAttYGlBr0gx9IzA1bK8fu3g6PjYAaC7FoZAKANAdZ1ijLZ1hRCbrze2rZM8Oz29oJwOi34xGidpnveTS7fuPv36q15//OrxPcZIvZwlWVFOZ1FPYAyR9TCOZFURITChrjYK+H4xUF2Ngh+OxgEiznjVtVmv/95P/eZv/7f/F8C4Q4AgRDGcLhYHe8Wjrx4m+cBKfenK0CnonE1zfHD94IsvDyfXLr+8/2pT1x663Su7WCpZNutNjbJ8src/PTo9Pro4uHPztTu3gEfeBwBDlCTjUR8zngiircMRjhJ08visl2dGKyftfDk3rnMutK2lEPJA816PRyBNe13XBgIBBMGHPCuuXdnBWPgEs2DuffqQMDI/OveQFpNBL0uZwN6Gi6a00/Wd995ry4sbd781vnyFefv8i89JFlEWGaPPpvPPPvwMUS5b0BvzTtm2rFRdOq/zJF43pq1qRLCSMiK0qTZcZBg7EQsPQXC+l/eXVfXWT/2NV09/0HTl/tbQto01BrnQ336tXj88XrcJBp01WRJJ2XkEMUTGBKecyHItFZKe8nhnv6+aJs+Lpu0G46JuahBwtVmPx6Od27v3fvT8m99+98XxYULJyatjRsHp/HS0vbuYLVer6saVAxnUxdkKUVyX1SCNJsn448++fvfd2w8ePXn/G7fnxhjj7/34x71BajjelAY4r7UXgsHgt3a3015/+uLpo/uH775981f+wq+3SgM8dHr64R//2Xf//Ds/+OzL4PBysRkO+2WwFIAnH32mOjCYFOPxNmawrDayg/konp/N21ZdOdhZbmaj0V5ALh32TLXoJbujnZv/4v/+XxvMe4ODdvbASAApnq6qlMSrer1q5H/5f/zVoMw//ad/upFQaQkR+Bt/86//uz/817Ego/0bW7s7WMkQwriYiISfnp1TkclO7exOytViUqTnF8fT87P3vvUzTw718eLhOz/7693hR/XRSbpVvLy4uPPBL9athohvTp5P+n15+go4NdkftCr0BkNLmbP06ZMnl29cTzmZnZ5tbe8GHyilb37rz29vpfX0i03b6BI/Oyy75QYmJOIIIHj5+s5oNMY2PD97fnK2ggCPtve3+hEwYL1cHp+ft7KlEAx3bplqjil2wFd11zYli9N+r7dZL7Yn295IHsVd22RxvClrD+BgMCSYPD8+ztPsYjn31lSLuuht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\n", + "text/plain": [ + "" + ] + }, + "metadata": { + "tags": [] + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ftDWneY3zJZ5" + }, + "source": [ + "## Sample an image\n", + "\n", + "We use the transformer in a sliding window manner to sample all code entries sequentially. The code below assumes a window size of $16\\times 16$." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 724 + }, + "id": "5rVRrUOwbEH0", + "outputId": "f5f860e0-6a3c-44ac-9361-bd4d1be8318e" + }, + "source": [ + "from IPython.display import clear_output\n", + "import time\n", + "\n", + "idx = z_indices\n", + "idx = idx.reshape(z_code_shape[0],z_code_shape[2],z_code_shape[3])\n", + "\n", + "cidx = c_indices\n", + "cidx = cidx.reshape(c_code.shape[0],c_code.shape[2],c_code.shape[3])\n", + "\n", + "temperature = 1.0\n", + "top_k = 100\n", + "update_every = 50\n", + "\n", + "start_t = time.time()\n", + "for i in range(0, z_code_shape[2]-0):\n", + " if i <= 8:\n", + " local_i = i\n", + " elif z_code_shape[2]-i < 8:\n", + " local_i = 16-(z_code_shape[2]-i)\n", + " else:\n", + " local_i = 8\n", + " for j in range(0,z_code_shape[3]-0):\n", + " if j <= 8:\n", + " local_j = j\n", + " elif z_code_shape[3]-j < 8:\n", + " local_j = 16-(z_code_shape[3]-j)\n", + " else:\n", + " local_j = 8\n", + "\n", + " i_start = i-local_i\n", + " i_end = i_start+16\n", + " j_start = j-local_j\n", + " j_end = j_start+16\n", + " \n", + " patch = idx[:,i_start:i_end,j_start:j_end]\n", + " patch = patch.reshape(patch.shape[0],-1)\n", + " cpatch = cidx[:, i_start:i_end, j_start:j_end]\n", + " cpatch = cpatch.reshape(cpatch.shape[0], -1)\n", + " patch = torch.cat((cpatch, patch), dim=1)\n", + " logits,_ = model.transformer(patch[:,:-1])\n", + " logits = logits[:, -256:, :]\n", + " logits = logits.reshape(z_code_shape[0],16,16,-1)\n", + " logits = logits[:,local_i,local_j,:]\n", + "\n", + " logits = logits/temperature\n", + "\n", + " if top_k is not None:\n", + " logits = model.top_k_logits(logits, top_k)\n", + "\n", + " probs = torch.nn.functional.softmax(logits, dim=-1)\n", + " idx[:,i,j] = torch.multinomial(probs, num_samples=1)\n", + "\n", + " step = i*z_code_shape[3]+j\n", + " if step%update_every==0 or step==z_code_shape[2]*z_code_shape[3]-1:\n", + " x_sample = model.decode_to_img(idx, z_code_shape)\n", + " clear_output()\n", + " print(f\"Time: {time.time() - start_t} seconds\")\n", + " print(f\"Step: ({i},{j}) | Local: ({local_i},{local_j}) | Crop: ({i_start}:{i_end},{j_start}:{j_end})\")\n", + " show_image(x_sample)" + ], + "execution_count": 31, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Time: 378.9832909107208 seconds\n", + "Step: (41,63) | Local: (15,15) | Crop: (26:42,48:64)\n" + ], + "name": "stdout" + }, + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABAAAAAKgCAIAAACKs5gAAAEAAElEQVR4nFz93ZJkzY4riAGgr6zd51zpJTQmk80D6EY2mneRZKbz/jp7ZywSugA96tN0W7d9VZUZsX7c6SQAgvw//9/+n/Z/18/Hfgt/2q2CwZkW6SElm0W9/ZIkBrBZNiADJsYQABKeESwVIMMA7JeEBwBNki7V2PZgBjjAAJSe7k/J9AwAlE3QEEjYAwBumCIJ2QLHgKdJgIJpEjQxY1NFe8YESJJnPADsFgEToEHQFAnOvLClx26gSM28JAgDBsvONxgAwSEIwF0gKVC2bdpDgYBBkIApAZxp2KQAAyKr+5VAG5QhGODc+8U+aoOAWN7nydwvkecp0pRs2Pn8A9gmeWZe0QQADATbtAgINEHaQxigWOKf9i85/XY+1fkXkQKAnhdj8pAYg6w8H9kG9/nAWR5517AJC5BkFGzD9pDe509RI8pG4+WYLHtAlc7bH4HwkBjkpgeSCBvj5ox4CI9FVs/LPE8YKhuAQed6AACGR4C0q8v2zJCADTAPnaThmaaHPPDYpP70/BaBaQPQyacaQxqEDdjITsjCRZbO9/MBMtdjkwT8YiTSyPNsErYBkLLz80PaHoDw2CBAijiDJjDTFDD3mQPZDs56hgA7621aWQsAdD8fQyAXB3hfGWXmF3e9IZuZ2SsA4Bm4iSf/Rj09r+C9VR6P/17/PnzY3gUImYQB3Ls2TDL7Mv8zDRdgUHc9026SYyNPWhRhj214iAKQ9d/z0d7gjVdZ7hjDu6w9MMhcVB5Argc2TUkmCWPcmFH215A87Y9geLIePDZBjrJvc3kzyqpCjYfkTFPenc0CLWpg/P38sUWenlcEPeDuXxAUkO1DAINJfMh6G+a97PvKN5t7IQMPUCRsic/rj8Ddj5ANEia0i9BZ3SAAATVukMSQMJjdzmz03GOe2m6kGgzBmSGz2bDxirRtD21uWBOyPomEZkO7exJE818eGCIIgv+Mh0xs/n43kPhgooDJ+h+/JPM8dz0A4F3/ORQ8IrinCXb9a9c0QNASbdiT/ZXL+O7fLDFLd8tMNnhuG7AAkmBtnPSQ4DdO0VSiPjCDDUnaz0/sUDYODXDPCwL7PEmPSWjczHO/HwoRGCnRAsiCJQjhe754wNxq4gP2dW9w+BtP7Gae2n1DCdISnPsySIkciziN916PwbPnaO4jdwLSI34fNZ31rIQEIS96f2D+D9eT/UV3IuDGEOVLbPuubhgkatAg6ckhvfkGvWf33pJ3F3mP6ryvG0q+P8u7ZXYxgpwBdxHt9WwYxPdX76pK1LsJ067gDay48Rl3c+VZyTl5Pff53/WfTMIm5t6v9m/uegBp0/A9qLM4nKOMpFEABmZSN5a9AZvf+JxTnM5+GUKqHwEeUf/68/PzU6KfkmeMKRZLm1NuuABs1aEsoJTADlDEzFglCQRL533fMcZTTNwviSoS+M+//6OfZwaYsarqTPf0KwkcATMu5hWwirCriuR0i65DAqc0ZhWLLPEUj1QF0QKfn5J9iqfEXKAhSqUjarepqBycVJGUQGkfVZX8DU9innOWwWD/c09XA6Skm7jchCnx37DB+7HKT+RohhMek4JizxmJFF3EDAgOQDbA//bz8H/53/5f0yDRfg9/srFUyhlvMNkKlUyoN/ZAPcMSbNvZtDkPYGIGQtXxDOC2Rc6YLNoe81ROi7EFG9Z5MD2bQYrQzJhU8ruSDXjsSbalU+AtFmACM7ypfKvkLHDJbkyS3JoZwJMUxCblcT6NgHsGCd3UOeg30RCgoPZAYlJGgawkovzm1lVomzfRgEDR9jSfQ3N6EtZs6BzMzJ6+FCsfC9MzLBKCB4n1MziU9nl+ozdZmAHAIxhue68fqsPpscEBSmAnUhgzlkgecIyhgbEPSz/TL1NmbaAQZgjwHNjzzgASAOoc7/NJVK13Xh15PG9n9adyI+QZ0DoHbad82EUsGvboFMyZMSzCA/0czvSM0EOJ6h5JmIGhIxjTY3Q2juqx932lEBwbRRrTrbsLN2DakFXHPcb8cz1gDJunMPbMYJSDqv7A73iY9B2EbZGgu1VM7E4aa5tKGpGM3SRnhlLKZ2gPeBOAZVtHQOfzbxjfNM+TcGOPmRViFpRiCflBzJgqzOT6mefDUTLoql1vMqzE/mRZzsLbuhP3CiFVkttBdsowaYTBU55JUrkBqA5mnPLPSnU1RIHjoe4ZSdt0m4JUm2km55tkNvS0SjDHk38CgKpKFrsnGGaQ9QyDxaTik/VgsA4xyT6x6wEW6KR0vO8F2RGQSydARQq+GWPTTLMObHdPnh3I82A+uR6gZA48sICZqdI/zvtsfJ86+75sQNMDlQzYPAee/G+Wqs6DeWdSvUisTvA2PKMSILg3X7AtlnajBY9JlY4xMKqyUz9uLqdzaGc9Z/1n/SQ3Z20R7Ju3UIfJqm9BlmKUoHukHE2bWGb9kLXR+pvjpYyxeYTZz8+CVx16erYa4a4WbIqvQA1zkRVAEJVqeTZeGX/3r2hOtzl7PtahE2/hux626JshyQvtZCFRllLE5knCk7Qk8UoJ1L4IC6u4+wu2tKdsTut9Pps7mvCwyLv+E8phJ3fGNKuQF0l8USQlpgUx2nxOTBpYhLeMTrpOFTzjIFZScluYOU9F3Lwvj46i9H2eW34xFaGHJZpb4qbck3bPJqHbMkup8FmEk/Z5U6NzgJ65ZYs5yeJI90AganOWm99ICoiw1+PvfnfOx9nPT75VGz+Z55hdHCTLKsK0J0Up876A8ZZ/giaLFnQnHmrjeTLejecbjzZ7V/Cu4X5+KhIvRONcA24an99l4u3fFZ4IJiDx3DD3PMLmAy0RkBOOk2kpIJHzsdp4iD19KonQN36SJRrtLA/J9E0sv/lMsIzd8fv5eRuW9XfjeZREC6bgMZOpqbIo/pTJqu6qc3703/7rhxI84sOt5w00OxFcPBKV3aQsO4Hm3PWm/JVb5yHGJMcD5dpoGQPy3RPQn7bwkUgWjEp2ZFBNV3ZUqfJyT7Vm0TNRBOuwSp45h09JxvOjoyO4Hj1FACUDSg2ScgbkKWxJLihrqJLySwE+kL/kXVR7xJCGagNTClSa1KKGt+RNGE3JvoHz1uopPHD/27AgcOBApvczNpcYFksH/p/8X/7v/28LPYRdFI0Z81R+2x4CSOKlYg7ZlCOqZLSYb2kIUtMvpQ2ovIfEGNTMq0BNuhs4qKVJmSrPi28qBvQ0Qf1FpWYWzgjIgEls2kMkp8ytr809BDlgXbTD/9jCHLfAGaqyRLwsAgSOdHreBQ0BUuEQvvebSnAm+eUW6v+AQ2S/GxCkTXKSjFhQ0O53f5E3vmMriC2xAcBS2RYVCBwebk3fAj38RwDKWRIMd0H65ROomeGiDkx5HQiRECbPvO/n1wDTLcoDVlAQtKcUWL9Vp/uTJQpYOt2dqjPvo0omp1+GtslXB8Dfk3NxNihY58w+/yDxst8AaZJAvu9bAEab8Hn+8b4cUuKLXFGaVEfji18HdJjdLCYwgU9zLo1b5q1MOG5fsoX7vtq5QQLWxZy8VFLWKEYs5GDmhTNgQD1v7frfd7E1M+4ewAQg0oXNc+AFTbYH0BeVXUokJ8x3PTsHoHwLhuBeUNDWMTb28wLRS17shyqQaiJUzu9d2tT4lcNwKHmz758hizVuJC1YmA3OflFQo1Txs294Ibctl4EbH7Bnm2csbkboMF29tw4CbI9md48Nu638m4Ds+sZWbBcDyz7PA0hG6aZqj1UO/mJuHLRm3xeMyQGZDKaw6+0COHs4J8G9eRuo7g+hPdQWLs3ikD00PVAVwe4OAfg3Hvq910qAk+ruLyx0EwL+XQ8XHsaND/Lk+gO9cPd70niMb3zbUObwJeWZJPTid42mOkzo0yaUF7fchHJRKF4c038B5LA3XiiaScgWtUyCKtwC6ZtfYgmBLLLvRhOS9GP2PAT8j/Wf97Xrn6b/fv5dmsyWD4C1SDex1HdAjg2+WFqRHPdCut+E7Iu6Jv64U6T9BX3/gQhlEdujG+EugreY2qAF5fb3SfCL0N+V8/d9K/f7j/XvvR7+43nu/eoeKP84X+yL2OV6tPEQe74Zr1y2ca8nu4ssLLM9uCk9CHvu89+EG4FG90MZBhgLmMr3vLtHcuCxyevb611sND/Ymz/wAoL6+3y+73efQGqAPff5Nx4yq3fT5u8OyubHd1kAF7y/2dd9TxegD4X+FxVPuXdvBMt0XRhn48092r2Jzl6t/b1h3xven781Ar4HzS19v2XIvpWZTrzPSk5gwi0o/v9KGoLLIVgOHHc/jhA1S4gsF0Igy5vJLgPzYE8JOiiI6qinH1iFB+cU/uvP+XnOQCqUzrMsymdpm2BEBVmD+zKzU8xG1iMmQDbxnAr/lqpvBiiQ+n0H418PZtmimWGhYIEC6+x5StZ03xPEhE65KvyqSRzSdBHFUvmcgJl4npLxSPWoiFPJ/DeZPyBLECsFaAFQUTDq8JL8Sc4JWRBMMLVzoELcsLBnrr859c0GsTnn/jkvKH+TvzZ8eQAysAJpuCjib9m/565YeA+nf+r8Sj2my2yeKoWVtvgYQ3h05GE21uZPqNL7fkiZDn7g8DvgwHXUY90t03DVwvYJECBrVUTJ3y08dherc/bkac0Aw5INLujKQcroCqe8Uh+RZvek1roFUjEpbXFsYXPlgasOCPaQYxUNIcw1GuC8uuEcMDBH+vTL0sZETNCjJA9V6nby9ZkxRypSiyiAYxQWZzJMNFlEVvEFFHyjgIKm6KLlZHFMweSxe4AqATLz+bJ9gW7bxryCUteFWSvyNUS0zUOQ7Ad+zYFYxbbKkXOMzTp/6H4xJRgFTLECIA84/YqHDE1EjB+d3/dXSs1Ytt2p3mjgHL1j2qrT3Yy+yJ6ZFHSQzh5xUSsZeb8Q4Hd8qrAQ01AneyZMpgF4xEp+kL8SFwWBLvMCKCoRuA7fYQHU8bSBUoF0N2WgcBPve3AOcejGxkDknE1te/ES5H5fzDm1F4sCZjyVQtEwmqWrnCu4DSVrl42SZ7LAZPR4eWwWDDpwmLXrYT+/Pan1N5ahvoUD/zLC39w+4BDHDXBP5JzHy2G6Su8E4FdqV1EQPZZsFDVfXAibLJXdUkojgcDbS18GX/yr5bIO8wS+zyd5sMdO4A8RCWKXkY1wRmV3AwJYoRaTNNQ9XbNzBihgpLqwReBygKa4+SUraFY9G6/yjpoUCyJmyAEPJwzI5prllgtokJOqUvA72eAQ0WO3QKT8OvV2VFIg1WbVYxscKuKcZVMgjvt4gNJdz+NFHDxtfrM73UINShWPwA0zvusZjawU1qoePQZqkV8QZUxqexpjAi+r7NImt3keWQko6moyF04AQnbiW0ubkPJ+g0SMw4tj8fXVBv4j79GmR5bKmG+ee3ENGbcYTeKiQDvErh9rP3/AoOnSLr5gyoHKJ/lFcq/wGF/phj0i7/qs1QcuwDqBISJK2UW9ZUI2fpCFPRqQI7nhZf61cO3NZiUGSqKUrya1pDSNCoGcRzIG9I0YEOBldMV9nhCSQ2BLJWHTTUmRb4UoGvetvrduzH9S2HiSJ+Ahy9ICk+JsWZPCJqhe4E0R85q1Utjo0kToBobJO8gDDPK5uXayyX3+YVVFcDBVmi2lC6kVefLOsOuhbq31fb8iE4UmUD+xz5Oqu36w+W7OFwyThGyoxGa9V2MGWyuLKE/KsFQa/gqwQAha9B77frGZ3fxFdpij0P8oLzZcB+RaRCiVTGoscjZMZS3fVAnL2HxPkIGh2ti66427d+FbiIv5qCiiNonMWq6/9cnmGzmVs0NSyBYDYipVxy1NxKSTCadAFwtEHVn85KuGz4HJnhbIOjmkMBBkkxyAr0fRTCfokZ4euMKhkf2ZOjLUhA57SKsNez6vZfV8BlPgQ5l+7ec8nnkRlQjA6pk9KT0y+o1MMRB3R9gy7uLBO8P618P3d0iMfDyPSp7z/HiGrIlwcKDx0FJpiIgNaQ8sMWUtAUCuDUtQTiSay+dHwEliXKui3HVjX/B/8RQg8VbAosOuVXxllcseFg4rP7AyB2gLDqiGp86f4QD1c+imhxOULqcN8I5Jcl6oCBoChwPD0/Oj09hN20vQA8CpB3PxAGCMJ3jwTPnMtC45O9O8gWvcGLw1nE5RXtIL/EgfzzHHK0za/wGweAFhcajIffJwjKxXmOcUZ3XoOSkPATx+f4vHfv+iQ+EyUwgDdqGcNKn7/VPnvYfE20iyC+NUYVA5pgeyFnTvN1kROQTTJBBlfd6HTZTlRRifOu15quYer8laVWLOTef80hGA8nTh2A121vBMSwXM4gTmLHnomXl0TDziC3A6rCB5KNEuJ2c1h/n/MxDzvjrgpj2oWhyBnI1vY+Htz7/qeekyxzNRLpMwznkwU3OfD4oGXTOvcOwW3ckeJj0hew55dq2LLpzx3K6HUQJtN1RAimm4gboo78yRBi4KDAMdVlOqgnH2NEk6YaI8LTzI+zICS1OrkY0wkcmHg9JC4JQ1jvBnseNHhw5HBWp6T3ci0ljJFDG0Zt4U+hhzD6IR2GNRtIUqchBN8C5PVd7XKu0nrG9WGmqvH6A1frMjdl+MWQh8A8+R5uJ6s8vNBMXCuMCIGto3+/eerxERwjV+l/HIMjNQVqidHoETAmL+oeAgj/L5tsmoVlLLLD6KlZY7JUpwDSoiFM5mrgltt3AmALOnyZt8LMY1Fw1jUc1Jlj/YFWTj1MGgrBuvfAigelo+Rgu5X7Xf1bxG5rNVBiFyXKwOqDkXDycJnnPQc1A2imz3QxiJD8fz0WpG1Bv6ApnbBgqclPA+UkhPkFHMpgVBUQlcFNiBeyBPCydIs2fXM1hC1FvjZhavUvBgtFcIgDRnOivumx2NBpdIPFLPiAQ0K5gEQC0rmhdgOBctTxOFaS4fE1ZH5Mp+bKAGF5w/qsGQBDXhqWGAqtVMBMW2XWRYZfmAzfTuoKZfqLjv3xi4LnDrWQADAYKigybgo+OIz3uTqCOZwoxwPB16HRbckHBpBZvQcKKdmUN19P5eACLrv6g0dtyKINtTzPpf0NoEp8eslDK1KhyPXSSWTyjMUAMy+52ov/0V49YsAsw91QM4Zk8YSA6/JYAhoI2SIutCiuaQEImHLG3lbIytSf2ak6BcgJ08FByPVKEUshySKqdyLGrowHhZSEGvavG4PC9vfKPg5l+gDIRm+ua1UbQw8iI6vXNWxKgJeWnmyfGyxSShXdlKkwQCA2/mnS8q1XbTGGRUo8n+t08PtG6uHPnDFiZhWbi8XJb4EcGki/5iVdv1YmzLm81bNYrytpBs2b353EJfHTwdi2rky3xp43A13qdg4AI3+/zhIr9bK6pdUbK862Hpn6JyNtWyJXPbQCNYD0M5EN/p/9N//1cZh2fcKp3DQ8IvTm2l4OkeFsdBTxyUpseWyT7kvwcomhUmboZpJDgErXaTGA/sdwbkb/+2DZOHnzHIn6qeYU1S654eCDOvWBiw/uc7VMlQUc1m/ZCfFPDTguoY5FEAJI81Ap/qniNRQ/EdC5fb8n3T2T8NuUXSsw1TZSwlWJjvRk0PJyfUxt9ut8n5nko9IpFL0Ri8LxyTFcw9UOfCJ2xP8IrZOINEI2JeznlnyuXprhP+GuM+o4FR467KajnAcIpoIso6UmWWGJlaJSmZ6Mu8OAE8s4KgGr/RueK5sGrUh1ttN43QIq4KcjLTID6wqHdaQaG5QAKmJ30Et3Cfye6gyGESkavjEKIoaYPUDMmP7XHjqK4kUiKRFClxpjE3R6kzoHRmXHqKA3cO+ITNTbMrsJmmG2ZjWMQ9G1UVqRsSyTH7PIeqGoA6EfVsJpHkZ6MV7On8jctuGINJ9pTgU+dgUbGogiY5SFprTSVjIKahquRnhaCsM+NpIUL84WeJ5boknCmVsBIszNCB3twjSYOi3R7VA7QQwYqC9iS+9D4KeV4bg2GgnGSDRbGC7EyPFxU3XYObB9HbY470b9B4SKAN9mIwoOoERxyYEKUFMLBcmE27V3ecNseBlwdP5LOe4tLmA6OKENPrbmjwgrKkrHRgrr6SaS9Y2R2/LVgmt/lhY3ml6yPgTTL1mSZlyAGYI8+SaKjueg4n7Bkx2BnYwdq2xyDoTBVM4CGHe9otIrXSlwWxxNRtl9UGgREi8SaAbadpWLVA1Hgqn58Np6ujYDDcpMZpnIQ0RFj8+/zDcEgbK+6JRokW0ABQ9d3BGDq7EWbqvATQ9EjkM46WV8+HcgI0B+R0KA4GuEw5vkIGeGZgdM5L+9gvxu3mc8HA0Jje3Qabh+MVBdrs+2AlTPrPEPbIMN1jjktRHpovjEbjbH7Q7dX3b/YNo79QK6IKDemJFWHk5M/O8sCeGw7L6ByPLOhvJpU+sUOEn5jdDSM7DUC8NFDsGG65AaBhDq7IiOCAkUORUkSjy8olN4SdFrGAlVHjJR5meUZ+qZRdWXMXjI8QKqsIAqPvWkIwwFdWwNbN5e1yyONhjgOdyjbI6vFq+QYgdHAFE2BJ6bgwsK3fM0RaWH3DQeJtJSYDtwNys+HQ+tBsllZ/5RdcfuYK+JQvMHtUqSSdNx5zhKBFAE5p0WZsmrGlVYUGMJ14uZ9RB0Ok/dEaBJ3E34hBQkViERPeJlswAWf227XZIAMd7HHFOoSMuqjkOBsjWpJB+ujIMoYjVV42g3RefB1YJpY3QkoyEVUYN4kdZz0bMCo1XcDybSw30gcaMlijCQpo0OUcIVxN+fI9lZfHlbAt1ABY24TEmtXYJ5/Bxh9vQn3DDb+J/639QBKquz1yc0s27i+sgiHlMbf0XCbtYh4rXmXofXAvLMtjG1f2grIVFgBZ3DP0y4ZTVUTvOTY3sqeWJ6ODp3JGW6Px1Orws7qXP093wz63glBwA9jidJtAATCSap7/DP7r/Ot33p96ZpLgH/HR2wFPqIfsZLQG+114NH2hAKZULF/ObNIVVjVGWxgA1ePf9+3h75t+jtTHgcTOTDcpn9/+ZU3sZjrH+7rcJA/ho4l7yTugMbQ90TS9fYz34ajqtXXqz8/h5/3z/Pzn069wKkRPLfyH5hQ4K8RFcWHJnKo0Vrw23QqIkU2w8FuyzQgpVxx3H1Lu0Js6b5pjMEUUFzHejwpgFqAItN2+gLVvKKoDcPjq/NgvtRwCqStOJbY07bGbr5h8fQi/sNCpcns1o6tdysFKlkPKDXDeAl/hiBN0lHO7e3vTchClvXOu70zy8jwRiHVprQigWYUUsh7QKCa9iAvBlw5YcPN2n3PMAxpNHWrYUTcReWwDdyN3ihLTV9OwLn8+80lI90yCMw0yAhNwaI2oHlfxKt6XtBRs9xhEcRvHJ9UgLujd3vcURHypRVKqZDU8I7AjK9pwOLG30djuQDtc2wrmPC/0DRwoGbSMVB9cpUayJdcB0rq/h6/emSMPklTM2GKROlIu78Xc5yN7KklvUuoAX6G/QBpWizV2UeasFvUyRsDMRnouBHW2QCDkeK2sdihhtnMabGYHY2Y/dJO3cZpPv7iwv8+TGrBGYN9+BJntqYMJmeeQFwXWdkDRLz7aIiss8MrrEOQNq4uwo3LEPQ3yPk14V8tmnLc8B8QzmJX4+8vrrqPLFZM6BDyWzbbArtSeicbXrGt2vWk9LXCZ5Sy5XZKXIg0qNZXLkZD9W539q5zlwNh1OHbB45lhaTeCEEnHXBQgKVrKk6Bg3uSV5RxqMtYzJYVSFrC5gEdHLkQpyRA4JLq2oS7RMxqy8AarNqK2OrF9TSYGS0kMlCxvYYpt1pQMnpb1wke8RZ1LbobR6bZWsZ3mUG56C3oXvFdBQsKYhC9LAQ11gGEXitxSwaOnvHV7x8sFMZKY2Ye+x0A64XpWN69bEkZqRdo8pjnFwtUY23qSUE0sFgJ3pCp0eTBJ9/dcwXb/Bc120tbdkX+DlQPE+uXGK+MLuhBLHRiolILRaXorqHRYYONVVGbc7JqLWgUU3RqQSuNKisO8stXwj7I+eZv6bT3cksSvkX8IHKot/7fkTEf15IJyRnLJmzTpETWCWoFLE/Ci4Em3XPe3Mrs9F7MmNb6beF8eLnILkDq2D2o4BL791DcxXcka0sBJxoRBBjUAendjQp5VmJWAdVYBBa4cE7OVgDcsXqx6SzEMyMRkmkkLZ+X6+SWztveb7jxPLtO0x2evR0qefy4yLcsNwFDtBXPcvgIqwMCsD8D+lb/rxxF6yQQ7xHyAvbnxDQrAazBC5xPAnzNpOcOip9rzN/gEjJRa6X4b8CI2C0UETcWlAYCUKcgVDBea2fijv/E7+4IpARDGzfirj7qQg3FrrwUwNoPbLgWlulq6ip5wTes7smAJbCihff8tHXHwDZS0b/dOujNX0vnFoIgLNKygWbfAiIoJB9W+wfzmj1iK4tYaJoHCkx38/n7eIaRfzhF+3y75oUrqURvoN3Vpew5JlIcN43oBsQf4SA8i8hGfOir1TI8/r2dm2p+ZU3+AntcvJHdbbj3G6PidKh/qnZAIRVf3f8iffqciNhLHBw1bBqb4cEy8L4ZGoVBUTVt8TqPYpfrPf351nqQeD+VPqw4xlCoY2UXTpglGLuIim6rIhYTwvRNhOjgLcdXFStKXk7Nxz9DUnklgNvIZzjGQdjRfTd3Aao3SBiVycFUq2yEA/l/+t//hNuoQfps6OSsmpelGLkJw5FnXcHACwc7mDpv5IB2pcvdi0lgWWWeB1IK/HoME0AOrGdW3Kpxa/KeoiE9ijJfOAhEPNW+Hmm0uj39rgBxrBXjZE6Cq/L7hAmYT0lEdIVxLh/ucUyXi005EBwaqBa6Z3aO352h5mw1yUhFv7B9wE17znNQkpYE1NqtIohtfNBxrH7P92mRNQhZ8MRBYpfURwV+uZ12lbEqD21kKwB1GIRu7gnIKGjQQPu4eKZsqoqj+vLPN1Z5Er+ecdhtHbZ4Z80gE20lt4r5aGBNyNV5Kp6fFFPGV5iKqxPl04qN4gYNzavyCh23UzOAckXg79VRko7zSqcTEmaCgeWpKChsudBaHDsS0gijMONYdCY9bJMjdwXVINJz3FFy8OI4nRBWB9SLAWoUox9btR1dQf0TOuE3DxTV8uoVgwneEALEO3GohH52a+0ZjO4eTtnODScIT6YN8Q92D2zg2gSePAkXrsoNxKkDH5m4rMC0ivUEiIOdiUwHcAr/33GYrNAITZdkzKcZgXds23+aKC9n9NapKXh00fogYvDp9HXc95/xiNtfc+IW0FO4N1oIAXsQxj7MCOOM2qJLbRR/noRResUnJ2W1AnA6em+tJy04ktApO65CmDjsyZnEFzCiRxJv8aOKNxJ5J9RqQfDw5D8XqSbeZSv6kv/cG7nfwHAAeHA6s4W2jmuQ5ayFyuwJyCXF5uvH2Hg2i5h1/NctIzVkVOaOAG4sjnKgNXIttbiKwFMuVYO3BskhtBIkELlpMBIycwddRaes7UfQ7vELvfW2StgHUCNBdlaJTN95ibbK+F7tIUKrGLZKji17aDuDNFqTURYVVynvtBpBmdyOi0X1ZSVvSQLIHCND3k/65/hfNr7qdLasaTHxwd2RuXnJp48/WQH9zpTUkXVCm59vY6+CtirVJMuAUxO7VwG8ydhPSIDUEiLAkWXOSgZkp63of5cdz0N4Qwq2r81oBFjntq3m/n7/ClTjGcIxvZ3IwNnhT9EgQtH+IcS8lBJ7C0m420m+1Xf7AVSCRDla9ciOK6VJb3D25rI5q0NGKOyDfdlUcaOjUxFh1UIpzzHDK8uXPxgYqIrktwJIXXJRw+ao9Gee7sjbqB5xdJjxVxF3P4XSEbnwFjLsQ7nv2t7DZt3P3+Po9bfHgZbn419QuvMGaHlLfwjgH4GYJmv/j9WRTrngVEC7zHAzBZIBnEFn/6ZIuMivvuswlGmxCtXz0Uuv40pFGb9kF/RyN8d+enzr6c7TxjSPUc8qGhmZ55h33vCEtAFi3g4EMbnmkNLuoylaBnhnP+/an2/DgYPjOL4m+iCPwPKXX71MwS8A5iIkLpyi/r0vJaFkbqxQPaAAUKpwecMRTKPIcDvrRv/7U/PfnBwfA/KmidOinNBgdCi6dI5xThlddt7VyFiIkFTgMoLkS3aK+DNmNDIhJdNToiwjciFCRL66t3za4Y9tIrLRHBTQgRVQ9Zp9sK0kQ0UcYnjP+Ad9zFolJlTEc4QfzDoeqBc8BsH0NxqIB9Z7AQ9V4yc6gI+HMqBVhTI/F6SYONKf+2B/EJhTANMDxG1IkGuWe94IQaYJ9PY4Im6Ld6WefDXhJgw5JoldL1cmkXlQ5lQZnlcozLs00cT7jihnW7ahbJ2Y4guy2n9K7ViR703Y2XqTOwO0KzbaddgfAw9O2btiNDntpbvei5vQp9myDkTdYcILuXORsw7TWc3D2ITw9c+oRe/MqxhawgHfWz+Z9xDetGn9tv9Kqa9JgoJQkilPi+9qF7ibr7ZZ+uhuaU+oZ9MtIXd4h2PCj85mEwMHeXRYLxT3nI21Npfj2uOj7fEqPNHT76thuMZAES5h/MP6keDh9swQszkfOODumxE7OnrKKQpjsHEyXvAYnjoHTA3FmiPOOS8fuqG9meZWA35HqppNvgPThGY4dxy2HA1XdWB0tXaef0lqM0G0qptrXU6M8M1sDDEIEhLfKMdT3rFjLlz2WRPZ4FOa+xpaO6AwrcHCvvLCVF1wj1mQpm3Zsuk1dnNAVrUdR3Ybyas47U3VS9cQnLJniJTOiJbW3txBM05ojRg86QKOD9sYFor0PjzzgGyyU5vjlAsSJ1DajefImOyEIq/rNEde4vXTw5Q/TdpqvwLZM0tFZeSH2DV6xJmT3YJ2RTs8Uj7sn2LTJm2TmaDcYGUe4LNbm4l6LglBr3XFYDRDXPZKnxedlPyzPakGS+NoBg3KpaSyY+bY45BXN2ihsc9emYiGC5yUxLhwIVed9LRsI4J2wQKfjcx9pepwKiklSso5Ekr8d/07mz22yYipxhGcBMX+fZ5RQWZ9tr7Di53VX1C+86gfcZvrZ6uT6u+Ibco1FM8lLXJlgYK3NmWZGOCNX1dvDL7V4M639RCJG1rFCy9HsTQ5NrR0JZgUYBfU6OzZ8eiwdXK7vFvEiFh7AQr8TV9R7IqYxNKf45nverudKTKCtm3vftH3RM3D12sn3bb8ApsWf1310gtdpWxsuv+KbdCJ7DZFV5UaTxy1AwH20+WcbTcAjPMv4xfmZnVRmc+hZrr3SIIgIVyBVrLyyyhb2WpEzSBQxXl3SglK+xRSNtINnnkQI5EU3hj4Gq073yBgOuZjO3XJFslDD9B7JGKaRFyCX6k9omtnj+1Dt2Zw35JJ3kUXGuLXu0v2ZHpCHqIkYNPXMJvGzdKnn28h0BR76QnqQLl20mN9SvDsV57JYKcmW087+IlDMjqBjZronHWr96L5QLuiM8rC28RLpNO27OlJPJoYEGNoiJ596VL3dobiyqrQWjFQT/aA99uuh8Z/+PDoHeFRlmlHYiuJQv//xZ95pJCOoPOHmikd5iFdVtHXSJd/dM2S/83ntDGyS3t+xX8MdxcqVyEeZ+Uk+kw2/7l6Wrapfv4I4Hb8IsV/coDLfWOTbzUiwqPOfeQ0Mfs+Ixd/+JetH+PPUOWmICr5YeDsosC5I3YYwRCFCMt6afuZOg1kOMf8xYHsBzkvjbmt/0q0gkrw6iPnS+Uhplw/bNr+ZV+GHaWzxSv6v//v/gPuDfwFDHJeAF2OgjBHG87Zdonk8nqH0ZikPImfd4mSiqlh+SfAgeuXJjrl9kDPzxdA3VZxtKgenG0DKHtuZxFNhvlydcOT+Ygn39Foq8P4xur9FR6M42Z3LgOTLkojpIrIXC6KAdmBCfslDljFq+MQIwlDwXuxsgLt7EtCzobRs6H5+Unh997+JG9hmNUT0OtDFIuuCFDEhCDzNzSlC5yEOrWbDF3cBxolDwAQfyywbAjg6t4iEb0cUMJFpuXxTS9gsgYi35mxMBjlujFSpTGBJDU16A8MSzK6H3EkSQiJSA143Nm5ARLpfNxkRje0KKsAFm1uUUnJAd+P6eRsBsRc9ncV5Ax0Gbd5iCJgAj8nHKbQRg4750sFIn+KV4iZqohcpSPUhar5g6/XCu3TN/V9+8+w92XZNMWh2DtgVBkQ5l/qXpIzP1cGu+E+LsmYj8aKj2GjhC9LTYGHGHOyaZwSI213X+3wSJ1JV3p9cMfVmj1hntLues2Q2lpoRAOQQZ2O2g23w5Qdwzzf+PbTune+EGl4ZifNrqyRdIUZW2/qZbgKY1GR6bxdOdr5swiaSbgYdMSb9yl+mFAvHsu763OvZeSTp0chVRqhOTE8ohYnp3L3f1cJvn3PyFuZgDtScLZ/nsk2u6Njx/I1XtNtZb8NVVAxdWRuTpui/s9K2fyEPI3ifQ8uJt9nHToabvslo8lYmJArrVJsGiCzJNJdLbCwuMExi8d2uOcEWoZpoxL/NCAElx9+K9C6i/NryN7HticNVINvgxN54FRpv1+elP7dtI/YjuHDkbPaaKnYlrlByqZmvS3426XDzJ4/imbPrYZ8ncAW3e+1I7yHS8IBZo5h1YXIj6lwSRqrW9NUm+eLXL2cxqW8a/hUyXZHtd38RiCOoMkUnGB2xEi/fLFrwrDqARc5ql3bHaSGwizXA8UTaEJjNSGA1SnmY3Oe5MYmbYjja74jn1IAYKxF4LuBPHxjGC0Qv7LWT5y74cE85TkxiMhUk3IFgS18gxdtF8AWcd9kBDml+1X9jK28k8psg06HEYvz4wV1vlyZ47xtRIIes28kJot6C5/t4ErRufXwxtyAN2JVILVm5kMwyJlilam4ascxe7Ja+ky73WMqveJVZ8A2U3zOKMeymbxvArXKTEK0N9ZvrSfyfHqqxIzprPypxyOHSN8B53WP2thebS5JJcvMfYdWYeWVJHfOizRjWpRgiu9/QeSV6fEg9evT89/8W9cP1gBcH+Pd/PsHpwpabrpQJDA9zhE/pX3Ws+lMzLL+N951+x5iZaszQfidCVtrDc+v156eWYkpMOifKsS3t7UN2m8Cg1qmHPLaP3EateBx2P4fF+vPg4KcJ6n3EfxWp9Iv3o1PUn8PnT2HwPKyqUj0V0eRccD6aehG+Q8PGYInStfzaV89/vLisnD2rtjaOiUh8R31ppb9YCgOJJWrEybRIqA6ydQioOCemNJ7/FGXoLyfqThAYVMAGTnhqm/IO24gAeh0AFmucE4ugFd7STN9+30NPxx2UmvAxfonyOJK+EtuwSzxmCrDgC+pp1V1F8KTknbZqNQa4bet0uisSQt955ZmdNGfDJXTT9HCk8/abSTf0M/6lC7TGOFvMdLtEZjiFXSwoMXTgO40rqRQAqIR5Gf65eD7zKsexn5kPQl/PoFCFmZoM0zFXKskslSDX6h547qSW5P6IrQo8GZjCtYCswQvLQ8y4koGUPUW1qycqu8PYkOsQ7EmSFyEpbZ+8hcFgTp3PvKUaQHiav/BJZnQq0K96HVWUjzo6Sk8gX+IZ/+YMvAE+qqbl4kv1TufkAJ/2hzgG8U4VtlmCKKpnVpAxuk6Cx3yDKwgDZbBLVjUnXYGTXoU06wFQprryr8ksJbjL05Clp+cOPcWfwS9xYHhaShaTq2DEZEmjyeM9S+Lvnn390kzDz63gIxQxwI75Fz4FpdMt4t2b5T32i01LKWoQJ5dKkxLWHm+vf3Vm8BViBdo/Tm+9JPcwmd+a58zK4dZxVfCkhXGJhu1fBAJS5+hj6bxD7mF37I9QBOHXhAqDwqyPbd7fEQkNQDZwwq1dW4rvdyc1XBIPqHEIoPfKFmKb8InKOZAebOr0jIpw2klfzUXD6YW/r4QM9sRikSW68QrnnZylKeIJW0KnHYws1WeQ/B3+6cQrUmmcAcZn0scIw+yZ0iEOaPiz1N96ckMcugRM0xxKUoVUlArzDP6T4tMeFe3XjpXhoQxMTx/9pNwYv8TpRijgi95RoqciohLPRbNon8EvUANpRpLRngP2oRo7ewkjSLaIAj5c5F1QNu0BRiwztgfJVYuG07w+7W1EzZrvbSMcmSieN5UuCT/2h+CWUzs398ALiaWPmEy+a3CI6nHghJvEcu2pItbZ/WvgwCmniyTmNaZEs2yX1Eml1+v25AIyAoVBDNeaRCrsDEyi9Hx2aLGAswPjTbiN2anngLRxddI1muFVuX43Z8x0t3MwtYe/Y1oTAmHVhRcyiALcdnyfdduxS3qtrbzmGb6Xf3T6iW/1tczGrMolfSODmLB9K+pQSTSmUpeK6tn9hznGL1JCecAXK6vE4YZmhDBIBwLP8HN9w4AkLEsFllcyl8GBJ7DmzvTFbPKa4kSWa3F0sd0AyAJq/GHiT2Anf1LEF9l34PlYooCiy3y17REEXpHjg5Vg3RYWkj4EjA4Mz61CET5zLZpnoIg0Uhpo6R2s4RsYsUDolsDY/ibWBheUSNofqdHkc1o6aWvUFqzLKa/wyiAkISZpEoCTvtyq2hyOzF9q2xM8bC3xPYoP47JDBg9WGy6YvnDjmsixicc7B/AWCkxNyInMCB0IPCnuonNmu//nf3DSxUbXzzOf7m4PWDXoNshYz2PjJ9keVYHz6X6SWH3wvv1+UFVtmO+MP79vMpYSX+rcipruuZl++zlCjFJmd7zNpl/qGXSZHZgBLfkNXka056FG6s5uONEGc/jvV/PMv37kmbE+/vw5P3qInuecT5vFnjeql8wKqFLCym1X4S0wZ6zpuIR7MYCvRn8LgTQobeE8S9FEAzT8OihJxpqgRIHnYJ9QWUH5BkLgJgKeA9DnqQZWZ0PPiw0cIlh+lmYECKrSkVZoEO8FNILKa2DpBSicFU6sN6CsOUIPqJeAV8L0KZ7+ikau4I2Lhr5InSMMJgKiIDdFkR68fDLkKx69sjrgR2b0wMBJ5NVgJLaHVQ2gqBgrc70wiDGnIkinQ5JJj6eLwjzCLytHA8ZNcWbElxXTEpOL1rwDCFWRRExRmYZsvUXMDMmrlSJyLLmAV8qAPI8/QJx6YjZZecxpIHxNEKeiv2iIUcebKcEb1HAw6SAQcOgH/p+qqJffd4ZxAkran6m5tP2CzzsY4imPZfapeid0/OfU6fmk9WAwwANOoTA/xX9L6KG5b2rG1IdiMNlS1F1swPQ5q3gWb+qFt1bRRAPtj0JUAgORPYSSxbrAF/gAL/em/kLCBD0Ee6Up7NCcO443SBWGEbGDbQznOaHzXwb2IuzfQ/a8i0vQ/koG5pDD60Y0foH4ZQbqFtgEegW6sf5dsmgQr4tpj/i2V6BABljJ6f0SjjDJKVrizPS34qcxqXPDoWS9AS/FVcsy/rY2VuW7ntR71YbugTIveKdUkoFmd9Ys1S5rKgWIPkQGZ/TgI26FvlcUYTjphDndqtxRdTl5HfUtB+Oa8oBmaR/7QuqvpxS+ZRbsTR3hVEEbIi1kSMlrRslNR3orkuCkKsv95oCY3pMe5qv4CBHcs4FvwNBKyRZPSeqM5/cheuiZdgTsGn8IuOO2mc4Xh1gnYKxxAphJr5xGs3Byb6zs17Q64LeIdjqUnRo3fNdY6N+M/OuODWXUSp3AjavR9LAhc2rh13eLQ772rJ/vTGfJoWMUYxT8TqqytbT5hSv0wvc54IZ6Y5BuGcDuXoYndQK1fehbwUbLrFr8XpZNajy/YibW3t6zPd+8dIpiY2YnHo51HdSwUhMD1ShoarsuX4p2HD5b8LjnC7dzl9f05rTem3qRUUqhAf7WGx6rLcgVV2PFcCOd352ZC6lRSK1AexATP2qV9nHfz/rf5EmGJyfeWvOq1s0j6ZM/f42F3SYHvfw++cLbEUrTad9+Ma/SyozlY7jYL5yfigRu7dTojAZH0PFZaDkZ55mwy94CJBHwvYxuoKgJGInV+4fqugxE7GWRGLuKUFLGaFtB3mUi6PbLUKzxfIP2lIiBa+wiVElSJ+o7wu7Y9wXXI6Aa+xRpnOIbyZwsQMALTvT8O75H23/HRU/v24ftT4pwckWJZof2A2jIWtpqX7ArwCls+701fGq2FAO3lE3CtLbCC/jSWGtNRphn812EeOVKWIHb7U1xLiaNnYb5BsK4/5eKo4kxetuAfK3Elz6e1ViCwOu8WdXlzF4jX24EKSOdEt1BNyLDAa7bQerq8b7yp3Y2zsz8kvIrHn8+n897xNID8velwRLe+L61T/EFj9gzbuqo28Z0fzYJwDHUw8/77ygBIMWA9J+TO4mhzXGdBh/3+27zNAB7hjCnIbzRq8xIao9RgIFJQdYRCxsfv5aUhmTXCO9/msKv8cPHPf6tPkO7pM+vJf5yzmGkSvCo9LqDSt9INlTFcDbFmLSIvwUBPUPVqlr2SGUo81HTmXDWcc74VqKhG3LeRXbkhcM0fkleegP8v/4//j88CmhnLLBaUg9STHx5pQjKo7/30o0n9F86SrYoFzyvCBXbXTgxYfQatK6esaDhELExUX9eI4B0/GRbfKLJsAH3TEkv9QDu7iqlihdEBtNF3Ns4ZfeMUlJCD/wicBq6dN5+S2dimYjxUE903yQcj+P380nZFhRzPOQRizUzuV/RxilCmI9hKXbG2u/uTe6Zk3+BIGcC/LS/gtcQTVzGPjmi94CBqBp/BNTJZN/Q2zWzehIC3HEwhMEjDKc9fsXH9GroQenwzvCZwJdRJvhj41QNXsbYu57pi/IEzrpnp07Znp7pyeQujMdNHqmYHZT2ixnWI/6M/y2Mzhm/qfeYhRTcZgBF5YVEedvdJnYk56buvXOE4gI07e1mLkdUivRzBuNljpAC+ibUyeIoHvsFpnQGn6uz150KvxhNUZ2WJtFG2o24fj0NGi7svNLgamlasg16KIpu5xOu58Iydlj3SqzuPC5xuGvDGF/ffdh2bBFSoCK9StkZQNxCTVYEmF+2/wI7oyIm6NhlybbVid9+0BQ3mwpR9Btaw3i1TcVnZmlWIH3ZiBopHNZMyrYcxznDdrTZ2ALaVXiHRFQLpNQDrKXv4GrJfec6rQqBZDbEeKqWIgfiVhsYmIh9qYIyguA0jCYfxF1kFTC6wKgHR4grlOyXRMntKVVn1smkOMlpGGEGRKAKbXdGW6Uv5Y2icWXZAHe63VgHNuZVSfIEC4Awsmd70x0F1SzJeyemtTsqkOARci2fP3nEOpxhwbT/o6qIaLaj1vcA8FU5XUUKBEyGCg9V5F1Fq1Jm/CWDAoKkTLPb158nmFVHnJl0OucNEVbn2J9lJjEiO7OWour/q6NYOdg6D6ygigDh3gSSWZ+4eiEPw/N8SEsYZhFE64K9z+Q4i5elmEl8aOIAvq1+xetWkyx/ZXGs2EVTiIvSBOLuKwixso2Ar2g0GdD1+kMkJcVtsIletITO8Q6/iIqGvhbY6Veh4O21sC4Dt2liKj1eKYp7De+wtU0QhIhCo9Mb8iD4W8CFFeTcggETXVBuZJvjGd3OzSQDJmbp/EO5s7fJ8AreZRBHMvB6FoEQNPRpTyqcTdc5opjjw2BG0K82VFnlWfvkam4ju1NkRclfM1eTQUAmgzVv525ABhILFQUPIrFtFRmWEithZA4U0/ifG4/5q7mFD7E9OdyCIE1AWc9hDVeYt+rK4C+ODmdthRRHtSAQCZ9fDvTSLilE5uvrj55v1eBVQm0hsUKebeVOHbEWiNEMb4UQcuzrheF7SVrJ8vd+lwvj2lcTya5SH6eiENYTwb6qoiWfOPCBVXz359N7SoqP9HaXgM/oidFLgaNT77xHz26LqHsBeZuAf+qPMUK1P/MOS57+Of9638///Hw6iqYqg/ZLV1ywMC792O9PAXVg/jCCli49nS6rITBF9qJWoMhCxSpOLp3xFPlGyS7/0aHNn0JHzf4+ikyaxpxTf87PqflvP+fnOc+BwYdOG8N5VFrZf2r/FViD4Oo9w/dtlhVz28zclBRR4+qwVmuS2BCjoRhRVLAXeyNDXl7k2tJP0UKEHIHRxT7P83RIjyyouANCxfn0QE/HH80uYjhVeLvXR9Mv0NeXewQK3R0j7ZkRh4O2TrwCEKOZtBGlJ5RhTk257nne4cgJzETnb7jOiD8zrzSumFmru2NzsjKMK+c2SDWLmPH82ytPkl2eEf4EviO/07JM1RZ7q6mQDBcrfpKcKxROXhxAq9xves/2sO2E6ajParPMOwXTEYKv4tzFQkl+P+8sfbry7v0skZ4O90yg300uoBPdOwKQzm1woUF5Y+88eqIJHtoa8kHnaS+moeK8zUznKlnF1RT91/QntjZVqWQg6MVKeJOYPefHGNENnzUTzcMCQcp1jt938JszvuflYPBhPU6PK6J0yhtMVAynuSS1RGLeMWidbaJPgVQFS+UcX6NtSrKASZ3geN8FqaLRKrGH/M3p0W6ag4GeyExzvKXASo7i23YmpthciXEyDRvGzIqRXujI7Ru9zaNtWXnId7m9hX1EGjq6KiLkeF4nmcoGX+u25J5srnS8882SiB+4gZ8MV9hsJJTDgggHNmQGY06bevrhUIspAd5+HnLauNkJKBzPgH8w16P9uhGo7uTL2Q6YtLVUjMGJlaDA2BFvv+l/Ad4U085w7/GgVtsGXA26cEHYaN0MJowB35ra22nhFHbx61WwSQLi2WJgzWXOFlch0/ECoN9188GMC67pAQo74hdJcYajwwFmyDcOTKgFXgZbd36p8/QZDkT5P5NAjDEKg8FtAPRqmuUQO5UuAMeRJQPOKK5gg+AzkaFkKlg4Hn9WHxXctz30bOpr8rB8fYrDOctzOWJugpaEUnyC+BAZpUbyaOG9oSiV8QaTrIqgYjv1J0x0yiT+4uoItGKePyveQEO7ga8iMC1MJC3U6l1YYEeTgG3qpfFCot+o0iQaR5tq/MQVdFF4J/7jCp/T0hAVIWj0LptkP9smDvR2iPLjIB6ScOyX+MG0KaGRZplIpbda8Kbhd7G8hvFySb82Qxp/KGHe7dijIzqFm/gDfCIB4fbsyvDaYuEKNKmbwK+EZ5PGzf7jcxDzItz3ajIzgw2c/HLgZTP9oJP0FABwst4GcTQ3VoLg+x0dvRBuzomr2J5VWHbOQgd+2cU75svoeDhafRB5exUNT9wRi1yG0Ipm2T+w4bNc6Ld9Jh0+/GSINdjJjSRFQOjU3rTR3JS3VQUoEx4DHSWVGqeNpxLZHIqITVaUgc7+U2E1Qj/hB8JHbN2Ki9SHkwFXkYGlQcWzGNrm7VaElcFUqOsrCET+m65mK3E9/pKYvip57gb2iM/43ZYXrbI/YW2rhe1EKqZHEgREH4SZ2ZKm45gRZ9UFP7HWXVG8pmAoILqAJFVpSxygimQJKKvxksis03eGHIm/PT81bUxD+FDP5/NKmHT73QJPfj/thsUXI6q6/90x2+qpjJ0ex8edwDhmBSc+cpEP9LznaDLkcOY/gtERG+8gcwo4DZAzk/wHHjfNS5MV1WOSEaj8x58fSm6yPu9r4T/v/Dk+5ymXjbcHwv/3beiM3zoHBv3+iO9rnNpKexwB5R5lEwW5d8Apl3BKzT4rbMOCD7wI5c14ExtSDUwK5UAzxobDoJOe9nUVsBcMIs9MF/90a9YuRdZ2PVYRnDjDJIcL65Z/MPAIv4OjoFmJvCVxsXePlVwRwDNs4Rm3oo6lLv2XeyhcM4LoYgMpjKf403zFEl6dM/OeEvTD+ZS0uMyItX1M9MzCEbP93UVPIve8FtHSM9sB0CpuqzEqWtpUoWNj0i851BOsue2nKvNg6bYI8dTpcPZCKjux3m6iPtNFViU2VPweAtnOtGJAX2FEUcDbc+r0tJJriiCq6m0vJsMiEM8H4rTf7ePRuvp4451mhnyc3X2xy7ddVTOmiu46Z/yep96B3Osjhjn818cf4Wm/0o6NOeFxg3UMp7vOH88HTDMKq/S2/+j0ACXMizrSJbncwzAUln563oxTOBl9pMJwvnDGLic5uXJY1wHqNSIlvbHUbQxwxiZq8A5AyOwie0JKDnnkcR1gqpSxxkNH+yv8zHKyzQyspwq5noDpRJrUko1G0UHaEZumoyCqsHRyCm5vrvNG8IOlbrHqFG/WHj8Go2n2JSmhoILL7ZE3vwpdb4HoTKiOcDatin5BXrQv3LTHcb1ECpUUdObWGaIaa2oEFTGSortvN1lgU2f8xiSGSHYoeUBPFEaR+UKDpQrAFtQzJXbaPjwR1JYYmBN7lvgrh83RkxQuREEQ1G1PZPq8F/8LzM5oIe7ZfZ2ajm2yjM9Cq6HU7UfcrgLYwZFPqvRgpVumTIAVWEDpJLBIaCMXtl63mAwDXFDTU9I7GZ7WZMVxYgLrpTUqCbL6oow+EWoP92vdiCulY8sfS4BPba6liVJyBcophJMcYJYArpXfbxaHVW2gETFuughxjHcta/zSsqYkrf5x9gSBiUPfo2iFpVM4s5mXsdMKsjjUC+20IaKFMxz6pB8gIuld6IJnGOcTnYxTpGl2LZrKpu/z1KzlCOgZzPVTO9Z7vRtEmNutC2zIKgd3xGsEDO2zxtLq7aGaGO9UsS1xeqdod3Is+NBv9j2rdsDAFpwZaBMSmMRjzAO1cTaRL3osgaha1cnXg0F81s7uanrWPFAxdfbal208GLsCMUsNANKteEJPz5IsZgbNEAa301/UyGLYBhlpJVcDaSiO58RwS4hAx0F1wHh2jae4ost1Nwj7AA5Vy81jc5K8ZmOEE0nIX0JutrOEKmICi0aaYwB8DRKfdcFJjApTsV2T+3w8h9gJk5fBtfM++R3hEsD9Ol+m1zXVH2TiLAjOLhcznK/yXUsMMA2rr/A4ni6Me5tV+l5dyFRuArYeXhE+BfpfV4ZgcH+rgk28VwFVCzLdd4lYF4ZYSWES4Ax8yZoo6/Di0hkbTeFQcNjDbiteM8Jma4ej/+P5C+QTyd8tV1AV+iI9Gz7CLMox28+hWicAdo7A9igKzzHoP6feZgk0pw+djQB0v7NtZTaJY2XATvUYMz3BSKZQA/zn3//pTs2kSXM6HB39VZzi1E+6SHo+Dr4l9WsVxi0lhivcYOZhBjqdbQTRF7MGF5bwYEo1eudtaN73Ofp0jack+9CYF7b/g9bD6RdH5/AzeMjpbGpl3qQV55v4qQQ6GgAsed1QjTGpvrnfWjAbQiOUWOjsXXQA6GLaMHpzDKcbHyBm+386Lf9Bsv/X//1/gG782C8rpyanmxnxu1KAiWawzsAPrl4+cTYH+YKpYwtHfO3Fwdoh1sjUFclfJGbofdbmTAPWeQYRIznaYNRPbRvBBG1cnXQsJ74pDshizY0LhrK4YzNSkXY5lk4d0SRj8wDAKv0Zvza8+7GnLVSdsWsZ9ZCoVTuNPPWEh1VFvA5uw+7WntoYv4KQ+QZLZg8osP2CoCpJGyT1gJgQRshJEPiUU+SbJo/AxVqedSk9KP1J2WYWiQkidsoOBgz09qLxerwslgW6WOmHQzaScoTGjYewxBMkDUgnWruhnVwsD7aIxqY0dZ7MGhqPzinh0x0k5O1ery7t7HdCwOFCySk15y/KtenwHiT3wHq4BXfarz1NgnXg4DuTVk+zkvsgLYM2hmk0n9Rrs93Agxu1YUYqMN/35S1MShupbaTxSbWZPSxZ5mtgLWhSSRqDAFSBrrgeCwTGbSdQ52DJ3+Z3/2kDEAgnos5YZuVdeaEsU5lctjqGeMPN5rEEMlbcWVy5+vGOeA2bjOgQBmkWmn3fzgCuTIlrjCCktRD7OEP/e5+/twANSwRH/Ih9qgslJk0b94bCC8/OAhM7BjMBjWkhQAkavxdv20JxcyaJDpKz3HS6BHXmcu4BHq2qLwQdMMXkKb3TO0f3289e9A5ySfu9pl8z2rJ3mrR0vHWEHR8hVoWUwFW4GDziG8l9EOcg7tHV48CDOjI69jXpdXkBs86Qz+LYO3foh+50B4jxS7KElQV5ELNuf6c15wa+00lnLPjFCGad1IQ7hHETEO0NTyYNmnZKEbKSBOPSLr6qoPt+o4RigukyQCB3GCVWJwEUF+1MF9TEqylGA4YJz5TyZrF1TEJl6L6Unc4v/t2/S4OlSrbnLrdJV3+th8QA6NmZyHd93OKc2PjgK5cQud3k5h56Z2NlyLbEP0BFLkoQDcv1crjc7RgxGH+no4WLuC8EyJXALWq2Dxa5hMLGnbTTN4g1ggOICtYJbcU1I9K1lTG2JQamTvp078cn+Yur2gZ4iMykxTUjCbdxR9Qhqd0hMvtU4Btn/gTZjSnbLZv9kVMlP5/p4IGovUlfiVbkJeZaZ03uNg+DPtI4bHukTclLhzEeHQHWlgkL2TiMRqp6yEPrjYQSJuBT2hPLG5zIK3bFNQ1YjUc+c4JNJN6up3IA9e27+Ad8iwxe1apahuQguOEmZElE1oWae8wlNeMqgzLsh7oDgjYP3J9bYHj25EhMIMhiVuFiZxPX0NjPJJeyQZos5owwL+u8SVqCzhZURKHyKolQnrwpQfCe+v4xF17gEGNr0SudQCqG5v3MI2aunAjyNNLDqErDDjrPra+/X9GuOtDchp6szM2IsDXeJO2UjsdPJeloYK01pCGqZKN6AvYtSR4tqPR4rIq9BJmws6y06kxRgj8TAovyPOeQlvTnPKemVD8Hfx6qzp+ao1Oe+uGRVHVQYGtxEyyKv23jsFGl+HNwA9IKZ+6oSt1MNBxvEI5kwX2qQlzCLWroIxkoqogiUTj362yBnyMRqjGkxy74Ny98EhbXu4QgD20ed05TI80zai2WE3OtlfrPrKeJrjpEpQfVHg0HHk6aE7SrJVjcw2tZ7yJdnbkmaMikhJ0WpMowmOzzonYeVVSMCc13pgHggmdyPywQpnWOZhILej7hzsImACfMyeCBewcMxSVgRmSYdloAehrKqkxGmH1iQEc/xnDYAf6Wq52rmxvomXkh9jTi3Tg34CwSVq3tw0oayBLAmalTJzl6rE2Y3Cx5xRlAmObZia+Ri42gOTwD7EQvqjfwRcaU+WLp7uVznvEwEv+SB0F3wAfbdfPE+yXBYZCOJ/Z08k+Kn/6MlcmjhqVMgLep51SGpI1j04b7/7PogjkCaVkPYqRB32O/EDMqwyH3IxxPiSB63sN5t46NsH4rieDKuyzTyaidnNRaUjCHnTT0V6zAJzMLjAjpdOlzpnkrVr7NpNUxZCT05KoY5D3hPhg509udWIxr9J28o5gGyJubzdUpLWjEzT6SuDenKjjQ1sRAlouXEbG+z4cbJXYaCdLuZpPsWR3BcsMSqPGwUAvzJ/bI2PUWySGIcUWNz8TT2Exsrt3fXoM3CoVrVDyqTHW+wxMtpAYdW/9QIyC5iTHgyWJhQoivMnLVPjQFdHDfwNgsejTG7SO4Fa/9jmy0h8PY4dOmKiZLKSjBddyWbT+I1mvRgeCOSS55ciTijTnDAD3lr7MsMqPFKBbgO/kE1PokuWMYABk47vbfKC97Kkc+PullMPhOPEwQ6yEuUoqSnanr9u1EqbKNMwC1vhJxN5+c+zUYbss1NgovXqnt+UxPF9h23kLyp3jR4ipOolBN3zOC+JYQUjWLKxWx/M13bT6IOhojYfqEUAMBp5knE52J1UWfzDnztDLDZROcLI5oInFZaExaUpUb07SQKUPkHR5Xg4Yj5ADir4VYWvMB2i1i8CYHxM63W9dlb/tKUm3MHKIVd8tdb5rM6EV6ZijBKsxYrnUKT/65qF7mzxAYbNPIhPyPTgkL72Iz+EiAKnnhpeV3C9mHWGw8MzQ9bSZeJZ+O37dgQt8pmLjHqBGZVABKzvhw4bqLkHOE208HeCLTz8BLZp51UvT866xjr+QJ2r/Ny1ufUBTzfNKw4s6EL9XjAJqebRQPSBKui1xr2bUajz7HsBgOLW7x6bLKfhXdZ49CYIXwt/ziPsI88wqTklqfIdoWsUg8XF3b/FUIGVd5u9xIVkjc0om7Z1PRrVWomBxs+zKJFWwmbO4DAoCT1KVK19TRKw/ZjqLwN4LPNh+n5cPXHyy1FEYxSEtJ7WHdcnruAss+Cgi1VG+tnhvf0omeACUZbljwa6ORyVcaVeX2Y4WUsiTlwwux3ve38AO1/fTbOk6LlJ0OqLyWKxkiojRjYrgNnJ63JGxJgPHj21KFQZvF4VU7gSNo8KbFkzxpGdKkKTEN7DU2Op2rLp7hiDXQr/mH9WmEayr1qZ/P+3LSyPX+6zmI24kn27fClI5d0sUK/8LKUdf+PTrJcKTI/SfVko2vDHZsqTrmMkNSQd6DJ820qtIWNB6V2g4BeGCYVagcWll8qgfb/TzeM8AmCj9Tv92ImHjQ2akRNYGQnhMq9NDzppcDGO6EtYrugjq14x/bK5ugaorV1W5BUyD8Rol+JW8EuEKuSm0TBOSA9vwap3jbQKILjseCTYnzbhJxCknnsnNgnhBS7lsrW66wpGK/B7ryJxCYnqk6wBBVVd3v+TnTkw0QaeUpAeyBOacOKc/M0vo9cokSrXpfbDaVRgs7IkTQ4JQqPEPSy2QXtSeaklGBFA88HU7ck5EkJF3oYSTsnDcs5F9ykzx1ulvKRC2ux1wav7ArSGCxRjPw5tycepKZVr8ARyq7izXdMy0d14hHQNvnlOdNz2eAG8WCYV6T1KF3ptfmhGuT3bZdOuRe9bt1mj2rdA4YEFSaMhuz8hLVuw1lbvEEYzyzgsXJ9ey6PLvGwrLXSeQL/JLwv97ZyXARaDJ+JldZh1gsL4u0+P3qo4KXZ/1kiC1Wy5fDNfkZ5mb1m8ZoEbHN23GviqsPuiR6lNFhAmyTygDLPCcCMSyKLmLWbITVqRI8Lxg1rYqVGMFJYgljlIoD5QzYrkoAGLQz25umWpTV6BMlMGoU4VaOB24NO4i9HXtKmKQc4xhNEdwRKTCdOnU5kOGeXtyqL49nsHKkBeoIGpo0WVGYVjw50la9KgDKjHQ5qhKd2Gp4lPU/NnbsK5X2tqx/MDwYW4fjYdf28SJrkuuGicRJ9kwdYoJTpp/kJBEBMeyqhyCnGq/9EJ9IBFlAxTaOgsdT4Y1g7uDPzSnBE/egcHcrANi8CxXvP7BXc2ISMaKOyA037SkrVOlF0IGtJU7GiQC9Hk0Ekp0DGTAUzxywel6p6HY8+m8jwzXX9yqeA+EjvnJpMgY4PdpiuT5f1QYo8+Xyp11J2e88houEH3qGU0EZpmYHVnhhH8JKfzlQlj45/3ctCYTLZZulmt4KdesRgyXnD+fqc9s+xKDAtHAg8adJiME1GjsmcgmkdlxrN6vZ3BueDBcGyfK8BDoyLu2hQsDqdB4OYrQl29MOZo+N/6lGWdDw40VjCX8yCCLimDiQKG9U59rtJyULCJIQu7mojZnPsPZ6Nwds3AwRi29sBCMBvLEh9KoSDfbONCDoJmNs/25U1K7ASrfwrQylM52mBQ4tlRGx5JCHhK4yBreU2bDamwwvv4V4qiDkDx19SjRjYLXCYaB3zAJVVyVSYaVWUhuzNl1A5db2cMqDIpDsY+2m0teTegj3/A1qp51jsOVtNpcmzUWsHheRwZ0qYyeWfMsyJhNNYa64NdwwyO96EJCYnIxuYf3bA8l0/eTCYsW2F8R9eW50lAyh5dKewm2aG8CGr7OpzG1fU1IKCzsxsBf6YdlgSR77pxictQXOuPbsej1v6ZlpPRUbXDBNcKHTwsGJyFi6pALpcEuNMDSoCVAnDqaJ8rdmIEEPMt+nr/Xfqm1T8mXa1UzJJl8CA+/sF39ibzDo9At1E+b7sUrDXwtdJfin/rz9qmq2bcpwKV+uCuwYXkOuft9Yd6Qihsozu4eCpigQVrr9A2jjfcP6RjTtuFlyUZ+wUql0+XZLDABwUGU38Af+z7xhukhOp/l4QD7GL5aYf+kqxIUuUK4M1znZBz2RaVKw6um3GenveaQF4QG6Y8JEg8KP9eulbiweeyXdYo1ZcT9eT75UwMVpqjKhNimMtxMvHuRrJEKDrNgySD/bXkgQGvRMT1r1ZozQtYR+3J9QcPYQVfCb2hWdLsh6nuSd9pRJxca1Pu974m9WNaHIwtzD9MtMSsHP+N+rDfRw2x6V9T2G7Tp/ZhrppEYgrpGqp3UCD5xc9YrCjRj4ECB+Bh+yZ10QTwWjjSIrH3UObPhpvAKpY3Sd5/O+SuchMsqj7XR44eOM5Rv4MT53uEUzzQTQDAl2ONj6ge04XrOcvOr8zOdNkxvPA46nL6ndSeNAFw/oaAm9GUyy8Q65qAtqGAmFx/FhzP0xkgZ5KM2ggFY9AIxn5uUap4zqmfcNsmxmzMBaE80kx893PMAvsFNXwJPjOCK05lYCYABvErPcJK9zuC9VLAN0/NcJjAb/kDmtI9Pyx/Jx4rJD4sVSCKv8Cps/V5+a5uEEftJ2YxYlUjljdPdK0ookLCVdRrMObPDxRPRc9lAPu8PRQ7XaRRvUjIEmw8MerBVYvFOPgPGJltAkZQQWTzrIyYgSL9cRPgXrPcI0OmRGgYeW9I7K3pQ6jIcBdIj+xZy+Kg7JPcoIEqQ3rg25k20TGJ6zYLF98xWCyXEzk4hx5Mi4AsP2h9Rgz37bEmegePBB9+wAsE5Z0UUwCm0PdWaHWwfQXgE5VU6VmsR9aNQ0SiuQybxDT3EF5hkqH+oIhFFFTKaWIA9jSZ1Z4wIJa53OVSg7E00mdvVpSibRyTa1VE+awx191D6l1MOaNZYip1OyZaY3EYTL0JnuKu2LIeIXRmA4+VXD5AE+2/ZhbOcxuJ0VSl0b956zaKZ3hslk6SJjXzNfcpjBJhfBJ8ruS0+mCBnjuEXGCnRUBXgCHBBYNddzZ4OaVY5lcJZ1FEUpOnTgzx5Nmsx6N85OcEvSquOkSIkRq5Q5UYPDJItb6aX62vbmAODthkEeIA3kEXS9yLTvxIQgQxaZ0SiE2ywFzuRZRxGItOrBCkTStRFB/5l+s0o7bepAjMc4C/1C2lFfRsaXknfCfTprUjL5tvFAVG08H1nWturHdcr7eHUIZ/4DCOX562BeLtBA0El34qbOFCoQeYyOFzip1UKvybgRYzIuPk8UcJSBzmFn45qHssEV9RgsBFh0f/Ut0Kn4+tvr5zJZD6k2euNtKp8ZLChUl1zBNpdd4m0uX7E5etW34SlETZoFV6jpSyKTy79lJg3yR+xEqJ1JR18sCSxtIZlcPrWJZqBaJnvSljkVjk2IY9XjrQlzPbVnGXIeGeYsq5LrWBYjr3R5F852QazI6plls0GMDMVGen2sTjjlPFcPwfPiVB3Pmxmj0c4Ng4J6IBqqihoSKKOxanlNz6wzCC4VEyvG3Itj2wKIY/J4SRYCze9MhAWhelLmEZNRIdvJ5okHz4y74KYIf+bAQNcYOrSm/5Q8eEONfwk+qKep9LIY8DtvZRzXtfKDO/yRl4ROltjKd4vOBtMySTFIbEwU5orRMmsAdqg0GHH0xslNvv7cbZL+/cqwDID2R7VAoQ3Yme+CGpEzBX8AlOrtrqMtBFlA15Gzf/5qopebuoZUNn4BnAqb17P0fsdULFKeqss8gyQHKP6MP3VqPVKwIFn0rIj2fx2NvgBwYKocRFsLiM48HxOYWV9/bXuKhzPN44OrydqEwSV2RnAQPT7nGc/zHCT/NDw5jm/HIEC8MKHfqvUqMTA7XdasZw/hMTBH9Xk/VSeVIVSDec7ZBZ1ZystTr1gmrB34BtvLcWsPhLTwB2xC0ZzS6R7xrAgEPzNdEqPxGcQV02nIumhe2p7S5GB4huiO9SuWbI26s0uP31cnPqFDnumPKjYtus2tWQouBhu99lw7OQs9wJqlJlAue3znHAOw3VJpe9gKnhQvO4wkIZaWq91VtSkny9MBIjGE19E4wEfyqTSTq3IeNMkecCkXrDeYF5O++l0s1I9FmymsUHvNvrxNDjsdJps6EW/EY7y9tMDOAE9Gf7XR68UYbGD/M/4zCbtI5ZLfgg0tBT8m3bIn2qIAZ96HCvHMtOog0ZgnUynMYFpr1nXrmfFSEEhbbB0A5ZZty/d4TpHMHeS9DHcJJo7w5vNC8TNgZnK37k3fTPAcvCEQisIqq4TrdeNRiq5w/AtvV5mzLvBrwsyVY08e3Rm3VIjgh0dsDJP7ehkLCPZLnggL88s0eB4ApzvwVuQHU0Rb+CvRjGoc5lA/hZfrGox1QiNnPK9KkYVgvvnpQ+jcIisEf9ZF4E4kXhkH0Uz7AO9ReZtcZb3RwfVIkays0ao8Ex+HmYLnti2s5MAGYtSbtQFI29ZE1ywRNRFkgA7PTh4u6YoK0fXtKXQ4pAlmmY1sLCkGfsjF9maomRRGekJ8RZOdEzwLKAZJP8b7VA1AnJT0Ob8S+hfu6piPog4dvAIcGX7BE3VTUmiu+LESmckH6Kq108Nwkd4Am/mZVLz4mK5Ke13Qn0Uplap7Xes1acNisq5jvNbOg6ft2UG4A2gH7IjGQOJkNpZQ8JcNJLcVRvekSY73BsxebdYq+axDQUbZLQ55khZw0euCffRkSnR2+gomFwwu+Mq/uDRnTrCk8bsuvgFyPX4uts53IQw3JPBlVV1UIhlsgAYugRkee0vub2JZkZbsxkrW36QjLAsYvPkyEmMXp8fNwqOBETNbw5gIXZpcjyU4J2XNolcT8jNvZjCZC3lZ+i/kfLWKiYeEt9824DS4ISvYmnHrqC1tWIalY8cbQOl1ScNhDr/sEPMG+mWcMrRPg5crbkwJS6zGKB4IEbWSjj9Blh6wyg4bFSVsgGjixDdvInVmMoEgFgnbX516DhCGz5rlkOC/9jV7BpPH3ukjm7tv31dP0Bznsuh0TLFhkZ+eU8FujFAiJM13ay8GKFmrbe+TshVYIH77HlaQC4DTb6koG0Uy9Zw1X5tTQR/H9zR7I60c3X44L1iIWvsCMY2hPd2lxVd6TNTAv3Ede4fm85wwML2esNOeYqUblqDt2rZOfR9gctP9Q6MoT49uS6GVo2eALyM6WwGr27kdN1vvWhCl99ZlzEnJXpZJNcAgZf7OalUtV+43npNngMz+Dcwp1syb+XjYa/ICAPmr5OdF9Dy1mUpYZ2JWrXsevy15ug5SDITqfwG1X/EE9+75GBo2pTZKpWRwg9BsNPRUjB2C2aw72DDGkqHHFCSYmONSZaJeZSoiW1Xz2yobP5hfnJItnvYHqO5P/AWOKt7eHW6AijyAbSoRFj4Reu1e53KLQBc017rY0Wy83XR52szV7Gd41QLrhZ2OM8ycS7oaQxY1yAOn1lViDunI+0hEX7ViIQibioxx+SNQIfFID55iHASAIY80b5O0dOZtaYBDdN6ppHGbNfNi5zBeRXIUeKz8MAmpZkbLvK8bYvpt4vY2PUrvE7tUPTsBBkRK5FkIKJv+eJypKEsHmSAmT2HWgd6JTp7vHIQAyhxAirlqcFtx7OLXNZexDXj/0r4rwGQEZuJOcIOJFR1vtZmDJarR+Iyp0lwRp6l1nkA0+hfL/zaSL8iUIJsMzcEfeeniBP44zOaE3Akst5mCYAw95yIxgrMQoqF/J1edEn3bkBVEOgiPv/9J0BWyYQ2WZPC4vz2XESqSzJBI9myr5hZIC3hbVTs/FRw3nG8kq3rH2wL9XhkG93XtcJ2IT6BCPHz3qbiMF8wg6o7xBbauzSuOT8K6a4TJMV47yOiYKCiIsaBvfLiXkYEkNW+XBi7Cf+0NPJw430ftK2cKy/Q1poyyQgUCLLHROtpkQPaoiiXY7fSGJoPzK9Y73WtbkVOiwfDDwXS2IiTaw54Acy79dH+QhxsQebXHPWYy4tQsQSdVZ7rTGcdUcUMCjca6C6V+Uc8k7LXffSODDN8IVOm4Etythm1lz2EXTuUKNQfBaDZPW7lWxEhB9bRHi2k3dlTl0h2EbMar+q6NaN4e8SPueEcg/axFkahJFHam5yY0g3Ycuq6ghGKU+ihqssdhXLE6adRhz/o94lKVRIcxy6EqBVmgAXSJkymzvHwFBLNsFdtzT/03L26DivMKaRdmKlX8FlU0mzwTNjsueq709Iu+1nNh6nqkiujEtl+KAwHlDYF1u3gt1AScVgfYVQZcpGldiVpdqrGJLp3BWt0k9TYYcwTsqJDt1/X1MlkizreUcVNpfrx+NuQMxEyhPuOBM/poNUu+4V2rFCkacOzOBLjTr7ww1c4Ug7Ur8ZZUzOr64vZbZk1egSLlMleTs97lEUljbWmCM+Y4WCkUrw7J+brbBpM/BQ6+PV/JirHVPrBV2ko+Y3OxPBK5ERNaUjdqzcBcMfBUTtseZwgaMiZAm3MuOJvcLTdukG+qqOshpeU3DSJU4KwAOfWsF8Kg02WJCU2d7toA0l6LM1IQmUofeSzauA54XPW88wrHC2elbyhZzpSe8YAnFNWEKUOwFpDXw51ct2sU2aVD24hXWO2ArVSJxuhdPDHuCuhCAZjOkHagYlQ4xqGNGZTeca1CVxnx0TOfXcaRd6Xa5DuOq/cIVIfuG7jHR2s4kTra12Iy2oBoRmG2O2uw5YpCD0E9GpkhxxXTxvNvZowOn1K3K3wwKb3wDoWZCd86jY9A6KHmAN2giIMDZu59vw1whjOvCrEwirVO06gqed4Vk7AKKy/DF1PaaEqJclulmbd0YI7j/CP32D08fAdke5aLj31DVlkUQVrDztSWDROlELiCM9Bnmjhc7dFoB/Ne5TSsOgjXNlD9TH+YSW+s/rw2/XrwOU/hzWFlWIPROcDQNaPsZIkCe7ymrjB7JMbOv9un6u1fSsCbtqUZogl8dB73azuqRrNNn3i68GTGIrd5dBYhwuyBQO7z1M/4t+rEuSA+LHDZb+mwczQb6QqME7lySsVEI1EU3Z+osChQIeXZbdXT/SmVOP0S2vEGxnueH3UMu7Bw5TkCPBKfwS914n4gsDtNiekmSD+UopiFPqJC5ItmFVxiE3+6XzB2qgzOorTwMtbOePuzEkS3qmKJCAPW0LH/wATwfyPQSuDZqcyYBcJDBqXs5wsAWF9hSNF3sg4nIlFckGmvCCA4CuuzFEqMnuy1iaBJexTzitTfHsqEpzvntG0Mo6RMJxaNRVwZ9nXzBSOtLI2lN8lbk8RYMG4eMzseIl1fd7ZWrjC2275kzBuZXHjcbYXDlk7ByWwCAj9iao2D+YxDp7VUOzHdQO7h61biB2y6HbzPcKQVGEPBW7eUV+3YUdSMDwfidAC8TdPvoZGgzUHzggDTn8ylGbSq+DalGBgASf9mmuNY+4bwzQDr2V5yTpXGjnq8+K/xf4onRiuejuYt91u78lfdcjkhAM+COCXinbb9IuXIioYzftKl4xls0Vtgvw0O3njqeUKtRTLQk6m3Bv8ANkdKljyDmau4V0zbke//af8bFviZYQXKs4APmGRuzWFs9ExVrA7y1iZqqRCMeZJ5UzQhJfj6BRkKYntAsfvLuHBQzGpJkRy84OGewa9NuxLtyz3DWQj7JsAe+8muTCaUWhJ/y57lRL2CmV8gcz80/UZiOkh/AjBKN0WjIXor8DSt1giF1YYGXIqYk7ye26rZVgfah+/bLvgddMVPnZo1wQyuOpxqF9DjUbGnV2cTkJZr4QKCOKVOnjY+pIlVktvvbW0flTZ3pAAO5/Ckwgk/bWfKdMVbf9hYBfOeAwOZjJCb/EbwrDJoUVcALAX8EjRX9pJdvvlrspfUeAZK1UlpeEElEH7G7/V5Y/CIjAW4JWhdQ6dItNeOInyvuA0sgKVn/NYiVZXFd1nDQ3tNXa9OVJTG5OO0X27zfYWL0ERfsPQleQCIim9s0jHELintMVmVV3QBg4wF5yFoFPEaTNW8xgSXMkmhc9GcBTVAfLFn83I84PcJBNLFVhXrGbeBmUtbrd0iAEtLngSVKPhXKKPAD6pqn789MVeJa5vMQhZY+nmWWNPd+/FGDiaefXl2muG9fu4CSAG0hhQVDCz4S4E+RLM0g8HK37GrJ1Zud4CgH+Mlf0SRXl0Vb/6RQ+qSELm0tYoCBsFRLU05p6HbdTQGtCrHGrzgH+E1V6KZLn4CmqtRCMxuAOdFyz/0B4YQ9b8XLt38XS8H8H/9/MTQ5nXVsN2vAnxxhqUGpqSP++wY+OW2tlWKJvx6fQ/CaO7AQWssu+3J+BscgNWG7QJ3KijxRtdooKQoT+KdyIXhD0HUKUvgO/HnalEcGa8hnKZ5Cgjjqa+mc06mcH1POzouy8QA8w4Oz4RIggX1dIGetoVOiQrQp47dpfBFBEdHM2/xeaM9ZdN6jWQWUk1mFlQgd5RJlYdXlhDD8NSTL4BxsykdElhgjBmsGzrXuR6zMY/OjOsgg8lt1Dmez9GTvsh0Ir3un2JHKjwY+NRZU8kV+0KoKFpgnrje0iJ6UJX7PbR1bdawnV4Jr3O4zlpfSgUb89fbVKDnhcszZAQUsRVfg4Ydol4H7uIz7lTkgZ4rKkLkkGuy6rLSynzlOBWDsCtKLs6pmvUZZM4L6ci/lQkAcpyRx68WZR/PurOfkxnBKxW0i2vmsAV8WvJBw73gCFqH021XZGMLCDO66Q65EfLlInNldrHyiLDrcxT/PKx/iqRrOAFdD35HKJHRbxn3xMAHSYZkjAIe+MDvACUFId5aN/CqaSizYCLkFhXImfpaXbYgpCk4bcZYhneNyZLzMZoUf8/YqP/Cti9+s3KTHPH5wT2pU8fl/2/QjBkZTnO2TXhhoWuBfI08ycARQ6fZ68Dt2ebpwDxRqR75W9Csc6I5YFAHOHGj8S1OTNNHTNM+iO1nXUrrK/3oU5tlrCXLNzMgo24NbqsVr3Wp2mkDkNB7Peu1n+gVT3rvWEKpJ20SPmQXEWtnRB8EDlNu9yT5lIlTWLE2DOqsRoCzXv/5+y0nprf/qaDLNZm8s3lG0cYb5Rmwm1P0lC+/zvWZGbW7Sol1V6sXh6iVdEQBsborTsMBh0vV0WwIUHzpwwVFWFbm3Njov3gamrqmjRIuiwrSomdmpmD3lt8ExJsn2U+CHrKYww9/he9r00wC8SM0vlK3UtNn2EG3b99LfyfjXIw0KulZ+DFlCQhMCbM2wGYmJiwpEZhw4IArKWG0nvxY4rA5yaTTby3fQ46DKYwVT9UVpUAYcxW5TALmqzyRBQ1Hco9LHrgEeE7BjPhCy/WPM/kgXShk2IlIQahaMCD6Fa59irRwAHkjetXFWBwnpVGOZyWN1xBa2nJS7SO6W7Ht6Lq4laGxiUaYEpCW5vtGRbYRnBoIru1EO3BdsYUOoDxggANBszmcIxCOmlzAWtMamXqzd4r0ScY+c9a1HwYa8va2A+uBaCQ8Btm2O/0vwZvJfOm2XRFciI1MAI/L/SaVKxxKIYQLdV8OMTCzwSuaDtYbq9AovnehXrVM7nfgnXawJY5u+n7Zsi0FktCmlfxK9UgygNJyo9qa4Kpg/orntulanPYRa/Z+Uw+nGokOqKVMGO0bkJ1sJ+vbt4oZvuU8n61Yjb7UHgBPKFDs3KAk0t8W2/vkYY5cy2OAUU8hZtMISTBiY0+/xJ10sUOqjmYYqdrh8KthMpeJewQfpsDN1wyQDthR1UyGwwx5DMYLhzRdJFUR+C4DL6H9HiA8UvKywG0x/H3dJYEeHsOawx+7+cFbfB6d6cxHh4SGC3pnTp2mD0nxov9WhslmgQW7Zzyjel4utjZJ7Jc+Pbmixov3GpYgIEBR74zcSpQdiJJN4UB1ZWeRvomci00qU4BBjOOBLm5+VDGdDj3B+N9LaaVMKBID0D3LrZBHT4hnKy4cFV1OdC7kYrqxD5JOT6SrgWPnqQIxCQCbHGXNzQAKaeupOr7co5e4h2blSZ4oAVp6iuyBFMhxwOLES/FlPUHMJc38Wgeq9mvSUKFm+tFPLoHrgsu/pxkzoUanbExnccZEIqRdsNeUzPO266HGH6OiRp3+HJ2bXTBRWvqJGt98bR490y2ekSWYJ36WIHaSGJqQ5kX9YPsNeNuf5qSqc3Z/jq9SRHL+wDh67JGUBl3Ug/fdmVB64mAowvMZPKrT84ndQKF6uqoi54XvWEwSOOmEw3JktsHqJDlBs/66Y23qwPFI52aHAPdAJeCtAZ9MwwUAv4MKBWQWIyqcqdgizVc3S6jonLuvwcKBfGERggdt1LGHfIAdAhwkoliN14vOWtTcbpRL34X8CoVcyYrG29SlSvV+AtPQZwuGtdZBJHpz+34Ar4YkdpIRLisJTZQH21KWKoDRCUT/GTBmcdhcxVPU+AUUbXymmVz3A37nCQhIuz+xxpVk5r4kNmbsbNu1pVdWVIxu+UEmcoQyzjlQ28mXDWJ6Z7PeKTlUGquDqVexzK9/gNdUG2/xx5nTpLkakwhYQYJdzjEgYt6e56eq/UERYPG5Y/gGR3nh2TKgo7IjS0ajKZivkJJljZGmGX1VyvGeiLt79qVwRUpM85XjbUJUmotIEIfBMOzhUB5M5qFL9b4vWRhL5Xlf6yka48y739BBo6n1t7XJYu2sh4ZP+jEKH4LC238tU9Qzm8xhiGngoYCZ6CcWZdCgsNlbUnpRDfeH/FMy/CH1HEwbgCaqRUT8lwM3WQ8+UU6bUdCurFFeDeC4sl3IQFs6PINPUI+kWUAhngPYrmVkiA62ro/FzTWuLnDkA0HAOzGUw3obYJ3+kgpiMKaoiDcJUBEP/KMPAawkTWhXbqs2ecRKDLFXF1fmGv+aEkRwJrOWIvCIjRUZm8Kb420yUco9JGJEYIPM1CwRMkS8jFf1lwtFrWAp5qATFi/5GcY6Ys9nITDbuDZ0s0g2IC40xOLAPJR3upauSCUN6PYGbq9AP2Z/aCqrOpTKqyJweN/33IzbzjSy8XdknXfVbZnFpVlCr3Lv7J9dr9kBWCstF77CGHCuBXDg/kvH7rbcStvjmAK4gbI3rxJqG57iOxiXIOQh1PZN4UrD984WceGNgDaxJDeveJHLjKUqZLx4tlvrZjS9KXtASY7IQq+rPm3gOPhtovm2AzE4e7AfDnZMwZbIImJYnCcboMaXHqCXQKutynN9KU3SsrLncJ5inoCT3F3ahT687EBmWN3/uekOsGALghwhWZ+2CppCNZfn2et2uv9Z8XoNSskcsJtM+E7FKcprUxbEQPALDHQMDLO2BzqF7kyFH6n+eCxluFObDSh22wfHeA8BVMTD6YQdZvjkzc0Z0KfLfsRu2z/ND4gPP2c89oeYz+9/HbCSsMkzlCHVYXeD+pAHtZvV6EZRn3kz3ACEwsYPUdNTwJuTtgcDPsX3jR13rLCbqIkzWKMZFzXc6YqDkIJvn8xMRkfWJOCtQ6pWYi02nCGil+MzPdLAq1Qz3fG/F7aff2Uj9Hy+gCb8FQLP9fG7CzmiDsYt4wVgvDBJDbaFWDiUTp3uzzp4G9+RnPBqRJPWMK0UK+pQRLKgulurtopqP7YP3LZIQKrPtEgjj96eVyz7BW5W6m5XlXRCiHJ6sM1Wn3tb8kzcE7dmx28cIAbwtLHi95mWy7bdR/VpMBOB9KxP33hmQsvYv1hPhDBDLNX4zYBVwJAM93rf4Yg9TYXXC7+ibCOhxCqt943VouCPb8Ub4EzpfxVsR4DV1yKwdJBLyzhwT5Xel1TYGWWU2jMZ9xSC7PcmxRGjSNvJhNBuJN4MlN0/rpeSjcm055XZaFJYLHEYpjLqUm/xH/3QwIwoQkGeTsxAMtNx3lCPX9xfN2atvEyMXfe4uZ6hjrHDGKXTEy0ymOoJzuuOOEE8exjkVMX+C/OqVKwtW65jfl/Fzfc4TPntjbZX254JZfkW4suNFrldqgvHkpfrdaBSj2/bcW5ytYFp+gTOUY99PXbSUdoBn5Fxl9ursSvdAK3xKKnx1m2xkymvCN7ww/IR355rZTDb0ntbwks79YWB/kHcMRXAC2oL2KjqY8McEeoaFgMYOz1znolhVMeYZcBHP7/ToRzy7p7nZ/rzznLroPdRprUmwl07fnKpLb5lXSx0vAs1s7QyzmzleZTuCyXYckTJnO6j8pIyjUkKEsx1guIAmjdGQ0MI7p7D2MnNh0pjPKo08OEJHKQA1PMyxiLy9MrYXKDNHewV2DI4PnK35lsozFBX81wlvu+qgl+D9HjGHhov/VOHNTOvOUdHSoskpttKKiajbfPyZsnnZgmnpEpoDHagV1KN5bOK6o30Fo/JI72Tbv0ht5nebgJczKlXe8WPPcAHYHrE4GHFgjA4UsiDmtU+gQVmNlrojyiG54YYjLUtWGMzLtYRjSAGYaE+Ql71GBVQP9MtKChM4pvUc3a5cvWQLEopSgPFp6JbW2f4allTYDhNPIbpFAwrJslDDcMdrBWDWD1pCZD3AuEFVkVGn/EFbEeDtzD8auvHk4Ec5gTZH/RO7mOAgJRmuwATIuLMsA36GKYJ4lb2WhuS9bjiSk4zx3PxXGzPKAwr4z6CltySIOGY4fQYY+7ZcYiZaXBTBWY9JmEzmo403hjFjJW0c8AERknPro40Rp4M4hKXpfE3tVU0B7wXTcMZQ5Dy0drKxGtjdF9TKpwOdL/rbJZD2Z7v7BX0zp4LEJOhS+sBtUhvkuacDov1kMuXLlofqwZwdH9DqQBuo5uCtsR8kPCF3nNp6YcOlA+GSeBtsIaNnfERPmNbOwDDr9e3DPHS24fHSKnygjON3lswpSa1mZClXk4sIFd2cC46XYPJsp7BZwVlgH2kdD6f2IA69E6OUUGo1wFQeGdOe/vRo2/iQVQSxAxfdJSW0QWkL2IGSMYic+aQGWOct/POAHrfsesDnDM/QmdUVttnTB3o9zNFulaQ3UmX0Q1gOkPSK6DD/4+pd1eWZEmSxFTVPE717teAAgMWIkuBAoufAAUCJL55ZfqeDDMFoeZZvSI7MtN9b9U5mRHuZvocTM9R6pQwDWFkfFKG9XHV0Czy3dcs79NAGQgwcJX+ed+HBZr/y//4v6Wyn/GnR3pgsnDaHxvrrsE4+Zf6aldXpQQcz7tRsIFimd7lZXL3GVDIcCcIi07QML3fVmhzroaBnRdHBFnwO56eYEHHSKxNhgghhClTKBDgcYsr80hPYFvvRc4FEgPdBX2J92t2890f6Ho/UePPYbWnJKo8r+3pdHNEZ0AuXXXszFjGcNzUEKgEJ42yL90cE25Gst9YxMTgX68x3aPzZJFwTLGxrcQHK6Or0VXSldY5eQyk0bBk8CRBdkTMYknp6+5pqLTZDoyDNO9mItjIRqNLq+nw9pGhJOc3y5lmE7422nUSjWcNB4hB1ayI8H4wfbELdb+hqkpM/46/9D5zkzAAoFMzByX5t1YDhs1j23H2Mr575NWeNBjfVJDvykBq0IH/yXHUfEkMIIQzeANk5K7cmrdM+pnSro1qp/Ao83XgIaYt7fvpYScx4HK2O39g/YiJvooCLWKE4aZ/vLn+F4PZtyk7Qjaje+gHZ713Qdoe1opIRGq+JydsvAI3w2DfghuxGi4175cjo/NKh6zEFQd3A9tToRr2iv27kEX+kts+wrrptoqZAL41J2NrynwZi6HHHvIQV47B/ek3ez5z890hCQzeECqxNIWxBpIq1GaqoXs8VUU9Ajzd3QOcs9b8uYubHaq/85cBURKHgfTq++/clvdkN0fnPrB4PA3O23ieSvTTYKVAtrFW6b2JyFklMW0fUDmIPC14UHv9JiWXlH6IGb9jn9JewFXdry0P0R587gkXioDFWC/uDZp8KwRbT6LtKu2L6TScASsTI25vKmw/IYWxPvV6nscz72tRdZ6kszd6306eaP8Aw4cwlOljnahXN3EvE4qg8/OjwkhHmJl1biHzhQHsTFR/gdU8Fjw85oQACUjnkMU5bjBE9ViC9HheszOL6C/weXHyITf8MchO2kMr0ZgrUbwrY2QlGemMShMFMB1M9++fj8tVKplv4IrrYopaQo955X2VFZm68klFnLwfnLYAgbtsMstpsF0SvQwZUCx7Gi9sri1ytecXp1tMmFveYYriE1H7BZtBMmz23uB0ZIErhgyUTC1Wv2I8wocE8Gbh2TJsb15u5g1oN7ldDHK3JBbnZprlfMMVCS5e4p2jd5j2d6AIO7qxf9gnXtpjlNmvEht3H6ubyriedV1Dtrekb/cQ71noIrvnBuFF6HJ/no1x3DO6ZiYsEIC1h9khMj55JTY9GAM84js+hcGtXJw9nULvRhWzGA4WmdEGxWrHa9rehjJd6U5bJUc4dt2iiDQ0+wPYcHGETcsl+W4iXBQNmySTZQZz+e4g9EIlbRF7g2doLLg3IsLOk0rGgUPCxNk9EzxVdvrIV+oEMBQtVt9EY75FyuIfLvOY6DOALM07fXTMKVUgWK9ktCo656A2BHGu6NY5GZlTkpNryRBcHbc1OeYdGzvblpyq7yrgXypWxAIs8Dn816Ofggrojvb4eXRY4GgRo5QGKaNT2xp9+pdiD49kW8JRpUJhT2p+iCox1VVBTgVlp7CGXPYS9CP2+JQgFiIcYgbh6ILc3XkXDCSEZ3bYvi8bSLnfaH9bVZ83o1qsOZ+sgBjgZMHXJIKHr8Du0dXIkh/ip1dqxgj1mPSfHivMQOgIQgpHkZcV01CE6bKnNg+1I8x9M2GFk5rxAPCpEqZdTEaBvAnDZEFvXCshXgnymXmBeQ4B6IWnYUM1/WuynRhN94zSIvaM8uH0DFhPfs0gFPZm/9W7xhe5X8QQJ7qzqTTqeAb1tIeInsd1Fr7ZW3RG7MoS0y3VzSRu4qcjlKuafnlHth1U51UiGkqT+QzjKGaEtp+ire4XjOgTmFAffDMF86an63F/KCLHVSfD0cp7G08xZL+7cQ2sT/7MmTGo9KgmWAbyqkyzRkYG1/AY9R0Esb5AF8+7FvgKhpxbIZI0kd2pJuV+ONwb1PgsfDM0fWindIlSNcEZTzrp4ieixiMblabaRW49d/YzEldnEJ3CdmYDkk7WRYngAd+CJtE7Of/vJCXWTJd2EzUmWVGrKs0FZ8ZL4HXtTr4fMiBoVLJLSAt6AaqBmh1ko7EJNLTEmS2qZvayM87gdd7mAQoVbUKWMsXDgBI5NQby53tgIOE8XHCnA8WSYMXwMOBYYV2QJ+gEM2WGoNJhPKw6s1FmiDLRyFTATWfKVRQj49xgPT03rbUBbbyYHfRixvSvLVZF6v7pAQ7Y8zuS4NZ5gJ7Q5UFhMztKWuFai3qzh0U8ekPGuhPmtR03IBrqXnSU9ExDVeRnkGqOc2ownk6akiPzJTA9PXNOfn5bp36mDXrmdxmzwSdmnlCkY+DVSeZ64Mg/1Ec445ccAC9ekkSZjdyAiLMvMhIxEoJT9ve2fhr/5pJLDs04TgsqQEz3WJ559b5vw4/ZASoTDbUFFqF1hBmndi2ekL9EFn133QA30UE4Aus8n/0FI1NFNGMlb/G2qoVRCgjPLLSC4vi0r3Qqa3BG+MFLJkUSrKjpOn7fvNyRT2eNyIAY8ZSSJMEmqoPq3GQlhjlqTBt8xZw/eYrlcJz5W2sL1mGRpA5uqklcfOKBGomMWLnHXKlk/itEsFisBsgmq6OL4N12QkQ3iEn2Ii2nCBqP+RK7s1OxTGh5ve/XsAmV4K4AAxDphoPJQ7bzPw2hlpFPrznaq1YKYbZPZ77drJ0xA+b/yDWF+IRgRUwbMY+W/YiAuy+GYY/npU5arI0f4sPNIGIk+kINZLRYbazXICd4SIiF3iFmqM0AwevEEFbqmVF8O6S+MHcwCN6dbWWgxn1y4FECwqVqf8nmQjLDAMBHINDX7C/C0BG4Br+BRlXTI/Qt65AhsrVWr7WLZWPVXqy56nlDI/5uHkxGxZ7wYT8C5d3VOSRciq4iz+E3iCgDZK89A0jf3OXmvxSNxqbCV9DJ+plxIrAA+1F08xxBNtsfsHpY/IFeuV5bnMT8r/drLRO7iGy4mYesRkttLJdJEtv7G8A9rG2we0HJ7pVAesj4oy0eIHtmwCkMq4DWHO8OL7/rEjWhfNrh+zoV14e9owh/NP02iFfzHGowz9XVedAeiRKn8UN02TMz7uY/3TPUqSZ+wH+/86fa3aQrLUB7MMrTdGiUqfBbWlZ78IIoqj8+D94xeqDoRP2bTZJYGSjlyAMzacgmH3iMDxR7TSQ3Ee12xaVHwZZrQG7ueKc35JJSBxpVAJbpiCE5BXLK6Hi4AbbJs2gw1QRvb0xniQ9NuPhPjjoL/uRm4FZWDXo50x2Dk1aD14ss41T8ByJQiv58BEj/mvnN/To+MDr6WaqY6GyAFtjvqE7Mc+5GkqxhqQog2eNieUHuzI/Qlk4bBbjsGvajkIdd53waRa5z58TJnXGwYBdF/xgvuXUsILdlh2+BmvgcApZVGzjRn4jso0oMVHDTxDLkxAr8KJL4iTN7pVG4bCdNm3jcXbW3/WtYfIRBfavlKXW6QrOpX8IgiKBYJsXvm+ww6e0mCDzjFwzeRu8Ro5WBoLxJtzmFkba4zZ2yOpPtHmIfltIvOJi431b6BXunZ2TcjN9hScy9cALAr836IuoCJzAm70WRq2tjlD0bLVLmX31qAS4pFdlx1xF2Mm20OPhYRRmH/uxliU34jO+TQJy7s3Rc2FDStVEMJBixfa28eKVcuWirk3e6/QQrck1IizawfS4oJywoGgp41pIi0LmEVtBPzhUlnVyreWPFjYiLZZSJJMJ3fcoOY84hXiyiSrLsz9+nZS1rL7C88WKi4UJZ9MzbUPjpoBs9GIvCyVtZtRrTvPypoeyJVGDsSPuVBhr6B/3LRA85JUQrwyUgnVgPASSHnvUWPU4FrjTgvODZVoHEjgUZ3p++3C8IW+yH9fa8mB6V7ef5AxroT0/pJ1w48NaKFFCS0XI12ubMYIa0Rjv6+NAv0Lay64mY6dw3UvDIZMLIOMbc4mCTwhD4BKMG4pVCypWq0mYg+wNWIUEGNod6I+YZwyZ7qTmAwmFCF3C07NxyWSSjdgDHQQE37PLufdYEiXdGwAQUBaVbQQFwMK+EzlwWJe3bVKKdnwRuxt6hhH4LO5Xmkf4q74HID5bZW+owFoLOTbIIVkgnjGJt4j7pCR+r2vN2j0MiF35I8osN/4WdI44lvF8g2v69hGjg995DLUCISTmZimDclPe1gIyGIql+bLgiIoIiDRpuFhIYwNMDesQa5Dx87Nm0THIhtAlINrSGXZxZcCBi2EQLaNUU/kc8Zs4SI+4z2VZo0AUlv5rWnEIrIN9xJLJF5jXxMqRxTZMxWxCYUIva52GAQxIsotMxYS4Jbk5yvfJrAbZrcFEmM1L1EGgAIuDJwXEP0lxlWu3f8kX3Ww0os9dCX5E7TZTO2NySryUqeBmMVVFmu900Una/2LkT4RO9O9xGLYUbzYFJmlx4Fen+cxBv7xi8R/RjDzZB+9t7CImzgRCD8IdO+CmIsiOFby6ZFGKnU3mRBz0LNve3agJ7h5gzzXv5Mzku6FMIrCE9Tu6+h9Bhqr5m/MtVwB4BRBzMQynuGETk6QqVPfElh5kRwBoM/UdIo1khfwteAMwwmYXeAFp1Gi9A8iByauXojgbjHZ6wO/Sxf9ugGywhWi2O3yuwxdsfDyT/6HGf5AX2kMcsovQC5fl0EEV6JifzKyZ0sI1537bg9/OL1/MWivXr9Hnwh2MtNDPAT2C9sDwg3jm8Aa7pMIer8HZBLuh9++j5U/z0m8gsmCpFc4pTNcjT4Jnfp85kbc2BOYp48TWlU7oy7oLMbpAjHSQSJX3cXCgamE4Fr8Sx3T3vc3JcXWWZt3cilNfuGF74Y65XKwczgXFTJdDpFYr4QrqI8npZ4wEBImbIu0cnKP4rJLanP6eqc7XRQp4rCJtAXdK4aJ8/x653AE6pJF2d2WN/KIB43cjLWEco49Pun/MkuyNeH5p9k4knjhPK/dZTGKZSWdlBzH5fxtceSBR7EhI/AOl33OdUgNyxan0QBDaTXTvE+HnO3HvazItk7AsUfhi2P0a4ZmOEM5ifP2fm/aHesaTeGt2ZIUvPZYdDd6ZSU6w17OmCTnvUJrI812BC4rqKbe3lnmQARm0E4O0RMdABCLy2T7GNICpKxJgJeyyrSp3qARzJTs9yvMm+EplobILfdC/pGNwSmKpr2KK9mf+rAAjexi36GXhT6r56mag/BwDMvleLKuRATxxaIz7AVEpq5je6RSQf8IJVvtSzky61V+uQxesDtm+qcD51f+yEouTt2cDOiQ1iCCW9ZFsi07GdtqCrPspDEZ8Ajem5HKgjbJoS2ijtBO+93bw0880ooUCepBqsdMdm0gyq9qBYzeHZepJ+AeWJVIbQ9bQIeK1I5Kl4Sq2ZfWdXUwwU8JozZkJsd/EF7ToEnu6P6KpkTqt7yFGV3azglfe+GaSwO8NFYaDO2G2vVmDnJEZzV7OtLh9W0bco0EA9ZOdsmBR515NZrEOWk3+e5wV+pM/7YaX9G9e2eYKUkoc2+EpVKswLg/ywaLBCFJlAVx1bUEo5PN6MyewJu8FG5ZE5RBE1bYSMVog8Jbdfstq9yQy8KRECfObT0pW9De2X/EM82H23d7PUT9bvdh/p0oYLo+Us7pgviuQJdK9hoOXd/owV3uwYH58GJUo/O/qgSjb6hFhbEUL4ghuJDlIcufajiGc6kOCa7vPxeKcGUD9hbw0kZO/OK0EBpEVDMy1my5JHnhdBkxLuwyusQbaMmXz+XMDdJNOjQnsw7lxn+fGA7o3ySn5lD+aswgPe2ypc1mCbG8I9xyej7qZWx4jdSd6quij+7CZTBShmmFOcBIeHlB2C4exR6EYVTwyclm+0OcPQEvdECNjP5WWw145jc2IaWxFNTlrPI813FK3YPydtQoutmbTfXHGhjgmn72vCYfgGGKxIKjb0hRXvErb7AK7lHjGhBfOnAYhhOcw7RebP5+Ym5XnInN9csfk6TnT/i9u/F2xpAfRV78iHBR8QnF7lTZ4SnoyuYvUSFB1WgSvHye3Ly5ry7tv7I17Np787gDIKheIYwV37qQbberH7VSYlARNtDHeqnlWQrnf+6vIg8N3tDBysgBZKmVyR23oxUO61rPROnluuzzBrzvnPFZDB5BTPxGllHynor1jgO9kV8UE2JcP2aCSON8iOLI88gn57ku+343wEBO1aqShcnIa+2dgCEqFqMwVzgMd8oA2Elz188R7W2698ntL5OY8w6SJ1+oA2MTKzV0wC78evTdbb/en6fH7b+gdGPf/9oXLbb4Irpt+q48ogHiD1VR6aQVkqKMv0QNPjOSq4Dz08T4UGhiIl6E7q/zi9InQVLQjPYqWxgeVn7kXgO2P3mAIVFFP0OxgssbLdWCtswBqb2111wsIAeAdC1DkB8kzVgJ4ma6/xNGcFR42IQMdG+wMm3G2qDjxAjlqSmk6Fu6Kt5ypuow7sNZ702C4ZokYAp0ke4R2YJaAtNhkZ7vt+Ihw6dUjExEx48O5DxDGKTB6woW67Kr6UkqpbJKZfVW3sKCHVEHWYqsICayfg7pk6Hv+CPzOvte8oCPSbENlASKudC/iZhqPSJD+uNG5V9dt1zoxrMZD35cBbX4mJECviPs/7UtWep7hVLIbwJNSVWK1/+PpkbHmMGhaLZZPoGKShQaLNme089/AdNamcDdG0cJ1Ty3IMaI4EzAfkYI44qLDP4o97ZWsUYxQHVueDHhQhFNJOkMyrPdezNWQ74TrU3EApvsv54unexNNxNJ5olXo+3DZy1AmYQoXlVQxZq32cRYblKbHHQbMZy/3c5YTf4YyWrstNF5KP2jhrxsZBwnvsBsccRqajVPqBwLxRYAYMNe7YN/MR0qfWIPah89sesI1jFjCzOuAxrziTPgk4cfoY1zKSx9jz7cjzjvaMCISLn2JCS4eBhbpN6jUOInHY2UDjtm2oJOL0AqjLBiWrcea1ATYCVJueKDQ7IxsEaCoJu4lBwXv9kVmafJ71B0VR5QupBPGceIlY8pCnW8BMDymLYuQaObySCvrxBExPw2WwgDhjbbbxLqR8b0d5qaQZkP5Mn+fYdm3QDLG6ckGdDJw1DLyQi8Q8gxcpP9oVN5KIz/746SKdMc8Kaqq+mOgRoAfbgDvAG7Q2VX9v5l3DUWdUwaZ+PPkDP3VU0CQ8cY+d19jdjEvLGvMLYab3uGMBSIFKWIMMbwYSLQ0dJIA4fQLgCqw1N56F4NzujBMJY9viSz7O3u2HEJAC+8in1/ie5KUQiZVQDid/qo3HX2Ys7lvFazIddyAPeRgizgidxSTOlUFMv+QqiCgKwsbw7MzIxSuwRQ76Qzc5o9B6QrrkydIm0kQwNF9/q5vieIplVEKjp9NNEe2iIOfyGdvUxFIhFJQtrtFYulLETMq+BOphe1O8yHzriAYNoJ6r+Rz4AAMpOY0O5LPXdajT0B3klazkiiG1Yqd9niWiV8GyOdlIIRjn/iGzQp+1/L5wal+ianM28yVfJj3EkwOUTKjntgwBI33X4AXmEX+dWTcValbSm9T8j3M0ZP0IKJFnlyQOI1D/JoZxougJV4zoYbJhENs5ywXRD2s89sQeHihuYSm/Wa8JHJRXyB7xGb1DM7DezQGdBVL2zEP1JmbGopKLjEvXkfGMXmFE0JqlNubLY0G1T+4ycRFxhM+XHUQ8+3vBFh95IPewAKCcind3CXMjOGViE05+uBE7wa8JvIvYZu8P3bVNBZ+wQNyyqAbPdOSmnGkTVEPV0wci2iR8xkM2eRCxAQX0uhklGIfZw4c4wDft7zdPH7CaLzEBYAsrMNSJ8U5aTnrsuOpqbNZTMLw0nDWNDRlJRI1t4NMz43fwX//+mPJ8LNHvP1Nhnv7ZNRJFcHowb6ypnhLfCXrwKRwOpSkeTWeXShbDAdDvS/6Jr7NOsjNOp1mjCOrU6RnY6cElQZyesLj70O3whkXQYc680h+a66cTDqe3XRCzhsdq8+d5Oj6pecG61VRcDwmhoswZjXvQdMfeDf/An8CpM7+HZ2sJ1l32kkp1V+kB3iRBBTCqA7jjOIoSfY8nlQAk4OUeAp/5MDoF4kgq0mq0x5UyYJcnzw2JWFMnFwh1El6thKmCz4F5gHeM6YvSnyew6onlgE/7E1yh50OflV2tNxpPEe4Z1/npbtSGZVI8pe5cDRbFg7TNymhMXTAJnnGKMV65xq/OY+MoEkAc8Z/uP89PbzuHWafHz1M9XMoKEt+qH2/g+JS0N4K3M8czHBkdps9GqRqbHLYSWyUAwCtJ2OAdh5uGSU3t9I0eV8UEUrbPORu2His6u3TeafrYH0l/TyXEazp02U2eJK8RAl1ZV1Rj16Jvuf0APuM3AlnPgJV3A4gZC4QwXQFWZ804Xpwlc0lacxeEWZoSI5RXjY1oItc+TcrKmj33Hwaw2D60bxk2tQacWmZhYxrgxIbjoJAoDyeKe7OOQr0TViUM/vgmogDc5iNEGFImDv3O8s5ZzniW1IZunpHrEn3rhljtGHFxmk2xE3eeY1rR0dr1AFUeqxBrMkGIxVSuLKsDqsVJ1spwZmKPIRIGUrUriulVHgtjl0U+FHjgF9lxuejInlzBJflWdi9V/03SqHGiWSoMT8YWWOsRowwc2eb0u+n26deImCIfEK5WPfOYCmwaCLT5WPUH/uSHMRCbdiVgxa4rFoSLZGE0gjoyrXzTxD+hhqsSESY5b0mRhs4WpORnMdNGvEcgvE0XGTrDFmbwiExw3MjsdghXNIQD7AsFTx69AATwUBwtZ1WT2B+Y0mwYwbOJdgn0cxq9AbawUFc+nlDGkVWLuErvIjb3tuqZGZCJzR8kmeBn8NrzJkTBHE4aWcIwVm2+AviVKgz5EBkSZc0TQTRXd0tMgj0LlRbCmU+jiGQKrIxHRIb15E2DtodbRhnbww6noQ1ORDgkE2G0q1AlzGguVAFZmCJWFxL8hRWpSVa+1810lPprBhoSwlmsJZg4ennX71F1X+CxnxzLlDA8STEnmZDi3LfhJ8IEOuBykUKvdIIC6m5rkXKdGOjJXm9DhCuIl8+IUo3xxhgosE8qO2MEzQwDMUwi/pgvrxGbKseLs1iIt7wiOE6UPqT9piYWPgPItXQnnBEw6nlfFhNXPbLf150P1seVnxOrnY+zyaGsbZZwcxO/Gso9APKozkDcEuUQVCl1Ih/RqplZihGu0no82kaiul35CwyoM8zI5LXYgfSCq29YIcxcsdDqaeiJGzwrRM6jud9r0jR90br8MgQaQ2+EdyR6iIooC5O4bTx+r17X8r/G/5Q4rtLsersdijLeqlTSEE6xm8oNlZLxFCkKggN0cgVy/ZUAa0+kpN5nSM7u6cycVhzs+uxUsA2GogMPEiW387BWRje/mdVogz/o95zkmCtHHqrQb7xwMz2OkFLvO09JayLj/HYfYDhXzDmenzUtI07LGX7e/rTfQc/U0cD/4CkYw58+0Ly9aBQwD6oOTwlEf/oIn14xAqjXrtIJgmo/yr3mhzrUAd4ZVwlOj1o7P9a0WIm2zrgB0NOoGbzce902Z7LcR16aWseoJX4TCcxNaxIqOtipHGhXdHqkNxzWzL5E+9BGPpHgibCiD+2AhJKB40mRbDbIOqqBk/uXyKDtGb6nbcZJz5LEuNN/xGENF08OHed7lY80+BQf+J2pANIexq2w6HuOwpNcecfZfs4Pgm9NfPGfmVivZgKaI3C57N/nsPdQ+zOemYrCoRJYTqqarki2s2Hr/MBdenKaVFT+ezOOoH5fneLNfcR4n3Av9tMIyzKiiN/IZFJY9PHUn/+GmfP8y+8Ap+dDTmXbsIpx83rm31e8irEYXSK4cG2CukCwycwuoNkCuSMgDflYbzvZAp3PU8EcOIJmGJ69Fp84nZABRPAtkdOGZvDPLYwOwH2wrWEGi2x3cJnfAC40k4TC+gOv3X0hDSUx5dWtYmWKacLRehs0xj74SaSglEgJv20kNHGHlRsnoTp10mtr8DbPZyi/rPHAGEXBrCVAJ9GpcBBLyvTuWU4+dHHtnDvHa6vfkhYOJXlnrwk1AFsziJqVhIO5JrNWv2HVMxU3oPphGOw8gQCxWu1w+vYEqytdQAnx2SUdNa3e9siOGwi7RgJhORhVc4S2bCwHJCLS2DAHuaBKCjYcToDEZxaL2Fz5yJMAEK0TqErC0/1GO5GAOI/bOs8A5N79bFvncFyBMC/RxtS3WRPtJTFxceZruysOlaLSI6yNPKqVdIWbgtojgTrPRhFFyJXRZ6r94W1UD7EfHfW8XuZTbzj4AVKqRj9gb1DljOoETPTQPPlpvyUPXivuBJWXdMHI5G/AfHcGvTpxu4Te9bJ4e83zazf1AhHPCHjAf1bIQtph0mIXAPkQkbpFG91RoMJbI0PyCtAhPUSuxs4omYyr1DIt7kS+M9rvODaeXZJoOpydQWP4Vm2qd8Y9+kdgMyYEXMU7qI4qJf9cg0iQFKAVRsieIpwgkVDc6hfvuQoVg57DbT3L85PJUVKvftCwOfEype+AwGjSmhWIgQmYyHCITP1cSsvk+vBiKeVOPjEMjDlHu7UsMYw9/zNKY0/qAb7vuzvO/IsywjXo4DQrC4p2ha0SZgL3Z/K8py+u0l8E5p7H1FsMnxWxTWF/FG8XipHUEG7hOmENrDo3bhjCGTbo2hjW0/gQPyE22zZynsxt9mUWnt3bgdkQPEG/xftZ/N3xFmrJzwNCbGx4hGIvBX9kk8EXAmg1A3CHgghDc/kprHRTjDwdXtVqBhSNfRaKGJJuzMrd6aPT2YzBFb+7A04bQNo3mc6WiBnujM6QtjAiBEgBtAN1Ar4tN9yHfbeWVetmYQ7cxMVy99mefdi4uRF0fHr5aQiAFQWF82xzaAsQ/+ucMnByO2vSRZVIftVPz2sW5IPHnsErkWjxz2w4HkCoQD6YDVKBkwtHOOhrPF3G5rihgprkTUpgByrwE0HwJR+yQmyoCOLsUgmybogZPVNV4X0oPUPjT8+HVf12pEy2e5rkc87QxGm8ot6XqrQI8myogAbHdrcNfqC38dvsZsOCfo3krb/9IfFffiPliE0hs3a/oN7TP5EkHvg3KJsVp0N7BGHwcgwU50WfZdep7UqLhakJjKqwJoBe3JEsxQZIA6c083HQrBgAlRGiRqYtPZ5PdG1KrNF1kMSPn1zj5QLzM0RPkwtAsj9MJjlz5qznKxBF1NqXQHSKcdd4BTMCiYW/OP4Ai9FP51wFOPkuTDsCg2T6nB/wHaQh9Nhz6meRCrP3iUsULOAiKPmcn+4PsGVTJD3NQfI9lgR3kxCzqQax1dt9fk5gUYL2q1KiIC9mmoXpj/1mzzIMiPMRNfPu5wKMO06GICeqA3Jm6lQRnTManV4Ir4E/G7N1/nR/WEyVAAVMg3R/UON50ZrpN6bMaCgYjMyPCvD4FdyOCt95/XITEEP9sX937Uur74b2B4j6BGmhOHvPJANby8502bMwMDuJIWMvshwkXETigIQix3kSd53Avpgr3CR/Zl4Q2a9seDrxO1EBwnB7k7kW7uJN2dn0sSBDAvZvQfYg2S1BPNFm1SnYMx8VwBB+iyz1qlGV+s4O6CXAHPbW1QWau51E3ClCi9QExpybxpB3BXkbBqk3jyJBZcIz55BWuwsY7tics83OdQmwJp7sWTckaH7T8mBOVJW5YeIPRviM+zwCKXFRvFShvIO6IA9AcO65MYiEHap3pSKZj17cgW2r3xJBEBcrsHllhqfvrJQxQcDTM88BnUPAjXe5d9I7yfHUiKfnxQ68QnLxAYcuyd8/6YDNbRmxSWWWyoSRHzoylcuzZP+eTYWC0Uy37rVBxq27I8grPGyee9Ox0ocqr4IWdwkzv/DilAjojbicIfCyem9ejTclCguY51HZuzBT+vUDRnuxy+4KRZT2RgDATO2IdBFcwag41sugQLGpnw4x2JvKmJNRoOdjxt6ZfINKog6JCQt/UqqcSM0W61bFw27kkB9wGbw76mfg9gRC8h4LaZ5nrC5IH+UiyY0x8DFioFeehlBus7RJBDq4eufMiHlNehzsxPf8KWfaU4bgrKxZWAPcG4h+f8CIVDcNgN4KXi+Qu+kwSARY9pz9BHZDMBeQvYEqWfOj9csNE+FoIGpeKIYgtpfMtk+A7EGqzTYsbM9m5oiJsZ2L13AMFsuDUz9R+coxjaxpjvJMKfic93BQwgEGIKoItO7kmkM4EVo56KifDMM7T09tsmVYMbywbZs8DKdE+DP4Ue44aDxnP8So2ep+9fmDWpKhMieVZhgEOOUuCokxvHqTh5hsUkV4Re8kEmgzcJKRc5SCHOIvAyCz0bX/FVcVme4PLvgS5+fuCilRQxE9V/AXWP8Qq/9MUkvwbCdbhgNx1XfXGxD4Fqmm1zJ/UT7nqWBpZZuBJTPrVyReCekk4iC5qyS84Pi9dVZItePbRYh4O4AZ5ppjnZOYu9TXqsd5ogad/eanNHGlDgc8JNnnPN0vCedAQTCIeQ56Oiie3ZYwn/x89gBhO62tuPa4Ck6WrDCxNTYmqsYQHzZvWAKewsGKbBE98YFGgHurkCi8lD/T0Gm/+42qTmnIp0qycExOAZoWZaaHSFElSPQKId6ezzuvPeRMf15o7DKF9kx7RYucE8QKc+R/f+bwNzjKOQdZugbTqIRVXgR8MKcO5z0bcBfawteJn3OO5Be9PfZsHWeUUnK8JqJQ0Ca0cDBFdjzmEqHqvvj4dYJ4HI5MpzC9t8uaaxI2aNEqzdRMnyojAtq+oiwDUAHWNwtGteUs03Ggzw3rmmTrXpKzVGO3010S47+R5UInZHfiq1kH7fcnxzqOpwHXqsUqLT5VoLSewqq3J4GzyI0U4tuKUPmcMzNR8K4Swv45ceDEztgoclg67eZq/V4l3aPwNikmR0MBjT2ln2APRfbM81PJwcy4cCrHToyF8UI/UkdrDr+WT0kQ6/m8vcEGxpZIR8XjqeKBeka3HXYtYVoRDWzLhR+aZL+bKZYcV9tbXwesEwnL1ScWZtUssw/BkKQ1E7lcm6OcKl7fSQxk4wl/FSA0vMFiGENzqEMoMvEgOouiESys1IVcNeEsJzMwVbVFMLkCNnIBGapE2qyTUSzBIzlPbW/niV0l6mwR0t6/x3b7Y7zjQwb4SyNAot3y84FJMncwsOWWcjsreNImwaVSYvJteHp9WIJcM7Pv9+y6rQr6U1FyHD40mz1o4IgznEOTNex3xe3bdA07IW7AUKr0EKf71Qr704o5tdovUZHsCoI6u3bTR0NVahdpyvm6vZsceH/RKlrSmqpNXFZ9R8mFt3LtBALrfAdEdhQ826bAb/qkTamyXgTSp1qUS0GsCIX2QSBy5i9i8YzDULdYQWdrXW8VO6Lq0CafiS8dvwOfFB0mnaMsm4onLrNd9lPYnJmPW/QpGtLIkOfXiKhd0bGJJ+DGTPy4b4qAO7FmBmE66VW6W2MAw8ZmjYeC1L3GA/UZwvT9rpmYqEgkZy1FCemJyCXDSi7HJW0Kq7fqEMoMPR+runs2cWjPmI0ptJM4rlwUScZKaY4wfpcHQQlCgRvDtfyO9ki5EyOjA1chGol1fZOTH316jOOd1nJWw+ykwbHCDHIQPsOg00tIYBC78mpNGkOFD9rbU5T7mgkQnEgn+lNwyyUS3Mj1vevi0rky1i0UA9atf85X9o3KuN+B4BeXkS+cgU9pZvsgJcrreYu4aWcTZAWXMFNUeifRdt2HPekXyw/n31Pyr6W8UoeGeYiOQ88Eizj2u6icIwJKdKbhAY9qm4OADSYDnDdteIhmTZok9z03qBEcerNvI1We82XUUMAUL3iiwrjWfvSOIw8akj2ju2attZfgWke40Yc5xtWekzNNBDDEOytuZH4krANakQUUZAedsI30ud0Ws/wv3safsdGZp0CsNTG6OyZ7qDLYYx/aRSKGi40QKKp9OGONme0xYiNeLq5SuRmkZRWJPk4GjjdvwdNcxqyEVNojDaffWNgMv74wzK5h+6N8/zpPzg1IHFIzPmqwvmdKKQpqE02zMSdOiyTx2eB7D1skTc/sImQePb+pK2VC2ISRFVuJRJTP4LXCGTH7RS1UFXCiZMJlvJHM2VbNA6yAaDFqxqs2eWWUGUcT76HbUbX7n9VWgib/1PManJOSqEQhbRHqINcuIVm5Bv2eI732532nR+ZLBYr4fCYW38+8w3K66NIRyak7E7Q7ynpzktqTt7r7l+RRATSmgTitEwUu6H3fYp8IHmaGMsvb96aoNY5TJCnCljQ9OSdgpxUHqZSRkcYZZLHqCrrm5lWYXE1wIJX878fzXl7TUr1vk6aUHhpjMLOafEJVAGYe+zfnFtw0ZCZdMJd02JleyGG+cAKQaWO1uNnuEFGXlPU9uNbM8kbdGPLEKcTeHei+hip5+s/58/EnNbnJN7yTS5DnwIQHTp3iCfxPDiv51qngVuhmt+ucAcYtJgj0Z/ybS8x4RSWVxuCWv5A6PzbAmoSCSu4u1ft+4hcDBI2nE1c6ePP0g4B/2lMxjPND6LCGI29YDh0P+NAPMs5CcNepfsdr3tUEYgneGeUGVYB8Br/gmBVUWfd08t32cgl7S+OtWXT4zrzNL5gbULOOPeDZEtC0rLEaHTU3UGantRSrnuho34Uz/F2XM0YoXXRXWNyQOobhmmvXmlnZAyJ+dRJRXHXe/oTz9b4bmJHEqvN27wZqOAVJYxLdnywywgF+zN8cvmRGt7wCHVTlO6DkLQi/sXAi/HUIeBN0ayZhHUNXwAmCzvsVdFxE0trd7qw/kbOHSHiS/5j7mXhO/q4l/AGYJ8KSZwWWXhfNTcMIekraR7UyVRjT+RG9XxlkHFTfUSDGnxU43Cfk/p0T4/43W2IDABeLRKJSdlPDE/a/u02rkus/JNGkuEHGE+nOn/ZvaDbMkFWYQSWtfnO2a2XpSCAOD6ap8vualbldAZUSfMOv1KIDaIpn5iO+9+5kfvaOR4rP4FWtuo94Z6DKeO0R5Nlu3+XZFu0e53wN26MvrhqJHSnzG+4ZWhzgJNNZafXm5ZCCJxiSHFzeEdse6/WYmz/EZBk5/WByQRPHiDRu6YBvIHgp2mfv2Bx0HWY9iDw91WW4180k0nnzbZBPLnDxbsRBYd/Etg4tnr+vAgeZb1VefY65aQhDCd2WYrkyDUZhno9nrgTqGX8SY02i0rqAyDWXcKkq20HFGLvwWKruN8E+NgL1716V+uQtx3yGH3tNAMRGX9z3lyRiBghaGvdD0IklyzNTz2Zbz7TqOJm/07up2VJNd5jmfM0O5bYAXGpNQfwMPjlpBjECZYbL3xTYuDJtJpNJvLY59DIlRoyIoNyNusgTX+m0maKGnjSErpLfS8Ix72JCia7EZWxDk8kcYSLk9YPw3ILzfTRCemcLFyROW4FvOSb/iK998qNmmyrpdl/OpVR9UzXb+wiKqu+XybwOWQ5GWybAKT7ATCSjaa1GF8t+ifXSiMbWhtDAXt1glHhRXYKZAy5yHtUumVxYOPtF78Ox/RItMt9EiWae3BNSiyurP+EBMo8kaDpAsFfNHwglV00C8SOGDOmgXTzzlS7asnAFNsZhZRyRA5Ca3je3isTjxN5wQIQjo2V8wLB/YY2PmMcwsEjw+1oMpml9zKPE8PhfwMvcurQ5iLUvqbsO6nCNVkgNiRyagPFyRGBTJr3LCeJKco42w4OSN23esDaAeQZwoyRW71+RFROVSZVmv4GEfp5iOqTefkqjcaM/Xfa/nmL9/PP+zi86MaDH/0CfN9rOd3EtiBxtcy6SWgDnXSebRfV0Oz4jxggkZZLnNNz19r8TWlfib5vEofm//h//jx5/3nPCZXvCC/k6VPKbpyIkOgKPM8cwskqg5Mba6OLXyXuOiZUn13hqMZcTV1JZpCwJ0GMPfAvvhtuH2uOBvo1aGFGNzh2EKWFeb69WCkgSG+JVkobyi7d/luLaKGUwKrfZtXsa8Sv08Khi7dCKBNBei5CN9bcB5sIHGjReMm9ahk6LGlvetTlFXxJ6htrAH0pZjcbN0FPejj6mO0tlOm2v04KbFWX8ZUrQoIr1vlsr1p6qZ5ERrRs9QPJgONTZN7cHUoEoeYImT/M6twTaqcwFiHlHKgndXfVjfGyzzsotEww24zT7Ogg/VQfoHC9zwfU9qkMND2ftlXPFJZSW66bXHhvHh4GQ0kR1oig3JYRArybJOefnqwLWGsW+YoNYnThNoIPcRLVvM8wfjN6MGn87BIrH8yaiGDroDmzrvZ0QML4KRNEz9q1roafNOAAwMwPLKyfOLBK9QLSHGUoIROzF1dKHo+AgZnJ6QI/3ir82okkyv9zLURgGHrJnt+L8qR1JRp7n3SKcEc9Oe3RHA+CcjGFv4YYgVE8rVvJ4YDnjiyDl7LSyPPfbc3NaMHeMQP7Z2MVyr6v9CcgaYDjQspfcCYU1G2JKwIJ7xRH8mhi9QGtILxCBwv1mhpwV1mXiaaDAiCmjMAzrX5mAenDdbtEybPxTvHoiE/aWszHBnzcNyG2rYDzAm3iWJz3iqJmX/BcM+INUolAxK67iwgGhC55Un4RcIlNuY2+AkS8PN8Z+KkOvNA+YDc1aZJlxy+QaWZQ3OhwPR/F/3ASLfXnWfZGgaN4hgNfeiI19C0AV1VAuKUTQ8SoxNdompNpkpEpcB/ZM9dIHi8vim4szvv9YcCceYIDOzqs8O6sy5Z4PIpGU27xyQDIQkiiKiEXT+rcWtywZhhd/TQvfrrfIUBM8csdX10znSzDItUX5AqNCVmxkacr2iT0iaGkAepQdKLqYb2ZCPoBVAa6hei4GG03OhNzI40lCPrfNNEfFfr/pdWK2I8tYSdiNbc/QFngbM1f/mD0EHIhOwu6m7WToS5fP6gMpojvgN4ZfYb3HEXWHqF20Iu9LLGAaz4ZThDbJtQyWsqMdhvDAUBLQOHQXEerASEyPysMIXA2s/C771lwuNWpGrUOH517TTPx83q/Nb0myTezjKHO40cwVYQuvAdkA48bDc8nMiUoH+wEmkfy+vwuMeg8NIE8OGTvWvo4iI/+xB2vUjqb/anuilkC8LsgPQ8tsb9apFnvAXGbRGark8CoRrA6RbXmH+kvm/YX6jdpzLvNS3tRoLa5tbOUyZkCJ/Lf3lN3tbeZax/jY/1RMl+6qH67hNw+3TSS8iAB8gGP/JmINjkUa72wLlr+5ASZ2c/b9otdCnhRfmMYHphhwAt+1rqqjkft6NLxh5SjJHEnc9rfi8PVLNFkHHFYv1TBSdHGTLTLf5U/VEX79yQd04Soc4JyfP48I/9OvjX6RuTXgjoqNhnkrwHP2JCqu93YzxHNEqcm90DkflkqPagoHdvctzSoX0QOIP/SRjucT6f7O8B4LW2YQyAZ2N4qGcqyWikoMS1smqtAhmzxEUeK8Ml/hAZKpbuG0O7f4dmbMbqT25Gtxg0e4+XHSplPOGgEMnqdsW1bjBerRYzeAQr1+4agJ8+cPo4qPSmHVmIxPYndyJEZmVHFMmAIqXbPtK+IDJCl1rVX19icI0C5L69trm1WHbKPy9PWigzkmZlfaHJKU7WUtVQxRiCnVbKxv57yWatoqe05aALkq2IFr8LLESiOqHtakkch5BSgAp9wuAwcX/JDKwrEaAptWa0NUxI5V+m3XwuJPMeK7uGAZrGsoaKPRInKIIdLrc89VxUSTDNMMFo9QNhO46vhmCWfkuy3Bjrx+1qCSPhE1WivL8N+DKgQ1oqVYgIQODbyj846ORRLzxlOYAZNpzwzKmT87tmOxGQR2UzwArwKdkmk0VXMqtZwZXwx/EmsbZhLjKXl8eLxN8qbL/JAxTitg/LY3xpG2eGYEMIks/84QeTXn0pUHnoX/89jTZiAXcw8NXHMk80Tl0B50aSNXbmTtMCNfjX0KE7D3r+LIvQVS3vEm532e1VnG0BqxHvSHWsvM/c2DhwXLD8CDe5MUVpGYUUDRjQTgbUNeVYEvEw8kfDaPZUbWTMW5cHkh9zWP6rhQ7U6ACvFn8Lt5HejQShkMLgIfDbATzLo39+wVQiizToR6bUuj87z9VnG+E7IO6Y9dqnmherp/MSfBHs/z09OyfOEGlu7zH/C5LpFE0FL1zaLOS3CRl/hb4okh9nHME7WYBX3hTnBPYAGgIpeYkSLay9ncjKRe5eFVycOwVEQSPHWx7ezA3kuMajes3Ur2RaU5yR1eX/oS7KH8eH82gKILaDK5RbAHSXDZ4Sx/bWFHkqgBM/VmhsgKfZ8AcPdhYrHkQZWcgOu11OZUBnk2D9SVOy/C5xxTM71O20XU87Mt8ZM1LSoQEauIDwXNxsTqdjBh71QBoXeuj/m+jEUtPe9+7fkRorbeDykqaYDobQDMyrezPTJfLlR9chbmCQpKaLZ91thzM9WErPSxtwVo32zv+5/s+He/q2WGaGVhkH7aW11s+7Yo5rkUb3E3Bme1tVswQrb5EzQP4arJBaJVtJlPjR2at6KFiWdhMfvlJgSX1CgGEGGoqhwgjFhl/Q2c3JswH6Y3PR7Ca4ihsyNS+B5eWrjnzWsF9qqEghhcCwqv98Iqry8Wuiz4XTyv8mXltNnqN58Ka/JeYADpVAhR5B1XM82TtycVNStcy06ah5UAteg7kMRbnHCFDgPNXt6XuAEBARpLGt4a3uBcy7kiT6BmIybs2Enz+zEDOq3Unw1hb4OkYUZLXAL1AO/ChlIW5njPwDbQE9t4JXr7KYAOEC4CqnX+kEDNNtaZnCSGZBxplzhUxJNAKHfgBg8byb/BoGSsuy0OxLXFheXgwbL1eBRdI4FCM4n7xrgOFAAjwbnV/U7wMgPWCxTndyLV5sDdgxXJoqou4c3Fb5O8vYv9uvG8LyU+fVJIQnD4PFNgdb9v0Lihk6L/mSB81ryeM3CpbsDRJyNZ9gBRxAN8mPIUbpcbs1iHiSSk6h6p7IZUmh45lJY1+L2iqzwdJjH0wx4HUxzIYFPzfhLlhv1/WbbBaavO+Ncg8Dt9rl7azY/0BJ94s65wTNdug4WFb0AExDChm1rl+EsNiqfRtdgpjVVMO4/V+qHeMIafnnN+3vdXpVnJ8JRqXoOG6uBfv/2bA+PCGjLf7bKxCZb8psUoomhcWI7lgeqZ/pUYXHA6Ci4MWueZN3nPyDumE3Iz4stJr2HgtNCnyWdYGyGSj/fM/C6BRHa/Ad3Gwyq9wSlEPRGklM/rF1uj9YIz3av8xr06NbTFmh7qmB9od1kmvsakkhfeQNjCipsuAp5iIq93dEkqJnPRw53he/MFYr27Zd0VJVisUjmgN5CL0jSkp+cXYoRsZhuwBbfOg2kjQSSrrSNBMmV2+xVjvIBCgMSoFaxTegq9Kea5rIct+vNOTRNn8C44uCookRy81PGYPDO/3LgMgAX3ziLU7hvJtsqCFAEGlhPDXcvHKYvJkd+6fvpJR9LOuKs5XyQRLtUMyMf+rMwO1f1ZBwXeUSXzz8tLBWMLWLRdBCNvph7G+3dw0Oiop5w3NEKa/KZBEHfl8+X54WB7GfmwsupMV4hKTqqEJd0xHcSeuUQ5kgJORjpPUzWXbwqo13hV1f2hMP4nNyU4dA1eldBeOMkKXhlN1aKbC+O9GXlX1gJSE9ri9XvOzzv/wLB71Sdjt38T1fshi8bHcLoLQrsuho28L5iNqyuvpueKEeiqVIdGQL8BJ3aBvfBGep7zHIP7GXoR81VmZ+X2QrDBwgLeKasca0BFhyN0C2zoYL8tI58AUgPXdyIYCuQEZJeKenpeJ0YFnBlBHVnCRiuG2Vpp8xK5SZLYzTD3eMFaqmBo0/gwIr4NTadjiltu5ICz6jY0wcZd4rEaEVIYij/G7832PZ7PMk14laM2/5djhdGKTVyJr3UxLfW+4jVgiRFRGJJn/Fs8Tj0w1lAHvKOnhlfYnasSyvILsxYR2MyEb5XIHQWo7BhRmxavH0Ur4updQDDcGVkYiGf8ig+ImN29xOnrKs7NIDKH1wpOAwd0wJekgu02bsPHoUYLg8+RBm96xNaDvvBBntIQNQC3QVR84DSkN3HoDaUZjiLEat74r4hBm4ZxvPbmjRNxPOSQ7jqqylA9ZAQ8CXmj8yziBRV1WibvVXfxGJ9l99K6xbA9cw1/uJSdv9qXO3mHfrl3b17dvPiXMi4lRziPZcoTBMhsorIeZw3OYhS2g+bVIzIpipO6iSnsARXyMqERPfNkcdyAohxWix5ZgWwWdC/jg1Q9eq2fNFFjFrbHl1w5JQRM5ULIk/rR3ZGltcxg39yshoO7FVe6LxROAwbGKm4oZDaYw9pokGGA4wJttb9Z2sLVbg2BBUG4wuEwQkb04+M7F6ywBSmUEkOJPMSH2f3YjYT0RMZCA4cFe6cpurxihumeqpn3hIqZBur1PKGApxsH6s/MB/YMVdfN3WCl3vjNRZ5fwCsJpSYJPsFXBdpNaZaRCWSy2FM831lWJijRwKXxpJvLcGFGdH9yfKJhqdvprgpYae3tvkraHojtpeBD5KmntaooQV04tguJeO4eTMyu9gmIvOmckt5LRk0ScLznCBdLMH9OQuox0zN4MU991WaoitLa9MSHlNcWGo5Li37YvqK1kU7C8TeYJ4GeJImDpB967COPJ2xjPED5pkmGXdokxlPT7aGKnn6emgFPrcAkA/O2Ib1PVY+Lt/pu+b1ecisv4h7uce8MCI48QxyPS0mi6SqmfMKgzKGrlrm99bIdijNMq4C2X0/VT1KSxq5siiMjXj4tl4RXPEua+yAp90XaNBsrnOgoB5JXQYyz9rDEQZDo2HbjKpFnlErbTNQLzWSte0NBGCdF4gTsF845Ba7zCDvZkHKhTKO1pyKtHmz5pjnTDSx3yk2t+SZgwRXAeEajr9Y2LoMT/wBg+nRcNgv+9DZ5sjzvYIjnFFjC2zoC0ZkuZgD2fLY6MRxe3E4i3GJE1FZkx0OUYkWnZqdZ1mVsQosvheVVTyWCIwD63p6+jgeJTlJqALcCRz2umHKgWYJPFxYaQq/nrAbhuc5aZrnZij0vikOW0Exv0o4CnGxvZNYlJ5Nedek+NNvzslB1Bqg24ZXbYeKHxXWZ3TEij4szFWqbmr8jVabUADZeLtEBpcc2OblDvOk9OyiX0vCQGyqrJ89Rp/KPWZtyuuguaGvfuwlnMiYmuC+Jb4DhUgIcL4E6ecMfPa8/oo4IPvO+4DyqxNZQ5fECiwtpj5OpuptgQNf9ZDLa0t8fFaRve4Cw+bDBuhB0nqtKWdeog6wtvNL7SSzmvulM+cfysvLyD0BFoqQ0Ju1+kMUzOqIwufF45BzIrlcFSXygD5/2h+J4QHcagjJe6kp+4aOv8CAGVHzlBFcH14aJNz1sJSZnIDn6vZJuX/3ku9tZKGXgROUZOQrS67dymAtRL5R+6EujpgZrZHV+WcWpNFe/f6k/plmZXsLKlQ/CMfZEzhRwAZuxlyrZzQc4kQLuXpaMmkD5s1XvFLE2TdKaDDQCXcsy7nazouzvK5/VPcdGVsr9PLH/8RblpkNNYs6TjMrJCISlN37H8IuAH7EphrzMTWzl0t6HcbW7i2ITXO1qFu6782ek3ZIgmPAJYs5tZImmeDd9uqyGS5/Vx2yMIyU2VBu8gc4Lg5RBdhHN+/YCBqsOqHEL9bppWymIYOpHQ/4t2psFAcg9nwsr/jrt4P/ebjcvy0aRG54zl0QKur0J5HnrtARpnDxpMrscE9eLPl3xBbE4Lwi7zmagIORCwwgZZY6puqZFN5ZIMVhGK7GqgDFhhbjJcd/gZCKLyyb/zN0cFqlZ2CNf7bLWqyQBawf5JJkyUzjAyW+mNC2ACbE5RgyC75LaSRPeH33uyxt1y7J8cRzuaaX5K+5f5st27aPW2sMK4WaGqDDdySzjazsf3tZbeo6CsAjkzLLQM5jE9IVMVcVXYfcp0Pj4I5wcSpZ++y0Z8JtYGmepQFuln19/AGDk6WU6YI8kYssEaWNiWNR5x4oodZivPXlo2ao37htlcabtkZmqeqIa3TB4ONi6lYkkYZePibkeKiCxLH4OuDrILxnIR2dcql/6jMswa2Vp6TQamNqV7852H+Ov7116qMHMUYEJXk64S0EpFi7r9cuf+sOJSCEsBGSBENF9U2gIeV7XQ5rdZumBGvY5P+NAp1NIT14xYl6PAvA6f0Hi22zw6OyxNUNuiTY15/xpv+kkc264Uwd8G9zlF7XM/QxCwfHtVyGrwaQIYwt2s9MjKiCgSz/wjKKEabHAc4eOgQ5xab15B/WUul8j93KCL7cvz4kXnD1u/+Y9g1UYd2qPndCGvcofO95WCO87dVTtf7zjdkDaFzoR0svyvuc2TCYCIjQxe15S1FEnxqbFA+K6bR3RfzJWQxUmIp92Is5oOtEWDNFXhaIalixwZha+yh0W4ojpHNhdXylxZxBxjmOqDyQ3VjzEi6whFFa4h9w2+qu0aQm12OQuN/mjZ6ag6YxXpBjngElBwPH83hOJpWCBxtlDU9Zw6eX4ETOGdfwGC4k5J0K7WYqOQQ62ZHMEjOvRk/I+Ic/XnBNIaQUMMFNo3Sb4AerByidWzZ1wZZFwj5ITw0WNFH5oVt+QPTAhsneMIMlNwfO8E/0rRZb4I0zrl3o0ZX86/2aKBtNDieB0k7AgeEtZd1qjF/aCrtM8228m02seuz8yHOtFeXUfrTrjTqwZckOESSeJ+FyYc3H1fJD5mYsgIh+SQiiG3xikwwupjzm5jVQHfMGx39IhbPz5vP+FSUzmPMQ7XUNJn3dKpCQc+J/k3d2ByculLIW/zyYVTp3rDolmNiEgnMw93qm6N4aQ2Ecy8AD2lltpMYBs2VnPlPiMZS6X/rYcnwMBo5G+qk+uceSyrB9eRXwETByBbo/lZNJ83CdPWeogA26hgHaI2UBCoathR4CXY2C1EGFdCgtffmFDz26nDJSWhSjkeRBnbJkrV5i5Zds7OEzEFxLomTS4RZfTxgPgRcSx6SB0gsCvwxaw1lwSA0AqMzNn1xCxW06iF9LmHaYxG13OnwG3MFdTOpkysJJlw1FqjvNTss1gADSmEtnvS/PsfTdAZT4WCJNql5lKMPeWVu5uveKPAN/GWDn54hYtbOqWtStJ5aO7RpGARwDKaOlE4z6EkJl8B1hw1kbngl4iFV7RTN7v2hP1Rb7VndnzE6ZbmBVxRA6vMZmr09e9xfF1htiWcp5PzO2+OWDhh8ZTPNkarukZzAYXYw/ya5oY51PGu6h8LK6EdD7fQwsUNqyl0DYOBbSJCBN2o+PfETqzu+y7v3i9zsiSy4BAg05afyDhJNLuzrthxMjzsL/+BddjtcqXC6CSqKHB/bQTW4pwKfbGc+VrTVrgHrXARnTvYLFlNMPvnh5e1FuXFh4jkE9ekp0aM7+3SlTPhHI1D+AbJBFyL/VUXMTU4HYML2ohaPjmPWTqgBZJ9GJHOPeTJuixi3fNknNftBnyy2mtJNpNoM4TSQv3dO5l3BkmJt1d7AnN5X9/3gwef54Hg39mKjj+/UZVAZXr7RegcAAPr4+DMy44Feb7TtLrNzJWIjTeeEJBDt8eWdaMiTY7rV3AN1Uw7PA6HKZn5pQWDyO3/NzMryezuDDZZDvcz2tiV+1K7+O8KYBMObeYw95XEmmgwJlU0egkWQVUKlrp10s5YObTNubj5SG6RM9bovGrXiAwzuN4NtyvkzPkknhQkR3Sh6Uf8vd9BViBpV9EMGcaI52Fr0mD3hckTHSLGrjkGQhsdKkwAxyn/O69u9ekQC/6vJoIGmDGQYIGk5PIOx6F6a0ZJAQjI61YiUIHa/zuT9MDmuy/ZoS0qrlL9dpk6i6ow2J1e0dD0e6MVvgG1yUXvDR4YRgbyoRJx3OCcGWjZ0r1DhE9AetUtCUVYimXn90rsGKgVhiWztwC++mmEPY8kfHjVc2e0qy/PgcHAs5pYYD8PHPdRKuDAMYzpceJz+w8DgGPFvHkd8KLbAYrEAzehoza+8NArPxrQnWAOlAsCMWeJpJ6ghKazH9klsbz1IJ1dCJYT9WgW9EL+fcGQQFGLiCm/4nLAvmqNAMJE52BzFemD24kxZETOws7axj2mfgihAnRnvgmWSUW/Xqc0Fvg0xG1IQdrYk/GvgGdoJNqx520sg9dB23s22nFKSothu2/qpio5mYGS2qT0yN+Am3O+wseGuIoOJP7/kaJctLs6ymT0h4m/ubNAQvwkxt5eedicmJ041VKhLOK+d7RbILmApTeDX9IjzuX7uAulWn7CkLJPI0g3Z3+GDMzeS6CBfYm1xg2fAPGa4DzXw+7OTF7vvOKuygeHSTKBiqe4RAPZsBLl+X7yai/0xAi/tnZfd2E3hXhjrpbVQJu2Ay00xSST3gT4vPy7Aqn23mVC1s3tpIpixVprvLGmuJJK1LQj7m5BIZvUOM78bv1Dol/Tr3zqqT14lPmpzsIguHr8QPc5v5oEZPeDyTPWQQFiwKtzz5AatRproCvCXTO5p7dz9t0EaTfecEyLF75mHcwiHiIcV6a9whdmz0iiejNwiIvVxH7774kXv9uuMxy3r3NmgxbmEf4pk8uyFqESujOZNVcGU8gjj2T4SE8mHUw6tQeKG2tFCzayZyQvA90+O2/7/jf3dnLhSyQm7EUgnJjT0IHiOD8xvKy2fUPamCw46ytOqlRXIWNX1BjByBXWowXNGxiHFwpFt6MD7mQ0rRl1Da2rry3dOxGEtOYqwT7mKRrwTeLdeeFwxTMLw71Qd7c9V3vAD0zG3OON0MoBSGKAyP9SpHSQWDvbSj5fgE52+DPZsuFEAF4LzBx1UTOgbOveH545qUJyU95KdJbab+xEldgswQydKlrXJ0YpdPbd5WfcW9hLPcVxLZ2/cuDsV4UM8orrjvFBhPbvxajIAMR5+UvDdBAeJ8hkLu7k5UTlNPbRtaLKxBkQnOTVZDpNjHcCVvfPzDB4x38Fgi7lfCn2bYT5cW8Q3+EnBMYcAH1OLKXr4gJY19A7hUiYj/YPW04v5/3qceot1EKK0ES3xQ1AD0bnzCGZxo6B5/2vE051jJJRE/Qs2/abO5iNF1YbwnIl4Eqs5224kkEqUW6gF368tyHsOqNRPLJ13ED1GH0D4/XupZZnhT4v/2f/y/pRlYukTLfvZFzOS4Pix1eDdohXAao0l38PdZCFJEbD7DkbovocdHGEdzzG0iUOWxjGVlLB8ghvRqGe8RLhH8GceuvbjhPmT3Cax6tSHfa7wx1Kv6NXA4GPIUBFd5EV0oVi/AeAHv7533ZT9NzC0yXKw/tVsfJzvHERGKXV2xz8REklWUBBbJyr7VnYTckBykY1z64sZ70pNjPznSbrTWLXHjpnOC7jWZ/qWy7CsrFg6WE88dvpECCobMAMM6CYGAoQcbn/mMRpWAwImfJOucGJQAfrLo9UrL1LSkoWDBaw5ylEPakYn65xfW8vx2gzgsQBO6rCUZnrttjzjOeGVfVspFLCBrW2Kuq2LIQKw9hGa6JqUAeo5yvXzO/+4gBMaHm4ZbAOuNOqW3Gqp6uvSRod5piN3lLml55IEm7wQMD7IxmK7NFvqY3oo7Ih8DNAUojUk7479qDAEwEVTO92/juzbJ/9+EihcTUfAdC5f4msa/9UubGugeCuB7w3S7hGOgHvBKRPOThIlbl/61Kpe6PuaswrNXJgO4GZ6BYLdmZHTKKH+3uGNI2crmXODMNvldeXMAEBoNtB7Np2BvbRN7WvR1dwkw7x0gk8ssDJYn8zuzrB9+lwgsvn5w/k1BzbiCO0RlJVE+oVmysRKSFvYcUIGkdflzF3uCd983iLf70xL3zASrVXTNmPG/O97uNZqLtA7SWZtHukfmuQWN/gdA4MjaWgO0Z8IgNCrNJBdjnIeOtsJe6iETgcd+kHRZ5E8KjEZYwxkMMMJEU5h/vzGeIpp7ZV0I5RFKosh3F9lT9RP0BrpghNY/7PEdDz8iBikwRb0Zxrr9nUZAFWcncfpGxOmGpm4WC1b4PL4h2dVA7JHpPWuEY75Y+hS10ouL2M991OVHx9rD/Pv/7AQEr483nSZg5NlW8nEDN7gGGGfdxHlw6CHIklL0/f2jjgIYhip263xfI97satcg2sqEgMTR3S7LzY4OhdbIU+pCvUTtW0wb/Fjyl05vJUzt531epFmMv8J2yuRVOy0WJZyZd7DiVRp0NjtkzKQ/NPm8kwXLxrKNgWQgbLGvfY6A4HcsvH9jDz35uqItKZrOLXqj3PBQAhi/k3ktckionIP9GZO3zH3sEfoyP706UA0sTrD8kiwdFdkxBw5eAvX9+bgrnL8pSGXfHnUOFx3wXEAzeTl11IZNosqNvEnv4LqWpk7cv/yXnBF6EDeY7ZSau5Y5AJu+E+Zq95/OKX1qrxF8b8b6Wed8zHjgKHN+9JGd9wHiasXd73aq+MiAP+VAJJAtmqafKoPES6ESckBVnhcWaQ67x4NvxNo5PqQHOAMp6GW6upPv95jVE2CHvwr1Lf4bZ2q7ucHxj6mjb4vNvaPucd/L3isRMtPQTIijow4JiM9DV1+7GQLHeDi8i1jOerCGhGini3o3bk73ZUMd4c4AVy3wjoG6jxO2Z2CPfK5dPRQYvTXKb1kzpfuHZA5OyCOFItZ2VMyPTdJ/cjZguJrRzXHmGAghplwCmG+byJn6jB2bRM8/zZ7rJMD7VeDlDPUsDc4EZ20a/YFV1Qll4OGl4gV+XWPLgTDeAU4fEdKB3t3+hFn/aLZiq6ZdiUdTpN6TRSAdZk1G27MhRivNCEdYLw6vOnDGqDhYRgojEtxtTqCCYqknGdw66/kty1eAFT9Tq7SlAqk2KQ+Q6LBBm20m0eCehvKjdqKLDPpI76KyoZAdF4BzhqM5Mw6TibbBhTCP1pQSM5l0ukjbMqAYjuIdOAnTv/bCzaXKPbBbcTuF9eOu5WeDSC6CTRNP7J0xLkCcaOOlEw5lgk0AauUPSvW0S6BnLz4K/gcJSnIuaHTv4TieW4QurqeK6yVl/Zj6QOjQH3vQYxjhbzBo2ezuTr3F0YvOlRqzXIxg6nNfoBJV6KgQCsx8EImuDmPcr9sMRLvkJUehJxckQ6Jd8JsGUTE7SlDg+My+xJjIYIxRPtwmrnpl3nKpIOa3zEvzQDdHzUsemmQoLNUZMNNOE1iPgiR+oqUISATGCGi/Mdb2X0uQdXUQRxsRDJJzBLIadGJzEfseEXZggdIGxZ+4oWZ4XOckjQiS+oJRFoOBMCkIpfMpgyg5xBw5xEJVTLPhFe8km8xCv7sQXMVeyx8Wyk42ULf7iTztqJE6Xyr8USKuk/GwSgUbsCn2RjbqznKsUXc2uB0CElzOZSWAMc8nlBWY2CLAEFPt9+9cGXmWRiY+NNUd8Zw7PMCnyDoO0IKUFnYj2Q75L12axc7BnoutJQk4SHeaboL/8YgnWzOZAh55A8J37FgXrFvcyC4GGnUJiLF1NzEwzQjJPWD/JyN2UOST5Ajhwgf/sUrl356z8FwTKtyll4v3nf8z0DAutSymeRS1yBjFn4N9/J3gnrpIrxGEyo3xjqpFTBSIFd0K/zbN/RFROMxaEp+EYo+O2T0EWtCIydLCUIeW54oPM34H2p29cSJIvERXTjJXY3Owe1OyIFa//XoQ9DoYtVsYeU8QZfIhCRhltQIVWYz2ehs58OR6XV1khgoOpYiLCMn7wQs1EGZCarL8SURge7c9DxE3BVZmb6/QDJcpu0WLex11RKEgV2LzHwo/54bquMInq2gc18TiT2EjxDM1IQ91CfBNzHdAKHES/RNHL2JsH+IXvEyDB71/Ky7aHdSZd8XB0pxlusuOUPExicrRyC/+Ung4UsggdnZqL7zHTC1eTXC0aQJzxGz6XGEUUsd1HGS3b8T/hYP3K90RRAbNspmCnotIGl77fAIBdim1T8x/bsJeJjblkH/GaDAVQGrqyKl/jUeUClpkc9gXBjRvmFq4paOtjvrqm/wC4WWI9PgosHXAqwWB5CRIJbdAvprxDWp26S/aE5TnAi0A6YYgRI35wpYBAWsiFmUi788Co+6bsZsrPFjFbuRBs01MhgA00JHDhTn04VGoFE2PgQ7wbENAmn5n85S8Lg+SVR0m0+ClxguK+JOqZbqgI0Y/x7y3XCgpVeA3a1ItLVhsTt4+UL3uZ+NpHQisAiksEvh9F5D+DTBUaoOh2WpigvLkrW0MR7kkx2WAjd9ezpKqDKE6kwccpsmXY5Rx1I8mbCO9yAU/hFzNjeT4k3GP2qNZV6Y7sxVAbhk7l225SJSY3V4kTzivFFIhNlTg/4394xXGJzvI06iOAc8xWFDs4Y0FdhTYLLwhMSa/jMoI9r5dJzLAAT5GfjcTKrh4ofRcsK4kxZp0of7V8gyEd0+hchFxjN5zloueNITmShuU4vABU1LVmH0XqEuwHyWy86sHKQVZnC6GEPz2/RDjSE9KyAoPewOt9Gdt1XVbKBubw1z+YtRfG3rxro4eA8C/jd2JgwwlcnUYwdHSJMc/MJWaDiI4HG9Phn6O3/caXmhoa6eyAhcf+NawDgpjtKMlkL2BjuXqgfNw0W0pkePisYnUmIontKuZLy7s/XiFqJWMEcRij4SJJndkYoQghH/iTe8QkXRfvy6EcSHuhBRBb8QZT6l4chIL0yO/owquefUowOETSAdHiHt+PYv/QZY04GQKwVpDIu4nYrDEZaKIEnc9CVWNEwYumECOT1/QgsiHBzCtWyA4/d2e9Uk2ffUOzysNfxh2A+ADBr2BfbU0ckZhe8XV+tbxhaZXG3kcyrFIlrYVhveHDMxvjDePd4ZxrZcSdQ7w36LsHy991MgoWXqAqyzJWuDrtnRChYNZfnC+BCejJf0ftBBSUzZOHxOwdM8J98e7KODJnPhcQzONq6FNkxvWkDrpnZi6M1PvjjFk8OM4Y6QKZxvoIGbxzwBtMIvxe+q0Xes5jauN+yHZj591V+oIg2vEsIiBeco2zGxgB1Bdox47pcX1HCIG1iyvvQDKSCZLBJKI/FgrQTGklExmCCb3AbCYsEqycn9OnZB/jFQNuO+8pkGT1lYFgsUOtRiOAHvLhbL7FDlL7qTQYNqaMSaVRpputePIc7B0FrC5+IUwX3fwmQWRNWvkgChWUHiv6KGhf2EL3hmbmJwuVkbJCr0Taph/7s1YrEF8XKaInqYuXYkfw8sbZM5EtAl8ivwQiDCfyJWonU0I8pFHhFoZcVfyKArgcxUYnuVlAnru7GGI7kg4MqiHCZwiUJa1PfbmFCZIN24tJwhyxdoFTsEofsSFigE/CkXo1Pb6Gd9qVnydDbKSNebxrZc2uygdWjZjdMMbBa+R+HlwuOvnNxU9GaZI71hKwUaHUmkmRCBWdMRNFz8bYXt6BmklM/nR+4tAqJzLeeQZwjYpuid2gk+eHuRB9Y3HofDZDnLE3MIR50lbbny/otmcOYhHzPeGpQD+5k3hfhxxod0URAOrE7jDU5Aixb+ZDnt6lNPf+yprurb+NtjZcfpwDQHaMA5+snV4yCjKxtao5gigMdMavlEDefPSLYGK5wZx0+Q/SNEzE9lflBuF3XlL/RMlM0GcwvMq2wcT/49vgkYNiRUernsjih3t8xN/Vl62uTTv1axMcUubR5pqEYMl5y6t50xslVaOKa1VPc7YHKBOovQql4hmwOP+Qfwy1JyWW51qBSE4XOUV1sLppUr0UYd9PzZcsyBICoWPRTZRCZMyglNmTjIZY+ImNkgHCoHa0tEzr0uwdqxOqlkEqQOAlq+r0vCshOB/4dDcZAAW7j1nSj/nLzMLxQJ4/NoSPPXXOuI82Hz33F2n7pCwih5cO2WcaULKlmS8e+iSiMzHu/QF5VD8AjF/IT/28fpVXExueGiv9Pg0usSDBetvkKz7cxLQBmjzZCIt+vUyCCc+nFNG+HqB7wXHnZ1t/obb4B1Xy+xkFonFJXXLIxOB2gnf2roiyPuKmEgfxCryZuShdSyka9AAo4J35nKrJfXmJLpttlxPdx5BK58nvi+STkFFkxLAxxnR4o9gXNhSoB7+KxoRcLsR7h0RCSBMuVIxsYyQrjhI79n4kH0/vR1Jnu5hMA37pZGtpT7HkUPMY73+Uyg/q5H5KgI/pU093ixz2NIRpO3FUwXgC1k5bHOL4C7+D5Im5IABpgWbBeRm76sYgSBjr8I6fFEbF9KXlet5Ot8ySsdxMKIxPANrU8xK5UOjJFiQQfmU0ZLLyQabPgNejye2HxzLsXIUahHBHnh6XKKSlgfEKbl3sXNZzib8p04d79KpI1fSQLZYEd/6EJBtgL8mkA4fmN4050uR812qCss141+ssT/4L/dsZBbYUYI/m/BSbzxhbMOCAT5Klmuk7F86yNit8Ct24IMSdORLdxpwgdAWuNZjqrF0GcYwmzSQjESk34Ip9evfGxZWw8YJQCaCL03tbZA4co5QrRZWKZWMqr0zWJvdsoEB11DP6GUxJA45LboqiUEiuMsprpt9/t6BILi9KC0o1G7ESJQP3+cjnmhljXY1zCQEDtnIwEiim13PTTjPpFq7QNaqb7FegMIuKEyntyM7Ky4CpB0kFzZpxj3YHuzFL4DS5TbMnJWjTL8MnIzkBZjYfgrsGp0EE8J28AWoZJCSLErybeRBcAIwmNgqWtV9lh870pdpfEiOcUAobQJR8hh0gw+AcQMQ7cK0+e7A6I4WLGBYm5tBQX6ATtvIWnwCWoO236txsxulZlJcqTgM+UqPKY6huWFstSj6GxJOYr9mbc5igKVpE9/JWmKIsmGl6LsoKcyme7NP5VFRn0fHwqR7qcBGkLpVjKtiUTn0FKEqdFh8A6URhvFvQrdV2wvYuSh2RCJjQXiQtzEAKaPN2t07CqpIaGML3SVedOY/UrjSBFdGOMQgBeladQUq1kiVP5bzNmtvGZnAm4TCy2ByzJ8EYa4Nx2U2OqhAblY0crfz5HkZHGlehZ23RWu2Zox5ZEUhM1Usc3vcrP3CR3J5jAqZ795Bo0ENybpCxo2WLvzMfOa7vP4AutvA0JxQ8+VwZpHnlSov55ftyYdt18n4hLFie/62Ue7yTQSZz4RJDWOAUDtccxtoIYb73WA7leO3X9xsM6VKRkaQapMDGkne0NDPiGfdA99uJEhehOG2cAIv2xhSBxurmL7G5fsAAHVpuhYst7D+zADtz33JvEIozG8hQWPnrXjLxwIBZjhhJM2amT7HvXn+3oz1SRK4oC9WvxXe1iKZ3kLvUqBfGwPrWxLw1lN0lGJ+wssn0/EZvCBgrCZ8DA01AYhyYGQy8D/lusmByZjgyjJM7rNeYGYljZ4+KLSMLUp3SHqrTQKW/PXj4vUoozvuew49HdSAeFwn3AzbcZqUsLF1hUTYFnzrEdDT57pR04Kxti7RHx5Lcv5RfzKmav3okhTsjiyfKu6G0ZdsYa2p19p03TxlXfHp+g5sBwxLmc23XNmq6xeQzeHk9Tt6Vgtv5K2YlWqS2S4d72g56Xko9ht+qE70MlSiIM34dvymAHqsSNg8y5xENuDsWCHIDH0aghM8acPqGqeTn61flMnZfj9KH+PgjnYATjq0tiUqeFg5BoUIcT1ldG6X4jeJHkrBSBButyJvYikrAVkO0W/VCYv9CBA7x6cA1dgkDjF9whCfr7n2YX5U8v6nOvc1RnG4hvQGRSpd7AOuE7NuAZhbSGLcRTBiwJ/EyCtoxJjxdpZ6ueihVzu4gbv5Ij7nxTLHaSHI3uFXnwMKQ3lkMCSO7nWi5/14o9FvS5cBKdkre9i/fPtLNwRyXNkFKKxWTMwzQhAcPOKh0JiR8YKAKmHOjXxq4Dh8h4mCCTtxxmU7juhX5duwicYU5JVRZgcYpRcv2zXL6mDJQLbaTyqrorr0V3GFaco3Z0SysP/aCLiupNjc9M1pVPmCS4wuL7WzMrec7XEZhDhpKdVH860re84Kdi2Bxt4cwDMnVYHD3iT4yJzziANjZunSbJYAIEEzQpHsOiWirAJqvIjDBfh+AU1Zh2PXxsLY+4tgvaLpZB+ygdN64jY37ni7VUA/cGQ9IkydRjLHWCpWXEbjmDVU5j4eZYwo7IXl/O6aaYwIE4NBD9iCyzEpYDbKqQ4DCMg0EDFRyI4lnfKWT71YrXGhH87/wOfOrER68eShZxQW3R6e+bEKnwpecCVYNrcsnI0YBGZUAv8Q3azbX2GodePvYNwb2rt+xZewyA8JvRr8IgQiYLcoThfqb5z/6Z3rImUSz722tvZpjlKqbSrCLStQjTTEvPffzGnxr0dZRu7YK+V1LVb5n5kxGxEvfF3hv6xAhsWeqgh0mNIFyHkn3ZHPakcaTsbGQ9z3HRT7VoseJu1YAfggzcsXliyJPGvYCCdxCurQTdHhjyunQOxI23BS8p1no4AKGD7NWQWaHhwXcTOL+swgBc4InZ3+5SgHta5/NzJojRUf49CK9uKPbTBYfXs8E9uOXMHOVNstrYbGCjNwhH5V/IYpVbAg/i5P+xsUR4irYDSdgBGO6cC35Iqe9KR9DBLtBR6Ycf13qSZcTQgZcAWGUZIwf8qWHqD3HM2MlgBAEXyOyaiD4s1eoEfhuAGl3iM0o2PrNFY+QnElFwy+AToaPznRXJT0NVu0eHU40tzR3OcxiLLG7k3oJonYnRCJOE75McFRYrdcE1DSW5IHRnuRWclMOG47iwKYwL5aor416pTIiEvwPwRtWysgQ8ORGGqxcPna1bEw2MCGYnHBZqps4GbhSio5joB3SMns06JZSzUuvxqbBcws3MxPkWAxGErZjquKPJeDuOfIwDy3XwY9EpzW1rwmo9puDWOQ7yUnMYVDaimxv/UjCY6KpyrWPfCTMM0WwE8rmlRWEpzwyVMf+Y3xgAS9VBEsV29ESfRCmuUcGxcd0+WMZqokRdj6sB+BPPYMDv+n9Hv4eP611EYhbzaEUFyGbhA5N5YriEnTZFFHtN8gezmPPn6PXucz76mce1CToRfFL7uaK1brjAd/ij/0CitarVqKX0BzIY20COhnzk9LvJ5/RrPK7VmmRiS9YGVB2x4IZiGo4Mp5zPLM46ExejvEok4HVEzsYdCIgwVL9uVQBrJdYc911kKffYGO5A1bPOxFS0DOcA06lDMgGJVr4hNbIU76BOFQhvV2ds63xZtApEumVoaXT/QlmQPd5fpir6SYyUeiZIw4ODHPt8uAP/BLHaGNEtx2P9wMGd+gYTjBMxzxklGVP0ouDDUzqpKS8Lw/9LgLtTgNaBJogxRrOEx9wnR3bAxyKRwfI7/u+FhiU6fF/6HwIszFoFulea3fkhoBHF4GNWrUIV/TKSBY3gN590DCK6JLWOwZffDQSnIN9HtxJRIch14IVm6THfRmTmHcS8ZRTlSOzMBjlQ9jA9rlnY16H4LXhC7B0jNP3ix1WIu6WFxSPNxf08WoNoQtNR+WGa3mLWnEQD4iMHBTKN0iXVmb6jkH+Md8sRBvGypUt5FQM0QylIWmosye+7/wXUGqY9W7FEqtkJ3kY/eE1wSvHa7DysFV3WxmfjQhEE3k+bUh3RxB9R79dAgunDvHt5zBJzUxiD1RcIwFApxzsibKxZ+KMR3Q1BgjpFoHT3kabk4nzyguJrKZmpCNBXxiAn2v5ieB8sMXZEY0DSeIjkVMz0JYW2QxUiWNMrbJ1hUgxs5Jlv+QZGJx3xEQbhXcUGJtC6epoojl+4I4D8rXzNa4zyGumkrnVbtES7I90v96A1jvH7eqQrwxEZPG4+BN81iqXsTXwh72uHgB7WTIqVoPgJ83JmwV5TwEg2Vb5gD/ACdE0mOsnNRk2by/sgIpwpBMNQ1V2nw1Ge5ZMtaEDTlVcBEnLTY/yF/C8uOPSurgiqDXCXPQcZtkkemGhiyZmdk9Ab978e1JN8P+nNo9OHuiQ25eYX8eoddQkgmzDO4F1w08xhH5aakQn1zIJ9E7Q6eGZGHNA+jVaWyUGgkXKNexD9vgcLeblQeXqhl2GHW2+AKSwyYN3X3by/nkCphaoTdB0fp2inWUv/3wje+C1sQFp4+OuyubC8N8krI4WWqWJ6s99w10T7c/vSZJf1zHHeTZubonhSGFaN+fByngWGU+QlRQtuDPf56k1Lvo+C+iEXCVOYP0NTDO86VJQY8sjdA8ZrOovd9PFsIA3qUYl7XPn91El7E46gw4TGGoh9YtLaWCUkG37lAImGTu/5TvM+pZHnWhCmbSZM5U17sbajr6SpqADOaU6SO6GAn5vtInj5eaQMIhPxucsZkAkJPuu5EJY5wWGfMDWkt8LmCSaKI9UI32ob4GiaJ9dTfPz5xAqojtg20Ju2p0F0yHf8j4qEtMgSkX3n0qyCx6qu0sPooFHuncrxNBaKZmRPShQyLm1ajGrQS4BHJHwACW9bYDprnzjy9uM+Ijca8d4A8dGHIqDlg4Tm5TrHIIbqkJHC8d66QMUgTZdEP67/Hn9kmQ9sAfvLFVGojxNYfArJMjJgOgHSU2CrRJmpgkRn7AyNGZmiNJDvISJI1ZHsDOb1aK1r9H+NzdyKo/+N8FwUEeeaOkoyIlOQ1/W4EgwzbOfEFxMJmLYwZU8SSGLiBnh6UGivoGDeYNrAJGyNLHC5jGq5InjvWY+VFSzIIqMkLa54fIVJUhyUrmNXiBqjNuy4ODfno90AgcRJ3crF93HCkGzLkQ54Rp0nZ/UIUMq0Hg7Bk3b84CfmZFgjKySrvZdMy2eQUM6kIMAJw4qzoH92P6imECuwyEA/96j9Uhz7/daoUAWVZ6OsA+2Ts+brgoqvpYNOgEJPJ79g4eszdZKzn8prKfZdJ0/uRcQMDRt5Ave7rd5Q4o2rtQAmrOJ5pzMWIlgi5aELnVPB8MxYm3JS+lQcN0ApCJ8cvLZfMexmwuSSbDxRk1OkmZ7Z7Ws7iEigF7qHIfoCaZGLW+b216v4fAQk/bYIKsLfXjGA1UhWtVg80OwHnyVFDdKacH3iArGUOQZ2UlzdQoOvOWUck1glAubciW9SxHsDDNmDn2YnON6ryAhdEuulss0t30jHVkghdPzYcDajftzVUUHnEd9jW4p+TBnmisIJBY3SU52xbBjvfn4wZT57JcOOJOy1mia3zFh8RJkPsnwxeqhIxoQrsrPG9gSR6vHPvjv0/8Tm3yPzA+q9YlizXhvXjdg6Af+INpr5842dTfHS8BGCeH1jQSViAAyap8AYri3VCPBH47mZz1RUaoMwOAQsXygyO7E1VHC9CQgx7C1xlTCTPZLppyMWNFE7OTAGOayWsPT9IkqL+rTrDHCQ3/u83Dv7hwP+QYdm/tKcAmsPoCG3hjkFwth8gq5lAu2BpJacDPP5hCl+Ct2cpu/6sGgISfKPqN3j97zh7tMR/XoZF2qqq8APTMxqn4Sg5B/0/kawlZEPN2Bd7d6av/DJIZFjEIhPk6vEIa5XdCrA3RxHq+p+k2yWZC7Euwtj0J4Pf0wrSnc2LFZJVakXEIb1Vk48I0hxCGuFipo/dAIQ50SeigDWv4lHrrnHcuGir00DxPzpiDvA1M/WtoA/I/nOdeZkJja2UgShO/C9Qv5isHyOGhDbRMVhw8D0Voczmo043bP1pvdLcnVcRFMfmMuUCoz7G5esRGj2Bpzal+wKBSEhciXvveSIgNc93JYOdVVogM+5oVj8P0RYY5W47gRsgQZ1XEkP9SGK+w9Bundsz5cHlMb/1cqmFQGRfTKhDXLsPSHu2tgHxuYeIyBa5wItby/UZ/PPskb2N+z2jwQRVv6AWzM9NL7BHvoqC6R6yvoaf5/Loy/KeeblZPRl7lS8xXHEZKaVgD1NekRTk7xfq5m2JX8c7pIyXWHL1C47PdqYPtqxBL3PPcxZEmrOo6cJp0D4whPsgsYr1bD7XGsz7P3gmPVqAGAj+pMGrPM113133ztSoSHHcFYz8tl8PC1/howErWSByHBJ/l0YnQOPgSy5WokpEGEBP72uzCRhzg9KPiIuxwfinygT4Hv22E3WEl+oFR0gNgPIaYzdwj+T1tw03l5tOqIEEARCYLTCGQ8+MjoSf+ISHBkuupghnw8nVtftRoe4QFns7HyqpKZrVY+CRKP6cMKbJtxZlIDHZAgZ8kkC2LVWln4MZDU8+ZOjYlPKR5MjUYVxlUiOO4q9ryl3O0VuWYkdGTxG80nooWVsbHdjLRp2izle42GjjXjTA/g0kvIej5M7Yj0Q7QCdkMzDgJpzGHqmUMohtEIIQ3pJIbI652UpwXaY0w2vemGJCFlqVwjHakIKnyUziB4uiiPGzjyVh9GuWEALlKKuHRYu5x4ZiXuQV8XkxPFVBZcp1qv5Gg65eaBOuvQKLtLKrHnpQ28oqzoiPeFB7zSBQRn0T6ZF51bsaMH8K10QKnyHulE0+mreAc3kxDe9f3Sl1YiUNLd83X95gBdFawim671lxvX6YuFUCLd96IEsfIgw6QkGk5d2RS/ausPFm72yi1TVh7aX6OteDRu6ACiCdlT1alezoCtortS7RUFwh7I9womv/9zFSJJUhO8qLS4j8vqXw1WDDwOpT6Z+TKj5267wLOwoRPDwVkeNn4k4N4ECNZwx69WZNkshLSY8vKnSMQt95An731IADjYDSYXf8qDw7vHyxCo26FXZr8vEjV4Cxu5E0by6oJeLEK7wgDsNXXF5PuRUoxOzjq/trYhgLGyYdfnTIxoorC0ZECu+KWPaYBrhfAehET+eJuQ5BlRJIb1Xf+tqSSN7lP6fMlhEuCBfZ2SwNTViOdzfMeAg/3sQs9NNlm8/vIS8esjIhNsTd4gLhpECBDRfLNqv1xf2XRO3pQTO8MnxfHUelPwXK4nKkIukgjG2JBO3CouFeU7z0B5G+/ZuB6xO0ueXQ7pv/fLivpk2FcmvlryzGWDnVsKpDRckZ/HPNg/nyYx6/SdHPVZbDMj5lbyVJXv11+lWSARd6fLUFbjLdjKHtLeN99kxveMCIzh2DsxJUZ2HwXKU4o4Fr5hxcDYtacQuWKxMZj3PWoM7I+cPymFjVIbh5jgaNhbtj22XiBEf7710/hPFXhcQlqbKzNozlKJO1gvP5e9Pp9emq32w7rDaubBjP/3hd+fn3vjRLEY8Da6hgwNE9w3uykAuPJIraM6cy8vxUkBMTosQViMgWdJBFwKNSciEFw5/7kpbqru3gBenVgkD5cvuaL5nFgcuahZKQ64JhZ8JQ5aEf/sLZqkHayXHKExDfKGYecMA0Um+I7LY2T0F8aRg3qDKhVVFf7GxF5kB6DFzkpV9jwVZX94PsMaT3mAWXye4ZaHgduR1+1c5X5hXlKTqgPRcFmzObNYFD5yPQTHmeJKBJ0jRmRm+e84hDuXEw5KDIueole+ESYglsDOIpu9JZedeBrv7LOJcVM/k47CPfnr/imB8LynXXSMKKKAt3Q61Op8vCveb7JN8qBleTBXILDTwdqZDd9NhmdN5DiYjp4HvCJKHlukjjT4R7D9IgNsnh1+QU0dgFAdPwEvp2W6YoFURuZDvTMuoaRmvU1WJrmmSL8YV7ZSPk9i1OS0kBBQKpJg4mR4qKrCabwmjEeAOfUQKKvzGZIU+yKZTR7A5xDgp19xNmoQm9MhUjobvasTsejk06ddExUwotGPHe/LtM2r24oqPPYELA08ZfLURkOi+cKs4moChigSUHO7J+WOFlFUb+RPHjwARIQQVVpZucc8RQegkLlwpSPD2VeNV4KnQtuHuaqfhY3ruYf+AEq50LiRHElv1gDSbjvD/CUmmDT9DC7J/o2ZSsaHlbq+OnrG/4hn9kRpaOTZ+CES2PFRidaLbiICjjrZ+78pXsQDG2rp5WYYfVZTTikSqVBF14R6VAYwR8Hn+XyrBTCHcOstoVCf9GRCuFbEEHB3Kh15WRXqOyKqSu4PeZiognoCa7U3845Y8QBgZ9TOy7m2qhEfoFdvsncf6J5rDyOFSb6r6bAHa0qeaeDl2ow4Uyob7VBWqCLHb3C7lbNu9lngXmUd2ePbQ+lEtBB/AXswpUjCX08FcadmTXPukNHU7tC1RWVf/ZDsDqxih/8NaNj22R9e95ryHeaTTdN7Gu67tHgFN0gnX/oC3NsvHR4fnj3Rs41ljMzcmBOeW0sFfinvnAbDpJDtKlHi9PSOC84NNfdnjcy+PIshESVy0ESmvSIAlgK2UZNGxWDt2Ft/ZT3BAu98GoVWiWvRwWJOuNxS7Vbyzb/LpUJulY/zPAWxSgl27qDlw3fEGuzWFA1MARNuLjUxi+TuyxpF/I48e0pSoqGIREc74AO08IMtzL7xNDwE6DHObUo/SPJS7qTMQ4hQOFoyLnduZSXnRQGjePuCd6IHpV3qW3EHxAkGG04/490jCQScE0PxM08tk3ZivsYJWE5kavZM5YtjTa0uOQF0plA+fSNTVvKQ8SzJRSkwomo36wYe4Jd0VQZqjE+mOHK0J3euCYN1XOOpE7H45sfu6Yi6q+cRiDRgEIC30iZUESJiBJlMBIMW6hBGwyU4UsAQSYggn5BGu0fOGs/Ih2o71932IlAHIoY4cgMbzlH21kLFjMPSHUYFoMtGGtNmvI+dIWjcVZAo/wxe4CTHiIqf9hb77A1Zg5HC9ujCt7FvHE6TJh8MwZW0FYmLbWb2Ll755HzJFxUjQj5x38bxMPhKLZhwJRp038/nCOgZHXqWk7CSghgY4wt/3GvURb5O5FGW47nLatp3PNlQSAOLCl13z56OQXaN5UCvdi43ds5/2L2IER8iNkXlfVpRYZ5PFznY0AQbaWMBOdQUeCloISLOVdjzmgCdDt3EReenPKqc1+MRH/sNHbwSq8QKonctwY/QPeFMCv4n/2TvCByWJuPL3NObZMEfAI1stjR6DwHucRZ0PZtQTmL7W4mRcX1jWOmNL2Vm7CURRsoZPFrTW56Z/W3kN6reNEs4SgdoMNJHG4Ya491d0AwDJcEnjjGTZMRjoPVoPgbWf7X6rT3PBWXvc4f6v6pIxL96fwDCHzClvaOd6wP9r0Yr2JmBgyxXSvBPEDRcz/1MnzonXKXxa7+bwIPxNOoklTOj4RjEK+KEdvJ1u3vOc9wjYPBv0sWaeaWf7nenwyoJno6MzO7GlMrgsMZvMVa6hqVhyKIN0aNZB6CnjDa6qgaj80x/9oBOyl+somTqV/cSmDI+9FAKzCPQUJBQCxjrRLF6kqtFRktwAg1WAHnFiY4qtT1uLJNag9+LuDjURZDvS0B6WwiwcQehesUIbMQ4F4NQLK7GNSbu/u3wtu/AxNkGa8Ov762WuXA8xdRj9U5c9loeAxXDErflBnlRhzwzKdoOX0JMU/uTYdI6GW4l4GEg/5Oc0UKR1RtimBs/ea+bp2/6qFIDSaRcrDzciHMN9Qd+ybJ7p2pCemb9z3kb2u6ZV9LMux+BY9H+hNAmXSv9FCd/IARol5wImRiiRjruhgrzDlylTZVHGumW6yxEMZFWskiBcivNDLwpH4krldHiY3yitwApppcDG7CWZLqc1NiM2vw8Dr0VCGPGDDldeJt1tCHsrfQYuVW1+jcgyTZf/bTH7ZfidqszvRZhKR9hvGo2XL4pEqPyNAklBUdpOQiyHfAqP21wUtzTb0f2BWAu/B6EGvH1Z/rxuqYcZE41fhfeyz97t8Q0ACGjXqJ+CMbls7sr7GTgGgN++TrMxceGKvuzG352BhF4Cv2V1CyuOUOPxLedcSpI14KHPYrjgtqDZU/DKBoHaykeuqgJPBiaJpO3/ZLsK0NZSYu2+SgmQJP4DygXF//0UuR7v3tp+PjJNtHv63LzRiZY99klmUUygyOxdov7TcF3NI9/IShdvvR4I40mHgLUuR2cYY2+qPjKMwzkWHNEzMDd0+6vZoJIWHPgRgMhLe7BIiYLKPoa7RGmNVjXNvJmtajCvEQ2w8Wn/ZWEIC9rbZz53OQTIxk72EB/EpAe7zo4mdozZmibIc3Sd1/JXZ9xhCu3Kt4VRdgGsdzpySIPGY549Yh855LmRvlwI5WvHgR5DgFAfIYvMi1RXw8O4mPjUjkTPg0Ca/DJyOmoQdKUtEtfroAUFJb9AjuF7w6cZVaKJTEjlO3VIgHFA30QYfgq0oKxN1zrxs+BlOjWTWpMlNEUahJluNbxnJLHQCReoIXDSZBigxF66LI0megHO3LN0UmKMCjyahAhovMI5uefrLXYURwGnE2yLry8W6sRajcHU7ZzbV3fdKZcMJbfXtoCRFKb4QVCVkIMbPLBKgPyMljnnhKGrBE40ReACOblL5+xhHmeiFUv5/X23Rty0l4BJzczd3eK2nYPXR7Xed8dR6euX8T72kzhYPpCHsJFCJZACxD+3c9yD+T92umTdssDlW1hYhOZCapSxBoz238/pVgow13kG1qHhkFVO8K8pMyRkXNcqpXXa8EbeAMYqXra40Dm/RPyIHrpl+1aNejbiYH8XxHTxY9WurSiYTGq/ZPX2bs3Tz7/VVj4kx+7/MytFk+RQuFklKRmIpDUkwYP7nue6hXt2BA9G5YuNUyd2OpyUOSYvXuidkPPeQSHxwsBnwc+g+rMa5j/+//1/8kN/On5xz4bx4GAuLmPChVoGYyAnlnFohF56LZcON0xEDdRhgZvoV6+nAMjVk+jozIfq/ZJPagpzmA8B9OpzIt6OKPKwMUz3VTamgQ97d9YoVehi1coT0948b0gTf0x34JHg/mBP80mEm+LhgJckM/0yy0nYvEYnT5Y52DC5hVfivtiUBTkYF2egv8Wg4el5MJ4Wt2tZy4YfmnhIP5h5W4o6ELjIGOt12W6ckqx7cUfJhE91zKsM3i12r8COofjjggTpDKvMAyX6n0bAoUjwSnWyr98zM0az7m5hguuHX32EM71HxlyntBNiqMbwJinAG3lzmIZC28Dtlig/n+m/i1JlixHFsVUFctiVws5xXv/+cF5UITj5enKcAOUH4rlO7uku7Izd0a4m60HoC/019lMTCbMrIY4+tbYzODx5nMC7ZU9AMNi9wLkBjMBDc6BSbJ6Wqx9nGB7imp8euIg28Sd3Breji4xc/23ujBjN5sMN82WxJI2NqACITT2DTbqRLm6d4d39E0waMb/JJLTA7pWLEpjuGOAPIS+4cC5DM0RmKljMPYKGV+edru/rVLCPPYa/JyxzjlMo5tf4iWDhKkFtZ1oizwdbJXk76WTBmyvJCLWwE5t5QtlAbdhDbDbRulidfDErrSYS0xU8Uymitudsh6sFK6LWge+ikEtbutbM/PWT9HkkFj3p1ngO9ejaDIxWjFG5mohb//BjEUP/DhbwqamCH1B/+sjap9Jrh/aTi9q48QYkK8KOkAbyvPhgl6Kzu77bID09bPnCRe2xpIlAf63fxIW19kAnPRH5uBNiUoqWuTUUkm+vyfPFR3sN1lw0mvam4ELB0re796xtwmMuIOMqQeAiksOA37gN43zbZW/5yaWHd39k8w6rnhm4Q9uJb+XovdiCrsGp+RaYirw6PIrAV+3AjKgMgjNY3iY0XV5TEHHTVd4AO5o7jJjjZtEzRp9ZWT+exQPyAInITBOEIA6zYyImQJeEsK5NXo3IBSYCn57wJw/Ri+Wmrs9kRlcldBFNIO5xz8dyaIXYc5ZKmQuk130uMAtopEmiqR81nqLGznKId0Z8mPcPLRQ0r4Yq6TN9EuZv1Xyqt7LXuFxeigjXdxRhuYMdv4LvQ6FO1SRqHyeNNlFZZ6RwZSMERxHyroz5G2jlDoiJNWu3Nsg5amFvUz9ejvryjdyCTORB+fu/8b1umbtQXkhlRDtNMY0dnRunj9v3xbQIO+L9NSGBDJoLv8FnWyYExAmaFX/EyUMGuG+kvy5x8iiQ/nye7rtbTXZX40EusVeGRmPDGYouF1yRB4LB85SPTmrJ+WQwcsrBvVL/kEOtzfylAAA2JfCElkqck2DglHOcOExXe/0lgYAkM4T3DEx/PI0OcTX0J3yadugrOcFf/Y5LALShMH4kLd45c4v258AtK284bynOzMkx0OVeoW42eEw6zDh9KCQUiEGKG3Hkfnef8PHO80SUcA/XJP0wTUcUDMo+UU4M4rUTDxp2/5FBGHgqOx0y2/fA1yIeDFdnwJHcweH0X7FinkDIPE7LOElzrjpMkg3JOD9jtA+hFXloariEhp+5PM3kA4/4C9Z08hYVTKahxLXZgfP4A0Q1/ZVB8GJ24ppLE1/i4dCcT5J6pgWkxbcGL5UvFRvNCSbc6LJQuE6/jrjFbNqU8hUYGnyrB3JWX2M+nDtev8FeVRjF6T9+QBMfJhCxIC98S8BThTP0zZ8qKDObR7smOFcRnz5aqPvcsVg8sE3sLE8k1JhMrbmW5JElzJx/CXsNgJqbxFX3yMyZpkkkJ2ZrgJ03G9u2XF6x/G9TpSWO8fVGHTp2PlhMFwVvCcfegnaEgm2sWV6MI2k1FliXxwFqiHQXnMi6KPqVfgJcEGhiyGsDAMtQngabbsjt1CSK/e0CUlPGHINhycDsBJZF1QjaaGQgYLJas8Rp6T2VNLlVyMWsjW7e1KWdgr21CFrhQVg8RjvNgipCm8vBhMrP7pY11b/Xoh9ueOgjncQ05aUudSJW4As5p9lsM8I4iLZYnkOYG6XirAqtwZnVvZ6w77x5JH1GLCs4NPDnWkzmf1n4KouBt9efwUpRHRQ4uaCB5jeAisQJC/YFO/Ult9At0WdMD+3Ov4i9LmMU3hvxRolNe+PR/wjVe191E4xW9ns6V9uDZh+YC2PqwKeL+a/67YW+p3c0hzgnJNi1tVIYkAuNRiZzJWiUIochXuwRg6+ni2nKzag2XWNJAZuWHXAPfyt2Qvod1Q6rB4n606gpZ9xaNhUC9p5T1F1E3HbkwzrlCcR2i9Y+1w1cPCubRgCV8HUY7xMhek8mU2Lw6p/bvOTxZVLV0j9EY6HyBTpbIfgVWcMyuLjpRyTkVdUZg/Ev96LlGRMeEjLRXYXY12oNo3g7rX9vlFdzHYRuPsnuav5plv2pewPr7C6KDbxA77pWcw3jGKWTXBE3uyHWLPiuryWnrpRSACK6ZYFIIE6qRLzAemMf8qM3tDPGLPlsl8zP3/ul/ZN4vTF8QuJLJYVLjTdkJ0r5k5IYyVk6laBdxjSwGEHK+/IyT5ZNnv7Bl/g01GhpNNy8FT7q5jPeZjmNacrUtR65TrUWGZnXvQSVfk4FVjdQFEdtaRV8IY0ZFNfUOD6Wv5+nsACw+IiHowoOhG98SxXsi4DDQBktOj0OMNXMCrOFC+nCESUG6G/F/LvybWlr8c7bdUMkRywjE0aTsIqgg4TMI/HZ4vSCwZkjBftneHToTozZx67XXmRad72Nk8vSgNchd4efzQiRshvcSLp8u/lucsyGsXYigvlmns+c82yibx85wJCgVMSiPT12t5Gq8Yvb5uLBLHlYmT0VmeC55bhw0u7kHaS2YmQGm8i8JCxQZb3ovCuzt3oQRDznglLubh5H1kIUu6VsXQQV4NN30w/Asg9l3ZiLmQlRkuxF2fpmc0PBSGhbphP7iMQw2FJn+k4ejqvkhj3moVVPVOA2feGbNFAwu5JVEQO9zAO+XC0j510r8KHZ2fsSp5U2EZARm6+VWTudi+9yEk+m3MYLg2+/EFIsAiKyPnBeSMO35GHBJbFybTSzwDkqI79W4J7pGfwjwDjl6A7Q5I5/sUpDcQZd0BG06uR81MsJ0h3BngTRj5hr5LQRrn71H96/n9QFX/HxzNrT7WHp8D21ywFx5AweYjseVWLpXARVScALiLiGZT+Y/8fysZveGnDGHk5KubWXAhKiMuT+MkIXsvEBBbC7G2ySccG0NLp/s10ZFj2yn3HrQqD5t2ESiNuY8gydrSiN+UEX4cYlUPHjM8xwWWLVKURpz3QuTTg8pTwq9L4s9CdKQyJ9RD7rIiQFwZKbcIX4OwdMRLeJjCn6jjx952YU8XMtn3/slDaDzf0Y9xCOQgsdGLY5UFO/JBsbrtVVUbbTiIblMaepPiMZ+1ScdJj9vrH5t6KBbvqz8xvZZiqlYkKsO13KHXO8oRCeIzi5ozlzBmaGbQ+mz+zKaLpHg3xZ9DEMxsvMOFkoKZ+fKXA3KMRkuOZztU8aJLp2VRy37jA6MYUxCySJexdFE4fW4CYpx3bPiOODJSZhj/myy8wCSCuqRgJkLosQ9YGQM28CN7phqRkzgF5tvyLlyak9LI524rtcQH+q2IJ6b+YnJNJPXtnaYkGYMelYs+c/GUOUGY3Bs7xYrZgPL0q1iA1cYzGN7licw++OqQv+xlI8hI0GXnqGwsSYWzKQ5vgqU3ruxDRgN5qenOyQLwB9KMMWXK59tYWBlNhlre5Iy/olDDhvmmAFDzZgvJwynRSqWph+FLtnPZ9M1cLF9rWRPTK/CskAJkh0OTVZcVxgsjuU/Tc/RqcaUtAgMl0Tu0YSi+W5Zu4uaNJ6+7otCbK+l7um+92pVxsO39sK++C54AvdVaasy9fRIk9YkLMBS7JF2A1KSALbGc0cdZ+HkVAvgtJe5d7mjclLc9lgkiC+D/CQ7/jGP2mqDymBE4ZkWl0ojn29wrAbJu7febq1MnYGB7jF2SVx0X3Bri4lVx0rS1kYFQhg0h9iM2iBTqukv0SO/EnKUJFnsFvXq3h+D3Aq+PIPUSQJ7wN+WO/XL78GL8ZUG23Sn+fZ4DqRM2x6EJsIiTW1Ogv+8+LitAEz+B3JXcx8obDuVZ1Rjq7ssYqcWaIY7BUPZ/gQQGn49vNMWel3/OyzuuLoPFys0299CPnqr8hPuZv3Qnn4GSGH93AySKlhAze4kuWx2LlvifeWcy+RcU1nHk+5raUJDCHCYRb9HqfzJBwXwmK4SLK/CzoB65tHLzb/zYK0PJZKdDSaq7IGLcX4q2GZ5/xIjZzS+QBvByYX/rkcf6LnKR4NpJrka9f+2Q1bvUXP9eeuoxPBlbkwVjQPefzd8jAfQ/oCEtuLfRg3qbm7fgj3TbZ02F0B4HAMZD5IGa4HDpOcZu7KK+kYBZr8C7utqcd55KBiRfxPqeQkkx4zN+pIJE6egd4819hBoa/99yghUwbeDoTirZAm/emc14rXE6iuFLecK3RJt3iihsB4ZJq8A9ZVtuqXIgDoJtn49mBhKAKU6zm0GJFTvUOfeO8Go5voW0AnfEfUOLUrorqS/3C2UtgzNlo+Oj5+AMAfK++AZJOUVAREE/3SE/jw+3XaSNURkqLmcqfPVRJHn7USzJa8CudscUtRvMiDZBD/YxfLmu5uToNJJjTxjlwWAK9kfSnoY75DybwVuEdP4/G8o6OMqWZRMjHFDhp1LKGpDSENf1B0RjI1WBpOoVbJ6IVmMwEu0QQQRzphjlVp46A4ZTq7OiauSBhgxEwa4Pq1Br3UI4P4246OTKH8IIZfQsMiu6xdwJ67nYvfswV224YZSC1mUHGwIFkXStRjvste1IwndV6xBJrLxE/lUHgQjBtmdiRz0khlbSYkHMArtVoHA3i+laAq+exByvFHGOoEdWexNdz3ZCiXmBmp1daVrcVH5ZBvMEhS5unQ9Trrp0ecSKfaxu0WEG9288qlEjaQr1BwpNIAJ8Mu3dwdMmkikTDQuW8XutTHLTIV8X2zKS2DNGKUpanh7mxFgqLZxIcUyWD2pmgddlOk5qZKk6PNo5AYz7bLsbvcQHSgqyeIdeSuo23rcqhNkxQv3FzDPKKuYNoGE7dSyApJV5o7q9AYyqW3UhEFrxPsbLkbX7pIsypdGGsdnwdRgu2BeB1jFAOI80LwnA/WcCMJdGAB3iz1LfEQcrRjQihcHZW4loG13Yty+m9FzVfAQBQNG5iGFLHLlaZH36BsvWKnOVewu4kJsCkLFRPS5ylYY3UyncD20wu26pEtkTLX+dTMZOSAQgqBGnbmM1lFBJwqJ2PsbY0XQVriK8cp9C4WRc5dZipOdrnnvvqlgmz7x+RSeeyXfNmEtUMAjsZR0uapKUW2bPxqVitVNg+aXqjCi7Pdu/dbBjuYtIuoeheDCaaJjL61RiQMPQUY5UHQWgHxSi9TWuTmLYHyx0X4vtfqOaQMd1eZAHOIhhUwWtYF5K6SmX9xAvLTu/kMWpfBUMM1BfnBS9eDAI+BYPHcxlckpBMq9MI56/to+iLsk7IjByQ7BGqF6wYYcD6Ml2521MYk94TNUk3c4FSjzawD1hTY8RxCyxy1w/W+ziK97G3mU5gLo6AXGWo7eynsxtEOmE2AWv296ZWpgNeXGkKMHFamptBIWCwMVNUdn1tKLBMJ8J5ZuHwtQCe5SG8NCHWgHipg/W1AVFB5KRDBI1LkvjeiOs12d6mwNVF3Rb6Pp80nBpblbHXaYJJZPzBzpzZvBhsIbo7Jr3HWlvCNSzHCcscxOo6+I642HpiQd6a4HcSEJkpoNjbwT1ouLk8yFSiAnrB4W309vDBpekjIVjZnxjMaLl9hsqqF97B1Li6LuxWWX2RgXzwHI504np8+Z/hyYmV355ZyjNi+YCYEbvLtTEPodXypej0lJhJK74Ikm9/S4Z635agvFfDQouBzbjFQzCLnKCxzST/F8F4tI1U/iiXgoPNU7fOIi3pzXbNfHc7o09WLWHbeMROh4czcAbFhnrNKSrquqe97mtmM+H2V9kPL4hrVMgtw+gwepMSR0QPSE3qQJjMrCFcZjy8fY74A8B+0YTK+DzCOJE13OvIPnXK4NwJV0RJPd2yZuEU1LoBJ3gQ5/PiTxHQL2zxBxxgfuo/KQlsHFdu4vQz45cbxxOUzgQzLjhShx6DTVjF6c/w56iMFxb5PMcz8+DPEKcMZFyn2j5FhOigYKJW3Drzy4PHj1MZ+636T/db5Mxv+z/HkN4GKgmaEQHSQZkzAn2NCFld0cb0cMu1aaKsjGhagWvmwvgdnyOtGTSDqzCFk2KikWmsqeEQZaFQnQBubZ7FejpyPUf3PIjHA0ZcCjt4XECYB+9W2sBExAG3cgoI3SN52MWT1Uk/zHCmeUkO5oAdb8VGH9NuoO5ywIL087KObbvNERQmRJUu/A6qIwbzruMpGOx6S+2kMheM1z47QeSKNUzpSUEWCLzwBLAZDBjhRcJFAT/jHRlId+OR6p33mtIw6KOTTZJ0ehhgHCzBjVhrjaphJigFf9+g2dU3MayzpTMRgFUOeEMnjdLtxbev46oqq5G8hb2F6siAqvYPpSsgGbjysNtQYuPfhusIRDfBDRLm0T3xmsaSb8j8WAyi58x5fgVOBKbJH2AGsaOERppSpQPKYrHB0oVDbyjMprOBSGHIOxHmslj3J5BnEnaUKwdXgbsFKL/BOatLiFsRlMZMPDtN7RxfXrpugxG4yg3lTXEwdY4jgFjWMTuT91bGrX48LAHp+1KbEkRqj9ujiGVE8AoT42QiUDuNrsaReWfLtaCbiZVtFzYOV4F37P5XRefo96hAwruzjOJCCh1ccxG4JEFlmEkqYnfVWTlCeozRzeabvfl4A9rDRKm8h0WqsYjWn6WiVjOTHB5fezWuPiE/mVDmy1g6RvOSPUlOS/cPoFCBC/Sl3aAJTxhN8ha2CEiMm460EgIfAvfnZ33NqSfNYxb4OhOBwHn74M21kIH+upqvly7nBWbGdUDzQ0o4KTtO1S2YNn0muttxjG374oyJp312IgOxOacKKIMZb0bHxh4QY8yphAltiQmDLC5zRiNhK+m5scJB3J13lw4XY0pS2ECJE5HApBBEATjY/ZpsDOOQNF6TwiFsTlVt1XwX/aZ7BSMwigdulf5SEGZK7AggNx4aJCPxDagVbH1zTEhH6z8es+/2KhJzp9HnyKjS9ua5HeKU2GWHNCAXt0llXczHEhe+2TNK2KC2BgG94dU3JHiXw3YT4C2miRjl73noyjjycWbtRgCz6JuAPWoMti/xlwNNxNaK2UFwgkcjm6c1iy1g5xkFV4sbaqUJe1KntHG2amm2g3e5Jlo7bG4GFuf44uizpibE4pAsrCEjc5ag4QdUCODWnMXmtvintdO4w/Ey9+P2/oshYz+DERgN5IFX7SP6JSXB3aJ2QkoBVsgvEpvbAEWgMtzoi2Ao3F7FgDF5ct+bZnDP+K2ud8EhJX9Ol6sGB5hIXOoOw04eQwazsICPYfAQaOuPmCCN3GDOaG2NYjva+mTPetuFmOG+KdDZvwJxAKKGRvKsrdVz4mXi9/dz/kK1f72eDy/4DgrHflX0tO4ZvtFTVGEnLYahXitqVtsi+AJY+Akm6x1a1KffjwpwAS/+mqnL/mQawrZzZKLWwijVioHrR+r+pMgVq1jv/BoevqISnzTzISi81H+Af+h6MdQnDtFgroCEM/7YU3oy16koJ6oLqCqRD09397zD8UwcQNNTlFgQpz/EliUrPMp2cntS9f6K1XiJqhzTGMClI1HUO70AAxXnQFZftnbR0wOdnWLMFQ6Ep4h2n65FmJFs//y1KFXx83blmKK40UNvpsGXzk2aj2hvoTQ5MxcWVw8v7b1tghzQiMFDmbGw6yKo24YVWFAxeREHUq1eem4GvExX+MVNE0JhY+jdM1LpcMepTkpTxuYz4XL3qIrtb9y6uSXMIhZmrZoTatirVp0N7uSpeVPPAbA/ASFhDBtQUe32DVvBampnphdlXCCzBypVFHhjE8fk0cN5ewN5TuK0PGOsQVV3c4SAuTDDNWw5xqrcjyDPQt7LBWwGC1fDN1v8TOgCH7JNYoZVpuViuZPGv6uMnJmxMZNh3LrJJhywWCvzTSba/qObH5L2fIus2es39ZedSiLXTjxzXBA7iv8yVdQGh6O5s1R5j+jcbfyO3brpy9H9BIL17miEFl9udu8KrRgMAP/FRV9WYErV8wbvBU9sukLBb5ic+JZTAJMCNgx9UVWU5Lcjl2KlEgbXHmyX4ua8t/XKh6h1z0ZWPhfHszJ6hsTK/KAIG9Z7Si/RP7BjkYk43lv7Q617/QABAABJREFUp6jP6J0mNe4sjdirS6cHIJ2shpW3Z63to8+vv6YM9YDl8SseZ4rFNHLv73ycyemQUiZBW0j4A0dkz2aMwInbzJo1gWKFsUnzGnRSFJYUSzMZWl9AeuO+GPWGhezzVfJTw5gj/6zC89ARMQddB7ARToxON0rc5D5H+4RhQEFmlHJ6Q11IPlNoEXgLwwWZ+7u4toAEVpdCCokH1TV3miyTxdochjy/pKBgf36E1AuC5mlfu+639bNTqg6SIeLsmEiwUPqhZ31mlPGumc92UEqd8ZsX8O2Q0v+I5Zmt4ZJCxQ03S4vGFZLsqDySd/5ujM4CVBskuwcyeSpj5vYQCy7dW5VyijV+Dd8hjPt/1pGwO30vEl2VUYigYGj4cjAb4pR6PueP5tJVO+wvNrzv/RvxiIf3nNWW6cCellcSRG/nBDvIEiFmDEVu0rRPJ3xBX9Q/PyZVDXwjzdZaLe0Y0eGKO6Ld7cWowi9ZvV96q0+YC/3sw7pBOjHKE8y8agYkzUfw5oNlI638WTs4JoAXJKodM0AW2JtvG4BQq3vU3wmxez7j9qhedUjc60nJ43Rmt+/fmYmWlfZ8jALe8QYa5v03cBKD4PuF4N5Ow6T3vAuOsNeiBs6VZsP/SmwIlh5pmZRJnVldiEtZRizgKYXS4aYT9VIjwewj5mpennffwKodRpGKMHO+c8Td0NA95xOqgVy6xcdyfEgl7YntITxx9/0FzLIaXUTvI8DmrvkFNX7zHjaljAZcPAz+m65a9Q31G256xmHapRhia2wljwlVUhzW2Wsn2XR4UxBWDE4zSfxHZGNuMuOM1nZxRBIuHuANhw6i8HhHR6+Ap6xIOMbQseAOuI2qk7kGhjB+6EQO/+lpujoZZDR9Pv3fUj7r/wz/S57myIJxEhBmRaR7UjPSf/Z87B44r5s4iYNXAZxJJvf0vKmIC1oU4srixkPyaIbC/M/g83MmHtICQ6zPvFbZ1PZSJwXdOwTDZqrnV6WwAhQadeN8DahmFrDkZwfAYA7KMwgE7vG22fBkJMIal6V0lZ2Sfnl/KLflEPAJWZ/Q4IYdMn2dt2OClSwttDfMyq5tFOhYVAEIlZRrRraSNhHCX3h1ULmSDQAzlGMiyizQTQGM5sBFuZKytGELEItuqSLQvDZ1MK5d3hlYcQtiT4QBxh9G0ntDDzolfBmWI6XXcs6TsSarLUnFUgQHHxFxWMQwYIw9Hr5+gZB/T7qszlFBTKak5QOu8ca2cljuts0/Vuj7oEgZ9RrTaHCpPfLWvweCHQ7SYC0gbgDzznvHUWA9SsfTYEOblivWhjHTwtPvSzixrOE+N45E8ZTGZlML0ecDpP42UwhdSjFseeDzsdcd3vnWgbdg+97YObSSIRD+Mf64b7mv0FRBP0IKOwXwzFylrrLAbrrVbs8V+omN1qkrwcrBqnYmy/wr7ZY2ZlIeMgOwVgk8ERk4OPDXQmcAEAejTC/99gC0PaGSFBAWADss7ipZZqgxG6gZFP3O1nNepXEa3IQ8ZskBUYIFi0MQFksPPFSn/h6/darHirKbz6VDokIKcEgiFaFF6Q4OGoyM6RTXRpSv1i1vDAh8mUqT7dW4R8ixjNDsfJ6t1QjrxFST6OBa2ymJLx0MJI5ee/4EZcxa07Y8tySbffYG7Z1InirpxY6WFrDMZpA7BLRhNrVJul2558xpXNQ9TcxuuPv5O878wwfs7YQAr7Q5yGsBb+gaUmsO8xvpYiJCSO6ItNHsMgg+0uk6KzW2K1SnlejSizNmONGCf3mjr03Ab38uTJkRgdrGXOAmoiS/TFgRzn2ACZGL8WrDSzFYHXvp9irEekJjzYTFMj4FDLQIshn3l2cGH9zXimxMp3YHSE9bQ2kzwVD7NNMdbX1zWfIr5OaCsnkx+Stf51tFaEOvBeHeO699g364Y+PgrH/jHq3BKPPJVuvzLcEC4K/qw6sqcVPRjAt+DcXZPfuVlzGAOnKK8BgM2GTAcy1QG1I+TkOF1AidUNcwEHxhxRR1q/MUlcQKIAmiqJmOiF1EQvTz1mYpsP13Bq4oE8e80qTtR43wqV5R1gJV9hjjtf0OMuI+zEPwe1Req5hUAR8eO/2w0dPXV/NlqhCZY7jYuWsR2PaClwhYN+LN7xHEO6JxCn7Dfnffen+Bovt6QyOGIaz4cXcGR/j2e7aQOPEyDSz4knCzvf7EAMC0i9m7ISKzk2od85thn/Khc7UjZbONSfQZcA4FadzjlmRm2kdWJD2Pp6U27BBcOwQg6mi1Y1g3yBqMapWKfiMsPzpDER/xCEU8DZCf9SyjiBfE+E2szgk/zjfEQ/HYr/AEAUzO23CIiVo2X/wrNmEkiAGHB8U6pOuUBzM7QcoGTx//p/kCr3kGDb/Q4/VcO33yIuw4sdwt8sxEJM8BrX4KY0wP8Pv8aLqzC374/3j9Fn+tM/0Lz2hTfpOO0J8xbX6u1qIMdvrjtg7l4ZGk3zap5AwRMnh4Gh+AnRvSDYg6mI8pcciqDqEYLXsQzdSljcmeToRZDzQG8WZ6ywDFJ3dVaUi9M5wBT5SU0ZjOut2bfMYfwHY52JyZsWydrnSP3dSh0ysxh4OgipiMSslZGTPLJEMpdeas/p7CY7VMLrixYzSwMHaQgAgU8Blrmvrp2VpVKLLfnClX2yTVZ6EcEolniat/MrTc4Fx1oadno/41MywSJ+N7zViNuQTd8B0DU5QnpRIyTyBAaN5MYoBmRiJ17NnkrARmSKB6GjAU7zWYjqdTGXVUlV+36LjRSLZe4v+Txy8qvun047lxFQQj9fEW3R1QBDmJ6mDPquxsbRwYhir49Qx1LpGZco2MMEEV9X1ubntKLeAdbhA1yxgdvR2jwkg/4w9IkELMEEwZISa8wzMunhAKYJPV3ys4TT0lunuRpx1ho5zmyTwlE8eXyVDJAkLkH5h1Fyqnfg502lAxcFSgx9lwHunMZtE+uPDoroBoOWItjn8x41VTz1lOQ7kwWRxI3JyrlXpYFS1N1CMy36XN3LzKYzphWFn+kVcGw07nMivrgkmMI2YpuEnznNz0AOmn8SsJENx5AsODeaGUy5+eKR2xGpmotfhTmh2BxhE/xgO2WUrgenwIVNJgI8agffs97GJgodKypPU7g3eZL0/kYvc8EXAq2tobQEGX8QF2QMFwtMNjrg5kQu0ycT2JfJkx0GMXf5jDUCaO1ZnvkM0d3Q50A4v2ssg1hsyzJLrtYlF0qnVencSOfeYKCMPdG6oIsQ0WfcwXCF3oiZya2qjiUEpj0NowlgY0mIi0YINTkXi/uBhdpWyitPWVF7CwMuIh81z3TI3f7OqrOoOrYse5yK7IDbEcQaqtjRWlVadRENn2KVjRyXJPgYzx5OMEDyWz/DtxiaYO/N7pE7mwVZlyGeQx4+g2JOSV8nnWmErWbnmKrMQVgFntEAFVxiWtwiD9FuvbpcEIi5n7Oqoto7wEMMIljpfBaUwqVXsTsClELwoE69mMnIGAd3MSPNy4AG6K/s0uXD/GdhCNFJ4EHBwmM90W8Mo6Lp2QdcwgZMTjkZlPIHOFeZurJdjx5X+o0C+uW64Ca94bW4LwGEFJMUGQV5oP48kcAXjDKGc6be4di+TlYwDERKIpP1GgoTRX8gb/DP/hGoVszPVgDb6mjLAnfAZOyzcrCSi6c7JS5CSzbKmP27bX4OU980EogxMn8B0ToJfaNLAUIu0LCIqW2ekYloAFye3oMECmhilvERuhuuy7IaF7L5ByNLikPYH3ZMCJvpva18cN7+dVMvKmrAP4BrDe2d6BowADJ/WVNZ4M32L4Q/9tN9ydWE+CaAOvqfs7/KpI7awZiswg8J0KCrsfJSmL4/ZuW9t/KhNCBNyprFG4ln73wYKAH5qo136ohEVWgmPSFGHg18TMy40ADolCdK7nM+j4+nV9YEYvuiqCPhI9vwCLBAV2mJ32P0ut8qPwj+iTYURQslgqKg+2qR7U7byhcNxpAQ89hw/xZ97/PkfT+S3NBa9+xaGJoAQwdTyuqsDTAsyf4fF8cixveqfBtoGkFtuH84LhkkaCex7+NH6PHuBn8NZJuNCT7vkoD7GputkTAfWcZlW3MyyS/p+ef0qcTBSRjmeGwzeP3R4qvf6JdkLqsWIcITIY+JJk08HDxWhqjJQRREHZnjFeLMLLxROdYxFnJpR68jdI7BGYXyBU+6OQiBad3FSnAE5BWKlI51UcfrTxaSd0Mh8uR1OXdOPrupyOIgXlaIODCz49n3M2l9ah+N/Wql2FWxCMBZQ1tQKFNwPapaC5y/dmrFMOjiLtG8PKCvfadpY+YOF4Os3UjJwBmJUio0vVk7bdtTPSM8ftXihh5PhR3i+3bZre2YRSXcDFy6ebuZK1+HN4yQBkLcl+iWEZrq2SAdww18hWNgQ3RANh98ZcBzfjFr4KFkTDvZTnN1s0EDWIKPQCYpGpogeHhBzvdYh+wNVGKF+bQeY2puWqT1aumh/ldA5Rf80q37eVDGqb8iuaqRRr2j8XoN0Z6bLJnriAbVB7UFep6cwNWOAQEXkGzc6/8HqR+0R31o61rtqgDrQcyYX360jEpdevAucGa3gl1zk6l1IfsAYyXBXJr6XjifLtU9bY4Iqv7gaOa9q5s/eITtIcOXZFroyoGR7i5LN59R5R2H/gMh191G3DDRQSrsRMrglyGRdiawU5i8fiAtH+dpv7bAV/7vmA1TViBwHFgLE9zuwNn3VKklm6ItCevhCxqZWgpO6MyCFPFusZTWNQy4d//35WQFC8CHRWVdRYz4E8U5s+lmyfIKnDALEMyfbt42gUpldPEpTI9zveCzVvbXJjCJXGH0NINw8nBIsxG/uaam/vmc7otnUZwJwYWF08s7ZXGhUKqirARXfA5a00Vwc13hHRmafiLrEp23UdghOFI+BSomkD382VWlXMP8asTzX4x4Td7RAGcA3gClwq4s0cwFi0CppJJ8MV3qwS7QH7Go8DjaWJvVyfp4S0/AARGWc+xHjYexaqiKkbtzV4wfJC9clbm5dLqtO4eq2NA93GGzLyWII/5oRs5sCBjbPls5Ydwu7+Xc275Ba5jTjt1oh4EjW+20tjKLlrwCUoN/UI13S8AMHO0ooIGNdE4Mlw54VRMEY5L3EuAIIYU0TPRs5ixVpxEE3CsnB3MAp8gd84UH1bUi4JkHS4qHBDBiK3PsltDyL+huEfu3wbz0IPiXxxzx1o3FxyJUxdroQJyCEsLJaWLfcXpD3cwSuXylmQ1ipF/3xFp+TXPnT5Yy4KCer6DJckjJgHRJQp4rlCyUw2zJnYSw9fTiivcJOF84AF2DK8ur4YoXuDOJmVXfBjvrmwr9Cn9DdFLcpylgeZOw7AB+4NtmLyizA9Ktg++B/jE60KAPqnODRL0yYm3B520qV9WN4Ue1yd4fwNgGFV3jQq0Q7YsHsC03bya6M/3w2b+jCJE8S6oIWjKomf10cV6bCUUmObbOih/5uKF3x/xE9mvBTmshwEhLe4RTC4lXMQO4vd/wDQKXIizh2X8KJ+Sk+//z1FlQAf8jOt8+NpW8bHU6QLzaqZ1uEbgt4tUYLG9jAw2lLS3YOTPYKfmbc9KgL+c6oN37zFSVVAyXN0bFovjB6DIx1jNWd2VxXZpg33y4Sgkwfz8ViHe3UER+Q4QF0AZkwQLh193iEfOGMsQ1kyHNNtWjMnAJbusG55PhCq2NkEnCv0Ka9ieIqalIR50669BUkhfvG3KpcQ7/8c+9dYU/whu63CzIrh20rGuG0BrLIxDWCtqMasJrWyi0BMDwmrnmgYZqbqoidBFWcz3scJ69iSX1Uzmm5jikXB40HEFd+TZzOUoEMPPTPvOcck3HnRnj2WB7XoW/AKbVltj/hsxbKnQIGRys9YRIJxW5bHVcEWamGuK2f4orQLkaAwPR6pYqph5WJM/QMqqArzQEmmooebJ5hSKtS+/K0QzCetTonmdPzEJ8di0vq50W92VO9YzYUw4zmVCFdD6a0UHGFun5dSg0VsLu1st7LNwVDb3pneyyuFwCr3mnQpREpKXnunZ4U0jNStbvTq3OzCwJzIODQ62y2ZiAosDQS6uAXZ3gbmnnBh8PNtxZ12R/JJs2saMOvwutPG5Lo5g2Ezuu0q9sY7YWvo3HP7OElU2d0RqFQK8Rw7YpnvZm35iEigCt0elYIoh102GKwdG7eS3mGnBw7A5NSlXM6m8fW8LwyiY2cmSQK+goel8MG+UyDJJYMvNH3ozLQa8Qcw4SvLEuGqVPqJdqtICUEaH2JUFVYxPQZXkOIbGR5xSGOHmz7wgI3NsY3gLTIJEDJre5JUQgIvkh8/BpNEL3nBkNBF2DuPtbczwaTjYccULUSqcP8vVCfJMjc/ZaxtvNPjRGBA6JSTsMTWQs7bk7Me+J2wdos6/+GdrPGUetXDVvybKX63GswyHR5G2SiOWLhFLRlMPa3uTOBvJhbzdSQYa1jMK06xzHybyR2ySvpoJqIumswuSk8rwM7kDTN/pOLNAtiFg4VALcZxHq4sALK8naEXkN1DjcCIwtVsZHvmUwTn3SONONRctGw8tZ3liDUeAqxcWyRHrOgJR8klCxlwiPYlB0PMhqbgpt07cIh5uM/IO6UJYuQKOfKTrkOEzkbSujc3Mr1QrLX6ejSY8wSODzscJJYESzmcdpSE4W9mLeIuE/Ij2VOH1bCSWU3TmTsKpZdzp/ANvI1hmGlTu2OYC4cX6iZLzrUcHuxeUuJSbaCSnR+J7HwdZr56G4BBzVY/mtIzoFAsbcGD9tgVnwHFtaWSLqBHRXy+Mp9lkBZ12KA2ZDvfqwckZ2NbvXU9ctwFAQq+Rt48NK/kUTulYqVnhFnRPAFZL4leBJjDQAp3krpohXsa4+jkUwH7/WMmXPQSuqobwQcYbgbkbJ2dwk0MPQsYM8Wf9kuP0cEIdpu4gYztIJFJ4VSUIyvNJThQFXbASjCHIKtwWt0QQckR+7m90GinKqXCezxpTCGiwOYx/sHGKOEkHyFgQOjHTDOkhzTmU8IkCm3fEA9+CPb8n+JZaz52tov21C3NaNKUv0P/JGtybthOSXbV8fsP8VbGVjhOecmFaaKlAx5oBi650ecUjapnGphPEPfRCAHh5Z1WxsILAfMBxnyxCsr4KyuOC6PEY2KQOd6/1IMZqCgcPjnfPm6wURlX7j1/06J3ci2+upOwZAf+zVuT9vLNoSKWp8+p7i4V2NxCfshjFPwRIllp6fE0dUGsKm87mwihNKdh2TQ9UUGsPcOwfcqGe8br6cw2nwZWn5x1XEm6eWYamNLPdLD2pCeANI8MGpp5g0ncA9RtZHrGRbaV0d+U3G8MKKWF1R3D7TfbIs5/jaiZtrhejsLGeQDg7FRolx2k/AKOM5AN1Mn2Ifjc+AwDZGFWYpPzQMGtaRROGoN39UvlIfzm4ExKxrxThaZPlVW1zpwC0a9Jrho35n1LlvEaCDhaInA8Arsiu99iToyIwS9z2YilZxJGpApKbVBok+BUMU/VmziSDV6whznDZes2AFv6JY+83VWykjCDCd8Qm1OQmpw3AWH2CAJR0bM1TDSQDJa5hTdWNqcQO1OZRw0qozVniELN4jh4kwIRsRly1ciFyiF41nUEJotXKD7TTQW4UmJnFkRedlJQoqS50Smk5123SzTzGVHHBo5tsLmusSlWzydjsCRADzLHNGkKEfJytrqK3tg0OlFdytW6kphxX54nT5gxKXTRQz7FSeW9TU0lVMRshivlrGXzwvMRZNLyOvOX5RemrdirARhv4EKiKBGj8CyTZAgveJn8DhDoCOQSckBwMv3dsgV2undjVPK8e8iQ0sHXOcTKvQG8ESHQrSC7fiNd0uYfVQypxAML/pDyMGuefhczYVRPYQNOIJAYuZkYh1UMRRIdJKxy5feWSidFYNTqWaiaSUVdNOupxbZLhP0hTwQYg95cMCpgOuFCkcfuUWISrlJ0Q9pCTjXp8Zw60NlUkSlmy2xPaiojRdcyEEKuUJtjszOUI5F6o7uCBiqOpVl8cHkQG7XWIx9kFkcyrLXTsAZNFkuA6ArrKQ+o2xzMNk4SpimhKBeZ8yFGi+RL/mLjTU0djFnf04tA5kwlyU00G68A4OTNwn3HAg6qBNLSHGmwA395RztSKFoJOFeIH+YWmXvmk+wIfYHmTW1acxNCTd3oEp+0RtjuNgxa8IsRSJ1g7dy03WYuHi9lZAxUM7OJU4iFCUbnLax5Rz1TMZUk2NiRRscuZdg/4rzTxUoH7xjgK5MXHYVKGE2jkg6qy3xkbBsE8IEpvOkF8ge4VihitKXJTgEJkxXiNbHrTyRYdGwwc2MC5paWkQE0NUoPFT4pM3AQ3eNLJMa/b1GOaHXMmvlc4eAFaXJK5edsj5EXuT19TtjcqjaA1xlAthrai4IhgVegIdcSrlqEfquCtEZ8EUXJPsTNkI04fAe9gRHHwiktUBzzSbwYgL+Bo1UzPgLwtuk4a/nuk8+J6rlkBuFwOAe572a053u8jIm9fUGnhjsEdNBDpRleBUSRVA1M4wnfFSL3tpOzk7g4GFR7RzI3gbCU+9wdyf0tvP2Snz3QCZrnSKafU3ufjHX4mp6hwSoSD59YKse/PQL+KTyoB93Eh9QKMePtnhkkTgZruQl/KhyBElWvY0L6R4kxP/95J8E5vw1Pyj8WPcMp8lGRR37t3GX/KCwwROSf6u3/lqRT42p/Zlxa9e/bekmVIM/YDJFdHFK/yvz38G7uSpNl2m/pP4P3pwicRif26yihpfR8rHjP1XHj+Z0p8JOtnBTxmA92zKpHMf9m8fFg3mBMxtSivNzh4fNGbESKGcDOUB0fs27zmv/xHefZgKt+vAl+JJLiXGAf8h3QwKEiuPHZICGceKjSexR/XsQROlykaPtOKpVikLxjtwGOrE7MHamd/OmBjuFTz0D+V1LKFeaGuKjgbOIlT3a5b54SGXBzarXh1dG62NTTfh8peN6sVJ/ZwVFiZH3iS85c/NAz0plJFFXZU2SuidnTZ85JOjXAww2g5jpVivbU5jRkyaTRkpaB3ciR2NejJQQE9M4wDc1qKzMsjNRpJGjl31pTo34cT7YjT20gsePBmQKAd7F7zCrbXnnOM+7n/MxOf+jQUDcwY4/eXLTlL+ptIlRzGW+YQPs9VBtFJfEnEH0K3tIZOnPAnYQfaablZQCoZ29WQqwvH+LFiGBkEOBVIGApPqzdL0U5kX/o4qKwQBVtbiL1bO/HS1akdAaNT6VA2HQzEnPqCUKek2qBLSKTpK5dQxUs/SKo+fnYkUNtpnH7CCgoC5gG/JJnkHPvCJ8BZlYAU0X5zSyqqiBvwbzbQfcDbNrGm60Xwh7Q1+W9z5DICAJi/ccGduIEczIk+DRvO0n1QvjFlRwg2/yqurK+J1Yl74wVzq7KwTIY1kpxzArcM1rrasjPfRCGjM7HNszqLwQIgiwuoB2CveMi9EAYrh0rMTSmMFt3Lo8mUqzBHLInTBeAFI6PMUckq+fXTmkXhjRTFUbrprqjIqVBzKmBzeR8Gc7hvOOfDTKhMDECHVbMMznPKNAnsGmIIfFgjZxDreZjC4XlrWk5X+nkYI+V/Pad0hmmD1HY3TwWRIGxQtCVySRAycDqrQn7LMdyWax0zOP9vgwrY920GN7IQkjqSAjQa3gH4khJWZozA9wsNZAzn3jXyLPbZ7dlzqE3eHLt6yD3frmvJ/KoTVkws9+FmLETFCMVd1faTGfuS7NkUXdhbJe0EAFs3TMeIaO7oQTcpN5Y/Rm14UQRsmKRkTD2FxTv4nhcxCiu3y1SQQg1cohxMAROYGQNgvclqc8beLlc1V4ygM3C7POOEOjqk3KzRXjJKz0iOKLCPN6AKpi1ilok7GneTflKDxKNycKXKa6AxdroZOevYjM/gDmFOVqCh/dQyflJJ5PO+cTYCUGh9vA97mdD1S60DdvsCDULX48DkPEFUFGZfJnaPQh4iEkDxlChDULpDfAQGfK9VIYZJS4c/Ce3iLZvC7VgdI+WYb2y0dgvnpB1EQjm6LEJFCvwYohiY3RykUpoShMTEE+Gt4Bo88imTjJs0JRuHk9KfRUKkWYhtgVuVloeAB0ZNHlFO1auyw0K+MscR55xBZa6PENkF/Y5deZ6ioEfqYSPTehgPgDHM8Oq6mnwJHkK/F8A5zyIu9A/sN+2+O6cWr+ATsGusWDMzLBBHfDwIQj+bArQTEHvvNKZeUXG8Lc3EYA5YE23UyBt9wnVy8XLwVqv4fgXqtJ/zhwMBi/4j87h6O0J3hiFlRX5noQ/j/S2pc+Acq/Unv/UqkpFqz3Pebg6dZPKs0jL3RP1WFuyP8UD7gwroZaRS0QpPI4HoG/iUZRLTDNSyzkd4PUqOYg5jZceljyv6okCKXjyfKm7vd8MJPXtWcwBFZtggpTgWXEbJo20I0TI/uF/r4QXtuDk/U+kyELPuuYmAgNsNZmhcr0N5neuyhLvIIP3A3yjfKdtll3hqUI8FZJEJlU8diTYWftSDKlcZDICkBulQ23yS4i5sOUouJycIdosIoPGg0qU8D1FpXOi4U3dOX7BNXjBq0FI/Jeg24NFNd6h0QK/AJ4dJ1VEk+s0563OSIhnwjASxhN/KkHj1c1UiHxzQoHn1EF0FAMRehHcDug0xKfi2VysMZeYWWJPE+GjEC1WMCaAoRuuWmytXmQUtEc3x3aA0tkafKt0G5PHIPP1cBJMNkLFM0jKPlgb1pXkZrVwGOPPyoihTCsPQ7B2CQmTeOYYU4dDvTl4liLiyZ8M0i+krTvkAKlhwUzQWHvXIFwQbG6ITuxg2QPgL5GhKSt8nvWHbWliO78I3iGPGVKuzJuamCKeFOYp453Dy8YSv2NmoojJylR1cHqxeX0H2SPAWUrhmBhcXiT5BZPwTWSIHKpW3Rys/qS/hekR1E5UXdYgCRzZuLO6xR+yyzIsHmMcR6l+6M/k/6PIqe1in8gPrrfO+Pt8gu7tmFjMAVECHAQRmRo7ycfSI08HT4OlJsDSmj3wwxWCves9yi5Uc8XfucBdynEX1UonBWHGqgeoQWuVJCgSmfGIcPrwDO5A1MQxlxpJi/YO3K0dmrack4FBAxRrom2aAcf8XVY4MfKR4M5wtbpXQ8NJ6E8kAxCkHy2PkcqjZ1HJkaN1jb4L2pI31XrIkWc3FpDhaObBjDVZz2T8SnLmMi2jd2fdIGoUKQXnrp8fGmdrKBm9mWRZ+dE6R1+X4gppQxJGXgA6fAYO1qofY4krUECKk9RmE9mDyFKKK274tRCe1FXPmA8T/8vhh0z/HzhZaY9TnYKQpM2TWbBs4xu5uqGIPck3wQBK8M6NbAh9oygeIa4YzJdFNOpZe0r6V4w9YhGqMFO2ZW1qJBKjcmX64ZGYFAk6iS6xuSdVK22Rt/EwhonHTaQcyQ1nXy2wN+qgIi32vY8I8Gimc7+jweT9SPgYSqm8mn5sEuX19sTq7U32DZp2ZZc5LaMroCu3IQmsG55e+0HgJ6dpXBSbCsimDN/Lxq+i73kKNXrWSqpY2hmiNxxLhEVcMVtMAdEBAiQO1fT52oIHzv1lfFXuDQQ8OsSnvTKgwBMgknWFzW/mxjZET5EZIVg1teoMX/rc3hLftH5msYLmEM9J1TEgq5SlU3DDGcsDzCB0a86qFU+0MTtknJXdT1J8VtumSOsy+NZ2DT+UQl8FErKH9MwIW/SfKAWdo8kNTk/j0jDc6oRBYvK7PxZRp1B4zNfT4GeP1HXNH2e+L37sf8IyJ3MaANCTkwfmnMbHaSGPbZcqiXM8lchxOJDn6BkD5Zm0+gWp0IOMrU84DHqlgfzfAGNhJUSrRMhLt5ksCvmq9P8CJ6PE45KtPVN/Zn5BeV7k8ppP0IMoawrqaUMqssYDVuU2ln6Efv2KtpuIUIk7jw47FJQb6I5cy4UGIB472W1lf4CqU/ALUD4v8NSxdMjp/7Jq0zCCsK/6u268R3Aj8ppFSZbQfnH95EMeboQTv8vVPiWjObBfrw+PNQBOKpgUMitThLqvZAWYjKtwwmDiwPV2OCtxvQKUbH9kAVkmll8eMGGdK0vpkdbkCruXYnLnCAhrkxFP4ymR5HhkYLszgtDALChg1RMBW0Afd2G+FEQvwGoPLWlSFBfByhYlYHxItnsj5BN0J/bfAjtD2RbHGfooYiovK4fgU5PwLIrkT8LaleT47aasClAIJApgZQ/BiVHETH1jJTDN9XLyAtsVgDPX47KQSOc1qxkEt7AIbsjIAReqmS7XJukBd9DI3jBOIahCCgavhz+inQRRRG3rBL2I+3jBKHnkEdBLbi+nmflBWpiRmVp+tloigG/GQi7C8KbaM5D/ym8xPZmGG/VbS5qVM2M//2Zd7uUiAu4Kzbo1hGoEaES4sS5VpU6fLqCDaRuTZu1aJw1kqjasBXcXBd9xVcUx8DzlxEGmfZXTLgQvJ9gFDSBpxpmkiRXg4s5aDMnwNblH/hgMT1dMYSbb2L2HcSJJNJUG16Hx7GSqT1+wdswfagw8EtyA7KnnAOiGPKCbhxpmv/JLntTgLSr9bdR4Czx6pyAkZzeGKYEb3oOXTPJ4yfSaLOemwWwTl5qHiMR2yB0bApLOe8noF3mTfDR+k8Q8MPf8JxziZ4yTNjp7sIdSYEVELYY9y7nLIFJ90kMpAuXesCr+zcBELOC0+QSVhwI4riM5nYWXjtqiK1Qho3IOcmuaOKyGhEk+HJYE2yNftbU1KznxE6pxFsuVnHdDsYE/iIqA6VjycUPpJNuXGUeSExcQEeXhIqO4lRNWjpLkGVJOSnWxenv9zOLtk1qDZjzRqOsNAFgLG3lRn7lRNFIg5IInGH9R/voSYpmY5CKEX+gL+17HZW47BewPoxJRnBff4G2jV3lh8yz3ZZOaUVIZt7NI6ZhqJq485h3pWhrySWa/2i1Eg+VkT2HPPUSOBb7lDcFrUIsnJOY/5/8O4uCqQPPrx4HcgGAuRoIQuZZ7E0Sp7M0Q25p5q/XEguwomBT8JjlljFbItsUEwhsjGWVhV3KLpu4cgZdjXF6LqDhHZxlVeqtQpVLKmTWrPtBegCmmSWcCGyDIbN0QvKGx54m24tn8g9vRmINJbRH0kJlkgmEiglnj1tpVYrJ6o+4xHVydMJP/HrUDPDDvpBSyMSheTAnrQIu8UyD4AZThLZ53Kx+8xNCxsOdS/iWcfIYJxuSNhjt7r6dGe8P1iU2sGGHlfRt7ltCmlxFYEvZouaGNASV2pqEQOJTod4WqxCnO8MMyd3weH50CZqagZmYSBVfLWfyHtNlHDQzrvO8cPCFSN+ICb0BoUsX6+K2HnvY8JEQW9P6+Acac83klaTR+iH9QPOzS03aAf8biuUNJhJQpq/aPSO0FjZgo8QO0+JTdmO/gpBjjqLJcfGn66G1AB18TUpYSUREq+nk9R5jJU80OQC6xk96Zj4jeaUkAzbNjiYZvzzV0lAMd7z7ykHhwxkO/sKWB/SDW1Reon6pBHZJ097E+1A8wPXOq0sGmiScNNgD7wJ9sgqIMbMFxBWa5eE/GPs9CQ9lL7YEP2ZGskQKm5+RbsyYohz2lA1iuEt95z6mZvUJAnsqIx1rzIOtJtBMaOGQnIygm93alI6gdAmJ4iscwwEdPh/y6kUGkziSCppadVv1AA2fEPTnWlETXsOEbNlKRSBqZgLwSw529ErNZkF5Igw9YhxrkfSA6IjrZIhvLwauxNweairWHmzW4XP8aznodT+KzGmtwFBuz/BirsSFNdZHkGf/Ci/lK35YvSBWzAl93LTHAcJOpDKmCCUzpXBTy3ZwkTpFEDd/wJxTqojvZublnDzhrcc1Z7kKpuucFK8ho6G9vBllFhU0yo4KzmVxW3APbFWwIQQHD/V4G7CGPNmVyQbXSSbdgf6BH6M6oUhBAr9UwvkbQ44l/FNQ5ccWp4De/QCxVcirKjpzxSTljfrI+wYxnF9E9yVQx9FJETy4JrHyojE5w0iQOEQ80Mx+zijU5PjtStE9qdalmPkq9sgSws6z6Io5adJDRN4CA34CxiYJHClvkPk1t0GlhT+psXNFFQK9ybhWvbtBmZS44azYXCiNGxzgzq3gtPUaCxiyXgaqMMCfgjgu/7PlEf5a9OsTGzjAgToyYA1YVJ41TMpeEnZDIccbwGiJrBwImABfMqCKvX4IQUEfV62fYkBATdUIkxrQzimEfNBo4UIMuiqxJ75K6KVmbTmuXtlgHNeiK14gX+iO5DvmtP7WalkF6/rKUUm9j75NwQJOeCBjBoM7nivIBZOTFId5Ibg4IVMXjitwjz6Cr1J5NZ19FwWxnRQOqNcrk9GoT6U4RTVJIZ0WJ5cWSuJ9z5osMBMAKRNAg5Ceoavo95U7ORpnJwSc969t1onlRfJLvlKI7o9fHsfQFf1y6ag0/yGEArAxJRwZVLvNDMHVuqXNfaP0YscyfwSq/vWddvnjWTNSTlKsw0WSRHRQ19FVvFeD67pfVRTn3xVzWjkhHt4GV+0Vw7g8IOAOBDHibcYGoun2hPLYOc1C/aTS3rbDhl3LAptsfbf8ywOYQoQYjc9ZsEiIoFFmtJR+HZl6lQ4OltgHM+/NtsysR3xh6qbCMurvHMxDBQ+iZ4DQX+N/zynLccVdZB+d1AeEezRQqqbW1gXS44pfhnRaH5Ui4OGQ6K4iY2s/U+SdpG527O+rWgHDoK+4aIGGjAK77P19l+15lB1Gi36IIPfgZtxLPCIEtaO4s9+vqwSCB6fdCXw49cdvGTmFLZFfY+Q7VW4iAKzNkZs++nOxLLOQU70sv5KlygQy6WBP7PoqZOgVOvR4U5ZnDpOaODZQ2peVUDT+y3B+sO21UhQ5IacdRMG2jdIxrEPcANfg92TN+vUjUcO962NY5CdWNHq4oTlM12690Q9T0tD1Vm38pYDxGv/aP8Lo9/yURyYNjloTGXi+TzemVVcwrVc+OIrc/QDfbHlW2X6IhUjX+VlYY/6FZiDZA6V1oqE7HyOZGwmrcpXrfPZ1UXMJl3gXAgKKIkf7n9T9CwstWHveNIADjzDlGc8Iom6Rkombyk2Od2oy2zJHFRptVLFFPceY99eOdkVHuI8VDD/IP+Uuf2XVfQIuZtwCAVBl9teuZpGbuLKZ0ydm2Iyzymnj4oRDRf9Ku2M54vx6p3tnJKWAB7ZnVt7l7Rw2BeN6IHQEniCO5VuMS7ETjx9uu5QFUnjk6b/dmaFb8GpsGY+/cN5rgM/4NN082UQleXlQp4FSdhUkTObiZxyFDo4zK30y3n0v1CoNz8BlBiW48pbnt9d5hi3+jrznJ1INYU9IyVMdMQ5ZnFkYzgGP8Ioc9Wzgz4xVEbhOnk8jtE8e2WEky7e77Z1JDblWIHJJI2XPsD1YpvhjbtmorOkak2851EaRnFnPzao7jH19Q48qLDGakXLTDmbKweXO3ns+wCA3GLqP/ItwqDnDdmsHnruDqvhaWk7zkSyshrbhWTAQsl4Lv52cw7DXkKErRqGUmozq9cWwkZGlij7nIdoDfzLiINERMYjWS74xt9Q74gotqx0NhbkLHVObsnJnJtXsN6xZPRsDGcXHfdA48hw5Od4c19GhRQCN1T0Dr6+w8N021FfF9wPFAmKLHxT+DD3AyMlVRWfAwxtloIQiuWj+CmQiIaZb9hqOTHtvxgLmnqkzVNOsY7yLHUaEQwJE73Hc61LBb1/EQVFngySoBbzXhMeW5VAqdbOIN9Uea8GFCxteiI3DGbWcM0psSChR4jEnZSLDwjN+oAhlZRqTZsXVN0skOPKoz/Qadg7ILnEAn20Dv8sAB3oUFAOFZs0Ss6rFUJ1/VceCk2JpMQVltcYB4kShPq44xnoduCpXobdV0R9sbutlpjZZw6VphgK6rdSUfa1D6RqQEuIUTioINW0kd2Q7TT6R6VqIhSG7RmRqdIlTnM709e4j3VH1jBuADBl382bL4cgoEV+OX4jUQkwP5GXgy9Zlui1EeXBAdq0PjAyTTrAGnqrvTzeS1+8Noe+NHvfZ0cPNSUlFvXKzDd0ZA7uj8BogQ0RFwZw4DQlbH62IVUp3vajLj/N0WqzNqLSweB6C5gTwR/I2HeKANCIocSIygMXVEnGK1AnwT6MzEEDB4cbt9L16QmcbRxOaj/qxfYC8mRU4eod8W6ksclT2rpSHyfSle+TQx4iLTXmoOME+GimifaQ1uYx6mZLH+rfpNL9crzbxX6x8ONZRLAN8VsaSdG0CAdCZs4SV8rnGGt/kqwOIh29w5USTjk6ZnNoI8OYR4ZyGqtKj0yyRSYb/bDZ6wEUesK7fQzJAn124Q5NX2rCzCzIjAi8OuoPByMwA8xeI3cwoD1BBnfkKshGKmPT6MXfbSdcg5yU7fsAy+Rxj+X/+v/w8euGMCftrxUK8Lw0nTgogH/IAzhl0YQ6/bRmNYpZ0S6kzncjJl4Yfzz1eFq6S4TY9Q/pn5TVE2qEINfkM6d2aa5uB0EkOJq/lOwsYjNBNmk84wsbAJi8nFWgv55JJWvf1P6eyQ0inwDbfozWYSoVx71JJosHLkXXolQ7tesojT8wFqUxNwgJ4VqkY1/UYsMXduKFft8TPwUafEkTMfEQNfadesui9FK2b934SoHjAjV9DJG+BOrQt8A+hsbyAk6vpFpIBPfCirgcKZafBkVHVkjVgtgGLjoTPjQ2R7hwgaLCjLIgbv8c0wXrA6A2Xw035TqQDaK9MXwJiJp2qc4Hvv77Wvr8amx8n5CXNU2y2jQVYKZF6cPWzjFuOpwIcJcNm3t7sLO0DNIKdBDyodrZa29Yajz+pTc8BkRp7Hvf68jQLZEgipfh1Y3f4SvsuWRvHLa5UDsGPJ8VXTI1oMXz1mPv9KslJ9X91fimnMJIc61U4SJBPHSTq39Urqc41dh1/iiCOeGWfwQ/ZRnr/AJKZ3sgO3NAzqlNADptPOogZ3MjCiSw5w/l6gTpc2Q8IIbCewEQPCjV3wNNdgDZPVjsooeNlCOXmYWXy80vVMiLHzEIu8iXUjwL2xaMUgVpm3ev1toWh5KexeVw8GCfH17byMJZLXMgoCfYhpvLjxrI4ZiyAq+qJY22t/ncdpHXtybeYVI4qCwd5ok2hxLHD1vcA48y6gvJpdhUGCY9DC3g7EDqvxljPfC5U8jVZmKrq0CbY90IlC3S3V1+981S0kQOalx4/7RssHi4pyOjrcROUnS6pwgUfW853MTDdRST3U7sycP2y/xHeejpIogZsHZlt8jCmNJc4R3Fyfes4fhu3EDmPh+ktqB1AAROq1z4K14a240BB5QCsnxooatrfPMZyKlq7ZiyDpWWf4kh5YPjDNN+sz1iCs7TfEgUtw7nSMgwlu4upq1mM542U2V4MXpXNYdyiqxxidMqJRaYrJBd0YQ/Pix4AigYjqhIFrqeQCAxwX/fr7sn2LW2JjCRCGRZWtxoh/ogJgKjyJngYeBLNHfyVB2FyEDVeNqQAL23KAuJNBwp9t3lMD3Wfo4QojF2FdIzL5pj4V4A3vupPA0oEgI88KE81MMrLQm2rPfM7oNLwRT9zVcrXgXonV9qNMPNCunS1BlrJLDlgWB04EXbdOLa9YizfdF7f2Ja2vow4IPrJbN3lwUXIaLwHjIca7sIMazxUp7M2LIKGZ+pN4hv2bNyPhCpxiUMrZUqsgEjHN/TCpl/avvYXvPV3Seidd/3uhcaMd8gpv8ctMFc53hr2CmZzq/r7T3az+Iij5tXmer8E7MPtLHS1YDxpu77ygXJqXs1tesdeUNzfGD1vm+n7Wiz2FNLhrObAnL9STqcVpiUlvNxwgP8ehb07HBAAMJlExNmc98HbMkTDtWZHuOxRK2sI3BXC+xQaFWiUP6nCsslnoxncvJoD+sX+JM365t814HX06APsdLl1VVMsaGgzRN+Og9oOwm3DET2YM9Zx6t/ZwQe/BAT8gekR+oId+8c0JctsJmmuouEYHDN6oM5FkCROu8Qt3tHAzbchsuIx+wwr7HRwQQvW06HtU16QqRof/et2SCJWSFQKwZrIQ05eF1hdxohmaF1Yb51ujEYI7mWtg8DO7s67fVEW1zV3OChLVSgW93YgwybMqx379BPcUUMCbgRlrZijDdKO5LBx9dBWvILxm0aNqf2IlEanav59m+on+BMjkOVjGC7XIQXNV4KvoyFhhCRlfcCyXkJPDGg0TsrXaziaPdkiKRLyegsaNYP+L0U538Pfb05PW8fiwcg4piBdbrParQ3cYumfTCw2Rn0RCw80QPZzJYPmElebmsgGqMFPK4BE4MHaAGYyE6fU0F04GsRf18asNDpuVM2Y8UPRzOQP01RwbljGnToKJgmEDaYm3YAzGI5K1KUxDJPbY+1O+1e02mzKNg+miM758bKMyemLx0QHYKzjUHlLt1p2islazVJRL0l24Dkwbo5GVMX/hbIYocxDGUbYr/opcAIM3bdsyCXtL7S2d+9Gc700R7LB0Jhu/QjLMStU5QF011xYxXIFS7GMMFcacC+GylIDYw5kQY+IzfmFBHRfdJv6q4RLO9Z+w8SFq8OYqCLCJtT3scC+sarWY+AI52lAgBO8xP5K6B/rAFI95TeVoIm+BpioTRkMlyasj49C8UnIQVmmcGN+xNx8l1JDzL10KRXs5PLMVgFeAEQniDthZ2f0XQqsdoU2oARZOiI7hwE9oiEPu3OQ549EOcs0Lhnd/5WTrVQoMiIKm+KensVWQVIYeDcxBsRAfnim985EjcAkusgU+btiIMovDyuIT6LJYDlmz61DWKrKMGIV3lFtySgcOZnHtrQFovfxszBU8M1P8a1LA3i8HehkQJ9rya5RFsup3MwwQP1ea4JdEfmMsq1CheTKXJYKOZKBP5cJ3IjW90kJun+xVKhNaxXlO6Mv3XAg2DyJ++TAG3++h/DLWbvrNcyiPn3u4B9FHTQR+lwyLbG8rLl6jcMAkTBlYkwAgqrNzlCJ+KaBpnHreMNj0YsnXyFs6m0+DRGZmrOZJpThwxPpZH2vNqHSHpCdlz6Mar/xtPyjP5QT2rshJ7I2CiUddkfdgW+6USMrlu4MT8OUYU8jt74gLknse7j/dN7sn+Cq+tnBNgljI1kwS20JVsKvOmzxAYfvrwEOYwZBPwLKc0wKAM3hXIGqk5/esVGZvRyyz4vtbI+/c/hpR5ceNZm6r761sc+WlebeAmajaE3WvhV3SLANXZILUuNe1khvKswHhWTfkVnVbnTuR4ZPp2hscRAdFTX+bwvc2YNi2MxdN+Kz5iuuyUqln3LdjyEGtm38fyUo4k/y8KZ1B00KJ8+bKu+OxsW+R9ff5bGcVXWUkGwaY2tJcuDpACMOYLYuVZZvV/3Xy5C4qeRVqpa0AA4QYZoL18ZUbXOQ0yRNX9kyqAq/C38yIp4J9yHDsBIEDlGAli1qQADPHSPJGefr1/wJuSOdWPJbJyVyFhA+GHiWBVgk5Q/2Sj/EPSeBD6H1fgx4Npurx+6nSZHwvsz2zdn8gM4FHSc2bBmEN/Qa6SLp5Vb3zlmj89tB+MdmWv6Mf9wtGhcrEUHFH8BTxAzbnHThVviHwxU5cTwaCVemTCvj1nIwhmw+shooZPOV3s9JF4i2y52DVEAL+0ZyAeGvVvKiSXKqfnn8kfGXoYwkavFsa099FnNabeOEDnTt0O8eFb72MwjLZQp96PvMrlfEKPwmA88ywFRaBnJkBIFOHjqTnmK170PkOCd2NJ9Ih1Fg8Pb8rxrQ8GTWl1++pJ7MO3oDKACpus0OUlNFg4Q8WooiHJmaUIATS0/1fCsYnJWb4x/ZLPuiG9N7UYUgM8mTG2sRb6K7OBGbAoniwZlR/uv/RLrxFjoMoRLU24IsBFRPpfeQV8TZoo43E6QRpmtTTQS0KD/Xawx1DJTLjfhNtmuzzjkovHQmYzMRtLldR5aYVN3dmruXnU/KOPU5+3wsDrHGX4v2tRn7ftnpMsiSbW9d6enJtDWBMehLm44HAS140+qqnBi1mwn3CeiLJEdaufYCXWxvOai5XTcIYrQOJFE/jNbCo2Z5WMlo4+f8b775EkVvQ4AIhXpBwKzCORwRvdrz4M/wF8lTPzMegB8YrVSqL2RiJDo8sKHQBFtTCTsNdEsJxmXkXXA0+XunC/nzYCamoqYH77h3riluC8juxK0CG332v5GhADBKlGnfCUVaMzt0E/vsJE34yyBADv6tKhIHmTtgJrZcVhVitAEmBwGclaOuowOBVPcoNhjFMISN4RRt/GPiB48CFHoT+iCE7F7b6sNqfLL2I1uaruKCifA2Nnlou3XSo6bEZ+0piL7ZszCTv6JkSdfXrNLk+XMl9etHEU6yPa2iIjKQs830XsI2PYO5YkIkl+/ILf+x/KALvrB8p5XRzs8U02TfanUEIPnnSztvd5Z6p60Nd1dkUeIxPEke3CN3Oc6jieFUH0NCx0tMma8Jd0bxZLTEbgxfOPbgQZqKWrBClC60uIomYf1gEjCJrJiGh40BPWyAOWAnbuxj/QqJXJ6vksnr97qHtttIdKw/qoFpb+DAgO3ANilvpAJmDnodZxltUoKjFk5yw0RWwibIj5kHloOYPgMXXvG8h2oWLFufVZzIDyTNo7Rzk90tN5PkwUC5SsoYxyJ84cPY71+6cfn+V9revBK4jM33I1wa2yJTIOxmMj55xy3CIO2fAaPblAHMtnVnVFmr0GnvAO0OZ/Bjv5n/su6h0T0AeQ0Xa7oW99/Tx98jjbeMJ4AVsBalsigmZgENo5JKV964dyMzINoRGv+BWTlAEImZaV0afE1k8tYk+YZDE9QvQQFnpdfNo6xa+4Hd/7dlxAVWCRqkGrxzZvYw3HwgIlpf15vx5A8XTfOnH+kNHEbAcvL85S1Jua8HKWO+VjRXRQkzMG+nGbcP4Tp/NHQrFUStdp982orTP5YTgXR2lRI7r2byJnD95fnuWb/FjEPw5OV0PAc5/yP+K7MwW9FPsblzhRhozmZ+Vv9i5QAs6lN7+FBVe442lIxN8ZyRHsboIRsic1QbFyh0QInG1HBN6M3RW8Tu6GZFQXn/kZWq52lP1WQZ80wlxFHgwiWWDPNj58fAppG/eudqqF00ejm7YIrpfGIkKBrpn6BGVLBtScubigdF6BlmbIqrqp91gz4AYKhWw46AtyYMTCZpAa2ae0+84uQSkRv4hQQbtbISxTdfOP0+9+ZryOIqEDXS48MW6dHFGo9kRaRQ08xtb6yOhlKkJlDAdDxGo/zzP215DE0FPK4wHCVRB5sc5y1+5ut/STsSmX1tIQEfOVkf9kzafAOuwDcoH0V0so83sYU5B7zZ4lvVxH27XghunmGi/kC3tG14EETzPWTkdV8A06z8elQw/PG9S5GRCn/apMLNGaC2W19Q1Yr0zi16QBs9z3qQu5rYqbbcjwxKmUO8enQgFf05i4WBjzCohI9Fg4GQc7GCdYJfNdLw0St1IGkSb5aI+eRkCJ9kOWFgTNmS4KjrvnPR7vIUH5kq+rmIt+aMrjzzmHFQ7Jy846vGdUrB9HdfoRzqjN/fVAFTQ6lkkI7fgvd9SenblE8BR/LQh9UW6rjpJX9q9Mu40/csk/xALnCVTrITVj4b+R3W077HdbEj57SJSSlSspTuUI93F5lyJ0Z2k+OPYVIpBKs2cOoSfkPE6iUaPsMm42Dlpd0EdeO8WWssVJ3cihyA2izoXQ4FtGihBUA9P+R2U9M17PYL/Zk0QGzxa2Js2EIAmCe5KJahFd4iFZLNemH/lK1QgF5LnKoCojOwGbay11O5SmRlh8JDM2Jkqc/iBxDfOJI/FzybRBPqCz3nWXYfTA/Iza3aro82xZZIm4hoByTsZIhnSFmVNvRs+wdqpFKkyaSZMqQZYAc9e4FtHBuw/OX94A0YUit85YOXTQ8m99c6khSQLm+Vb93aJumW0s7ucpJIjdCa/kioZI6vl8Id0zUxptnBYcSNCAyek1V88mnvBXxDUEA4xC9ZDShG0L3obBJl8gw3p0h+nOJAWj4yqcqSJZFFnDwCgA4KLRORn6xvNli94U0iZCV6q401kcAzBxHWv7nk47rQqxEW4c0GgimkVorvNEHswBUMMGjeU6tzTEhTFTIzHev9lLHrDYNnoEtJFRV+ZDHgD8RxnvWWPS5gVXCwbJIS99QLbNteJOGlrF0oKvgt1puDAnP2t26UhWNLG9xjMHUdcq5G86SPaJNscmnYWUCXc6bpk96sF+46CKOGhXuASEVhKinBA6VpXD2Uw3RDGvQo9Ki5O097GI6dsVuZ+6IQW5x/nYLd92zeEto1wVdZwBZtgpDTAZqxs/P/YCgmUqnJXunNne08qL+ZP8HLdQVErxxWUiyMExHYOAidk4pvnGFLejnauvEG/ycjwnhI59QxjEuvBa8g3clTGWpbDOzDTS2BtHqEnNnQozjd5z+dYvGrWftAmhmi3drutBzJpSxMdZriyWUtYnvOiWtjXcR+scyNlVW8/SRg85KzDNoGHWfN2PLH4xdKz3sbBAF4D9+pfyLrR8KtYmMJ9Vck6tkv0TFKf446b+YgnQ5kjTNfOaWNpBkf4MCZ4vCXnchjWDjxLJzrfKyd61xr+5n2Hdjw8WWvOMT4EH/Jzia4SMtlmxiOx2cKPb+BRewwbbcHO1OXfyfhGjHzM/yYUaiDxADRkfIKFrhuJhxidF9bgE+5G9OySg8SJ7MfI0F7xwD3mc443nc5SXGhpGXMOKW613RfooVV/gCvilMYf7oQbHXD4C1/qh/qpP3ONhFXP9i82qaLlpA1Uac+IwVv6E8Srd4RWQIAZFAbmmBQOrTaeiq4dxnLXpcJkzBOEtX+Yb3IYrzSBot7V06ahnnb9VM28lOgjzOv5OSfbfRk8c5NYr4bp6EFYATMjMIUClSSVpUEJuNv11Ol5namVmPH8PIdZ+BtlR/Fw24kXYLiw2vFGY7LiG2Lc7AuCYNp84oHeIC7MYI4eM9bJKKIOqEjsAoDG8V8Kb7tODGX+fArrCHowkSNOUvkc7ec8pwzIq+EYo84J7AtO5RgwiEZRcCv1RpjEuvIPutt6SPa0lefTA59zFEA9n9ggTxJMiY9cy4rqOz8r65eEZ7auAki3wZsszvz8Nk4Vta2v42FhJboqhUWJM0w7FIiMPtnrm4n+ja+lRNkf1uEgQfXnPKl5QRAZ3RRtfFxKEjENKo2qho45RuDrTKfPTmyjFLOjRAv+zKjiyp9LKwHSiZLGyZi8YUBI3CFqFSg7Xdhr18EMIGrQoMqKy23lZ9mmzDEgTyN4GhRvenLLtQgi0zHSz+3b1v8E0ngdVmv9JTH7Zp+u4ldbag60mUgZdkFHQV66mZLGVX46YjgpYvpVV6adkxeSDDR8xrM6QrylmmZmHtIZRFFRVcxkRhi4OjqXzuAf6qEJzqgf1mrQmRR/lq72mNTmCYKKxTN2fILJ3w7JHinV1kDxoeW9D+YcYV3CWCiQD935zrQOy0nELsoMzJBn2NdLRCZIuijZH1DXDjTnOXeHcsXFG45zWc296N9AAov5BBRwWIhNyEiRNeg78CyxPLVsHaLmzT/8Sqa54XgKKzinKu27Yfqko7318csVbJOVeZqrFifX07sun1TMcYfHxAUCY7rqOHZiWmJf23XyzokadsNHBEuoSH+TuWkDfjNYZJu1uJUqmQcJPijtFHAvNbrtpMhOkkHAOMdWGFxqkdca92ayT6yPnMiI4tOAK6PDfK8u3o+BCHNQa+5qV9YbdpelYMY+dxj3v1N0CZ7xjn1Zr36ysXXZgT1+mVkIWcPAXEs6wZ8dkr3vF8Uf+5fRwsZgTX2fSciIdL4LXjAMV9CuHGc51rZpSAV88XMFBgoo58XYE1QTmEMr9FhDMMga9J6oIAKJ+uLey8wD1j1L6dBETFu9Y+hBCjOuiUF/EcbMvUqrHOwgzs+r8tkvmfBAKHljbAOqLV+MLF9sT+b9UiHt7LWOFAk+RRMvkr0FEc++3L0KLJbRBiAPJ9AX9aTE36aa97iGkQncuypz4ATtzbCLqBwHO/wkfQ8mQ+DhrcN1eQigv6QMpxOlQLyelIf5cQ2VXqg9hAP4J+x9Lv+lpEeALtXkvlrCjaRPEIWgizn9Bu/qu3RORZMz49frWKTnl8jTpLgh4oMPdoVFPuA7rBGX0n2Lz2eGla1RIA5Pz4xfoK1D/ybEI23lUqUuFRtJJnlhIwCLKinUeVsgDn8G6GljQIj/z/Y/ZM98YthfxAeu8KYszijTAPRuawUhgjdvltC1TZN3dnmy60xmAmXdzGagdwp9FKcqBWDiy5xf/HWMBOMck1f208CpDGjkJyyeeE7V212wOdQZ/5Jld2rLYnleW9DrDBjgx6hyM85Irps3A+/S2RMdJcQI4hFsbwh94l+iN5+Fj4zUW/yqazATJysZCT6ktEaSLbA7ubxAKbmyMHpWkriV/SLSJFCbVzfYAGMnsTzNxRBFejD3h2Waag1QlZbLZtupkAi/sAcZyJJ+lN7hRMTmIjFP0jC3sy9eM3TDUg3x8Hnng4DHphBx/Mcg+nPLZew1TMQyt8aHvX17JaZBjbKmnZ0iqhQTTPpyR+gyHgwipo6TL0FYAyUdL2UH1s+XMSJB36PgDP5EWEfuVHvOWyB7Yk9s6WB68953OJRB9dJxAjICheO3VOGCyXWnJ8MhN7F26Ft8b531Q7FKzvTPKIs5BucKeBcdIDwv9GzhtaOm2YPajKU91HPizTQGRoNH+WFwitfAMRO9HnZ6AynPy3qiJ0y4TsoIUyeYbgaYzwtwo43WtxECmleJFYUHjBHK5NLdSfBRvYv0pafTUbVfT+z12m51evHROnRvDlLAaYCBTff5ZW9G9PdIZaVwL3IU1DqjsvM3x8Bck5iqTk8CXkLoG+og2bNt1W5HRVYLFcQ1m1IU+VP8bGgE0wrCaG6s0FRxRV357BvBgCW+gDT7BEA/Op9mye0RC9SpmpmeAb/XS0bqLjoTsjkCn++L8Fqbv6UOI7yRFE5xgp9D4lFu8RR0+fx3fon2Lsx0sVmgJssICSfQvspN6yq5wPaOdFDmvs0YbG21fg07uV+QcBbX5rtoTbsYMhVUgtmGvPG8TD/JQC039Wq4Hy7U/3X6LovUpaIoVlKkzE6jvbsmDEsiYjLnYeMCI9HJFb0q7J39FBsMECmSLECP9E7ros6gSCV8NmXso0opkyItdDPS9EozuPJPiqK+nvKMX3MWdo7UWOMAkO0ZfkvPiGAQGFpEOpWcwLx2i9kluMMmfQs1HcJ7wWeTD7CjhW4Sqi5mno5pV5wiJFqlUY7rFZN9lUZaRZDg6+1AqpX925Ukb+RyDWkswe+8ocR3HLFBdiUIgZp1DUErQ3ekBQmToLeruYOY8IWWidxBoXbyX3NZoMR5Lf2eHxs6VZhZFSc2ZsXIN7qBiymKsAXgvi76PtVZK6Oi6phRaY9cbNvmXZ/02mB2Myt4CsHEXGlB7IXE98dH47B9xzoN82nj3N/mhYA7VRCpoiaLneFjteraBFGkTQibl9ccP8y/uAt/0SjcNihwxs4KYrrLtsVK9H7YoNlTOg+Y8J1xkNeSt8jVjCf5xG6KjsoR6yt+RO8zDzSmpINo6Til2RnHkx7xhi7/hkpiBAq8eS3/9//7/xtZvOfio5dvX/M4RxlyBMLMZZa9M+7S9ZvK442hyPfdboHAjP6lVqBnOD35hysQQapmy6PQfEK1f7mr5qE2Rn7vhtA+RU+4A4yHbpszDb1jiw9QM58YdQmyfr4gJYx387CH/lmtwtqefE9ILvyBMU7FE+bkTuRr336Ouw0TLU+aOD1vZjcQT2ZSemm22ZyPnaz+2l4iNdMT/WuzVE5RBwtP+7X3MA6aiNWTESkb95jPAVh0g2dgrIjrKJ4es/m5WuQCJtMXv4+X3B0e4gWkUON3UH9bdjBmPgPJRfiyWwRev7cPLt5N1jmytsHdiKE1CfFMYgEvThVB5oYNbepoTswi3G4PQlEseMEIlnRLh6FPo8/OBRTwrto+9UgyV5ccTKpjcfOvJkQhDOosRzc23TPyATvzp9ZGEbUayXXrTy6U2uE1ixFiOeHYQj0b1QauliZlZW6WqIp2hhSZCSwfU5dN8S2qwrVOpp98n886BHxd19iPseIzJpKiZlzbt8QG9kWMnOeTuLQd5rbqnnAKK5Xi5Zi5e4cBv8Cha4c4koImzPMFdDwsfD9ewyDv85gxXzeo2+8tHoXAsqRXxHGTJATdwBt831bGfRmzw2W2Gwdmhu/FXTe2AfStW7Vzb8wZR7BeTGzlnsL7KBH9OsjRMgaCBffgzToXK5UXc2ePNPAC7YVolAJuCXCyq5F0wjgWb5BlONi4Lbe6zhHl2bYk4nFMisXZeiAbiU8QpQ4OmObGXwFI7xFO7Bhuvov6oaA7iS1XjAt4s+dsckmVA3xMrr+ZY5TAC9pPng63vR6wcyUTEZtlmWG/KBKdfBcgST7M+r/1fs4f3xVDACrSsmwPPzaIDMVMguKW5Gvz4UW6cw3jhBmeXUGDDcUsgvYre1hiEwfwxC4E7Wm4hWmAdZJ9P78lkRYe8GPXgGRGBkGJ/14oBsaRGgiH8N6OV8uhLq4f3LCjplGm2s2Yc+FbwTN7SmPLTg6w54mkgEvga9RsnZ31toyFp4Xxwi0HaKvvfj/82lgiEfEBPwttcor196DYezok0irgU/M5PBGcH5LAHMa78t3vCIScJPa/PRFWTdaDxdeDaOZ0AAi0zIluK3IJNUHfYWXeuzsFr7jrNOPhaPK4zJ7vecKkr1502XvmKff1zHCFtOkRZu/gLIg9TzIlJmc0wa3L95N89+NjD/V6Lh5PTndS8rBfYQYLOme9RbB+xxlsKf/3+vCV+NyDeu9fVM6WvQ+3863t4VP4zOotvcxVdv1Wlwwi9VWDLQCzDRr3KDMIobZHB7mvJNkVDoefGk5wUs2tXhnY9+fvY9dFS02uMT/41UVqwh7gUhO5djfVFHRdayxJ21L8sttGeG2JEyHGnq1BjlcLddu5pVyCmEWqUPcpj64jfIVOqw+cQAIw1qRCnzpwZKU5iHQ/W5ZILeG9PsxsbNSXaODZ7b9r9Tgzi3HLA4c/fsHiNPEDfpabgOw+JK5Mo7ety/SJrF/BdvcQEBpVR5tuNYT4jOzpUsSC7RUA5Jw7iaCUqIzB2q1eS/ZE7xuIVGRw3bIhj4Eq/UlyrQnhYTL1NDNtt85pW3zDw2TiMXqqnnbM7KBY56enU0A8cfzyMdqoQ0Az44PPbWGTifgCIbF6b0YGtYfnFQ9mRy4Rx/zAu/IYvy0Sza+qJ2Qx0cTP4EOQ0my6SH5CZpSsUQSrFb4cJPj6zbotqd1izV4zyT4cY3a8zuIlJ+lJ6xvbgzMFTMCPg3WcYwnQNLY2xYphOPyxuHiZWHq8OFn1dDCQhCtIBDGZLAxDR9Aw6Hjdw3FDuvNfmYhWOum2WTx43jEVbXFS3oQZauen9huitqkTOJUkeCZV0RokkVnZ0fUfnZCzJSWWMQ7O9gsM19ZDgBJ7xaXrwEvbgvTfgV6mqyrMX486syeVLHAB87rJJa8J7jB05DPbs7N4dKonmSGwy3yjS04LwMr/DpO7ardxpOLzOy6N837zjW3Ase+8nSMvHs2Apv96/swd4TrKSxtbp0DMTKl2VAItVKOjVbi1jcgrjQFEYSJDAVDGG2EGpisaQ2hrZRWBWKal6nHcfnllSVlLqRTsZH++6rVDkQrP8HePd7SAEmZpFvMo12zkAfGSYqdboJfBTyRWcCQboatIhFg65ms7g6B9j2avLg68Vaz49LTkFRvsOY3tkwg23CEelTkEEAcFvu5UU1aFQtMVfZTnM0DpzJq3H2fE7DR1PH1U61+K1ydUmsLFSTjWBzA8PM/0L8vgEV77bJT4vOCIpzFFmII/QN8MpKZEzHQW5EhRg2/BZ/xuJTo2TX6i6U61Q2PcosQnkBVwej4ZrCAi6nPWRaVQVadXtkf7gC8N5AEFENr5fVaVp70TVP6YIw5Qg5eOdsrpPCVOz2KxFBybMpFIOmOFUky1dy47zy3lSOl5bcnRbNE24nXq6OEvk7Fd4hBeXVxzKfRB8j15El68RDrSV/00UTtYd2QzE+WMne/b+SrBagvbuOd8UMiyNUEE5ozvF8jEKi6HEuNE0OE4QvBXoBS4Ci6JPHYkOUpSydZaGGlpbsh2i7WJDBB5jI+gwO7U6gPJA8g4O+Jw6ylw8ZekfswRopBWivgtWqLdD4ZxjDfAG+md0xRZVLJkouGmGO56heOAnSGm/GscS1kDbepROFHNqiNuXLXs6GdJJq/ckXwUQzYkuzwkJmyk6HNSGwPYELya60wASP03Yc5vq4ELZ+9+2hZ4zekplAdEe/1xuZarqmdWr5Mn4S1uo+LzQvGLQYaywCXH1ooCOPZqbgcdRDRvPg2LQbrMD2LO2ib7khRZRTbJGWGfyM4GXozDI/HmC0VN/fWVVRwISJgAZ2Nacr2k8kwZK97VDTChJgETt89Y6sOP2QJ9OQgDwA/0GxQxBDbNpOfxCw6aa9BjEnTgmdqfz03NTnagRKyYgyQRwOJduidF7p6TX2B0FiPKfzYAM9G3SS7DAnIRO0WvE8/l3Ihe3K+b4jwIDwsp1e2+bijGt52MP+8ltCLEEM6gzGQuk1SmDSzB0biCvf9EwwuAPuRnWECzCqmQzMGMXdPAyYc7fno+RQxPVIRxiFKHnUndpLJcTkfUVYlGGEg9EcCgLK9oaJ9mEK/432tRHzc+AWYGNhJs50JjragEICvBptJ5Z4pI1El9/VLB69ykln+LW+vrp7FZj8eZmo5tawuuUJoBJABh5XvABsb7Hgkhoew5nk/IQKvtf4iecZwldxByRB2KvUaMJa7N0oS1ZkE96cJWEM+gay2wF4rLR40zzA2vH5t4QY6PbSe83BJ+h4on09hUpBXkra88a2HnBweCdnSmHS7F3Omw9kZ6ccoJBBW7k9mQx5FVTQ8gD/hwz5EfzCcHNmY2fqwn+fFyDUYKXl35hTE0A73eUJjX9wR+e/7adc9n5s2w0CCmWnJnABTqHojeWffJBHOsgCE+dp6UESsW9/Rgaab04/4lp11jQyp6PVUO8rfmYdUDgJXSECm5PJSiJdlxxbkONh/d2oWki1HBJ6MG8BJQpg5jIADVzs+P4btzUWRXlC/6vqcZ1/OjM/OG8ycnLoT4nbRc81Zp2OF6oGSLezIEI/GJ5AVLF9zADcMP/O79mji/FUsYcLFCBJigzjh6KIbtlE9qcbiD2WwVwcgSEu6X/mQ6aVskezxD9frqUrQS4wKAws2Ecp0TnzCX28/aKREVq8PsFU55bmZ6Qd0fCFj/EoCe1PEBmEKUIOX+NkOpavfYd6iWHKoP8bJiq1wMAE64Z1cVorIFhApqK7dpfXHz9LE7M5fwR8Xt45OGESkyZUzbzDghqaOgZ4BzG1r//H7++bZf2SurSs50jFzkInzcHVpFsPEh5W6KCg8FglGTVvpVqjOrgyVMMf/UTLymv5Wr8/1+3J9cQXLvFZ8LFa7NTeghiJNzV1qwn5C8U08wsviVNBBnyXA/8CfBs5E2iF6JdmZzYuLfMGo74wDjnIFoaXsCwTAn3EpAQuYzYrhWssubOzNGYnYasNdeZLoS6kxhXiCSlkX9F80nb3jjTS4iEmO/5qxhIl9SiEhA30AhwgV3slW/0nTbMVFc6HEHORqHWhm30jJ1is/JvTm7478tUUDpMV1MwmzUwX3nGxi1rmuuo+Zf/x6+dGwHGPnWP8tr/q2hU4xtR4KJwTcL5ouVrgivlmBvZx55VsaqnULhTirxCN6DFgub3JT3FRfYLUwSgVrRSuuvfaGuB/O2BTcDcBddhB+LYF9lLTa2F5lLB5hvWpWEt2R4zl/vx+wttJvl8hlpI8Zh+7DqpWyY6EUZu8GtrG63sSbvaIhMrux8ba9MuxUkDtzxKfuDV/qydIJD1SA6WOaZ5A/icth5rbeNCicgYpBOYBUssEW/oQey8BebxC2bc57GceRlG8JjsVMFAd+gD2O5o30BvAw7CXg2fTS9TzRAjLSg4AK8IWBL6ISZ3zEF9sQPk0nJEY6m3/YSmkvQ7CMCSHaCyO8qj7v0OhdCNQBXJVH7tHMBbbzGXVJepdSFPGBvNyfm6gNwR9HlzY1DS+adSAAOyZ73qOAkCfwypFuk9YlG5qn5QCyW7dzjzrwYXtn2HvFPZqhjppLnMK/IyHpmZeA0fMiQYpScFwpLRUaTMsQLYBVOX0ArTXoPov9F/J6MxXO1ZwaIUiWwPm0bbY7Fnx1U7mbS05JtZjce+XUdQKcYQCHbeAxx2vIdwZidEFUCD+SfnjcRE7ySSvLDKyDjXZJVD0DM0Ix8FnT262wROHsTIZqFJGN/w1xMJbdwFDVYiHDQViGp8EV8SJVqMMkBqZSQeUZTVrzYMkZHhtDNVFfifAexZaYVhGuVlH4AzrxSkAkSfWfShEHztCAQLnI8dQTUp5uwdEAEBSVMndXS5HixVf+BaX8o/qnzuuUeOvMacpXa9RfZwrBk883n1wHI4CcY1QE7uMEMFB0IQLQxp5JxNm3HvT6RsVi+E4DATI4I8voyx3S3SUzr+QG6fCT2hn8W0kcAKeiKCVS/wU3cw5Lk1VwwW2oJpRyvyXCpJ8TQPh+C9RNKwXBVjRe4zux4h1xd6IWB9KpIP/M2YfHB1SzDpg7YSuDSWKx1oOMd+qgGmZfgWqsC6RnLvNohgIr20TMxLeAeTR3FM/beUGWKNdjzAZXIdAndqAQPGqCn76S7QDISLE8PdwDAnfydYtGAXjcGYrkOkREPPtIqIlJLzMr1x4QyeB0rAAE9E9MIiJ23tIH2DXCi4QT0/LGt9PEE6jzk26veW63/N0UphBdpa/ySVszgBu2Z0Xngnr3nsuKCLpQDJUoFtnsMalYkNSJ714PoBZJrzV65WWhw+9hycHqJL+sPjKPPXaVZ+aWczb6z57SIVlXus5qx2aVDkO7BwK3nIV4GvIkHsv6Ah/7AkCw9AHp8VCHYyO+oomgWrKoYhpHA0Ch6OUBX/aSFyK4X2/oDVOC5R2riD/HOOhZTIqyxdjUEwyOgMpE9pqDBuMf8VIT5O2aDoFkn9Ct2+mSkC1xdxyoaxb2Vz9BFOkNzGNMRzPQLggKIpx4KoXa2X0q+8tZGiNUh8/EcMVOQ0RAS4TmYkVsrbs4mJpQsaGfiAuJrC9Lr4knRldEL4gGSatYpCNPWFxp2YsVD++c88IKWm6m/5Xol7Qe5aplPILZHiu5O+wwizSO941DzfKIHugfDMgvbtl45Jy8kctOxtt/J7CRcEXTq1AWGuf+BsAXBKrpT6v5lG2lw11vSDvdXzIKyKatj2aEYGym4p3YcnTvTzt/lcaez3dyf5QiWtSBgjqTUVJGB3owCRqZX7IjO3/B2WZBrRxgbtY4U3ONl4SjfJx4mAil8r8ItZ+W1WIRYSbDdGgPCtqbdiaycSK1guEh72zWATlms1QIxPEPw8YVS9vdZzM7SFXrus/oKNQn4zmML/HmFQVyMFQDM2gaNO41i9Q85MbcsZr5cbXWxyqtNHsQ6Yc0irNAK6dxYwX7yFmJqVa+K9c2KiWHsmqtTg3uL8uAisWPxSwx826+4utKc0B46Q+jTEOYQELlKvDT4/IIAazbdnizDy0AGs1k1HZQucn+aKdbXsA6AOkBJfBNYJCJO9AQcgcewPe/IIeYK7qHim2io9+akrjlump1lMSPMS/I1GBD6Daf0itEemKzpl+S+jY1tsuWFIfEBqZIm1465g29ICvPSgn5gU38wXcLU1Ry6iIyqmo3iwdje9suJaZw8jooc007RAp8B5HegyWauA3dp2n2qwBMYFn6EMV7icQ3DV9CTpPZ+Hb+TmZ/LzT5K+NoINAVRetwZw5J3mZ0WnaYK91wEelxBSCHOzJ4dkMjNOmwQngqwdUccwP2y3psKdwD7my8SlWC64I1hHFIem3t2m6TnBoVNulNfmIUk5oUiF63wq5lnIRN+jUi+PZvy6QgHoJFYiIXfvfGyMawEG1CsJpnw5ivEFooqjEjJrwmp5s0v1uvZc00UqrzYx8xUrYYfCQZbWITuFyVjjhS6WDz3RnYoN8yIKnFRx5S0GQAT0CWRAz65NJ2zdJElkjtuHSqZ9kjPispSVg206VKBncI+Q+mcIwLbM2QPeYCOIaKblQMFILVcbSjXCDA2fVHg6wHZV2IbO0SixKQxCmAYvtnj2e4XQmMOdcV8RfLuIatq2hIijkpplAuoKndzZjDuYZMOZM97kDY1cRRSNZOfkVFyDsaPMzGVBuHRtQ0LuBIIXycTkqMcmJCAG6puTpGbU6vSpCvvW+zNYMO1ACa/bTw3sT+/tsnjRe6GhFalNSCyvJUpDeTRQ6D9SxaVnJ+08kEjBgIGhRuuCYesHFtXTkCN9BPObToinzNuREqjIZ5BIiDOJSSf9M0NgFY96V0yQalQA0bWHURQ9cy8VKrdoSqCBJrkI7zQYZgyTOqHIT1Djjm6L3qrJf3Yu8OAKg0W7vlMgDoVcGceTiPEGisi5lPyanY7iY1axCEGDmBqULHwDgTmA7cKO0yrKnui+EPO+JP+LXchk/jI3KLe3Hgspa8DouxguE4cp5gXisFIYhVWdBOxyrtSH/RKHiKd8IBQUQFf6J2G4S1iLoaae39u7UKqgCObeIjeWXK5enODLDq+wy6YBCdgwZLiV4fXgzpcI7bfaILkYvzLm33x9/tGkA9RsaGlMixlEuZsYO9LaEVDgDUgWOWeaGq0+DyX+gAJJi5+BndUsy/m6lJmJkxZYG6fTOEIW2TUwYrUk18HrkIJguACZLyB3BfF9vCWwxDpQ9g+9AQuooiLCJsGV+i/IMl2jKlt4zhtXKdnqtoE58BZz1sPc1UU2hFY4FeV+nXMrMeD3LjhqQXgL08LFWfWxURvMYbcBuy0UmwnQyuRSoNoErfNiaLM9op6ow0LL5mGbQVLXpG6sZe0A/Fu/5OnkPYeHrZZgBWK3MDGQ5EKRrD7ZfbnI60VgrgsUn+AVYh5gV/BvegIXvIJgCH/iG9KbGwPGtYnp/pyvcsGbT9m7USFvB0SoI4HVCOwvU2tXAhAOd3XUjbkSFFhLOPEHVvACpl7jdNpqwxcVwa8Epr5DjrAtnHRlS2hvMJW8l+xIyCgecCXmeSQzjCmCgAMxdShnx0BXRZo8craYJQ5CraI5J2k0bERtaWJDfDE9KDE8CtvuqeNlmIfqeDqjjVay1+SR3rjs0sPP/EmQ2SU9PBYef01MHE4DZWBWn48hlpJ5aQ69YSxUnQ1xHgDJeD0l1cJD5qtaZI/hTeJK7iZc6B5PK/RHVP3REgwpBPUnRCcVL1I12tnUMlkOKc+NGlmjG6RdFM1nhKpk9CxHkHvTJnH07G+H+Tg1LiTFOGIz7fxzhf60CgWxTcTp93UiX4JCdRFAzUotiOUTXdXKuCqyWVFMsR0A2ZmqPlVeE6cF590vFbxLny77XaszNawVt+WF3STpILGwZkSr0sxAYXDmZ5H1enuZ7Aa+OMIn1IYCxiNtPPxgMOTzRrZ/bgTC5s2Z+tcQfS885zzBmWY1qmxTx2Pxq9XGrKmo6VsS5RInhhXwXFzY7IC8RSJZQpnTp3xJDxb5xg+9eOBPRPgm1BEvdMsUDokqKRBDQbXlwPERb92/1P4vFOq89Q7yhREcjrAP7BHJqZ4Xu7cHGgFRuLGLZkD19h1n79Q0Ab6HJ3eI2fqHACq8oQa6EGBUJpYN5TokPBr4dnHGaTQVrH4xJubi3fsOs/MNFyewbHnOcem0Q5DKxX1ifEDvj9fvPrTZCq7459JH3+WagRVz42NCpUyRz9kdf/iSlaV8mwWxj1Z/4YZdnEUjoZLMivJMMBL13lsb9JtyfTRydOwI/CoAPOhSCSdPU2xfDemUqyUcloroeZED06VcWUxJ0OdD1e5+74d2C28N0FLkmRkmJoBDzqaWW7VlJp/DjUpTQXp8EJDksT/P1XvtiVJkuMIAqBaZO3Z///WmQxXIfYBEPPcPtM9VZkR7maqciFx4z9QuYHAKCQ/eOwfINP9GIkP4PEAf4wfVfiNZ57FfIxghgdnZuh/NY8DxOqznWFO8PV+KoSRCQyHxEm9Ii52UCdFsmaUbgTa3WfG5OIZ/3iGwDN/1q/NzfikjJp0dQyEH31Ckl8BQSSR0WMh+kzrhP96nqcuFh9KZpZHfFz7muaPDPc6D07/FLzL8sSpUqZ3eUBEjOxOaUgNuc2pl6JvAM5J4+kO7g7eKgQQi3YqovILuqVU8VCY+AVD90fb9J3LkwWD90DM/C+MUF9rAnZSNt0MOn1VHbicfmUY1Ei40qAgsllvqgKJSY3+0LuOi89tuxPYA1pLm69jPQKh51LNmX87JuF3Bg78Zic4Yb9A8KVdUvjebcGFn4gh7Jy3MNnY+8KjlFypXeC4VAQBiBErZLwdqFuwfARD1UZmGn6DB87w6UWhWlQ3Hr1jYxgdtLjCCjoZLlLH4KS2yQ6kYrTMebuda3BLP9icxl8GO0OWmZtEYOPmRAXj/DZQT/CFekXhcwD4B7Hi/GV9dQdiLoagdDkZWXgOvRmBKAxZkD1fKxJQZmGUIXTyLhipmDO64f/3t757M/1fjPY5HgpwtVBwn0Jw936ueDpxsaq8ZAB80CK+ebVrgG/qwrt6WCjybik0XOv7+fsJKrVAIY7UzT0BgTjuyiABm/keoSKifsTVnNPpCPsLeVAKIT+J9mmtj7QP+ablT/xtMeHvy75NxeVKmIzZXZPMEE9hPZpEPz3UIRlIX9iDmXQUqY+5TnobcvtGGTisdSb9wfQpyYpLI8qJvL8LpKRpejT6CH5216JSgmLhA8ZIBHIA2UO8NhbzDNFgB0V9Lw33kJkM+0kAOYBHn3NeYmybB92zGewnY6pYoK0HSSokNeWYgPUq0WhrjT4w6U82SWJSQiwiDdOgDot6gD6E3n2ZTuMaGDUTLjnaD3viU5xwi/wHBnnIBYZmRp3vevQXRmYQ7ZL806SRxEQ1d5KTCWXZFVkRHGx9OO60kLtBYk9Zky8V0nFuBGGM/S4s1FVVNCQVRPYx4M3zNwjFDSno3GSVpILk3g2U7AwQSNpcOsH+qHvUM3oeL2hl1GK3nO/W227SscO0RDIUzEE2vGkHkoFfsWUy4vrbgLOrz/9wp6uDn0LnnuXbINB1sUNUlWwMzUXTwoLbR/Vfi9Sl4F57Pv/Dlprva4GFgdApebvga4CUJmxwE8tCrG3WIEDqGcKP8G5jd019SCSvMgfmAZ94Cb0LejfhY/N9iUUEqwumnkRPaeJdezDrs0GeNX8SdAUya+fEQM2eq5m20R+aBJRwKLi0roaGj6mI9g302ju78/yze0Jl/MZIG6OP286tEwGsiGNSydMMGh5B97sLSTOAH2PjZ16QI5yCKW8u36gnnz/Sc3y4C+7iSD3N7TgBuLG+UbJ3U+gEtAoeQ+/O/K+Ocvpca+7hznzWa0iMrzF7xVdrMbcO+K6qjReWXcQK/wEVFhLg3diXBc/8Q79ZB8ZhpluFn7Own0D9KRe/45RUXlw4l8V8/tApayDMqb07nNpznLs7mqfY2R8yYyEqcHCrih39Of4rPVO1/zkRlTmhGRb/B8M8ixN9xjHkY1HP3yBIFxrkzdOIFvK1beh5cj11kqphzf+G1WysFeZEJvznPT+/PmN4rjEzwGReXxxIcXUAGi3x5Cxw5pjM/wLzu0dArvNsLy4OHENheFehs7wed+6vkXjNIOmKyaqcuo2gnjd/KhKFL0oVrX+0MCuz1xEIjzP4CYmEznkz8dReGZBXID7Rn5KBXPJGwwg4TSl0gA1o0ejIkLchAKjIAniL/Oz8QWr1fPJpdjZf6sMYARXTy+TGrtuFHyFSiqj+nZBBtuOJ1rXiFCrSUQpPun1yDAFZpTM0POPDKCUCCLpTjeztmw5tapxkIjbL/3p1e8ExrtmqeCIuEuHZVu0ACQ2Rd0F6HIiiyRJwICEa/Dvtk8IuqOrI2x31JI5LJnVfk1Sm3RUDpucfpnHkJnWLoSRwtUgixSSXVJJOX1OxLeMAWRKI8rp3/C9cSGPo5iAYJ/SkCe9rCpdoMMIEZybzkJnau1GSkV0x+Wp5w9nVkcpszPtBtZ3ceqJamjygvI6es7msiMPKXYKMuKh0ZCf5R/Vw0Z5oaWmAHcxY87EHSOsUZgFbSeyCdRp9Ef1mQUaMgOimoNubdLd+eYlL/CHptwa85BO9fuo42lGyIFlYfSNpmidnfPHopBu6zRbs0YRmr9AZNDCNNGWV4Zu6NNhgZdmVtFUmi6w0M3R31RiuKsZ7MMEYspdAOKMBTnZvklP87VO+PAmulCTqp0ATXfsKIQlhNKeQcYw8xz6PDeyJHWyJfNUejj2see7uOWBiannWlXn8TIZnJX/DIg+AC41vvYeGlAJtk5ycZRZVetrES969iJqGE8fkrYdhNAQ1GoP9CsVirs852VvxXUhK7LZN7/EM3/hds0U9WB2caU32djvyNSsVV671ONsnjddL2vi7ykL/GiR5dYkbUxLdK6phb21xA+Fkcxkwh0pATYKlGSCHZM73UecJZ65QRR6Bj7LDZ3RWEV0kkTqnLDt2pkkvmtC1iRuJFo7M1DT3mNy0UUiVJ9dSlePyRHH4nh0pZDUAbgmWZ0g0Mye9SnqEse2nQEnHZ5TUQlMpU4pFUMCbmcxq7CSQZ/d5nhZtQbro4HhjrB8VEnlqNGn7Ta8nMhgs7piNY9+WJrYErjNGJ5TU3V6oLMB+Wmybwlkm0khViFDwHm+NfcCJGSIZDpyuCnHExRZ7D7MZLMR1+2S0eAB1RGSGd5L2s+fbiPmiFyYHT4ihedj5vMl2IzC31cjpGvgNwX2BCuGcBKTGfUAHS3v9kqQgP+77wvM89sHNscjJMwD8Ce6VEGIoJa0y5qsFH/bRGHN8ANlvYEBOp8UF3U+VXRx1GFNFWn17jxOIl2QJrcnkTvgADpR7E9HCXGQUYbgHDbk4ufB9u6gxnHQKh9r66t9Y9Iot9UgOdWwTx38Rc5kEybszGQD3EyBPzBFOAF/hbAUmaIUTkuOCMieLPfBp6uZMtVr/Id+03YFwajGswc9R1OyeUbThTWNKAsjDZ2Eh4yDkG1bm6JGnE5c4HCqZ7mkSg36N0fXPsDttOL9x8jmFfKWPiz1YDZ+kvG/CByIme8zOMAGTwZwdo5aP+rRtsqGnpT5yZZaQSHiiyOaejsxOVicubpt7ZXjhd9gr+MTHmx/K9hPo2PjbnFcK2LtVIRNsDoQnzcwX6bxhEtkOixu3CpsdShBBRIqPLZqYwz1kWoS76QAl40xKhNTO6KCoe9GLXkLLLVEQwECWeDbBJkw1hbR/OAIcY0Rx3hzN32pv02NUwzOPUxPKwrPA1eIVpNHCUdTHIwD8+pJtwqNJ8cH7qdsD+CYVF/v37W1y30WY0aGs2ZzRLLHoBrFCSzre6ej4VWAgda6MRAHFw3M91LmASWWYJ4KhJTYxTQeT53SzpJNE1+d4j3pU4OEw2PW83t8dBCD3WM2Dha2zCBoX3PCBL8IMwjzMWS0Yj9PAoULvLNfgzLqylZT4LTVzdLRGJ3KZp6U3vk0jSFqdKpfrhDcM/yb+yNx62NggkJaxxc6TFsFCb90m24tJReuzEZ2Y9tSTK9TfyD7xtHHsV8mkCOzgnhBFXnT5oPvSePu4njmludYsfMAuV9e8kFG2+bDdI5EC9qDtOdE3nqjfC2v4Lu/bwWGWvbCdxi5ZGaUsekb3E15MpTshRRA7xBDQ4oSKKi0IMmZXu+FGjEaRKNH4/WoBMVhpeACxRk/wd9kq2UsaYkzxeA0daYazLx8s9HmSPRK4kRUlhGC5DjpiiMdc4eS0NRLMd/bcQC+OHkDrDTcR/5CJwR/g0ANnbOqnBQkPNm7ct6bvINOOKBzkHYxRp0peIShpzx0jwuGTrNJI4jBxVrqHIDGPhHegrX4G1CbAO1gQCejgQqHGm/sh26AuueZiePSnEA5/wCh376eVd6cFqKAisUt8bhZKUoW1e24jqwc4SaZLLT4zQf1wRp9zfjQYDZn6iY2QtglhPahGRxrwycRZ74nOz+Cni54de8pBDhgvPMPYDCByx2en7QUTLIEK8gmQn/m8e+bRbglWAx8OnfspHZkK34Qs1tmFtCMduPMPSfs7ctHNaqI+5rtnPtyt8WDhPxP9aAMLUrklhBDQ8KwwgqSVz3XLeBunZ+9oQIiP7BPJh0uPL/yZSceQDyMNnel/Oc4O5Ml1j2j2XJVht7eTFRDUIa3Ggm7iBJTQJD7cg4hf21GccAOqMn4WL7YHUIm8NIAaGuQ89rbrO3LDwT54cp28hwQ5E9wjTBp5rP2I1BzDC44JMBPuG0A4SjIg3gNr1DxoMomm35uKpDiMuaJ6mfWeSczRs3tijKTApNfTJ0cQ4t23Z8bHPXwVzgQkz9YXxdifufQwEKl0zQIR9USA4ZQClIQB5qHfXelK/cRIjLI++8RMQXvnIUgHu4+6ws9BpndUJQzbh0l0x/OHfr3PwLuL+03nAUyo+TqUEBfMwf5BjhrlqtgSxnmakf34NAsSKmDcXLJVK44/wI/Bx08bEJh35E1W5ehZv8rgaCxFZt6sH+KvqQgjH+jgzDz2Cc81moxsLPhKd2I31jjeTxxH4pkSipNWFKawwDnewcQBP8CLV8rQsCG98oPkJseBteTDpG0nj5wvBiJGWq3Px8qXirJ5b6dvEoPHaGDOtn6daZmsbXcnIXPRTExdHDwEVtdfXgROWM8XWacQzZ5kb6PMNPKFcnLvU1NyyYSgs80q1fKsBS1h6h0k0te3DfDDSbm2LqDJKwqDef2WRGbUNw54e617HY0oHwOpabZ1Hj/fdG65Fpl8NtciI69S3dssYQXgn/Wrie0vXlmJqpAagygEnITrF/wkCRE6+dgdqk6QHE9j4TvQIONo0kwm6vGAT50VOctbaSRxYpNyTuGL6+IOJosAnYb8/Z0QnuMzSoaZ8jHYZrDpwuG7BDdUOeTxOGKik1YF1yJndHJIllSubd4PmzuDd06TQ+2QSFbHAT4BWcj0/FvhCu+oBad7yf044o0f8FEBwg/4Lk5IPvKpuBtnb72aoWe7UJM/9pZp9CVaUadkMZELO5+YbAMSMlKFuirquqrtHnDhzis2DOqcTqL3QWrRK+Vql6s27viJTFc4lSVxlWz7tiv8huoAL8I95TMAuvXeL+xVTqndgdt5IFKZaLN4UZdUAEO/dYATuN+6KGwJAN8CH7BwjKdb0Fdr0WrfIaxu/IUTXFk2lCzHhttVlIJPsE/SVjPxo9TCmvJELOWyIQ7EVM4/wFPGHge0MoTZKKqDYVBeEHukD0dkOhCDGRQJ6jMn5R7NmUlN//od4eE8Wd8a2Xv25/PMrl0XMBdca3YPTYE35CSp1gcr2RzjyEEBlzfk65ttg3uRFlpu0GQogERNe32z49K/bK4XiSZn7SAHyf7xxkNzikyB1Nrq7IvvqFpoLfvkfAhenbrAPg1k84k0Nm1oUAQi6QhJnTsLQceuZvz1q1xIng2eATi1YI+K0EERkJh6Gr7e4F4mPN7nlCBJuI8n+EB6QpjeJH2K4Nl3nulwgkWGU55ytR87Q4jzAB/uoZ5zTveoBlokCfW+F6UFSBYT4fw0aDIoLw1uAxoyLyOjiDx6uDuaczaGXTPYfXRDl/+dxCc0YC43qyjVxMCAAEbsjAZm95B4ngfe0bznmJgECWB3d2PqsF+/bPhdVY+OuRkMM4rCrQQwn0a7ng0+l4m8Ot7Gc1DkntOQaX+FfbdOYC/lBgQHerlHIMEHfsk/Z9/E/AEcDfLrwLiFvQf3pEg1g1wcfvO7lhaezXyfACwQvHoCcmj3TZ2y9sxzzlv0ItjvduKJNzO/M8pqjnekJTJdMtdUzFoF9+aT88QZ6hl132UCJTLehwrUgPK2gcqa+W7icCfjT6+JqnIlfdJt9eczyqhB1idpJOAeFYflPmMq5MdoVhilMb3n9BbK4coYo7OwF4gVgRz7ZW5LyEW4MkVHm9koBjGLk3wOErSeqrp5Xxb0fPIkmpED7a7SzkmtA+gNewObKWVSTyvwXj5p9DO+69P1vz5FqmLozRrlA7wGxUmO5CTlJQR4LiCRFVI36NnFao4cw8TwfWc+ix+v3BBrYl9qsOca0QLFb7C93lUUgdg2xsGpG1tyEIsKIqhR/eW99OaqQ31eJ109M3Ft1F/hUDP3fIhx6dav9DfdOW+NesIJISp5D7HQR+ftyVmL214VRGFYA41AM8BZaCBhO3uXBgshBfHIwSs87MWUQjOUI+CN6xxI4GwYmCeCzdCzg0+KxCQsuCdGTtM2A3lBIzWkr+c/gE2DW2VEa9l68AD6mqeCV8PwN245LimBHc3he+sOQ0cUtY0bIyW17EOewUMf8slAceMkeDbHuHJfBIW81t9dA2MkROF30lo6jewp8rm5mb1PFwfVMT3U/pLi6HlLMqNFdMskcXbBckR7w6nzzMd+DQ8eYHXP28UivKJd7tJIAG3aAgcYv+99enez0QL+pplFcZo/vqS0zZRLbZgPnut6y1TwfqdFZh5xXMA8JSeFWJ5ChZ+S4et2wlbyDwELH+DQuOV4YAN9j2FSReDujwaChH+9ZAdgopjufxWuLCvAtOg7eQPFY8ol/Ap+iN+9PwkdvybiIvEBtGNccYl/NRUCZLXjzmXY/Tvel9RxQ1LaE+SrJtjytmVBR+MpAPqfLrWU49T3Nta9YNNipXdWQzw7jmj+465BvPtExFrdupfyQJkEI6BeYIKckX3Ol2Gh+IUVjOvEqHW4iKHrRKJ5BnVyMcVFJJIgyeMUkZPhvl+pmgSimkgB38nKwGfWGNibaLKkVw9Ho38eUvP3vIDp96PUJ38e4KEEn9DK77sC/v78RGaePT0hxp4l8a7HXu1jHrmLn2Aau4bV+At4s5Li4XKJqKPW7BvKm0tcXQDTM/CPR+DRJCA1V7HFsHUP8WNS/px9487KwZpO9GbhZd7KucCW0fSDAy/njmS6UdjEC07mTt0e4rH9zHOCweyV9CNW4ZMdCRN8Kc+kwh14rSuIT3lriH6ExcaQ61BDSXJEeeGc+uk4L9AdwOGVNNwXdGKvuZnPTGNyNuGHVNKd0Jvic/an0T6eUtz5kVsiGYXAX3YEHXa1mUMSTLVFokEP5t0zEuyDFT/HrwhrIn+MGi70a9Lfb0D5ITmaVka7IMgkQMYtZdDjec87I9i7R/OJLjwN1J4ynQSxGFXafuB8/klof0NQNlprBUP0a/qZ2Y0Vck2QT9UhEkGnE4C2anCDjZuMXVZDEhlwkCjsDraOBIOJ4vs7z5PZHwCNpYLkZxzyXqwrxSK/Nx8nuRrcxTabbKdpBykXdzhnzzypIdAIXbJmktabNWFMpbDBSt5nKGi9u8wUEblKTDsCl30066Pn4Z4N7MSF+YxA7ptFE3iMUyFi52pEvAP6HC7OCqKvFDp3JwZxzE+eq/iAR8P6tAINGJujtqMIMzziDSDpWjIX8Y8Vl2UgxpyKe28v8QM0iKbXQaoB9+fnpy8AvkVN4JgOKi3NZzWvviHqNbLa7A+wzzN7/8kvAJAKlAHWoMDnAtCJ6yACcKojFI6o4IIx1IcUuLRwmnNf68+BK7YIxl3PIl5UgQrzLwH7kbX44TPrlxjwAI9wLA4mxNcJqrep24KII+ktN+19JT4fLcUVcKwVYO4DHgx8xBYr2xRENeniGUCAItqq7br7K+s6kCSe0f7iASctuTAHJRnIz/FPKgkI8GMcjZIhRrdETsMWG1O3jt7sdgNOfnVGSzyNeAbizkzvFFhwxUlUGm796gtYFGAUEFWSfvSLmBoIWp+Y59xEOSZm/WYhr3+EJ3YdI5noQYWDtH61M2k9Ss8FI7jYqstaA0EgcaOVKZOjhGinyH1SjwbkhwBy6cd4rZEzCOef2LWOV/hzK7rb/rCnCZBCJFmgzFQWiM0vzJOxyR3y3AmJTHLLHZhNPqhDgFcvkuUXtPkt1crMxUpdu0MJsecK2MGsT0aHpA3LQLrDzIgI/Urfyup6tPJJ2m9FYrTKpdj9mDZGnsNVk3cAPMCyydpxkwaUDl6d4gO9/1gOrqLvSRpY4maxsOQn027VNgXIOgyil1COgAJfVbeLvCBsoYFplmV10IPLKQGlZdFx81WxpFkFn+vi4PWMpRtWjcxpPyLSKg6YGlrG26JP7I8FcFMxgJh/iUxS6+MBsdM+OcB3qo+VZp1w4Up7Chx/kQ234k9m3i8hcM+OC+WvFNycfSsMTcoq3PAika/FHU8ySlhvvXGdP+33EqXWP1/fZ/713ZL9uqiVJQJdlqoKa1KcJo9zu6cZyijMRcQtTHqIwUiYCLK+kSAy4dR81YC+4BoSWmVjVGNDUOYet8sfUAkPtiUNGHn62YX32M8mnX0jkIDsk+tZ9P6AQ/8z83NI7wOu8QH2RH+GJ7ceEiHKzsiEvTwVH98xN6FpxILcWx7sxAHvAlsHDJkeSAjrGnGDvzIg5+6BEiHFTe0KQ0iWeUIMd9fKjC8Egupl/lxJFwAcn2ngQ7TJSSKRcWrwgRzNNx+sJ416jjCMtYPJEBwtwBeY/JU0ZsbSiTzLduJmDgLMxgwnqirqvtBfjj6Fm6k/Y9z5SHiNT880U9TrQ8xyaUNTDN48gJQM/HTOtuSDlAy0E7QR2iSVtU/UoRWxFM+AI75KzH7PR+Ps4bDcaVKoNN7ATT01iu368Y2oAQ/5wY3Jj9DBmQAGcopDiDp7IiMqbyOBj9cteRILJAlLP5Zr/1MUTThY5V4KULRnSd+KF8axp7ZfpzEbPXuCZHvmiXSmx4FQ//4c4Kl4qNXG1pMag1T4b99DMZjfhDkdnJ3AMbncluwsQ7aaVq633LQC98DqpLmLuAcpDb6hkJUbT4Z3RzIhanfJkRuyDMGvqAM/xEoR2/AN0ulNTdf2/KYuzfPxLqIdDvroWViaNHd3ApbDyVFLPHNVsWoEoW5aHEuLGtvc5AEcxYT5eHe+jHqK9Wert+ZiMV8TeVCvclk9+7ehJCHvA9GlIYeWXT84GIQOy+lPPNAhvcea5Q4qpiTjO/1O+kvWq29Jx99WboAMA3q4IJLdCTPdIxunV9BebC5H4KUAY0wSi++HB4g5dY6ediH5l0B3CslZ7ElUK5wqN1q6U7Y+Mz3mY78xoXoNK2lOYQObg5P/mP8NF3+NBJzmcEIvMLrZJqwVS8CJJf4Rj61FZnyCkxAnkllmAqwdPjEgehaO+ifl9GuO+Jx6efuCSZs1LGa/BXrxnS/eetm0V4MWmsYmixEXx5tEs+4V6qbkCEuV/dw0j5IrvG5UMGOfJrs3B6NlWjVIRP278AOeVj5ERIA3tkOtQfNfJSeZqjE1g603BJWC9Ks2t3EJnnXC1bnMIGQywju8l4dT0EwbcanhFvJfQTLh1J/B72QChyrbh6Ju+Tyxnco583Ta/1C2yGPMLywL2m+VC3RnkH2BMhTVt+I6HXgHAjE52RzHiYJZf4/S1Kap6Qp+B+a/QoM4ZvO+ki/dqLBt9lPGVkajh6VR8Df2UKElgunc14ZzhaaFjF/ilr0TmYBJ/EYa3QiSK+DKbUuGV21MvGHjZj6mgop+pmE+hhDgdZIU24GEq+ttsyITuh6RTDAGr/QHx31BLhLK9dtkzvYQzGmYryUQ8OgqjiOpAanJOedLL5gWwmombz3SIt86J/+7Bq6lZRlbZDvT01XoqohErKUMlu36LNMeWX8X2s3Y6bi2lL7M3XqyNZKv3HI/a5Gh6E9rySylSB7c/cgvCLHfFIAWLeWIQuZ0TBDqD8aCD8NO9pl2RsAtKlvo3cJ0wwJcNC9HASqPB8lN+GSuB6JKvA6Di2IqXVx/yI7mXP9rEpyIFHY6JamWhp4GJho2ztLDkf9n/ptC3/YD7+5rfuwfarzvkLsvkyGKn2ymO4qRB38rAiKAv4tKbeCIKx7ozXvKFlqDOImbz1HRw4wP9New+S88uz9mUuePNLskcNB0VAidEco/dxpXxCl7m0wbiZMgHfx5gBcYMAFzxx7vOVxp6EPyTXxBVhyWxGmxvxSMjBVbYRIaETIJBvARPot/YZMH+JzzF34MLs7o0WYEadZuJlgcQraS1mwdwvTa94SpByX7bUSubkZqFF8wNe+eyQfVnusTKPKYpdnwx/QxDXnIJi1Q2903xl+S4p4A5ybAE6wlTsyW8YgPGhgyM6VrrSK08bQRNfdUYADqY/+FBL/w2FXXHL+jB+e76GvOGe56Bp+9gUUsMux7S6969yUa5jn+KyUJSnsyZZnGgT7J/La/RFljVVPMMRxkVSJLdPTTjHZxFX5j/2S15mhIrb9+6yJywJueUjlSxTkd6RRUeYFQBoatyfGeI+JZnOEAErR+87SbL7f3KDEX6Od3/uRyWtLAuPItXPibDIo9nz0/EQUc65yYV7n7Ps+z794UUjivl0nhG2MFE9dvjSLnsEfaxVPT1ez+xH50H7VB7v4kQYiZuPQLaeaaFLEYgMy4O19BC1AXTQTNooy/uPDASU8K7p55lBTjynt57cwclL3fBgqerLDoL4NaRUkF8uP9iyESxbs/CyQybzQ8C3SWhEkVKB3GO5+uuCmxqVWy6FvqwzIe4G/jJvixf+ozpU1/8Jx4LJIwyQYziEqFnW03xncGSAuhYHHMkNZjPxhH7R0N6uI1RWsXm/x+xmMAc+lPCzauWT/ULVwMooRYz5y/EDTYfYD31KO0o+EanPUVmkQ2xQm5trEsl4BKFWFg55ltRKQGs/h7ezhh31TaHartScg0DPKx9saSa28eITq7vWcIAagVFDak0V92gtT1MvqD3+nLE5lTasVifTHkYCNe/zL9UU9KrMzCQRNfCvZhEkj7b0/6ohwdvHeGkAyCKfxJwMvGfvewC+3Ouquj18ouj/klYOpJxi7DMZcTsCpYyip7iAMGEo5mclE2GFImii04HehesPXal5kzN6PB0heJHLOCKHLg170mF3pS0oZ4t9oTigvcDp5gMlARrcULdrUHfYJ4DwuR4/2LFtqVl+ZsZwXYkVwgA+PjqOrBiuJrua/L7VzVH8FExcEnKFRbiJRYqcUvVKEQC+mT8pbSVaXqs+9mT4o0gbeoF7T7k5rcmSnRjjnVYiGyhA269Mi9mfM1Ci3dwtgcfl7/qM9Hxwf9G/38QAi70KL5x4cYZ0fceKIvqeAmERYxF2dbL4GXX1qAXOi58gpW0pPTRt3gpLxvdfu9EfNFNghk35ccCfBlKo5uU4KWceHcJMuyK0gLjFhaLMKYxqpRcMap5sul5+kxBbImeMlXEhvvNvL62p+DylsoI/dVFaGtPmEMuTzwA53+sXytuJBxYZt+++uRosxk/t4/H1Vgg7DETLe1hjp45TEVOroXK7f2wlz5TnmFp+BdlmBY6F0c8PbEeHxw+C4/XC5JngMM/468mQe+zyMq57n052cvnMkPEnEZkAO0Jji2oc0wxnD32OuEGBjSnvSxiOcdw+5thSY8WV4RomXjngz+EHByO90+TNZLRP9lhOg5SanIgIkus6aAJRXexis/5hsDNf0DKEftYp52ggIwnBMmT9bq+Ei7t0fP875+uMvP5gY7m6JpLcRhBpJ74snsQjyP9FOdHWQee3QyY4ONeqkd9rqZW9APlDxj0sCrHNfpPSVzNbPYR3pj9o8byxtHERHRaM5VRnxPaMOIpA01GJLsKt7G3HQnMJKu0QSFTS6cM2qqzz8IZOZlZqRRLQTuQoeYWOZ0WBlYmoII0pAe6rXdhHAtNkFVNoptdDgHFx7yNeeeGDaGOm0vZ+3PZLW4LlseSa8BNP/77HlyKV9xA+HPaIHdFSdw0BaKkFiX37R6DRXHUpTy9yAgv4M88vDNhnwKPB0oa52bgWrcnix4AwxkWuq1UaeRxQSPBBEeKX1HO16DbbkI71A/u8+d8IfFiMe0zkO9h4pKLeEUGsp3wnRcSjzwKC5olOJk0WHb0hy7g+Mw8c5GnTt4Fn5mjq0JohMqYgKQJIgx4UgXwd2ElmT9n90E7EzcdW4rYPDgjJ5i5eYMADyJMrOWuWIhzM86A4t5aUmKfyQDp8ZtcAMSEKSGB0S9elWmZwvODLmXqY2hVvZKOPceD4P8iHn7jrA1JVHE2q0bTLwUHnTQSoDAScueFiblIY4CGWILHZofMQGluYGj9S6gaFAdtpiQKNOPPpuIr0nWyuM9mvQ2b6xQMeASGZ1ZKmJ37zC7TKBR6EInRY7Jj//WKhwZDc4wZXmOdyZDuIOh+hGxOgio9eQr5X/goXSw5BGfKu45imYoHQ6g2sxBTFiFOEErWvA8qm7ILelxNRV7hdBF4BBbW9S+I1R7GfC4oGOnd4B3YhcfwXVWhCYhNrVXxN7hQDeY6Ea4RN469TGOvWoc5N4Pn/NXgEdw0rFdPcJtm7r+LyjakOaIMxF7NMc++EIkKu3FkjNYUJIxg5QKMVHuyPKsMfJJLYWSsrHcLZae00mwp+hGJPZ0ZlRHkCCWLUypFgqruXdY6PTj+JiyV2P4qXTH39OvV3kvXSS6K5LrroLYGw5wp63keZZa4EWqKaQ5D82Fr2Qv2o/8V5CIQ4238rYEWie1X1mQ6D+V+OPAwyLAyR39+nubou8rqDOSb8WOZMzvEGi+xdMs63QKW35dMmeRXKhYjF74KgFsnqGPGeBdF/Djb6UKAeeKTGEQ65Kvv5QFCbAS6AV+FUg8+A9InSubhVO/KNdX6eNrr+t/zurC7/Psmu09S3C5tmabSFZeL+Oa5srhyoUk9fHyAYvWPvKtrLMFDFZFczucfPawAGGbIyVSgbxQy3nSWJwOhPepWsub+WEqaKGOa4orDCAPYOJUtONop7vrLheFDq/oNwCBxK3bmP6c2+yllMoZEuQ+TS8OON3PwX4c7iREUb/pJaaKhK0lLPkckxEUA4JfPsPH+jlr8+wa5uuXmR+YhQ3wj3hWIt7+THe1dpV4udGB2pK1/GsgDY3hj/4YP7gMl7ngMyYw0ouVk1GPc2N5E5fy1M5uI6/ZL/0YiBsSGOB1rpAkeRPXZzxM/nSiaiOwUWfvpRuAgCgbsHlZ/B1Z8EKK9+7AfzQpOmNosDs9bUF9cxTgZFYQHdEXtuj1f/p474KjeBCHHvusk4Qd60x5Zmru+eR6LlOOt2NOuSd7MNz9sWzEyofPjPcEAM0VOEzks4wVZnc5iWKI0S9gJBqpjJN7KcuzeSEVuq7xgLM4eb8MT6YJOpIdnTYhJXc8Zg+b/uj+wodeNN1p+IVzfBbzSOsDUkzSyflUguj8Q6+VZIjUPeAnwYJYDFktcQSwm3ScXJs5TB9NrLDADHE2051v5QzTUpLlDQ1sfvAc/yhzYXmRpfYzIZni/wnaxzsKN9BXtIB0Gl92edKPU+IZEu8Rk5tPt81ZOzZ3ALQPZxB9HwhI2NOgVZ7dYDOpCWAlOHXBJ67oXpE+HGYAGpAp1462Y9/l85HOCaQXW/ZmVDBioc+5EGQXxJ687Frzo4+qiCQmRd0r1tw1B1KUerFYLU6CViszJe764RZL00f35YTybZAtAWc8he9Yu2zJuJ5rS919RrVntXDE4AMYOBlSPJDnpEIUaH7uJFEne2QjdkplArk16CBKNX2R+igbRD7dNHphNPS96mPHjJxUo9Oszew2IkoNR8+WDeJnPsZVgEl2yPRuMXLWL1UTQ/UIvff1xYvCL6eaoPDX+zwTmPPh4/KbmH0Ci9IHeoCfR08LIr1RRZidIzPs+cZIRHCaVcpmCYg2nIlp4a1l2jJHiVnFAjOmU0EsxPeE8SjiGDdXQFwcP0NGcDwkKPvO0UsoC8Z+xSeZemr1HLjl9kr3IC2hwZYMXXrUwbn6AXRrfMsXY+Hh01OxKlIbxgCVvE9XQkG7AtUhiAiFCSk/U6AVPf5DLDatrE8hV3G4dIURcARAAB5UdKY6InnvF4FdEJPvlygcOAOFl3gdmpGvCVVLsI/G9/pgYB38CSMeqv6DzIEWuHRGYff8L1beFZMvpw5h+G5xCYy6bJVDY3grNNzBtGm6qsHIBdH76AsOh05pPUDwSazZQ94SKv8htrXfM3sCgelEmmoQPre6y6mS1ZBZONw4FJjDwxMa0T0M8694K3YQD7V7KiO9PVuq1eNhGJXbnEZfSwIe+FSresu4fk09TvoZ1h3mXYiodW3xHhyng+0XMG/GMR7W01OMKogzQYclanYwkWG/3kq/8O0C83wGpfTT0MmAfcrzCKx3+sJTYMxdbMkugYenylHYRMbjFOAKTqhr9P32bfYd6HGLZKRReYKakXB97+m1sut0Vce3NMEX3k/1rr6JJqtuqtJLc9zuUxHpfUv0/Pl71KKYG8AvpVeOJq8xV09fUdGV+7eey8/4Ngs1c1N2BbH3txLgE0q9HmYg6oM8gOSrfC/SGMoXL6N+2xxuudlM/2DfH33mGNy/ZwcgntHjPY/tPT/mJ1kEQx4fMnJzp1bLm3bToZ7OKyamUngsnCZAeuSXxIuVcfB2TzTqMaIXVFKBDVRX2LhnZ8EDWltV2DrmAWI07954VBM+9uM7+pgXj7hyqhRGmtsH6Dv9x3/JD5KJm2sMOpyRjl96EfsfFpjjwxNJL4HD5bHjSrma7xLhAk50mV77Ea6YIsKDjDgNxoZvGk8yc9L8yaB3V755wunPX6+Jj/ctZpKpuZFpKhEHkzi894SseDkP/IJjv6gLQF7bnbP0nx4U8b3lmqJzNCfvHhGKiGPyGZ2NYTZsQZ90nIpFFBISoi3MUJzHuZCBwF6wrxIRqQBjXnu0bmy/JlCQd3mBcxiUTgciLKU70scJS+mH7XbmmsYeM8GMI3GxCV9Kr9O7Hrat1URFN+eLIzAnMsl7i5eCFO6xfvcnpZky3vCF8whnOEGKpRx5OdihuTcRJp3S94xitbQLpw87+fmtKs8ad9ZR8O5FYr4dDM9545k2vPbEQXnNSPsFRuxTeQEZ9Zq0GceB3fiQ0tiIbNs2AWriQD23kYtu7NZAkNR4ORvQWqNHndD0A9I+LosI0neBhGTOm1TfO9bGqCQ8Ks3gl/Gg8nZlx3J6VPBy7c0F1vT2HsQZjxVyLCiSGv6YljItSNZnqioNOYPj7A4ly4uJ8E0B/m1t4idMd9Syxq6YJ/+Z4f9kZE6cSCao6vv6b6W4ofc/0rubBmz0LDpaG3u2JS+QnVXKFM88yasJ4zQzu0cAeIofkxuI1zsJV7HW1rzwFzNr0cxbDYd4hvlo7N827NGzu/C5Is33qo95TXs5AKJxa912X/GtnwzokBw+FyhM1hkrXSGXK+Q/h08IyRrpQlgE5/DBRbJ066duFmTx9DISB4QyAN7RHw3W9dzfkvZWHhWA4b6k9F5VVwFJ388QERC+6Alvs4fIcHqi5Z5uTUA+LYro//xG3111fFEH8bP2lW103eUeTi0ydQbnf8+Hz2uLe6J6Hg4e+7UTWSz4B8lCCX7My4IC1AsilECq/4iv0UMu7rtMHdvgWXlp9SEwefC4PgSgd9aSz145xrfAbcZKzvHUlJLipEqKAEL9P1N76NawmorL9xGCgDL6nCD53MIJsXu5rUuup1L69q2YGef0frPg47wsbptuJ3ObsL+thyvxbyIBjNYnd9JL6jZzNNdknFJ+7yiYAFUZJNEfelNoydK2vA1Sfg0TPBzNqcxYg0Zu1k3/fM7+gBAsaN1/neXLMoSlzXKX7TUvlQRB0rUQRVeqlKYm06mLlAk2eZsI8xYE3xcOKYK2kZqgjPbcLrMVbj2sWXkAcFqZ909kv2PodfNNtu8Xv5u391K2U0688sh9iPf3KThsxMa3t8xn85VMNdJD+DW1FW5gy/Vb5u+1evB2SxdOVyv0YE/46oqcJXxPx/yXKFBZ8jGZfSHHItIEBFm5vV901K4WMJ/F7hq0MMIsD4+T6PjsLucZarn2rqURM+5juZSQvhsRAEeOujgZoSXNblIpuP7ZTScnYcyT6iaIDdz+294HT1qBm5G7SJZ9mgU4FqDAGhG1L/auyioxDJ+Mm+Uh1o1ZCDYSVDUjddBA+ZyZvba9PHOz14izts+69AYzL+1duo3EHgjnJ2rB0Qd64c5HdKa5QN14KAkOclDidPcQTH5hii2jCdEgDG/N+JuiqmhelbSpoY+S3+6Taz8ohY/Bc7wan0MhkwLmYOlJGZIUiDd5zyyxuVU7tlytAhNKCkfoA3SYZ7uGNy3rqHlFm7/VzbN4RVF6okT0dEgjLvVOAfKssNek2RInDu2TXFcxw1DQoYLFsw6OhqAePMYP8Ky/XZ+MaMG5Tj4JUxQwZioTmbzQz49jb9YcGenO6yPNPJ8oBo12aZHNpvcgufvGtM2yrfgP9XPwPZWiw8p/VG7Cq7aCgBM1M+Ga4uvaIrRt4ymAg73NEva8LAAdyCGqA1DAan0okjH7v8BsOV+p5Yo4TNjWaX5QRp6+cAz2P7drUa7RkGmg4Xl9RhI+NomjjOwgOMpQgJrDSgco0Kzg9bENnLMXtmgyAZkaU4hGK19mMMYbbCRZ2tFpOhp7n/x8EYySxMe7GZKcChNNBG+mF0rJLBREP9f0eANCLzGRf6rBamtqQmiZd5bntuNKQ9B/lLeGCAXNIWYwwEs81xuwjAM91L2PEeoEHaTWUbsO1rRVflSWnGcUDfejx7sj5zaNychEBPLAnyjLoWNwpMxHXP+N0nhPKHBH8WLQ33HxhX4I4cHHfIkHrTV9I7p7NbUDJogh5+BnAfgu6Qx3J87WD5hrd3H0cDDR36ayTM8KZ4Y5k6eV+e8EyuPpbKkUZRZB+AJ9zYIV67s0Krb547dqbjxacfc4J0XiBmyf3MuJId6eiTDZwiPv98YvKlqPLqP+VBi+RUiucdIP5lQCZFLiA5w8bvzWCWb9q5VSPErW4nQxp/4BA+Zu/NBEwm0WG9aFlL49D0fw4qFJvWViiSe5/hmJedY6AJA2sjhU9kve1yJ3Jj6hESh5ownIok9Lk2fe+u9u8TWySvLh5VSGbfXT2FUjrlum5SB1K5svYdOSjg2uZqPuvIu3x0hlYMUqQ/Atkh7BoUJbldvjwvKV+wOrctOQv3iuk8GC1r9AjJDBnvLWbdCUGD90X2tHlPJb9cUVFgyBwM7ih4B99riK0eLTWU5PKm3bi3XOVxxYe7sj6L9VZcI+lrFTb8ZQFmxlPwPbFPVOisbjiBTG5YAYyPG/1t6kCaVAKFBvkHmYqZbXt30BFML4eqIT1Xob1m3Zuhemd6qyX5tA/D1sdwADg4q9WZ1XPolDPzsVPHsw8Vcb4vb2Ob3bTCOR/d+S4hi/I7eNSwzBZfnjNm46Esv7RU3SZY+i1QVLsgKdoW0wjaf5YPn7uMMSu9Lr2KbdMwSthtkStw1bDrdctj10fAlPbFt0xHrEVLHQg56QOWiOvbudNRvu0cYzg4YCbiw/idNKleLxLGyf0zVyAOxu0MqzyFjeXSbQmhmzmuPGlqL8QKAmNisey5M+hwGYcvpEAmWKFHFOcNNvrEresl6/8IBJL4XkxXWj4Fkfe+OtNAgcROi/BxzhNDyR9J1LLsp+Uj7uCnz39ZLyakb0JksE0JOgbgKMZiMWmbOn594GtxDpc1I+VbuZNefSXABHDtIPQI26gGC/Xs0/JdToDP9i2cpUOKLWJ5Hm0HyyZZMjYLwCV6NdECMku9Iw9JjrxegTw3OGCSydQmO9xBmRZMDZPJZ0arYROWwO463b40uwBgXcxYiDeYEISJKAkHT2xCtFFNFjJscV2rc3qV3VRWdqk79J2cBZjDycnyQskF8hi9fgZhgGD+4UCgK6jXg+f6jN2A2TVhB+lgTe9UM66kLGG7BkrX32aiTw7L3OWN9kCsj4Njkqla/IM6ojuyWjhYndppkw4q0wrCBHPTdq3V6DWM2zG9c1M8/iEc1E8DB48FmMQD4nqm7O650wHmfBM6LAExilF1MivbN+tjjIvnz0mEth1/DMA/ushwSUnpN8TgJbKZ+tBJrY07LAGeZbDCWq5fz8JEsRfCKF0vNcvoHi5/XJ8KwT1aOErRRHVIbirE9r8fDdCAlXIIqTKWJuCjptYHs+ROQCaRbXtbnHsJQ4hR5cbJQNAesbX0M2uUpYPUygKB8kHQzO4KoEsqTrr6IDDZ8NrVVzEZTOJ5FfLJ0SgQozzLrwOIxhx13ZnD/4Hq27g4PrAmI7g1xXT5Ya8Sx/omYM8jGCMT2AhpFlUkqOqvECqlGKD72khyXwzGzn1j3ALHpGke3x8/yrjWHgW8/FRsO4H2+GycNGzgfxp2AMa8mm7CTdFED79oDLMMyJ6klNygrgKYMt2pTeBwsM1dcR1UegQlKcTSqowuIeSZa4zYu+AhCGa4r2KYUbNZkjDqYTTm20ule34Svz8y+uj9rxhMk9gM4DGd9Ap5YzUWDzDEQet00hPZj4ax8C0E+IRZPqDLL4yKPreq852GiOZ8m0UCgirMHZxh2FGZWFzwLgG/tZv2ZrfTJ4f0/sZ+/wI/Jj/rRRbNBN80tqhM05lxmIhLhwBxXf7mhbwy6DecX6CUSjdwPXO+kgzcEgPgEkYhjkCtPeDyqq4PitcvZy4zS8P+niOJHLAxzvEpZgTi1fJL1nPQTxJMocTRoqiVBOI0UcIiZTo95y9SPDYdIEuaX/ZvZPRpbBwPBz0CFIm6m6lTmj+pRtrSxN/H9BDuwTrUqlL+SpJ5FwsOdMQJmNo5baaOITM0WTmSsSZISOwvqCVRbs5vC0L87uuZoLZ3JRmwsa18eyyxppErUSHOtX2qL8fBYxp0TXNJ+nGiMEDHCHEwcmSgPutdKmWGmxjO/IivYpcM3oqGauOqPA/+Fig+U2sde3ZifmNjFhB6ZHLL+aNV2+q6kOwRa+QB9q3rTrjSQx1yfy7YjKOqRFQ/jS9njRgAYa9mZbuRP2itb4L/k0CcYpHMsB5Tk+hNdvSd5z9ACMuDZjJhPUsQ/+DLg4B6D+GK5GBhrsW2WhvPvkdvQLIJQCkNbxY+/wPdcJhVAObTHDwOZA/bx+LUjyzmCXj7cTjJT0idBAWRdh6/bP4u9MWDbYbqmhIDPP0vCRYH+2USQbko0cLGtw2azldNyvOWAYwH98/k6mzpJkuMFDvkw2WIW7XvzpnhAKarFznYDMLqKvS5d5JbA3ScMcKyARsammlWmrqSPT6dbwl1zOH4rYyUS5HHneN9W6ye4B/G/3X03o3vc/rTi5Kz7rjUhvd2CvEniZHapdTeQkLXX38nHhyQA8YTAiOIgau6ffonPTWO3soqE8+fn1rynIbX7DMTgXuiBQ94S+qkjM13URDiZJXaShY1jpvsWOeiTv+9hSuAllH+aSq8a6Gll+d52DE5Xlkviawbum+elrc40aWotOXhvKF2tFf54y9PViLG53ywjD7uc3lKOqFI9h6TEw0olwM4+hlA+IkLo7wgRHnHQUO7k+2eezsceJoz0r4YW0jTL7Roiu7xw3Bu3VZCI1GOsHPo3G9wTh25WyVHLRpT4ifaBMhkrLlydxIwfv9Kt475W7WzB81VEmIAi94A18HIqMjn3jzUgvXsA0rWxu3cbj5AZcsDmnJGu7ArgN6Ws+PbQ+Vl7XCLuayvWNCT5GfOfMXJ74sd/2qiqPa3cSn0KkBZTwGOAkniW66qeFek9qI1moxc4kTD5VXnT8DyJ2f+ZGXpIDnLIZ0GbTrSlvktG5bG60AybWAmEAEGf3ncEeBqRwZrjKUbP0giSPHwHW0VyvLEQPY8QK1mdc0JDBy+35zyWVTsGLBQc2OR0FAxCfi0hVJZq4iDh8UaA18d4l4Cr3z+QGsKSP+wzjE0xVQXqcKVExv+YRXzMRnFgxO0EC28KJ+YK+F2hxOOJbqw7sqZLlj3BOhJZpBfnlCHpEZ/+HsfzSB8AMj2sYMBuwcetGbFMb0bHl0gNsBnbYHLzg5yJq0+h0GXg6tYkUhvDR0KhVt/eKUwmxRDS9I27wNZnWaVUfXRovul1UJe6AO6UksRAisKpJCeYUbgOZXC4UwREuDOySddfGEDHbk3tcnASgm9Ogvnhm043mQ2VShJlUCfJXf1CBXrindKrZj/ss3BwP96i6CzsvuHcAL3XGixanZAvgTV8YupMo0n6v8Rg5u6IBOdmT8bNmEe1tpOKIlWgPktC7QK3wt/2sWji49RqCx9y5uauMErJvXGtsa4BBY3fXm2FtPZC3Ltgqfxj3slmdPb4Wi6Pi2yicEYawWFWTD9EI9VzoPoPEZsBMhPA14BbSLvh9UQn2x5sRW97Fob7aJJVkCXb+aYbzuDKGnDzb6gRyC/3c6dlB/np3LxV9Fw+mCWMtPkjSzlCp90qAwEq7+3c3vHTOwGgnsPDh16pxjQRxbWW1hgS7q6l2fJdQse5lyf7Wkhy+dV665BJ237yvHNOIMo13qgnKm0V9iezC81Aafnaf9c+fP//TKHjzQt43KiJa74J6If2h/+KVuaU2mCBdIwU8zMJhQN9cg0VC5JFwMkgJGzO9Ar/FFs6muF56QZwwQBm7RAG5x/Es3uEswA2l/o4+4M+FeuYiQqJ9jJl+QMgTINYG9uSxVKK6emY99ktASuCrFwsf6QP/IOjvrgjMH1DcF2TK8YfOtMM1K+u5OVHpQGe4UCQv5J2EBBBHM86fxzfk6Mn5tVuCFT3SvCkES6YGyh1vRrtkDmvLjhgMwB+p4+GBPeX0k9LE9RlNFoWVvnkiRcz19JSWhYbu59/4Oy8ZtZwcQLM4LqISkHfhnVElux0iGnIqHw8fwZkUMXDCCyJrK7xYwM8XCqrisrpggviMDoyrSF6w1b91gKSpBMKhZs+C9SAi6Dc8+mOe8H5ngQilA2buzhRF/GUwXJzr+yLvtaCUcY2h+25REVCalECKbXxTpse26QU84qmcxQuBfMJWezmUo4/a4QMBG5hqyMInixuQyxny7Et6nlkTPjVyLtcZyzUGHgaCPZI4z55D+9FjAiUFlvMxT1QKd7b4R4DxGud5HkPelxNrbCa82VbQkhAsEIHGzF6CkA4ZqCn56WiXkpwq+P0ahZOUMcDrVEQuCxjMGzGDJqR8M2JmvZeIfqjXK+PsgtVPdYxNfj7R18bV1XcK9qg3LkeVS2GlmIxjGbL0BDMGNxCk9SFlH6wlXmI0PCALSSHvLVxgb3U2fkfAGYVG8EhCrw01TmWQdAWIOJJIHYvNpVnOd/Bin7RGgW6MHX7yzIJEP/MBDlN3xyEyA1A4RBzBQxxrp3xKZ6WJ3owzyx2Kx7sJxDQWxSk3V36zS8qJxtQ9Xes95z1Im52UjlCHHE7NRY7WOS89zzQlZkM/iK3EEFcC6VQxN08mkm7jOnlcy0+uMxtdmVLnCYU7j9F4yjKV8ePFCJNas9DbFpeUJ2904/QdtoYBwW+m/t7mk+JzTPm1RZx4nJq/XtiBd2ejIHZP8ojvD+4YwGwUJjMZ2L0Fe3G2W3cMI58ufF/sOnNThqCgfP5HjWm65E1BcPwHsAyjn7sasJBgr/INkfrB24AfKrUSr7Ci9S3r1bnHczAztuSN36QvKlbW0N2uKLsS/B1Fv8fMD2JpvhA5bl4Tcj5l0kdr9vyeSOAx/R4BfVHstjRlu3vcQerVY/rKmTHgSRHU8iYqgfuIuu+ZkdOBopPiWVUY6lFfA7tRVKlm7A0KHp2hEe1H30uJC6CQVvZgCd64Js4k13ZJ4kS+9vvXde1i6vmU9PJw+feiy5u/Owe8xgD2H1XHwqzbgInlFUyYngbXBnYPgtUHiK/UCldf0/OZRo3IAG7zmAohaEQ2ZJZ4G3XDLBJ/f0rR/Raq6QQyLfFVXEYUUQYfqZwusgAU/PvuRxX8qDSUIMuxtW5DcCJmeilQU/dtTe+iNS97hKudwIpJj2UOKO1twVod9aSTU0UEos3yTZMDpsUNgEskDJfKqIDRz7uPPgDot7eoPfN53/cz2nOsf1F8JV6cJiiXvomsb9dc7BHC2pe+jAC/1pYoZXbqi3ot7fE+/EQTBHy8MKcZlaoWwljgQI8zxN58MLZHWEVle05HYZ0XQgaynhca450ZZCxoUaYP+cJPh6B4NTRWj3zerK3hIEy/N2mtLzJQ5If6ExbKu9T5dssqB8WEivB7xOQHwaA2CtYizUJSDwV7DG19DRJPGJUQ4zNRalLA2Ydc+gX/JNIH3if5lZNEpVXl78GNzpC7NMpdhkM3QR8EDdBApId8vENCPpnfmqMNLKseoSqFgTIBCRUZZTBhhj5fbNUAzsJPMt4xIs8+ZiyGnTY8BMXIzLI1ScqU5I5rJR1U4wTswUVYcgaNtF2O6pTcXiHC4mkMQiJsMlQLzyUYSudCa+W3nWRcsHZyM4Uoc8LQn/Ub2vUeMW1fWwsFyUmFHEjUIJL++ho4beOhClrDOgYKVcYmxDtBXqUpeXbvLJS82ikpSEevUEng/ZRkomfjXGArFo6rjzYAjnzSkvH1IhMgaIiPCrseQBNFctHOKAcM4Lye0Boxv1B6FkO456O+StkrE9tjRWiepiDTSWMgriO2fIlirD6pfZPbJ/AcWtuiXw+9mVJR29fas0MNMrHKUdnbDk6J65KsPwWb9Z/KnuDugC9z32sv1pwrZew3UEdG2Og79qkZMDB2QTQouQS4JMZnalcOBbCQU0OAydDk2/oGilEynO040x8C2AZMGwRb4YJO4A6B8ZB4UVXMrA8gGRJInJOSQy78nPx4FiXK94jD07jBtIQz1Yv87ItVKgyBHCdCKkdKxPiHxDB0uRY7qbOylxie74Ad6ZWKjWJyY704emkYLT7zcY1VWheeaXXRaloJTYLs0/Pga8vjiW4kjY3wAYY88MZWTGTgkmt6wgkiOw05wPTW1zXXpow8owkesptJiBys4vgJlO2Yb5JDn8lfLqAEiqpACPniAgS/goCnzfuyrjsAUu7UmhedGIbD9jyHVTUseGBzoqMszD1zZyfe0WytR9bUylRqmNTx0PoxHuIvuG38MLoCWgKYxyDjtFMEifXBtQTvHO6fnI85jNmCmIhwxsc5xWMCDRmTElmT+oUN2L2imx563+N9oU2eAa5MlOh9ShwHSGwj0HosFvz2hp0fHqkPb/GY6kqZLEiSl8NfRHqkQsvwa0JLCE7/HLTuudfsQy47WxYENrLTdtVrXc1WXqEB0nsBMjTnEKPJDosqXU9Wk/Zhjd3CLctLerF1Z/RgKUwPomiY1vq6WDtJv99T8UZk5jzZsyvOf3IUrGgj+VWNTo4KOm5Vo9oYox8AX1Ynu7fC1/S3DXh7S1Wn92hNfw1HiYUk6CdR0Q4jkRxIfSGdc5v39AuZDxn4tJDLt8O4HWU6m+boA5DHGOKluCcrc7yHcoPIunyYk34rmvNVQvlmkZjlaBNKwmkYdD9NcNhLC95eLZ98lkg12v9r7P3z91q6/zAFZVF/tUtXG6MbbV/vfPnhxc8jQPP8M1wfwYT+X/35P3i93vfvZ7SatR+KqOsiBv/FOFKB9qNh6fNGY7/SXRte2PADwU+7q7xRjP2vqOPH++86cWN6j+Mky4vjpQ4Njp5Gh/jAf7wvwMx6/+Acn2fm3ZVGOJiPDc2nyxx7LHDO2bgrJSz2CaqJz4XWVj2MHuD8ESLs+5C7+DOfA6w5ONAzBGfOLjIfl3WrH+COH2QkpyzWHK9cYDtEP4C1+T4U7JnkdA7P6fxqJdCM9slMThjW51YcfCJjijpPvTBjFjK+R8oZbpQMr7ebm4+RfNKGkOarH8PJD0CUGkLndqUTOOxkb5cNM6gj8uz+0TjKQiDpewKuO8+dnAD61oYoOda+H2WpkjuWI5B3H+9usFg3FnhkXCku1tfbKuhlFSos0h80MnD8ErOb1Hn7Ejaj5TqAY5oZSJAf4LTJfjNhYDA/eVJd82jU6o3lyWZJqn0TYq6oIzkaBbz4xGbgIqUHELkD/QT5440HUkZiuXBlot7YBikRNYLP8Wfk60Pn8xid+bXtopNmqR+6myuVIDmdkgagnpIc0MwKoWK702gDcNt8ZuHRsxtRWwY2WIhkdG+vwlDnAZZzoHuXen6xfoHYXT+aENYV+NAzEnQiB1laXnjVkibo05OHE23QYqAR9uYkUZGdn0SRRwF5wS6lhTFsJo49QFSuH0WL9YWVgve6f3viKdI8az/0rmfim6q2x6nXMiX9CtMDSF1QU0DUF8V0qiwJRGKj87pyKOXMXwHwuf4TgQ8B4F0Ter0hliLpVtKPEVZK/CZ4s39fKXuE9foz85rAjmE+dU9wjn/KTH6ePRsqMLVWAmQNJCvkFuyH3yIjPzAGtTrFMwTmIV+a5GzKVkyCTld4wA6G52lxk0R2gBkDXtR2WxagXpF0VIUIE7+IyiGDfZb0d0vZtBBfpaxJhUCOiqFDVCCYtmbWK0z4zzxEehY7SlOfir1BTxfRuCqjcoRxzEVPkmUWJU123he+TeUp8mOerBfhuFMLt+GPLaaaVWPrjoXOMbzbU46XrUxjRjjqPLSy5dV5w+A7XCR0mBnUUxLYneOGDnrJrhdxJwdPwA0U5ry9CnBjEGG0YkYxAkfRGewg2BpYmyE327MQOtlpIpFN9pnmq9MX8wd3YHtHPJcGig8rl5YJ+K0rEroBuSll3L7JBrGoK4G9xUIYbJD+EeL8qPWxO5qG7YRCHNSd0BlwNyCyhXrK6chvggZgmSYt/PxBoYL0ICLjfLvP5wUqSVZ1SBjFkJMqNAVoDhlfK68FemubyV2SOUYnxynpym+ODSlrIyVdNrVvV5ZqN0vH5vltO75cQF9ODp62AoEW2m9hGc1Lu/r7OdO+Ab5TF7K9rJXb8V3prNNvLNFxq2Uzjt0peEG9fIe4teDgOjXBzRBAi2ZdeUOvXd6VGS7rklauGQf9ihn2h+9euidNu1tUO9XXQO7XScWSPQTyQ0N0ZvXrQvm3kOljBFM2Gt8aCkj4Zesz0N4nwXHxAgcihB6Qe/6SfyLj+N+fP/Z51j88/3z+H4Ov0VJ2F/B6zSMyU8mNn1w/9puvXOrhrhDhSdNrny3hsm2JOl6jnoagV7AzJrCwAHKLCI56N1GogdEwSiIhAXnXHOHh7sOAed+uidTHe5YrJ09wNwQ23ECAyGyEH2QXrsGZv42+Qo2Y0j920Do4QfSexFWfcxDBtjY9GHgXSH8+SL//oQzVyFtntCS4mj+Ch7NeaRY0zoB2qq6vRCdX38AJe3lcqP6c1FMBmXSQAT7mAgcrPQIm4KtTAxaA7qhJr3myscEnxlzgcX6kENV1gh1DjIi4P9+aT3BX5VBjkYjotuxkfr4BUWKQqRS55JBz0yTuKsSIoldIulEnV6CR9eAyTRd89bXAuwwXk6a/LovNZk4af1JYcgEZhN6cxVWhPC1TieMdcsTjMxxvLAbniVesJFy8K2ndZ+BdKxa+SrfsXAkdZRZWnkAwi/of7PEudJab+PfSg1G15eAroZ+4rlKzXGc2HTSRFU4Lmz0Gy0NZGu8mvGcIqHkDOYqdzM6gUhksLsLJE7nniv7EnD49WTZHWX6wKJ8FU8GUdQCYw6xGWs5g82lFJ8c9KmaT8/xDe/CEDDFOM+zE8Rxftz12DFHwj6lWCEt3dlBszQ6OFbFJCzv9o0swECTftWHkq9aNA5urfsuqBhDovix/6JaEQ2VYWIA9zfzD5XCL9XvblNzTPPQAawO8Zkz+B9rUIxybwkOYeMwX8FhQ9BiHmBuWFrAptUIeAQxrTnN0IrQ4AnxM8F08bCgTG9apuCG90DMfeDNsDXCDT7yUwoZo7X1bDIZwujHLkXUdin7z83V3aqrZxXIexbLLxaaCuReWZ/FegPA8mSyJH3PSMGbBgRQY8RVhkZZvVRL1aC7ySEJuEErGpODtiR82nFB8pelyqZCZ7Ry+hBFJtJWRPrYfPXDGplXrD57hh3jRY7E6X/EDHIb5WC0OZ+AY/Wdxej9VubjAkzqsjwy7KTuIxmRF7kyA+DzP7g5BfGDD11GKcPzJC3LpVB9m7gyF/USl1qHYWfN9XyFvnxSNnA9jFoBomG8LEY4a3Za00OMQbinIIr+KTItK0nb8B7D4KwKD+KTWaVmY6pVgTrlAtVxow2MQyfxIdeEsfbXgYDzEVo6HFBQf1vQf8XOK11tJdgcVMME9qOExKwD0lXYWtW7FqopgZniRPn1bLpLQltlK07Lf2PwQrLeyddirOBWK413kbmHOQ2NyRPQfNq1l7GMh8RxJ3KcKMdwrCcyoIGIharKQWVhtcWb+5Mf+p2q7BljjIJVW7KDBkjOclHfW1fYGDKaWs5NpxaoVyub+np9pffKPe+4Uv2NIxdbMFwSpuxsLxrrDrx885y6j9YI7u124gbP8Ch3I04UJl4jqV3U/qScxODDNZDOROuTDTEJEwf2rTSCg5sjQKOOeerzUWukWCR0x8jvMJNwlsInt6rwhO+r9XvkcFTZ8u/DbEyaEw+mx1e6LjoUeJA7aSbTZuEBHDsal8klh4CGwoPfkvPz7rsYg/pnHB8Z+Rmx4PGm6roB3nj+JxKFh/1BP0m0U7OdXV3eMJ6XfbuAc7cp4x9P5L6I94h/zh3wArE9xilQPuNkiCQa/jdah1IFcAHEOqDOBDvF2j8OMCK4UlHC1qkTyU4DQQCKBp60Lg4isD4mYlWEeFsdCkXts9hbMISofVuqoIZILEaSMY2T/9QB3xSqtW+UNbJOlu/dM9OVTyiQXG1WR4xwpDe1Phmiq95zjeQkAKPjpWUtfkXjmm4Z0PiSoKJE8AxbmybUbmztrikrFktgTXGTKDCabABNWlpbBe/lIpGRytz8/swuD4tw6KPBBusav2jDFK4PpFxhIYbdRBlySNs3MmnOPEnfrBiFsmHS43wTvZIBlPGuBSLNssi1RhjIfrUyUl+DMbAJMrqUNLXqz/yNu2rY1ueiSzQ980aZiEgGlBPEJZpa2wF75Nhoo2hLNne1l8hbtBMDlOM043IaJ/acNiuep+8vJUzWMswA3FGWR8LQuWOiOVqi/B4QygwDn5lIj0GnlZtO7cwRQwO3yWbA+qhwWRn+C9DmCV/eAP1XM5qAPbpc1QC4nXj1KFYziFwfnbMlRC97m7n+zEbE4l/tC/3+HFMkaR4+XXQ1vocBatURylAlZeSvhwdJOhHrDIrFOruI5KxtdGenMJt/60cWl8rVD2hrAVZb3RwOIKtPQG2w7xTASONT3fLoD8Me7nAUFHuVxkIrlZA1O8/VZ8TdueeOku/5+fgLnTq+oIp+Y3aORELdDRw7FhIKl+ZRmDyzJLkbvoNOob5heDrSc7jAY1vyTOy6YyBCeuY4ysgRoVEPhF+tUSdmulmnxHmsYU1X0fk/idTelBp0zaAMoW/S1KbjnW5HIC8uhNoNLJjC4aBaQyE8EhUpzb4Lvw6Q3Ltmsj5hAwHOT/QScCI3MrbopgW+ZCNh2cRNJGq9ngpTycdk66YRMPnAUeXlYAI1O5jbeMKSE4rIHukBB53AwT8rI4DIirtAXEan2KAEsjrXIORWN3hGUZh4g/ISmC6KUadnsQQSfmLwahZIXdpOds31J3/2ili9P0Ap2NNnsxWyxwAcXHJ3gPt1NJat7njAES7DKwMy8z+dUksJm5+gbBUoAD5wQNovTq/sC1LdITtBQCvrsSCYabO7PD/MNAZR8706YfnLBxf/CuzVTzLuXfVQe9Wpna5cBY3IT8nQvvJiUJeKCI74gtCLBV3Cgb1JVVwKTzSAyMtoUOW7DavRsHCHB5UJFPFnPG2xeiRkM38b+mdtO+bbVafr8S8riguDddJXFlDfFRESQJyLCGPi0f/ClhdELtpr/SP5wriAidVmO4CQvT4+QNESM/TidoZJTtFBTXJBvF6jzYa9ImHU4ZsPDcxVt+ckPYMWenhu/5uiUOGklfPXTLGjYEkyAtjaGaBFhwFZkM2iZx0u+NczkkkBq05R+Dbnr8IT/9hIYLzUP8AN4z33fW2Xfxfz6KL37hPqHqcfm4JzWZYMoPQh43fBNYY9Ku+TOODk77Xeo4xfw4pgz6kEQZaR7DQM8xh/6RwxrmuSIoCzivA2J8+mIpcDVeMGXcH8Q9UTrLNk7BJN5RxjH/lCvcT4TLGcugbmJzgpcIqXrmgd+vRxPW/rkZoQIfphatsUlwIN9GJUlT+jyzKtNP60KYLJwc1V+zq6exWZAF1KFGgM/cMCxTFx2hjwzo0BieXv4bpk44giPvcqBxLXPKEd0L4oU4k979+nJw1/oILdyrjtxDs5dUD1NvUfcbb/l4SRZMMKtVMnP9xJ1HWsEB1wfXdvWCIQWh7cY781lNGOAQDOTPZGI9CfhUWRpPF2fUsOx9u59T5wOwtvQLtqeZJIE0mQPPXtHk9jVHCy6xDHtNg/MibRhO5ZWDa4tXeJZm3CwnaWaR/2sd6j+fAK07vl5i2+hO3yN0ZxkoeagOKvvlSrHv7fkExn6x7BPsoPcdYUn6D747hEF6RPzGU9unrN+huIsvBsZjGDTg33lpR4Tg4hw/YzOJo83q8EjgjrHYLD+sP/H9Yl1Ckf6jNjpRAjCbl+eAPCDrJ/bQ4rBLXL1TseppHA0wOGKHs3iJCrphBTIvOXCS9VvoryqdQmKXOkzg94jA1Q1IcI45Ad3witJQqdC7pWqsl9vEioTs3l8ZuTiekGkI2tOjA5AZnBGjLPwofpMmqBpQlYRulw/kQQ4+iGSuzHt2dhntKDM3TSnK6dePwytN/sQxGwEU7RgRt6ZNCpJ0GI+eA42ENudQeU/jA2YXgzH4HgUxJBDvK8wojCnEyIittok93MjYAWgJ+1fodeUiiQf43W5yllc43Qv1uoHBQA/4BPIJ3/sKjdCZzkPSnXbHPE6WbFATalTudA9ab6L7ZsQ/58KBUhZ4HTVyLQEHhL2obAe8SEPlpN6FEuaLSz3wBxdE+Euhh1UvEGlaN/jtgpm3ux9wfYj+KJvVUi4SNOh5sHUb5DfB6XIpp8L31iIxSdnqk1Loj/Gz4ZfIjhnHGPJi4vrDh4jxUHbNbEzZyOtN+qFs20e6Ll8Vx6CjwuGaKxwVqkoCFDT9B6Q9csGtnC3RmEWddr8AUSdYz4t6gOYxRNU1ce05ATABxOh4/q+2jp36z4Fkrycuz6ygl2lng7pEOobTSBCMkQ7/3E863q19K3Hv77rZtk10cN90Vkkh1rSp5qXJanChY3RTd9m5yDNfeokLISU7/8kqDudDQeRTRCCjgInO7LQuSK3/K02o776+E1B3VHuVaoRT/kzHO7wk3aP4MEXzQy7+Ml37JC4lPT3jiMU9KEVLmn3krhvMsclcuwjUpw58HNx9yUGHuG4vzeZwMkL2vtgK5MB4EJ+Tk2ff4LY1rMX9F71pq74gl8CH0TQRSrAGEPa1Hjb2zUQcC5vG/qiHevTrH5/26JbU4GlW799SxQDd85xMP0vh2agbjGMfL2lyHNpkZ8NecdBt8sKRtDHRT7JUlzyeOcZaPd4Zk4E2pm/BfAcSV4eMxlfpMkTbeS7LzHm3iQLwz8upJuLkGd3kmV53IGLgHFwvQmcec9pIeCh3rOL+Ktgo+mAoNYDZ1gVhHn3gEo+yuYxzXMXT5ODIzUJegHtQXI1wgLmQIcR4GXW70QZmdsrHn0TMZ/Dms9XV+douyPA2DdaAlTudarm7P4NEfSs/8L2dbelIMLRb9BbBJUYLKCd9J2ad9/02HmicJ6ewTc0EGDhnxf/N9N4X2zyrgH5iArVyCiG7U/YJulJOMyWvgqGkUEAm164dy8g/GP8wL9GwI4eWYJXJYeko0SGYfGJueLOyXI9895GzlXeQhJOAZT2N8V8gpFyPleZnYGOz7oFJoDRc/bkDgroea6oIlJwtcgYN+rkyuB4u2lcLaEiIJrdNUE9gEez3ir69ID/8SdnIXWf/m7pJYUZOqC4WAlQEtfBx3uMIE+eefa8QWE8mWq8qNvBLYIjW0gxwSQ3Z+JPWXRodlfPJw6BjJ+DZrEzc963yIcyAOVO2HEu0jKy7hj5+Lnz81m8Ijf+ZAhrJfYoZTTnfZn5LM+Y/iZ7wDZOPL3huDual1BikC7Ys8RM3EFFM5wWt4GVl1/JFXLjAaSketE4DN8XbCWWVJN4XLxR96Yvh8wST5iLLQeLn0eIYI2pKvyV4aKC7kCWhZpLa0BIENcFS3IOw8kCTngSSwhdYhU3mw28KNFtVOrUEIRDPn1QHCdizEfJtYGlj/FeKiG/QLysMYpkk3wWPzHiiDuY3GhdBwChji4BgTf6+rTroUeyoqgTUV1DycLNe4HPqdGAJuaSeKXDqvt+gCWe3QhH1djN3pcMHtHx4FiZJ/l6rPVNpPFswlMCssLDpNYU+b0OlgJowH35t/oopRzYDj368jvZ9ZOdwRCrg8tzleIIkMawCHaFrEKA1ZCQj9BGi32UfOp50mJh66rKNIqUavSJaw7YybhpngbmUirFXwy5+oHsztT6LQ6YMXdCbsOXqMcOl8ME0Kj+dAVc8rmzApB+MA6Na92uhCbv9kSC76rain+0uQpMmlvp+EouftObk5iDB/TEL3uLsxjwEJN7f07uhtM7swxtvvksdopSZXLMMa4svKqYuYfXcXUm6UVP7nxpTMKnqndv2ahABChbazNEu/n9+enku57TjikrXc/um/sOzBmw+fz2hTejP1KuD4YFUatBIJmzKddzt3tzMQXv4A1jS1Oa1Z5TZOvsWHu+eg/zfKifyLKcOeIckMxsh1hRzzTS3ortHzE8lKSDnaQL2JNnzsxCr/ALPb773pUP07s4Ydb3vwajrjSqGporruJlXdo25pxGe7acZ2ynhMfVKq8RN2yyGI1artlHUOFi2gOUlU2L1Wv7ycLadQIP5fv7UEJtMHAMWi+hjmP6Fv/shZfPL5LViIHSL5ff9a97Q+5tFsmszDTYuA87JHx6k6YV5X0FHPGUmvy2INpCmdmPD8sTFSHJ63hgz4dcjcWA8U8S6D7ESo9xgPXz0PvMQ+76kyZv96y92OHzcw8U1Jh1wEXPLJjQPFw4JTSO+JzaF99C1tgnTuJ9Su4EiLgIwV02B/Qz2Nj+1jOEDz+SeRIFwlf4A56tpBLEmOfT4fHC9R7C2MOZzdYUAL8zZY52h3g3AMYkQIHwkm/EnekZHOwS+8yna3eTtJUCQLhZPWtLf5FTJAySbb7k4DlCnD0dqWJvYlINEB/zBNhL1INNVsDZDqk9FVeaG6f+wC+FxE0wM9Ha0D7OpkI8KH/sn5ulo1t4IdT4dJ9CWPK9t2DEcwHLDvvUssJX1O4bFbUBYexKBWBGOhCDaNr7KHsWIJYjeUGdNbjJzstuX9QKJur4laK3dzzK7b3uA+H10eiCbzDB5WXd3oM615AMA5YMpQSeffU88Fmf6gtbL47LzCXyv0IH+v5OZVQr3qhOQ/w/LUfCq0nzc85MHHAWdXbD+qra42IOUaAjb50EluITTso8ezic9jyMjgHcZ+ZnzzODrvU5e55nTvjpdQyBqbL7fHrdmjF8deT9cmL9SKgCtcfUzLw+T0JQ4Jhc59HG9+oSqNkgUmStIYuObnzBSRL87+QFxm0g0s7j5JDHnolBn733HQN/SkUDq2l3lwNUOGZMPYfXT07CAQtwdMUp6X1ar2TQsEOxZRBqfmOugHOvC1X0EgnhEymuUsblLanQadpRKPgIPobpBG2ugN/kkYt1GQe60hyvgHlSOn2wiOHPeFuxl/rpxxVnM0XK3/oA3ydWwBCvLmOUSY4ctkFhWtVDcpyaJrM6QHyMo4l5a+Co/JBooWsfyUN81fliPrsJPBfMZ+4Fkeej9ZnpmSo01dS/t79COwXVGo6hi27R/st7syYVIBoI7wkFV89q7tb4clAh7q1MUbniTWK0aXxUsd6JywLdJE2irCPAe48Qp3dTJFrJ15iNfH3csyeOloyJCenTJPP2x1x4GDnumfnA72BOnoX0pIrk1tkSJUtVEPtbCfenJV3ULfENXhz0NkJ9hIj5kT8lHQKTZVQONnOg3bzFFR7zjVW3dBQxQXb4wHBm1blA2o12ifbjCxg8gs9sknf0i5Xs4+fg3CB28LIQ7nqrAt3RGDdHLcDamYuv7xI4DE2chJkgt9zxnFotc7U94HK0xviBAb+oC46d1Vq044BpaBtDl24Rw0Ch+fzCJLeX3m1IxrmG+KHhk2nT+cp7K7mgmvooBeAQe6LFfUDceSZ5JHmP68UbdaKUFkGOpABwIYwc1pEXHZHDPUiqmycWyXxrENrhjmd5gIdGMY/UvRN/FVuP+QUG36lpWxkhGbljbyUVi+ng19Qo23o7auX4O1rlhzLIso1WPnW81+Su9eRm9FNTlg8rwcgZe8ZqfGLfXDrXZ71PiGln26DCVAAd8LrGDSj26XFMjIhKLiwYnF3QNeeoPMNzx0rWSN8+pM6UNGzXoFS44e54XuADubWx0X+mM0r/39kjY5zrGQ7HclO8MoyMZDGauSqSEmGoS/73MASR8yoP4hF13hPPN/zkYJRoRpJwwJV5XF9zMm6KNtLPTpiZz8z6YJ381LBdKWBaePlKDKPkyvzWDDTamHc6c5TV+M2efeK4qO6Gxqnis0soc25WI2IgeA/B4zVf2w9jXMPm8E47Ebc5nwLus9IkjRM8H36WZ4bnEHqR7LNcFJT5thHn65pctn4pNM+kwB3H64+aJ9Sba4I+hYiReaqpLUDCoILozO8ga75zfNkLH9vONTyvgvVeAq1ILW0e/LAHUxpYpxxUwCfC15m5fmOXRBWbEAeekB/X+5+b4jHXUKYn0c1zsinM7nFleezrjn86v79QaJoctbQIiuYwaJf0bZhREMFwbaxaEMwPBCaiNlYp3oXeTSb5uON5bpGfUiEPc4+pBPFflX3rpCAzwVnvgeFQ5h0SmYJ1zw47a0LxwvK+yfz8oL9XFkrx7OYlLTfZhS5U4kbc+yIoyvrHpjxDWPq1J7t6T4CZgMFPdDDHC4z3rLbuZ5KZhiAFLzWTBKQ9+wA7tTUIYj51FPnnsMhCKx2Jx6nwz3U/ZSYN8/Pv+s8bfXbz/HsAC4JWnqVJn7NU0EReE0lEJClpUbd8oDI0FvBiLcnAntcZjMrWiWQCz78jAOmbQ5WUVdjw6LNeFvaNNqa4jYFn5KzaSeCyxScjzgPERmXltO4JVYoKhyT27jg7wKO/ZU6zJOozwENvRrhGUUnWDY3ev3TmE6TCTyp8fhTjl1XqgTauTeBucDgo7kbYPJh8XKubthPUx27GYpHyTVkJW+feplEGFvDMFZ7rCosYWtluLYKfyHDUSQd8sqHvE0Ydikphl2+aHa/aeohohiaRhab0vDxXNfgaUzY1RTn3enpdmQSfzecuZ8Hc6pFZDbVawuJzYjPqCkwDnLs8J82JKlmwNfTAP7AfjotZZBqINaoHN7WEBFqexVGjs2tUqxbZmymzuAxCEEM5I1me4wWZAPvoE1GyLkCrWHHC5euKmfnbPTWHIhIdevHQnnKI+j5yNZPmcWUJgczzxnIwApcMZPqKZPKUaIqfIMyt556/zk02+cDMUQ6VUFpd+sS9GVaH3e/lEIhvyRgma3D8dKumUaSUxX+HlfNL9KZi9n+YuEWfUiZML9H8z0YTcbx4vI6Xr4X05r5DNTYvQZGn8jNfQMf5M72q7FuohRR2y0rzAhgx7wWMSQZyrqCf2GH2OzvWTX7r//g2/TVZlJ6M7KKsVEvplGhtrV2Jk5Pcki1I4vjIX19EB0dmdBLkAwVCE7IwemvPDJAkDdsvSYrFABIvgXuJ5ViP17HvJBRoapr9QoahKQAAR0HK19fWW2ePfdDSBpdLcTC2iFcJOZlM3cLfx2h8fZr5BznuG9dDsO6y70GVcyTrxASYXFSjCeOhlipNLDefw42IdNZNEsypnB2XsSShm1wqA54El6VYiFiBakmS74fvykFwC/CiG1/zyW2H0L6/7R3KJ69vkF1rUIDEA0IabiTI/ya0QbTHw6GfNA2GF3/RRqT1f7o1HCwbJ2Lu+pASKlvYpnSbxuJszzWA5kRb9+fiBsaefBcH/Rpev7yoD/x/hyJ38cCvDVgH7zNPULw3YYRdg4ecDvlIJxzvLqoQtM9tx2wT/gf4P7DBf2m977EJzOLMjE66jboowiUTsyVVbpmaFi3HMI56Ume1DPETBNV+7J+m5sMQxtoAHBD5OCHcIjai5aDIh9CpLtJpIOnLpQnGvwWOSeA4sASONL55+UgQGlNFmniiTDbtq+Zvh9aC6lpLkjhEHL5xaDX4MjBhDefLS7ZlCCw9KSi2rO7BrYu2URjA5VeO9444kf26GIbV6JTL0fFLCptQW+yLk7RuAr2Z8T65+nUFYFHNZk5ZGptcj5dGuahZyc0QC6lKcjd+d6U1olHhwjy7pyh2yrLbwemWToGL7ohZ5JfE0rvdrq5jAMRmYteNLGBwXyERdNhbE+zM7LuSvG1iOENboqxGjN0HncxyI78XlCajfzXrIyoM+sY2a8AHeu74DnexKBz6XEzUI3mjo8vfC1HGoPRYioP9m6WrziajS1k0cim3G4fNeYiE4zZJx29bjqI+rKOxIk1LPnCjRpCTInXzEAlzluXYZj/UcSfXdj0B/r7izpU+FeLDBftp8LUf4HVZ6yM+zizq5Ko2DTApchNZfarQ3WCxOft3bmtACDPrl8jDCCWWhiRfL7KWt3BKEOgaK5lPd8+DSyXHR97uGtRj/yTmj3jsn/tRjmZwGn8cJC58a7U2vyxfQ8j4DewSruyb1Mf+K4LYk1FZJcMzz4SBMINEABAyKS3D0bJ1g10hxWK3bdsEDIQ566E/A5tP29ImFbb0KXpJPdTipwcGJPjmgBjXqFesCu4ZxDzt2oEZYDf54vxwr/YPJkZgRjJCaSDBVGPVBCMgfV4VSZHkWT9x+XeFmPeWn4oR6hvbD7R84a+Z9/Ab4BILoMGyHpOTikmrLHN+CW6eZPTlx3TYCEh9GntXeUAHvoLLhs+izVPXly3DA95GjOfCVjfIgpmOvXdg1hqbYynVb6vjrslC2jWnpvvJPOleYgXk887yZtBzmtQDv8V08CDTBnrwsslNkaX2JgIJ7jgFdkCEfMuL3JE1JANRzr4AG+jtv9HSFgZzT9Ru31yTIvwYh6bJJqCnrsnJVbFLvtNj/Atg+cKZ4R3MFCiwyIDCbGWc3zUpVizU9pqamaZ9wYgUFEbm3mS58o2Vtwh6JzW8gLYTSwAs+UmNAIN89f28d56dK/Hy+Fn9bJWNzmmdcAvfARoXZY7ZbRY5wFMg9UQAFrr0HJFEfOF6QopVC/t+XRZAcWVz72mxRB55K0aqJ3lFMWIJ5BqoJ5ByGjR0vuQcLLAMm2xkYE8DLtrYCXyQxcXi9Fm7WZQtc0BxrAyozY7YntFwu4u2GsVYRkrMum9FyfTfd3EVl+kxo2tNmMbpssxzqqSegrVUtZcF3tZmvx0yngX2BEXr9gqi7D3Aag48wK7Po8lMxH9iEzFe65w3g67LsNAP040hvteczuP/eP0R6/W8+6p5GGMfG5lTctNSE8f9FfFv8E83bhmPsJb0B5Ff8/2Ab0agy2UtyMgW23yheQ0JLSgq4w0PPNQxNrk0xOgGvgIZC9+rHEk80+4Zda6bK87jzPjiecc7qdB3GUoPv28vXz3S2Uml5PS0R4ScJNCbP9QFsX+m2QC52c6SPtQCulMKjznJ9t4vKCb0DMe2l+9GOizJnS6F35Ob5M3n6nqNOa00FYUO4EghG6jtMaJdQxUxqvMRnb7ErlxC+80DuO1GF3g2VVQo0/GaNAIgp8RXpk1fFRJ824N8lsKnUej3MktRWjyzC62JQsj9DCby3Uvh5E7jvbIuVDXFuRfUsZ+8xjSRORXq668fLvRIBJQDBZ3IB9yCpwH4Eat0qUNupumhKBnDy23xV138rF9AM+BOHEyFUZWc3tRvqGdJM5NvE1v2mC6htEzhzk3CaWujLDWJ2KFeO0YYeX52P6Par7zpsqRp/wMt9uYzBA6DAeoAeia0srG0PFFG5K/4iHccSjUysDHSGh/ikDzpLp6f9TC8JInz5Ke3CcHSMxPkb/i8uyLW2zwDBspJN00xbEAbgtOgFdg7TPTClY13WmU0gxlzzxYFwTvpvL6RIzm65HM+qNb7YF6YWG8C/jVNHHGb4NAeaUua1jWv03miiMotzND2cW9Qz9JzAnZ4a0Ys+hXRWuoSabiQFEejDgzsKOcPxCTLpJwhrw41jeCGlq+bC1fw4I/sG5+S1oGTNZ9meSnQs94MXwPaFOf93sEytrRr9Tqfd1eDSMvoN39sHoIf5D0BZPPZeE8tlzjk0Ovm8FycTBsrcOeILTDL25h7oU2e+mAbiFCt8FvvE3vbAhTPVra7gxhR1hx4NSbGfkeL1kzZ2BRV0nD/d/CD4W4FWimS83WiFgnyBI6/L7KlSmqjrIOet3meDW9VRGoXHM8fmFu+VLkOe8KH3Gosq2ybtlQmfCtkpFRFwAAxKYFOtkeO2baoQ1gzdutRXkws+TOERa0MlBLsh6TYLxzY7njlK3lvh9PDn5pG7dDufQd4tioEc2WbkzmHvLba4vGL0oZJU/ny8X0MV7S1pae/EGzcMTG+xcdyrg60V8+T9dzCLChhTu32KIBRkzc6d4KpIAlUvMheaBazYJ1GPGcS+DQ2LcAcUnyZ9CKsCHlh19vpxN3xHCB/75bjp/1TjmogDE9A7APDu4k0TapKEZycEfGbTzJNtmLjmBFM7oXM8/qAjMWKeq6IvsDD0nB9Pplw55irQhcDiTyBYztW+lZ7GaQ2ezldZ47mVo+FkxeQkjaeL5stn73JYw8ycu/ifSHwfFhGwl/YSeJ1ZAmIeyTV0Yt2sIteybh3y6U6qK9+pJ6Ee5AHOymjwC9LkX/ZVC1jKwHDFmhsEVdbguu+aFk4VawFzklR5SfZYntOJogrOeJeYxaHe4A9PkRGhObtUJStqUZ0gzyYz7MfmyRf/1wkYr1rYPgsXrJOlwU+M1kAxmo7FyYR8AEK0zwKx7EGxLslAH6T4JumutkrPDzUBBFZ4Km44AJ8hjQtpxQQKFL5YELv1xUl8Mc3bhSgz/E86ijWDMBbYKbQak9zTNR4RnOoHg5SBEtB8Wmi80BjzEqRodigD97+XdG7k3kDSls4a0uDWy9WTnMHjhK2Hm2kyYF+vyfU16uw4VWIBybwRmN28VTTE3JaQJz7uWzJw4xbrJQGxLTtFuE3ExgWPzkGXJNrTvh74/ZIzeXSsyjOYXQiQ1f4rVZQw74PlXy6LPdcY5lf1ma5t94kCrcnTMILEtzVwyCJS5dAvW3VET/46lZzRsIz3YXfDXg/Zy4gPMHvw4ffvkGprUIJNaJjoQ+JWpbLX+7nKRF572x+hxcaXGOKO7zqUICMpbwjYxzKBaHNpx7rvFCv9zP87cFTgTfxPXo1PxoD9MHk4nm8e+Gfb0KrsS/4kbRZ/0m2wf555uIUWUAUa74PWPTwuacPLszp+/MfYk8EbMVRlC5iei2LeFM2dP6pjzjld0aCzKPngVfYA8/onhzU7prPE9pv7d0wredIaOJOCiuOMvsZSzGRDqzYMwmXBeF42YgB3nJaFgwnVVXAG0kvm3Y0inAgPS0SNev2HsaHk4GEfYpEAkgInq8tnU7z+ehZ/4BFkGF/OBcB82T4+sz32GJyoFKuuTECLHEXiYGzvOQDjEjzRYxDdKKEknYRADI5MVHMBnoUnkB0aSkrAivjH0nIJYVMcZZv9y9p4PN8rgjQTKNyB1EFxTw+vOMtjfN5npyYJxX21rMB2LT8wGtvSzUMe3PmASTm2AXzyho6aEHOFPHjC5xHZpe/O8Ul+kGVazZ3EQEMIZub8TUp7gKVJ4qheUf46Gk7C6T1y3MAXxvUzwP8jUnWYdjKyLjCdxKCA5kn1qkgfkeWpHbsAaZt9ZMFbM7Yr9NxpGhKK4KV7liYnsoHkDDmzyE0haVnPgYeyb7mp6HSGeb+AIknL49VWpqYnFujCKKj4GdGSFNqOZ7oNqQrEo0Aggw/j0PgaXzzQdN9btYbkFzg843cgAiZtZgDNnciplsHkUmjwPRPgOGn4pKNdO83jjF26nuywY71ZXkWtyJJJarvltV1dbfiKlep1Ca/F0tJFmRQGr6gC6HFQXm2e2ESSXsuLtm6JjdR5amkyRd+2IEAhYq3+pM4Q1IZnRQLwAliBfJX3++kAKbA2frpElrPwjKbM8EE5T3qRzXhB5/1MaU5vsAY/aCHTB57OrEscy1qsoImcXDkgJ8ANA2qAIE3Wf4u8dUuea6zJlMEfwHlNhh4mCZtr1m/slkgUGY0ZJdPxjj6Z/ckfuaTwhpecdaZhqMukXbVOVAaUbWc1Bv3mgwMnpFn2gYMbMf2xD3R19qVHzmcMQUaQ3HCzpFN7ZenA5IIcMEOX/S3vVh+O4oyt5F4nidUuAw/6ll8NglKV4renjTUgeu6JIn3y9Absn/Y8Zv8jqSJoDxXxHDeXfCtE5czfF7/DLlpz/waVW+yx+5xSKjMClIcpa4DPOeENx4RtHaLUNZLjUZe7yLrOqm0OFwen04HXYBneHoPgYWulujboriGGlXORtYsVKiSDsK6fvO80z3nvRA63hG2jd32BfYyyoN64aYY0WcXhDQP4N1lZ8WFGHmB6zmpV3DBj/FDs4dhLgmL/hLxJveBlrQPkv7FDqBOdRXzTc+f8mZRw8elcPgFhNMbM0I+1Z6KffScWj/P6FkU/el5V54xC7AdMGHycWYYqYV50bg7DScqm16EVSulXqQydhVVooTlSeeQ6k69uJun+UvDhUUmbH+HohN549+fD/Vr4Hra9lbrDupXE2++jcGJ2No5CpLNz6KkjMAJ3A2cD02uuptyRMM4t6S5bQV1yoRa0inguqSeiHIRKjI4H8yTnnfJJ4B8fmy8DO2eIhZJViuOW9xmBkJO/Mk9vKhkK4c0aGSAMJceDR19ylkyJqoo7dgFjhQsYFKlFcIgVsU0xKG2AI4KBbFkQsbQqv+6h2PuP5KsCY88tTCzdiFvpicIieiKi83xRsdBGMlZd2nqYI7XWXiBXS/MDwLL8DFIm4i7vMJVUiuBMYpVcIhI7ad9Wg7KiH2z5pyPsc0EgZEsy691sQ3Dtk306EmRtZVi8pOp3kTy13MVNKe7TFsDHS/NMYuMzbLKfGdPbWq+wKVbFovkLf8K3i2pb6YHiCjITxIz/hNLf5WY2t/dXrcG6ZPJsaQSWpWC3Rd39uZ+fjS7kbp5IFDPPO+eByJC/bhn9hp+h4J085ScmR90vCVnWAIt7zVn8VMSIHEIHs0bDV4oH9/2xf61VggpkIO2IbRqP3qWdDuuFN1RwI5J6pGOmzycIemuHjXchZ70JpF4uc1PDxwObkZND1JqLiELkDdqkrYiRO5a630zfGKdavfbzHQwsjHeeQ6FPmMVILX5UBRyg5NBfWXnv6QbBCKbIY7NQQEsMxiqm28TAB6SyTmZJUYPSlPLTEwnraTDuTdman2KinOujW5HNCS3p3VyChwxWX6nyQpUEkuTroYiDkyYG+wrEc95ewuKW0KjNx9xOYqhStpnzET6rciYuT382ds4t0+Pa/S+uomlsdi1fbg35j4lURFMA0RHE2ZN8ja/+J51wYYmsoLkT9wuMzNRAv84ojmg9ehlxyXMKOg7UEMhegbaoGPud3HpxOdGDbbSnAw7z/qpKCiuDESr1XEgUdpcnKJIVuIQ87wDtDajnwLeDPOISCaYS1UNzH1KpP+auwnDPkXIfRykJo+3KZYxANjBX3m+NEaeBhv4E0I5JCofPsenVsJ2G7BhH8RLk/MPt3FMp/UlNCpvTzmhWAXwG9v/lVzlITe53EDzZiL6y+tmbOnp9/eibM3Cy6K50MoUIgbuPw1MIBPPfQcefXIZ2udy3WEzDi10dEqmBgnKW0yzm6QOjLVOWXmEwTC05HAXmyJmZgKWxHmSsaEHXYpO1DUZ6Q+wGVMwfoCzGrTljU8l9yKfYoYiFOvq6/8L85xzCZkCq30uV4j2874jBfaL1A/RANR5JkeFl9MBVIlAmMemsV7lbBbYojivL+XGbuaQDj/wS8wlGZpeHvkY53u8eUzoId7dN+EtLIAfyOrJZerqDX5u9/uvdemnQgsBfx/A0NAnwnhBwHP2B3psS392s6kmJ6Bhs5LOILI9rXSXdQOoSexqkmqZrWDWzpLxycAkTcV5v202aiDLW0ixMEoLxfujFpgWh4BxiMgh0wbcNgLezSjN/L9vaZhcPW7+UsQdcH5FbVNuJGHslSqvhmI4EaRVtJ0Nk6A07D11cGdpkhzIfo3ZW3UB/DZRZ99huZ5pO9Z2Ylteltr4olGBVWJaWOOjOTi3TIudiN2dkvvzB7AGxLOtOH3O3k464AeTftVBhnskgfP0rGc2qUs7AaSe2T1PpNA5jqnd1w6jce55VxNMfhdlJFFYJPWgZjaDxcRTl0NiXArpBjvDFTzp2D/zZ/egOeIpwTRpUmQCSSWb1PHJjsJu3X0ZEoLhUL+JLsSTPnmS/XfinJA3ItHm0oBhHLOVId3MrW7aWIgz8OOteoj6EN4FT2YgntN28zf+zQQ2KGruIUpPvGuZ64coJ4JMP8Aq0DK9/notstPnLPTtx7r5iwKCszgajcdlAHAzQ45ctC6puN+OZ3oDnbgPrmchmyyDPh01CszFyQ76M8/6JZ8KFLCaKkq7oW6s4oPJN9o98K4fwgfft0Ovkg+xhHDCa+UIHuGcd4A3Ya/9vrw59LlvJkdxTfiV+CMgRUw1LYdhkgFaSS4wegh8EjLBGnayG9RpVhEn4Q8vVOYx3/yooIqBEMBio8Kc3ebTFJzO4ZLn2p4t+0Wz8pzdUXqkD/FZv7FPuknYOYDCwxRF/q9X66I3dJvkyxOyeQ9A05RITbSUWdX5v2bkFBEFP5CXYOKkjsFHWsSbvwfQnuAPoM6+jwbkJ+Q/pmBG+KL2RozaKLLLz8Bny8I4byZl3G6b0ShKTITVO4fMUGebuVliAUtLDtJ7JiQK642Lw8E+rZ27qHe/TwUmlZTqJUZjmKs7rmkLCEEgjVXhFk+Ms9w1psczozIyEpWRYw8kd3/U+L/AfhEb/Vb/G/cI4SA1sWv3vruHa68J+nZ+t97wBUfAeN1QxC1PiTaQcXhOqSKSFqzly44u1qUIwuqFzA5wpQ4h5pNWNxkmCZSDAR+RMJuEwWWrin6jwEFFw78rtd3INljDNvCkL7aZQItW1hFKpQF3er+S4rQZeLjzrvqUbtUbyNiotXIycC0HYCDecgtAnwBQUejsnrmO3MRaHKeNra8c3x4XZxpUdg/yUnZ9/gbE6+pmeQ3A/JpEYu/B3Q9tmW84easelNlxKR3jVjhBnlCR9S201NkDWS09EifV55OOZabrsUD5jZWw2Y4O4/3B8yQYw7uGNhhPbmjOazNLdt0BurXMH+YRNzhcBYYlQgehiSupJ3lws1w4wGmVBW2OY8zr/dVgpXRgLKw+fsURtJ2NKvPsyRsCxMgqDHrB+ZMyaTgwXjg+y3d7AnozzomkzwvzZPJAO2cKnowyaMVxSeEsT8YGYS89/Li24LT8aWmXCD/usxnlsQSrZnWLSMR/YN3QA/a+nmf3pAbN7aAGWebsn72JDTMKCk6fAw3nONdUd0GkuN2UAZQQ8z+ybAGTz4Cm1cc+LjvROZPXIx0ExhFKfbXKcfTm7AAsjnFyqm3PLHS/slARgg2oRBaiXZC+kCo6JA6cYTL1GZY0442deizwTO+3W78HvLaPJNFnL9pH+jqQYpxAt2ZUmMi1VENRen9aes7X3dAoa9q2z0xI5JZnakeZEk+xC4909tSfong7ODeOPfxmHgA71zb4nHfPBHgyBexuptPn9YqT/C6yAVTPJFp9QTwzAI79UIJeg1xpSrGL2LU3Px+3ll2qIwgAcHJ+xhbvG5HDXYPTENIMSospYqtPNcgHPhlSBtIe+1yqRzmsrmq/yHajIIzIFXNa89Hpr1zxs1jxEzxgfTIFCgec7PrXgCBpyijP7B569aT3c6YOCcQkk8xdPLKTb5sWURPwCmTENbGocNR5JhCZeLGAq6to4DHp1TPvMFSd+BwfDYA5PtMgxtMqukeMF57RSSvNYeayp/3BGom8YoliPug/nIIlyqfMGDuGP8xYLNdBGtom1XuA3aCXiqtehDEB4NTkSgvWPMexNSsP7dLOB4SGp/fIy+AsCmY3xqs7mVtZO5Aa3U2BR094gOOV/tg/g8eV+53GirW2ZaJah4/4SQBEoqgHPcVSHhmH1mZwjeYEgyige4SosgIu+1hWMhQT8rhMQhFJHqUwQtsD2jdyQPCuMDMb1AwgH+Nv9nJOUG976CzwITAKyh5ai3dsqAgkMEAJ7SQMpM3pK+v0vd1T11hODA38Ek+O6rxqQeAQ8ZWQXCvD6hx1qz3AEbwpHXs9jPEm+AzeD4mpLwvAE8ULOhjRZc65DpnLW+Wf8Lc9wTO5/OKS5ik75Q0n2VIUSWqOqtKTUTBcJPs7LQjKWIsIrFh07pZGzhAYABzuInBCkdOInQpTC59V3TjrI+QQ2OpJFTrJ1xnPcBA5dRl8rlcN1lclEwQzGAnGLaW9WXmMKN4uIpa+qvu7EG/U6CyekJaq+VwQEiOaY5xPvCWmF9cYkFDB2xbTXy9B6/wY3VOSZARTJFOX5RbgjbEBySLNWXymkBRRpY6zW3JyVqmY1k7eujxJLPpFgteE4MpoVLYCUIkvQUyaUFHvuGUzY40FGTDAO5CvDbY3baO9AW5K1yHTc7idBpj2vljvFWoBM1O/BcjoTgtNppxwD2O2GU8zzSD0CDKWXiWncDJFLAtMTOW3kc/RFKAoatnCfOz/g0g3dDwfam5HnV/c6oURKeGUXBeQBqv8jXG7yCdfNw7/+nhojeJds8Iv78ZlEksYMBrvQxwmZIYvsB+uMT/7k4UgisTZNdsCT5exCVXi6J3grMwVnlGpSwB+4Ld3lE8/eriuXXGQ8EVjHXlB1Ngi30TURU4SxppMJhGgD+oZ/+Pzk+X4+pDGrhRhKUdjYwYbKzjGE4cXhT2808RrfoC5bjSznJZmHySePA6ATHM4pzM40jr2ncnIxBY0MCFtyreVxZ3zVwx6vqhcfg5I5UTjR/hrpLU7Dw0e+A/qyyQ5T5VqxL7YY/oLJHXJVxk5gqm9KEuszJ/FOwDxwKZmvR/8MfYbQaNMpcO1iuVH1sP0ZVQnaw18R2mnEfqSzSrgXTXYgHa55XJqUG8NhOlZ7GSH+RqQoQZK0MMiYf1q6AtrvxEu0K3XYvjLRFPe2PGQJykIruKT8mOeSNGzO9XGvS/n/kKU2o3iwhzsmy/GAv456AM7iVovpMEsVjqVxCGakMKwBWWwT8Jsu3k586C3GcHHvi16rIaV4NukoPWRcjM5tTyq5nITDtguO7MhOxITp8r4lFdQME63Eo10OU8DYRuy6zdwAilHjbDkjdsEktJtYKhDwSsHO3TcVSYbLvHNeZ16PDA8nuEhV9YaiwU1JKkTaD/cosaBOIIZBYCNUJstT1BF9T3WhzMUcI6Y4Zjn3HgoQF48MDzlbQbGPAGJZAKpI2wktSB6M9kf7I+E0zDCI3LPOwEY/AXqTH6AB0jQE5j/YBFnWrS3yRvcEh0m5vgNoRnC99P2y0wUYLAIAvgDg7OpHoQ9EabmyjZMRwdJ4CZP23jgN8iA7hzKs6sooPv+l6IRU3CORkQQ3hDzaG1zNuYMotVytifrNBmJ4unResf2PFIJ/Zw0PO2ZCfMkfBbsKR0UjYjSQzCfSKryyUz63GTejFdM6yu2ZX0SxZQoiEMXgwJ10wbR9dP2v30544jGQy92angG7FsgxYLTv8yIlXPwAFjrCh6eSgBEZJNxZk7UzEyowt6jJTV6C0r2Hsrsl+kMgX3bjbfgSLTUEp8e2a0puXgqgylIf45JJNA5szPWTmrGHpA+EePDSz4qlVZuTcpPq5l8wDMX3M5D7K/hIyJmMyNBCeyzwCYzansFqkdV4ndlKN6LwOAvG2cZCPGS25Fw5RqBPXAPfqYL6IST0IL1ErpA6oCbdzjA0cCkTxDlKD1yNqLCJGSic6S5KI/Y6+UisimJmhfimwfTXgMXLN+W9UVQC0Af3B4th/DihYuNaot1O/J3PHBSOWLo2TdcgRI2ne/oL5CMmn2jIzndbcD9v/hS77Ex153iAffOfjGtAbaG8ryWHPgmHuJ8edbsDYAICcc2BAbJw6S5Adby/3dMa3DgMCpFra/sx8r7BZS58PAdhJdKrghCDSIcmNBh5W1Gu9lEZAfFzBXPJurkDvHyRloVcs+/7dH22wcxtRthvEzkFP4/qv5217IsSQ7EzNzXuZFVXd0kGzOSyJEwAiRAgF5QD6C30LsJEDAQQEIUQTarqzLi7OU2P8z83Jwm2azKjLj3nL3Xh7t9ube0EeEo85a48zMW3Blw5LgFfhZCLl//FN8acZlrrOPL5kUA33BSwOkqFRp9taFJ1tLk4ogN2YFmJzaLuRrw8XXvbGqn+lPVOMBPo0RIjwtENsrQaVA05f47dRSS0NZDH9MPNysNvjgwqFcOCqDZV09Tl2qOUXti5rp+MSohTyLXiLDgQfR8DTyoEh4/VIGs3nLkLei0p0jcyyrdgRXUccHt2h8WwNa9LIuYqXEB6/xBxXouVKziLsFy3TVpVccDzsjWtlYCK8aqadUzMIhvt1+OO6CIOw29Z7oJB3sBF7lgD3HZKOBuvtSALd2xzSuhCyOSHqFCDYdTuCCLeuT9aTX/zTm0IhMSws0pthmjVelPl2+CIgGyUlQjFa4jBgZrldzu2xnBJt2whxrDdMpa0a5tJt0G5K6q1QXs6zbJxkm6gVOxkRk7wVYKzGh1LuMQxahPqgASwmESr0gA07V8I3VzLtN7RzOmi2lS3QerCGhQ7Vxlr+Oixvf9ZzB7PnUysUCral3ZMcPVxoEyRAhB5NgWUWzzf8YAnesHQafOghdDoHk+jjQbR8TuEt2QdOXVmL+Wlb8yMHTSfckYqv9+s5lFa1u+n6vNisFrzKe5U4++LmVya+XJDcdxX1a7VIJIXHa1x1c5ZNhN2QROoYFHk613mKsFziQW2vcrsh3W4ebTSARuKM2c+yTQXYmgZTQLJRmcPHVyMhKlLl2f0XcKNYn3LxAl1HMfMjJLYEYDPl1O6WX2k273i3DmnVk1VviPQdG06niZOuYlbFlz3LccYaVAnK6YoLmnbtUhVMlPJHjy/MCRWRVNSnzvL9skDjUfEn8vP1UftwYwi845fQjf0W4rnX6Qree9vA2cMnOXGDWEBO4x4DuWhwwCBgLdBOsIxtaFjGUFeIlTPIQfib6THRdyVU0j6Kf9NgMdb8F13JC082SizjngZcI4b/WhWjjknVHH3wZm5o4GdUo3SqoGUCaFlGEbrTU0mahVkbcNc1g7Aeu1eAeFCwz6RAUCEY3o5fyf6bqfHuZMh/n6yYAo1JiWcPs7mLim/QjwXTIoV/O0IxghH1a2mvCDhBCs1tQJlkA5agYkhl2ATqKW4L1IeKqGD0FWYcY6mY8iAlzYBil0nXiR6hOWM0Hshm5N2QeMwLr+nGL2QgTRYwkOXJHlzBbEDrVusQ9g4axIi3ocxbMhAb6Ast4FqMFL3wl1dWvrLNjmDrIKcH75BZpmuQEL23Lf4dmeN5AJdhTUZ5y44DY6LYQLs+gofCUyvtzg6Ey9Xvt53E/NrERLWf0X9ph/incjXRVCo7QkbeC6IGZ4qhvoHlcmONLEM2KT+rUU+omuVa4EDK7KmqrazCvklTIJUw629mXNrMhPsJqFb3LCZC778R5O6WZpK5Ng6fYmwh4vZBUfF9S0g+jDGya+yfUlTY4bivquQPI0y1N/Vuk9uz4NBj/bwUYHGNGhq18gw3vJx6iuR4i648QHTEAqEv+gAInbe26pav0JF+BgPs/nFNrqHmKgovnIodxbcjvuY0XtncfEpYsfUjOmu5wm4wAljNWBnEMC9ehihG+zUWHew6eL77kCZDEBa8dcgyWW5mFVDHc1Eq+qW+1SgzXtITYyEQnLA7o4z5tNxBkpoK6VuXh52Ch6AFTJC93QoUokj9OrSnNvM/8WOl0zge2li+IzuI1y1OnAoPux4pELd8R9DqeDTxm/zVHrjqv9gtzAmqyCxLquNFFNqMHnsQhvipUb0/GMVh8NxHtOExwaWufODmvpbU6gkgRB4A5F6c5UOU3yeo+56XRGd9VUd+yn0SiYVNVJkEl5JzjwIqJHFsh7g5/3eoFd/CHjN4+z52vL6YFgk1COnWqPwJM31a6N3P6WOxRZ8anpBV53pAg3t8db8cMXMmYjXw5DfsUAtG0vqMoQNFWTVcje+BLe7SOyy0ImD/JwMd8BIN3DWMwzYKcSyxEkUhxXh6p2W8eSpC/hl2v86po7xs00SghGaSxV/yN3YGeXGwJuXK5vXCXdkk7HmyKf8l+kPP4VdBofGhbHy907MeECtgHKsvEpMlfkYF5dphuKPSjoppeJdSUpt+ROUi28cHw1uXbAx9bMG4GrXTXrHHd73MJ8RmTAmS9+MKh+YS4tQqSqjmVUvaCvn5S7mypetFPthlbuuXCM5864lstFN/LoKpswzON2W8JhOiR96jpwR1OC5rJ4oSqacYJqg8vIFDI5x0d6YKbGcxAWgcYkGdSVsTu1QtWX7lSPWA2gjjSFWB6vbvGkVpXaVSdpOxIto7GERdjxGK5dnApnOPkaim2QdgGxN+14wCwVeGtCqNJcQ5CmUyBdMJ6vgHuwP7v55TlD0Vk6TDYydwFjYxhsOoru2//R6q5GqdgGYR99CiOOisTwQAW8CXYzfFmAz8LUsKlfiCRgeD5xmwWQ50ANPUikxhyawF3dcKrE8cQ7QuQldPEZy2Sgw/DB9UyJ3MCVJMoCdmq4BxQYAuI6/OrOO7lkIFus1gQ8MM9j7tdfjP48svVTzlJ8NFzFrtMpADgdWDPdphkXxANQTdx7o6CzXEScMhNC+ZVqrUEdEtewPVrQzq5zJ6Ygx64nx0ZJouEkQQje4AYaqLT51d62rEOM+HgCGYXBpCcrahxoXTWpie7cStotESXwnkUxdbl/aAEtXFVRj24cKPJRqjC1cy0cMEPjk5bgyNI4Gf33YCz6X5HUVLXhZc6r6nm0cmBjm84STL+66ZrUlo/4w34U0gVamaG7y8GmEZehnWPSqJ1cP2z4p9IG0KvN+8fY4ozHHun6yHPMYEHkK7E/Y258oDKY5CZYQFznDlTQ0DqVYHi9jypxNB9yv/w66iWgZszC+V8OUqhCm64ScxVAxicUksG1v0jMbQ8ShvYU0NCTwZ0WQHQrfYQjE4vWdUCZnVieMGPPcWDChiBQsOezqEtD4CDYvm5rL0PC/NJcsLwMfHn4esvz95RoatqnOR1KEA/GGAEsVi570eem5Go1OhVQDhWKKnjPVWP1sIKD21hH4RnI29DFpybiBxplHmOC3z+jgkx4Fgkcs+BV9Y4FxPoMx5t4T4bi8KuxDEfk4N3RotXjN4qZqpc17tVzhztO5l5o06wBtX23Ye/N25X4eKkg5Ng0LQ+3W1GA2G2M1UFD7gQE6DpkM/174VV+B95vcSzDKRunz/KeQ1X1j0cDVekZKz1xho2ZhYq0NiAs/1ffDViVux9B4IngTQDvAeWwRXe2GpqBt0CZpXk/OLYrDHGslzZY5VcsD8exfMa0JFjj5gC6RWWOIMrrNwEWqoH1jVVIivjxkCRMtSIxXG6I7CIm4nBYi3YoTUDH0yfLzwibSrrd0Mc75fkGIjQbssFgVVLXgS1IgvP3VTokrfn7iAb0qekI2m9cw3ejnVRlPj36yvTo5g2sTayoSdHAnLJDtFKkIhQFUKsyN5X9btTbcJCcKzxdDQUZ9kk8mnaeD+lr8WSDc/avOU3ReXdWMl0SeDfqISh7GI5423k4M0zEDSPM0XbwBX+Cbt7UEEajPIjR4yvnKm6fsZkcw2aG95qWmXHE02jOZ9qE3zF1L15xYKNEdg88BwCShDvWEvG8kbDfqnIkxZHy/DGOkCo2lhAVcSgtHQBVLZVTw6q6gAsEDyADs62gqdy5mdNEWyDcve/amYITysFuV6fBusdVhAOOi3osGak4WYCMqy3Ac53uzNfrSxgPZOOg6oye5eAxQhs1q7qqr9LwJbydp9PsSZCG743y4lm12eTYmDnd5cQMT4Il7vB0jSTaT9nyBDe0kxKuZQuyeMljKWlPi1HbqhtqORceXEu4NdGoixcYp1SgRtPV6RSha1RWFvIAxBqZSmhZbuRixclvxQIKZaW2Rl/VDwCoNTJGC9IPELpiHEblkKXAtBWDFFbzV4GZlQa4CNnezTol2uRFEBhMq8VxarufuhoHUyoBF+M9EIxA6HqJQ96R/T78YT0VicIVTpp5Z85+Dzg5nvnoeFGCS1aWJVH8HHWruleOYPtHiwO6OLHdY065QjjEbQc+kZowKYDxP534+lyqm9BIg9mOQuMcloZOwGuQYXUcr2mboxfGBVCHkXGRo9tkdAIpZA7LqhgmnKhPJOmfDWiwV04iJjTdFcOV4VAjPqCPpHJQmzuYMicojEDP0z2Ycae4YxPg1T3pgkj729pdPYqYq2MzD4i5JhuqqjJU7F4QvHEPTGpKEwVjjgZ1HUHm5WHmRx7nprk6hF3XYZBgWK4uAdxRoZ6RQKuAGhxUMvoUdHasZqkuqakLh4EAayrSdsUk1CyTTE4/FewkcYKPMfphZPOLyHPJCbnVSC9BD8+YEAJJszcvjwFnhja4T9RfUpAzx2f5JWTEmw2EXhXDKdRnwCWE9pMlBblcnAQoXsLVL8rNs/UMpn9THhmxNFk5qfgY/tsTuElsBV9ma5VUjptHYM5A7IhCO3WWrg/7/LiVIJjCJlYVb7Z8AzBke4/XuKLtdkBt5eRRdGAGxGVURAZPosHbpl3RLO8yY/noMN0kWwE3JVnp6IIxpPQmwuX6n38fMP5dyBA4IfYHcwbRHe3uFsTMjv0gToZEDREde79G4+1SGE2LtV4O4zOs2YeAQzxWoElXbOgplEM6iUvaUmnd4XCulzdk5DdhKUXe+2AtPhKB6vZ1f6ARdWfIpm4lJzUefKLsXrE+I99dn6SamjuqCBUNP6Zwb3so10Cpqep53kJJWpFCN+uJ26Ew3inXooz0xvBiBEs3KK/8WcuChk/726/gY4aLLYPNNseweB/VueOLtjGPqpTMq/gmJT9UWjYwGGMYKIHHVDhyiw9QnLc4HbVqDg3vem51VgXdsiylgrWDuJQrS0FPtjeN6BWnLgYRDYk8xWfErpcKui8fI5E3EeMagXjFJ3dcYESTuBkIhdjmsg392CwlxicF4mlwD+xMmlwipZpy3kxxiHKGEQBFpeDzMXpD+zjTysBDcCE8L1R6mC0/chZZ6HDVxY4rA9jQFV+Troi3U9QnznwCd5shycuQQZPtW1jdWFA8op6cCkOxj5sMC6OBRy6iCoCBJvf0qGDQ9vmVxV7+/AVen32ECWeO7WCgn4/POF8SwL6NEfyN7/DprXUMxXFJXFWEUOFIzNdOmgP2kcdZSiWKM5iqBubY1hzHqMluhAYNEtEZ50zDMxP9KScYWFvcCaqTTKmPaUysqpL1aBlLBbAPJHtOHUl+ga9TsHLuZgyU9JYfBF6UBrPLRqRTsAYo6e3+/NV4RqxgkppS8drnb27Zli5zRQhaM1D1j8pJnl7Yl7MaadtdpQYe27sH5TcPzoPqOH3L48lFAkuYdWMemlIYWdByB93l8YuFGSG5dqWCB3m6OTNGUmWHT1I8gADrZoq+KHhStD6MCcRSse9c9z/hPnP9+LNpVFBqOAlkgw8jBxKparaVwFNT43ilkHSlpBCqiSHbsfcuFQ6ON5Nqrm62HmaE1b4IXecci9KucHU1QkNjLfiUUBZysYtWdcHxZIaIN1VqPiempYsqZLopLWoFdJKAtdKzSByqMjusZ9+vX8AB5kqgbV2zbXBtAKBtHzr1UhIJ3fF7W/8hMHSlNaVCquybi2GLXHdJ4MtBGHSlo7EiQJhCk46bGqxusAKmtG8pjk9vt6DMqw6uQaArCceT+go0v+zmkfQctRywdkl7LBlYGDUpTJ2vmDwAmkzH/lc3F36/gaXJSEXor2ryb2XL7LIxUPY+DcXj88GXEsWZDVE44gNQDnTKeePlbXQcDrCqJaWSvftCSjldJDfZloywOI49BWg2NPXcHqnJCMolCdpU0z6mkoZp0MXIArKXmJoRAciKJTxxTe5q9ifzcQFfmhZUGCAVS2Gfxqyikv/lrTf2LV9aluL8s2jVg80X2i97/4Vdv55P4kKKMYPJQQqtuZ+bfuE9iE3g6rF5KX2BS23wqmAVUA3UdEkJiw9CWkuQx3vCAprabDlnkM8W+W59prGQOy2NJKDBXQU/QFQ9mC/yGoC8MdxbK+EtCB8vDI3MRDwDbkMULM6NXvaFOT0mrtnbCkjXqw/v6kOXOe3dMa9l3OcRJtc9ENAO65DymVA49Nws512ykU3zRmSAxmc0mjbciLfAC3pcyJ0kKI9m44kH0Kl+e84aOlIGqthTs+FJ6ogfAdSVunGVyHTjlEDPoGmRrLuOTVTl8nc4LpZPH6KkJwevawYnnXuafHA4gU7eBtl2ej13LIPTyDHhwRQETIafV8maXXeZLrhAdBIemNtXd5d/ZwWRw0xFcGPzMRwWyG6gNkRWDj6qOkYeDDZf35mctImM/j0uVfPdZUrEiSd0yhd8sCHdvqShTkqEtPIibwSL8nGuDKcM6NTGHgpGtTh8NFVO0q0DxGXNR3CCe1Oc6aJ9/RjwRUhvV7zBNuLXuYVPXv5Ix9+FuxC6LEEZkt3J+ZI++ImD38tV29SOy8jdFle4BNgm9T0fx/nfOlUbkcVX98W0YjEE3Vctxjc0wOmbti32kvMEEPsO6HraP9//wcvBopfRHKcTG0pwamH07CBOMk8ZQgvr941DSJbQ0m4ZT0YES7qvLoieY6386iUNpiqWtU4iHo345QSZvcmyBo18e4+5dRpVVVVkr2kWALhGsgCRyCA/QkUnDFLjgi2iStNNhdpF5cCubCFxqBc4bMM87J5NoRbGKJLq6CbwRMYyMU2zOaCgjPYU9FK+r2EJvwhfb2NDS7lpoItyel5pF96jhga3nb9m9a1iMnU+w2Uz2Amr5TQpXzaVGSp+pD00FXAclJ6zKK/VO/daEW8lRnoFF8dIAo9vXY0zv32/WmB3QkK2T+2MA/Td4WFN1K1q0/BVVhILZHmEmQbk8ct0Jff5LNFoAa7hleCOxUueQgMXGpsWRgANjDmZs+UrtD02qjYiLX9yaVU2S/Uy4BiJVxGcoxLQMEXvMByHgYg4hukkh+2Q+IxzDLxtHeX6ywkC7I566DJuaJ0iq++o8Tzx3Jj6eKg4+yCFxDJGB6M4dpx2evYPcV+0QORYpQW8wYqooz+gxOpzaRCtSiVfJYGZQqm5qMksHnnDSBTvgt+TqgRYb1WEIgEVhCYuZlUeFjRWY0bV/JhWUm+CVShnIhkGVJOuhJxGb7Az5hBUzCJcKaYvrRCO3v5izn+M2BGO16qhwWxHyetPSCdjMCHRLlvKawV1uad8uaRqtNkLPbhHqbUkaydgysLwWcxsULGuXGt9LvEJJAGCw8x+yJYklvRVmii1a+4aTa3WR3hbAJnlTIA1urUFRA4eI/kg9C3v4bj7osx7Bb0j7EH2tjHfwpErIVexDtF12sFHS+NwJbOsI6sAYfVJqA4Q3UFj3cFVaYCZlBLMvoY44/YoJTKyXlkCDlO/0ZGa+mCQKM5lKdZoFjIftcxMWR7hqzFNCKsybDskbhYAydVY+airNuxjvde018/+FOQHuvx1GFTclP5RWmhNixXa40cnK0bJ2DkrRbnKwlysfpL5wIzMy00xNnIB7Q/vopd0i7517IoAgI9PJy8Y5kaxFZ1TPSHXGkRWKBENYb4ouPjYsiEBKhBVEkkehhBvCsLhfQ/AOsZ5PF+iCzc0LejxaGEj5rUh9q4b7Ih8Zsh5sW4GbyOhHzo+9d8K2W8j0Oh2V5MNXdvknXup276Rs2ANVR7JSWhLfQPH8gcwvDTYDnaTh3Q+QzXVda6uRFbk1TXejaw1S1fhzjVpEFoKCaqHgwhAsCvR3hoe4u3S28t0ZCJDLGMJ1owujIeapItqIn+t4swYu2X1C3ISNOCYvIoDlxKToaEKhhaX+Bh09TQDzot/2Bp6uR0sUyfbI5qLtMyveAbvDoz11RDcPkYkLd3HXXnhdWg/kooMWQjJIU5IKA34WGWdi7LSsJThCKgrs9ikClvo88j5ZR8/S7rYyprDRv2wDnBxgbbcq5z+Vro+mWntm320EcgUQfTN+F6kj6cF4Vy3ESzMp3DDbA8KSTrdDFInPBYdRDNAq9Bg48wmyicohiWrmN2SyqmsWRmAax9fAlNIG2zYqNOoj08glE41Ms7LjR8G0yz4DlbKXdc0o0uQDakr4QCIupGgnDzPklWOIuoF3lTVFd0hUVVG9hxVlWQM18Bo8Jb+YMczhggWp1Gxu4U85fGMxhAsvgMPEzJmdPR4xfiYbJboLisrYEg/QKJmxZEFj3WbeB/1CZSA9aXuB914ADd8dETQlyGkohuA6pSsngark47m2+CRNUuhOChH4bkZcy9qcNMAlakgA1MFgJPn4xrJ8MVUvMD1xb56RyCWcx6IdqNH46SBkl1AIwPn0CkWajjCsTxGUgPULaGqgW704QymnRTnu45lhK02dL/YAfz4cF7FJ7Mr7WRUgWN9YxPuq4pFNsGuvpju3YcBqdzD9+ghitWIr2XcLJdhDQDQY7rMfyv9lP8ODde5kHbphJqrMidSZR28yAuUUuR+xghmMBNxwIYesnDfnwSDJrqKYuuec0Zg1cXj3FxSV9O7nAKty/PIXaU5BIYGQbD74C01WeCx0tcdhAXn42PWXlU0jRYm54YieYEXIdZQrksuSatJZUg/kJ4piSJwddnVHwBwi9/Zaan8VAEaU4Qd7dhTqvwkZ5tqAh+HaMQmt5TbqrEM/4PKIyxmEDZkC7pOieSbqO5oKVVRz3liObLp1PCpjsbI6lTR0qLAd0pNpLqyVAlDwzKeomaBhwhVJ/X8MwLcU+RcTz2WORNFtdJ83lDnpGc6G6c3S8cXnVKXX1upsFf5XoBdNqNL6gZU9mvMRJL58H4AEHvUpow4Wy7ZPTlo42Yoe2hSIlsP41PT90UAm3ZK0vASx17L7ylasD/t5p1sYo35p0zBJslhUJ7OqDhE3jCaMD1oGx5iTwchdMru65PUtLuMcONaeepGxhfoeFKUKyEtHwgg9nLb3AWI0bSUr1GjlK4wTSYRnpzpEPAEbrogcddCUKJH+XkT+8n4mvNHdYa821LXzMwt7h6iaX8F2CjrfYMXRlry+aDrUxaSahu4lsUV5qWaDz7sP5p+yl/GSJy7AKe8rhKo8re2XQh9EfkQvIoX+s1Koy9N/2yAOiamfXPMfQNjsYl0i6+raycBpIl2Ku5xvx8AAQAASURBVJ3Xc+1scElccJonkCE98/FkVFRHuhIGt8BX1Z0LuYt9Xc2B7I1dzisBtUHOHcgi54gN2Zinup/n2hkPFvEougYAevRU1U0RNQkvgXqn65nruUShsDl27iqqUnLdawnuvjJtlyFdPKD5wJfLEqXIrkQEuT1cgV0OG79ZLT2aH3edvxUB1Ezza/AGavTLsSdTdqltJYWSHKPkacAE1x+Y6kJVZmycqUpq2BSLc8kzoMVn4W55KZo2a9HzDQx2f+Kr3HBNbBkYPNN1WFdu0tRVdAIC+XYpWCDAca/TjyIzK+IZfp0erxXWt7nGZse5FopYO0fPRNVYZ/aRXPUWsW7Bre02pI3vOgtFF50Z2tLsAJYzLD6jam+xC2txCc2TUcw40i1esUS0pdSw14hioVR9ZmQTnu3OxPTihCOVZ0/pbvrYGMlqTOE1fHxxKIS3zZdrlRlrSRG1wro9DFqkbmABMzu/DFEiZfiY+BiU9kld34XloI5m2J1uU1bXHIu/uavUndOeAQhTHAqpPOhSgQRPNBx+Pujt3IyAjLUmVdGi+rFXYaQ+BRzYqC33bO4/a4CqV2bQYVROCvIZU4SHAPhWjAAbORpOrTrIrmzKPJu8iRBwMbyE1bMX10gtWJhLCLSU33qLdBgOv+mG+0w/IKs/r3xoDXmuOfuoKWqbNzfFHKn7uB0SPCW6IbGa91nY0SfgUMHWwAfwlL22xAaEanps+m+rJoIX7uiMcdiVhQyhhfjNNBjNzQnlUc0cwl54Q3wvnGeuyj1lIKimIXkPt59CYaa65t5cXkgmgNki5D2yiOZRXadIAaDTICDycdnTdtatuKTwEq1UMU55XGq7HAtEyxO6zBr3uFvPJ4yZ0HB0VV2FGlyNPG8U9sKQX7xTPDq+VKTaUQAEpwiyhca9xOtiul+chO4PzE0kYlzANZNOd2KDjPlrxs3F+InL0mrzZN44X+Q7/XJV64ck2hZU2XJ79rskVFuCWImbk8n2PL8O2Jwl2K4XykJrNGAKF8VxOxKG85uPCBmCgItn3MPk37g0T8PsyuSjX7ZahRbXEPTQhiATWi1LA5fhVYJomBRZxFPlWCZZcKWj9jAA3vvx4RKCLnHDCCjAdfM1PsD8wdDNQZcu20E9hEdap5hnUrLay9stbQAyumTYokpyspBvdvPioEFMZEKweiMTMwuIgOjkAmDhbw1tn3StZ5IwDZLNKL7y77IiEUa5SYuaDzEFIZVFSddfTwvk65O3nbZwcWUTp1v3gDtLyp2f1UpU1EpCdn0UpBMQNr2ikYvLGLhBROxkl4gLZ08Pq4Wo7BBYMNyd6jGSyKVERvcD7YWapsjfRr/K4CxUyK1gtGJPDXd8cXCXOVJPTsze8IlyA4Gn7AqQC1IXrBlzrWYGr6pkPMwMgfs5/zUL6u43bZUnDqRcTBXvk9LQW3Zaej0DRcB46q5f1ucVhdyoJc0YwJgFY2IwDyEJntayzfr/4//5/wLGN9m9zBiZmQCKZtVA6AEw8hpKqq7T7DCPJZgwZ5Qf/Zp5htCctJ6amTspRDO/ZjDSEXDqXowcN7CWDgTSMy/WdzxvxVVOD54sW5m+8I2YF+aVczco4sW5AFSFx1ZcsAu6OpVW+ABvxxAMeaqFR8l4tIbkIZJ3dBr2QwoqHhCdl3IKT8ZG0xuiwxXmfvMuROuwbrCB8LpWzDzxwjaEioCFVXrgMSLW0qUtwQzKow5ClcCl7+mQcwU8PoeWmWIeUYFWtrscNCff454o0dz1yXd3VO0sxuH5SuTuXJbQED+zxa79rHKZyVe17EJK4wLwBcxCFJLq6kZR6hMNA/TgOhzSbo5AhqGIGV6qeDQqjvM8KKOwdFlnHTn6mafsBpMnGNyig4mcgHCpHXboXaOSi83Ci3OxM1E+P/8jfAXJet/bJIk7zpAVGFu3O8KVgZmXsV+Z9gTbLXxRmFvNi/yS3C66Xf1clQ8m1xmuyTZA6eruKQou4O5qUpAlVld07oF1HqH7/aDRN8C2J/X2eEaH+zcwk28C7MFay49j3CTlThF0L4CQyznavnGdAV+GDGBFUEkPqyuyWwOudSPXyS3pUVh33ev5kfawxKqVgcq+WMBMLRhGcJWkLMtbUNf0pITCUcPUpnUkGoEHlPAAjYuq65K6SODJoROUcFlRXJeQdI6RN4PAOoT83nSHQZ6ORyrImO4BbiJdyL2NgqRWxF24ugTLoLQBEZhtMMhnde1AwzAibU2gg2EVNHiA46PdH1ChcfPFu8LPSCBaJXf1MyxfAn41xtucF5xTKEhXEi1c6sKn30Dw8bK1oaQqHpDE46KhNPYaI85yfKA5iyS6IOZL+baL1OTbQ0KKxBndj2KgVOJjWsPyORO/EqrOtZc+sOII7My3Guu4t0hxBlkz6EIYYklgC2/y4F6Tfcvj+3e42lqthYp4Pp/Zs67CtsnUorarGaD5ias2MBABdAWPNtKJl3QDTwGd+TDZsNG+jx9UExc8yHY251krNLCuvECP67L1M8Vl8BdWJT7BgLXbe+e5aVXOtdfU3XpLezL5vw4sSBOHxnc5jiqJMA+WLH7LSbzBXVmn+0VwKSTVcUY+xiUPzZGhFu0zzeKT/VldmIvZT+fzNjBgeobvYedMrbu8u88rpD0y2YCcjrjI0pwU4ZBnzCx2z49WM95C14NG1H0GmJVUAHAIiUmiwV0gli8q2guXkK15pyVQMOiPUsSTWAkozz95x/pAyDTUPIPiVOG5VaZMK28kkHfqC+XQd76cC/qgCCrgV9T+8isYn5ii5Wxp+jKhJDs9Vd8YrDHg6A44D9hwxlhe7jor2QJGeFjtlaIq3ctP15oOtAALT7SP2mOz/KIVm/3AgYT5VCXgE95Nbq5ytHfCR40Tsqay/bn9cNTnOTWy2RAN46e7w+efQ9ZErNR7tAoUYTuGNDQmJdzMr8cpH0DkcQoIDB5RkizV6kImARd5qa5hHOzuFrooC+ns3AUR/yXlUJFHrSpB3WnPdGcgzVPkFUa3dht2HR8h7Xc1u8rpxtGlfn/s4S0+7j+IK0vZxrKzK7tmvWRFGCtF5frPbikO9AKfYVa08CIesyopR8wx8LpJcl3/3MdNArTaaDbsFz1MOQz3tD14LM0h3jblGJogIYwGrMfTu2IsEKDHmBaXORQu2YEFu4W31yDnAXvoi8kVXZXvmbl+eqeOc0jGo8iYEl1O/ecRLyCqLu525Rkd4hsiyVN56mI0MN5303VGlzzgNR+Jb9zAiSfucU8Iqkqt5bBLa3O6zi9N09pbUZcxlDw8R3SCnmcD1dVEPgBGa8fC6IjT7FQbaD9qpBpFXYcpTIL7GM+AU/mrpfEgljvX06+dx1hMePgLzh4mNM0Yr+TcGwflCGRd3IpSReHAgdN2g+uQM9NZUixv8dxCBNnsJ1lJI02xHaY0vCZ6rXhJhY851Y4udRlVUWLmIPDsvC5qsMmHHNwsMGssOTN2aFSQxpwsbtjM2LlRIbGJYF4eHQEXRo0NlAV88CgRZtCMZUQO+eYHVZIsh8l9mvgd775QAx6SyJhGvKW3XPAj8KFkT2XZtkXMNGBpsv90eeA0431BX4kHDt5xHXdd96DUncwEhJ0N5Oc7hhr/tr7sV71Hp0yEb1w9WJvMg31WL/rzeG6X7/uXZxTOhV2Lh6+NtEGuQ7k0RbEwU4IbUeUdS1RV685xK48WbtmjxxM8dYHIgzalQ9bU1kO08jGouu1avohck7JqZo5juQ3oACo1+uKyqUsH+dIaNongreGYP3EnJni0047iTE4cIJ6Ze9Cimq8JvWhKnmJNes1mkrq+qME4IexcXih9TpOoA8neqtyfJNl7rsoiUo8+BQAOq+7a66hG8RMbUeRoEkPg/8nk7QVoLZ62v82ze0yqJiNEkqrapiHyyaJTYe7waO6Owh0YCYeJC2Fc72LqxQiLbvOM7Y21XYFnUtol4wYFq0lzi83HVR5W71whoT8SAW9DAug618Bgeow/vCXwVGm7oQIdiSanw3Irv1o2O30XArqnUl4ZdwB8+C/O/kHvtBk1sbK8bKNsLj+qclwPr2TTqpW/ZuToG4sVPCPlH/hBrIR4goMcSVE6Uin17cZKBZml4aBhbP+Gre6Ccrj/9kXgjk6sc0PIw95I9+duiFIV+mRBC9MbqxoUFPAeQCQg/BzzXr3CHvhWHSnj8AKvWByGPW7xVJ1rTLcg3VKPLaUMFhhWQTikM1UGThsZKLNcJIB3oh9xgjOA679aC76mBaMhhAY8TsE5H9j4n7pJMEtja8tkYH7mIHLr5SNgOCuAmRVQLK5lYBIHKczEMEVeEL0LX8Az/FwrnfLbTYrRPCesOIev6JmMqMuxnSkGjNqevOwqNRcmOZNDeQWFb/9MIyqvkt6mvCJyoDKxdLt3QeWkZm0JlmXnEo7rEzOx5GvBUjjkP/vfTc4KY1GVZo4wDpI7OQysL1kARxKlq2FRFkXIp4iAZwDiSDW4A8o+8OYdHZYGgwkCkR9vNc5bCaRtnwt3sZmS27tTmPEu+w5prJv+2lINWEbkTtvqJ+BYYlMEMHSJG2hjfVUwgV7QuyjhEWv0RliZW20gMfoiH0ik41oOqqHrYU65P5jSZcEJEkcD8ucAxENirlWnfOZWf2vyIKD8TAIG+hDThkwaQhboo8FXKGe4yWP04G1i4dQLVU52zxwZASp+Dd5WMUCluRF4Q+QYpb94L/lq4enKinKwDabBhzwBNHjlOEqyuzVz4Mk5B7okR3f0VLWlq8O3qziCL2hqRi9WzYB87jwGs1Hta8Qddx3C0qy+mjKS68wHM3TZFk7N994Cu86V7zxDrtf3wmAKHUbcFZRrUhsscFw6Nmkldyg5miIMtUpVVQvP5yn5EDebDNYLL48+htBdQxyYqsg5ySLmBsDGB4gxDjAATp+ZX8bWBxdUHdcrZEp/0eniAsGDeji+vVz+0vy+LniMXWXmrgih6sikF59IOCrXW1XfO7WYkH9kiHu5oPMdbfINSbaR0ld6bEEyhn0jC9vkCIoz2yf2PpKwrz7tEPtUL0mjAEMxm/lqR12i53O8LzSSd2rRTvAIgtTcShZWQ88CKlL1cTY2RYf3bU2U2B7XoZE7+h0xzPIVIN1uEvQkqQavPJePEGv05EYUiuYtBf7Q/N5dozf5JT0Z/RQy++W4H7s+LFujzZ9VZmlzxewGlR77MHwEoV73vtvuXBzpl5EF6VZXTRUpzaqluTHWxqIzPO6OG2efcU9VfhvJ7tI8IMn7wuveX8j5f6vL6aF3qwFYYlRIxIDtho7dyXUC4WkjbwCArq/RTxKrdTbPQ3JYX5SQW7n85198Xb0tOgcO9Abbtxirevd6ZAXYH8hj/c6gybcnSCwcGUuGJVuni5Ijxq8SZioOdB2C5iMYnCM6jloZviig6RKTuc6FB4Jwq4cYn2cKtjvw1PY60GVd8CWog7VFZNHhse2XrJmHOKMBp+qFuUSL15ICzywkRbxmJhvRJmCR0YB7l7sA5KkePeXDE/t/XLKzrGXcbEn/hjMOaaEIj1Og9kPOpLNyexMdjeEkeLp7MUNgFE2p0OXfBaBFRE/CS7yo9w0RDZNEQpGP3B/6s4q++mOHdoGgNNSuhIxn29gweudStgyvWPJpUEhW4qg6Ei8zo/J4hQ9HoKjXDOJ5oDIJsdnCG2jRP8FKL7/ABpTZdKBzupu0fo/buJvSHV0HzxKK7KfSGdCfxcx7tG255oTP+Zo9fHCm3l4cvmwX0YE+EmhaEqxdA5eBhtrQHAwppl/wX/R9wQWrnQ4MQZZDhYjJ/buFuNCeAuEouNxWWn4mBJFjloJpo8Z/fRMIsv0sDbWNm/kA24w5de0SR/qlT5Svuj5docE9wpoceGCK4Uyl5QLiv6yot63lovWrzi8t3gEFfoYMfMoogZYSre6yCY9RNtQ/22ZOkH0UIrXaJ4TtJZF7YWsHH58mbdyQbuOetoorLCXiHanwWkmhc3KQNx4AnI6nj1ZwcunvXdmIrHBNDP6dh6cIak7c/yL5njE58z0ItFHEo8a9FG+6aaDYPIVfI0jv8z3n40iruyOgGgvgC9Hc1E3buyLGrrJasjZ6eduNcWcHjiMUJ0UQALAU150LfcpowEnYHMZRuOIhpuzmpHmpyeDW3orwFtqN7jCVQEXKZxxlknYiVM1YkRI4hYcZ1UBBZeBt2CR68KuowQM6weT4RRgOD+qhm43Pnwv0pKJNn2RloUtt1gNUPhclVekBCp9ZJujqoU12PfA4rOn0Htb2+Qww9uSUu3FjayUGJOIahCXegPq0xBf1jH40r0wK6wIXuqOqKR1bIe9MUdruwO5Wb8G7jEwZEg3uYR8hbSurZQ0skL3p8ETV1S2LS2bi2AuV06NroXlDLtSKB8UbTo0FltN+F2RwsqQKM3MIelZd1x3PEzBXjVNwfH2xpupeyxJ8AOEPfYGvl+xigkQ/coaSTV6fQsqYXgjnZrsidhIpAIu/Y/8J2pMZJjFQEKccYm3y3bV1XakrEBM2Cam4Ml9VEk/ck6UwDUbtytv3Oqjqsh9D4RsZALG2gE8NsPNuuXIfMwZFtyEA2eSol+O2B8mawtRkiKEuPDEljIuGa2KYIvC9HpSJfJDAy/YBls7jsPwpQczoCHfUPef89vP9PlUWs2pu96lKQ3hFSeoNGwehOX3eM92B8ouOyIUDqM7raG6t5pGqZ+45ZVfvLPNGQJQr9bs1OMDCOceepzmoR6mtrJwsqs8nEZEWpYzU7fnnrq91ms74vZpWPzIt6x/0qjY9NMBzTs0kEVn0mD6VlurxJpWq7zV9iLme3LuM3EG/hcKdjP3LgI5wKfhkFvvzxikhSZqmrTvZgMBNDRQEnxYN7S3sU6CJ4a78zIiQXnb5erRkdiKtCS0OqKupuvnQtBgpkQIG0AayKTLNpvXN9PxkK/Om2IZSZ9rym2a5cGGZTrDkksXdKRiZE0h1NvUHhLjoi4ZECZfGKWE45xuTJx8PusZeBm3xljzmQaAleIQl2t7BTDUV0QvW4GP8NQxRoqmB/OdUfZY4lLUIHQhz8Uqjk3sWBrslOtrhpcTLxd23+vyCaMykPSCZcPTEXwfoBOuBCE1cvEjWwbWxkpwZyvAE2zk/oSmcMBtXgZENY/bre7opgAOSuGxFs+/61eUIw9lwBTvZ/E3cm4YILcBMQCiJBvcCSvf9QYM7oXxetR/RlpPfL62501Qm/O7gIsmd2qoCITyBoqLIyqHrfC6fFqIPKxCJLBPwMZ5aoOgiuoK01U2rqFreUlvUpb8JOZaeBYZQS8B1Z2IktEIlxd/sDikInpzKnp+yxb9BqGnW+FLSeEkZUXECoXElA/Hl8emSPSwklo1x5VjUjazaBB4l4RBPNWeKljHHrffNpXn2VILpjSsKdO16g6kBAGecGedtkA/rpE2v6QzI/H722Th5O+bAvPTgttbAmtUNk8vWVocVZOwdnKYgyZCWd2dlxFzuzX42zQCRc9uG6/2bvkdLTMEhmNL7mdNt1n+sMoHQdh1WDW5aPfQlHPYy6paGlwd2z7GIOQYg40n13CTdYZ11AJ9gQGq6tSltMRIbB1IznTSskhwqmyA3OCsjgQaXwnXE8FruyA/nbiJYhdK4JhiTjNALGvRDDPT2OF2QiNH1OD3S4il7IWZTh7XQbmQr8Dcg5cE18jeT862NUBFVL1NXKpRemkCritMrswtYMzy0AkjHaShES8/WljPAqSO9UR0JDHn4iuA0TTnqtDn3WWn56JYvx6wZH+N3JsaF1TrrRLklGv/GKf2aPk4/lDh4ikkhfKQXDuoZ8XVqUE6UusJpDKotkc9wXAGiPYeyMnECaXqrZRA4lkd8wFezrh7nuZK4mlc3LcHqhD51dYhdasSTWuTWUUoHOkIB2xM7hPQCh0BRb7dkRc38qNdARvEaLd1ThwYJ7LR3jp/BcQ557gDFqzloLaEoXAEFneLjhqEKMrG1+LggTILPwkVXNLQHJA4waAT2dm9RnowBXbGaNbj+Wg0/HzN5+FxvUeY4kWdwcPQHMEnJzKw/nHouFhzL41p+TVSRIu3pmMbbIOIEjzB7Kp+y99Ug6i0FinE34PsYdjYZBos+IH2Gsz+M9TiGYYsFR4LSYBMFOjsyRzNVWwkF6bBl9byiOvmHH030Bd/3dr0KN3GufQri1NWw4SQz8rlVp3swfnq+Q4sjG0OgqnqAIoo1M7+9PnECfqYkq2PBuARfYHTkGOC5W/0JA71Gb7pUM6sCWKltc57Al7OntLiR6CY2FzXrFDDn4ln61x0WMxEi+ZHJUfpcIsyly5N3CEMxJbGahthLI31VzWVaMUxqUPqW6QRAgqRGt40Y0IdvOwatUkQGdpwkA47Uu8wiwcQnLTolrARW146QLscAYCg+AQ1cRsU22Cz7v2MbkG48glx1Z8TE04CB0owwR5mnheSIBtvFi9XowS0iUwdT+uL6PDfgzDQ2CcDdhbylHbZ+WyJNTsgwEOoLMUB3hz5cFgBGSfUd2VVRnky2oZYJkGVmDIa9f9dQHOlPbpTSv9FtvXb+HaAFUPdsgGUSDDIP5j6plKe+dhFAMSpkU4yC0YE8DUBum71dsn1HrHaf15YEenkaCBHk568hGeQPaljdNKmZoJ3ZReobNU/fVanKkXbjgp74IDiVZclKX/Mu5AQPyisrToo+YS1zSJW7WDIWRfb/3v2gcRIamAEkYMVxgxWI58FMJlAAmRsc4KrTXebtRCoAFMpyRDd/UWsKNXIlrU+3lY7Cv4YOYsaMGTZ/1R6McUOXd1fqglHtTxsEGISfMfzvFgg1uIBsQS94nMb+VljgHjNNlq5/UQMhlxxc67ajcm7BxEU7y2kWHRd16nVxbfkjNB5jaByiCtgkIKtTLVMxa0LEbWjmycsGw2DPACnq4EywgGg7Db0nfSh1+Y3ycIv3XQKEsdvdQFZqI0VOYKC06d9/NXkqpjTG/+GTM5xcI33+OMmjGQuh5BcIelLPGBp26ksMmrujIvmJ7Kl7e4pO29fAo6tEmm/EQmBoKCO+Z65cApY8RKA8CoIgImFDszSXVtFQVTVjudS1pcZQoCV5ViG4HB+9c4t7/uTcSP2weR3Ro3sbV+GgeErPdeJXItQGHN0W6DmgvsCgWLpRirAM6eOUFQifnZaYeTgUcaosVXG2rEDNyF1gFUOtpZd+7vTpyURjb+q30AIseOk6Ho9wLQEXwUtUrElLKkIwvdTVmiKdS4pb7EyffviJz6uSrlt6rpF/8NR29mRRsQm6MbVc2C6Cgb2IaKrqAG/glJ40z0h7dlCGbZZA8KVcxXrfsdadJPXAf8GooWV/4Zf2RhEouZwag2gZ66ZX1dvErtP6iCaJSHdJAZdrNzsMKgSs0pl3d+Mwaa1UiMKh2Y/cy2oWe75wHrEwluskzAHG3mOSw+buF1DNt6z7ugRjo4ZP1UvUcQ470BXbQ8HhMij0zWOBPBqafT+KGW7MnMtdDoDj6ixq1GnDmpjhBi96MFjOCO7pDw9tEo3orgLGBVmoOCF0EEAjpGGm3DdWFT9WLmnoiNjMCMCgiwOZzhjbj1AkX2z7NWerxqBWkXsAyTIF/jeKZFTkBQFUimkNEHtkgeiqQxvjDNFMaF6XHu0UjRelmXkddMtAxHtugZhLUnPBTQG+Myi1f0HuS+3sCA9DFluURbUHlK5Mcpb9nsblLN7xaWGeZqKoj5pDAdicMMP7qnp8ThgwIoo8YtR6c1FVPCvy8dCq8c6FAFw1WfVCr5nXMJKP+vH4pO/gamMupBIqJXFdXooWDJjRBwnRuOPSeHFsM+iTwXIyE71G8zMwzlxrztcsqHzBk9dN07xl6OSDMjrPdEFsuVZO7xwwr1jEuSBxsQ2MlZaCPlDMGuGB1J3llGoZUp1gvubBLB9Jgn0hlY9c/fmbbLqErstLAmA7VCFgnGcwe/0qNG5uCHevBaFbLu0sOiFYkQPm4RqavRIbXjTMPRy2Mbi1nUNbvLrGsgnTeg+CKLY/Nvd0qAW7vSWzsej6zo3WISZXfq5jLSAwLv5s4MH3U17/QTQY6egIcxs+jeIUYbQT15DvSIS9JD7M/WErEKr7DAjKEdefCntU2DhTrdezeq1QXtLpnbNgQaLiFM7zMFA8rgizThfeTRaIa0mlmNEe0OUlGkQcy275JJNgPLVo7Py7HLWiUFZswdnzk4PCTh6/YyePDKQENbhpc9pMTZQ0Ka4/rPDqeUJQrBBu4znt1qdl2Hl6uwGDwYSAzYPQom1Qxs6GNXC0qc98aPbr9+e3Qpdsn0W+ROTMzZS/psX0+fx7AmyzkZ4npb450fGAaiM9HI/0NLCuScoBkCPQZGRafu2lys/NU+kAQQDD68Tpw36kFgc2PVtkoC30cbi1vF/HIClji0fg+2EG8ILTVuBMQCyX4nPRYODxfgpwbu2Gd7AbYWsCSRLHCKBXYe6SAjHsHhNpEen75RRAcu596vRBPwIdXYzaGNAbortK0wH198WP5KQU2SyHFShlG54CgKd7YihhS33nYdikQEp+jkLihFOr8IklA491LXk7UA4KHQCocWjTgF0F8SrypUenyDmFiSrs6gq4EDilqXZVzM9++PTxho/98cw/NOkgs8KzZmMjqDAeOraj4aoBXcg5JFnCXedeFeZ5Uu8a062yMpbm5AU2X8RTntc8mJbl4ruAfP9p3Fln8gk1d8PpXKpxLld3ql3PhlD8AThoa0wt8c0GL43EGlb3+L59rlUpPDMDqj0/khj1zpxOEjgEZV70pfqLfE8qVoGRghWpupFB1wst3AjOzKlv7qHI1QyUgBcJ1PBKgq5Hb5uycKs4CaweqCyzFvgDX8KVWqu96XJAWbE4uMdKN8wBHUiVCVxbBHcSHugYXIbOv1tYLe03OMAjWTpg3gXF0yktKCITJdEssXEnFZ48nzPj120nqRqgDpwgs+pTahlMg9CXSJhyiwiNRgEzNydPoIYF0QpQX93DRtXXlHCNS3+QJW/CYl3T1HRx4SCRkfAxY6x7IreM3ALRLDwgD1J07DKEJ8aV6A+cWe6XOKamjlp4FsBEfrgA+g6bNJibsLkBpHL7DS04uX8MBAJHqMh7H3Y9wzeGI0oHNXPJjskWBHSlc/hSY15TjyudSqQJre30cey06iJaBHn1yE8/p4rPxbDrSNlXQY1YhTN4vKYKgK69ECfSpvsCnGjgHu/q6aq5Sg66+6dUJ+X5Ab5YR/eyTtdgLVUu72zULQFHmteKBgCU6lqdBM1cfGokfq54j8myFXuqTGtMSjIA1gltm2iBUnAElJEaUMUjflLLK9eYXGFYN+bDyz+QWKi7jCkbTpOEhwz75Ja0cJlDzXL2KZyIAWOcdrxxvIYOutGelawgODJRMo4UrNYNAkDPbi3ZkKcshTIuG0+pPEbF6Z0G+uh4GC93bCXu0U5EE1CExkns8aLZx8TcAYujak0Ffjs53RJWWMCgHAieviD/Y81xrRho1QxB8AG3nCTKOfog4yG3KOJTNutTPPl9bDR64SCJCYvvfue+WH4QOUKpRuiydS7MQIZx+A/PpCD08hohzCIsBuFWqBDS4mIbSqJwPG2Rdk2yckRsOUhrwGHwPKAEK7spjW5AGdd4vsqyXXJIBplE6I8F05aICpRWrn8Glv0q2c53ltNNYNvnueISTZb0BnkFoF14yBNCTeA5QSuFu0Gl/sNvt8pw378IXudIpAdLUwroWiy/yhJQLUOMCBnFXA4PgvQ3xgejKZ047LJdcX0MD+58Os8EFXlwg9ufUDAAMM5omWAj1J5xlQS+sIbX+pnunilyRJzybUi4B0DKcvJXFUcEh1GqVMKsvKa8WiA4dYIag0ZEo+eOh0b7SC3iwWxMb3+6CHMm8bP4elv60stLuceS/rOVrd8TtqT2hZLmzX9n4MmRvvLjwNtr7ZBPKgJQugf7xSfdeUlSs1COC7zhHfUITcy9sqob/Wg8W9Mv3+kMlhjege49pa56bCtAgMDRlMpKWCIEtXED71dm/5MJVq9mOzMiSRFswGGbU06hS+i7d7VT45MXOYB8eqM+qcDdvPd7B5F144qD52iKaE110Rp8cIxB0vX0y2HPwK2qVaZ676uqi7jWz+cULsEqzJbeXlllfXCObIPrXRhVzWVSMfDj4ieX/KKmOsWQZauYx0UG2blRqya9hDsSq8N17dNNZ9uR/eAAz1oDrfh7HC+IROu7CyygRrdKlum37hSo661i/avudLVLegiPhBkSd6LwqlbpfWME7NFt9EOzjQadBrira5cXp2iyUF0G7zWJNpfL8oGEJ+npvueJxEW5CFRULe71IbC65qLC8draHdZQd8J8O0MW44OsoiGplJ+zVp7K2s9ZroInxVbaNke7AOwGeKlJI07qjB5PcCz6OtBykCFsr0dw0ibjYded5wUMYfTaJKYbBqhNwzcjIsYq4y17cBVdFYZa+V4+hqbqXF0fjAHai2B4qlMN39pUsy9wMIV6GIZEGulWOfl0L/rkEXxXeyi3v6GF6Fl0CFLLcoSthS4HOb4KuiNVOdxLXLJaojtMbrFyMb2VPDBFFjlTWT5hcj846XX9FNOz/YpeenZD6uliVXng2kLB1TXspge6uC7cwuocHZQd8WVWQJ4/e2Gqp0vjQOAR20lRiA3UVm97jNCOw3P+rq8HTdV9od8Y4FI9legng85dprkWtaqmCfSaCDztp5/p6DgqAxtpL6Pnn517L+oWez4o7EjQ8VhEpYAT6WBvFhlEBlsp6hwCPXMRmT9G6ubhSaQja5KHbqR8/LazcOm+Fx6yV4SS7ISUSj4cScoRbnF4IBpX86cmwlW8oO/K/vSJABrfSUprBRL5Gj2CfZLjGl6Qdv9W9YyqMcLpmoGc8eoyhKLNVBWdtYPekegFaR1OIoTbNgMo0CUh49OB7WiEGuv1r5ncN1Ue6e0SqhTq3jeLMqtEbikD12exBDksKEHxhBhjqxIK5AK74Lx+bunjk7UYSDa0wjbO8evQ6P7Nj8h5Yg7X3blrOAxnsUWZmWX0Bb5P/f4YpUKt7MdV6MIUdlX6Z/pRAOr2wzH16o+sxEGSxIUfR0wy40LLlVQOq27Njf7KnaQETE2hUKpp2yr8HF7iO4F2Beh+MNeIdmDOBylMKRMUualM7oDkZheD3v3+19Y++1j3IbWkKGonhPoCuqngKzJzO0nceDjiTK4BamNfewyEsXwEIdyG75dt9gIMOrzE4vtnK/UB74o40hj6FRrugy+1ai1UZyXZNyis5koJGL2oTbHh9DKBi2UJEsqxxESxUMqwyossoqKuGaHuSqE8SsJ6SmekD2Y+uDi0CCbsxk5rsB/D79HxMyxHBVzgODFQU3iqzcwDaKBHv45qCgd85mnWAzBBRoHd8d3xI06hz97zrkhbhWy+pQzCFmTFE7TaZ3ZrIscdlgzA25c+wDu3CsdHkqtkVNtOcmfAybCMPjUjdVfNfVcD+i0gisNBcFE1AucKapzRE2XG3AlMs/0ZgtjAG9pVFGA7xicozArSTTu20bqFN3EBaWY2+XXz7AyIlMpTAsjUHd0ZUHeAB5DHvF234cFFrohSge+KPKGMDfZcgJZ4F1r3jcIDHT6s0Q1X7VU1zp0oSGcsK0wBNpmvIM3sFAaMSF0DJCzwyi4uwtA43Nr8LM0QN0Wdt4GI0s41sD/ZONHcJJxYiUpAM+IPaXwpYMoKFOpU1EEtlMd7wVWSBanDgLERpg8lzI0OpJq8dMAWG3XcNt55mi9DIpypcjFT1B2Sc0GxDkFdXzp9gAc9IiK5MVm+b5WA0ATmDPVqXY+BxvTqCXPYU27PxGOhRAUYtFCsXcoyZfqVL9agxpLs95YxX4tlIdAJiCjIpBEy6txjwTNZqqSu2lzhxFlk88IO1HQUaFhcTM4Aj8d6jD0JFIiranJGSWxJeYojaUYeYg1PcwyPDLVk8/1twj1SAWRfPT7nzJA6aN0wQpHV5fzOkRXDOGwHhhpci7u3ih5xX7b8aLF2AmgfXFgI6UPt+pStuhuJx8X+fJ91KPmwQ73DLwKA1UEooBzyleiGvPR8Y5enyQ0A6DgBP/5ZiRLDqjV8LZdiDzFdZ5ld83t7GLdkR7YpxH1xLEqmMCheI2mkC7ZB5oU9gm9Un8X3MGYAUbCKr3BIiR6zPhnopnb9AcClPP2WAdVOZiAxwrVdyA9x3ZfOin0BUN+mkO6zRnAWgjMJVKyy5duqppbJR+JuDmkTWuORtYxMr+ny+1aXBh5EKL0SVjFeJxzxAJ7mdqq9vNcNDjliL+WjUg/hg5M2xKojPY7oJkA8iqnDiOCElkMzeUqFjKZUkGOV8DiBo5kMb/OopJvSQNSx+9Xa9gjg6SSaIzxVOVYC6wJ1S2xBmkWN/lmOVqqZ2VBliEwKBlIcgmpI8bAVoeN3TTjlXT4L45hQ9u/S5iBssKmM3yjyAliZu4vEb2DQdDCZ/QWslDFteW0NnzIj/6MVGdhbqfxEfP6QlZWwvsmYBUgPG4zl37AI0mBsX4EgugsOuBX4cF+0DVoU1H4nFf4Ww0q5jHzclMjhGXZd4btXScMvCRyM2wj6uGAis8s/qWqCmiQrP+eVrccuRtCq9m8miXZz9QDX98uEZtm6NiNKuZPGA+5ogNqCjq503Cy1+8DwCPvCN7VBk2+HD3PlKC4GP/J7vvLnyV8G2bD1/YO/cCwVmQWEwGV2XOmWJiNPsPK863m8cRBtoyQy+/Hzk7LLdr0na0TADO0g8PZ3chotjrctHqLnclrr6IscQWhIYwxL+cNR1HOBTvvvcXJ0XPKZLZYRyhqKfMsrE4OJEJvRtUHlTReTG5NFqqxzX3Q9mk4gqRjJIqsa8sSGdXmOLuvU8YszQUWgeG+EqV5cBmJ2o1u9g8o1iET2OMo89i3DfLMYH5RsT2wTzgNdmsd1Cyl98PrjzrhAy59yYGgrK8MXnvhVjhrJvLR71w0vIgJGn+4Zu331VCB/JI0GgeK42lBL7SICgoov1w/lvG7QgpKKfrKGdBKWT2Pvr2taAPIrEhp6oj0NvSfXZK6kVDo24e8Wd2KjL7bugo5mPPyCECfbt06T02OZZZVbCJnkmyhmdMFpV6C+5FgZ3iEVKwnKovSw6VZXuK5yVk0xWkTE9vRPzOAevENaRFAqCriqdhvlKfexNOE07swpPDO09tevEkOITtOAjyWRB8Tx2ZQFvZCZqvho5Sh7MTmWtpilqrZBAjbySnPdL0hNPdXJ+ijW8JEk/gJ+tJsEL+Oqhchyo/ikZF0HN5Fcg5yZxldj5CYBLOLBLR77GHz3E2pONJd+D0ZxvIAgQF3moVoZaei60NJuEajmoJtKrKZSKW4nfpu8cSh6jND0HlpGINe5T1KVWaLAtBKfFMkOStnvFjWIjK38qlcPjXLjzY1ctnLiFIUaPRCdxdSkPQVUbJG+WeyDMMtRntuULkftfBOpq2fvU3K6GxrLDNdMk+7EF0QOWCaDXB7bBJ4y/ur6bOf/ag87d6XKm3LMR38gQhdWVkUnikDAxDOTeT0jrRAjBYfRZTVp3dUHOGkDSH5rIWDby6lpeZFcpyc9oQwEKVMPCteagmz48AbFKKUk+nwrDKtNscy4SECjxi2cEJZbXRAz/AHVFNrmb2+KROLap4EJomv8o77MAxKsqosqXdUtdqb8lmZc4iuw2rJ52hYISvoS2+lGLSRYqfrLATUkyreX7iRiPwqbZRUdGiu2diSLMstVHF2QIQrjeXZRY6XoZ/GQ7M010+CeMrfVc1VGl61GQl59Mpb9VKDGEfvgbZmEhasGXLUXZOmq1TzrJPb9aaCZSyFwj2Is2Y47OrlWR0Kh2RSbD587RRxwoDuJ+zzUEx7MV4+tmlcwfNWus7o8R9aPIF9QStW68PoWlFjt/HcroXaNJRUdvN640VvnlrPYoLJTGTCdSWPJaZ6j29U3fT18ZDb+hbW0gev3vLaoDIB9WNh9SPce3Hq9gS3Vt5NY/GXJJKOXEDzW092heKGwd+51Jh/X1Ri2jImyhVuHuqxz3Du0la4WrCA0KBWij4Ea3yfJLGfjd+fo5N7BYJMyzfWkusyTFW1yAXK8McsorwifkgT7mdIchadOzigREWBQkMK2cUJVOuGUjRa8pKtCiCKssA15O+MK5AqylaUIFeV6jPzoL8AoFYOzWHjkSny2ewONMWWD+5HnRDawujVspz1E6rT8S0wSInJfTOX95Xxb1MZ3bExPNvGAXrWuOB2U4JXrYGK3VqB9BY7ESCv/QYPQLDD8hhGgwwbxWGvVsVUkAsPpS0sr+yNodHzwSZloZ31A4tG+H1dHkuStrJQuGa5QhKLzKWYreJ9qVw4Crbk8r9juwZwuyn0KEJpICr1fyxfHHVXg6WyyKzXm1GkDbiR15zp9XYzV96aqd7832g4aON2joCbQjtmiNG9nOBq9mpRB9RaJxryFKxxAsCOtmCabziaouZi6DEnapJ5rIVbRgydrCsE7y8AAPj22hpfb4a6rR8BttyPgHZq4u2IElzgs3hRiEh8QpxJGx/ZsCYzawUeeqlEEVI4ZO6SBn8LrqoTrJejNqJWSEdJct+Pt7Cos5k+fYI+gVJh1gACMbkUW0fF2KGSjmlJziXjR5g5HaIyDqTQfhIftqWKgPBqK8ufRYZrcpMXpm6m4Q/8v6HFH12WLZpU4OuR1TeTQYtyyr9OFJAjwKfalShl4bGdBF+f+hBMnyKqje6tqyFalsrbwaRIdmYvNmNpiXTmQ4M32gm7iLSqKbnJN8YIVY6WUkFfhFIgaPYSFAj71LylQnu9dAIVGU7d8MPbebx6PmgtwU1hIsDKOoWDuMrxBJeqgWs9cukmo7OSi0QWrMnANNrfJpJS87ONTo9CS8xYCWRaP706yRN1Bl1U0bjjNsGfTIWnMPuuMF3uFVa0/wKM4Bgv3GP43i0k6t976zBIGDVy6VcBikR9XDTsOMD+etGnmXhDGySdZQNcw760oJhw6K5RQ0DuPWy6pFBAEYhfULZQO8FSUzfWxt3zql0IiwJokM9jMEGvXrHrVqmrSVBE+gLivzDNz42n3GsGUY8/I0T3d5jgbkH4IP/O7fdllFgxoBKHmdDwDtbjrx5yMVIpm7AfkQK8uW2qKdCSoy5FMuihJyeaAAHrEPRZ0RPW47/SEbLLYzoFxLcJDo/KXPIlb93aw6clRxq77XIgYGLDjAwmjFD1t3WyKQ9IVjAODbt9QpR68/ZY+D8iVNsXj6AyMwTrXr2xPGnI0zmWWJqCbgithbTIKTSQQxDWth2V2FUDVSRfnIkMGq5TKHzOk5h5WQ6f6x5ce/tSf+s+v11///vzrz1+FFlt5cf5JnChrPHtd7WFS3nR0tpjwgVCFLl6sLB4QPPzbuE6hPgT2NhhI0W2AJqGkW8FtH3OIx1t4r5REm3zQuRBrac/3n8Zr410WtNhoQgmIxyDbNrXI5/1ldDH216DA8hFAq+Bi6v9AXPMBoGk2Uw5SYBqe+SzL7YMsP/OFVeVA1T9UsUsDYBFOQ2uGzswXfpInQcITr9GG0qzomWbbKWWy7Cq1eQU/JPiCSDx2xm1blZQjg8jAQ3KmGXTAhC2hy73uGx2TWkq8HLryea4LCpVIWX5WmAmKUnnV12PX8nJuxkYEpHqyrBW6iCdr8F3dqugJNCBc4foxgm30xC/Ar4Bwste+3zRwvtKkaEZxK+BgeSypUFWpmBGRSkr8mQbBOhScUGU/RjrhgTWNgk1yPswm6w2UAe9iF8C2vqzL8ebq9HK2CZTJtfF9FK0/XtUzzA5wbEZIgIzn8piCLq4NgNUwlUH5R7txZnLD5u7u8OHpsjUMT0pUJlUCWfjhj0JI+CWltKtPkWhqy+FulXrayMek2dEMEeD0mxZz1PIcDa6ewPBG8Ni582Pudpt0qCddNUFxFD86kBJRswzOxrYNOANFXHsFEVN1BM/Q8AJKhtDEp8oLcJIB7LvCEBk+y0dT7Pa0jOsP0GyVrFXix36xJQ2rzqVVhC3Ocy82U5OgR2sop85qoDHN3gpchIw0v6ovTKHkTfoIdfrVoxTNHviW6sJFTUzVhOY4PwP1XGxqYbh+xp/trJgCH+NR802kBUouXpJEP0BVE1f9og9pMsU/S0jT35rhpXkyBE11zdZ1pFsB94Er1O3IVeHcEcxTbBWPuklxPjD9aNjtEayEUNOkpiIgl17sh2rO3BKkadOSVwamdBygcZ/Obid0C2cwjdEl+P6qxp1X89rGr6C0ImSiDc8VUbdpD6aJYBRtRNyb6cNE79hpDMDbC5RqO3znP7qyEMZ5GuI1OerSxLpTs2GZu4QBTJWMrJ7C/jwjjIvwgYLuKUk1UgceAIWhXkmgmCLo8YGha3E6UUg731p96s59Fa4dVO33qzKyGyzTegP4fjBylKhVlms11hLpxuyNv6BAfbkVKSgZrOMeZ1TFpNeIcOwMWSvXHAXdKcVNgQnxb67xafbb8mDL6Vw5WDgYZIMjeYJmkD2qmZ5uIHRqIBKznLgDCoTH74u7nJvIf3O1BdFMiX+Ul1+V7vbcAD3wnICF9ttz5gDHdLdwMcPuR6aRnNSIRrtIUhKjfpbqwu3rACxWhUdx/Rc1eNucFyirPrjROPRDqurroT8+YE01Vnvzx/1RrOALPkEtDS/IoGU/UjOhus+dr+MsRXMrPZgDxwJc4UqsKgMVXlEEmg3WQFawxIwE2NprMtTebd1UGeJ6mF2dO0LXzZDxeEdLb/lSvQ2LmzUfyalcJaHrACg6r8oSW7i/pO8NTPHIWZMi2w4KqOp6BHI0M2OQzZYZB4ct1ohBvZp//vHnfvR/+Q//dsDXn+pPL5A10n3mP/7L7//f//jXe/m+U9RTYs44Q0C4ysiA3MhOV/usFG8JP5o6o/WX7ToYSfQUML/NdOX5vtZh6jKiD9+B3HI8LBhTqOO7wOA3/B8F5tYhaWnBHLrbT+xRBcDSvj008wti4oe2suN6nZ3DQjgJDZ/oI6TgNAUxBLIRINcD3B+HNdzk5X+4Gi+gNEb+AxN7j1uW+YjoUwrh+5BJq7DeJ63gkFKzx8SnlSofkAwfd7b0HRA3GetpBjtnf5of+Zgxx/sBeGNeHwFyngQ+iC8A01II54Ablh3lA9SETiEVoqidROT/O/TUB1s0k1e+L46Yz5tMg2RGj9TdvLyosV0vAR/vnB/fRxu2/atZIe0T1Xj7woOsEdDdXcMoahuTAS47HQjhS2Qw1oYI0F2+Jz6zrFhzHN94OQ7rpgOhRmPDvWcen86+iPksXApSUkg0LeujmP0eOTfXbAQA4JyI+b1Wpvql8UQpHzUUEi2yMxIQRQA+CrX5XhrZWNwu4cMF+OAp5MKX7fjl+toLhJ9tvdvjwy5/Z7NvXC6uk1P9dU7WlxTaKbq7yT7nXE9TLo3XnYUDhYqY04aHEgZspp09Rq3nXm63D2GoTh6tKyWKXsoEhuqMdg/s4JMn7DQiHfAdTot4E2yfP6gH6H7lcDXtxfvMFEG1GZrkkRDW+tdcoTD1cOxero/xn3bTBi4acMTX+ZI8VtSYxeOsIYFUTdwhn0LTFhRDwyBPTA4qQ1oDTwJ6SIwem2QkUAkZJa8+tJArZZ2CfBRtzK+cNSTV5r1giQ8zgz4dKIocuHLnblSnNs5jnhxoYti0cRmGMUEYz2N+IPTSvZePvQh+H3AJCDDd40yqq3rQJb3z31R8tIWVdAzxOqPCLZtGZFU91BGxiCyqX0x5h0LfBYUuljHSSFd4miEUAXp7yyIcd74+2Gp88RpnEUD2J1zMrbwwkbTDlLk+2hL37EoslwvTG1HehnBQ5UlTA9rB5m7Pt8nY3wgBqFD0XY7TDg5ZRQ8yRLGuruizsJrzWB3nBDTpdMur2eOSAWGst6jomrbeJY4L/JRrRWxB7NpwZGclUJ062Y5DL78KYeIuas/FcQvAIVjWJ+UYQdqdqBCmqxa79IO9x+838F3eHAB0tznZYuem0d5WQndhYXgZjZh2qZV16/RudCQQEwYp4KEjh1TrTF4yBNCtYgbljoy7VAKpIMJWZyxSN24Cu0tz6rj8GVzTtU4EdncBXfFpeWIXRPsXvFcGVso4eiRzM5ljkEAdCr2PVhj3dtEJ+idIJE6wQZtFDUTK5ckCvHn7VdQt64D6vAh1hBy6cTirTks3ARoaFk+uLmsR/LxBWBqZ3dwfEfggiq8+0QBCTgJSqdQmXkaSMm2s0LnpKWdxN7ccFthIcTYaTrGc5uzCiEUiWUMMyhsEMScscrf6Ib0qKEHuh5Veo2quZUj+kPfHF//n/8M//4//9PW3329hXl9fP9/zxmmWhJ8/7z9W/3a+/vr+/bfzeqTTAuA6AGjxHp6rW4VxOOBqeD/aEeS49f+DZFAwyLefz4UW4qf24M5R53/gw8ylfYHGI4gxmW35GHz7ZtRrKhFXmS4IKeIQRjq9xIUtE305E8jgMBdjUXLX1vM+Bxc5XKPi5/JRxU/EtKZe1m70kD1E0/0uFPLyFG5jYDrT7Idj9NIL1PYxHxDV7HGnMPI8JRR9hWFBcEv6EagRtpiiomPLsnGPyJx6n8515woDU2FUKrETAng3HsFEiRcAtyXDlUtQf47vhgoqJUbG9H6NjBoAagsuXBmxjgGjrCk/CDZjAHaVddOH0QkB8LMLVi03v8RojLtQrvixhiU/+xnSI+GMMPuV+F8SNRSi07LHl+I1Xh9kKg/BnQ3XCxNgmCpPNSPJzvWBW6zRLUZQU3VcozNPa8fYlmraMIu7JiS/eAiHrNS9t2zQn80CzO8uzD68OtC4QHED4+rSaXvXAc28IoVbTkewFUm6TtSLofPje6bPmtTd2VDeAlwJqy+D3RQeurbOdUTt7OYz8p3yy/ke30FmwSwIYgGuMSnvD4LFQ9LyssHQeTrO7LHwV12RB+b5wKU1Ar5k9KsyrDclpzzmyoNXBpm1sXvWsjmbF1HGC4MVpmrfeD8weZY5YL57bRdJ2+eXxUgEx05yozXb1c/wNEyfMeBidcEjzVP3+Qwp797DCMRb6Rj87DHzeIkquXlAPqwPaRK16lal5jHPYK7JMKCNVR9MzqupGNiYPj7sUqgP7RrEFhuoBKuAoAgzU7pIg4iXtmp1qZWLeT6Ykvd0iVowQqpVBHkzlcMm8nI2mgtF1l1NKq32wk5l69wm6WW9aZrP8hsYe14BOuEguGZy9TNKIPNLDiJbWLVghJh3awxa9Hd4JvVkpZEHqgw5Ww/mZe6Zboz22eklny46di8fUKYBXHuK1eGUrBPQgM6I4oK0fmYZWBR4/ttalwqYHAuFfew34om4WpZaA3u7g7D52uPgyu4Zp7lygvOVbTa3gnrZWxmg3tvx+LMbKUw1b2C45MxYuaVPdaUts1CdC3hExxtyQ2SpkJISynDMMGKP4ofSDNSbtDsviwpxPOlL0XAQRJf0fHlWRrQkLKwwXhCPredKiLCRjsVBHAFkqtx3i20MS5NnbIDiGPiwN7J7KPrm1VGIJEW1C/3Y72IWbahqLtqcQfh17T2mxx/JDYdpCTPwlrf50GiWDElGhhDbnz8d6PWZkKGuTFV1+S5zO9oS2YgyrcZys1IeONofqQhosioxhnSssBv4GGQZuXzqTlr+UT4tinWJDshXbPbgxrJXRq0YHN9XLjK9l/ROgL0LRFCYCtfrlnKakeLjA+nFVrGR8Bpr63xcWYD6sZ0Qd4oNx9XL54i/dykHOHbrKaiUr9wNJEbIYBIqzKitFP41//Dn1z/8+c//0//+37Twvhzcqvr1a37+/q7zrj5V/Nvvv899c/71db6e56nqe6eSMPE5p++Pill/BHlmN8W9m0LNitRllW1i3vPX8SkouD0vAlPiyHEL34KXJPvmSUNxqmgRSr8KT/9oByoIKzl2HRRcfnJrw3SCC6kth/x0kaeb8wS1rYgxMgoWnUI40APspwmM7RrdkXFBdz/qKK9xW+JgSDVw5+o59+ryB3IluhCr72wUB6jJ85llIENwu9H5fIicwxBzyPgyGRIddXvv8YatTwDCoZaZ/5Bnn/+U6jLxoTlYEKm89kL2d/GKB5zgtDRC1up+1crlxLSLKatqKYDAw+UaMzg7PpXb5ENoEWMBnucj5ZLvPFjfG46aRTlfOlW1naNAyIccePn5sl4/B6zPXeUA4nej4OprpYSUpv1OMvykg04DCDqsrvaFtUtrN2zkr/XoHpZHieZSZSIQGLfAANO96xgsq5rxJbyN2nptK41KOgAYFSQqolbHe9KA4Md44HLjdBtlGmdzbZeHDJH1W8tzyBoDGGjmGwnAQhgxovjI3z8ILdvDgq4DuPabqxxOgz39tF+DoVBOYtec/glnIU3RA31ptzE1wi8m7dEoUs+4/0aROIcC31Dj+kgKMzN0lOcC0yULYVrDqIvZYm+d58+6qegy6+g3gZwNADCFHmtjbJqBXh/iTQJ2ZeccuNKTzomYqVMreTJToWpV50zk3bGYpRp8tFQA1eiZm74cosPgfEphhaNGqH3HGXrykBKlSN8wWwdCUuxWGZqDOuwQbD9NRS2KpWYNHr9+AGUtbSqA+pDYbpG5g/Do9eTbTSp6wiuhET1zyaljsSEyklcRt5ZDS2ntEl3XJV5ln7gYmztdvGATB/O47YASZkJeg1jF3+i5cvki6jrbdlzUceJEcbrmlmbWPqFpQxILe9mVNXIAWU2IRGOMoWW2rkJHcnrMwxenI5E3Iu8/7fVw6aoBos8MehnFatcOa1mezt1f2crDEnQqZ8bOaNmS+DpISMvg+4S0xto3pu84/3RlF/PCukKy0Q+mg6u6Iezy25bW9FxXac9f3VfqSnAb6fD+yNM3Xd4tHEfXxmQLZH3TX6U3O/X5Ink+S9FGBhfxbi6Na5gcFlMgNyn3YZnArkVmbDwoX7LG14ue3+eGrsvlyG4AgjFvqFwhEAQONnXADWayXLVdrfdBA3htVKbrUyghVNdCopRjsj+OqD1M+THkeA0IqCB5zv+L/sQ/9gwFeEjdljTc0NLyvWs9wwt1nYQdRatzChyZ+A2yQdr7mCAOgvlNnj0AdnioNPj+LMf3pSLM6fLarq1AKPm+9dVrTKNfFQef2/8PsuzHw/UlETw5dmOGJI2+M/ELKPtXIdmZSYhk83jc0tAh4hKPD59lck9sUP5mn4JJlyB0iyFBjSewOvlFvqz0SS3TjEW+KDFe56xYp7bYPFqfleooonvn3/3bH//+P/zz/P3Xr99/fZ3X8/41Er5+ANOONHv033//159//fVI//SPf/77f/75Kl7OV7WtVol/UVVhtAOeDR+oNI/nBPmINZ7DDALzTW9quVbgYvXzbom0ULSPY3xEIdFuWfeBjfyGXH4yU8tUbhyUcigLLS18fsS2xZaG1Ud/n5shr141irzL2X/uFNwQSk/IJwtEdkPm8vSvtJFqf7ir9JaF1P78ZJTt+JzAZi2RKIscBcpdDOBYf7fMFkmPvzVF7jR6H42uRAynu94wC1+jsV92hWnSit6Nr1cEPMX1aBqpvJacQdE9KzAP4YqoVpKwJA3zA213d4UGF0XKWQsC5jsX3DL5Em9INpp7pH1N+nyqKn06D2TmAXzDxkSQFn28NUwgGGpwX97EfO+flJfxAktpxXzs5Ti0KsFnidbaJV8aO8bBMnBE/uMaEAubK/jRCK8GPETPqzbHerILeEglAGMAm3ktZy6NjxpyUFnoNXpMexef+FuKpm7c31SeTnoXt9oye2bMRKBvrS25PP0GRE8i4F0/gIKqebEm/VS226qYMdgdi3QshrydYIoQPKHXIqn0+1HjSftUaxDXXlUma5muYmYKODO4cBChqEc8HlomTuLdZhA1/uop/HZLmHrZNI8LqA6feRdeZg+Mz0hX/JKNueDQaa0+HApG7Dw5yE0Q0jAKmY7kfkML6rvtv4iAW5ubPsmthHUwm+LQwO16CbuHgeoCLIbJGdSQbDqYGfrzY4BQtCQc+24rOWv3lZfHtZ1U5L0j7uCGD1B1kwkGIlmzSV7/GjzB3cjP37Pa25fVWGVYAwmZL3ccvuWFn3u0ekIAOD+4klqsB4nF8kQdeRD6TfA2BKh6NCGb8GEsst4Ua583O/1yul1z9Oi6MymOcMtBhmboKiQjwcFUQnCSc8Ci5hb6YpoIrsC2OExieYq0R3VaTjcWF7Ao1LGq784Qc2C0W6mYqTrtI0MWWekSWnNbwSde1VC1DUcSBpPE5zLUkxRVPG5lmxPmwPksmk0zBPD42FgAT669qnZmrsZn2DHy597686+Qg6NW4MLdZqAyUHODE/19D/sS0ikNaAas4r7hshKuicmxTxcF4hApixCl3+dRp9be29/4t5eaSfGtvR1gEr2EcThmKeuTBB9ca0JJbySSC500EEy36+d5PSvathKDmmRZFOtusVCv3NFZ+u7wADnsAdL5tidbIzbZxHPzmPdeaaLZ4nX54pukXOFa8GgxaADFigqW02B06GXnR8v5Kx5PnnBWjoZxkFdvlkRwSsqsWiAjlMVLmBo8RcVDrirM/Vy9MOzWslnWfBGbNc3W3ABqfrDSJy3LrbELs8G1TRFuBTEPYFf/N3EleYODaBRLxb73MYWwwhWVB+LBR687pr56moDBa5H2CH0+P2bDZY9NI6VX1Ud7mULcCJ5XaX2qc6Jzp5DJuzzjBWYoohtzP3WfYOFiQRfg6VBeDDLTIQRSjR3pwYJXkuq03vd//Oe//A//9p/m+fnb6ff71+/PL7L/8g+/Afyl+defv+59//z9/vbbb3/9+av7/Ls/1//8H377r3/99ddfio556n1RJusw4Dw+sMy3YsqS/mDprgQLDq3FtU+D6bz68vqtjqO63QCkTSXgGQ+OiQNQnbvVp0LIxYz1BaTppJFEjyvYwuKqficEgHssuEac7QGYx4yg0RUXBZfn4RagCdX4zAjZ01ILFxsjm4rBK00P8pLGVa6bx8kZED57ot6ZLb9SPyFX9b1K2iBMKvoKNdaaB+HMnMis09V6Z7oCNDs3TTwKnl+rzV+ljL9RcHqmzlUKWO7m/8DLESGZBvbP9F8t15DjUIdR+mE/amSNfE4S15FaYH0KNcjoXzKEENOhr5wAzJsNd2d0wpdGZ3djZ44F6L/mT8G0JcQUWtyJnBU+xStFn5RHv1ZTBOtWieADWxRhUoQHhLp/HHoAoujjzun+VguAn6+/C9GXp0unGZS1giLrLjFSRGKPbkouLjo7C8Qh7yu3WgZP5XS16NgSjPLGrI86aDkPR1eaYFWxMU8ZsYEEFzDw+ei70UinYr5KpW9gzw4in5mMSV7AMkWhseHlfKVqV/BsmGe+1IJWXmfyraTj+xwzorr8D83b1njR1vEfEVx4i+SEajBRYzWOy8wf45RoOeC8AVwHComDQ/10tRSR2GcfLnG/CK7BxQ+E4Lxz+WgYsnT91INH5rURHlyVHQaHRTjGYXQzQiXsQ7Uv81EX2JbiFuoS1bpB1AxawxOuXEqmGcZcsDDPZXMaRU+n6qqQ8L7LQc2jVOylqh36a6dgmEdAyT9QtvkCc5epejlD4NrBt7eigQ77IxM2KsNmuP7QREM3c7FEwIJjEUYPfsF+eTTH8/ZECA6qAFDG+8tP05VNxtZUUYzZEJ1LDa1k6dQekg2h4VIABU/IJDWYcSQGCOEKTjP23CWUeiTwAt0nPYt3afPceb/qNTDv9hLvWD7kxmjDKgZ4lZUrZksG4GnK4AeS2ZYxdu69eYtnNFVdmqGldVPsi1ue5p0rG4jux2eQ6/WMpkwvTsJaZONWSgvOaJaf1EMsyfggiQsrqOF3NoWamtxCGKLd2wxkVZMjMkcOi7+ZAx+NQYo0/5k78qHwDTdyHE/r0eK0sXpXJiurKoaBbFV7Q+qjuzAkG9jJRZVwdQ1+HNYaQtIMcN2cBUjloTwpX4GYigFgug53OqZ0Y1Hrdob91il9de11dXfq4UpyyL0Ew+S2MkJe5Bd/yNrh5KZ0MwCXGfDuE7alZRgA+k/qRuh4KomZ7fKrpHv2pMb0B/L0nqeROsPAae9s6Y50UjBvcn1Hs3IMM91jI02dQsnqZhKOWgEiXAdT4VpdSUx7NKBkTM49ocvLTQe3psba+tzBIx1HL8sp70Zpt5wAgl7iZlIY9nagtX9CxPU+JQ4w6Zf4eA9VnW8OiniFzyvOI4diIf0iAedJXN3TUSUVMHiaZ/gkBfzDfLPMVxM0ytCObfADE2jJDxnzC3B5dfWjm8Dv71/626+feBfwOvWXf/oLu9/v5+fP9/vXr/edZ+bvP3+dHzUX71/zo88//fm312v+y3//KbC1OUg+4HhdvcmtJVJeu+4mnaNYg7syMEcWXo0KTzBKhPeFGdoN3bcY1mrPFHnVyZYMVGqG1+mMrjRSS9Y25JmCYkQpn2cB4Jw/Ri1dEh1IaxHQ1rj+hgEdfN8F4vPlTKxVzFJmpMCVrvGglUq7hzcv6hFNBnMU7Ym3bQj4kM5e3duD+PNPCPm7ShV8yGLpVvXWXSQc83I/pd6aU6HYxvzoeXWxSRID/gFNNz5R6SPwMXKkjmegNuyXWVpEIo/RU+yL2RYqEibFO8T0OoUolo24GeVE6J10zjvAYjLWwHB2aXZwb9o7nwsXW8hnLoWrv/3EK72C5qhk/bL566TNG1pqg5ols25V1E0ZRGNFbn+tLCp+BOrW+sOtiHNxx5XuhHq929t5YaRe5ZJYvoaqPkTVzIBIgquRp/SzNUCXrtKdJXlOpq7TgTyBRhEAyBJWGudQDx6zyaSNv+X8iCuwbliBpuahlcnuMeOFTR5OsTVWSQRoKw9lIxdcq6g60sbYq6WJTWiKvWhISSzmUiuV4Oy6AdJVX6qFIxF10G7fzuQjvuz4LeI6/nErBQnBUnlilvVmqiZwNcLb3Y83xkS9OkPxynVSe7Mpb4wCukfSx6m8xZV5n8xkEoWCHqgnE/WM44Qe5DAjISqV3ECAA+BJ7ZgG0heyoxtiRvDByGALnkIwEGV/12SbOgDUsp/lYRcs4G4jn8W+r4x19uj6vXmOLdbkHIgAEI4Fndsim7q51hgYYgTHoUFjgI8YTciUZWYEEyXXMdlZ78Mqq2zNFVoe46apjYdTHD4AiFbSBXiZySaGMByFY0c4LbmxhyC/nVcoDfs18PypkmM2dW+Ztd3gdBKDRqOn8GPweJXVHI8FK5V4PQHUCSFMJee78p4663eR8G6Q9izw5g7ypeIV4U1V8gTEBPXyserXNzGZTozAMDEqJi9nkZNCJ+rU5/iCOFrwzGt3AqXrI64WuPasXVJofwCAqANJvHtp2+GIweRipfNTlF+hMKNEMD2poKAtXa8njYfg0Muc0JDHFiII6wlBaBlbnYqukfGAmQHCZFhjVcjVSRUPIPN5VGmT0rH7eTftK6+rKeZhxryWa0SxvnuFaftaX7KN+oYIhC33v3HubbPA2/TIVqNr0+354LM6MGoyoIjuRccU+02/E3V9kY7t6kVV8MmQrC3o/Q+L195UR0ywWvMgsxcCP4fOzG8oXVVuUAp4kcNKSNOD9ghsI54q8KFKvKGDzDbAYEviX7Qhi+25PBJqdf8TTNMGUzgVW+VgQ1PiURR8aozUTOmDP9e9gC4Ax5Vck6b3W1ddUUyE1jBB4MrWI9d8tuZCzdFY8XhGMRRjJWNfyucoekpjnfEgcKW+8eciddyiZHgIm3ajda4spuGs6omzwienSLOinxI1MHl3E5CmLwv3VQXdX7///fn77+fgtz/9+PqHP/18P3/7+99GeL/FPnfeD/Dz/e5iN/7lv//+b/7hzzz4t78dYP7rf39w+rk41ejrW85uX8NWntuluR1WSU2Ac2jXc8QuUBXbg1cB0jlXemTR1edVA0F2Rd8EC1q55BLXaTmRDfWyIFuDWzDNLNNILGi96kbP7PkHiMh5m9PQAoO92gPi0d3s5Ej6LDHrVT6oq79ahNWpOrwuAzP5X7l5+06tTqnsqn8dyN6/Zur7RlCtCDZs/5T20nZpG5mRAKxII9VyGpiAULlSPDkGw+qZ+2lhilBVYEbfOoIvWQiBhPz1GHZV2WzueB4bsrjOLdee5mD3sX1mStRn6W5lmhYIHxX8Dt3QttCAzQVDxVaBlWDou7DJHVr7cFIDeF+l+FrZDzN5wDgASYtaSiXqWLWvVTqh4iT7UEiBEOYzF8/cjq96fDTWkY7t/Tvex+PxfRBVSjNMcpIiWbkFE+uzK8paAGFIZ1ViB1LZ3GqzuLzx+lOrYvvvEOKA9Qsi3atvc5tUCEjtifFiBwHkikUNyQksrtefOWuDTfSenwYvDRTYt5OK03gcfRGz68R0XolaIKfUV9duGv8j88tNnKHu3MYZCPhlUm3HbrDE6BVdY9g/bOe1m1rTasJEH83HPUlEziNw3eRT9WxDyYIhmUrx+gmX1eNBuET6PBG+xUElHQPXBYfmICWyACUbJsIeCjcDhdIwa1Bjj2yw1QFwkcmQVnHcnJ0CPDHMJ54FIobvorVXGd82wnY/GxXfPtob/kEXJPgQnjI45ONxOfn53jZVUrJ97Rouxk0t/QIqVFrFxOSjz6MEJlDNK9c+QV4E5QvWsTGxpIcF4E2AeKCvmV8xUJDF29PDeRy8BBe+k23ZgdyJS9a1Os2h3txZJNZ68o9Dr5fb5wBn6LbhCLdmUO/C1+DvRUJv6YX7y9aO4V3VkwzekJ80XBAvaHwSOIfKTXy4uK3TCZJ99atoRsVZo25oL3h2gofhqJBJoDitvaeN+C7knKYoOw8s1MUTDRAoXKbGGeCk83T2xoBuC0IyWsvxuPgCsJe3Mt2WYWgLAk92EAGMIQfZrpdoTTMg+mI/ePyeOuPRfeBOaEQPCqhy+ko7WBqkJSCOUs7L7/ZgKU6ygiOCB2QvzTjCJBdJLt30Gy5eX9Wjp7IetjfTd3Xumyn6JaLqyPoId8TzpKkjPrMdUiHEFiqB3SBSQQvTXEdQKLkQ2tkNNkq4RkkbUeCQG+Gz4gdjKiAcUQeOc6kNrdGGIgd5BSVSZQFa2pzzpghHvcIPqbZcgIjGsbD4Ai09/pTksJoj2OmQTqAr29rZZo6PuJVUiy18o7d0ZX0SgSCJ7WdrxNF9RVTj/raOSDZSEFGzwbDxb3W1VQxX5JUE5/4a+tMzbmM3FqhwhMdmc+cxeQO5I78s6l0dIyjyO27RGjMeIHJBs6yuldtyQc8ec9/e8XIjIF8FyZyuKmAwJ+UMgOodoS6oukbFmezU03xjdCnNrzeIQnX/dm8/P3/y4HlG0N9/f875mn6PHgn/+vvf//zjx/PM8+u5X68fdf6nf379l7/+Or+dqnmGv//klbpJ1Gjec9uBhd7WpAP/ALQO+QyIVJpWWnqHyt48rpq7gKyCrZuQUAX/B6/SoBFKU4QlIyOA6I5L/lMiIauVFGwqj3jP56onZHjRuyX/bCSa38pmm0jFQl1nx2U159LY9YxtALKVjI+nXcyHN3noOG7ETQ0hET81gPAU6ZCcMmW93TNpQW9ukc8wNGR8WpzUTqHw4Tu1V7qn1m3wvLaSbZ5rnBjucG+KYO6R/umeNmOqrBvKEb+J+CSsYpYW5/cpzhBW7lj1Tr30+YtwLVDBGzYOTiiHxpYM+NyiL+wsHB/mqZgijN//tscIt5NzDF6JYAO3uPacDJK1texElGFUJe/FL4VVFs9sC7jGDgCq4cfDDFTdEVdojVyVHk0VPIYGFiAVbas5YYXCC1C6w+Nyzke5mSywONdoAHRg8bgg5yGbNSpby+hS24ib5ptA9tG6kjMioU8ooaoLFzrkcemTER+8loqmF1das2ZP9O+Gj5IIjHBQzj3DjgsjeJxKBStOvFn9cfDZ52n8h/fgx9Xf2ZxIvuj3fiCmr6xyso3XAHAEoQZztaX8ZqWOdzQJ4cy8XRg91yDQGYt0QLAWIoUpNmvpCQlX2zHHewUK1XzdLRvJvkZ8w+rdEH/pQCt96idvC/pApP6pK0dUmgtLn5N4ZflCln0hDbiLy8qX98nH6JyH4J2UYLk5MuXXGMW6smmgvMLke40uAnEN+vpdNTNNd9t4DaIZhEETXFhUkgM+WRIsFMppOIH1parnGuiyHsgSRZakthYLGKtg8WI8CgMI1TER7ZHc3BgZqgqtc3G7VpibIx4vcmJ2EHiuR37WkEd6syclA4yF03H8DVKvt6b4XA8cQne9nvFx/IhsagKZiOyq+zkonD8AoPj4xKjATvNVvEY+0v1O7pmpU3UtlORldBe+1LwbfJLS5IS7vuq5IXcidGgCLCvmb27JXJN72O8cAxdTVlGyZ/zzlWM7yLYAm1GqAObRMjEfMbinkHQDu+jKjhUEaTTIIkdY7q/5qMNs0ElR1OYvIjZec6yLAgYECnRRsD4nTYa9BVa9VIHqkbo9idHOP18mvAr/Lpjv8u2ppgHXND9g0DpvmruXmImWBSrSUHpyhRanYlS61E420LhHBZH+yUX2izD9sxFTs9WEoWPjedhORVsTmeH39RSHXHC7T63zPRXTO1jmrCxxV7i/rcaQon/Zjq2yckaRVlyGSkocXIoT/+pADcL+dS9SbG0kQlKHudYM2AI6Gh5tZe2r9CMb9Ydorj0gPYZhp2ozS7LRAVAXUa28YmU6vT+Mi3LKEOBJmxYsPpo+Y6LFuzi3PSHbFvGrAPtlDYKNISuReKEnqJORDCuzYsrz7jsV6aGDtJZJSynmUBqsQ2BmygBNpewuqJosarqehwf/+Oc/9Tk/f//9VE3VaN5//x0Nokf6299/ndfL0gaX6nz9Cd0/ftSXfnCGf37N+/f/07//y//u3/7pC1Nff/5f/n//+i//8rdf79Hpf/nXn/fvltbZ53Zt87XVZPpXOxeE8usH1pNlc1pzRnAkJFqMpiJnz1618H42GeoUuLRYLgQAccKU0PcIIDRpBgb4XqrZjq4Lo1RY5IK5YsPP7UeRz8HaBU/58xhtif2JxKY7ZK+4KJ8mr10DdOduyL4kcVVPKajpb7cDAlSTDsJimeVal0R2HdMrTXT/dWMp3ONH4zzmKL1d7xt80bmhblykz8pvFyd02+xkmHRZBqOt1J09gY2Mj5yBDcljtZJRFGT7czqFfXXRI5vIuUoVLxR3EAU962+4lYjRWzXCYRwyvayJn7hsjNzn744RPgGcizDyVeiMoFROK3etRIQTMQ0rBikRwvVjigYwuKDNk6ZLCNSUMD5UypEADsYtxB3UcZCOwjYLsorD6Mjd/tdfapo1+3m4LddnPe+it/FwXJkZvUogitOTCkTFDp9LzAtSxxKiNH45rRWoCOQ1ruVYHAEfNY8BXGuYGqg+5u+9sEfT4Y+mWJb9uZ5xO1lA17lzrcKmC3Nl9lmqU2q3KoWfr+rrpLWl0yUdZC2YdnZbOvO5BVmsQg31FSWQPFtniqeqNVPzAn45hYKRIptbmwncsJ9iM9envseEOGFE4tiL7Lg9Ao4+rHUCEN2mcWuoUkdiMJ+wefcFl8zoN/1B+DiqwTWIilzdNXjCBnAGHdpzhacWK/osXvnIFVQt00NLczkGvla85YanexHvSsYfiy4tKcczCcXGFh/UyYCUJdxyepObV7ZlregzYnDFKjh+Bvn8kWe4zDuJTvAByTZMrAZwlY65CD5jPYPLg1S7ChpWIC704hkmW7NQEoGWR0tShZ4kcNPCuuqX5JmX02pd68xnNMJUPaf03APcqSZ+iWK/XH8LR3YYU8BcnPYTqKwwQcfWGDlU04KnqoRBDXDC9uB6cK/DrbyPkmuCUQm6QqNoWjnzZqXjUYih3VoAqyM2K0K0m1YUrRZN1Y/N7ALl8aWg06Lcstd8gmkCGHBuCsS7VThz6S/sEPRld0dKUrRA4ZopaNSMBy6CUKfU9PonqY2dBixDsvSLMQO42QNRGDe1FyIU4wbiTmoGwf7RPbni3bK2lQOEFWT1eIRJE/AcGgOdpgRcnjEetDTv8+l1jJmFbbNv1DObAxoVwdlZTwtB+em70ojMMhARakddtfgYuiiPpvT4XrFKj5+x//QgnrS55T8eaCoVTv6rkMgFAM7YiYrsWy+0xRLYBveMa4b9xVy2gd3+lkLxkHpc1SP4ErAv9CNc2frQdUAdEu3LImSI23iJ2wGQgCW1283kebKUflM+QF51Ao3m6ka1YYKoFF12Jx8EUJBhA6usz893JknUKSl5KjrgvoEQpcStNtPXqCH0C7weQlxg8+vqFo/wmJ8oez/SEepxIDWbmu7znqerL+YcW8hME6vIO9PNwT02qKFKz2jaoaKcv/zTn/7yj3/pav38yfuuZteZ5xdZurqa93u+Xl/vmec9KZ0xAH/d+dsvvA7+/NX/8teff371+9f9T///9z//81/4k//+n//p//zv/93PN1n4b//6t//3/+c//uf//PtvP3780q8fv329f3lyzu6SGsJ2hvBaS2xaUIL44PyQfbzmiiPJGqVaUpErh3YjwSrgLlzmxf0JUSCcs+52OOx30Uw4HEgIGpy1NGdhbS/JqCVonszvnVgruo214AS5j8DDUjWuSllaKe/2GRFAu/4mSLYV1qb4AwUsPQuyNM9ETwAoQ1rSe2zArrb7//Qn/AQwooy0iJ6B2BVzJx2WyPfcbg2UeibRKy7tr+mLP3Q4i1YwFXC+zWKc0RfERlWLisxu9TRzuWOsD/vfFHk38K3G3i2QK9qmFvr2Btyz6VoqXtqTPEeVYyGwETgrBXV9AN+B/XnThTNCvMSuZ/KihwXBqXVeITnHPOtHRMaBEullXQ/WHB+LMonq/JsOaLEn38WtijToE74EwCcGUmRDVHUn2rj3ULZD2WCQKzS3pGsGNUhdDpH1DueKfdSDiw2EnU/s0vcatJ+hhQsmRtEiMGJC5BlurlGdO/eQd/XfZA2ucCy0PCOJzwgc51tIIqbrQOM8EU90CXiIl/CMh11k1xgfeY0hTb8n6iTN+3MPKHIsK09c4hMYGNfzKXAPjwDNL79X76tyTPO8NV5zNbqFTa/SpqQ4WS+QBn3cP15FqY9dMKPYE9V8GHC7xPDBwXDHw318DbP94Cx7327T6EeyHUBgrIv5ru4MNmii2Y33nP7g7sTkC7Rg7XAMAW4vCN65GcqEkcociBeqbTdC8BKLzmQHsCttaSbZI97iisxSaycIrPBBWhCgruMqkAo1IHGK/vyeizMy0pGixZ+3r+QZRqnxcD0NCQv0cWU+VZZRqNmo6uIz9xDXbYZG0MiWUlWOSzxCA8/o9OuZx6Wkaoi5KGmUPWYq6rjuFoYIB+DD2lvGFYvZg7uofipIdJVHUAfVmeC1INXUtS04qp6srJVmmmYDERI0UfiBuHznmTFSWlfXTdozN0ahuogIV3+AybCDogBaunZob1DS/bw+3XMm9c2qmix+H9g+wi2uZbmQ9nkeofwyNZA43WY5Kt/UXKc+6q/cdr4VGnWxAhJ+zlVQEYodI2G+4WuxY2g8L7DG4xcws04F7WYBoa9ymRAIs6s/4t5ZltK54Fjoe+U8+KMIlR6b5w3gg1oq6jh4lHL6Pbd1u2mPCDCY4lLFJZBtuKxpRZRMEgA4SM4fZI3BkJX16DcOflyMyl9kyg+jlUAu3c+N4gYMn/WhoI0f7QQbPhYM5WVRJM+uBBuDXfX3J8wP6PLtYSgX2Uzrz/Wbj/XZoJxB1KxJEQnXN8i249ti4vMg6YNI9QcTMa0FDvl+fxAEl/eRi2A4QDP7wNQol+I0peVKnzgsJ6G6CaOWiBWnkFDwjWJV/DOu+HFHpLrYyWrk6epgyQXN69WYHVQX6bvXlboL1pKO4mgfFvB1+Oc//+nr9D/+5R//+tf/+jyApk6/0HZgvbof4Hmk0h2974B1r+rrjPj++b4Snmd+n7//qK9zfv7UxfPM++t/+DfPfd7PRXMGuvdV9X//v/4ff/u/4b/89ed/+/vv/+k//7W++t4HUbf4Ah82p3gy4yLI01l3qkuiC+xozqz+CSi2Nbi/eNGu2ZyhzliTF2UlyuZTsfqoqTQb+DhwintZG87lB9f93F/BcfYfB0JLeRbPbm+M4gS8UqQezrKLp8k4/kSa5pvQgO22+kElSWWhYGaCSvFb7AmUg/ZqjQfcQy8HfvY4dqaQQxoMOewZjYGu5HWjQ1yZ8ioCF2PIxiOcs7+tSwiCEHsfwaA/LsS2fVauMf91J4T299mz/38+qu2B+vQY9QmszIWWayR9niGVj+WgEOAsTB3z5gQN0AUHdqRHQ1tPn2eu7/9gXPrzdT8njON38C3JNXABdzbWAvxxeRCh4bWRDxWN4eSPWQOscUQiWKEw3MUmFMZ1w96zvo3C5bTLmmC12p4KlmIBqRbNg96zZky/+FjS5TpgirxWuOtDb8W+ZHx9Q2f9tR1HYd2YKEeU1mA8y6yqyTnOuYQeL4PyCLnrcvFVTiQvaVg2w99iCXe+AaqH5OhtDoNoH60jVs2VwxW9NgDwgNTcrRScNZTyl7qT+MQI66uoMRdt61VXFTnP8wi6eO6ojMc63bkaOMJ4ICURAKitfPEkUXFqPmUPgMJIjsWQ4W9SRqdmHkI3XYpGJcVyF5CALOfNuDNTIVOjuLCi715D6uMbE54u5BVjA0OeZvu6/4iIWKRexN1aoB2g5AzR7w64XFvPpvkk3cO7eeB8DJcxQLgoCtG8EEJmHosZQVTfvQou5dEvaG5TnTrMC5FhJf9wVXshL359gAc4CroiX96Dj2B3Bu2ym2rhPfOAzVsFPnNpr4mpzlU3WRgzo9qS8nmepm7uATnZlHXAB/CaeEZ16vXoXWhUvqafY3QxCCbTmTWdTO2ZAd+BfNy1KOZHAY54KZdgUx7Xl54Q2gHScMIAPydQarGbi+RT3yBnFT8RaqDjFEUetdNaQqwRMVyQK3iN+NCy8ZM5EcaLvboSvUX4kshYiIbxMQPKqduCjhfGY1N8Sgaf87BtV3GeeePOtyqTf+lJrinbCEGv8Euxn4iT0LEwUfcYjqPd6z18S3YEJBTLZl8/oyKiVtO07wBNrECbylCfbtxtl1RVM4aLYv1V3iuwpozgKhupBKJj4ksyoO8Zdy9+Fz78mS1m6JeNa+IrKCWUUNaU7tzAHxQ1LrY+fV3qKBvEsecpug48Ikh/vIezY2MLzkaY8OmTGYjHCbn7uKG4PpbCMYRhJ4lVG5NyH42N8s+fV/KdGJU9ATUbf5h8ZcCbW204bw2gOMkDMwksTQwSqFQnIUGKXlThcA6YQhqrc2NzG3FkeAJjPk8J43OYgKrbJ6aDgaQLVhWJsVnB10/tzzr2ZwuZX5G+lEUVqxB5c3kCxuEjHS8bsKuQ2QgawTM+OQ/RRTTPn3/78ac//envP3/+9W//fS7I6fNqTWn69ePr60Wwn+eN9/vv917H04FOC+OcQ917f013/Xw/xS7MP/74859/vH70/fVT//CXH7//+vVcENN4/fr93X/57R//9Prz149//8//9L/8p//2L//tb7+eZ8KNfGW8kcFYELrH02k4gT7zUId8QQ94sOOer1AGHSxGLWLQde693UlndAXaMCaVNegjUj6xwLVUu+HY2/YjFsomTaepXB9ecNryiZ8mWv6bBt+MAuICHSOmMS1+LhZvTIG5vLa4RKpcA5OEtqyryJi8yGQjKrZzTh4JO0VaLtk9fElsWDVSHspHrZlQpBU1vuC+ozaZkcWj+1YmmTovswZ3DVqyFN/AhWLRThST8MlAUUgYExFRSURnwygS3Dgg/Ch8hAlJ5HTl66SSe5f3jfqJA2g8xDAyUHDzBTYhdUX/+yiUeDR8P3e0CxZlCawij9nbLsfEsRpitlVxFNv2ENsrArVQCvL5dZMZGELYhp2iS/AoKnwEMS+xBHgyoRDEM8Zu3uQgfj4/bYPelheUytxs2JJvIjqnXfHI46cUIlGLSiHpcvJlhz2YpICAFn86Z2brbRwcdybKUe8L0kBz9KI5rPyawo779DbX4HVdiTP0YNKsgL08oWaI3CBwrvBRx1jInY0I9Zk3EaESkmZ2CCzEJq9w7yPyeQbziwDYV78AAF9XDwZTt9m2Ngej2twbZcpcwrOsKPXlUmGPUCVN5Zl0PZ/MdHwNflYmqI48wpBmemrE0nOhBv3P8B0fG3yRLlG86zNmEWMZABsq1NtLETXRwOV2oVCYR1L1pp2w0LY/5qa2DLbAgGgGW1HeciE70YtGxyGLBajGm7YCT1cZfNbiwasAa873fc5caUVltOee607Xc1X1AQ8wIzRf97sQGco+WlNAh3RGrkKGin6PRAlvYxXRp4TcEdGGGKvrqpUBNa+rt4GHgu7MaVMz9s1y62sVX6lU2aOn2SI01vwd8o7teImUSYkRqMmeihnmuXowFI26XE3XNPudI4wCy+DL2I1tkCcKLs/n/dyCqdDtr6Sh4godRKMR8mqfz0Q0TXMQtupWNTCUR+6SwEMdtI88y5WYOie21Pq4VwOUV/YyYCuHinReKMfmoJxWM4N7qizGbADN0bQuqq8E5+ghHzatm8OYXHBW7ORgYQZQg1Vl5Z+niWBrrrSjhjrkl2VXfpLWingceAwDwXBodO7vlICUu30XGiJKjZYxNX3qYbPeU3RuqqEaZ9GGmc39AkaYzm1KIu0RURUYaODoV5mvRqEeOOHSaI3jqg3TmY8eokY3Fky7miaZ+gRMbVlkkPUzf1AHec5NLosB6pySIA2coyogU+nc3zm79nPNx6drlsjEzGDopDUDC3kpDQRnd08TR7KBf4ueqZVh+hJtybpKU5dsK9p8Xi18N3T8YnCuEE0SPSeAadjCg2UaiocwcJY8K4+X9mm0bDQrfndDKAO01Y0TXCKVEI6ViuAU+JAc0uWZzyQFALCL+XAwejVLOH1eLHk2gsgus47P+/ntx3l9vQoszc/7vH//WdC8n1b3V399vfDr/eMv//Dnrx/Q/V+p+vOwy9KsKhBfa7/n3Hu/Ib4v5pwich6qsuYBCqosqoqhhAKZQRDFbnwc2hEQ+WGrQKMotmi3dGMjiNCoiAgqIFMxCCUlRc1jZlXlnBmZGRlzfOO995x3798fa783ouF5KjMjvu8O57xn77XXXnvtnf3FMKY3HvuS8M1hxsHDgw6bTftitj4tNNuYTQowhHezaRzsz5dLMrz6sFyWvnZ9NwzDpBTCS+G9tx312489d+H6xSt7UQvGiNIhXKIXQeQFhw5dhkLPgVHC9BxXeMmUWjpzD2hwi/pDhiNKWwagB7ijoeEkJsrzQkMOyyYuXSUCtLOEdsfbL7KhK66CBLJ76dCUn2br1bBKy51IFJ4HKGmvSHqs1ZmpZmQkPtF/dI6hgVQ2s5Cc7gl9qxpI1Z8EMsVdK0St4eLkFVpCRSrSI1mONgEYiv/eoDQz8kbDNYDLkKo2fQGYst4sVJKK8dV0WCpFGw6WIZLgW+7/0YlWM4yC5ghmHxIifcgIb9okvT51KdnEeLoFkZkkqZikL4hoc5FZKJDQYpPIuxirmitZEjFxKI6R2WGJHJ/Le59XRI78bGW88m/KdZIkSeCprqWZubdAotfPVoa3zqlBVFGoOkC2xbLpa1pUGk2RBCj0ljxe6kgnvdQFazt6IIKZiYXds6GKcNcqAG11y9KuuQ8BOaUjkJwCY2VL2V/nRicgILaegSha6wtmhdNKhVjRtqpgImMpiSTo0jxXl4I6r7EqWhVRLaiEmixUKlmI5m0tBNtpnKLL6eCapyUzY0DmVqH/Cq8eVWJDeiUAs85jtGxUKenmR4x0KKhpr7l6Rp2A1yQPctTdskPp0mE7LMiSki8ZNZlZ9RhKsCKiPQBpEUCBSkFUqkvQnhX1wROtBoLWRS6SYDslouxGsmsPVQPOBFoLpFHNoaeGqF6TbRKOUoTTeYwMeDpYBkb4wKYb15S6CoQ2hRgAbwy2k0xQR0QpK41neKDLlXJke7eV5aeOMbTM00jNSxkip2pKzsdpvrnWajrZEsm06r5qQRAjAsWiwjSQ61GLmXtoYqZwBCUdshqmcRZVyhEFGBBD8hraVGX0yMLaqzpOrQIPp0C4jUnl56gRPBhe5G4kQshoNxkPqrypBOg5A0DCBR2k/KZ7tACw8lxuLL1HN4YzuyZGVNDcaU1iDmRPvvHIqudS9l+oCCl3bdHGNM0QM2N5Dg+L6s3/kyWmKsuQGJE0oIY2kCZoJeEVzHZIDjNhRIrIiWZKrNldWtbuoLtaXyYO2Kl+kYJIIWjaT9pCx8rVLE1H8oJHszQ1shZdWjBSNtzOtlgOruhBZEpD9NbAXqrLRK6LMETiDdJyY2IqF7ICyr9FYnrhllb8pyN4Su+Tm7QEkBGt47GyJUCEZqrap2iO6VxV0gTDzBw0l07A0THpPtBaeS9GEmAURTlH9h6dKB5plWqWCT31BiGMUgANqnhhhGY2wnUBSwp4Osj0Rs9k69TpgyrfgC6pnkWo87uqF6MRUyV5itXJzBlTIe1Ac5BqR1fXwFWGpfQ4v50+RZvmDO0VSnFraql03CzUYRUwXWlWILKUzMU/jScJIPsv2T1I9Qdb32qVrPLqKU0UAkYvHVfuQ0B0FgxMOwtPi4QOxcP7Qisa3mIyZUQpDMfGxmzSl3D3IMM7WNFMH83IWqPAvOssbDH4/PDQSkGBL2uQXmvHbqgjwFKI0lkPRB9k1KgVPg5dOKedL+vu/nwy7VDHME57620y1DoOw8b6ZunLBOFeHZySd2wf31hf//STF/qYDD7vu36oDl09qMFaSxTtlRPoECiU1Y5D8xQ1VkNP2QWkBQO5qC4JIMVe/UmjoDVhr7wpPEEwO/HJC7fuluBzO5kmQ7aEl5l9s/SWByiD6AI1kyndaNkiDh2Y7Czkics1cNEe/CCYZ4wO1BxRT8QbGVxTrSEkJw7AAfNaLYsWT9CQbDqlVofDwp0Ilxw7n0el2/wU8lKUARrd0DU5kmc5EJGFqQB3PmaQgi9JrlReJ+BzNj8CdUVWV4+rtnCo69VKNN0YxWZLQ5QELB5m1aNYuHbrRrOQXN25SCI4JX+NBxHjiSBafNcHSA8FK+mbQMkdqoTWcHcWxWDBFt6I3EKxCWojHWBUbSjmOFCyAsqipMKsOovJ+0yDyRpESatmMpnJVbrX9Yna+KlMlU2Uj154qA2dI5qcSd2eKmYDEXCLQvlbJeYjNDwqcbir6SZqJTOs6dODkHub+BR4YHQHUG9ydErUHgmgq5qx0HMc7VkV76+FIdHIMr0vVlq6bOIF9GQK8azK+NxckYUfavrCrIyZYGR04vAi7fxupK1VUR4E0BFRwxHZzHZ3iSR8HCGNSlogyTSAZsXCWkMjQTyYJvoUelI7LXIYCTmVny7ukUo+iTa1M8uMCm2S0oWWtTNkbpOqUpMWTAYP1iqLDAnZwUDOsCl5sZFMzNab9H907QhGTva5FdN84w2onIU2jcVjJN2kAw69sB6nguygdEhEIpmVwgkJI6rpbbJujrb3T939nHSKNLdaTbAm7tHDjQgZj2pXeXImklVFgF6K2gIM2SWobNPtkboGLLk+L8ZQ3PIMBWyAEysRWk0Yx6IB2lYmSrsmACEWoSBSeWIm1wUvJRcsqvyuTmOYFZp5ZOM50R11RNqyZ4lDadqUaBYUH9ZWXkcSnK1zs6IZRVxEloeazjZ40DtDs1iszW2giHGSZi9FBckLRCDM0nZTuESi8TxUmqqUKQRFDYS2Ka+QcbZKFfXVKERkRGPrTUZLq8pMyacichhKeFo1SpitUhs1G9ABztxfuArlZlaTk4POZ2jaKeRRzKJ9wEBEFISZVbk0ZDMyyWSshtgsW3wF6WDrCr7tidJVcZfyNz+Kck4J0BTkHEWaFk0hSeUXLeK3AIJ8WgRCIrxAk93m7j0J7XhKv2FHljHVwuBVqphgQNNgiNyQJIyNsVGhlQiYlpdBJrNALfqy7lmgW5Y1K/JCk+U0dgTg+bCpS9bSUqI1hHFMNJGUGhTiVMlH1FLM3YvZmC3LvNzJh0pHmne/tY5hSZirLLsBzLAicdge09WNFA6vWLFI1gTfSW6Bhuzb5Hw8GUyVlzrlyb6pYFgZaCs6qt5oGqusWvX/pIs+CEcx5WgD3WHmNRfsIS2LSLnYdXAvkHbMwmCwsUZXWsOKZK2ldKV0sICzdEVNI4UkJ+CuT1KmHcHZtHMfh2UdHRtr0wDd6cMC7sXKclyuzWbocLC/W2t4Hdh3XR+HhwfRd8M4zCZTM3M1Zatx0hHjMB939/d6s8Pd4Y5bTxw7dmRYjICbM0oU9oBNJ90wjIQjgqWLSuvMPbrJeCTi5Xcc3T1Y9psnr+weDAsba/XqjkL3inBol1meICY+htrLEQmgAKBr9HDyqmkXbyQjc7CmyMwcDbNzVdHRVg970iFit4S0m3pa9xqhaO2eUx0RkY5lzDIwmMa1mqpSEM5UnLFK9zvEkGs+UPE70ECzWAfmSpxEnsyRlchvkE0JI2JsqvFWImSYbmgRie/Vakk5EMwbUEx8n/teSKuoKm711HjkdEUO8wZEAEEAQUR92yabZQwiDdaRBKHGIh1uqTRNgMpoQwrIh7IlsxUHqLTuaLyErFhSz9Qe5TQ7hOaCdDMsh630BbORoZcUoZ6BvzFNKdFp5YRuUs4RNzJO2De/PBuca81MYW7xZxD4s+ycI/EivcvhY6d+yiORVih4xkpkw1ZTGGq61mXtoSYXZZkejafNhkdQIDCnNVQ8RladhTGGdmivyOtVA8RyBFpSjdbNjBXyDspqHAhy1FHKSTOBfNE9vJkVb+E5izxVvNkCRpvkaEWxPmoEVmabJZ8Jea4gKL97BOTrmPFZwva8j/rYnRJStDJZD6NHY9+4glWo7hqESIBVMqNFRDENHQMoIh5UOjkG7SihWdSuiVakUHaS8JFmWXybtaV6HcO18FdYO4vbsEBBjEFVL/lsSq5tYUBBDAFUL6D83Ue1PjpDsENURFhaarZkHJCXqic16I2mpRUyRcYh9jSLAUUTA7EiXytZ0mkYcpmTWZ4D1W8UGxKo6aabBg2DlVJ8yQFUDgtZ5o6Jd1LZqi3LHQFybOdmVTVKHr+a0iBSCKzlOF2TEnnONZihqpWm8rcgIlA81XgkDDSDe5jDKSALBCRsYFgEC9LdQGMDLqGVWbhULtkVTh0CGkrIGW2p4YM0hxldcKr9HGCwCg+NsorBVJGLSoaZoTpsMDJ9WYiE1GrGBJBbt8KkbIFLy5GhP2jNzjGwmgJHngGmioO6YARID9dCJYIS7Yg9yRl3axnMWBT8YcWiVlgKHbASEUW4UdtPxUVrGVxHuNrZ0jcAzQ1LVmsWQE66JGIzr7lapJbk1iosN3srArKkQKKTXCTaZyVkFmkrTVC4gYLWBEsU0a7CTEohK04c4SU1idKPo1fICaQsWPnONEOPlmyAFGyF/GE1mJ3SnQRuTdAVlWDyEnna1eJR7AyNB0qlWkjCiwqYAsCqLm9RKWhByBi3Y4EWSUizEyUU7IyNz06bOlXUMMK9FMtHVMnOW9CHG0Kau+w6tsJOm66p2ydVpGeTkPkkFo+qqr1TijXAomMHNItMRmPOFWLYVIhUVCr52LAlo5aP8t8i43XzeJJku6hTrNGuUIrQ589iWQrqACw9NCNbAEpRUK+BCKfkhi29WjOcI7uksoxmZqszlr3boKB9ZOnppLlkligrpo7Z8ibDrFSNb0n9YyBjGMZJ38Oius+MKG42kqXrzRmF6dGR3fOWv+HVehsWw1BHRqyvrY0RdRiLlWnXe1RUL6UrUWI5kgwfx6rqs0yns/k4n802Z5PJMCw7j9J31ndLXz7z3PPr1k3XiJicvfXEZDYpViZdB5rFWFXVkNPZpBTzcVgsYzZlZ1wuB4WfwvH48enRTU43t+44sfnEC1fPX7g+LVhqMkiciGBLtD5z5gIByuLJnWtUDiDhDKs5KdlMOLokuHOXRQRzRQ9DfaRgaui5epfmLskVdEyiKot29XqTkE7wpqMCYgVGFd1bnNZ+gKz71STMoqMLpg7JMo227oFI45rQF2BOjQsyoknC0nwIiQNbizG7XWnXoV9IYYXSOlSL11YmRKoLsnu8UslXd6ZrRV4F2TJnVZI0V0sTYuPSpefmCirLfKHrQjdxzMqVSZTJ9nEVmwTRo5h8n8PSGj2JD+bIrK5WkBm60JiqQlSxM/nXbZeLLreaEXIiSF6fqyVfaTMs5UomAEuyFUg3tYgROSGRw6iEpk1Cw7oIGArlzky0FiDA7I+7sLJlZ4GanxCIyHFiTTfVMOYaEaKQrgXVK5jurmCWcQsuxKKkqQtkhUBO1pR2a5g1lXjRlYCtkp1KJnNNWHkjmUNu/UwBC9Ny5EaopKpkVZJUfaDjG9qc2Mq/polnnpwb8xKtEA8gW+vq8KpLqtopzy0zRJh1CBC1dVEYkCNseHg4c6JIQO0G5wSSGGv0Ct0lXX2CrDlO0xl6tAl9QhOUDERHRi5pC3AsWbFbq/5raIpLSw0Rkqdrf2TTFOU0ZPJfOm0RSHOhIjbDRberp2+s5oTocPP8MEXnpGqTggqLFePVpDZZdOc0kx5MpWPXEKoaTzQ57FsQJSoLGT1KqG0Q7ik20D0u3apLE6RR1iuF2XM3upMWVrWKQkkt9MyDzeitAOHJWYwI02YXJI4j2UIpzUMGVd50e/JSqBqZ7xKGFsbIUphMIWuE/JnCyuCAae+uJIzekZGQTW0Z2QPJdkEKgPZQ00o7N0k+q/0azRozC+mmOTLzINgZItKNKCIBuDvM2CEnhyJzRgMEKSnOp1SyBC9ilKTAgZE2wrtVExZuKM1yQWggFVEtVQinNqV8IktNfahRJu6oFkgsbrk0G00No9H+nAyDQG1JB2NxxQ4D4AaYmrfUDsOKQHDBKC3DOsxKtlXFLjE/tmyv1NyvIEZlUCMrUVJZkiMyvfU1J5NY3YvYb5EWiBphJWN4o34sCoGAdIrIjkKeSZ03ZLGWkopIYitasMrOPZCyE32hQKCNTma2cKymsrMLE5aET4OwZCbprJqAUiK8pEA5FSaEFPUJNhVVOuuqdpwlu2I1vGfeY3OOyKOYJieROSiauY50wB6gFg0BxtYKiBIICxPJCUTRuJlYsSR80l4qUkxw40GwZEVrnjq53FipgdZKp+lgQnFQZXOQ1JSSQrzggd7OIulg6fFWHWYQ2RhoQgNk66MNCSZ1RobKReFLJPpJKolASpAKKZYzHQDZLkGqH9jyT71B5UhepvldJOUWqAiT5RQYRUNxoHlEYQW69NNybRt0hHWlurPUrpQaI40x1qi21vVmJBfL0UpfzGKsw+b6OkMJ31sREO6uuXvU2jlQR+v7MXwcxtQA1bAIM33Swb2Oy+rui8HDHYHwOLKxZbRhPvdx7Kf9YlhO6YudK76zPz15erbW33L8+NrGJMZxb+8gppN+0k0m/ZRGWNdNagxDre51OdQo7GlmZdJ1Cx9sMiWxtrVBxNqkPHjH0VPbkydfuGzVSA6h7YusPrKtPfHwjqXqmaWpWyXJOxk1HLQJuxHjpPS1ujiMWlEQ1WoXrE6aOq6Ww6lgoqOGNpQkdduQzy9Wvc/kqmCrfYWJ1WXT1E5T3OgYCSE5m4mjumbZ5soAooIFDWJljq5olTcSdkkj5C0wIVxJjDcdSYJNzQdZDDGCUZJUQjK0zQggVlBUVEXAHRInSOdJ5nRQtNiYjS6V7Nnqz/nXrKwD0bya9RbJSgs1tTBR2X6qbcJt3TNYC7cic6u7pK7Oprqy1Yo4IKl9gCgu1yJozWu2qLM+0bDQKkNno0EhrARufAcV+rHiolvrQIMoEaFEakghomfHIBOWrlRju/PvnUY5wiWPQeYYuu6wBYJRUvcQHuaMXL2rcUOBiRW7Cl3cgPQXZs1cn0Sa5xRJDQMW2VDSeY/i0qhkRRfppukGaHsyNf6XpEzVnY9sEyjjN+o96foIpBxMjX1vTgoNIyGHlVubK8tBD6SIJznKiMRgq1ID7WQjrC3pao+WKpL8IJqpds/rAwCdAHCDp9ZZhMyVEOH5xhGDHt7OCuQ7oXqM4mcRGBzQBIvOVw3v2JvBaymMwBghz6MJw0vazmJFAIbLnNNbA21oh0eHpLg+UF7kJLG1b8eYG1epKg1uKq20BKd56OohUgspbsQzICpY8kFllT1gYk2AzZWQ+dx2EU6tTQ145BCKGS260Q8RHWha+tgm1GBEDdH7cl5X48gCtRlXAFplJi9s5HoHsM0kIuNYBguM1njAgNBcNK93mpz1YF2JdqeJQKUXm1hqomDaakalaGm4BOrbVmNSRgchYk+KQj1n7AIRGJlnDHK4okx9NYyfdgfFdJFgxRhC1BFwVKw6VooHDLRttHmDxPDVZIxaLytlEs7IPlGyMmQlusjQRGct2k2IRsZizLTVxjXgDpNJF6iBCbUw1Li2cK0Mczeayfq3DWc6opgcjjsiZ0AcUTRJop9UdSpnLUUoaO1uGlN6eqJ3gRpqfBkt18FGK6oLwJoR3Yp5eBjZMRisOaRmAe9LFxI6h1tYhcPYs2OgRzdGhUcxRERnXcK98GKl0QshfyljQa78UPLyvnU2HJBS3ZWPaV3e61yrFi11ZOOiqU2IFQAgKD9HR6odVXtocMiLlUwQFMGv5StJOBqZ80pQ9h1p2e8aZRAplSUTECD8xs4UBAPawxquhxJZRCkQpmA2Woe5uFfR2rQWtgD5r63clgqdWZOFhmwov9cVtk7LPVQ0O6hWB6duF6s+uPL3ik6N3Hel60AriUqEl9C6TjlkmcMkWRxTrtfZzAveMBrJLpwePoXh7KxEyzVZVN8oVlbjM2TO+INFZXuTdyh1Kbyw1VJGsJYoKzSh51hMc+M4sg5m5PwaEfJK6FnGnKVWza8o58XYeQxj1xWuTWhbZRwW/+lnfxFD/dJv+Nrt7WlBVOv6ju5jSTUis0ypinEFiPXN2YsvXIkYbzt+fHE4jON8vVix3ibWGdl1Rp8fzDsrs2ksR5+M1T2WHl3XLYdxODhEHY6fOOkx7F3bD5vsDyP6yclbt+6895b9K4cGq5UTWkgRXAeWGY11OGRwMV/2k24y5Tj6clz2pefGFLWOi2G2NkE1WFks63TS3X5ia3PjyKeffO7ipYPpdMZCkH3pRIzUGKUUVCu91mpAsWIOsBvquDWbcIzr1w82pv3hcj5b74uVxViNZoWVHgUWKYleIXpr1SqyD+CtA+DMhTCKtpQxNK1T+oD8X1Q9eJKySPTvRt5k9uIlW0zQSpcsZsGIaoAeIktjU8nI1aFI/Yxk7swqQKSuDB5EtAgSg6BnNEvbrEZge6SbGbSDEsGAC2kGmugh259G1BRlSIPh1YSohN8Ao5EFEVq+paTZoIwohbzG2lGlB4UKClGCI4DwfCIAsG0aTIDILMLrqn2djRVRBWbyjwy/QbmHipIImNK4yzitdX6SZM7nW6xh5n4QZC1Bp7c8urqAqj2bX4uOTK7zVYEvmJvciCDQ6vMLcwdduTNrlSCihIWQqouXkVpAAkm6RIONh9TvRvYmVbwhh+z0+lmz3KCKSjZwkyaXBXMhtLHa87zLyTSXW4V2OIcpHRsQsshXpkOkbqcxH3r1SKIfiMjFtdnnyfNfI7KNZ20OUBNX7UAadDiVdghFWmaZ1miWQLMgMWCUrCOyCxGEon5ITycaXeCX3/kDP+ExggWGXEaY84tdq0gy/iNqbnDXvBZQ5WwaBvnWQrP0RcoO8w6oFqVm3UJG3q0wRlQGIqTREm+dvalEuSRbC0ysgBNSnMiwxpR54d6IK9QC5jxrRC1hIGrUfCDhnvRFKvvN2uNJIN02YYKVwmcsjGYeFUIUzgAFhDTIAIFcqUvVy1lt+0FRB1UrHldsvBKrwomW1wFIOiHb/EAwMp0LEefaI9HsAWsKOh15yvUo7y5aEC2i3BARtAgWwsja/P0oxbieLc0lBTRHX6w0mUFoejBCWRoB7zABa8qZtLvegijGmnrR7IOmTiHaQlwDapPBaGG0NmWtallngDnUJ7GUNY6niMXRWc5SW9xqtgVL0V+t2BYG0UODKRpxSdjPNOJsu4F0HtT3kqRAb8TQ06pj21mR30jWTUEaOsFvyfpFgZBF55kt4KlwyWMr6R5W1rgAjF2wVnhiIg9YKa00Uz3rkBy9ESyAGUsWkRGtP5D6hoytuozorLhXh1qhxhAHn83V1rFr/wA7s0CI81C5Y1nqjJaMY+4IpZJoMimlHb8b07VNxJ8hXivEheYjE5o6OUn1NIiYbPyKK5ScSI2DGrJSrSKd21pZi3ComeltaN6bWCIjHgpW1GS28Am454x4eEVCnGicXN5phzeBgeKdy/qpSXPka5aOD9nhgECSlswj636dFdUnIjNADdgFWMQYNDFsa58m1+qNul/9g/mJBMubmZ/yTIL9xn+AQE5XNG4qV5wmI9iaojIz8nbXFP1JA4qs828iaFezL0n1ukqjpGrjZtFzWU29NHtkZNmJHsWZa80RjmImlkxROCdDXAsYI8AuvNJg6xhrvfYzP/1LhzsXNvu+21gvtjGP5Zve8oZXv+Y1m+ubB8NB58Ur6rgMWDHW6OaLuj7ztc21a1d3PvqpR/d3xr29q1/wBW/a2jiysdFNp5vVx8XezsbRI7PJ2rA4jIrFYjEMzsL5cpwPY9dPnN6hOzzYP3XrCdSYdnb82LH1jWnFYjadPvnEs9PNreXOYm29i4CHH84HC1rfra9NSxcHe8uu2LLW5TD0k7XCWI4DaGtrE6s+6VnKpOvMqyGXeQ/7h4vq9qlPfGZt+/jW9ubhMBzsL8OxHOHhZkaGIdyAsOrVuq4YS1c2ppPrV67UeXzBGx/Y2Jo8f/HqlZ2D67tD2GQxX9bl0osuaykW7dBGaLejAktkdlEjKclIZG6OVqqaBqTCm3CIIQorMYhr7rQmy4Iq7oo5gqgsi8Q9obOc/KjCMaq1CTNZCBHhCXagGnyFbpr5n16/0qMBALaueIb5nHJXxR4JskVl6j9VDAC9zn8GjqBKoLRoXMWYlJBIlsPEoq1L2d6TqwQEwNPHTGi55qu4kYrASfwC2t6o59cjufUWLGgJ+EN9g0QVN6XXTgrYvINOMOeWEJU3fQkBj3TIEW3cVrwKXcG4cjkNrkKUpYOAvos3yQpF1wHZ7Vl9Zl+FRRo0XGiZcm84henLEAF2RFV2h16fyZuIDtRXB8XVeCuW8sdVxyYSXtlzu4dF9aIBzqSIgECu2BWEyjHR/EA5YpEt/tY0V/3VWhh5Yy3UFWm4P+c39QOrNpm+3srQRpdIm9KS41mxJyv3Nn0Pyxe11UMChENr6d2QuYzJiOlaSL9OAvyO7/9xFJXUMPbhQ+RepexWGScRS6WRQnN69ZvrEIto62Db6VG1YFZy+6kCSnTILUg6guLPLIIOrS8NCTtBaoUzWCKqAImui+iAyNs8kh1Q1YpXxdQq4SZha0W+2JFIRWKn9K8yXQ9eeyDd0ssbbLufPGu4QFguW2ECyrxdEcXM3QXJne1hRhiaKbvimNGghaYGiEdrjzbQPokKU2sXlJH62shEm4M7UrxUJDpqaTgawLBq7P2GRoIRKKHKzbMvEgbCY7DSofmaB1naPz3bKT0Q6SROpQEjUWNIcbfKMSuoNeNfsqWuDqmuZ2FfMbbC1SJ3kTf8kZjIRnhfzCO8uY7or1Z7kJAUUQSqsY8YdQ266J21riASvMAYFiIwU9HIAFRHSpzj7CwbnIWaRzfTGhGqI4TqQEcU9EEfBaEySOQm6WRiyRQ7FbT0AEdbbxypY9dJtwaDIpylZF2edZl47o65DJIIzbRqsqUqLBRrTdLIc55jk1jpRADtXKPAn3ewmmHHk3WONNNJvKgbn5UAVGuyVS8J+9MAkm2aQlPPK2zMhkzT30pfy/JjQtdpRZs4tF5E7atkUPQtSpPV6gGRb7YKCmsdAb9BaZAIg9UYzcwY1aMYb6xHSrI7jKwI82zDtkIovIZwhSozWTCsNDSthGspROv+knE3wC3g9NI0OC0Ze9JPudwJBI02uheTDiGFc/+f5eXG9EnzrKBUUFV4ASkJUtzoQRApkm/XKtiQE1TBKFXk99VpZU3jh7zvTNapOQKK6Vm9UpbD7i0KIRuq7U30h7GKK3Q1ji1ZueaCYomE1FUPc4zFSrZnW/4MsjP6SCvVWUxOZtrh5mTBGNMT20f/+Q//o9m0P3Ns+/TZ23f2Lqz1W+eeeXZt2t/7stdd37v69Gef/oqv+ZLTJ091s35Yjj4MwwB2djAc/Ld3/75NcXR96/rezrXLe9Mj29tTe+1rXvXq17/sYDEudw/n8731rSO9ldlkUt2XBwfz+TAfltPZ+u7eHqwc7B/ODw/vfeBsjUkZ62x9fbLZ1cVYei6H5WJnsbW9VccRFhXsrQMwX4xdH4WTw/39oVZNh0/7yUgvpSM5LoauM3ff2twsHfrSDYuhynHDENUPDvb69e3CYW1jbdb3i8Ph2uHy2ZeuPf/C1WEct46sTbp+Ub2YdX3pSllGOdzfvXr+ma//ui+y0W49dfzwYJ9zs55XhvkHP/xs1MUyZrWMqeJmjfQSD+m6PJr6uQk92jlBeyxgN9T1sYrnyVxEGo1Fe35bF028UNK36m5GPqrRjkc0cN5YgRYjb4Ia7R/t32uKUqHerwCPllQgiTVRMKocpJMn6WAR9hFyBjwb9fnkCqvWqqOOLiLYcibat9BHNFLlBSOCHQPi01tiSlZVm92ZRK6gvvz2dMWiAIgMCzfwnWbwVmUJS5YN1F5YvU8DEnpcAzRHFKKq2NAntfZ5bgAl0OFtFi4a92JZjictArR5YqSavZJQrqyRQhuksJtYDUhQiayalcgNbdEGRjzQRVSGw4rUVg1auwNGTUiN7rK6R+PmVDLltQVMeDJWmFllFap8RQKNNA9XH7KdatGo8jlKosZkYYCkqphvmGZE+mxhXWqsmGVNdn0jsmuDlrEiHxJF6Gh7bLAiCSNTBLPAcEM4OrbXQEshWTagadv09unYJ7jJoKopNCjAxt9H+6Pogqx1NHY1XA1wQ/oaRXKADjKJZo3IZU5QKTqoJeMext5DwrwsnbVZvYaD2uGmfAKyd7plU1iCeASNVpP0MhXzegIZMerxdwTZgwNSX0vNBkYwR/wpMqy0o1lVdZABlKoBYqSrnXqIK+uYvGtQtSdCtDSo12rowrxZeU2EbeXUIXEUgIKozY+8ypdDT5Nr11EiJG1jjCzosxaQfLHkSQCIdLpui95boJFIQvMm2aZg9mGyJjeAhuLiVTStaLSQvB9ZyDAMXUqTcmAi2fMIV5dLHjlEOLxEX1kT2Mm8X8b5GL15DqNxnDU3X+RzV4HCbtQxT6tVBZOyAr6BKChytGvGmgZ25JjkQp4v1SJy7ZE0ENW8qKMkdG5pkm+mXRDSF+R3NHnbqJ5JbsNTsxE1UFt9KAxTg6wcC2zlEC/wbyRTZS4xqGsDBmhqXJiHTFAEwSLUmKslba71EZAWFoowEuSjioMHLVhLlv4IM1Q3CyB6clBI8yjWLj9Zm5M9AdO4HMhcz5fJsjdLFbpZTaI9k3qxkI9YMla5D7eqYEbr6CkUODKNAWxS9uDq3iEajaOhRKhbhqSWCbitPEXFRayoJgqaZ2EU6amXp1xIuaya7CHwGsYuNOtfAhFmLu/ajImu1kGQrFE1YQFn1QnRsCFRx6pe/hiuCXQg0DQFwWYox1rMxjpY4Vi9dPDqnYaG8oyCtbh5Qb90TwPbcENX67KzHkZ9Wo/R0DsGtRssmaWc5mGryTU+i1y1hQJqew8avreV34twQMt5TVPrGu/ID9faBHkBVylKxT6bY6ROlWqIfJQISJEk1wBnblNx7StgBugIUPu3Gg0hcEDm0Fp0OcGdFqLBKCi5dMnC8gt5UNvF3ZyIbntz8l9/9deObm1ONjev7V+//MgjD953/+tf89DVF1+YDz4/fOnBM3eesFKH+Xyxf+nZa/2sD7O1tY2Ll1760AcfKeO4dzgcLuLWY6dO3nbXE5/97F/8rr/42CefnFipiLkv1zc2TMHVorMyObK5cQSIQCkb65P54WIYxvnh/K47bjtYLvauHNbF/Nr+/uWXDtbWJ5N+csfZW+fLg2EYSyFYqvryFp/+5FN33HFq++jaxWevbK5vzjYny8VgHRc+9CBgB8thOFxsba8TXa1e+m44rAH4ELY2KXW2ubl2bWe599K10yePY1JOrm+eOrn5+S8/Pffx+uG8LjlGOTwcr17bMa9vfNm973nPf3/Nyx7Y7DbGadk7PBgOR7PCcZx0dX2D55+9sHn81rDOag7pBpWFyahWioHutUEfS7JNDohE9uQ8ijVKBA4UT4IgQJF5YQLtyVaIQ4iSODKdtJqIg0kTIJV1rShsB1v5VJkgqxW9oQErqVnII0XxjdA4vQdyeiVzr1QA7DLviWZTacpI93ZadsfZfH2ELpMcgVRrSM+W5BnkyZgVTX6ArIqUw5oGvKQqO6G8UB0jwgyBzuBEJ1F65AZS2XEWw5jKqfz8nl6O8tPOXmqBwl32UjU2Rs81CyXCcxYbBtZAal30izcKm5zuX0XamzB3iz96OzFn4TDzgIqBLuBAJ6rO0DMqrNDHZFpKBxBRs2/hNcdCIhh9aF2XrgmKTmDTFGhgU6+fM98mRa7c7QCE51J1B1pLM27g8EQLgFPt/FRjp+kHIshOIg60hpjytI66uq/MVU1KeNEqWkVW3oi0EizDDCl/LbAqhNV4mRuJgyKmVEypSbR6LRU3ljlXz5QxQUnrNyQcNFpIteUNLOgjRgdJ1pDoXzPWsoJsot8AqKksR1hqTw25XayQlVHNiBhSSGBdWbFzcs/0GiW0+heUfYchqiRKhUF0wBg0tcMRUKsnx9Opgl6pvwIm0XJ4Z6g1tx4Zo9NPkMFQu74wl0IxWEsqojQeLq9uEfDejJNawkSTq2ZxlfM3kZWu6TFESuyLxVjDS3Pt1BiiPrKgcJLDIPR0dtQgnNbQAmRRwBK7zJVguiXvHvIdS56VooFXP5BnOAIc0syGojgjp2Y7SG6leaHV/hhqfy8maHUz0w4oVm19hBapwNiDLCiuD5Px3Jknsj0ZmezFNI8aCw4lwKg5XkvLkVnKXsSSmswzWoQLHfIMqCH2qEnlko8MECNM1AdUMqa+BAYUVeVA7WkDwnBDHScpDhimPb4JSAkgFxDn7tTRrBRpEUMq7lzS1nJBNaA387TRjBB1DYAdolpJOSIjyEqzwNhZF5EVHyCHb8gBMVp2QmRbqliNLPhAVIJdJ1WKAS7FOTtrHFHYSmaqqQaCYQZH1iharQKDOaq2Kzcmm9mXDB0yZP8emcyTR4hIJBgkvGvbFxRijLIYsaxFM8Vauk6vKg0B64iOdIl5LHnHCDeWtE5LUZL4GPWNZDwXgMv3VtWqbI4MK67dmbWS5AkJGSzdEYWewagVLmacARbzOlT3vu+XdQRKZ9r0J1aGGkYqzE8RI4mxR8qN++UMNnRBK+YTbaxDVGepPqJYCXenOxkxWMmBaVo4qhVGHUuhu0M7Zjw6dm61Q6lRCWNrd3pUbX1q09QlkOUZb+Q1A2piFJBIoxWgKpsknMkHtvFK0TB+ht9IaUQG4GhuTCCTuImIwi5t6fUzLEBuAAVLGz3ImjD1R1kGtCo6GWM9udUobs4iNHWX4hBzLivWNvjZT/6PyXhp+8Txcbl3bXf32NrxF55/8aF7b7FiZ+649f577jp2/Ei/3l+6cNFseuL49sVre0fWp88//9wTn33O3Kcb2/2kHt8+8bY3Pnx9cfiFn//w9ctXT57aPtw/8CibG5teR03DRYX13pfJZNIZO5CbG5vVx9vu8W7sax1nmKyd7J0RPGr20taRzb3Dw8l0Ovow7Wfo4KMvPYZhefXKldvuOrqxtbVxZP2hV2zFMgbW3eu7ZsUCtdawcdr1169cvXp9sb2B2XSNvpjOyjCMk/XJGNGtrRtivZ8Mpb+8e3U625r0dVbigD4OZXt6lFNzi3K83H3HiSv785/79z/5hje9aXP9CAITRz2sQQwehWV+uLx44cKHPvzZB+5bnrzz9tn0yKIORgQLUUmW0GiQCAoxhoAYuxygBGSJhyBr5NCd6tsSuZg+PD2WrfWodLJixa0iqdpIRVjm4JrqGrmWrX6RbcQ3ZRdp1RnZAtB/1aaYryLcSpgnH5LdQuGiiqBM4mVXlsxytPgp/SnTssNVDijZGZrEIEUoZdWZKGyBLrUnaLgmRQopq0yihBLcWSZNBuCWpJi+5VjQVSHUdG7oHJUsNVzpCJHmC7jR/NX3cciRABZu5BikegiNgLFgNXQenp+81SmEOdyi5AcBAjn3ceM60pt/EeHGqGEMVHaWXWGo9ioV1UyLSitRNEZV4YwOq88dCVlxU59Sgp4g4QWsoXxXJF4VqvYSxVGNBdq8hqw2E+8p3Ugjmn7UbOyrQ9syU7RqWh9kuRJKh3xs9uWNLMkbZzVSrtMOX1KyuvWtw0vJV0UrW45XZInW9p9yRYBFlhbBVtJItYW09s/pr0SoCYw9D2x+yJpVBEF2DG8/qR+J9jXYRUQqdBAMMgx0HSzlA0ZHqrEUhAe7ADyHsqH/MYq8TEUKDQEdXzgistXePkCYsybhvtLhNVddriAkDUCB5TAdVGenpio5AC5qlpAiIiIdjxM3NJ46XfUzeEjHHzL5XT3JATRiMkcdgkCNLMISHyQyS4cYgKaZJ+FOF8eupWqOYs2WyvoxBrbeaSl9hJMxepT0PJJCrsvGIOk5VqhOU0AiK3IVoqBtttRKW2Zjx0SHB8xCSwkzHKJ1Wp0eoU1HEtdKVchRA68IgGO211p1THa+chIQFZE2+KKz1TPr6Ol8zOyzyJDGQjulAuBY0+OH1Gxpso0r7RdXvtQ0Su7XhKcFHkYN5Air39DT+wo/pwqGke1I18Zijc1bwCOZDYOcB1qlrJoWmfGSl0KlmewxwiP9fgJubqDRq2YfEMHUNnjQKFWYkBACucLQRIOCZkXWYGpxp+0QCURJO7osPAvpJqSekjvJyKSmiOIIFBhMoxzhcLjWJxXF04wnuYsRIAq7CpmGhgXDg0UhINfG3AhyKiEsezXwpBOyPAgqXuTUtYc+pv6eVJTK50oFEECEC16ssCaITmSePDQVMFWmQuw41BtZ7XsCgzfEJU1YoxIlT7ScujofRyusUfvSaewqpLb3NDkC0Fmn1wiv1TGZdKQNQ91YmwzDaKVTq64iSim9eyllcVhp4VF9gq2trTLU6xcvbd8+6byO7OfzGMlJsb0rl45tdN6VkTMvdRyGngYzGNy7YtW1l5lOLcC2MngtFHkh1qYaiEDRpvOSJaMe8zbOGOmEgQaT0oUdrYpPVUBLClr+I+VUNp2yKepZ2BpltJq/b/JBE+BRg1cvle8eog/YnAgCuGHdpnShMnOFitg6vbJ2ULhOCKAivKRHPWohPTqzJQNL9/XS//7v/sbuS5fmuwdcX7/l2KnP7B3M+iO33HJ8x7vHX7z+9rvObp449vQTL526ZWvz6LEnn3kmhuVdd9/7mSeeWhwuPTjb3D51Yn1tfXOxf/DD//Qn//Sf/or+1G2x9L6Lw/m8K5OgzDS89EbycD5MtmZav1EkWi8FZdZNStQxRgarWMnbzp4Zl/PJ5rrHuL19ZD4fxjqEsTjQ9TOW++9/cFzU+f7+wfIQNfrJZGN9U0K1znAwLnz0W26/9ZnnL863Ntc255tr3TAMXeljqKX0tDrWoe97jMvwtYPda7vD+JnPPPOa1z5wdGu7EsNY54fzzrqlV7C7++4H77vv4eVyfu7585ubG3s7BydPHRvqeO3S5Z2Dq3sXztvkMGZ25y3b87F087ocRokeFJvSfrvdykQYQW1iztOE1rJW7s+CAU0oKcJVrILJ8Cr/G0zJizUkGs0AT3+b0TWp9WjmNlI8t+7sCuohUCFBSH6aFSb2WLUcsxTVC6hVuxo7lxxA8TvpMbEQOXrFsemPrU2a1lTPEIk3wtt41KoTl7qExIbZkdZ/q+dWnKb3JNBSisrprFYq08JFfVBHWzWYynwECyI6ZL4QaqC1l9RDyTErLkkakrwDgpVVZGZkJ3x1+RJwBS3chcfEgjuMNuY9MA3Gjdn3zDfV5yQQlfUGieThcGqoNCznhFhkQpr3UwdQMDOa7pOpH/Zm7yD0SMBZBdB03ZuOXwnTGdSMnCU1ZRER0ojIl5LCq2rF6LBJvhsM4ds0xdKeI2Q3JJLkyvo4L1/23hMiZ62agToA4ZrG3KsX7cgpxRxhI2/YOaQr/Y1HqlWcginePC1lveqrgZhWBOYcODLe6rcyGXckQuZoaPb9gSYvdsKqDabkD1kTqprXk5QxPeRu6TmAazD3XN8RYoBqNZPRjukaGytYRMZrhCAVe/lFNdRPKDehGCwwpLAfJTCGTAuyStFtq5BNU+tNJb2sg5yio1WXLzNppEzFHMNNlrdJF7aJGbazpvl6WMuBYcYcTVTwGoxdWq9DymIyomOpHBFFF6ozd0SJQnphl5Avu3QaRRICK6F2qyF3KutKyc08ZRNBdAHmCrWIYr37klEcHnBLhUeuaXNLeyVml6XQImjkiFbVJU9jaGWjWMChdZYiomqJGOkRk9B4hlED1mrImLdODbuIIUBdz+zpJ+WTkFiHWz1h+TQooCZCES4U8NUnK4jQUlXpU6qr2hTN6TCTB1tGEmYW6SBtM9HQRpvkEbzVcxlG0jFKHlZYPJZkiezpmXmKPJU7pF3Q4kigSdLCNQpcrASlD5NkXmSYR9RA12YvUeG54FStNjiN4dnJMivwQS0aQ2cxjgF5PNM0hxHJl5lM5hQDCgKEmtHaAOZgKSr6dJELYVB/TsZ2ZaUXU7wztk66VjNBdF6Ed0m0lFAzV+EpTRsFFlJbmXAUudRK/SVrXnIuIri5YqQCPSEhEVH0CkbCCJeeUAoSFQGRvtmiodrgDVA6G6NOWCLXGxudoKycEu8WKz66x9j3NiwXwxiFXbhfubjf9wVmnRXS1te6idfDw4NlXW6tr8/H0boo/eRT73/3z/w/v/zc05de8cAtL1279KZ3vOaPPvnktXPnb9/a3LvO03ff/e3f9fdOn+FyZJmt7+7sdl0ppUSUrhQAdfCgYYSjVIQZmol7cqE10JnXBFlWWggSCZG2YVJHpKWQJtGSUBR1pPpImYDNBjlrfPXfIoH+imVAOo4rh7U1DMh7ozjdikpmESJykTeydkaQxljladA2TQOB7DpGaOadRc28Lmt7gTQ17azMFxHLw1/4hZ8eluPygKduO3nu3DPv+JzXnr967fjJo7tX9+88u7l1ZHbbyZOPf/rJL3jdGz/11BMGP7LW78f467/2O9d2dv/sn/2mGvMXzz0/Vth4aGX43De//Ow9Z9cm9XB3nEzWJ8emMYbXqLW6hzkR4+babNJPutL5OMbYaF94b8U5i34cx5E14LWnTfqZjr7XihiqD8NyHIbhcDFsbm8dLBbz3f1x8BdfPH/fvfcMPtKrV4SVZfXS9Tu71/vObr9lY+fa4ebW2jiwcFqcZuDoIBfDQJaxau9YPHv+pYuXLm+uvXradU8//bx3xYeIiPVZP51N7zh7z3Cwd/Xq7qnjJ6zn0RNbgbjy0oXDw739vYVHfMnbvjC60gXnO4dHj65d2ZtXHymqg+FMkwaF2cwEbRZdx0aDpdpAI/xBSeBKABpVSeRthkgvxEz00Jyj+qZsxaVOV9tCgOiCVWeN0dgFwEyzimGw8NrYU+axaVBXTUu9fqgTkNHBE0uibYmS4wvE4jV/YFNLVRwiwaCV8EhyPaGVYlg0BzkF8LwOzOIkzTv0scxWqhulkNpyFdoKNqSsRWwfAnK/0XOVjiCNU2Xmx/wSTWWaYgKmH38LyZkc5IGzmvKRbjRISAamXlxiaUR4yr/EGEhkkKykhS8li4h0FUHiaZFK0M8Fo3eM2bBpatHV61NKGpChba4jYYo9gQGhwtJtZSCQy2fRolkwJvrkuvSN8ohEqcb2+UGay8Xd5Dqsmu3GiCeJaNemKfw9QhYSKgLapUY6sKOlOuTUYjRk2F4wYZ7lGQcAE3FIQVFVnaIfk0iNlbgIjUeHpjHyh1e0TqqzyHD125ueIkdNNKzXZEYK/J0BLF0EglY9cvJVW0RhCHaakMm4rSfELbU6Aof6Jtn7axVhQQSiI7ThSWWj9j3ADIzeo9Jcnf1V3S8e2hO16ISpO9YoK4IylhbHjPYJqNpfZ1Lpyc00epNvm45jVrL6y4a13o0tR+pUrujfNqwd7QPk51eq07PqlnODLDAttEkKOXJeM08eHeFdV0IuwiwWBekVVAF3wqPm6Qk3WYqnHITSIQRSq+95g0WwlOaiJlrRSFRwJcNgm9yQBtfAMaKUmrQoRPRo/a65e4E5o21YEdBiUiQqT43Z1WoirdYXjNQ60dOnLaqRRtZgSaGwTpRpD6tiTcCL+YhWgSMZKFVl0o+FGJRVBk6FaEAr6LS8LwtHN5axVWsGGxDFakM9KzcUkyOE3LYYOakmwFMBzWxnQzZNzcCcDpFHFZviy1k8e+A6g4GJpXUjWQwx0hGuL1aBQg2O2wiHoxCpX3IYbUB0Fo4w7+hG61zEFWtQ4raUd0Bjvi0Aqa/l7noKBXjVcTT9a3aFm9iUYeg026LJFWvzuJ1lHyhWrF7c/AfC9p6eVgk3Mwu3Ejx1wTkeD+GKDFwpgGo9A1FTMCO8o8bvSkOcYheDkvRqGDWJa5GDui+dwStJ7UX3MiGHcD0pNNNkVTEibPSYTCdDHa04qw+LEWPt16bT0i2Gg/vuOr6/fxgYO9bjJzY++pEP/oef+s1rL1z9pm/9kpOnj54+dnwcD85f3Xv/73/s5Mb6m7/+LVd3L8fji3tnmzunTj59de9wqMvZcrJ28Z/+wJ//gj/2im/8lm/b5+zsmRPXL1/jtOuKLQ6Wy2VFqVa6YfSuL3QfKzzcTY+uIViYm9qjGQK3coo0tqJJQ7TJ76gdShoTuCRaaDcvGshqHL2iL1vZpoTaujlZpGNFzaJkky/JlPxxRFv/k28mAkHPQ4obVq+/6hRmB44RWHmcashBg3jjgNIHOfpYzxw//g9/+Ce3Zl6mG1GWR45u3bPx4NVFvfPM0fMvXLn3rns/8ZFn3/27H/yc1zz48te94RPPPhb0/YP5/GCxc3XnzrOnvvWLvtw5/Z3f+fCp48e2jhzZu3rhzW9+w2zjqNk4zm1tfW0xLroymZQJesLjcD4/3N+fTCab/ZrJNyDcrFTt73OfDyMxDnUZoU1DzBrfYORyGA/n81r1lWxr+0jfrV29fO3CixfX1idHTp987oVzJ46d6LsuPBbzw6EOCE4ns72D3dna2nQNh/P5Irg2WwtjHaIUuZeXAIK1Lh2GO2+95U2vf7jr7fBg3k/72fra4Xy5Ppu9+7d/6y1vedvmeu/O2249RbPlOHgNdvZ7f/AHt5w4+cDD95w69bknbj2yc/1gurF2aNi9fqXnxEqNGiyFLuFrtPFVX0Fggh5RGHJRSCohh9opV3eBg5JGJ60fuSofk0mXiiMiH3xquU0mTw+j2pQWcGkVIsvQPJaBEKwhwyNLjWQOdY6zvaz19Z50VjQbSUYwpwY8ZbsrxhRIDYlUNJBMxFv7IpTtVQVHTkwJYVnSo94qapXrVCYPMlFHPpZ+I0Q36lJk1JhulJBGm8o93gb5lY/TII1St7SVTt6ecw1o6bJTNEJyK7D8ytKAozLlDSs9iSYWS/aHUz2zAtZ+0+tb+4JFiNtSZRCaRNRBAStgbPYKOhVy1rbQinrVNELLkZcNuagpmz86FBaARkg0UyRh1Q2DGgU4I0ICYbKpcDJINlI+gvIZp6xt854EyeKSZlhYsIZbqyrkpR4Rqx2P+a7p9s7msdzio2bmgjq6AlcGq5QlJFbLWNgCNRJIIVKIr3yqiU6lgZsRqza5m+UYQDBFZeJOM0anZqNV7yT4nT/wE5Coj/SoKbovPaFlF1222JVkE5EXKa085WIV6IjqedXVbbLwsXn3IaJ01gUGQQNQlm5wMi1iogRcSvQAU6OBiHC03faRSN1lRIh8FPUsF2MEJTYSIRZFW2Nyahltx1M0PoOrhkY2pNWGtmAa5HrN/U5oY0k1zz4Ffj2VxYnDGZQHKgJRolSNIFsAE9koRM6KFKjvEiBqlm00MCpQjOqJFmgOqDXFkRKiVEGqqRoO1MIOIVYXKleqU8PHuj5EIQWaorkNQIIhjX1Lribdm0c2PXQZQ6ITKV7CtLlKak5v6g4QFs0Evnl7J4vBmw5QFr/RUF0NNro3oH273gBLAGZdi2I3yOD0DkczGEzeWe5Sq4aoFaNHFSsipCEFjrdSHAimYX+jOSkKwNUR0o3Xha4typZk1ruOFhxX8h0AnWSatLSdDA3SUCYGRcVf9ghyAWHIckq2zd52YYpJYSFQpagELUpw1JXS9aRcrVKOyaKk3JQXYk4ESSLaBU19UzRmHqPWAoAMFI1k5oFOrreJ7lvaZWJ6rEgMqlaXI434vTbgkb22dIosTMDXNIhBrGTBSOUPgJA+S+Zk+mFNeol6oK+s1giG1ajF9NdmKZAJIApsdId2NOoLRw8fw6MvGMdYjr69Obl8+UI3mc6Xi/Up59cPXvPGBxcLv7a/f3Jj+5d+8ddOn1w/c+/dTzz+1OX9F8/ectenP/jZh19/38P3n/nRn/q56Tj+19/6iC8XP/R3/9b7H/30Hz3+yLs+/zXPPH3w67/7O/e+4qFzz3yGMfnCL3ztwfWDs7ecWtbFZz/9/O233/LSlavf8Tf/2vYt9+5eu7C2vomhX9Q4WCynxQYrLz19/ujp7f3Fcn22bjF0hY6xMo3ohHDAgHeFUEjTzKYkBQZpK1fdF2s8PVppmeMoyihtfEKHJBt3OU+ZSONGm7GNk8jUuDDgicZcIyAFGldr265B7WfIjJUFd6k+qlg1Wg1XiWGgkSvXpiL2TzHVO0ZlWV/rxqvPPnJpXDz75LnzF176xCee+rK3v/UTn/js3S87E7W7cnXnwXtu48SfeubFr/zKL9y5fvDoRx69/6E7+7W1Y9tHDxb7Tz156fjJtb3d8U1vfPWP/Oj/M+02Xv+al/2xt3/B5pFJb8Ud43LpHpO1WTcpxTqd8OrLqGPHGclihUQ36SUSjWANXw7LcTmf9rPS90qSoTgbXutYvXr4MNaIsqhDX/phcXht53BxsN/Ppl7H06dPH9ncGBbzxf6hw+bLCmN04RXuNRDLZT2yuVnM1qfdMMR00jlrrV5rrcuoxae2PjuCyy/trU+73XrY2fTdv/5rX/Zl7zw4nB89crx0sbG+Pbp79eVyuHjl8tGj2+ubdv7c+bEutzZvOXlifXmAAf7sxct71w6HwzlLrM3WHN51VvUgmyE8d+Cg1IjSZLcjUIgxwmAJ64S6AnLOTZkLRN2nn0xAM/cKK3neLA+noM1K9W+SuyA1uAnQ8yFfYZLQ8yBfdrk0Wy7tSvF6Sjs9RcctG4k8h3KX/ljObwx4W4ZGwcZ8fYiSL44xVpR6oFknZ9zNINtqcj1ikXyZtdfJ4tlhiDEpDXigFBTn4G28AbkVQ85IejGsnl/F2hZFm9FDggWyCWsDDcdpjSlGsVV6OuUAK1/6/JVsuRjUDElw7itRdO7YIVxreBso0WCp55YAaJq3WIBFpo1KJ+pYZi5LZkJMja2qF71AWtWJQtUtIYGqnWtkhfIXLOe2kxTumqTQdTAjcoYzE1wy7XA3xHhzl7NjcQze2HoDZEjoCTo8BBKUjdsbqqNEsrHV0ZgTQ3oZNW4koD1uGtckqpjayA8V2VfLLFuEIjz3zecEV2SURho/sBPwIBhR0wFInxetlacmQGZ38Nu//8cdS2olcM6dwmDSgznQNrAgvAQGXQ0TxM3c7Q6jVGFkqnNVASXWDICFJSO+RY1qLMZCoTRfLQtvcCWF30yf+xT75uE30FcPfqOhjAURDgOrCHSymBYmpkmOtQImH0rtPdAoabLsSKZcVh1tZFvnkKtqVM+4GZir6B0yR2/gXISkUd6gTi0KQ9N+gS61ot4qxNh1RtCKx2jFbpoYagLE9gTm9VlV3Zlr9UF5o1pQVGjnU9AtC1ddHzOzLqKGvJuge8eKCic1IADTIk91JJCOzmIWS2vImEBA0+e0FKDP24Z6Uu3RPrKZAenMKgIeRmOf9JDYlla6IhKCsP2XMbsOeciCJYeaklpgvr5+Mxi0gmJd1dBiuhjfALd6/JDCKhainZSsdhoSyhnkzuTNr/cLgxWjsYzRanoSSM5W7ocoaPslzHLYi07So3r1hL+S4QmuheivAIwd2YxlIoI51JsnWXUj09shDbHSzDSkD4k8E5npknCHCDQUGoiiCdlYVVaI1tkBVi4+Smg3Hr98aJWlpUsSHa/2ULYmaVnD3dDYNcq5HessILPQK2Z5yLKNJVMPornsiXvMAWQEIjoz5BJs+YqmuQGi1sByUftJmcT06JF+58qFw/3d973/Ay975f1nb70zbPmLP/cr//HfvHttze6+/9Tf+PY/ff75a48++tyZU8fPPnjmRN89+uRjr3vjw2942T3HTvW//e73vvxVr/3xn/jlA7fLF5774nd94QZv27145ZEnH3nu8Rc++eRnts/ecvvaiUc/9am3veVzMMcX//G3Xl8u/+3P/Yej69MveOvbdxcvxvrRZ889e/X8tac/+OTb3/UGbhz96Z/9Zdbajf2Dr7r3r/y97zpY+InNreu786Bw1OjiCG31MKEDR/nZqjhVYyZJgVU3aFVcCRKQN5j8TA+tXGiov/2BNZeqvOh59oSSAhkzkUEgaMUkcxC2y2FNtOODLPlW9TcJNpfkltLVlvIkWAIwYhg3jq0X9z/8w/e8+Oylo8e7h172il/+hV9/aWfv9ltOP3Tf2Z3d3dd/3ueSh//8n/z0xvbxV7z87rvP3nb8xNbezuHpW48fO7G9NZ1du3Ttjz70yOd87sv+0nf+8Jd96evf9UVf9vAr72Fv4zCPsZsfLjbW1vtp18GqKDqblB5dQTrijhi8RtS+78CiEBtOoA7DWOuwNttIojYQUTPnkHvzuRUzWAVGH5ZL7O1dXxwug+j7bmNjs4OHj2ZdARaj+8hFHfXE0rBcLNfXNqazYlbWZ/1iOWB074LRVR8Pdg+50Rm7cVyc/+yLz56/2PX2znd9/u5+Ndb19bVZ14Pw0atzOSzPv3hl1ncbR2YwFHD0IZzVEQPd6oc/9slHz7149s577z51zMbhyPbscBHWdQwfQw3PkgcFJXNxeGjhSQJh1fyrRj2BLAx8lUXR2v1Zm3oLOOxSf4tQhEpbjojGFrsCYsrVMk0nIR2rETXkemxBEwTa361YOx1fkWX6xKuKObN9k7m179TCE9GKjiwn6k35RXSJR7SkvEq3scpfuVspxSjCizRncxyODNBuCvg1WxPVknjsIrQha8XvMllhZa1AaIMlbyBTttZgolJdTzPdv9V6NClslS/0bkA71IltOg2nahYvkXKCeWRuQS6vpfGm1/fU9rC0AJXtILV/PEmlZG+rECbj5pgjEx4gyysFt7zACbtN0pVoxxGZ2GseoSREoEEntALJHDcya6Jj0IoRI0Q+VQuAZmk3hOaPxNVDr9jW6oBkXDTcqOZJw4/Mw6BF6sp1Up6FVdYVvmfrJLUKOaiRyqaIKWxK6sa35K3IgCznBo09kuLkdPeo82tE8Du+/8d1+SK9LoJk6TpZmItA1XreVY4PkM0ft1iXKn8EckBccT9t/gEiBmWJzowpt3KHlr1YK5LozchJ51bCLH03k99/YCUnjBtgjar/AmaWVr6yHhbeyMeA7UrmwUjqVjurAGu61DwH7jIM0hM1CvkbRRiMocFdUE7crTuDnGnUDg0a4AVdLixUz5tdbhJx6VimiBFWw9NUy8oEAWhAgpEGGm2Y3iMHowBoParoBMMkOOhJ0Vim2D1vz5ihLfqBMaKqB8fQvhL5PqfeTDZBCMLE1ckOLJ3kYXn2CYZaxAkPCBC5xsrk19t+Xr3KxpRTGhhFoNRrsibFn3NIbACxFfDUdlvVcdoe1vzmIYhaWsuXiaGZ/dJwyIPZiHRNBJDspjWsnldNg+IJQRvdzvBcedogVpFzP1mS6YkR6AxEx5RFaGWSysnKdJoLgtXD6GktqYCogOUhCpvmSDpc9Yfp++pCuFrSzdtFgUUkvqyEGMitnLpYTAo230dhHbEaoUaGVzZsv3pGdPsEFyMdCRS6wBVPkYCRQGtARDMsS/aikcrqwWZWSdygt0wNWGvuOf4/ODHLhmZNk6muIHf2wLLbkErJYqW6h0VXGQU1WGpEP5vOcHDl5G2bf+Vv/YMPvPfDWFzbnJ1mt/zGP/WV/+nXfndtamvudTleP9wl64Szc8+/8PlvePVjTz91ZHv7icc//dVf9OWoy6/5ss//rd/54PufuLQ+XezP58cm28vl8vrV68fvOfnkIy+NBXfeemo+xpLz0+uTw0Mr03E+7N13+vg3fe1X/5f/9t8fu7q7u1j8wv/5s7MyedWr7/7iP/Otl86dj+5w4bbz4iVgGLns3LY31yfTY+PYve2d7/gTX/l1l668ULojs2nnkImeOww5sK1ZF1khR95qRfNMIQn681FqRdfqNNz4F67MSSJPxqqTkHVBC4/thKjIbjeFITuxBE2Z5609yAScUcC0E2/PtkyBWAmrgaJ0CnOiEyjpJpt7F1742f/w7/77xz/0pa/7/Le8/U2L5c57futj8+XV2++712J678vv3pxyb/f6r//S77/zy95+7123PfXYuYde82BhxeiPP/HM/HB379ri+t7iwVfedvzU2TvvOXHm9tsXO3U6m73vvR86tn3s5a97+TAf+2mBRYyo4ISFvc+mU18urJi2eLp7sQmJWkeNxmeXwsKiM/ow1mLG0jFi8BERNM4Xy+VyAMp8WGiWMGIcqnc2mfSlGNzdvc6mHaIM1WtgOYwwDEOVAHK5WK7N1mbTyTgO88VhrbGxvv7op59YHg53P3T7v/9/f/O+u0/94m+95+GHHlruX//27/w2r7a9NuumvZYvefjB/vzw4HA2nXSTPhh1rHUYq1cERh8feeSpW8+cfPzJcydO33bi1NEXnzz/5Cefv/dlp+6+58yijl7hDitRdSIkoIURUZGsybAaZo8sNmUo0XZcJssejoafQnW6SXicRBCDDK8I6SdaQ2B1PgGPqjSUYuOsFTLDezRm3Wvag2e54W2C1hqFlrVrO+KCy6t/5jHMIgArJkOJTruJ5N9RXP2MjKAF+gLIq9FiqGTUGWBXlEcqKvOTa8wSxNgSeBjM3dPOUpG+YcybiBp90hTKIhUeNXLJWOvHpYqBEZWI7PiCHn6zUD/LIpFTqvMRDUpKaBqMLjBGkkCU51JeQDKiWjDMVgGk7TtKPlNoP7BqOoZwSxpdGBjKntbERt7G2pLvtYYfWvyqCQSyHZF4OSLSlEU6npWGqvVDYpUNY8XXKZkHpWqFFs96q0BEYAlsq+DMSqRZpbGlrNZKz/6nrfJkEHIJF5MVqd4i2TV/GrSzLRCgXU9N6bWCBXSXnSibMinLoQCqDKNkpifBCJqNdujpIwuD3/n9PxYaO3OX5M99LKVEio8K0ZPudTDrh6ikE31gRJRG/mkHYXE9G1o0m2g4DXLNwl1yKZJd9aGhA3lya7RfEri2CVWzxC5fka4RV/ngoEUBU5ojYzVsQ4sYA8hREviNk920QJbCPV3MMHZkVGad6q25nRWVQKW3CQxCsiWjQe6lOdnokVujVKoVCe8VVDSfCnRyhw8Pi0r2jLGyEkQU3bAAoZYaHJoPzoJS7xiOrEcA1ZRRrBAylqlA8ZBrXtf6qChkbZY3RmrelBQAHbUDVwc3u53VwdIuF4ppgV9BQ9yJoV1bxLtAlbicER5WzAy5RZC0mgQGW0mv4lUUOIylRtV7U1bBSRS259LkRJFS0BpRtCWXqzsUIgCkLck2Q7SCB6xwioLO1RBS+OQTKtIiTeraMGV6V+t8N4TKxq0Wav5E4VbqUmQlmQTFamtDdoVWy7UCBVEdps0mAW+ESLBQDSzLzTVhpjW3LCzOyMIsakEjcyOK3GSj0fPMfnBrxajSYesNZQs5MXbbd6+PbyxNJMukdZAVrd2kN7TWezI2NpgrJkJBN00CVzBe5yW5DaxY4dV7JYHURnMVCVBSAMCcFAIyMcEKKYsKrZXJh0Qr8xSJopqxQ3QTM1t76rEn/uH//g+Hg0v3nrzj2l49f/HFbn3j7jtOvP71r/p7/8u3//N/8S/+zU//qne+sTmdY3+9q0dOn3nhuUtf/iVvfs3nfsFd07ve/5lf/Z13/8FeZens8s7ecFBvv/PWxXx//8qVvcM4ftuxs6dOPPrkhdlaqbuLoyc2F8tDsMw2Z0PlqVuOXb+0Uw5RY2FTG/rJQ6968N677/7yz3nT//GjP/H07vNX57G2M7x46dzxre2B/ebW9nK+3Ln60i0njh347Du+8y/ded+Du1cvTzdPLoDeRw8SFVYZVK/WcpE51Y6NQNHMoLhktlQnLigXr96ERrIUjJv+LG+LGsrevKqiJdUs6/NURGMXdV/TSEvPpoKGvMbV140QXlzNfgg5CGaZ1qVYx0J2ZXI48Md+9Ec/8ZEPXbl86Vu+9atuv/3Wt77xje/+5fdws7zsta+4/dQb/vCDv/Z//5N//zVf9bb5uHjz217/8H23H9k4yuLnnj5/6YXnX/3qV26dOHp95/L1neWR4xunjp6qqE98+um1jc2dvcuXri2wXHzBO94yjHW2NoVxMY86Hk5m04Iym3GoWC7mfSnVOet7K8XdzUr1qgxQrHgdu64fhqHreoSXvg+v7m6lC4+AX9/ZqQ4PLuZzAF0/EV9UzOo4GMukK10/CUZUg2EYRw8Ui/3DcTk/nPSTbtJ1HQC89OKFyWyG0Xd2du+6754nzr3wQ//4x7/8nV/wnX/tz37kEx+ZTvrto0d3ru5vbMzCSoEthyUQdYjJbDK4MwaLbqzV67gcxv2Dw8VyuHD54Oq1ndlk7fLO9Uc/8ZHDBQ+qv/6VZ1/zild0Xb/erdeeg1fQOmDwKMbBvcBqc1VkW2rDsJoGQYxoUaydqDGCbTdVgleSWkh3A+pHo2Yy0TW+35IrzlNqDQdHNqESHERG4ygNJGuUSD+s8b9EZjVqWm+HYEMgpwMLmwFQJOGYvCwi5OoW0QnGBeg+6mEhOseIlleilUgNnZBRfVX1Jp6hozKaFDz0MGopqgOFMI8RiZQN6XQk6bPUldQUlrdyRxOArWvCqmUyTREoaiu0wjghnyaYwegcI4MSb99YnJxrFtIZ21hyQI7auh6ifVqwl4SrgdsV2E685wyEXOwUAiiJo7mY6IjV67f5W1GsluaqovW8GDxaNGvXrsuq82Zm1lZ6AiIkxzWmfkZsq8tufHX9rUF2sJCs7jJUJEqgZnnW6joVPbn7PapLniCXtiTlHauAmr2YKvm1pp5qwOhkDxg56KNEQhvXwHG6zif6j2TFokXaYERurFWNbaFlLMhpVklJ8oCEiqtC8Du//8cqhsIOMNeG0WKFXSW6qJ6L0rLygXVAePRARfSBkdYKZDM65Bep/fBVpycEIOHhYkcNbDovC/XkcmJCeaK0Vb8dkdywqjPPtou8pUp+JNFIyLWeSAgrG5h86j1y3yDU9lC1zS7c08lRhxsJ/kLjXFzN5XdE7mUOjS1Btt8e0mYJAVkWwI5ShKmilht8mwEjVq33VJqQHGuOtqCwtYesuAYxrSgAqmiwUpgq+YgoBKU8Ko2+DVSWDhBUNuPoANkn8JR2hDECWRnD4LJOQoOSIiKixsRYSe0tVs1cSdV+UE+4Ad+SrbjmN0xmHX4T1EtNS0CbmJOCUJlYkTq4Vl40ql9UMaAZGrA9tgoBITNqEW1M+5GsWtlALU37q/QCKUtSfdVGUoNEjehomknLRd+6b2bm4dopJVqBmBroHDsRKcBIIIq+u2WKAD3SQDONbwq8ikNLk40QKsoOabNTTlKKYSqHm45SAK7SOmhXGkOaeMQE8EBVTLI0pGhDIA2BtWInG3M5Ps24mTLJRyb7RXlcVE9RBmpJYyTca+UEW6bIirmlEx0Fq1FNltL6DK0o0ImgWcBLmEdVnwihGkgPpsbHJIGN9vfe0ao2HxNykqMe7yT7AFipFZNpRYwH5/7qX/6BWAzb03EfeO7S5T/3rd/83373fxw9uX7+wrXtjennPnB3b+W/vu99b3nja9dG+5+/6Ws//fTyR/71j+zsXVrvpk889czrXnn/9UPOHRvTtav7163v7rvr/qvPX9irdeDh8WNH17h2/z1nfu+979so0764RVnWcTq1ydpsHAZHjRHTvhzuzaMQm5XsI/jyO+/+Z//gR37s53/ml/7dL9xx7z0Pv/a15x9/8uNPfGRvb+5AH5OBgdq/cOnF/+tf/uj9r3rZfGfBGtUHC6/MaTBTRzsXfKYyVtbM7WqnXiKvrBIRBdWRQs0s/W+ycU06NMml8Cb6WR0lhCYs9bC0oRwBG4pPCGjntbj/aCyi5qzQsS0A1qb2yH1hAdJi0k1r1x/sXP8bf/O7MPew8eu/6oteePryHS+/692/+oFv/rp3/P4H3nvp3MU/9uY3/Ndff/9f/Rt/5jWvfmC5O19f277/ttvm2LHZ7PJLlx98+MHrV67XgUePrD/3/MWjJ47tXn2pnx15/vwLt95628h6fWf/2Mbx3WvXTx4/Nl2bVsNLL7ywubHWF9vf3Zlszo4dO7ZYDJMyFWM9epRiZiUtk8JZOC6WmmEqpScxDMOknwBRukKWYTmfDwtYV+uY/eswI/fnhx1NsWvSd2NNTeTomC/mi/2hhHvHtY0pFFuHZSUPDhdXru+dOXP7wc7+C1dehK3R8ZP/74//ya961xe89W3LxXw5j2mxShrNGCxl8EpEuPsIoi4XlaUMPmfgpfOX/+j9Tx07vb221vuknNpcf+TxJzdPbM/q+svvv+/C1fN/8Acf/tIveTMmPS2iypqpRrEYI+c5TcQlK1BCzBJrI/CZc7GM8Mg6gdqt660vLtV+o9uTl1esDzTEogAoGX7+ZP6xQ5icwSo6IlEuVEGwiuKXBV24x83juJo6zgljSwao4S+pKSMxN/Jw5qhwm2fLRZ+2op5yOk7qWUYURk2GPmXM+oTioNOup9GaNTQFEi5/LOUv7Q0HGF4J1KaQzhmtcMAcXiy3cUYS7XXFi+WQhR7usIiwnDgLJ1YtaYZH7m3JiK2rzTCnG919NXpgkNe4VyE0ZAo2dSAVSCyqa9o43zpW+TfE60UwcoZIHe7CG9iNtDb0xTYhoM3Knq+PtI0pBgUQjxS9RDKq6jhI9yxXqhapIsDiqCU7qmKYjFECUQzhMLprImLFMgLO0OL2HANQgUdawp7ShgXagCMSibd/j1B+D9eUCxQ/tbcGFAtstFQMtBQu+VIuQQMYHTASqp0abUdVKaJNXTPQOifWUE0rAMjv+L4fc6qDA5HMkUKqom8Ok+2epmzVybW0pBRBGmTUmuZYFqLgQvUrS3oSrcj03GwfgKFT/zQiFYWkGWMMGivQkQSqtxGynKLIfdSUuXzTMcBT80Rm/SQaMiu/CDjcrKRffpJhZIrp9II1rABE1FWHzAC1xDN9Js6WF2xu9vOAwTyqMbd26xLoDmc9LC2a5Arqk4YXM5j7TbGMUFOp88iSnhbe6kHp5iPHpYylKNtbo9uCRYiXANk7lx05hnWWDrr6PEGtrVWvSpOSqkHVYdD17aRlUpFTwzpx0+rSsmTLSfxjaHGPsriqb+p5VMJISQ7DCKca/SlKaG1BzWpTBZbQpEwMRWymB5LwcXYhWt8VQZiUBMVMyjm5IxlpyeXcoCEVdxlaEZ18FEEZ/3hCYmSng2aRiCpS31YsTLb5BdnBbMAq4220ooVtwqQ6SxbE6uW1phUR7rQCeMcSzK31hhIaynUa3XOTF3MWtnWDChnsLFCzjSuonzyDKokk8gMFhLUdT0FxsNq5trooWFF2bKwboJJDDEnLxa2uyN+gFgQFXSPjHrkv3VKsl1q2lqAZiJIFoq/aq5FjPqv2bUtxogsSZoZrNU+aPGihesZmA9w9zEsNaK6t2D/8vu+7cO6aDfuv+tK33FmO/Zkv/fp//xs/8+Vf/bV/5/v+9+eeOrd2vNtdjFzWf/tTP/UnvvirTt5y/NLelQnGoxtbh/DJZHr67nvWZlsvPP3E5tb61uapc88+tUTdmy9O33byG/7UX/qVX/ql3YsX9he7X/KWNz7y8ceXi/luHE5QY1nQj3033d7cOFgOuzt7Y10cPbp9uLt/sL8Ya62BM2dvvTrGW173ykc+8pklx82tad2Z7+zuzwcflsvZkW4tMKDHRh0P4ou+6G1f8zVfOUw2BjfGQHTE0BZqGyD9PZW3SDLocsVtk+KtQlvduhtMfaMws3dE3HRubvxuy7lCPI30p8D7qsWTQV5ehL5qCmQJXQpSUKEMp9Weonb0bLuHY8qYd7UOv/orv/LMuWc/+aHPnLrr1Oe99uEPvPfj7/jSNz/x+GM28K67b3/P73xkH8s3vvWtX/KFb96/+tLFZ186durIE5959s6z69/8P3+bJuMvX9o9XM7d7bOffOToyROAz/rZ5pEj6xvr/WwNMXbsbTodri/uvPfEr/7arwX49JNPHNs+8aa3fM7m5pGum+VGM6BGNF5f3CKt9KhLWmddGYeh7yd1GKpHNymAxVCtNwSXvqwAnHWsjA6xGEfv+4lyptBkdR99tK6LCK81huoRsyObiKGwH5fj4WLRdTYfx43ZxrX9a+Nycn1n98ILz+3tHqytz97+9tctx4E+Gcah70sQ5l3fl3Gsy3EEDKjDMEy6srt/MJmW6/vzzrv3vu/j20eP22zt+OnjT76w81v/6TcOr7506tT25qmT165eue+BB+YHV++66/bPe+Or6xIewzLYxHg2Ojpa9QCpjY3JPEjS2uTGAv1EVCeb8cMYzfOAFu5iwBOLq4cYGVrazqBcilQj2wRMKpuNS05U57jRl8wiNiqiRO65WdH5KkhrBIAql/PsmOXUVC6bVYGCJu+I1eu3UnjVuyDQJpW5YkGUvyIoEUeq37LVmQd+1f0kUGHComn3TSAbew1MILU1+eVEeK5G7h1k2l9krNUaFXFejBqCPUQOHybFziYR0l1b0flCvGBp1DuMupVKZWqWZC4zhKNYsCY7lNQSbsx9RYBqA1hjEdKBiYknjVlHZt8hL6iBbgx5ZXu72ELY+d4Rpr5Ay2NMWWRefwmRZM64UqYi4UEIYHiQWtiOZpEqYkRKGoM3ejGyoNFcaIaxaL6d2f+i3PrS1F98YCIOuDJXQJyhtWoB0VANoq7AvpQTN2k1pW9ptUw2zLXESX0SdYKUrdtgenI65kBn5Hf9bz/mehLMciqbZAXojZW28KoFHC55TkojegctPNAFBpXihqiwiCim1jQtLGuJGMnSUInHSpIaQ2hkMeTJitzPAENYYGhS8lBNlzw3tIbSDHTX/GcVZlR1KAjRlKZ6/Xy25XAsQxdTWzAlPREeLE3M2obcVPxll8kCojLQgkP1Vt2BuXVAM3mpYiGZ5juUyaJLy9vRZCnssl9vXLliVFt4WJBtSkt8KQ9FUBbyiNEJRhesN82qhhnBnilB6Qrh8FUfVWk7L2y+vgBxB44i4hx9RBR6M0AOsNxosDhLKRlOBTZD87tDMLsWxRguZ61qNJowCiOKeAaJvhR6RFwEC6PSiod8i0hKSCP4aoyxLWALLYFnnhXASi6pzABXmSAl+woKBy220dR4Qa1qYaALGmOZtXyI1QgtTDSS1gWiqFOyOs/N2rBx7Cb5qQKdVj1HkkTWbq7mN5CkyY02qekea8uJu5KL9qeopao+MCH/s9wJEB7FWBvJkO1mj1X7zxsch1lJijZXsFEeSZDGw9IUI1mizKmZ6KA5t/Am5I+cK81sJi4vKeLUWEagmLUIqVgnIg5cSZKYTyoCaXrgLW+tBCtpsRc3jUYYV3Eh985S9tUlzIsBB2M5tjb9Nz/5w7/53ke3+7VFPfjj73orr+7/wR989LPPPPbnv/WrfuN9Hz0yPVoHP5jvXTsY14tNjmzy8PD8+cuT3vuefTFwfex4z/23Xdk55P5CG5xOHT965dKOjzHZiC/+incdqfU//OJ7N4/NTt1x2g7G0ye2H3/23KVr19a6PjiWyXQ6mSwO5rt7Oxsb67vXr4/LMTBuzWbnz8/Xj25ON48c25pMYOPo88CslMP9g0Vdnjhx5NrV+cFyOLrRL8vomK/1RzdO3vYP/sH3zKOrY60+duHSWzEH9uMGPEuJlsRtFm0xwoprbIzazYYk+WsiKG/+rSy3LYuKTJeirVoLKVbDa229eKAi86ZkZFlL553H6gDI+EQtLoDBrouRs2n3Xd/9Xd/8NV++dvz0ZOyH/evnL734nt9/75VxeOvb//h/+ol/c/vZ47ecuXVta+tLvuTtdXdx8rYTt5w4Xg+v9bPNO+45c+zEGubx3vd9fG93cXn34m//7vuuvXTx5K2bf+HP/0/PPHXuofsfOLJ9rJt1pXTFiGJPPff0tasX18r0zJm7Yb6xNum7bjrdKJXXd69tHTtyeDCfzqbDcijdxEpxgwVKKUBlkYWZgdH1a4EILlhLHcf5ckkUGheLGqUrdewmtliMfYmRxeBe4XX0Wq101UeyIwGaV+8m3fpsGohhCB8Xy1oP9+dlOt27fjid9ViMXB//5Df++a/40i88dfaur/4T7zo4ODi5vb2oyxiqdQVhk+lkXNbFct713bgYogO9/uH7P3rrsVNX9g76aTegPvzye372F3/Xy1Yd/KN/9NGC8ezdx6fd5kOvvvvCuSuT9bVnnnzs7W98w5l7b9s+tn59t47DWIopMAGEpeMfhNzJpGCThRHqF6NA9/SDTD6PcEQJqxhTZpKaHq7UHQKwVPjwGoBeJItMpqEQFbFUZbYKIJFRBCSsiUh8Gw1FNYAbUQLFYqgCkpFrWEzuiABoimNADoSsRjUDCK8SFiNtD3MakJLs6LdgiCHazg0ykMq6EEAPFNP6b3jFqIQiM+1ING2yABeeEf3l6XFcAoUxeNY2gtPOnLgBqSWCHugA0Y6a9EEqDJTiXYOXRSIeEYIG9+gCRrTPr5rVZQHrRqCxYTU6AxyppE8eLXl4IUQimuI7X18Lb9IKCclBoHE8ASDxQ8CjN46qwdNgBAE1PVC0ihgqDW9IESLCAsIPCZkbPwVhwbAOatNEIao3r5KVbhmEImoOISuYtpZzRAGNGGMVLWnab5tlVO4BNUYhUb1m8QC0mCiEO4r3hUTXjQ6NNkHBBBEuz/ZoCkykpqbe9FFzAgvZworM/Qho9ySjKzQzE50eQXiNAFnSwBCdxeAGoA8MXdEuJBqiGjro8axVLXqEae6EptHniCrTrRqQDZS4qLb/R7dCSbwAVZkeDA8LemFWeGjQNrEdTMMNhHtYcKyaCRESi+hKB+Tyet1AsdqddaCXWOU50ZmWQ5PQmkcZ25bkIhTlHLLmZAsrzeAuzRP1jHewINzlixeMgORbkkUKR1L4vAQqUT3gqKnTFpMCo7il1OiVrJ0jCju54YcHOQIdiJIWPTQ5eQqqoQRYoxrorJBXYCvKbwDBhgOijZmSpTCHwjuyVi+l0yNQzDxoqBUwzUgnoShq1hkwlEj7URBuMvBFoTpxKorbR5Rjjdp2pRTJ/QySJJq7VqS1+EGXCC7jbLRyL6BvFKkXdemWc3ogqbqgFalu1EsKUSXNJ4cRsKotVyKVRSzRsuYhiyMQXkGwMiysi4iiSN7kM1n+y/yO+dyqngNXSrOkAtjAeFY6qbNX9aSNBjpg6qrk5KRKeupywyWeDJHmCNEf6tIpTDCg7Y1NAJpdYCVkjW0EoBOxYhPy2Df2Q5cF1HoyS8bE2k9k5AKb4YBimuRbmdVTjpJPQTLOWYHTsMrHThZo+WjOz+imlNHHjl4rSkEQHlFKGWrtShcIK6yOziaTzlltrbdTm+Wxxx77L7/6B0cnFpODd37p2146/8hjn7y2Nz+47567/+gzz37hH3/bx9/zSTdn9IY4fvbEM8+fH4a6fep0RX3wrrOHewenTxx94dL5S89fOQxft7JYxPrakbmP0XWzrkS//tlPf+bJz7xw1223XDnYOX/uUu9+8eDi2dMnL13eRVen01kNHO4eOJaA13FYW++GSVku6uz4kS9501uvXbjy3FNPXHxpfvvtR7eOb37b13/lv/4XPzNiaV3/+FNPfd8P/Y177nzgu//y343ry8l09vT1F17PIy89ce6eh87ujcNY+jouxSuEjdQ2Na9G+o2hdl3gtJhyQSVJ7/S0AViZF0cAUfKQtRwJgZe8vZbN+DZJElxVZUpbbG1eIDeeNmJCgIMuQzr5E4JAZXRJJaGyWGdk6YD69GOf6q1+5vmrZ9j9xn/6leX+zjBE321eXOw99ezTb3jn5+9dury1tbW+sfaxD33g3IsX7r7n/rOnNy+du3DXy+9fzPeXjNe/8mV33bn1j3/opxjLw93D+x+89xWvevDuu85eeeHqWBcbG/18XC4WQ1+6vYODO46euPu2W0b3/Z1Lx7ZvmU3W2PXjMNq0Hjm2de3a9fX1jeUwdF0/jrXo9JaujiNpqM5SImJRfVYPN9enY+2cZX643036a1cPNo7MZptdXRARy4MlOhhKJ4dF83A4oo6Sj1ckw0KGu9cK92XdOdwvVqbTyciKwu0Ta9/zN77vXV/++f/rd/+tz3vb546LJX1hwatXr2wd3caECLKUYjwYlg5fVAe7+eLw1S976Lv/9o/8pb/4DXfcfctb3viK3/m99/7Gb79v96XlGOdh/tpX3/fZxx4/c+b+Z554eqPbfvHCp+84e8sbXvPyo7ce++jHPv3wy19dOd+c9fuHu7PpZo4zeXQog4d0v1pCu6Ixm0mXKIQ0nbDGELnmlFEBdOQguWmsjhgAQzRvANBMA0DC6GhtKWsgUJrg7C4xffkFszLvgMy1lWwVK5hT9SDBonpD7IY4EZiYq7QTDE3NZUdCyNNKc5FvljsIsEV0YUJrST759giwiJksZpUB1AoSQ4SBBVFbpzYo7yl1N3Q9RYNkLQAgisTf+ilmyd4agGoP1xoEq0XJQV+FZ1D5S8+yp/YKKQ4Uw8baIVeT5VY25QKQNOT24jqC5BhRxESk0AISM7fXj0wH6teG6o80eYpO3nrSnUrykKwPAa8gOXrQm3ir0QjWrnMGp6wPRFBKMoSW38WlInjT7KMzED6KNAE1pcBoR5hskay9i0NSH+g7UDuVsyOBlIaIDRFczJl1SdKYy4NS/CKokAKkVc1x41ZnCbIajCeybEsoqxdAa89noyXkgCRU1dpzCFF/ytx/++//K0+5vqptmoGlC7hF6tFAGjujjRgZncqy6gBH2cJLlSZ3sJrVc3HU7NaRwEiQKLkJQnqeKKloCDFXuhdW88yRzXF2NaAG0t1BdQgxaniIDkShZX0JinHxKu8iCpMbw0oRnypZH+CFE4R7gsygbC4j0odAlzSHqJOwbXoV3bDVsjIjUQzNAUWDAW6aOsouz8psNMjeGDXGZkOT5q1mOnCWAZQR8rdJmEt9ftIq2BZOxWreN0RCs2GuaDoYnXnkHLmwqDsB2Q3pRnlnhuhWJquO6K1EqDBWc9RX1VMO4WvMOztnMBZLRVr2M13QlsXl4RBofSk5VbpDLjd530nwpldjrirMJ0tWRrpbhoIYBetL6QBUDLJ+ap0vJYhsoWm4lmZAdScb1eZtOEZNvDSZbe6fWcMpEkkeZUFQNlArNtO40lizNWdRjNl9QLaQc5ISAUiypgLRxJh2lqvhU7SnCNXOvzJYGkoIT4VTBzwlidktFCYX32X5gOXAAwGnG1m1sLUpFJmJMFZOrtlji4SAWcGsbkWKOqSVWlEN2W5R8NcXRhvvKKR83/VkB0C6xo4tc3G7esjGsvgPfadiiBEoxSwYUYrXWuixvtZFYFx6N4mun26tz0rPaY9//IM/fM/dd/6jH/rJk6eP2Xy85c7Tv/EL//Ervuab3PbGUmMob3rVW9/3iY8eDDtvf8MX/cl3vPUv/d2/ubf0Jf2Ou4997z/69jc89Lay/+Kb3/nNW12/tDJ4mbgvWTuUimrecTn32fSu246de+nwlqPba9Pu0acuTH1Y+Hji6NZiOJhNjwwTO9zZNwPGcTk/WPrg4zCdzSZdHB6M2xtb+4vly1798IkTm89fvvy2t37eL/3uu3/+X/zYZ557/Prlq3s7146e3Lr1+PH7Hvhjzz775B23H//gox/5P//RDzKO3nby9Pd8/3c//vzVqfWjR6HXpPLIBCat1QSPzNZtYJdsWVlaMk1ZmUey9a6uG26cOTS0whRb0kiZwXsaprhZyUq3sWKyZEXSBEz8RXXnXBUfsmeFsIjqE6NFN5utr691+/vP/6uf+PnFMnZ8vHDu2bWY9huzF1688LKXn90f+Kp7Hvy3//Hnv+e7/8LrX/nw1nSydDvYPzw4XPzmf/sfb3jTq++/8/SFc5d+7z0ffcPnPHzv/Xc/9tjzWyd7jL5cjtOuvPzhl7147tJHPvypu+4888CDd9qkeMXm9pGD/YPAUHpbLLwOdfvoNgKs9vyLz/U9jh3bnq6tWxAs47Bk37OwL91yuaRhHMdhHHuza9eufuKDn7zzvnte++pXVJsQYwSWldcvXit9WRxc6zaPbh1Zd3fzOo41CojiUaOG1BWjOyKMXd+XWsd+MhkXy73dQ3a9iO5S7OJLF0/fuvXhj3xia3vzZfc/3M1KcT791HP33HOXS/7hQ9d1jlgsqhn3dvc3t7ceeeSJ3/n9DxzOL33d139FZ+vHjm/92q++Nyo++dgLt5w6PesPD+r+NMy6jU8++sQrX/UKrpfpZHrx2XPrm926Ves2PvDJT95y+sy185f/+l/7tvnyKmhWZvRaDCLEXetOeENH4jVjjoaOUuAoqiaLSmkuI/fL+IqOhrXmebisJUM9/1prG/L01eIPivhJmgOewUwJS3+atowNh7mxHdpmPsY2OqxfcUR7hQDMozbakunNlySfJTfYPI5W+Tqa7bIqjZxtYqNY9PWF7RhVoxpo7TShZdKiC4Z7tJ5LZolCi2byma1aFkvXiqbsSdJXybuu6vU2A+YBMDo0h9JcaxuhkJv5PWkiaJ2ltua2P1JaEs3qbIIJJvZSfjKwuZe0nwesUJ8gMk4wGqst2EswEktQKu7KBnGtMWYR7fV1/dVNT8qSEETz9q2bUKfBa1Hp6oLCozaoFznZmFWiyik0HJWlpDYzrEQ7BCRNRYMN0eCHToijmma+0coJmkRxelYyBTa4WEg0xU6uxkrFVHL8Eg61eK/LRJHIqZuIECbWwCiEuwmG6Yjwb//9Hw+DwSKa+MKQ+EKKHDBQe/ZArZAaLo2Q9TixKW08qaIATIysnD2raB7QrAQGDeyS6rHJRyQXFoGdrPzzFRNMZ1GVglNVV5RuRzgyoK6DYkteiUTH1Gy5AxpS1Ry7jlUYyE4bu0LzOhYMTTdEPmYWCiwm98Nmq9p01mn86+lGaHnDgEh8VeSHkydE9ZxMMELCLG+xQ/2BvBQadWDyJ+4oBmlqnSrNO0JN/LB07FGfK6KZDITiilEawULTEVQdrGJXQDcCNC/sgdBCsXS5EdkcFRLMtGe30byp52hPnSSB+t6qNOjwNKRPpUf+MFgKzWNM1GjaCnzD3AEIY39j7WE+gcy6A25qN6MaKENOz5ZRQXgxKyw1Rt0pmrVU0MJhuip58iBZdHlzOKnJjupPmueSqr9wGFUDtdmhtL3Jaku1ZMm+hzpwhjQzM+YEGVVm33BDQYcsPm8IbFoTTGI/asgk56yRJUe0rxO5TBEtSKjAaFYwXHVN2hFLEsLVnwzJs1oJkW19RISmsaNVNNHK9faQ5GOJFK1CxUjz9Urdo+Ko62/Titk9st0BBp1aS1wcnuMPmUurBWkc5wfHt2ZXL47sF7PeJmuzjY1Zsclm6S+eP7ec7955z23/6//2zx574fzlzzy/fXr9O/7Kt/3UT//CGz73dVvHj/z393zg0otXr+3uloKNtbV+Y/Lmz3n96TO3HFy+/j/e/5HZxsZzT1+u8Hd95TvuO3vvIx/+wCNPnVvs7l3aO5wUBMu0t8u7B1tH1haLeXEs5sutE8fvuPX0Zx59Zm957cve9ZZzj1+78NyF/RhOnd6c7yy/4Vu+4v3v+aOnnrk07OztLxcnTmK+V7CB0ydP7R4syuiXL16bVPZH164eLArrP/0/vufOex66fvl543TaT72U6dZGb1Zn3Hn+8saR7ac/+akHHnj4+s6leR0/+8QLb3jNwws3Zz94zUJLA0O0RFssaRss5xPllqyKIS3Wzf3VRjZApA3Tu40Aq69G7lY/nPMaCvfCXmbNDwA5w57WeI3BE6lTaFXnxTxGdFbCWGhlyhi72dHJR3/rff/yp37x4Qfue+bycy9eumIRZY3H17d2rl192f0PPvLEk299y2v/9v/vz3XTtesXD/qu29ndK7V++/f87X/1L//59f2DU9vHh+vXj2yduHDl/Gxr/aULVy9e210eRok4cmR6MA7XL1195WtfMd/b53Jc39xYO7K2d7AogA9eS53OttbX7IMf+NDU+Pjjj7/svode/Tmvjela8XFc1q50Trr70r1YH8NyfWvt+rWdnet7k1I2NzfGGF944cI9d9x55NgRdCVqDQS7yac/+rH3f+CRP/WN7xoma52PbqjVx8WiMlCdoNZU96UPsus7C4zutDg8nPeztY999OP33HOfhKeL5d7m+sZsfeoR08lkeThfHo4Xrl6cdrP1I+snT5ye710z6+bz5daRI+imly5fe/8f/v7rX/X6//HhT+3OD9/4utece/HF7ePH/ugDj167uL+3P546Nr39ju3nzp2frpU6DJVrv/W7j5y849Q3f9Pnv++3P3r02Hqx5bGNI4vD+cbxk5948uDZR97/ri9/81v/2B87OHip77ZrHdl38JHFNINpZmON5J1RFHCqJLAiVtJ/LK1rPFJ9kj7ZGUCaHUqsPIOZW9vFfTQo5jeY3tR8Oto5hMnlRuJka5wtDBK6NKCfeMOaJUl7FCKaGNJX9L7aXaSLw5HqMnUaDsjEx4JFLHjCwbDmrs4Iz3UG4mbYyR5d/y2hHFlCBnphSM5Ew9QaTo1gZ/Bogp8cZGsqqkSs0apuOFaErAdyg4rnglUTDm3VRRI9RZBdVUnkzKN+yPMPI69kik/yTUlGyhxEELa5Or2YaidiCgyR3GtuK5OZOARl2rQEssyT1VlKHQ0lYrRc/3uT22OoxRJgz9wBFYAlq5b/F61iS0Ny4Ulglb5guQIZynB6/dZIV+bt1bxqITF5x+yOru5KIooIeOu2uuzOsvZDM9AGEu+pDZtTu5G2WWTQi5adMe+wZYPIWtc2GyKINpDNtJhXqLc2Ri8SiH/n7//EqJ4PCPSd5ifDzCpVkYd7lEIzdjXmJGt6mqprprl/Vc0CJF0NL0VtNjDoklgzwKlhmWI4eJsRyvEdazYQY6C5q4pwX0GWZIGRV6bVysyp8JwIl/FEexYYjKhBMxLoHANQgFG9xYJOgcnIFdAcw0vRDi+RmiqmPaejHRRy9AhWQwc4g3qHCGnmnbRwqW6SZAWjBbSgFfiY1Ts7BJqfhq8qBd1HGcUg64tp2JgAKk8SISCqUtQKgRohHyKd0lAz1JzsIqpFkF2SFTkfK/WlFxTPmjtDdkEJhMMDXqAKpzhyqKWpejogmXSu6vustwnQ3Uuxdr8g6yuStI6Q607a5aR9AiIQhQh0ZK16lm/0YZKQYfMriVqty2afrrIZC9lmshU+OLoLflgeNLdiHo0PYtLd+djqhrF5EWclHDfpPoNWCle8tR78/OYKpdkayaacjCsDYMmNgwWUnW7yOsJkaciSPvqBNlQUjcSNXHydJx6p/ZIoUDougkRtVFN+v2jB2bLoaiCeaEuj0TiLxnoRTTIliVFeceQjGrgRfuA10DVPY8BNqrmsJZrJVvZHsq+mg5sVfN7AlTFCcbq0tNPJ9GCB5cUnrl177qf/w68//vHH1kt//qWL97/8zgdfc/9zT1/85Mc/euv2+qwvdZzffvd9T5+7snt17I5uv+bVZ7/3z33L2Ydf+Vf+ynd8+tHnB9ZxGAyxMZtVs4dfed8ttxz/+Ec/s3ewGJaLtX6yubFx74N3nr3t5O/95gcP4nD0aqMVxOX58tj6jJMyODtgZ2dx26mTtjbduXClVCyD0zUs53U5jtunjh/bWpvOjr546eljsb63WLzjna+9/6E3/tAP/aN3vv2NH/7Yp188f+3ee0//zE//6O13P4j9vc8++olL164fO31imB8s5oax9BuTYjad9ULpQwxDreuTaddPF8tlOGJeJ1vrV6/vjnV0x/6CEQPNvKIpqxmQx07T4Co7eFihZ0smi8ybdwJpa2GoEQSS8ugSBRA1JZwZdgJVyyPVdUSTGqtuzp68GpIBIl2pWILoyYDXylibrnVjdGud23RqvP3W43/hO/7Ws+cv376x8fFPP/LaV7/8s48+V2zo17ozt9575XD3G77my86cuWWxc3Xj1In773o4xv3d3b1nnz+3s7fz1/72P/q73/UXvukrvnp+eDiOw9X93fnelZ/+6V9/xevv+5o/8YUXL8+vX766tj699dZb/tvvve+Be28rnP3T//vn/vpf/jP3vezM/pXdF547f+naxc957St36uLYxia6Usfx6K2bH/vgY3fcevaO208jRl+MY62VdRzrstbJdPLMM8/uXLv+iodeid4n/cSjTvp+WYfL5186e889HfsCG6PWqIvCRz/28bvvvP/41tbO1V0ro4MTs9F9PiwmZTLG2E/6CCss1lkhxtGjAMFF9Qsvnp8fztfWj09Kd2Rj4h0ms75UlH4S1a9fvjrb3Njb3x0cFy6de8V9r0Ln09mk1np95/pstvm93/uD080T9z744Gtf+fCRjclTj5/bO5j/5h9+ctYNZ87e+8Vvf/u5J554/oUnr1y9dP1g6CaTPR/Ov7B49StuiZ26tc39Ze2nPcbF6dMn+62tP/j1/378+G2fePTR/+Xbvu7MQ2ePdP3OtUXfd4PXrrOhaqAoHT0jvCkj0xExUpQLNeolRmnSGY1FJ1Z12WGu/krTA0hk3vC9gq57JIRbsR8VtcXVyER8Q1iY1G22zqH8orzpLcnoUAOt45qUpGhJGFBX5DICwcZBKUuoBaLHIb13FaHFUOt/SqCq9ybVQKAgQDrQI8bWoW14qBE6RieLuLlYVd3Qda5gq7mjRFQrq33MzBBLgCViTG/eRk0KD6giUAHQPEgCCIMUAbW5zYmfq7Jm8bxeFkGzGuiZFteWr59iE23ArUAP1jbOneXZ6sagwZtoc7XIVZiFQVha3beWhjVGte0CY0iiI/8VtSUseyLekpzlXcsldAQiJ2EBTSQC4umZnwpZGRpD237EIKYFQv5ItA8PtVB0aD1ocLAkZ88osGhFbMi1SfvcGwTJY6VqoRk65dVRYaKdgamCCwbd3BC5AS2n8mhkeF6ESGCOYuTf+8GfEEVTq2vVeTI7cIsuLM+0GS0MRvcamKhwBce8HJaMnRphai4HkaZcLKCBI6IDBjpyWSelfOikvmJY6i90VtQTScK31Wyq7dFJleW5KSwfRU326E5EK+XJqccgwpSlt4gqSy6BfqTXlqdlvieNa06YlCKBINXalGNXg6D50PWOalAdn/a5BCNqOiuFHlQBnZABTM5rhJOB6EgAFZ4nNYiG9iBf/84AVJZeYBhg+uzKodNMXdYk9FAkpEztuahZy3haiKb9pcML+2CVEs+s6N1TyQkvKCriNGwRUvAgSjHKvaxNqaiFAhaEkJ+jKeSEM8zMJeEj27Ro49fzIdND03vUkoRhZ0BbGi+nKhOyzIdRdo+6R0aPcKBrenYFu5wVV9FJYRuH5/CGHqdVjZchhxYSntJLUCrTALRIvGq8QCKKcGuQ2BIidxLe0WiEfAMV3w0GelnpKVJSow6MA1pmbLkmrxUiBtDMW7pSIGSOGauxpdmSG1hauLqQjipJ/U2FiVozTazWCq9MLmoGsrVIMtjkcrQsa1O+rVLtpuvc5Iw0aGJGJNyqZlf6tdWuOa2Ay79pzk8AIkyLdawYwxHD0mxtHPbjG77hW85sdmdOHqseV3fnLOMC5d6HH5iOGx/6wEcnG/XosbXLF/eOr9nXfv2XvOFN71jbqL/+s7/+H//Le175utuuXj547NmdE8emy2FuvW10U1q59dTxnUt7NeqIevX6ouu748eOvvNtb/2N3/rVw+U4IArp4ziddcFucN8+sr2/v+9hs7VuNpsNizEWgy+HGhVhQ9TN7SOz7Y0rl3antXKy0eGw3z5+x32T7/2B/2uyO//eH/w773/P+//xP/v+L//qr41+MvP4+Z//+Q988MMPPfTA573hDcXHYRzp/twLz95/5/2zjdnG5pHlsh4u5hH+4osvnT593DFxeG+sy8OdBfYXsTbhcomBg48BdqTED7l1HGhTFsY8saip7bIspwXFVGiZeCNlONxYbYOc3g6my0ikjKsRgNbwHJBFwOhhFu6QW0BSAiAL+9JPeli/NlzbtzX/L7/yax/9xIdedc+ddTJ95JMf2d/n3pW9L/vGL//sBz997unn+n59jPmVg8Mf+Kffd+aO1w/XHv3sJz995913Xbp4aX++c+dd95l373/f+166crnEZLI5fO1XfN3Vl65dvH7+k089fvny+Scev7hWyvd879861a1dn+9f3b2+HGOd9v4PffRX/tv/+Mo//qX3PXzfnbed/Il/+58vPv3i8uDq937vnzt29LaNre1JP3vp/Asnb7390oULd9979+JwOZt0wzhfDPVwb3/Sz0bE4eHywsULy8Pxrjtvmc4msLK2vt51wOif+tRTD91/59rWtI793uF1OKeTvkYXMfjCDdw4tnbh4tWN9dlQl6WwELvXF/1s3Vin/WQcFkP1aT+LDnCicLF0mr/vPf/9kSefe+c73nH65JnpOmr1YZjTy9qR9epL+OSZxx577tzjf/zL3jWvLBiuXjswj4P5fBjHw2HOflKX47kXL/7iL/72wscjx2/Z29298+7bNyd9jONL5y+/4uX3njt34dbTR4/fdvzd7/7Ykrjn7C3H1idrs3Lp2i6XeOaFJ289eeLK7uF0Orn/gTOB7smnnn/L573+4YceCB6a98vFQT9ZXy4GLecYEAXdiFqsDDWBUbh5uLSXbfdfzgNIIuhi/SPRumLN2LiRlIHmChWlVyeFJUV1i0CFrxYH3CB2S7a0VadmS4BNoCL9yAqTKcVIln1jn4AWOLa4KiZfFImDvchNgokv8/Vj5TjItjRKjQ2KpyTFHgYCKG2KsDJrklUztI8YgWgdAITXIPR6whtI67uiD5xNP5r7mImurUlzrwlSovUCo4BV9qdochSAXpFe+B40c7hMU/PvTbVLzVTCtrfTE4EkBYV0z8xuvgJRZG+AlK96KFfkmnC4FN9Bts2wbHckdcwhj0sJDqIHx9b3kJ+SltKpM1qS10LDkmJXEy+NLUpZ6ohSVNbU83Ri0l6/fX4dqwCajyLy/JQEF1AzRx0A9UyaTioZFoqmazWGILDOmxQlpQ2sCqEzd7dlSGXjEwVtq7RzojuB7AWp1S9lnLKvFePf+8Efr/Qi3Q+msBFwr0GDRXEYuQx28KVGPQplKInVKoR8klKUZ9kdiyYZggHmwYAWHNRVRgkUeGHu8VyAjOiNXlv/aSX3zrfQKUmTkMaaQnVYZNMPeUUiz24FCbJtVmoWSuK9gkGjV/184pOo2emipzA9CgIS8gSS/k6uAQ50QBTWoL4S28qi1i5L01ZrGo6aKFn7BJJuD0RtMzeq7dqUDgFaxxw4SrxmBdErz0ZWumyWHQRYrHM9V6pnBLazu5cuNXkdc8FEzv5buu7QTcbGUIBWVaDr7LKXThFjgF3DiBkFFfvSOVz/myEPeV9adbciEUFw5QpHdELVYU1cShhWJnPQ8US0yR4WFodaBWFByoNZ9plcnVYNWHTOWsRPSu6oBduNJE/chDQ807Ezsyz30lylpZRs91U2by+VMSvBfQa2rF4yr7RfzOCMxNaUhoNZk5oEo41r6tRAZ3viFCY8Qtq0lKkxt3+3MJQnpmhwd9XAaCnRU8yTuwGiJWCsSse8NWgDc7ov+eH1N0ZUjfeR2jmiQc+baopcxeGaE9JNZ2hdOZDzEEnGEYAzyqRgPi/Hjs1mPT/wyY/9+L/+1+tLr+4Vsbu38/mve42tz55+5PGnnj83nfYxYDlfHj+y1U/tTW9+9S13nf28V7ziH//wj12/cvXU7ce3jm68+MJF48bupWulY4RP+jKZrHURjzz+4pG1zbvuOXbH2dvJsnP9yjPPPnVtZwgfAv1kwhht0pUgo7Oun5mzn8R8HI9sbU+tnD93+cRtG0e3Tp26/dRjj7+w2N+5dPXyzKybdEe2N8jarx/5nC943Td9/bdcePTTY4cjp06sT8qZex+s4/jB3//d55554eWvf9PuS+cLO8YyrNx+y4knzz3Jsv6Wt74BgXE5lsKh1nH0nSvXu67rpmvzxejLsdvcqDSvh3W+e34fs9JHrWNU5BAfdGGjwtKvCnJaI6yknSLlxRlE+u8k7lHSIiMdHBtMyYNXwzuWGm4oYZXsQlwGVoMz2fHqSufVi3XVaykotGk/nVrvZXp03X/2P//8r//eR5749EfPPfXCaz/nZcc2t4bdw2vz+vf+7ndfe/7ir/zSH15fXi5dvfOBe3Z2Dv76X/2zp46sP3D/fcPY//J/+dXbz5x67DOPPfyat/yTf/gD/dH1V73qTVtH6osvnXvmsefOvvz+9/zOH25uTBDLK+ev/+n/6au+4su/Ng4Pgzh18uhnnnzmn/zwv7l07dwtd9x17crB4XDwZ771T/7YT/7i6x8+8+gnzu0dvvSf/+2PzNaPlGnpsba7t7O2sVFH3zq6tdbh+XPP7e5cX9vY3Nk7WN/cZLBM1x9//NGzZ87O1jcnBUON7e3Nuhx39q5/6uOfevDBh04ePz6Ch7t7S9TttaN9Nwn6zt7O2lpZLryU6e7Viz/3n39rvrP39d/4zlvuvn9jg8urhzu717aObS/rCNjEZlEc6EYfumn51Ic/8YrXvGaxHAq5v3vQF0bpJ30hjWZ/+L7f+63f/cMf/L7vvnx9wfne2vZRgoNX9/Lko88du2Vzvjt84rGnXrp44fmLF/fn1rMeHh5MJ9MHX3HHF7/jbYvDvR/+wX+HPs7cf+bz3viG3/zVdx89dcqX45133LZ/eC1qPZgv54v9O++8rZ/2R7c2YxiPHT3+/o9+6o6zty6Heu3S7jd/3ddc3Ll0dHPz2u4h4H3hGID8JLpS6+hmIi6zaR2WKlepPkDmkDBrmm8n0mlCCLg7UCKGJmlpouKmeQltnk/RERp7zrrqOSJtLFf0oqec2ttT0EVUbZ7MSBhCizKaa3gBN5aas8GVoBEVgSxVWoBt6nZF5hIxSoMkPYWWx4whcykCo1xKuZqOyhSv/BJtbv7GjqOka9ITqcsYrtkGzSJKhwLRZwGEN0/BAFejaBK86KWJKhgSgPTOmTjRVRGAEciyZwRk92QIA5bItVn5+qbtxIbV9GJEIcfIzm+KbBOyRHH54eldlDdShdUBkVYcK16eaPlLQKGCpuWtbnGDSk4CPVvSSVUDRBeoYDjC9Pry34tMVUBLmjSaMXX82vuqLH+DqlbJZDfezYmeiJvspFIahxi0XCzIcJ2ildRWbYcaTSOQd40pcAoUaPAefWC4Ib6lEntH1qaQj1RlxWofsul/OtWjCDjGghLupHfWj1iSYd4xPOguFj+irRWtDCO7iAiMxlIbV+QrvlYi9QjSKYqI5imm7moM2Q70CnbSvYV2thpX1u2IhlHanW7lWw5OUIvFkipVXaVFX013A2Y2g45rZwjVoSQG+eQgx5Ys48fqXWi5cQlZYaKAsVqyI7F7UPWPeFGJj0LSiRrepCuyM5bKwl3O60nEBZtv/v+fqq+Os6S68j/n3JLn7TLdPdbjbjBocA0kJEgSQohAIO6yycY9bLLZuBIlQQIEhwDDoAPMMAzj7tKuz19V3Xt+f5xbb/Lb/YTdMD2v61XVvfec7/kKMjIZh5QxEaJM6pjRmJhXwkyogE1kZ4V2XxS5CNqZJQAYrRAj23rH/8uKIUKpBGKoT+ABRjLSgltzfGkhgRkNakVKjJKkCFZo4+FsiW/J/ZazKEtbbp4wBZE1IxMhS0VL8qiEOaiENW+Y0XbVljcqRwAIhyBOamA4mfJhABUQGy37GhJKz2MAEDQgGc1Elg/DYOwk2e4EklLDEiAQW03LHiryHaMoVseD/HZB+eOBhWCi4s4pixbAkuYE9mYG2aIQHMD6AQDACslGCMZjZyJCy3+KE9RtSyf4CQNqJFGoMMZQgOSJozTVaG+QrAsZR7LsTRTvjrZ5sH7CaCW/HPcMcc8K8UEKdewMgHVcCrLVsjPaEAnpIEUbbcBgBCjrkwCNjHURAEC8aEikRfXdipnlXVOEkdHiK14Na5Dy21uST/77gTVPvVQoRo4uTJ296Mjo4Y/e/N77//HQzq17HD9RGBmHKpeqYWM2M23uNK2joeHJeQvPfPXVtb/96V1t7bmpHc1BRRsfezq7+48PmFoUaQwj46RT5MFpZy36+93fW/fczi179u/bfaA4WZwoTCST2Uqt6EFSJqF+0hubnETPxQjJgMYw6Savfus14xMnuBpcfNabXtj4ynXXXHT/fY8d27Er0+g3eGxq1NjcSG76lg9cu3rFKVWujY+M9K5Ymk1742OTxfLE68+sBc/f9MY2rVJb7vxzx5SWwcEjs2f0rnn61UzO/++v/Leu6P6jfcP9471zp7qZpON4AOAkAsdRQRABgZNyXM9E5K65799Ht/d98HPXnRgYyTU21yoY6NBBySN1re5EGNOix9SMCEbFGAqANgZAE6lYHmn9lIFFBBe7hQiwal8YlFAlcWQGFpezWIckrYI2hCrUkZ9I6VDnckkgnU4mUom076py0dzysU8MHRt1TPKS1ef13LI0m2n49U9/lHGSH7v5Xe+45vxSqXzrZz8MYCbHjm7ednDDixvu+/vfvvS1r1DWj0rBFZe9/Rvf+Myy1acODe08fOLIdG96ttFZPGPWrjcOLZi15E0Xvql/x6GoVm5s79gX4KK58ziIEgnPUdg3NPjgg2s9bZoam7dt3TVt6oxkmHz1la1ZxykMl4vl0bNOWfqHP9z5jmuvTjYkNE5kkhkiGM+PbXj5pbTnnH3+uX6iwU1grrmpUq2BUYmUd/zQ8bmz51ZKpWP9Y43tjeV8sbEx15DKhpEJqmG+UvBUwk+kfGUQI3QdQpNrSI+P5vPlamW87+lnX5yYmNBl3r//8ETNafLZcX2VcPOlMrpOJukRI5IbIbvojgyOzl+yRGpTo0PHUUDKURSEBpVz4sCRCOnbX/+vIMKUonRba0AYhZqBPBeO9R958pkdp6xcOW1a69H9B3I+zWjv9BL+0NDxmuZzV6/a9vqWwf5R3w/STU2HDx1j41z9tsufWrMmk2qbzE82t7fu3bFzWm+PidIdTT0HDhxJmtREubZv3/ZTT1s5r3PZsxufybn6wSceb26bMtg3uGr5staOZGW84GUTJkRPQagjxajEv0xsUOyhyYgkBB6LuiIDICmJrIc6vTOur5ntDDEmF5N1wxJMX3hpYAV8dpIpxRXG6KwgwPJ+W+AG7GZPjNKVxJisbTMYkOqbLDADSlYLWbYmy14qu5yg8VKPmXiQC8aqhIWFY41cbCMhZnPKgoCo7FexszZb/AlAhjJSEM9F27EwIWmOR3Y2g0wgOQbxswaL2cvnG2MctGHCYLWbaJ2jrRRAg63UZe+38xEAYLK1ScysijNxLdxoAJUxoIAkm0uuSWSSjLbhQ4hYjkmQobSFmECgOaEzWUxZammy1uEAiIoNEhiwPsNxnCYrZgYiNkITIkAwLMRE+x5YbyVGgPgQtu8jyaQBQdKuWAFxbAAiuKIFrWykXey5ybL7WY4K1csuqVpEP0VGekUCEF2WhB2LFFFL2ChqAFR2nwUbbEVgo9/RlpwA8XDe1kqS+xRXlHaygAL/x30jgDADiIQLZBAVAH79e7/XoBU6QpcyjMDGIcUoAwvXYooyp2AEsEG3saskSMWNtt/2AJjFFknWo9TiVlWrYnNMD4gN1phBFEIowwt2gDUqJS4wZCd08cgPmcGRAteufjuvs62vNGHMtmQ01h6JAAk44tjyMPavVbZfBWmHJPFeoe0kWA48tmA1gNi3AgGDErtPttgAIRmOmEW8YgSvBzlsGWLgHgANGwV2w2FC67Rkk5gAlMVcjVw/cEx2AmIObVmNDOSKB5VM7gCAUSkwQI5hsvEcSArZgA2wrsO5shuJKZWB0O4oYA2fAECu386MkJkdtDY4aFgzuwgaEQ1E8pEGtFX+WbUxk8W6ZTdwJF7RZlZY5wT7nlIcpSkgs/gBEIIBT4ztbdmB5IDSEpAulfrJSVvs8oryQSSLQe6+smuCCD1ENBig/BUrtkDhtNjZrTii21TJk7JcMViOBWnAbDmCdjsWXF0ocjJFYdtjMMjOjcp6pcmhwbLXW+jJviciYFcyvEMkxSoCDQRxsDkqGR9KkqEdj1lqWXyr6yMnOxcjwXuwjjkgAEvAQtwYSvsh3CS2Ig25NwD2mmUzjU8pJOsfJiGLtoePITK2h5GJ55ty8+XMEEwOxVLBaGbFwBQ77cp0in3Xi4Iwk1CZVML1zV9+/5uNW/eWxvK1apROZhO55Lvfd9Fvf35/SvHoWKGxpXFytACAjgOZZKK5rcFEPDSaHx4ZH5+YWLxyyfmrVr645pXSeL6hoTVqCK+7/sq5HVOL5cmhkVEOeOFpy+bOm/3oww8/89RGL9Lb9+xPJR0n6cyfN/vAnv5CKW8Al522rCGdmD69Z2Bg3He84/3HGxpblp960foNjzQlvaGh0tCJIz+9/Yf7d48c3r13xpyeV9dvHZ0ce+tll3R098yY1jYREdfCaqmEJiqHNeCoVgkRcceO/ZmGlOtlTaE0US64bsJwWA2L+/sKyxbNO+/8U2By3Hg40j/JQTWRaUhmE0HVFMbzVV1L+cnRifGe7nY/k21qy975p3/+8ndPDY2eePCO/yt6boPvTBar1WrVROQnVXyOkiFrdSZT6npgiVgQ2oqBbAJRPHglBi2TALRFiaxVsM+XrPAnBluIySACiVG2Ad+hQrmWa0q98My6ffsOnb1yQaZnSqFW3bj2+YqpBpF3eN/RQqk0Mnr01vdfzzW6+59PNLQnMs0dMzq6rr/lGt/Dh//12MTAaBTWuqbMOPOCszwMXnxxU0tjMpFIDleCaLLSNb3twN7h5ctnX3jB2c2dndXx8kSx9OiTj/7sp7/8R77pAAEAAElEQVTONmScnProzR/oyDYNjRTfdtkF5VqxFEQPPfbvHRsO9E2MDo/kJyfLja0drXMyeqDc39ff2Nyg0bR29oa1sUf+8aO+kerY6ERjQ4NykVzPdYDDsFwoJ/xUEAESALl9/f33Pvr4st6pv/3HPU/ff8crG/fMnDWjUhxvzKYqrCqFiXRDFgJwlQdgHOVWajVfueBCwnff2LH7M5/5cVNbbmxssrEx+5tf/vf9dz568y1X/eBnf3v22S1f+cIHT1+9gCNlQnAUkq8cJ8FoHCQGFUW1WliLaqRcjHSUSieK5WpULhXKQUMynUwni4VKOucyULlYSaQTCNCQSULShYgG+k788qd/GCzVWhqzb73myvlz5619cm3G944dH+aG9OE9B4fHR2tVc+DAwKKlvXNmzmpvmNLYmNq8eVtXe0O+VJrS006mWq5yQ8Y91pf/1C1v/tHt9w2eODF3ydy+gTEwlG1qeWntEzOaurpmTH/vJz8wcORYSqU06DBUTAbETjrW9Qk+zFQHjux/gNkwIcdUDHscWEWk9d1g0GCIUVsCiZBqpKC0n8QxpA9xOc9AyBpAGTksEOuXETMkMd60MDZeRDuytqGqtiGxQq3Yaj4uwhTH9YZsowIBct0zUPZ0BgBlhOQDYKuR+hQ0/qux/pQYwpgr4iCExvLBY846W4Ae64MHW1yZmOQk30jOj3qxyiBYGpLEEyEQswKscTwXACDLjJYMAltUSk6Cy6xjVqyAeHE9Vm8aYpo6QMQWHBW/f4uYI9kCS5RocnfiiTxqkeES/ke8ibKQnz1n4whj4SMBATgANRabwRgtJyDRGwgjH6VKBJchjMtEbaW1UOc9kT3GGBHJQAjAkp/Lcf1jO0Opw9AWcXWyq7yF0mD+fzPSeAgPcXclvB6yigWwdvBxD6ABSDKqoH57rJQdgK37NqOYtMVGp/HwR95uYEJCAGMk94nFOhG/+f3fR9b43kM2BqJ4oGRdXIA1kBCSXbYwtaStyfREdOtA4BgIEZSdLFtjRLTFIKMBBCDNIQMQOADKYBWsotmRQZthhWCAHEtDtcoMecKSN6Hi195CnYTMcdozx70sgQKEevIfSpwWkBZTdalMwMY2IIj1lWQhoUW97JoWcYUop0kDEBhmVlKrQ71ItV17PD60+jdCgd4AFJNBDVafQCCCSQRr3q9QiBNWEqCVQMagJQ6P0ZUf0DaHzi4sG0Ahd0wkA7F3JLPjgNaoFApDTkowGTOJFZZC1MaW5oCggZQxVqRitwwrjUAHAez2aoFwRjEQdBSwQVQMdv8FYDaoEA0BRESOic0IEaxQWj4WUQghsgaQxY1UWkF06zQTBDTIDismgxLRbIEEsfVkiP3MLI7ACIgajGO5aszIih2FPlMQBxRYnouFHETZjHGvbJGC+ng4Vs/K1mUHAFxfhhhvFUjW0g5jslHcrYCYk6Jd70wYo/KCogMoGVOwndIRKyATj5mtXl9+oQam/xh7EgILyx/qUL18LtcZ3vXfbRk+dmexEx9LQkIAyfcWCMaqsqQOxHiUh/G2LruKfXXsb7V/Lt/oJKsKwEIwlrwGlkQqQb1IaIxWgI6noBb6Se/wgaP/XvvM0QN7WJfzo9VsU1MhP5lpaGjKta1cOaO3s+N7t/2RVQQO+p7nueR7iVqhlnDw7DNXPL3mdTeZnDYtO2NGz7VXXeT6PHVG0+vr9rd3TfebmkYrwac+9t3GrHPD9ddkFM6cP+P19W+sf+H1WrWSzjjzZ/UuWjJ3ZvfUP/753nyob7jpqmRjY2G48uwTa0rFKpuwFuiVpyyZOm/OmrX/jiZLp5335kSm4fd/+98v/te3fvbT//WctrPOWJFx6Oyzzz5xYm+1XGXUtVIwOT4+o7d3+szpjusgq/z4xMTk5NFDRwsTo6l0o4MOJpKEmMs1atekU8ne6V3VagG0Uwvyo8PlYiHf3TOlobVty2tbtm3ZvfrURcUw6OxpmdUze7hcfvapxzduf2PrK3vmnDJ/366jnp+ojAXT2xqvvel903o7J/r70s1ZchwdglRYzPZgQSSShBK7Zxq0nAOsF/gM4JC1xCPWmuMcFzZMQDKYNi6SMWD7SjFnRibH9SaGJqfN7N6/d8/wwNFN67cdOXLiLe+4PNfY6IGz7tm146a4dNnCZ59Y19PZOXV25769w0Gpmm3J3nTzux544ImxkbHeuVN27xgwQTS1p7uxMdmQSWcafTek5559VUfBRz770TAwoxNj42PDb7v2mkzGd1RqNF9c/8wzmeame++784F7n1+wcuZ73/22ac0z58yf2dLUENSKE/lxNPqWT/+8WioqPwqK6sDxfK4xt2P7fSuXvCWEKim9csXSgeF8V0PmnHPnXnzOakx3ALqeT5Pj+a6eaeXCJCHkS8WEnzPIfUf7t+7acv7pp/cPjP7urr8fP9x/7TveMru7t2NqR8JV1bBaKxtAd0prs6e4GoWZZNpLePmJolHGZaOZ1j2/6d7Hnp49c87ocGHuitztv3ukpyVxzpvOffz5E1zad8/9/1McqTa3dpZ11QXFDniuK9VzpDUDG82EKowCpZxI6/6BE5lM07ZNm5LpzAOPP/Y/3/nKib6J7q4pRFCNImO047uuVtVqKZVWpTCa0tkO6CLroBT9+H//7/iRiZWnrRrOj2/dvnVKS1Nv7/JCYfDQvmM7Dh379Ic/euTYfieBfYcGG1oamtszm7YevfTCVQf39zd35YqjGlE3JWj34YGmFFWCYOv2PU2Njc1tud65s9K+7yYb5s6bVitWlUOBYd9JlKuR5/uBqaFBhaSjSOrYyMhZzIQQGFaixhItJ2FkjIMUspZ8LgWOWIEDikJXnDqJ4xaWLWcBASWjiuMtzPa5YM9LOwuw1h6IKCna8Sj1JAQPzJa5HjOT2JbS9QZFTjwEtKWWnRnIJgosabJs/6UGJNYx8UDGt1YxaMACM9Ys2tYbclwYBInlkJYGFCLHjZNVeLFGVAaAuD7ultoctdXvxqWUROygNvEkPAb3YytPlkrIsgLE/cVwROAaMLHciwVpMnbyb02ugUXohwDGQNygiNW4VUTEcLnFr+UwtwWdsQCsRbwVMtuMJrDjSBOPHO2pKBN3ER/HrmYg8yZgKakJ2RAJggYcs8flHDcMrgw2LQtFSFOAMRsKY6qtLT9FeGl7Ba7TmWLSiYkPXulEbV6QbTpikYZ8bRCKsZV5AiALHQitVS7Elt72AVm7BwLShusxUPH2bWdgWoTFUs0joGFhsYN2yNH4te/81ooHFIH8FjCkfMnWRRJxsfxTC4YuhAeFykBk33wkZCLUjEoaArZk/TplggF8bSKDIk5DAsWoIxarcULWbAcz9QJdGdCxi1xdSHiSfywrV9y1EGPbU2CwfCyyQIK8JTKTFpccAGZSBAyRBiIkab6FnKatLkSYDq5NhQAtTH02GonFIddY8xxABK0V2gZXHicqJObIDjrRalXtIkBDIJxtRRbC1cQKEQxHRAbYIbALXmg5HE+qCG1DS4jG9quyD5FPjpbOiYlFGI6oADUCIBO4hjVbsrgtKEFMbqR9FyZQHZQGR54v2I7GYDyCE/BCjIlJnJdk/0ErmJJGXZGye2z8y8RFGNCRaDhCBYwMWhECO4yh5JEhUMSG4vKR4xEEC6cn3rkJUSkVT7es6sdCGmDsLTLMGBGQIg/RMtUJQTMpYB1XyTIWMBAJbE+ogKUZRWDbDBhmZf3CLCpgd0nQhEhAApTLZFchEkJorVCMlSmD9aVSdkYlIx2jBDgV3TSCZgnIqBfzwLFtrx0qs1U32PjR+iRI2hIwMW4DDGDd1GyJDgTKsBE3UiNvNUeEBIAKLd1VdiaINWtGaOIx+1QgJbFvl7vOIPqDegfCGPcLUqxgTClB6zZqUAylQqPJTXku6Kij0fvX408+9Njawwd2tjc1kENQg7HxwplnnfKTr3926669DansC9t2TA71P/jwSxOlQktLsiXTkMvmlAMRhj3dbeefd8ay2Yvf2Lm9XIsGx/P/fnjN8Hh+Rnf7QN8EKW5qSK5805JVy89YuKw7DNw9L29aftbikb7C/mMHH37opVIUXXzmGdmmphkzG2bPmqtc78DxvbVi8O9Hnr/kggucBOQnC66bamlM5fOl/Xt2DPSPHx449OVPf/5vD93z9qve4Theb0db05SmymSpSias1BDVZD4/OjDYNm2ql8qE5Sqi9hOu4zjlfL5YDAGQtQbACDQox1FeEOpavnTo+O6k0bmOdnL9bFYd6x/4+o9+e2jzzmwm5Sf8tra28fExcvzqwPCVb39La2sqX4Tnnnly2uzuLftOtDU2DI2MOcorV0rTpk755f/dF4Tj+WDQ83I6KElKJijheTIAKiJgdpAknyFGUQ2Cqu+4dhe1eCfG7oomVtATgMCOhKA9UOi55XLU05V54eUX/vSXBx1wB/r7Zs3onj5rVqU2XJqoTQwM5/MTCT+x6qwl17z5Q7++/baLrjp/5szZb7y4vW98YNaCpd3NTb/+8S9nz5xydLJ83RXnjo5NLl223CWdbElPaZ3yqc989ewLTnvrxZc1trYd3Luva3qXUuR7uaAW5gsDX/3OT3fu2z+np6PCxJF58K+/RqbBvr6e6c1Dhfzrr258ZtP2005Z9q+/PTcyNrj6tBXtPe0Xr1p15/OP73zx0MDQcGNHZvHCVYd278kXx8YmJr77jc/NWzy9s7WrWA2ac+lSoeomvEqhaFyXIaoUwzVPPQUqPbW7u7O7aeOGzQNjR59b+9ovfvYHHY0ROKwDN+k7yhsdGmbUS5bM1aQw0lu27JgybaauVsnVY0fyjzy9Zmh46ERfsWdOe2GiNnp0b/eCGRdeev0T9/7t4x+6ds+hgbdccr5WnkbtOyrUhtCJtDZgFFGxVPJcCmtcrRTLlcrw4EAimWpu6iwURnJtTds2bb3izVfWgiqCGhwabmrKGRRjN6zWQiKTbWzylRtFejKfB4+eePCRHTsPLFq4+NFHHp8+teeaG6598IGHrr/5+ulN2U179zSnmv69Zv1w/0j/0Pj/ffOWX/7toc27jqS8xHXXXZH0cXBooiWbgtB5Yd0zC5YvHRsZe2btc7f98Eu79h5dvXT1z++8DypRJpmYO3/m/Pm9pYlye2cbIeeLVfIcNloRaMAoihyCiBmJTBgSOdpESMoYO/wERKONxR6JjJwIJpbZSsSQyKgsFzmuiuOaId4lEdAmCjMI40d2VgFcZDaKEB//LH/LciBtuRrjMDq2Q6iPziyiEhtPa0F4OJYvAhhmUISIFBkbUMWWjB3voScFA0K9IysgFVctRgSreQBLa0BAI0xiQABmhxRbpg3EbFNEFl2yPtlwxFb7jIbjikb6BW0kTFUDIKLDGJn6vBnYQTIckyTsp5H9bvEuYQEuBojt2u3ni1zMEpZ0DKtr22EJsI4xwdqKwA0iyS2yPCE7QY95sha2j7FAsYWx5yqRfEFbBsvpyYgo1jFgS22Kny/8x9DIFjX1twhPUho4rjeQgAxqjHnFiNaSD+ojKUsgJmuYb9tPBRwS2UZFxwCiRbMZIXZxANm12WBsBkioDGi0XoKuKCwQwGqRGcByZzjuMeR+2XYQQSnF+PXv/s5Yo1AyAELRVm6CkNFoWwgxGGMMSsC79BJCnoaIWZHUEXaSrBnEBZeNfS/lCyO4gKgl/YOUYUbUGlTc0khzjvaS2Qg52xr0AhAqQLSiDbvM4oqD0Z5PFloGK3NB+7qqujdezBZjBqG6a5FGIDCzIpLoSmYj7ldS8Rh7/YDoIGsD4uoC8fo5uSlwvFyBgUgham2ICDRoMEBkbU1ALDqE+cKKOTIiXUVSwAY0AyqU/cqS5uTVZmSwkmGwvqVMTGiQ0YCjFMgHAEWySEERo0aDgIQKiJgjyYHAeJpphLTI0ibZ7ERkFpMoAxzTKUXZJN15bPgVjxqlp6uvPo5ZMQqVNhGATGttA2cgjke2TZG8Ushx1IUtsVHFOIslKhkLP9sJmiKUlSP2PibmIlgmSzyOk/6fAJS0UXaR40k1OYqzAZ7cMhCU7WlEhyTsJdQMsR0wEJABLfipTIxdAosWiTsAxMAJkm2WwPIJZcYslEkUywY7wTUxvSfebQjjVh5BnKfFF8+2w/aTMT7S/nNo4QgmhHKpQryEOmVfmE+6PucAcJAjBhmRUTyNJ+mC7P5dn1XYRaoIARQYAwSgZMOS0SQSCBNNUiUIUJpaxQYclIYGXEeZMGxqT37nv7+3bv26LCc0OZ3treOV4pQp7blce+/s1YMntk6UJk9ZOGekf+Dad11880e+GVWjZJIaGxt9zuw5MHTTLVcuWz5v2YolowMj3R0df739T4tOXZrpaP/zb+7as/fwzLlTDu4+lk3AwMC40eD4qcaeXNpJ5UcnRioFZaB33tT33HDN6tUrmxob9+w4tP/QcR3UaiV9yvIFHlH3jNaDBw9PFAstTT01Ywb7DsxbsrA52+gkM9e+893XvPXK7u7ut1x8SSWoacVRjYNKqRSWGnINpXK5Vqk++dS/33Th5cf379i35yj5XrlYSyu3Z9as3t4ZTiKplKlW2CcIQU+WAjS1jc+uf/ill6+/9r0//M0v27L+/u27Gjy/qbU1qhaqxmTTruM6QSkKOVKG/ZbEf3326yPH8r/92fd753Xu2d9nKCpXqo7jJROYaU7mJ6MceV+97SegSrrGxkuAYQSjHB8JRD7lKCXeXMZojeDEByHZzZiMYTSaHJeU5L+TnHLKno9EwBoQFKMyxGnl6oPbtj609qk21dA3PtzemKu6/uT4eFNDauacUw5s3bBrx55IVwqB/vs/n/zNbV/wvdy17zpP5TKHtx7rnxibrIZOYfx4//GPfPLWfz351MwpU84976wTR/untk9va08HNT0+XjjRv3/R0tMRTTqZLZQnKlUdadyxZ9vf/vnoazu3JSrhFeeffutN7589d9723VtdDYePDRwaO7Fi1qz/+tqvIu39z09u/tn3H+mc3jatu3H31v4aw9e//9aP3XpHVB310mra1DkHdu1JpBKtHY3N2eTX/ut9npcFJzFWGs+lmh3HBWQgzI9ODo6eeGLNhjXPPferH/0gmUvmj45+7Qc/6J0/LZvIXvWWN/uJxv6+w7Omz5mEqNFXlSicOb1bkRtWqxPjhTCqTu3uLgXDKTezf/fhT33pdyqlSkHtukuvXvvIvZhRXb09c6bP3bRtfVhyb3zPBZdecLYhjwmYqVwq+74fmahWCfykr0NKKhwaGWhoaybCMGSOQhNyKYra2nOlSticzRrmsBJUqpUgiqIwIsfxPTeTy2itI42R5iiqOCpJHuzbuWfPvkNjQ/kDRw9edO75h48fa2xsGRjqD/LlXGfmzLMvjsZHnlr3antr7vJLz9m1/9DWHYfWPrXlY7deN31W2yvrNvTOmHngwImDx47Pam2pIhaCidk9swDDweHCgX0nmlpyAVZSqdSh431J7a5euejyyy8emBgBpKoxvpfSQTWohew4mtkBA6AUAmtku1MJyV8DyywUiVUEWjz7hDTAwMQYASjQJp67A4DMEaTaE3KGcIPEORBBQo2EQw8xrHkStOd6WW/PF4uw2pOL6zgM1EtHhNhg8yQOGTfTwulB4WzUZZO23rTdANuxrCVCY1zyQsxEre/cJiZkWzWenOUybVfALPxtW+XIR9j/a8k3wg20nJ/Y4lPqD1s12nZICKmsARSI+7ijObL1gFRiknsgmgdUzCxJuHKegOWunGTa2AGDxReEe8qMQEIXgLj4BVsXyy0H+0BUnbJVl4jH1cNJrlUcqhCDGQiW8IuIoBDZmstyfY5j3ySwGL6ltp4cf8v1x52SfdzWV0kuzpKbQUb6GDsl1u+SBeSMkT2WgZCJCSzJOX7YYOoDjPiFMxy3lfYFMGJTY3mXlnGLCHGotvXuMwbqZClgoQ25LuM3vvt7owwxskCDoAHEWN9xWItro532o23f2L4pitA+coORtX+OFfF21M8uMwKGBiQTNzLgyioV6ad8lYjFWAeEIm/fFVQqVkNazyxgIdtwvWRk243Y520fvO3UTQygSp2Ntm0DAG3xL3kjGSKoxT2D1WLKh4JtZeWTFNusLqirv+MKklGIdrZ0M2ip4gZAsTEGJePdsnZicpWYnRGiyFjtnEkeq8QyGVQopZs1AUN5o6XDQGQEh40B0vaZ29EEGJvi6hIDkwYAhZ4GoxQyqDqlJW5D68Q3svoHlh2KCR0NRpx8FBKjYo4AWRuhhREyMEi+IClk8URijNiqbJSU3PJWMyvDkcDRhC4gM2siUugxhCBMIrZKAIXKgDUystoNsvu1taaVuxFv1gKNGGlxTy5OkHEQybCO4qYKSG5UXSoPqJCMsqRJIzNoIoe1RiIDxEbHNTSjKHes8xYiG5SGGP5jFAEiUxYyjZ01yMpV1mpJ1hPFVjxsff2RrHCcDAGCDWyi2JWVZWOAmLRaB3dAeIdxIxe3AxyTBOPBI1q3AfundiJtKUuyv1iTJdu0IQjvUlyu0dQj0C3rEWynATIqQDBsHIsha0DleIi1IAp1KpXKZn3F4PouA/kcfvyrXzqw54AKKKx5SJXG5lR+snrFFVf0nTgSVoO+EyeqpQp6aub09tc3HWxqdYxxXINBIZg5Z+rXvvHRGTPmd02fBQDjheLaxx7esnn7s2s3Hh8a8FPJKIREJtnWmj2090hXR0MtMNViAIScgEoloEBHJiiVgpCclYtnfeFLH33lmZefePRV7ejzLn7Tu955WSrplwu1YwcHXlm/OZfI9k0enzlr2uVve1uuIdN/YL/nJ7INuYjDTCoBQFHoVSoT+XylODqyfdfWZaee6lKqMFnoGzi2etmKF9dvWLZkUWtrenx0kj3HI8f30oeOjLa2NgJwVOMyhh3t2fseefF/v/fDae3ZMYON2aQKgomRgp/wIkKINBGS65iaiQDdpKN11NKSBc/PHxkcGirWoNzR2NzcmRkcyic9FaF2XS/p5yLO1ybCbGvz+276wPz53ROhSrADsriQtY4UogyIJDhWFBpKCdOSLdYmTgyuo9ABowGMNuw7bhhGruMaZlehR66T9VU6++yDf7n3zjWe7zT3dMzq7Xzr269YPfeCT33+1n079yeas2GpVi5WlQ9dU7p6prbu3HLotHOXvP891z733IY//u5+bvSCUlQe6evozP7oRz/ec2jXr37/r8988npUmZm9nbqkG1vb9GTZyyRrlSCdSQ8N9Xd1TRkvFte89PKaJ184cKL/nKXL003Ztq6ud7zlPB3pSqVy510PNWQbb7z18h/+96//8Pc1M6c0fel773vmge1uKlrz4uuO0/KmC5b/+qfvvOTSH5THRjjlFAqlppZMQqUdMN1TGs676E1LFs6ZMWXqRCWv/GQ6k6pWqv+895/L5q189Y1tCS/81Bc/D5VqVevNL60vTxadBBwZGlx1ylmvbdryl3vuPu/M1ZddenlxPP+7X/z+X4/8o5Cv+D4W80PTps+FCP/wtz8P9Y1u3HqAkunDx/q1iRZPm9KU4NcOHF28ZMW7Lr3ud7/+YcVJUqJ2zeVvetfVV09WKpFGhTSZn3C8pDY1dsAEcM8/7jjr7LOXLFoYaHAdJ2TWgdm3e/vCufMxkXEc1towM3MYBVgLgmQyCQzkuIooqNQG+/trGjzPTJ89p1auFAr5RCpZKxe08p59Zo3DtG/PwebmloniZL5sivlK18yOOfPmPb/m1endrd2z5uVHCy+9/OrVb7+2cWrv+lef7G1KofJe2fDa8oVza6G+YNmKZ7e82pxIPb1pW1dz4+rzV9cK1QN7DhYmx1PpbC6XXbJkQTqdaOzqeuqxp4MSnH/F+ROT467jI7ACT5EBJIMQhJEiDI2W7HDDIPU8W3RZIwJryQ9BS8wwcXWMMaIODDKWBIz/zCKjsazM4vrxvye0UKBFDoFBHIQwhqFZfDzsXFgm+/ZQElokx1NYy2SWKwesV4JSRtiLrReJUgVaFrWDsXy4flxjXN6gPUqsVQqAAPwyo8UYibICfQvNg+BN1rUT2QE0pt7ZSDluHR6lxjaSOwysgDWjNhZd9gC0SM0k0w3jDsSaTlpYSsl3i2t/O5RmAGsuLJkDaBAcZiO6U1skiK2ILQJEkyFo+n9ENIvYW56aZZFQTGOWcpcZhYQqz0Wj5UWTGDshgIn7KQSAmA8sl6dAGayb79kHRraDAmtvaLOn5M0wAGSMqKrk6q1uRCoIC6jKX5ZRlo0QRWA0ELupxvB3/X/I4p/WR0izUUgRirmbUI8QbRkAgEwGGW1UGTCL7Z8UFwBEDuJXv/dbJlaMzKTZOJbaAEgOsEHlMABK0iTIgmBh0gMqNsK6JzahTRxAZeOyUYExwlEwbAwbBFdoT5FhO2YDtCIMtpWvkZA/sPRhAIjHXGy9EdExcHKBgbxH4i1jdZJyQ4kARdnCUu/YgGpb0CCy0fWaWjFobRsvByCKWdNsnUgxDuxGQtYGmMSfHhjJMChtNMWLWR6qmInGfHrSEFmLdHAZImAgEvsa4WkIiCZVaWQME9loL7QgXGRfLJtdYJnckokIqAyHTArBAHgMGgEMOaAjUi4hRiClmW/hhvrCsDMWE9NKyGrAwZaSbK3iBW1RCIzoImoTxzcS2OkXIhsJMQBCJG20tIqALnMk4zxgg0ohYsgGAWIXfGZmRylCRxuNBEQOMws/D2PuunR3kh0hMAfYYZ7U6zE1zy43MBbRRwLQhpVgLcT/geCAIoUCZ6CDNn4lUmi9e9HyCkkhWuMhZhmuybtA8aSJWSFKEopRksyCQKji4SbErmes0WpTFBFK4cXaCAUNgAhtBA4Ig19ZE1JkiCEWuWlUpycSgWHNQMSIitnIJk2SY2DFnXHJL2+n4UgmvnYeLM53HPMa467cLpf6QBoUEYOxwyZUJDQQMoxgZPBEqGR3QQzDWsLFSilobMhkck07XtvkJ7C9u1Ob6P6HHymMjpNnCJy+gSEMEmGxUKmUGdwmH/f2DVzx5kuefXlDErmmjQlqBA453JDKjk4O50vVlqZ0e2vu47d+4Vd/+83f77v99Zd3nL56eRCp8dETn/r4FwaGxns7248cGzXMyYSXbMhVKhNjw/nmtvbFK88u5Ac2v/J6JpcplSpoohCj5sb2cqWAXEulUqVKOahBKpttam4+dOhAWNMzpnfPW9jZ1dU7u6NhxoKVCxbPCIKwWqhWtQ6jKOGgm0yH1XK5VKoUa0ePHd6/Z197W0vnlC7ludl0ToM5dOxwLtUchjBzekexWPLdVKohk2vIVcNaUCp3LZo+NjhZHBl7/JkXvv+1r8+aOZ+rlaPFiYbWJjdk100lEz4CVapBUCo5jjLExC4Qg4+N2fRIf+Hjt37sYP8rC2Ys/M2f7o2Keu6SKd1dXY8+9lw6RZHhXDajEPP5arbZPXF4+LYf/Hcp5J4ZM4uliu+5YCDUEbAhdBzHFXc7ZCRHCfIZGqOQImOQHELQHLmOUkDgOeXxYmtrS6SYQkCMDLsZ5eSaVFt793VXXVsrByNReNYZp3/i1mu279z5yc//3xkLp41Wq91TOoKJctJ3ytVgakfn8b6jp5521uqLVrm12pZNh9e88EYxHDNBCLrs+q6X8u/5wx9/9ZNf/uSOf33kA+9av3nD727/8bETwwnHLUyOTo6VvKQ7vbe3lJ9sbm56+L6nH1yz5t3vvvzCM89rSmXKGD78yPMj48Vly6fOmD7j5UfWvrLvjZXLV6YorbL+9p27X312x3Bh9Mq3X0iYXdoz85TTsx/67B1RaaIK0Nbe2dKcckgVJsc9Nn4yNVSudDep++/5/ebte2rlaiWkm2/91A9/8Lk3dhy78R3XNTWlM4nkoYOH3ti8pa9vaEZP9979B697x5u9ZPavf77z+NHD2abM0mWn7Nm749ZbP9TR1lkrFsulspdWDdmW3//xjicf33jeJcufW7vDT1HfUIlxYkpTI6A578KzXnz4hdBVTdlsR0/2wvMuVK5atWxBqVrNj0+mc0lPuQYQFFdKYWt3Z2FyOJtrgIC1oUKp5Cjnkcfuu+i8y9LZBtd3quUyOQpJSZseGVDohGGAhgMdHTy8fea0BYlUIpVMguGgFoWkiViHEFSr42NjT/x7DWLCcUhlsxvWbfZJ986ZNjxSKBUqHT0dngnGy9X+kerb3/fVe+/6lQJcOq/DcHCif6BQKhcLlf7RgU+9/9bBE4c++YErGjuaduwb5MnKG3t2eQ4OFyouRZVa8djhgeNbT0w6GeXk33Ht5Zmm5t6Z0wYGJlq7WqtAbgRBFCEGUYQ60qjcKKhVAg0KkVwTidkwhtq4hgxqBNAGJLpI8HTZxGLERLjX1iGR7RFj+epgOwNbNFr+vwXsjAWU7ZDYUlYAgSQ682QLgWQ5MADIsR4y/hPWFioWJCVG9aV6iFEdG4gLwIgqjo01UC+sLRPHCGBU97Wpk49iUFEIPPZggVj9IEeJYVESKLBo88kuwJJVjO124lEJG6znIQCACxCxIKfWK9xO4esntTFMqGJUW5oaGXQ7zEb8OhHZGIWgrUOBtcYHmy5jLJ/BdktI8UTC/jbb+zCIzNpSiMBOftDyHcjeLrLPEOu1DVtyiCU7WGia7BexbBeKH5kUZrYKs9JqOZatCtWcvM0xidnyCGQ4I/MdASzRITZA8rqJa57cfi04ouQ+Q5xEAbZ2wrgcELdG+ShbEBoLaKIVb0grgIa1AWDDhtlaqRMo/Op3f8MoFlQOWmzSyNchG9JKzBGiktst+LcCllyMmKxAbATJliszdS4ZijMmg2GPwYginG0shmZL8xA2iIDobFiyktiWUEgg/R0x2JwI+fqip0TLSbNdQ0zzsvQxBEJttEIyMtFCBvDBtsiGhQsOEAsJRCeLjMwGiJCQBGdgcBg0kTAN8T86WRtfEVdKLBZjzEwkoLMxgMYgkiYBsAV1ZQYwSj7fAgeS+FRHIyxvCW0atnxDZnYIwRgjBZ6AtQYs1gAIzIZlFERkVxQAgGJQSkFM0JZCV/YLEaWeJKKzdTxgsRKyDbHRiC5jBFxvYGVDFFISKnmtJOPAWKmGTWAQ1AFRagwGjewwgpI0AiSZiIKOLLUYEIgU2No2bt5kp5Ehn1VO2+2dkNlYDp+sDysi0MjxpogxrxBABi32MQpVxXZodrAkC07uum0cmYmUgcimwBgC1AQikTcKFQMosLNg4DgYwHb/MTPNeknZfYHZxIvViSMpRb0gRtdMCIigre5HXEZFpMIIjCJpwZP/oNgkVSQaCAC2zzHMhlAZ+9ftVZHQllgrJG1DiRnQ9gJ2GSIxG2VfG4ophtKEyZZOjOgwGEXlCjelDUXIFBYm8n9/8ME9+3fteuV11Aknp5pyqlZkhyjZ2jCls21ieJIjAoRspiUIa6tPWfDxT3zqUx/5lAmrpTAwYIJSpbGpIdRhWK0FtRoTNKQbO6Y2nH3BOW+/6Lr+kb41jz01rXcWpfUr61575tk3PB86G1r6JwZyTZlceqZDZmxsqFIsuA4UCuVkKjFn0cId2w9lU26tWHMSKpVItnV1rF+3LgLXzySTrioWa20N/tjoGGjmpEk2dB7cu/f3v/nJqaee1phRHHGkdTKTMEFYrUVRUK6WQ+OgQ8iGq9VytVZNppLKTaDBiomG+0fbWhs7sy3rd65PpdobG1LTps5on5bb8dq269/5SZ480dDZtf/A9nnzFpSiaLxURiPnnwpDR6WzzS0NqZQ6svuwm3AITXdP91D/GAE6mcTYWLEpi8ODo82tzcYN3/vuqzc9t22kWOpobi4Hxf2HjjuGw6jmJ5LaBGBUY2fnli1bHnvkrqHRSeX5OpBAQy2ZmUZHvusiKW2073taG4V2lmo0a2BPYZJSlSjM5bLtzYl8fvLZNa9NnzlzbHLg9HMuzEBpZKzwxs4tB/fsS/iZR556rrut7bwlC6fNa0+76S079r22aavDiL7K+Jlasbpo8fzx4sQtH71+6fz5flpVxmrJxuk/+O5tKqOeeuLpKKo6jqu8dL448tEP3XBiYKJj5ryb3n3DZz52s5/yz1y92vWSmYSfbWrWUUW76WJ+6Etf/tm1bz377PMumqiMfuMbP9i3/+CNV1917iWXzJ/Ts2/Xnq99+edHg3LWTd72zc80Nib7T+R/8P1fB8Qzeqfcc/vX9m/tO+fcxTNX35JNQiGMss3Z+TOmHes7oSulZDJdKU02tjRlM50K8ze+77p53bOPT5zobpty38MPfejWDyiDlVA3ZjK7tu6euXAW6nIlqnVNmb932+vJhkytEJTD2vDI2P4jh6Z29/jJ1LI5i1UyiMp048c/c87Zq89aufqv9zxSqxTburqyqfS+HfvCqDZ8YlB5ydbehtu+8nkXaKRv8v5776OEv37X4RVLZ9/yweuGxoYp8oJyddGyBZPFgtJQ1TXPT3heQofa8xNhteq5EBquVqrpVFKhp41mYGYyhAoh0hpJ6ShCgxEEjuMrUkTguGQ0sNbVsOY4STRhFOpyUGrINILSqWQqQH/diy///vd/PO+cM8PA27N3X3tLc7UWgYlGxovaT9335+//6cH1zz2x5uxT5kzmxza+sYd1uRiFh48cz+SynS25666+avb0js50IzqJA7v3z5wzbXyi6rWlU8XoY9+5rWJodm/zZW+59JF7Hj+y52gqkVy6YlHbjO6Xnlg/b3bX2eecOlk2s+ctePm5Fxubsr1L5pdrYRRWyGCNEdlRpAlMZDPN7fxXMUVggNmazMX7upHkSmYWtysACU+tg0SIBMaCOPWhAGJsISkei2wsHskYI/N2vo72nLJzchMTtGWGHJO9AcGwOPPYcz9WCsfgl6114mQWODnZxthYRolkWYjkAsEYiwgaAkkKI7AzB2IUPFQxc9yiIEB9RCKdQp20w8xKzg/LyBGShh3po50AoDW3AUmYEupurFKMKxAZFQhP3prJMYsfI8TMY0ZQpj5FkQ+RngiR6iJY6z8uRV7d4RNjN9LYDekkj1U8dFAqTBGpSikiWb/xLMImeSGznXn//4mY8lViIQHYx2fDthAtMlvnUyDGtbfcfEugj0laEL8SUH+dbPdpmS9xv4HWJBeYbBQaICqUpKJ6mwG2TjLWHBxikYdGUnEjIU9ZNNyKUDlKOeJqaQDjYAJiNsgKkBGF8usAKIbIRiBYSbIMp1Tc8Nm2CAmZHUEtyaqSpRIJ7HgCHGbQhkj8mNDRbEhE26yIhIIcR+SBiqH7+BHbO0xSHoGd7xmxu7GEPBseYEfYZJtOklmUBm31jowg2gpi+4DRkEIApbUmAkIluwIgS9411uMODAj0iYDAEYOIQFGB0YwImuOcDmNs4QaMAEYp0sYy/QgUABCA5MRaxx6x/jE2bUJBvO7jB6zBAJKEe9u6FSzBxAbmgRKbWZBbAfFwSMpdcK00iBEJJW9Pvr/hukq1bgJVV3Gw5QWDXfeMDEiEBtgoyXGRRc+AjMpCFPI+KGAtJEvDOiZ2yZ4LDAQmkoAIQFLk2N6fFAAQsQZUhJFmst9QLl2EDbbiFhCC4iVgmJEsdV62WrY0G9kSbH8qdp+xYgYBmVAxaJkCWCtPkR4AIhlAdFAZ8VNHBgJrlYMoNr0keZbSzRIoQSTE3chCABJgrhGQwZASgMowRIpAGyGrMYAy8ZTUzqFQOl1p90gYnQAnBziW6cd1H0eLlRitFZIxxvKC0Ihrlky2IlPf5Q0A63rnb3c3OSCN3c4EgsLY6MI+PVSIEepymZqaq9Xx4qe+9T8jB49Pjo83ZVPJpuaOxuyyhUvHJvJuWqX9RNXLX3zOZb3zO/2k/6d//GvW3Kn/dfNNWBhPNOcWnTLnL39/jBwuVUMgyiST2abWiWK1JZMZqY0m065md9GSZRdetLxcNJdfcu1b3rn6s1/93sjY0LVX3AjAM7rbK/liS3v2tNVz3WT36+vXDQ0NV4JytVZzHEcbXcyX50yfdcUlZ7zw6mZHewcPHky42cVz5i1bvOTBRx8Iyjxtakt1MqgEQWuulkw5TjKnAWnGrCREAINBKQWODwCVYq1aqGgII0PVapUURo4bhKHvJxvTOWZNST/hul6kc8lMNaqueX19z9T2mbOnv/zUi+teWfe/P/vtjNbWhBdwcwul1ILFpxSrZZf8GdO7RgeHU05qdHQCERMmHB84ESQy7a2NtWotYCgVa2FEytMQRk1NyUKhkszmSuXITaV3vbxrxpyZszPulu19S2b3Dg7VBgYHmhrcaqQTDnkJtzIxfsapix/414Mrlp/u+xVF7kR10mjlOspzXNdJAhnf9QlcYwJkx0EKDUs0PEJYjWBkcmLWrCn3/Pnebdu2+GE1l8OVqz72/g9+/Omnnv7kZ75+xZUXOMnsvEVLzz/3lJuve9uWrZuvfuclo+PBH393++bNu6KIZy+c9s0ffrFdZZyMB8l2AACoQVgGN51sL+/YvPGM0xY8seY5FxxDbjaZSzuJSpV27hh64aVXx8bvvebaq2ctnfXcs5vHTjzyuz//ZdPr25etWLpn3/ZMounbv7v9sosuQtdfvGTW04/vf89br160Yk57e7axua1arvUfG0Og0xbNftc73rJxw8bWjoaBoRqAk3QQDB84MjhtVs/tf3x8Zk/ryFB/1vc95lo14sghl4w2CS81MTgRVmqVar44PFFtKLVkmwdHx7/4mY899shLZ517Cmpn69Yt6VR27/YdU6bNbvSbtmze1Dt9+p69B48eOTSlo7N3eu/s3rlGV9PNGRfM4SODVK0Ek87r2w6esnKRqgWmUB06dmzBhWfs2qSnz2hf98yeT3/6zLdeeU5DQyabdQ8f3LvzSP/Y6Gj39A4TBP+8f+2ObTs/8q633fHAPS2PNEUKli5ZsmjW/K6piUqlxORAWM3n81EQtLV3trSmwyBibSypEMTaBglJay3nm++kyCKKxmgIo1DO8ygMSCG6Xi7hgqOIEsyuqZbnT5160wdunDFzum+87T2de/ftm9Le8dILG3tmdBdr0SPPvrxwWmd5xYJjQ31nLJm952DfsaP5EsL0zqmXX3pWNpsaGc+fc9aqXDozWSnNXj5ncGiitTl3fDT/+e/+79Jls2bP7NDsrnvmhe4pva+/vCOdqfQfPTGr3e/u9l565cXdR7fVolqtFJSLRaZEUMabP/Gh9q4ZxUIp1+R7EBpD4DjAmrQRQ4WI2QArKW+U4CtocTqrgoN41i68cjrJLpH9EyyNOy6UDZJARZbGKWiInNK2DrNgoD1ALRobI5IsOJAtT0GKP2kICAAsQ1j+ciw0BUtftSdnPJi1KLkAW2AvIyZFCKseAQ0qR/BamVtrEC+POsMdrQIiroPR4pgW7pW0ASuzsMZvFslGQkuwR0lYFSwSACW+l8DO6k3cJ2Fc78oBZYSHjWAM2dwDQDFQAfs0AIGJY1I0CDlJJGaAQLbLiscn9WdhjUXBNl1oRZsxuYRsJW5soW4sY9UKHwTFE6hOaGD/oWImiLuTOnApnCkWV3GpUY2tyo2JySFAVqiuDTrIUvRHMadD4Hbx4WCK61FrnwFaIaBy5B7YWGRApSRV09LQQCpZGUEoQsNyhw2zkjKOEAyyIVbkRKQ5wm/fdru10QLS2hAKXKtI2VeLEMRjiEhpYMUy6DDmZPfCWvIiEUGyrC3LHUBsJtjGYtmmEZRhuwTlkWs02hgCREXWLBfFTFyeLmmZQ5GSlpeZAdXJuyOsaya2wym0C016HYrxS1Jsk6jlbdAcD1MYWATNUI/usy6yVqODyIYVgAKOuD4ERGRjBHQmBiAlPv8GFYHRRkZEIClHaMXqiEbbJh1YIdneGliLIZLgDQYAjEJB2gmE2i+AOBjDDqGtp60fgl2UMTNMfDqZkRRASISMLiDGrliEiJGBWGMDduQlkwC7dYAxQLGJmWYWfjeyACms0AFiYwxYDyCp9GUMYZDZCFsJFRiNqDRwXFVzjJQoFpMi24hIAqJIGxQSKHIYWCHqmK4OwGhTOeQpx0QVy7m3USLx1JIYFRoxMlIxPmHI+oGJDyIrtji62GvGpD02bJT4DdtdHhmsQgIRDGsCBFIYW5opWdDGQD3KC9HyuNheusUiYuccsvsNxvua5fsIUAPWkEJJWk1dDWRHIXbxyLYL8VGk0IJJ9oEiIKFiHVkREdmBuBXLxKoFOQ0REGW4TADaWFcjOQPk/gICgyZDNhqBgFAZDjQlkuT55qYPfToYOua4WUAnijSQbswmS6VyIpFwPbeYH6/kq7lMbnJiYsmyXo+wf7iqgDRBrUpzVkxfuWhRqhY+snZjQkEQhIP5/LUXn3/8xNHRydH5vTN37T509rkrT1u27Nob/2vJGcvauqa/+/qrN2x6+YUnnhkZG/dT1NTQTFGtWKhEYRiVoyCgQNWU7/gqGUWVaqVMDi1duXjJ4kULOmb8+e77RifHg6BWqtbmzJt70fnn/eNPd5ZC/YVP3jAwNjB/zvLju4NScTjQ5UfXPhKimj21/Xd//OWuPbtymYbS5FhzQ5NKKkI3irQiAoQgMNpUUqmcj4nJciWVSyrPnRg9vuHlzZ/+9Hd657SkE86ObUeam71EuqFWDZXnoKtcpOa2jvzoaLUShGHNoVTVhImUk/QShULkKGRPKTZRGClEL5VKJP2x8ZLWERhoamvt6z/RnEgaoFQ62ZzK/uZbH1qzef+aNw6ctaJrvN/89k93tE/xJyZriaQ3ras95VKmpWnH0RNTGluveucVcxYu2vna1oZ0VnHU1NxhXCJyoRaGEAn9AJXLxpAiYoiiSKWyX/zi5+a0tgwMj7EDioLr3/POZ5955fjB/aedeonnqmwX3fyeW9za5I9+evsf/vg/r72xt31Kyxc/8rV0g79x485zz1/1+W99ujocTZnWsWPP4fGJ8bNPXZ5J+Nt37d19aO8r67cd2Lp3xfJFSFgu5M86a9Wpl13S1dj56U9/dsfr20+MlU9bMf0z3/zyw3fcc8aZZ7VPn7ljy8t///s/FyyYPTZWu+UDNwRhtPWN1774jR9d+dbLLjnv7AWL5zmBV62GtWr5nvvveG3zYQaaM3fK0f0jk+UoOzVrhoJaGC1f3lsplUsRDx4f9NNeoVD2UsmWxuYZ03p3btnRMaPz0KH9KQOtbdNPDB/L5dzLr3p33/jut77pnO2790/rnHrmKQsrtWpLe2txsnDi8JFKKexd2jt8oug3NrTkXCdwRiYmyDGpVMb1Ekk/TR6HQZDOuUvnXDjn9IWnLlqVSKpXXtlRnpj48fe/tHf0WFqlSsHIz37xT0T3lFOX/vdnPrhj/4FovPDCC5u1AxPVyt7De2fNmLV0xdz7/v7weacvmztvpZuIDu7e97u/PvLZz93wrqve2jc2kk6lkdBzHWOM6zisFYCWA9kYA6wMRI7joJ11CuoMDMZEBhiUY6GHIIxYayIl7ESlvHI1UqbGGhwnUQvLvudqzWFQ8nNpCMPRfGH/wdEDh44sWzR/5fx5zU0t+/dtvf/+5xcsX/r4iy98+ZPXP/DQv5csWNSZ9tpnNk2UWXkNr7/0SiqZ+Nv9j3TkckcGx0YGh9o6m1VALVNb1jz72tzpTcOjpbdeff53v3jDhZd/7q2Xnx+lOGJ/3bMvqZSKgtBL0Pw5c9/YsmPkYH9b0gsTre/71K3Jhg4yEUQ1EBwEye6WEAfOgzFSJEF9GmxhXrYVmnA06vbVMaoFRvxEbNHDYI2+pbSWQpnjoYKU9TGHFiVzF+AkS7W+D9tmAeogJ9spgACykbXgtyAyx0WRLfaIQWzE49AjGWDbAp1jZg7H3Ba73QOwIQMIEAm6KWQAOYnEStWWzmzJEdoYrEPhbEF/E88n2JIC4uKFjY0Ks3Nojt0y4aT3JVqesWEH0RgjBYMoN9hYwaaN1rT3xt4sy7ux3psC0cVVnbCaAdAKce0NFdKBFP1oufjIgMrWYxJ2A4ASBgxSABJyZP6/Q9DO1+W/kSU2IcvztcpA+ZXSTdmJBwGBYVDST2rbRzEAskKlwRCzsuwjEIjeon2stQFBbVmYtwDGGDFoNtog2QgttmQQI2WSlMiWo2aHR6hNRIqEdBaFTKzwW7fdHrB2gGJpPCNZfTobQ/azpBbUDMDil4toAMlIiWnimYJVGRsWdxQ2IAwSYAKplBE4MsJtloxQAggNggFhMCNYt1ubg4CgGEwsgrSQpAEmckC68JOMZVH2yHsp3awhdCylARyNkRE0k4TADCpegQ5K8Fm8+GSAwIyg7OsFhpFEMQ7W/xCsz2lcfDMYiDOtyOKwQoVHO/mRPhW07USZFZLsRwIOSGwPknUkIDaAClFaLrLaXBNLveOCEeWKEEURAGgsWx7ZgEtIwAErBxiBZMzkRjZDFurx0WLNYk3BwcLCKExHq0qRgQ1r0IgM6CCHspAMWj0DxkUnCqlM9jBEg47hiICJPABttxlgIMVak3KkmnXQhnwY27GD7Bos5psIsQ+UuJSgthQdOyYDYAck9s/YuGZQ8ggAyRgTZ1iI1gVtgArXnxgAM53c0awxD1l/BctGVATGABJR3HcKWZAMyxpSdXogE1hPW+E/gZLYF0sxkiENxr+wjgoZBqVFpxsDUyiXIsG98ZRXPhKt2xbYDOeY5RZD+CwNuUjyFSlk1sBkPaSE+ojCZ5KzQSEZNmLfpJRjXbjtwyVE8aQ2LpIGMsY0Zr2gHE7q4ne/89PCYB9pImMqtXLnlM5cY/bgoRPVQtWALlULHR3NYRSRVgZMFELI1SltPW+9/IKX1z43a/Hs+eefNrW17ZF7HjA1Wrd+3aduuea8C06786+Pzp/T297V+c8Hnjzr7LP/dvdDaS/TMr37zj/+YGCosPHVDV//6ve9hOcRR0gGonKhmEwkvISXctNz53Xlaw39g8M7t6x3yAXDOjLZ5rRm5XHY2NQyPlk0IWjD5GMykchkMug7i5bOuOJtb5443P+tb92WTlE1wp7OzsbGzKsb9x4/vu0nP/3RmcsXtnbPMUGlWi0bTa7raWTP88JKzUQ6mU17SdcY7Se8geHC7T//04ZdGxb1zn9t6x4Iq+ViNdARGrupeuSEOgKiSAeu6/iphO8mgiBQkb788gteeWPz8eNjCZfGJ0uuQwohMKalpWliooJIfiLhJ/1cqqFYqU7mRx2kRGNqcmzyhX9+6/6njjvhse3HSqctOP0z3/paZ5MbOZhLJlfOWpLpcVum9Tz+9GtH9u5+69XvHD52KJFzWhu6OzoaTz99ZSEf1CpBtRIEYeS4Djmu7xA57KAX6vFwPHxmzbOvbXw53daSr4xddvY1JVM8tG3fWReftXjGsvZpqqmxdSJf9Mg77+Lzw2J/SdN3vvq1zZv6xwtDvZ3TW3oyb77kgm999+fdPd0hu+/9wHVbX38jk8tqNrUa7NyzZ+n8+Zte3zg+Vo4qbveC9t45rc88/nJQLrd3t59y1mlvOmXZ6rOv+MrHP9DS2lDFYGrrlERj67+fem7wxPGmpvTSBfNXLD/bKF60agmWx6q1GkCyIWOGB/O//ePdR/f3p2a0H9u319O5SPE5F1+4Ye0LvsJiVIOQ23o6xsbLjuNFmqNKwaD2ko6u8Kyly7dteKOp0XFTyQSpoFzMteT6x4uf+9iNKxbPLRnMoJrZ0z1eLjs579DO8v/++reHT+z61Kc/PXdqS0tnU3W8NDZyDDjT3NzQ3j2FjUblusTH+4/07T3+2NoXa5FZcMrS7pRa+/zm09607MYbrtuz8eCzrzy37qUd7d3Q3tb7nrdfmWlt3L390MRwyU+Fy05d/uy/X/zyt/70+c/d8q8H70KV9hpx9eKlH7vlulxT2+1//OuVV101PjjU1tHJCMCRzUsyIIWC57oGGbWtEMkRCR9FxliqtuZIB67nAzKRA4YjY2rVmqMcDRxFQRRFg8N93VN6kwmPFEYmRIMGEBzUQeA57pH9g5jxvWRDc8pNpnIclZ5/9uU5s6fliyU3gbv3HhzYc+Tuh9YvW972kY/dkM21fPfbPzl8otDU3GTCyvBEIZ1LN7R0vLD2jbdcuvID773wO9/+1ViZZvR0LD911sSxooHaZRedNdA3+sq6LStPm5nLZJ9+fvPV119y3tJVd/7tn0+vfb5CIZrqBz/yhWxTE/rJ0MTyXbDTfrLEEYhdGMVELeZFSBBs3R8T7KzbFrzCD6lT9gU/BoXW9CYmrtpaX+oRrFf4GJM2Y3Df4ttsyyljXW0koUmOX0Jiy+AXYrv1l7TmdaI8lVEAIaNBE5/miJIuKixNeyCIG4WSSW/cSRBCBEgnv7Mtjg2LqJRjHEmEz0YihThG+qRYiJ3kGNAaLrIFwCxtGGP0yo4ADBtEh9GAfC+Wg94RNa2QR5BsohUyMmskJT40bGzgEjOjxSs5hkDlWiWvQE5tw7FVoTDjLRM8LpEFlzZokBWAqUeyyvSHTqoRYta0rYTZgpZ1sk8sqHDi2bvtxoTnYV1J5PQnELdQoaPFgxu0filGiB6CFQNLDWk7A5GD2j+Sw5oU2GGKkdCoeJJjOUX2sYCONBCBIg4jNhH42bQOAopq+M3v366VISO6a3JUzEKzqKKx2Cq5CKw5IsYIUdaJRYVttSFqSBPj0ApEvgEKIADp61ABhyzTBuFyoVSsGlDJFwRji2fpuQkdQLBcOlAxx0uaPwSUmQQjyvUyxiaPFpUXEQ8Aoms4kk5LIp/QLhVrwQRS2EnmhOhOQCkFojxA5sgStlQ9RckuMCQC1CydtIlVJmB5JyBVlsvIVvtiy0tGQEfIY/I3rTLbIbtipb5HwyHGfl6GtRSxCDaPkGwHSjZlkAAxVlHLmmCfwBhkRkByrNxUKR2vPhLxNDgIshMpAxEyGDZIrMCNCUJslfHytEnZUDHWDApRy1ZBaBgdAmBQQjFnyRZgA8CKHLQQuCOvsaiJSbnAjGwMEYFHZCJ5kUGAC8s2Q5n82fVB+j/+q0ggnBjowZjLzgDKpqhIXAja7R4smCEFMALYcQ+I7AmZwDGOkRRvmW8CksDkwMaAIhuBgMCGWAEDKaxb+LOR8QzEMYESGixaavE/xnhyKbeQYmKitLgg8hdEw+wQAJKk7om0Kh68oLDsrHQLWCzcpAGJ86RZAysQtEgoXhhTMYUwxlQH+MGgZfoYVMTGUDxQJmn1kRQ4nuKQvWwCfDJDg6Nf+sEPPV9HpVphLCyr2qqFy05dcs7jj/9zaKyc8IEQCuViQ2MCgaNKMLW7k13evuNI0k04vk61NiSqMKWzYfORw9G4STb5F19y6umLTuk7cvTlLdsvfet1H7nx2rvvfbgp67dPafVc30nl5s2e8fpr+7I5fM873z1aoqSHySQZE5Fx2XF8N+n5XrVSUxgsXLb89Td2DBw/gS42pTPdXbOP7N/KaY9r7DsuOspRblStlYLQMLTmUlNmtC9euhxdHh44/tQjL4emAuRAVMs2ZC4471SOvCefeOrljU8dPTRcqwYT+aH2to5UIhVxlEylowCCSnlKV05D8qnHnvnGd7/X0NQzf2b30fHhQn7CcbBYqFZKlSAMfD8ZBlqRC8y+40ZGuwnXkEmn09rooBT4yeTV11+w7pldUTV/dGioVgqSCadSqRrlpJNOYy6XL0fEDqrIsD+9Z9bOnRtVqCPUqUwumVEu+r/44Yc++d07zzpl3nMvrS9M5HW1wlGUzKZHBweO7Rg475oLE2k1ZWpva1Pb9oN78v0lPVa44LLlnTPnm1IUsso2ZjJpnzxOeYlyGGV9b9vWzc8/s25ycHSoPBmNT3z5S5+/4p1fvOD8aa9u2vrpj90ya07vkRP9P/7Jt4KqmTWt54F77p2/fNZzT77+x7/dm04mitXJNCnlYSbbMDRecz3T1jGtmi9PFIPlS5e88Py67s70n+76+11//8UZp565fuverGcee/zFI4cG2HMbE5nWdueMM0+/6E2n7z1w7Mtf/foZp63aumvr73/1pwSZvr6BfbsHnnjhqUNjE4cPH3j4/r+1ep6DODx6YufmPXc9umYwP3HRWeePjg6O9I9NFooAiabWhkvffOm+bTu27nijUtJewm9szF52ybnVsPTyup1BLSzkJyNtCGszli8fGRqt5sfK5cCnKJtt6m5v6Zje/uKmPe++7qpMxh86PnT7n+7529236Wq4a9vhh+65v8h4cMu2r3zjs2eff+GRfftfeX3zeN/Q13/wdRPg/oN7ujqmTulpjGrmjWfX/fbeR95/07tPWTLjfR/4ZhSGa5/6yxtbtoZl+OVf/rp04fSn1m5ZvHrh5WedetZl5zz80Npf/vjum9/3Zi8Nl779ktu+/psPfeK697/7i3Pmde/Zc3T/0MjZqxZ8+oM39Uzt2XP0yPw50xKuh6y0DomIrVYMEMAlZWw+o8j1BWVjRY5SFrDRxhhjJ/rARoGKjGY2NvXTMDiO7zhMBBETEQNrExrkKDAAsH3zfvadFYtn50vaSfg79u49cbB/Wlviz3c+esUVZzz3/GvFSqE0Wg2CMNGSHSuOl4YnGxsT6ZaOscGxYqmWyqSaOrqPHDy6cFr3mRcuyhr3c9/+87SO5msuXPHJD1/5q789/cH3Xj0xWQxrwcB44eVXX7/m6ss8inR2Ohr169vvOrx/0/TWZghqN3/0o0UN5dAoxBBiOqtUdYBGzklmtuxW4FhZC5ZiHnukMBnQJO79EGurwAKqHFO8LSEgBvUsSxkAgYygzlJnxDg5xAWNrUTjAQBY3bD4OgoPQuClGP+WtsWWEBaIt5MBQcjsT9vSBuOvhgDCcxaFnAHrTC/Il+WaAIvN0Emrnv8wCBHcPb52OSkQjC2lbAsEQCR0Hdsj2TIZLPopP2esKlcxk8HIeqCCQXAoFsHGUUJoEwrQADoEBKBZXCaFAcw2MUmjRrA9DDHZe2hvMIulqMxykORLEp4MJ43LdcukgnqvEndT9p8Wi4unPxCzRyB+BvabirbEpgogsw2QttwgZAZWgAbAEQEvGyAi0XqCISaDTKg0R8KKIFJCsiAggwYsm9dRiJE2jqLI1L1h8CRRS7R+hthErIiiJKlapaIzGc9LuYjprTv35EeP47d++EdD9umAdpSStFpWbKQqkTukEBHIWK8qBxCktwNiZQnIKAblDCe/LQtlWFaCfcO15A4Le12YVVr40EgMSt5yY4loUptjPKFDS46AmP7FthmTOx/Two0duzADOAYiACJ0DJr4jQJmJkUIli9OQuuTAlpaXCP9pSPWw7I8hZUkbju2d7cL1xq42ppaPh/tt42pWZbbJwwmaT0JFdadBIgElychqhkNLO2HJmVNmcByN6TwNArJgLbmBfFokZA0GxfBdhsyzJKxCClDhhhJeciKY7hYo1GoGE3EGg3W47FkeKmANJs4pFomGhrRAwhlCRlQzCEiEinEKE51F5hCoG5xbpaoCGGvo5TC1g1KYhVsBqNDipHEDswOIRCRWVQBxjaOtskSgUdsgmnfGrB+WQx2OAQyhoN632e3x3jnRIiZWrE0lhmUTCS1UYQG7AwuZgsiWChFdAiElh5peZySXGZRdln4kl8IAKLWksYBKTDaQcdqAgyIlqtOPI2FuaDITqhjWlq8bbOdlWnrWQaGGRUYIyefPeoRLLAg+7ciJwIJfmFpdsCInbZRjAZAEWgAlwAZIzDkMBl0kEi5BMzMqgZ/+tdf1r/ymhuWM4kG4yaVp885/4JVi08t5o/s3nVs36bNFc+cccbKux98VoUhOeii15RIjVN18cyZbz9v5bd/+o/QRKBQF8quS+lcJj9ZiBiVS9rojqbGdRueA4C77vn7xERUC5R2w899+CYAOHxs4NjBfR/9yOcGRyaaGlK+76MLCXJdJx1FWpMOaiFoREW1sIbsVCrlVDblOdTW0HD0eJ9mTPiJIAwJyECoNSRTHhio1mqLlsx/59XvuOOuP69/dVd7e7ZcqBrW2Wwi47ojo2PJ9ualy1dzefiWWz6QdqiCmC8WXYTWlo7WaVOmdbVufGXbJ7/49Yyj9h462JZpqiInyG9pSUUhlculyclysVgJTOj5nkKINLa2NZjQgFK1sILKUejUolDXotaO1tlLusZOVHZs2es7XDFRxvd0wEVdTfrJXDZZLIXGhMagcnxtSooS1VpIiKzZS2LW8d1mb8asedN6pj31xNrS+OCUns7S+Ci6HldqV1x9/vVnvf0Lv7gNXf/MFStyXtP9Dz1UBQMcJhsSl517acjke4mAS22t7blsMuPQ/r0HHnhk7cGdh2bO7Cg6xZmp1u0D+y664NzVZ1yYa0pPbWk3tTKrVGQKPW1T2ztyAGZkPH9o9967/vXvbVv35KvjSd9raWx1ECqV0KAyoBqaG2YuWLjtte3H+o9lU/Te991yyZXn/+EPvzn//Et++4OfHJsc+eUvv/P04y/PnT6zuSN98ODRv/7pH++9+dod2w7kGhKz580/703nOQozSUKjurs63nHdTe/73IdntLeQk37xmScOHj/w2L2vBFhAv+W8N5/3+F33pb1094zZ0NBw0emLfvPbO5pSCS+bqlVC1/HLtWDxgu7Vy5eefuabd+7avG3XzrXPbGA2DVPbM27j4f27E0kfIGhIpIul4vJlswyqKqizV6+a0pA986wFO3buueTK859/+vAvf/7LvsnjRlH+WN/b33l9VCnf+a9/HT2697EXHj5j4dn/vPvem259z76DexfNX3Teyrfc/Jn3XXjOklRz6w/+5zdj+0emLe1a3jvz1c17Vq2YN3BseGh08tDhkSvffE5jrmH3wLGkprvufvDSt5w7e9aCwtj4aG3g7Lmn3vyZ70xrbxip5v3O5laV/NLnPrJi4extu/d3TOkwgDqMkITsrVIprxZGDhI4ZLRmBsdxQEp85RAoFiauUjqKmIW9SsBaGx1FJjISFEguKXRItrUwrFWjKOGooBoEAK5LbjI1cHzk1de3PPrUumqpfMaZyz71hVvXPfnKj77/41yuaXCs/+b33vjws2syquW6G9655oGH+vtPjA+PuGlv1vwVA8f3jY6U3WQq3di2Y8v+GdNy11z9lv7hY1PmT9vwwgvtqZTjekvn9IaumTa9pz2VcVKt6zZvcVVm+ZyZR0vjT9z3QGGoxCascmXKzOljE4VP33pTJVSBzH61YRBHcTQMHnBUpwqD1YTFqFaM1ptY8WQFhSiiP6zTTeTgYAbryWYLcrT8llhwBxZsBDslsHWjNRkCZDDKSq5kVGwR+np3wcAUl0No+QlGoB1mZjSiLpRT1QGUjB070OC4XI/x93gCAJbEIfx9O9ywcDHbZE17Vlg6ttBokBi0NY6zXF6Gkz8njF8JCxa6d/zv5UpYQCjLtJaykOMhC7GkU9qrtRMYYLRfSvA6sOFF9SmOpQzIBcvkwN5oPnn/5e7KgcgEoP8z30buNiOgIfKYdQzwor3bQpLDOBeT6xRfscKMBcsMTEzS/9jfSmDrE8GgTw6KBGim+gxeQqgADBrxTWepJ631Kyqyx7dmTaiQSEfSvSutWbi6JJQnRMDYSCSKNBIpg8Yo8Al1AybWb395396jm3fsPHZwCMqT+K0f3q6FXoDMxnUdg4jaANVlBagNgCLrfogABhy0cd1MaKtmud8sQL+Uy/GrzOgwaGBBpkUTodi+YITiw4Qg4yVk1jE7Wp4u1FFyAIhtUBSSracBtLw14khqae6GQVn/I2EQkRJglQEASdnREwmbHJBIVC4xw88wExpC30DEHD80BmGKaOv6IiiCijhSSgLeBOslsjUpaR0hMJBSQBoY5bsiGzYKDaFrIKwT/ITzD5ZBCP8h09EWf7UlvcscETGjo2xgR7zJIABoRCIWZ1wjoC4z2AAxsq0po4PMgA4CazSS0GqAiTkyksoAwKwAAJx4g0AARmTDESpHjGUAmMFRwEYk43I9MbWSgA04hBoNK2JAh+PdKgYyMM45l3dAiPRaOQosHwetolaQaKHOWysgkkWJwKJjF1Yr233W/goSDIUBBWEHMCI2B5AcOKw7SVlbVVuvo4msZEheLfvbAGUiKVwaQEQSBo/4MgnwQESWV2TlZzKgMYyorIUOMLMxgKQBlCS1SC9IQACkUct9JKDYRkK2czlnwK5FUfaw7QaASF5RY5X0aEt/I5w22dyYiADiGWg8XQYtw1ACNIQOK+MQKc3guWE1yCQSmUzChGE+Ko9Ojr33Azc2JBtz5EYGfKXchvRpy8/Z+vrG0cmCF1RqoVNTDik678zpb2w6qBxKphLFcuWMs5ctmtMdBc7dDz+ZUX7/4GhkAt9RtVLg59xaqeop30l4RJRM+GUzvnrFKXu3H1mwZP5/f/NDc3tX/ubXP3n836/uOrSvMDROCd8ln4BbMmmjUBvDEWpgJgwr5YSXjsBEgXaTaQ/CZLKjUu0rVWoJx/N9b6Iw2dEyrVIt1iL0XTeoFZAw25Ce0tW+4cXXunqaDw2Od7a0VCdLGEV+k0+u/76r39rT3f36i+uffWPraeesev8N70SCpsZMraD+dOcTe/a8fvTo7vHJiZb2KeV8OZFwCR2jdIJSQ8NDgVFGh0E1IEWgqLWpaTI/4fpeY3MTGnKdjNZRpVbWOghrNTK6u2vmpz78rlu+/MOmJBpyEipVrhZMxZRM4PheS0O2Wq3VwhCQPSeluRZGyIiRRjYm7aFLUVTzkmnd0tMdlKLRkREAbbDiRA5GQZSIOrPTX37xmQ9+9iPjpcpbzz2jrWvWug0bnvr3v2fN7F08Z+maZ17s6mnt6eoYq5ZXn7rsO1/7vyULp193+Xv27jv22sYHn9n44rUXvv3JTc889+yageP9K1euKo6Nd/XOS3nVu/55/2XnX97UnhkfGH5m7dof/ui3KuQKMmjT1Nagg6hULPvJtFGIbtp42NXSlkuntc5v3rE3kUicdeqyKZ3dR/bufeX1nR//7AevuPKC4lC5MDzS0tP4+ss7QOnO7in5kfLRY3tn9k6f3ruISeca0hyZvv7j+/cemTtnTkdbm++qRHPq8IGBD7z7Y8PVoSa/ceGy+YcOHGvN5d5145uHIu+OX/5RVZ3Ai9yUgyFE1SAMwWvwkirxs5/dteGVe0C5/3rwqf6hUUPGw6SjoiiqaIy6W1r7+4em9nSNFkvf+trH3thxMJPO9Y8ev+Gd7/rf731TZZr2bsv3Hd/e2tpU0aXxkYnVZ5y+Zd/2qc2d3/j6Fx5/7NkzzjrtqefXf/bGy79x25872ttf27jr8msu/MSH3nnbd/+06ZWN2WaOKOOkqLutPeKoq7GlOZs7/bzFv/nzi8tXz3j+yVcmh8YGhkda2zuWL5/6vqvOXzBjyWMv7vnXw/f4icSGXW8s6F10YmBo0dKeT978gVx7e2l8QgehSnoKHNdx/UTCsI5qVfJ91toYBiQ22nGV53pGayLFhtHBMNSFQiGbzpaDihyRZFC5SmsTb3KC7tGJE8fRwOG+w8vmn+I1JLTRG1556bm1L+/dc7QS8ZL5MwYHhr3G3LFDQ8X8xHihtPpNK8489ZQbb7jlEzddf3hoxAnDQrkWge5s7y7kK+QpJhVoN+VRbbJWVVAqh80d3sc/+Yn/+cJ3mxucqfOmjpbVwYGxt7z59Iwy61/e/cfbbi0U8g3N7ccOThwdnji073DeVJubWxbM6K7oVN/oiYVz5rjpRKkWEZIGu7kiGCAHDBCDFtvKuNbnuOBj8UKUwWwdDK5TO8BI0QhIMWUFxVXHJrYCcL19EKsU/E+gSaqZuD5Gm057knRkJQp2QGzhaXshHOP6wmsR23TbOlhKEvwn2x7ZGoZKzW0LcX3ygBQQHesHy398X1tt25+zZBAl1exJ7ovtQKQ6YGCFYKy5ByBw3I3YaTyDTJyxnjMp5YAj0ke5Q2RZ3/KHTEg2yw2ku5GwHDQIDNoWM4K8sQa25z+TsLNsx8WAgEZoFHJMoh2JG+vWamtLBbbbETUgxN4hMns/+WnCQ+F6LrO1lzIxqwdtLYwg/vTiTWqBUWCwJqAMCGhNUZHqxzsqYgo5spJNQrL1BVusHihuI2P/eSNIIZFCJiKtAZXHGClyHUgnE5OTY/0Tox/7/OfbXEw7vnYVBGpoeAi/9YPbjWJkpbVxlQi3DYISUhkwstJkbYUUQwjADB6wBtCIGOtoFBhpSSNCx0CEwMxKs0ZiNB4jG6MNK6QQWQGSBi3kBCmyZeajgVCS+hiBI6sItzM5QauNDJVkLGEZ3tYdkQGBUPKpgAFi408pxhlQaaMB5QMIIGJQrLVVTgMiiYTCZdbEhogNKgJkY4SPQihAtpHAYGk5BYZG1IDK6EiEsoRiHy+4sp0vAQGAQXYBQmYW73YZOsoM0hZrHKdY2GaHkYmUQVDGRIhSUrNtKEWNimSYCYzY9RsEsYM04mMPillsQ5XVHyMYUqiNFh8gIOHqGwG2GRHQQCQMM4xk9QuRzSBYQotCigwjaCCHGKXHY44IiQmZtUMOcwTgREYnEAA1o5J3qW5DiUTGACmSejWux2UDtWNQIKpP41jCB6zDvQU8kNCx3s0INj1aQWydjyc3U9Hs61jqbTsRRMS4b5SrikXlAKgRHYtu2B+A+EkqJu2CZ5DFsNkhNMagYgcVxhw1RlJKBWEIQKiM63paRw6gNoHrEIQQGc2sQsWkHc2GyYBmUiR7JSAIez9m9jERaQbkCMkR1p0Gof6hNoI0CGnNvukYK48JQYNVOFuHKBldARCg1sZxAJVh5gRSqLVio5KU8RuUiYw2nNb333P3g2teCgt5F5xE0oWQXNLNbZ3LVywtlSuTo+PDAwMNGfeiy1du2HgwmXAKY8GWN7a1dHSNFQpTujqbMunWpuza1151y14VIg2qvb1pcmzYc/yU7ztJrzA+qVzHcXTayRgdaeZCNT8wOOb7maWr5t70oVtuvvGjbZlGpBJBKjSR56h5C2f3HTkWamOQgwgVIjoqnWwvlgaSqaZqtYraDaqjuVy2XK6QIoUOkDbskiIEt1arOuQBhxrM6lMXDwwcOXp0pFAoeeQGtdBN0opVSxwvPLx/sFYoFgoTU7s7+qq8Yvmcz916S2N7o4uVjhnTU35Xb/eyarXY2pjpmj+9Wg0h4Gq1NDYyaiQzQ6lKEBo0nuenE5mkn6hWK5l0ZrKYrxTLGiCVzsQmWQb8XHdzc/eszLpntyKEjsr5SW90fMhLJjmIInAUQXNjslAsGUClVFCLFs5acPDEXk2eYaxVw67mJh2NBbVkuVbOZrNag+PoYnE84SZQRRCFXiYZcvD4r/76m+cfO7r3wJYt+xd3tpTL5RMjffMXzn3btdePDA1N6Whfcsrykb6BVDa9fPmKvftfufU9X2rqap7S3PThT//XI/fdfelbr+Ayjhb6envnD/Ud/vnv/7Bs6cIP33pzUMs/+9iL//zHwzWtgKJqqDky5Jp8oZZxVVVzU2t7MtcwNDKuHK7Wgs/e8uF/PvyPtilNtbGgFkAFqp/+1E0D/UOL5s5bvGLVoaO7+4/n1zzxxAfe/86XXn5l9fIV5aDmIFY57OjoGRo51tLYrXWtMZ2KGGrlkpNIIVY3vPjC73/zr10n+jKpdHOqpVyoKddU0Dz5xF+++b+/2/zca4XSJHmUSTVEUVgqlENtVDI7c2b67RdduX1o7IXH1wPpyUJp6rRpI/0nomo5lXOSvlOrhpnGtDLeSHXix1//7F/++e8vffT9n/j8165697VeuX3OzCUvrnlw0+5XN2/Z0tLdjCG29XSnE5mxcKRworpuzVPf+dGX2qbO/sb3fnXhmSvPv+rUBTPmPXjfmtNOmfOX2x9QieS8OT1tXY0vv7Sjtb2lkC+m/WzXjNbhgfyN1533j3seOrhv0M8mUHkrVs6dnCgvWz2vM52459E1Fy5c0VeYfPGNXWeffWZjQ+rjH7n+6J4jueZmw5FDWIvCKAizjdkwMDqIQhMmvSQQAutIg+8TIAW1yHEUgksKjIFqrYYQFcu1tOeS57DRCpXUmmEYkRxdURgGkQGOANKe7yZA16jv+ODPfnnHotNmqlrykQfvH5msXnrlFetferlQLJ1z3mmnrlq4YGn3D773h2isMhyUKjWd9BJkAnbU7Omr3GQ4ODDMUZBpzPz0//5woO/4Hb/97chY35vOO/PRB54YHxm++NJT5i+e293e/YNv/ePKd54yJZu566HXSqxOP2NxpVZrbWq95oIL773nrubWtqHhvqMjw/OWL+9oTPd0znQTTqiZxb0bDFo5ryVLsGECiACIkVDyPi0FJgaT7PnBcf66Tc615bOJfaj/A7QWZZfF7039Z6VQNwyxvbwMsY1Cx3DMj7D9g4lLaGMPMpn5Q1wPgIrNSK3ZtjgXAYiriv2bJDQaq6azzB2ZNNuaPPawtGxvC1Izsnx+nSxrkVrWUgAAnzQMNAiS1WVZElKNsiXNs3X9iJkb8f+rACKQ0FgAEasAGLR5mEL60ZbUw3Vre7aW6hYiBZZEN6F4GdA2cUgeBcbzAkEZrftG7MUf/4zA3Bgnt2LMywWMuSbWkR1tU4ZWnhqPYzj+pHjQQNY/3t5zqUXqvBPptwyIJ6m4BjEiaETUEBI6YFijdlFFzBgHgDqoNBuKixw530WnITfYdR0DnFBEASlyyHPRwchAIqcyfqpULf3+D79d9+LLXCyWqv68ua3nnnPu5ue37e0/iN/+4R8j0sooVqjYkc4K47oWmBAjQx6aCG3dLXINrr9MxFbOqOX7olhbhtIBMhsAx4AGYAMucI0NIQr/xMhMwYAiFKaUQqiHcUuYl7HWr1Z0guL8SnbZIrIBdBi1bbqBCIg5AiQjzvpoGBShzVeT0tOwifl6dtrDhNJUSd3kIGghR4A2Yh9gwyYUcESICI5hzVCXFgkbBzQwibGmiCRMHLolvZ541ZMt0EXrYtCQVUoLf93UmXmSzmVVxMRoCTCOFKMAJKnGMetJOknFyApClvuPGslhYATF4DCE0vgCObZPRTRAjnIA2WgghAgl7E3WtEFQDhstkm6QJyZIvGyQzMYB1tJvM0a2BcYIwRGOCinHATlEjN14kBAcAgdsCjJKS2wQEIlIKVTGIh3xoNYOERkYlEwBwD41IIUcyfKz/ROSASOmUdJSOTbgzG6GimJFDgvZHRwgAxEyiRBMKWSDDqHmSJEyxtDJeEI7a0Gloiiy83RAByFkJDQOI7moQ85mkp5KDg2PTJ3WNjIw5qVcDnUpjBK+W4uisdERDLixswkwAaTBGGBiw1oZBABWhEiSz4jCkCMTm7EisiizmQ3YRksEJTJFAJsCxugI7UwMLGIUSpK5jTaIIBHkilAbUKwIoyiilEseUbrBBCb6/Fe+c/jQHleblJfQgapq0Kbc2TEFDGdzjeODI9m25rHBCQ5qNdcDjT0zm/PDE6hZc4jK7Zk548juvdLLOTpasGLWlg37DCTIdzzX6JB9102l/IrREASVai0yYTKTZI6CSqUSVHSEhJjKpYJ84LgKDWMik00npk3tmtc75+knn9COn0olg7AGpDAClUgNjY21tUwpFccr5YLv++mEmy/llXHAGCIgN8nMoByFrgmN66pkzl+xauGG51480T/pJhRp4/rptq4GJ+WUj42B75QmJ7qm9r73xnedd+6Sjo4pj973cFPPtNe3b3/rVRd86sNffX3X9rddfOFQKXjPje/5xY9/Xi6Wg1ppcHQim3Y9x016qlCthiEqpTLZdLVQYlNzHX94bDKRdB3HBVJGprau8jDZ2tG9Z+d631PArvIdByCZzBZLQcTkKlS+V45MSy5VK5ZDNqRA+dTe3LOs95TtR/YPnhhINjT0HT1wzTUf2L5zXVArFMdHIzSGNUIYVMuOgw4rHVW9TGKgEv315z8vlip7dm0bPDT02utb0jn/p7/8VlMymWlqLFSj0ZHxSPOSRat+9vPvbH5hQ5XxoitPf9tVH6yVR99Y92rv/MUMzshQ/7d/+b2xPaOlsHjNO656z7vfrkLv97/5+zXve8f9f3rkyMBwIq1T4G/dfai9MZdqSBcr5Qqbju4mjR46qlwoHz28c9/GN971vvePjOcvueyCyy89t5DP//L/flk25rQ3LXjvde8dyk/M6Jk+PjrUlG07dHBvV08XR1iNoqZc4tDxI31H+7u6e9qb244e3d9AaXTKC5Yu3nUs/MA7PvSJz9x4w403HnnjtR1Hjt51/4OVahgpuOaKC++4+8n82ES+UvRTXsrzo8BUi7VCmRs6Gq6+6m0TxfHHH30h66fQ0ZGuemTyhWLSdx1X6TD0k8p1010z2t//wRv/61s/z7L+8Te/8pmvf/W+v/70/vuffvxfa46Onpg+f+mRI8eTTmrWrKWliQPjptDW2Dw6OZH21ac/fsuTj758tP/Ibbd9/aMf/Eo6k1t96qzR8f6pM2bs2XgkcLlci4aHJzp7WovFWjKZ8NBs3bq7sb0poXzfS7Q0tijfGT464jZl/n7H9376g5//9YFXfv/dj7y2/8TuN7Yk0g3z5vZ84KYbxvN5a29AjtaBIqUjgxyR5/uuGwamWqmmMw35iTFANBC4fiqKNGidyWVHRyeiSDseeOSQ7+mgqlyOauC4DhIhESOaiEMOQUOktQNq16H9+/YeXvPcmlMWrbz/sQdzmXStRl/88Df/fMf/7j022N3eWs3XmpobKEnNLY2H9h53PAPIHKl8sdDaNSWtvLnz5q3fuL5rzsybbrzmnrvvnhwzIyfGOmY2TBRLc7u7hkf6Dx6cPP3cxYePDJgqnb5qet/gxOZDJ7RKzZ3VlqGcm3Tf8/ZzdQhvvLH37FNnDU8kJgrDo6NDxUq4ZPkix0vVqhrQCNUZYuo+GFvxC/2BkCSECEAcN20kUzybF6DQYsps7H9FtiUAoopHtMJRj+0ZLGoPdjZuAduTDM6YtcGWuAwGSDFLpKadKYiTqQYmKdQtvUTbGgEUSxkFwCcDbeUvWrgU67zjmCCNgNLYkPA/bNkOsQpW9JbAwCKQZUtvNsSWCl7/OohWrHySKwEx+FZnNwm32tYqqs4WAYECwdbT0tTElGyOrz+m1ghcZZkiEBljLzuuTBgNgWtYi9cfy/MFktAeqVfRsCSJYYyv1/2exISI8T+/hugHmBDtnNyGiMVGnXIbhAgs9kGC8TPWaVAyIxCv+bonL2tAh4G1qxxmY4wW2TYCSiIqGlIuRpEhse8zcZtBxmiQpybe6YBEBl3AUrVai7ijs8nxnaO799JI9fYH7ks0w65dxwqT441OA/lOa2f2Y5+7YWnP4lR12r+ef8gBAELXCjlizhkox4AmsRdhlqkOM2hgJSRnivWDAIY1kattMyfKEKXR+n9ivN5iebKQI5T9bLK5bYAOM7BNdyYgbY262DADkaNBx8wSrs/jjJ2kWP2zMLIMG1JKg0H5M2MQWItWEmOGhu1mAAFspqu00WRkICVIMVOILPwW25hq0CRRfKilKZFu16BFCIjlLQQkxcYokfKzHRMpwVtBqn+wY5w4XJatGSUjWhmHAYPIwlPCOBkBpJjmupAHED2G0C5p1gBkwE4lARy2jEMNdtMyRI4EI5h4ViXyVlKojXEYWQnNDyNjkCGkWKsOAGzEIofrMls0xGDIARNQPMUzRpMkIQskYG+gpbQYowGMsRaWoJSDFDfZbEwE6IBCpYGN6GRi2F3+Y7t6BFnH0vdotmM+BEYwJEiJBGBZyoy0erEXJ6H4flpxg21cwBAoUGC09KIiKyJC1vIGsdFGKUch1SKTcj2NDKiJqQJOmquIWoNTjKLaJO/dvW/DKxvedc1Ff/rNPxbNmdE7axoaNbMl94d7H/VSqcP7D5JO9C6aufrMSwzUEslkzRiHQLFCNhGDloxAGUwxG9YMEMm4EpTd8RDFag1tfrhCNIoUABvJkiBFkr2tEI1gHRAxKjCOgxqMAjSaESnpuxwE1ajS1NLyp7/f8ca6raOjx5F1Q0NDE+cmolqU8lefeVptYrIWBMePD334wx98/LFHjMk1tTW2tSePHzpBZdPQmDixp+/U1dO3bDnU1NZSCSqVoJBrzJRLZSY3rLnbth668M3nHth24ODxoUrgt2bdo8cHVp69anF746uvvU7KdYmCGif9ZMCcUsREnd1dpUJhMJpoa2uZPnfWtK5Z61959tjxQ/v3H0wkk825TLFUTqaUA+mm3PS9R17PNTaXo+JkbcIlz3G8Wq3mgsrmUo5yK7WQMAqqpjmTnqzUVAJTSe/yy8954tHHyqH2fUok3OJotahHfvHVr/7wm785fqjgtWQeeOJXM6ZNbWloqeRrz766cWhoJEo7n/zYh37+w59sfHXfeZedfuT48Ly5PV/51CcwwgDBdVVzo0/sKF9Nnd5dLBR89vYdHUj62JZp2rFroLGBm3LJchCFQRRhLZvOlGuhB8pxvWLxWCqdNAbZsA6C0ETaIT/tu+A5iirV0AEolIvNjelyrVYpVR1QJ07sHTi+L5vI+AlVLRfb2rzHH789k3ITqZyf8pKogMKghjoExzM6iMhtqNY0RZ6nBh0yV19x2aNPvbjwnOWf+OANpYkiIZfL5aGBfj+R9rPO1g2PPvbwK4mM9+Mf/7gpk8lms6W+4de2726fNfOb3/j2Nz7/vrFdY5/8zCeam5pXnTGra8r0fXsPvP9D7zz9/PMuvvwSAACoAJT3Pb9nzrJOcJJ7tu69496nDx86+ua3fGj/oZ0Dx7ecOEy3/Nen777/jn/efW9oItZBczbz5ksu+sr//HpoYvCqi97S0tBaGh/lGo+HI53dXaNjkwqhUMzv2DYSBkwJRysYnBhyE55K8vd/91hT9rlZi+dXvPJLm16cvbQ3Kk9C2r3qqov++teHK0w9PV0Lls9948XNmTCZz5ci37gJr6NnWqZYVIT58YPf+srnyuP51zbvAORMtmlkqD8IIZWDZCJVKldChFKpFh0cfnXjznUP/uqD7/98S3uyvbnryOToGaeesnvfifRQzslmp07t6Eq1Pb/hqaTrOhkvHWUrk8X3f/g9h46M9E3m/3zX/93x+weUSjgOv7xpyx/+53Nf+fHty5bO9hvSr20/SPmJE4OTrqOqFT1t5qxZvXzo4H6/tTHT1XH2ZedsXP/G0ZHxhZ3N9z+65paPf3RkXN92+x2909qPD44dHt/7uf+6cbIynvA9B5VmDYiISW20UhiGplKulLhQKweDfcdH8xOnrFzpJ3INrZ0c1hAxAt63a1elZob7hstRsGDxolzCI8RyLUo4HoDRkXEd1wA7pBBdw6wc3PL65kqxNLW1+W1vfrtvzNSu7ksvueTI/qO/+8ttk5P53hlTnFC1zu24+IrzBo70P//oGifSyazq6ug6emzoG1/6+C/+cHeuu71YLHbPnNuYSvq+eXn98VNWz20qB3Nmz1JRsHHD4euvOeWO42ufWbPhPTe8+/47H/pn/7ELz1h52erT7n30hZGEX0sGb3/nJZOTtRULZz762EtjxR5MUv+eviWrzkLyXln39GmrVyUUBaJ91tZn3TASspKBgAIprdEWfQ6DmB8AA4PI9qzlhs3/kZNfxZJSRAuNI4K2oLw1/UZgFi2nmEIzxkB+HT2N//VJroxGyxRBsCk0dvRsZXvW1wTjMluuEIBR6imhhLKlLYPUaczAaNB6eUjtg3ERH1fzdrIgFBuUT7E5NgLKQj3fRgp7OZdZ8nAgNtlkgNheFk7ym+0oXqhSwuYBRBs9xADIygh/CRH/w+xQqgKL2gMQKgZgRJdQG1ZMhiNFqCPDwAYjm9orpZ6UeRbNBGQgpWxKlAxdjAKKw22s2rQ+H7AicZJHFnOF69I+609u2ySDQmKW785aeBzW71HZPDPlqtAYF9lTJqgYTxGTVoBBqC1/CyChPGMMuGg0+44TaR1p9lwHjDERgwEiZGRSymE02qFaQL5X1ZVp07v27Tj8ta/dFoyMF2qTk6MTZ5+7erivkjagMg0tLY2TpdoVV725Z9aqTa9tf+yJH0dBHr952x8NsEKX0YARVrO4CxlABuPJYuCY+CYCZEQT09JYpOoEjuGAQTELcasG4LABhojBRwqYSbzgETSAA+gYrjE6bHusCKSnAQZ00bo3xoIOe+9FhYEACBgJpxvqLomy7MSFxYrTDRjXNrJSBMfieumPY3kKAhAgsQlRCfNEIUTi4GhEe2oMWIIQCIVMSEb2G7FsIMxMsYgE4l8BIPgtooEAbdOBhsOTE0MiEm9TBGOzX43tKFmBzEAAUZp7lqLasLBfZBsBQgQDIdhAcqMkShYUQ4ToiFaYAWy+HSqS5AnB4EEhS8wCQ7zXAIKkfCMAKbR8RiC2Q0M7mRHSpPCDSLpBAATHgCKuAMn0RCJZZCkahrTCmswvWFQepJRlqjtiaSrbm4klFWAHMGCMlq2Z0GEOQRGDUehKI6ZQGWPE/8rOq1ikvIAARMoYIMkPIAfYSJ4FEIjPqAwLGIEBFbM2TA4zKBTfYgZkU2XOJCiqGWPY9x1lGMkzrjFOiotjj9392JSpue6eKTWq/eOBh2vD1ahaaGzq6GjNTJsxvVAu6XKpva3x8NETK1YtW/vM2lSyIen5Hbn2y254GzuNNV3DyESo7MQX454lnt8KhMIiOQDh64nhKxMBkCgb7FgYAeSnkImNAcVgHPG0EG9pMuS4Dhj0PGxIugQ8NjRcgMmf/PbXh/f1UzUAw2CihOe3tbYvXTLvvLPOuvvOOycmqpTkKy67aMfBPfu2n1Cad+w62tDW4CgDjrtoybzn1740b96sYjVyCSHSgdaA5Puu73nVUoCAHAWz5844Y/Uy33efeu65z3z6I6+8uuEX//f3M89eOTxWKJdLQRi4DhFzuVKZOWMJqkrfwJFp03qiCBtSuddef7nBz1SBXFIGONPsv+mUi19c92ylUtHocFBu6Tp9cHBDuVpOuJmWxobjB482tDYRQUOuIV+YTKVSnd1zsmndPmVKOtvmeeHDDzzQnM5u3noonaDmKbmVq1addfqp17/9ihtv+OT8lUuPH+s/46z5WSf797vuO9Y/cNH5p955xxMB1WocNLe2K4ODg/me9tax4UGVTHiOoxIuoGnMNna0NTdkkoeODXa1dxzrPzp3ZufimfN7e9sffO6lV9fuYohIeUgQGe0n045HrutFNRoePZJJ+AY44XiGQRsMGbKer5KZfKlETOQ7XtLxnMTI0LAhTvkUBFVER/mqd+HZuze/mgBV1UEy7Stw0Jh0xvcTfrWsK+VJ16NSsUyMyUyGdFTF2ve/+22MPJXllfNXJLwqKH9yaNRwyKS6ezpG+/tv++nPNm3df/89t3d19e7Z+UaRkzs2vfyNb/xwxpSu13e/nsrkbrr11rdcfqUqhfMWzE2qCP3s7s0b2md1PfivNVu2HmloSpUr+VJQcXwuFEp7dxzxwHOb/Il8qSXlNyTSe/v6v/rlL+/bs+trX//ypu2bokKtkB+Z3j3zF7/8y86Dr1955duuvOzyoBRpE2gOE0l0TKJUrbQ0NtVMjQk99g4eOYAYrXnsmX8+vLaM6Y6W1p4Z2QOb96az+KH33fS3fz7SnEocHR6f3tkJTmXZmQt6pp/60tq1+cKE7/snjg9NjObLupRiPzJOoqljxvz0ZWdf/Nc7HhkvjE1taTl85JhmnUqpbLpheHSkrSE5WTMJ9JaeuXT56uWHdw+kMsF5p555dGQ4pbzCyOT9Dz+UaUiXK1FDburmPRu//Lkv3n3nnxoyGT+RGMwXLz/3nKvfcv4/Hnr6kTXPnnPqvA2vbs/4XmdP9859J9I59dlbbjlwOP/Gju179+zMpJqzTelUMnV4/14vk337Zdc0dWbvuONvp61adP99T3e2ZT/w4RuL+cnpU5q6W3K981Y+9+Sa3qWzCsXxmd2zIzbMASlFyom0trUfoA6jkMNEIhlxdOTQwZ5ps4JiJdfUZMKgMJlHzy8XJjQ6YRQcO3RYnIfnzlnoJzyHQINxwTPGgEO1WgWV5zhkdBhUQ8/h8ZHK5GRh45Zdm7bvyXn+uk0bmtNZlUp4njrrrFPbpk77w8//NDpRTCh60znzUglYPGP2w09tXLRk7rZdh/PV8qxZp6QpVdEnimF5+MTYnm0Hr73hPdOXT+ntbHnozw8MDIzMn9u149Dh/uGKG0RVghCxZ8q0RfMWvL5x84UXnnbh5cs3bNjqBvqVTUdXLGh956UXD1YGnn1y/eHjA4VCwUB62pwpl7758gAADRtNFjmk2GrcWHkhxwQgQiXslJjvYwtssMSeWGhWl7faSp0tSZ3tj3IdpIE4mRNAiC5sQXdxxCeOC1ypeG1kGMbk+dgTRX5NjE/HXBYhqbPBOlNFmLJ8sgwR4atl86A1UrQTjviH2LL0xUU8NhKM+a+WJ29hcaunELtxkT7b+YGcThaaNGzQJsfbOlnFUxS20wEZodjPtF0QYUwxQDGrlQuNqRaxJMIwAZPWkY5qRIocjP8oRhOhjhFj/UHa+ylQKbLoTBHquU1gCRdyhyz7XYpaxQK0xTxqEOv9ODTVgEh42UKdwCDcMfsOsEKYqJhsQzIYGXVMmPTcrnldE4WoWowwrFWDIiqfWTsOOgSGiAyGHBI6xrDWBjSAg8RcKpeT6aRPfhBGbsIxpEsj+QNDB9ev2bhj56aESgyNjoJxIsKGBiftNqZSvkOYaUycvmTVDdecfWh49Dvf/OPsFfNPvXAZfvOHf4yQlSF0xNgREFgp8RbUEhwtwDIiaUYHmMmxzQAI3syIQEzaOrOLoX8E1tIHGFyiUDOIxJQBFDhMDKy1vc2OvFwGCFnH5vfSNhowYo4IRvQfhq1jEiIzEQojyC4FzQySSyUhqaAMi0DAnAw0iCdrMQtcCnQFGMYSZgWgCZW2rwDLfSAALR0DMgFFBhwiMV60Gn8bP0yGtVX9MjAYNESIgFqzIURABSwB4/JFbHY5WhzAkEJgYtZEDrNRwLbPYRYDezElAEQ2GmU+JW2FLHpUll5mh3Bk5w1oqXNEihEdRG1JTw4jkTSr8Sq2pD2jCREVkY38tjkpsoLJAKDY54hZVQRswIh3PipEA4bt1sNk2XQG0SFwGWt2T7CNsWuYSbmGNYFCJCI8OWcAsDLoeFMjdBRpHXMM2W481pRWEoLrFE4hOzlkp40IBthFiSBDw+jYGZ8cAwKPsGJgg5qsK5YJK1E27UMYJdKJ0b6hbEsT+yoBCIFWCXAVrX/59Xv/fkc2nV22ePqiObMPHRzYfvBgqaINce+MGU0tbeee96Z9m9aVy7U9Bw5UChPNnW3E7DqJaV1tQ+OF2XOX9syfHzFoJkKSgQZYGW+dDSWcRVCx2ZTIYpQcIZYtJz7NVmIhjEbxhkIAVMqwdhRGAbsAXibZv3vP/uP7Nm/aeGigb+ToUCqdack1ZlyvGNaOHTzY3tLc1Pr/uHrrODmre3/88znn8fGZnfXd7MY9IQka3AuU0tICVep663Zrt+VWb91uXaDcegstxd2CBUKIZyPrvjOzo4+ec35/nPMs/f74o4W8sjvy2Ofz1mKtFbS8MGBVgxWoo3V0ZUZOjGcTZhgSdIxt6webXvPY4SkOgWE7XTmaKSYP7p8jBhFICUEKFEEEAdcJChaZTrJR9TnlO7atv/iVF/zu5j/NjM8ggY7ujsB1F+ernDLNsIIgoJoetFrVWqVYLIQRl1XJVNcEA8umlqb7EctlzHVrLhJ+89GnHyBJ06+1Qs40zq10NmFbvlc+7+wz777/GUPXWMgcx9Ydu15a5D4nGt9+3pml6fnD+486KavVWnzPB9/x6CPPfemz//HrH/8ebbjgiqtXrez4xHv/c3hszI1gsRluWLv8Fa+48Fff/10yp6NtFgoF1mxZqUS1UqrUWls2nzI/PdHTmZubW5idKds5R0PbpOiGERLe1jUwPzWSK6a3bd304L1PbDnnjJkTw8MnJ0IuiOEAa6Fm9hUHju57utjV7TNfQypAsCAUiLVW2NFZoIB1zw0DoZkkaSc8343CkDGGHJBQglrXQH9lbqFcqRuWhoQmDDMKQ8PQEzmDeDAzM28kDKJpukY0SkLfpwyLy/Lvft+7XnnVeUOHR5JWAjialkBKMrnsroef7Gzr/uh/f+2he/9Yna2h4fzqf/9n3YZTfvPbW4YOnAijWjqRevbE8b/c/KvzL7tsbnS2vS9HKeRT5Lff/snDh4ZykJ+suosLs+WFWjKjFXuKIyerm3Ys3793EiHgPLKSluWYvbnCxMRoEJCVK7Of+dx/+oHW1uHU5xpJLf/7f/z6gcef6OkZ/OR736o5ObS4TTVkeml2eqrR7OnocRwiQKxe2/+m696/65E9TjphOHoin3Mca3b45GKdv+eDn7j3nr9/5PM3blm5/B2v+eRcq/r299yYass+fteTA33Ft73+1R/+3HemxsfqgQcAlmYPLO95zw0XCcz86tb7R2Ymi+3psYkpjPiGTat1MAIuAt4cGR2jjHUU2276/Cf3jxxbOTB41ppOj1hPPvbcL353e70xF1Fn05rtW9csW/Sr/ev6Dzx78tJzz5qdGlmzbc2Pf/X7ucm5YiHzzrfc8L2f/sZzG81Gc67cuvNn3/z9A/96+vkTF5x3BvHJYy8dKs/V0qksA1JfLCMJzz77nKOH9rW3t6/duOzB+57uKBZXbt3kI5s4MVWZOd6WdkpjU1vP3XTDNdf19g00GjUuEERk2DYAMCYQEaKwvLiYSDqmYVGNBAHXTQpcgIiAGG5rMWIkYFG9Wk0kUl4YiSAUFArZNt3SG5VqKpsWIUMkfhgKEEiJTnWgEXCsVatRSC6+8vobr73y6qsuHFi34n9//PtjQyc7urqapVqpWert6ksWUseOj41PzyU1sXnr2sp03dT4yPTk2afuODRybHqm1Tew+YnHHv/MF97+sx/91ckadlvPB97x5YGu6h9u/WVb1j50aK6rf9Vd992bSGiVaoObCcswbnzTjft3PTozOuzk8PoLz4zSuZHKbL1Gnnzwpde+5pSjx2dai9XB/r7DI8cxCN/yrg/ki2arxTkajDGxNNMKIBAr/qUUXcksCQBn0jlG1DC/hJQvCcDVAwridgEFBpNYNS6VPrFPQM0bqo1RYfoSLgfZq6Seqwo5xzhZROVGSIhQRdFIsfqS+jwGZOOVBdVroHqACiHXlVjKAwrg5ks7wNLEjLE5F1RhFcaSJfW8Rnz5z9Sag7G6SHIfQuWxq4AjNeLzJbGD+kmFxGM8TUgwUU7LSJDFYh2lWFJfhYptlY96zQtc5CwSoGmaRHVjYiI2MGB8KAgKLoF4osIJ5ZcAivmIXwJB5WVLaRZQaReW4oYlYZFcAFB9vUv//jL5INTOxbjaMVADx0g2W7Uf/eg3J44ctFvBui0r+wfbW244M1t+/Q2vznS2I9dC7mm6HgQBMkKIITSho9lwG+0dXZX5hcrcwsqNyxmiYZKpoeGf/fo35emFVk0wtHJpa641bxCr3GzY1My1JTTdCpteOq1fe/VFG7etfPLevZGBExNjCaCaHh0cHsEvfuO3khhSlV5yjCXyHavQfYFAOCoPIhKh1E6ECy5QWj0oEWLJ4iKHYdUmBQKAIjIAjXPOhJAjuxzRZd8uAAihAUQc1ckhgFDQOIbS/wJIle5LGWWEPFRxl4JAQAJaBGGst0dVckEoX7LoCAGoESUE4og0LpCQvnTCkKNaCOTpxePCNsEF1TVkggmU5a8yxocALp2I8t1QRdqpIjMEYKq4DCWxh/HdhoCIuJJwCAKaQAaCMwAK0mSCOnKBVAiuFHtClmuDQIKCCdC44ICMAABoBAQgE6qsVxlOlKBLBuGiTN1SIhhEQpEDmgBcCDPioSa7lSUSDwIJYTwCpESAQFkNyCkhLJIhRwgMEDXBfQ4EMQRhIXAmQkGACE44B8JBGIghR/KyW0kw2VJHUGPyxJO+X6JZRONAhaxikbFN8i6i8pplTxpIP1ccskO44uQUwCLi65BgLGNSFnLUiKzOBXWOoZBrH5cMB1ctgojA5DElApHJsGEWhLl05ol7//XsrsdP27r5+UP7rrjqnM1nXGy4/siJkX1DB4b2n6A2mZmtl1uzF5x/rltrHnnp0LYd6wKauOqyS7/7Pz9OpJPtbe1Bs3npFefdfddD+YR403Wv/9YPf3zZKy4FKgLgR/cev/K6a7mZ5lKYJAWpiqUGBLljA0UIQUZQiRiNARCoqT5gFBI7EIIqSxMB5ZAhggsghm1qzWojTYPjoycefXbP7b//TS6Xsx3H0nQC6BFt84YNl5512uETwyvX9O5+7tDJ0YnKbIUmzEuvOL23szg5Ml1oz+x59vh8ZSGbLfb3dfR0OL/948MmJa7b0izHd+tbN62bmyuVGw0dTaoRy0w0A9+yTQjBr7YK3dnTzlw3M1OdGZ688BWn3vLbO4UADhELwmwuXa83QsZBCKQEOeMhI5QCpZZm8SgkGpi2FQU+MUhP2waN+A23YejOQmmi7tW6e9YEQWn42Ei+vSfwPEbCXKawY+vKF/ccqlZqyXRby6sz4MBI6Ppmxu7oyM2MzTXrLd3W+ga7BwZ6gfOH7ngAqX7J665Y0Vb4yc9+ZRjZIGxV3brjZDQ0coVUs9Vo7+9za57fbCVSadu2R4dP6FSjlOiaEbbcjt6CV642Am7bZsSYDpQRCPxm6HHd0tLZZNDwm3718kteMzm+cHRkqOl6gJ5hOINd3U/terq7r/uKC89+dNeLiwvNuuu1dRauvurUR559qTVTjwS30kknYVbKi8AACBCCTd/LptOZpHVybDKbzAZI3JaXSFpA9LDlccZTGSNqtbr7OwbWrNrz9O5mMwrCQAfkUehYmu/Xr33z62vN4DP/8T6miVItOvL849/72e/W7Nj0h5//aqp80mQ2jxrzk4u//7+bX3rxUMNzvZrfwGYhl+/q6zll/erTzjm3t71/cbHccluvvfoddb/SvaJz9YoeVhJ1n3MODES9GRlZa82mVUf2HjMMMHQaRTC4atnxA4cyjhM1GmdctPW97/9A5NKWV/aDqK93xczs/Jf/67NjCwtJx/3sp76zdfMaLpoGpN71sU+ee9bp177mVeXZ+QjhxOTY2vyyt7zrY0L3Owf6T0we37Jl65EXJ0SrZmfwbW9963e/9ov121fwsrv5zLUhJsyUPT89B6Cfsrlj9/MnX3huv+e5gBQYmKadLSTthF2tNoGI0A/CgIsoZESsXbkq5OHRoeGEToAIXeB5F593zVte/cJzh7sy1gU717940JsYOf7Hv/ylIRoDvcuPHz/5ofe9rYl1I8q4zD+w56X1m1Y9+cQzLhPdPV1d2cL2Uzb/47a/Dw2NNgSvzrfu/tt/3XLb08cOTv78+1+/6fs/Hx6frjfqXsO10OTgF9tz9bqLEHb2FAaW9T963y4KMFOt9/YUDZtGXrist6BzsX51v6tZH3jHDcJKRpyFbkAIQYo8jFqe76QcAYwi0QVBoAELDMNuea6Ous9cgQQEYcCjwOchAEGkuteqRREW23Ou6wpBKSLnnGooOGoa5cB4FAWRW282OfJHHny62NV+4Y5Te7u6fnPr3w4eO/76a145X5/e/eSRx59//urXvurOv9xBMOroKiYtc3624oXewIoNqwdXVqru4bGTJ04c7OjIHToy+vAdN330s7+bGFmMiPvmN7w1ajUa0cJDT+777hf/+60f+HA+oVfdsKOrd9sZO5+767aEbTR9v6uvmM5ldMMyneSxobGtpwwKz7/ssrNf2HX83kceyGfTQ4eOvesD79q0fVuj4Qkuwji0B5XyXUm/iWK05RAhxe5STS6tq/GmwEH6RgVwBIltKRm/ktqAUrxIY6GE4wUIRAoqjwj+DeCRozYylUwRAWjqJ+IxXspc4eVMHSlPkloENbfLD6JyKdUKEC8qEjKXvw64DL1Y8tMq4BsA5ENkSeGPqByeSszOZZbEy8Zi9b5wScOslBdSxANKQx7DkPEUFtsvUY3KoBB1jMFCYET+f5zWodJQVc2B5EOIAMYFEZwFUaC6l4gOwAhoAKBSMeH/ff8gAXqJ3APEJgwUXLWFAoBK0cQljTOVEynG6a3qpwBRBo9z1Qsq1zHp9I2/DY5SQxMCsSkLfvrdrw5Nzq5a1kt12r98+cHndlPi9Swb3H34ODbF1PT8yoGBb33vW0J3wyAEj+mWBRp4jdBOOV/95tctkt779O6Ulch0ZSanZ2ykmkGrjRA1kkomMQrLzWZ7JlP2hPBDy6Kgi1NPPWXnztX97W1XXnz+5z75zbe843Unxptf/eIP3/TeS2ZqLfzSN3/LZXS4TLYRQhABSCgSAkCpBA4FqNpcSfQgCk5QQxCyMUsQpIrYkqsuyHjyJbJGABBBAAkXTGrlMeZ0QCpwBBeSd1vaAJAgQcaBECEAiUollV90vLfK46AmQoKERJwRgkIIihpBUIIPWdcHRB63JWs4Qco4ezkXRl3tiKokgiytz1yARglBDDlQ5ErfR4BSTXCpwV6CxlEILo00MkkKgDGQAkCudOtqSVLaboB/azkG5VZBIBoFRE0IhiLOg1WkBaDgiBRUBa9U+yMsUVFCAu2Uy5wyIIpHk73gBNRSJ+U1iEJQpBhFXNOQc5XJTyhFhDASlKAsCUEEqhHgRBaPQ4x4MBDIBYltp4yzOKyASX5NNokh0riCjQMiRaqyioUQCASJqWmIugAGHBgqhwmqnH2CSLj84tSdVBFxkotVqUlLukhAVHdlwgAAOAVKlhoZQMjMXrnhy/0DFZQDBACFJmSoFWURZ+Gim81mvvPNm4LFRQy8AHBlf2dHW+GSy6+bmHnp8L4TVCR2PfPctgs20ciKwsbytetOHhvet/fwsv6OYmf7mhWdDz/4XNUPz7/krMWZxYjxVHu2VfO4W11cqK1cuQI13w+j9mw6WezLtLUTPcmAM2KI+LaI0tuOVADKxYszEZNKLN5444uNAHJKCTLOgBIBQgPCBacEUdeaC/UVa4q7n3jmE5/+fGc6lc23NVotxgObGLOt1lnbt2lOqpDLPHzPIx29RdumM9MVSohjJX3GugfaZycqftMNKDNAExw0jWgWMSip1puIwjB15rUgIroNFAwugohjd3e7G3DGxIr+zl1Pvlhob+te1rtqoLuYSz3+xO7jQ6NgkDBkuiY03ayWq07S8QOfakQAWKgjQcZ109bXr1vFAA4c2A8RXz6weWz6sKFR120KQBaFrs80QBaFdjpRKLTX6yWq05SZveS8S37/1z+nUqadSgStsDQ/i5pONN3Rk013UXCSzWfdRq3phafvOOOJh+9KJBzQ9UarlSs44yPTF11y1rKu7scefO74xNSylf2pdDKIwmW9g4zSkwcOhUFIDM2ttwRgJER3Zzp0RQAiCJrptN2qexoPmm5U6F3ptRqLc1OarmUyTqXc5MwHKhqt5oY16+fnm34InAbEsrtyxtvefO3klHvG2SsTYW5s9ICeIw89cmDN2uUHDp14ftcRARFD0DXUdeq5QSJr55LJehSCiEqzrkZJGEWUoc/ADT3LNgkQTaeIyDzPyRgZO9Pd1/HMUy/qlmElMHJDZEwzdM71Vsv7zOc/zgxeTGZee90bvvO5T0xUK1/+5gdb87xRrVVbcOTg4Qcff/jIi4fLrdppm3ZUG7WO9vbrrrz4E1/4ajVCCl6yaJenopm5ecsyknnzksvOO2vruQGk/vaH3wAL6pH46Te/ct1bP97flS61GqamUd1ozyfHjo+mbH1qPrj2+vM++8XPjA2N6bZgFNpyeeF78yOV//nht5oRy1Dxle99ubun+6l793zr/34RulFXR+KM7efmCuZ117z9yzd9duTEzPzCQqm0eNbpZ2zefta9t/9xfmG+7NX9IHzv+973xa9+u7eYn2uWPvOZj3dZ2dGxOUikX3/FWW/44E3uYqnRqpmGiQw9N+BcMB36e7qDRj3tOK0wmi8tRCBQM3LpzHyllrRE2rIqzWbNpTd97aMnDx/asGKjLxa3b9nxja/9JIy8+UrVC5qG7rSac3/67X27n3vaTnl33f/g2NRc3knncqmR48OcRIWuVFu2fWj/idJic9ZlyQSetnX97PjcaedsOveUi/505+0+C0dH5x2wqqFvmVZ/V6rWqPmcU6IJDi0/KJXq9VqTaJiz9P7ezrxNPvG5//7FzX9+65vPT6ctIQzGiO/VvLqnmXp7RzpgkSY0IDqLQt0kdiIRuAHVNcExAu77IQhoeUEQMkWGstCyEoCga4Ij0TiEUQhCEyiars+Yp5mGAdRnoaZDs9rUNHrkxGh7ob2t6Kzs6Hvu0OGkadipxJc+9V2S1kwr0Z7vOnbspWQyOT4xsW7FmrGxmVxHUghSSKVPjI7VvXqhp7vPSTd0d8Pqjfc99HSLCa9a1YFtPnXLG66/5ujeE4ODp37oA59I99pCaMxC3gjXr1lXLk22vAZh7KILzgkj9tjDj/f0dw8sH3j64cdOOWXj1PCoT4mTT11y+QXL+7ooS7g8ChGBCQFUVeoA6ABMxmOoyEFAITNIVOOQqukV8YMPl4A2CdDEqgURI9/xLVn+D0FJohOZuw9CtuLIeGiIDQASf1dZJKiqVNUoKYWskoOXj7h4skcQDISsM5W5JksZgohqoxHxfKLkRnJgjj+JkNg5iLhCC2O5kHp3yuEg1IojBfJqYwE1wSvhrdxG1DoRz/uS/8eX57d4Qga16SwtDPJLJdLmilQIQf5t11CALQgQyDkjBGVDK4tA1gepuGzFUah5QKq247lcJkmKGIvlcStALOYSLy9v6sPJsB6i+AJpLOaxrzDuBpKlztJSSgQTgAwIcqAoOKdR2NJe2r3rrtv+rhs8k28rtnWuWnHG/f+8peo2VqwYmCtXR0dnBlZ15BPZVavXpW3S3t4+M10NuN/Z33PbX//U0941Oj5Wa3icEa4xWzd5GEUBFRp2tPdW6+VStRp5HkNi6AbnxAvCpG28/obLrzj/9EnPferxFwhz3UjMT0ycd+b5jzx476XXXjJTruEXv3mzOuUk8UEAY96KghCgITAuBEGJvxLB47ZSpJRwKX7hIiJKzU8Fl9OhxLqJCtKJzRDyGBNEjjKhBpToBKXBRpOOcsE5EnlKEKGuUA1R6tHk1SW3QAoCESPp6aSIgJrgLHbAokztVGoZ+ZfUogwveyhRIGgCIwn5YqwcU7SWFN9Lm0PMJXFg8SlLYmW2vCsQlYMPHEDjIqQqs5RK0EE5eoCDwKV0Kw5cmgw0te4KgBBRB8GQaBJwiK8ctfoiSISXE9UjIdTCj+pmIgkqgQSBIDAhBIlleFKapqzoqgtdovCUo1AGD4S4SAFYxAmq7F8Rcy9EXeWEAIk4B2BCqoiQUKJISVDcZsQFF6q7DbiiCwXIWGkkAoBzRgjRkMiAek6QoAEgqCZdFjJMjTLlgpCLfbwxKRmiVEERjHlWLpCqmFW5EAKATF0SgIKijhCpwytrImTzrUCupJKy+xxSWubpx+6454nHB9J6td6cb5QcjaLvp/M5J5kpFDI0pAuLC6++eOc9u57Zuu6MP/3+nx64WzZvPXrwwBlnbak2y13Z3OFj8+efv81Fsu/5E6+4+ryR4YXHH3j81FOXI2eTk9UVPdk1a1dOTE3tGxo7/YLL0/mk7bRphg6AgnIOSAhhjOuAcd0H0QSwJdeRiOQBBAGcC6TKGwNIgDB5GVl20vOi/g6jVZv9zLe+e2LvMUsnnssMygvZdiOTuP5Vr/rAa15/3YfefezEsN9yMx2d+UJu5Yr2e+96IZXWO7r6pmfG08mU5wah8LnQAQXwKPRDJDrnXCOUCOEHgaZpOgrD1IEiIOcMupd1aYgJM+GLaPWKrYNrO7hf+cX//inwWCptLZQWgaPp6F09HZTjxPikYdmE6n4zCCIGFM8465SJkeGkY7lN/81vuOY7P/qlx4K2bGe5NsdD7jOWSWea9ZplO77bclJZLvjatVuSdtuxoy8IxkMRem4zCDhFTokRhl7IGKEUhaCa1vK9XC6XzbR57sLczLRlWG4oQAhLt+qVaiKZaNXcFjAjpe/YfurQ0HHRClEnEQjh+ZHP9KSTSBlhq2XZNJ3OCIaBz4RAzqHptUydcxH4InHK1iteePRPUchSabu72Lb/4PFs1vQDHwzdQnOmXulob1+xpn/dqjXL+vtvfOs1f7n5n/93+z8mhyd2XnrmpWef/u2v31JoN7/x3c+87/3frsyUEinTbbUoJY5jNEK/r7t3cnY+ckOfQVtXPmFqk9N1xyBuwCMRQCSoRcLAowINk0ZBsHHbpmNDw9VyQ7eJaeh+vaUZqBtWM2jUa8wlYsv6Za+68Nxf/OH2V1994Xvf8c6hJ1+YqteeemoXUjozPL3j9E0XXXJZs7xw6dVXaalMY/7E/heO7Tt88oc/+vl0vdRqVvN2UjPpeRecPziwfHp8bHZ6LuWkDo+MpjPZV77ijDvverbV8KljRCzUNZKzrHKtqTv0f/77W48/cUfEYOdZZzWCesJJmnaqoy1lkMT+fYef2/9kWi+0IHjFRed87DNfOXhsvI20vvbNX3zgfR/p7DR4vtDRkRt94QQlaCa1asP/wBte9aNf32oD1bNmYdnAzlM2FHr7vvHZ/+0ZTFQrrY3bNr3vXe9pNioJIo5Pu3fcddfc1Jzr14ATzaDgc2FQU7OCINANsqy/Z3Z+sdFotljQlk2XS55jieUDA8PjE7Vq+T8//qGJ0lxCo119vR3tWQban269Y9vpO/785z/YmpEv5lauWhF4rVq1PjW7kM62VSqlrra2Rr2uaYZh8M5lp7pzJ0ICvYN9XqN6x233dne1J9uyl+68rj5f2rBjxb79+yOtefb5V3z/G99cmJvNtxU6OtoOHT5makbFD5xEOm0myrWySYAY3uoVy48cGakS4+2vvvT0HWt7lw00S+X5+eaXvn1zT1fmtG3biml60ZWnR27keSQUkQx+aLZa2UzWsg0moLRQMQwTgSIlfuRpaNqOuVippRyHI201yoZpC86BYLPVHD0x6nr+iv7+ZCbHCdN0gj5wEEdHRno7ezvyupVJmJaz+5m9v/35XyZmpvPt+Uwqk045PMIQkIdsenI6355B2y7PVEwd9ZRZrVRyht7d1xdaxdUDm//2t18tVCqu761cvmznqeve9Lpr7nliz5otZ33ms5/EBm9xN5ctNsLawlx1TXfn1Fg51WYIpl/z2ouefvKpwe6Otv7+557d4yBbsWL9hedsb3puJlfkxACDBVwDFspxhEnZNqGSZWYAaqBXJQEQY+4gBMQ9WdKEJsn5GOVU46OabNXjMiZqUbYzggLJpRoZlAZH3slVAr2k7JWbVD1HlapCTrVyzlyyIshXQySqFkCZ6+KZThYeIQqFTCuiQyiGIKbMJR4IqolMTeZqThfx5P9vkmL1xpb+iTVHig2QUx68vNpIoh6o+gP1WFZaeQFC6gDi6V0AxnHtajNSX4hQGZycCyTIuJDQMUiEmhDFh0gPBf//fz+yNU0oNPXfZFJK9SNeHv7kO1bxi0JQJRmKFwBCATnnQCTVAUhki5xAjgylYVQgR5nWDYJgKpt7/XXX5DHhgd+eb6/NTVNKDU2jBhkY7HvXGy/nVtt7P/qZbEI3HLta9wd6e46fnA1dX1CRTFgiFC3B0km7vaNj9PhQGIJhWMxD06Fzc5ViJhMS4IHwuLA0yLcVF0O/K5veevrKtJbZvn6lW3J/csv/1VsNLaFRw7R1rlGnttjAL3zzt/GsBohESq649KISQoTBIVBzMgJXPVwEgSGh8YIZk08IAjSESMTiNLmiySlcgC5ACMEEqOZjqaRDlCWmIOJeBgZAERA0EAyRchBCxCFcaAgRyfUD5XkpkAGjCAKoulRlQCfKZUaTsDGi1D8ARcIAVdcfIVwwEEhRo8hDAGlYkRA9QargeVkTx5S5RVp245IJTQVgSRu8apVA5Fw1yAlZl6YTFJFgMsJROmUBiOAcgUusmnNpVZdifwbKykoBGMbzHS61VsQBWSBAduPFR0BKRpgybsi2ZsW0UQFM8mFc9cmhlCdyzpVshlBCNABpltbkbxaCcynzRySCCohi/SBHQggKJlSAqDTuAucCCVXbmqxSZwQIRxQsQhSCanG2rgAOQAkics51jQCh8qVAM3SkOiVChrACE6DH0kVl4VebDrClXjCKyJEJQSTVKgCpYkWFJGPiGxCTKnpAoJJZUk5iiIlJBkAIjWjEw9D43S/+hxMXQq3lB6dtPbcxPf/Ik3fm2tqWr15WKjU/9L4P/d/vvn/6poGB1QNDI80jB064KArp4sxk69jo/uU93RdfdurTj7/06muuKTdmDr848ugzezafsSmVKBzZt3fdivaOvs6ubHbLtrO//rWvRFTv6+/jEF78mrc2KiWStDnXNIJcCN2ggjEAoVFNxUe9DEYpxEemJjCIdAQukOiUAmg6jUJiJcK9Tz/3k5/9nxHW24r5RhmSaaM0N5/IdxS6+171ytN//MvfzUxUuou2Qe1iT36gu/uhJ55PJtpY0PQ9106kdZMGfuh7oZ1wCCVeKxDIOIs0jRqOaZp60GpqREPCLc0wLVpIp/yALdZb1UaDhYyFIdUMxlixI7swX0naST+EKPAEE8QgjmkEjDEOBqV+2MqmUn2DPatWD6xe1XfPfQ+GLQg8r1Jr+VEk/MhjoWOlvMhrNVzTshC5Th0AmyClhtaqzQEKw9AjN2iw0ErYURTqGqWIBjXnF+aEQNOywtA3TAMJpQBB4LEwAg001DAQmVRibm4ONaeQT67buv7iV+5sb2974J9P3H33AwQ0P4o0ioEbCUKthENIGLgtneD7bvzAnn2HDhzd7XkiDD0mRKPVOueC60ZHDrvuwkBXW7ZgvOcdbx8dnvzTH/9VKc+H3G25HvNAM2kyldiyfcPp23es2rjm0YfufejOp12/cdVrLi32tj/2wDN7nj5GU9pb3/n6g08fbgatM3esL+TafvrT35cXK+m2nJOkpfJisSN9wxuvDmphPpH7x71PvvDSUDJhBVGkawSAhVEY+p5pmchxw7b+px7bl0k4yfb82PG5bEZ3fQaC24ZumbrrR4u18qrBZRvWDrzu1ZfNt9zf3fKvy6+6MGq4xUTaM6Kd207bf+zk9i2ntbXZ4zMT1Yb3w6987cV9Qz4jnEZp064s+B297QP93YW2lEAxX264jWZWS/ia4Gbktex6oxTyiEbcMG0eNZcvWxZU2UI401MsfPhjHzFt8NygLZmqRn4m0zY3Mzs9NoGEhK36zovPe9c7PjQ8UlrTUfjNr257+yff8MAjh/s7kvN11r2iv1WpmRjxgHT2F+fmZrWI8ahhonXplTsvvuLs46N103O/+I2f2m1p0ai/7wNvCjyiZ1J5p/DAE7uai7WJk8OtINCtBAsaiMIyHBCUE9bR0SZCaLbCsUrF0EFnWkSauVzGCMRipc7T2pc++bHp8dGTo+N7DxzNZDJf+MzHbrv97omRKc5ZJts3NntgsVQSqFHTqM4t2ulUImkvG+jbvevAa173rn/+9ceOTa1Eqtb0N29e7XE6vO+IXUylE+lsMtk+kLjmVdeH0Fjd0ffN7/zcctJ+lHz6gTuddDLiwEwKGjLf535IKXT0du/de/KCK87e0NOdwuamNdt9Xb/977evGFz9jwcftVNGEgXa+VRKD13/w+9+bTKbolSjhBKCrs98388kM63Aa7qe4zgECQDYSef48eONamvrtq1z8wvZdNIPfMsweRQxwWu12oljx7o6OtPZvK7pYKBJKOFwYnLC88Tc7MiZp+9YtXzw6Sd3f+ubP9t+zmknjw2dHJ4597Qto1NTkS9ablBym+efc35Yd5ulUrXqLxJ3sNhz9MQxrkc7L97Z0bmhNYe/++P3TQMhjBCDG157xSc+/fF//ePen936p8aiG6LWUVyxWB4r1Rr5dKqno70aNSql1usu2/nCngOLnL/7HW/Z9djjQ/uHQIBler19XVqucHL/3i986btzCycFdcKIEyE44SiACYIoKFKOag5ValmFq3GORAXTA5eIMqKsahUYS+4h1n4vqSFQYfwirppR+PVS4n089oPC+wUI5ARoPN/LKi2JqQsV4a1mD+RyeVFwphyfCBMqawVi+cKSOkhOwAwEERxQR+Qq7T/+LVJpH8OjwIHFOggVRoFSt40SgVWDo9JY41KnESjpKSgENSanBQAVL79nRV5wibHKABZAGXiHSvdPhQRbgZB4oEckPC4xRs64FKqrGEERw9fakutarR/xmqL2J6n1ALUUxeOTWldU5MjLkh/1arHZQSCh8nfjkowJKZErQezXkNVdQDn3hW47t9x6ywN33zeQz/AETRp2o8J6l+XXD7Y/ueswSSQOHD50xVnbpucXR2equiVSmULaSjfrC9XFlpmhfkg0E1kQ6oJU6g2DaJZjgSCGSZGYK9d2pJJtL750hLt+Z3fP9Nzciq7ehx59tqfbyHV1p4vdo0MnE1RggoQNAAg0246ioNlsMcHwC9/8NVJKgDAQoDzP0hIJKDgIihIXRQLACSWcM4GUIoCK/5czKwEgwENAqr5EpAKAEAEInFMUjHFCkABGTGq65Z4GCEQm1egcORGSR9FioQ4ACAIkUhGskRS7cwFS1C6AqcsopsMkL8dR2VNB9YipM5UQIqT2TwCCJgQjVHCOlCj9kvx5xrkqSJJeYxlbC6AppZwAGdeDL/N8kvSgBBCIwgBRB+AEeUyuSPJKbgdMOWIFJ6giSgUXVP16KXtCJYIXQg26QmgEEYFxVW6AQjrMuUIHeCSQSmhfCUdAIHIOOgim+AshVEOdcgIRQRSdJpASFEgNGTcqbyEUEYEweUGqniwGsQOVytuQvEuCxO6Bcy6ba4GFgEBQj+uBZbaa+rygvEQy4khu6BwJRULlKagRTddQxGGnAmXvlaw3ASQoBCdq8yGKxJR7idxlBQjlYIipCBAECAIBZIRTIEw5k1GmagECEShrGYAKnVKIWJgg+JNv/49maZaeOX50X7Kj0KGl9w0NtXen1qxbEQl44okjZ27b0qpNvOHqcw6Pz7RaOFdrVubqy1evOnHyMPeC1St6kRg61Yrtucce3HvVa845fPLEBRfuPPji0TvufqSrt2P1yt4t61d++/s3D/S2dXXmix2D9zz49KazTztv56XEljECCIID1QgIEJHgSBVCIU8duXkRzjkhRKklOWqW3vJFJkWRNe58+IF7/3kv1nwmQBNYCvwzT1/3mU9+uu6X//mXO/cfOBp6sNhsUKLNVRpOMtHVlvR9VmrWddnzLDgCEKobhhn4gWmYi7V5y7INywpcXzdNUzciFiKibdDQiwDB9f10NlVZKOsaRpEQYRRy0C1dcN5qhtlMggA4yRQRDFCrletUM0KvpVGm2QlBtMuuPm/3M3v6uovTk9OZVGJyfJbxqN5spDO5VquVznaV58eBGkhoGLi2nWw065ZhNZquYZpmwkbUQt+zkynbSFRLs1xwAYwgBYIapZEfMsYJRcu0fK/peqGp62HgGsR0Q8/MJj7x6bdPjCyeOHpifq5aLs1293ePHB9jHDTH0XUaCcZ9ZjiJoFWt15tURI5utPVm3v/md3z+pu/baVqpNAni9lPOHR851L9s7dz8aK7N6u5qu/FtNzrZzIO3/eu53S9NTEyWq4v5dKp/2cDg8g7NsNPpZKM6f899j9cb0dp1q6699rqf//znAPzk8SkhgsHVnZ/8+LszVnrzpuWesGZmms/tPnjbX/944MBRAD3TkTntzK3M8/cfOO66fO3GwYMHhinSIAooIYKHGkVCdQStUDRHhibtTIIDXHXVNf+6/e+arjVbHrE0HWjg+27gp9tSXt2/6dP/9ZNbfzZ3wj334lXveuPruOGIWpBNJ1dvWDc1fNxIJ/pXLH/upb3vf/unDIeePDKeyukbTj31g2//8MqV1qG9B9rbe4YOTHl+9Od/3Bq16LbtKz/5qY/c+cBjv/jN7wkI2zZCJn776y/d9Plfv/mGC5x8Zs/T+148euCTH37/2HS5mMtMz8ydetomxPTC7Nx8bWFg0/Zffu8bP/nNPW4jWLcs99Kxfde95dqtg6/88te/0DNQoGgUc5npmWnL0j3m5vPJRqWhgaARCwg++a+//+b+e/9+yz8vPm/zT375p1zemZoqfeyj7+3btIVVG3t3P39ycj6TyB47elSnQmbNaai7AdMotdNJFCSVNEvV6lS5nCVGwDHVka1MTd14/Y1HDz64/cwzr3r1VSxk1OM//f3N0xOlKy+/9OknXlwETwu8o0eHNDMZNBvJTCqfbTt07OjqdeusjEX07NE9z6ZTdq1cKuRz5cripm0b0umehNU2Mj00PTLq+cH2884MKuVMR+7Fx57r6uqtutVirrM8X0qm07VyTRDBNcMPWhAGpVYYmeRHP/q6VSuvWzH4ne98dXi8et/BsXy2Axq1s3ZumZ+fKc+5b3/3648dGD05OltbHPnER961du0gB61ZrafSSWJoYcQs22RRBKADou9FhqHPV+b8lmebVi6X81oNjVLdtKQqs9lsIbB0PvPUY8+dsm6jy0MWcEDmNjzNYvfdv+uiiy/sKhbr1apOw8XF5sCygR/98KflplevNZqtADW6buOO9f0rXFo7fOjgNz77+d/8+ebb73gw4rSnu/2UHac9t+fJI4fGbMupV6Z5K9Aty7LTm8/YcOnpO3526z0t3yu09aXt5Oz0sbof5dNOb3ex1nQnJ2dWrOzLJu1n9wxt2bo656TLi4umRbxWFTQLMDRI+8mDz1/52gs2bzq9xZYU7yiEROcAIa6gAYVtckQKlHGGqtWKCIjl/pJOlwH9EpBRxAGRLkg15YLSr5MlR4EgsSQ93hjUL5DUrWQbYv+qiDmFGMAWsWZGgqFCcKJ8zRGoRiO+NMtLwaxQoXryzUndixxmKBOMCMIEjx+uGL8jWSSsckvlB5Hku4itr7FqQS1BIHUZEG8s8nGrvhmhCsuWDNXyi1cfHgFAwXQqWCeOVVeNbQKl/xFQqrBVBInS40vOmwuBKDgQJKjJFib5KghEvX+VpYEx3heLvkQMqcZiLlA5+6iy3pckI7IrjcRmEaFmP7k6yAlJtqQJqcZC8P2wzbR//MOvj89W2xLFmbmpiENHMZfNWIvVYGiuumFFx/TUeDqZqlSqYcQCP/IjkU4meBRu2LR5bnZ2dHjESlgaEo5o6TqNNI+5+Vx+9eYVCy2XhvToS0dNWyTN1GxljjTYmjNXHD42nTBIaWpOz1p+JDqL7fWFqqHrjIpCum2xWqIa4Be/fTMDoaEs1ZIpnyAUeirTbkAIuaZxIVPWAQgKJgiCKsEAIXcGLvcwwbgAisgRpXpBADBAgwMg4YwRIhgDJIIJSgXEgDcq4ZuUqXOBAoUGANJtLGTbrkSLFRmFAEhkUR5EAhFkoDsRIBUgggmkVDC5p6CgSOUADcAQNUCUxhsZ0g8Qa+mBAVKqYuxFJIAiRjLFUknthBBACBEgCCKXbhRQqAATgMAE6AgckcvpmauyH7ltMhFTZBJaoIKrcCm1rQuME6+oTCnmApGT2J5AkDOgGL+6bK8lwASRC5jy7CplFcTOeXUXQUTgTMjhmJFYoidAoxSJpE6QCxkmgzzeL+SdR/ryo4gRFASkYxjkJSrl6fJqYZxT5aynREh5lSAgCCFMXtawFLmJCMCA60gFUlniBoTohBJKKFHKSxkGKpWEBECo1U7O/YSDECAoEKJ4KuRCUEIUXKFIV1ha2DSkDBglS4EKoFFdbrJMCEoiHhIuRHl43y9v+V1/ti2wtMHihmd2PVQYXHbd5ZcfP7l39559BKPujk6X2i3XH+wrjhwZPXfn1jD0h44MTy4G3T39tWq92Sot6+9oTzur16x2Urm///Gu1Wu7Nm1cPVcpWdT+2W/uKJdrX7zpg9Xq3G1/ui/t6GvXdjWoUV1oZJPmea96ZegZZj7pBsIgICJhGNSgGosiioJoVJpHBCLnjDOhEepHkUEJMSy37jtpkTWTf/3nX+/9x735QhabYr5V98A9dce2L7zvg5WFk0dOzj74+NPzYwvj5UU7nQPP7+p23vKOVwSLlVt++2TPYNtze49ZOm3Wy46TsBJJ27K9IAqjoOW5tm0D57pp8iAiOhFRxFhkaBpB7nkh4zLWViMEA9fXDU3XdM4FpVroh0CQUmLqGgBPOkYj9Hbs2OwYds/A5mxbcaEeTAy/cODFF6aHF6iIBOE6NVy3alr6hq3nl+cqnj8nBDBOy4tlLwx0U6dIqU4FZ06yk4ehrgMSp1ae5jwAQiliyws4ACJLJlJBGAIXhq4xgcCBBQHRRCaXTiXMHWec1ZZzEHHX00+OHhxruQHXDcsy3cZirREWCulkMmXquu961UZzy1lnDB/a22r6URCYptnf233d9WemMuve96H/dHQzk8k4qYRjp7NJY/eeF978ule+/UM37j8wkrKNh+74l2llh06cWLV6gAFu3ngGhcZzzz5x+NBQdXaeW6nuzrbtp5+y+/k9Rw8Nl6pNP/BTOgrdSqZTrZa3cceWQsbq6i6+tG/f4T0nCu2ZmbmaTmnUbG06a1W5HIRepJl8eef6Q8cPukGk6RrnmE45rudapjMxPqETquv2qaetHxkZveLq63/9y59HLASkiaQDERMRQ9vWDM2tVxkK0agnEuli3vncf3+0Ld9RXqidecn5tpE8cGT/HX/46yPP7lucrixbWagstMyks27t9gfu/XPkIUZRrjuNHNNOeN21Vz+3Z3x0bD7fTdvs7EP37DO7E16T/vB7n9+2rbcyU3/Dmz/YCH1L6AMruwYGesutxc9+8TP/94fbr331db5btngazXomlbzxxg/7NdHd0b73xeeyhcK2zasuu+aio8cWn3nsyb3HF4odiWKm79jQAd1mIgw9z7V1M2vTeqC97jVnnX7e+b/6+W9rVa9WceutqitILmWee+F52Wz79oG1dz5x79TIbK3WGJ+boxSymYShWTQixAaN6r4bhMDTCXvfieGkaXf39iGJTgyNLu8vzE3ODC7rv+aG16Yz2JfvHJstvbj/xYmp+S998XPPv7D/R1//ccOrn3r6tpf27rcd3dBtIMa6DTsGVu74599+VKstLF+5prpYmpycTiezbV1tjVozlcy4jWrGTtuZZL49e/LA0faO4ujkdG9Xd7lWtSyqUcI83moEV772Koas0aoeOzZd86L/vukj1K9qvteZ1L//w5/f+9RQLaARBIyg49g7Nq5Np7LprDhn23lf/8Gverp7zzjjzFdduS1Af2G6bKedpGmbthV4kW5aYRTZth0x5jVbVNfkc4cgiVioUco5pwpH44BaGIQtvwkh+IGXLbQ9/NAT99/3WDqBb3rTG5LZQtLWAxZVKtWEY+UKuVbVLeS7vved70VMBEK4NfeUbTvPu+zM6emxB594bN1g39/+8q98R9EQzmtffcWtf/5rtdmYnlswNKNZK1Ef7Vx6YFXPyp6u/nWnPfn4g2EYHj8yHkVE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43v3XI/5xolW6y3vuP1v/uRv/ufLd6+vrOzYOXPDS69/+omn00FebdaSJHUdj9hqxkEa95LU932Jbq6TJNGpTep+0O2nnkPAJk90xvnE2Mi1V13w+COHXnr7BVfv2/6tBw7fcMtNn/nKF666+JIjL5z6wV2POooC33MrfijVcBA5ykcljNaACBKF5TTLDfDoSGMYpVE/QiC34gpUw06fhN26ffQtb37jSmvjB3c9qFOzvLziBM74+Nig1x9GsTEaQZBAbdlx3TzXxW0ncD0GNpqlCIdxx2IO1mFjcjBBxccsMUbFOq24DrnSI0LLncHAGmMsaa2VIsd10crUxNWRei2sZknUb/cv2b/noh219Y14aamFwiEENsn8eh+Y52amkmGnWqtffsnNYJe/+d37m36gte32uo3xyZMnT8dZtmX71pMnz8xN7fADve/C3VEa93qDWuCPNpoPPvjk3NzEu3/xtR/5x2+tLG1EkTFk91163uFDz5vM/Mxbf+273/tEbzjs9aLeMN9/6/l33Hzbo/c99le/+1ur4D3wxMOd00s/vPtebVMttOTgymuuyI199tEnEp231pa3btuyutLutgczU81Wlyt1lZmUbZbn2lVupq2QjnRErTYiyfVc7vY7taCx1G7VK0GuU9bcG8Rac6aHnnCA0QqU0qk0ajffuP/Nb/rNg0effOHQo6MjTRajTz720L0/vDdUngHrB24UJyiUkMVoh3SeFgmEXtX3ldsfRgW1g9kIFFprIHJdXyDl+TBNEqkUgXC8quM4YVgfGdt19sRD2mprwegEyWG2SFphjQmzODLCup5UKDNjhGHLlGZDpTxrtPJEARiR5ApBgzjSmdZWIGRI4Di+yZM84wsu2n342aNbdkz0u7FUbBgDN6hWqkhOe7DeW103YPtDo3wFmclMDEI0qkGcZmFYMVkOoIg4z40UMtN2bHZGYD7o9OIo9n23WqkgyHg4yHSe5cZ1HZK2349GGmPzS2uNes2verv27bad4dr6ss5sO+46QrBGFxEIkTBOovmFVmVi5Nor9z/y+POEMDYze+WV+03aPnrghfZGByQo5QhHoMDOYGiiQW10otcb+NIBoYKq74n6yuoSSahWK3GvLxQpRw0GQwOslAV2iCHO0u1bZ7TRa6v9atXpt1v18drW2a1bxhqry+tHzi78+Kt+rJf0f/DdZxKb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Iw59lfnyECuoYxUTq1fZ+V+R/wIxoV0d5g2Q8MqIrlc93p3Z62o5AFd9AHqD94M7/ddVphWADOSqHLxLGF0BP203Bth1fe3K82sAxkiAgIb2qxpgMNBdDRFpl0DalRQQEVF3erGrfNKuDICoSkSVQ2z+yl/4k/1qvejafltuPf7E3ddfee2ll7BtwnK5aGfvePLplz71C0+9/cnHTh49fvTGX/vuf/Idv/e3vfLZ19731qc/+/Ir23XvvH3lu95lam9/+1N/8W99T59yjHuM+uEPffAnfvLnjm7e+DUf/cgnfu4XSh6fe+7F42uHv/P3/K6aN1//De//wX/x0//5H/mz3/NP/sL/8S9/6NUvvfDUE0+//uD2rSee+oVf+pm/9b9895NPPzafN2w2TKlpXD+m8XL74OH5mKZHbh47iuMIs5i/9wf+5X/7F//JEweHSGMMfuhHB9jOut/wLd866MXtF1//zC9/cdr2s/2922er97zvK1lf/8InX9s7apbz9vRscExCXKvE0IJnE9903fn68l3veNu4edghPTx72C73Hj0+euGl1/rc37h2AjWv1wMajEW5IVVKUoY8TpMcd107C45pvRkd791+/Z731s1942aj9I5oERsz3BZxsb3YbImgqavD5UHOqRjsL/fKVE7Hzcx5JJlqirHh0FkdiciRMzAQS2Npu7kajkNe7sWL89U73/vWT33iMw9PH16/eXSwnKf1+Y1r172HyPjC8y/MFscnj795GepPfeIFZlTEItURIbvog6rFJhDIw/NtnvprJwdvftNjn/vMZ27cvNk12F+smJuXvnj77c8+dvv+Oc3i4f7eqw8upyoxxlwSGvuW8nqFBQuUJx5/fD5rPvvZV7mD9bY2LhA733Bs2jJuQohpyGPKMZAYMnISQTXXeDYccwmsRLC5TIfz9viweeapN52uz1+/81AorFZbZCOguGwCkWS7HC+ZXHCcpjRvFvuLbrXukXZxF9fXbJVUIVt1HPphy8F7x3kqjJirdO3SUKJnq+IQUp0a35FHFadGqrrNl8it1LJoOLggeULmlCaPrhqYWMo1drFbLIdh69Fthi1RIMZaSwXBQsGDMQbnCBUQUXEqYxObHRclNC2jIDopUkqtkp1hRfDM7LiqqiIAOnLABGamKqoAgOBz2QA2TNU7rtXaLjY+bvuhFDGnAMwEplak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/v7+bkpbN5DedgkPAFvjYkEUNx7Lc4WJdavfvj7jREncmp7LwWxNxlzWekaeyyPmy8vruY7//B/++f/07/7Tcc6f337549vvfb910ZlzxcUkyt72jFThx3z8cn855/KILsqq8xwonGO1fWOgmZKgEmBE5v32+scfv0s3Je5mlfj+/NhaM+U/vj6syfRQYi85x+obdTUV/vr127bvVcHaYi4vbqbgatI8VkQS87ZvPpd2yeVFpGC/jF2i6xyVXow5FjF12wmIymZy4UEvH3sR10pRjulFue2bss50U13LS0BVJHI8zuvkIMytmfsqsEnLcHdn5nGutmuXViChLCbiUjAor3OVn0tUhruy8jWpRYgIodyrdSHgSviGewaysrdelREJkCh5ZFM+VkWGD0dVlpcLK801unYCn+cgpsjsYl6exaTFMJ+TiovYy03uzBGFJi1odTVRfkxfXtsu+37/+P6HMJMwEwG8gb4e70okXcJrzmnW1SSXkxAJuScj394+VYSyHMdRFb/+/d89j9GN9t3en5NRrbcxl/zg88XK8gwUvb8fvYk29TmjLkpLZVK4MxOp3ax/+fr95WVX0fLZ+vb1/XlrvTiliTBfYYBLoUkqPqOA6aubsSplHcf5cR7dWmYIs2ehUMUS0JsKb8r+8e2j3zcuWnMVVQWzEJteAK7Kak2He1Miab99+fO+v1WMtm/nmPN43Nsvy89ffr1V+mb8nHOtKkgydrX/6d//9T//53//eB6NBUTupWQLU7tSFQjLPQEkzFhag+djzrf7LmqUniPO50wq7dRFXm66IsuLCDBRUQYxZREiCpEL+bp1M/72fhJXJlcUEp4ppF657X2Ok4nVbM319fmxa2ubzuV+PdMrEWzMK4IJK+q+bcc63/adGUx6zsXKprIyrciaASWdd+OASuVjrjVm3/ryGueTZa84xzl+/vx6nWa+Pc+xEiV778X0cm+fPn366z/+7TjG68sbcramTmXKBQjR9BATyvrwhbXe3j714vfzkYnN1EQfz4czlOw6JqA4GACrUq7om86xlNkjiWAiBbpyXJlAyvRpxrpxRHnEyhBuAqIqVg2fkS7dysN6f219+tp7e0wnJBFBdc0Z4QwxbStHt35Vdh/jo7V75CIRIiGqSzxp0pSpCKbX0j5aUylUCUDH8msdcfW/E0VJJi3WCDjEmrCIZmbk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ZWhk55T0MRHHs7CLWNUVbi15cHxtguXOtczMR6Q4Vpjb3iW103duCzeP637PnDvO3dxlNJUiHKwzxsNa031TTyEDn5wIEWEpoHuE/+H0qXTte0VwHZqGztnoAwaOl858UGN0dwO/9h5n7qQ4Io7KosRlanY/k0OV3Gm0933BcByvr+tdW8e5vul8IKyr1wphv85DpYk1ExztvLIm0GIQHAIzdRw+jRDTrnB3reUWcd0322cJpj8ZA+pwh+pYPqefCco/AoXkvmYQpVTg3nsdgfFFYMqvkbPcY7V4HMuNMEN199zcSHC5ST2mzcqioVJqMSw8umqCVtk6lptHVylr3OhxxMBY824STreIfV9TqsSw2ayiur5zYTvvafAZBRUggL1zzlfneVaVLequFj4/v+x85X2NO9Bnq0hma1HmEY7XOickPR1A2Zm7zJ8CnQivzMesabizQrBwmmfdbDCcTUNPIoWG8ziyajSW41jXdQ/muFpASQve9S4639f124+/DLBH80Cd85wbNMt0ZGfQM/V5f54fP47wWJ57wtmK4+hC7j0qUFdeO0dUMSorj3XSRUTXpjlVx9gDHgq0pJ6SgRKX23qdmNt5Ed3rOHem+XDoDMT7ziP8919fRhzHK3Orq8T1QFkarbUWaVVlNHPuqsztvszdfUIactrTzwXEckGCMITmNvqjGkLD5ugu1b48guR1d+33vfM813GuORjYpEtp3SXRfDgZPtmtiCCtO20gj/PANHMGSWk/sUe6emBNzEqbs4QZukttFlCPz2e8KNBoFDPX+DxfpklnslbZU8kEH9+S2jxmUWk2L03qrs5Bthuwq/reE4gyC0ilqcfW7DVh1iWPmRy6s66rqI61SFi4kyTDvTFxuUf/U9fg9XrCfAKfm/WITJjTxkTk+XQVs9XvexuQwKNitJrNZu5bEwJuNPq14vXjAwakzAcJNHqtZ5eTIKfLZS3Ddwk0R5fDKFdCW3NW1yXa176t5DOET/DaPGClGsHW8EQhK6v2beEGRJiqJGTWOj5USfcjfMXH59cfWzXRUiczG6brj3f1Nlt35a5d2csjwv/648e5HDV4QZlPpCJofl9TJogV564rLNTC8iw1unZa8/evP/74/fO047reVYL7YA/P4+N1xjrO9/1pWKU+fIG1/IgzXuvoKozK1zDh83r/OM91xLHWx4qivb8y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\n", + "text/plain": [ + "" + ] + }, + "metadata": { + "tags": [] + } + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "3Pu-VjGHnFta" + }, + "source": [ + "" + ], + "execution_count": 31, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/sd/stablediffusion/src/taming-transformers/setup.py b/sd/stablediffusion/src/taming-transformers/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..a220d12b21d96c5093a218c406cf47f1e7c8761a --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/setup.py @@ -0,0 +1,13 @@ +from setuptools import setup, find_packages + +setup( + name='taming-transformers', + version='0.0.1', + description='Taming Transformers for High-Resolution Image Synthesis', + packages=find_packages(), + install_requires=[ + 'torch', + 'numpy', + 'tqdm', + ], +) diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/ade20k.py b/sd/stablediffusion/src/taming-transformers/taming/data/ade20k.py new file mode 100644 index 0000000000000000000000000000000000000000..366dae97207dbb8356598d636e14ad084d45bc76 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/ade20k.py @@ -0,0 +1,124 @@ +import os +import numpy as np +import cv2 +import albumentations +from PIL import Image +from torch.utils.data import Dataset + +from taming.data.sflckr import SegmentationBase # for examples included in repo + + +class Examples(SegmentationBase): + def __init__(self, size=256, random_crop=False, interpolation="bicubic"): + super().__init__(data_csv="data/ade20k_examples.txt", + data_root="data/ade20k_images", + segmentation_root="data/ade20k_segmentations", + size=size, random_crop=random_crop, + interpolation=interpolation, + n_labels=151, shift_segmentation=False) + + +# With semantic map and scene label +class ADE20kBase(Dataset): + def __init__(self, config=None, size=None, random_crop=False, interpolation="bicubic", crop_size=None): + self.split = self.get_split() + self.n_labels = 151 # unknown + 150 + self.data_csv = {"train": "data/ade20k_train.txt", + "validation": "data/ade20k_test.txt"}[self.split] + self.data_root = "data/ade20k_root" + with open(os.path.join(self.data_root, "sceneCategories.txt"), "r") as f: + self.scene_categories = f.read().splitlines() + self.scene_categories = dict(line.split() for line in self.scene_categories) + with open(self.data_csv, "r") as f: + self.image_paths = f.read().splitlines() + self._length = len(self.image_paths) + self.labels = { + "relative_file_path_": [l for l in self.image_paths], + "file_path_": [os.path.join(self.data_root, "images", l) + for l in self.image_paths], + "relative_segmentation_path_": [l.replace(".jpg", ".png") + for l in self.image_paths], + "segmentation_path_": [os.path.join(self.data_root, "annotations", + l.replace(".jpg", ".png")) + for l in self.image_paths], + "scene_category": [self.scene_categories[l.split("/")[1].replace(".jpg", "")] + for l in self.image_paths], + } + + size = None if size is not None and size<=0 else size + self.size = size + if crop_size is None: + self.crop_size = size if size is not None else None + else: + self.crop_size = crop_size + if self.size is not None: + self.interpolation = interpolation + self.interpolation = { + "nearest": cv2.INTER_NEAREST, + "bilinear": cv2.INTER_LINEAR, + "bicubic": cv2.INTER_CUBIC, + "area": cv2.INTER_AREA, + "lanczos": cv2.INTER_LANCZOS4}[self.interpolation] + self.image_rescaler = albumentations.SmallestMaxSize(max_size=self.size, + interpolation=self.interpolation) + self.segmentation_rescaler = albumentations.SmallestMaxSize(max_size=self.size, + interpolation=cv2.INTER_NEAREST) + + if crop_size is not None: + self.center_crop = not random_crop + if self.center_crop: + self.cropper = albumentations.CenterCrop(height=self.crop_size, width=self.crop_size) + else: + self.cropper = albumentations.RandomCrop(height=self.crop_size, width=self.crop_size) + self.preprocessor = self.cropper + + def __len__(self): + return self._length + + def __getitem__(self, i): + example = dict((k, self.labels[k][i]) for k in self.labels) + image = Image.open(example["file_path_"]) + if not image.mode == "RGB": + image = image.convert("RGB") + image = np.array(image).astype(np.uint8) + if self.size is not None: + image = self.image_rescaler(image=image)["image"] + segmentation = Image.open(example["segmentation_path_"]) + segmentation = np.array(segmentation).astype(np.uint8) + if self.size is not None: + segmentation = self.segmentation_rescaler(image=segmentation)["image"] + if self.size is not None: + processed = self.preprocessor(image=image, mask=segmentation) + else: + processed = {"image": image, "mask": segmentation} + example["image"] = (processed["image"]/127.5 - 1.0).astype(np.float32) + segmentation = processed["mask"] + onehot = np.eye(self.n_labels)[segmentation] + example["segmentation"] = onehot + return example + + +class ADE20kTrain(ADE20kBase): + # default to random_crop=True + def __init__(self, config=None, size=None, random_crop=True, interpolation="bicubic", crop_size=None): + super().__init__(config=config, size=size, random_crop=random_crop, + interpolation=interpolation, crop_size=crop_size) + + def get_split(self): + return "train" + + +class ADE20kValidation(ADE20kBase): + def get_split(self): + return "validation" + + +if __name__ == "__main__": + dset = ADE20kValidation() + ex = dset[0] + for k in ["image", "scene_category", "segmentation"]: + print(type(ex[k])) + try: + print(ex[k].shape) + except: + print(ex[k]) diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/annotated_objects_coco.py b/sd/stablediffusion/src/taming-transformers/taming/data/annotated_objects_coco.py new file mode 100644 index 0000000000000000000000000000000000000000..af000ecd943d7b8a85d7eb70195c9ecd10ab5edc --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/annotated_objects_coco.py @@ -0,0 +1,139 @@ +import json +from itertools import chain +from pathlib import Path +from typing import Iterable, Dict, List, Callable, Any +from collections import defaultdict + +from tqdm import tqdm + +from taming.data.annotated_objects_dataset import AnnotatedObjectsDataset +from taming.data.helper_types import Annotation, ImageDescription, Category + +COCO_PATH_STRUCTURE = { + 'train': { + 'top_level': '', + 'instances_annotations': 'annotations/instances_train2017.json', + 'stuff_annotations': 'annotations/stuff_train2017.json', + 'files': 'train2017' + }, + 'validation': { + 'top_level': '', + 'instances_annotations': 'annotations/instances_val2017.json', + 'stuff_annotations': 'annotations/stuff_val2017.json', + 'files': 'val2017' + } +} + + +def load_image_descriptions(description_json: List[Dict]) -> Dict[str, ImageDescription]: + return { + str(img['id']): ImageDescription( + id=img['id'], + license=img.get('license'), + file_name=img['file_name'], + coco_url=img['coco_url'], + original_size=(img['width'], img['height']), + date_captured=img.get('date_captured'), + flickr_url=img.get('flickr_url') + ) + for img in description_json + } + + +def load_categories(category_json: Iterable) -> Dict[str, Category]: + return {str(cat['id']): Category(id=str(cat['id']), super_category=cat['supercategory'], name=cat['name']) + for cat in category_json if cat['name'] != 'other'} + + +def load_annotations(annotations_json: List[Dict], image_descriptions: Dict[str, ImageDescription], + category_no_for_id: Callable[[str], int], split: str) -> Dict[str, List[Annotation]]: + annotations = defaultdict(list) + total = sum(len(a) for a in annotations_json) + for ann in tqdm(chain(*annotations_json), f'Loading {split} annotations', total=total): + image_id = str(ann['image_id']) + if image_id not in image_descriptions: + raise ValueError(f'image_id [{image_id}] has no image description.') + category_id = ann['category_id'] + try: + category_no = category_no_for_id(str(category_id)) + except KeyError: + continue + + width, height = image_descriptions[image_id].original_size + bbox = (ann['bbox'][0] / width, ann['bbox'][1] / height, ann['bbox'][2] / width, ann['bbox'][3] / height) + + annotations[image_id].append( + Annotation( + id=ann['id'], + area=bbox[2]*bbox[3], # use bbox area + is_group_of=ann['iscrowd'], + image_id=ann['image_id'], + bbox=bbox, + category_id=str(category_id), + category_no=category_no + ) + ) + return dict(annotations) + + +class AnnotatedObjectsCoco(AnnotatedObjectsDataset): + def __init__(self, use_things: bool = True, use_stuff: bool = True, **kwargs): + """ + @param data_path: is the path to the following folder structure: + coco/ + ├── annotations + │ ├── instances_train2017.json + │ ├── instances_val2017.json + │ ├── stuff_train2017.json + │ └── stuff_val2017.json + ├── train2017 + │ ├── 000000000009.jpg + │ ├── 000000000025.jpg + │ └── ... + ├── val2017 + │ ├── 000000000139.jpg + │ ├── 000000000285.jpg + │ └── ... + @param: split: one of 'train' or 'validation' + @param: desired image size (give square images) + """ + super().__init__(**kwargs) + self.use_things = use_things + self.use_stuff = use_stuff + + with open(self.paths['instances_annotations']) as f: + inst_data_json = json.load(f) + with open(self.paths['stuff_annotations']) as f: + stuff_data_json = json.load(f) + + category_jsons = [] + annotation_jsons = [] + if self.use_things: + category_jsons.append(inst_data_json['categories']) + annotation_jsons.append(inst_data_json['annotations']) + if self.use_stuff: + category_jsons.append(stuff_data_json['categories']) + annotation_jsons.append(stuff_data_json['annotations']) + + self.categories = load_categories(chain(*category_jsons)) + self.filter_categories() + self.setup_category_id_and_number() + + self.image_descriptions = load_image_descriptions(inst_data_json['images']) + annotations = load_annotations(annotation_jsons, self.image_descriptions, self.get_category_number, self.split) + self.annotations = self.filter_object_number(annotations, self.min_object_area, + self.min_objects_per_image, self.max_objects_per_image) + self.image_ids = list(self.annotations.keys()) + self.clean_up_annotations_and_image_descriptions() + + def get_path_structure(self) -> Dict[str, str]: + if self.split not in COCO_PATH_STRUCTURE: + raise ValueError(f'Split [{self.split} does not exist for COCO data.]') + return COCO_PATH_STRUCTURE[self.split] + + def get_image_path(self, image_id: str) -> Path: + return self.paths['files'].joinpath(self.image_descriptions[str(image_id)].file_name) + + def get_image_description(self, image_id: str) -> Dict[str, Any]: + # noinspection PyProtectedMember + return self.image_descriptions[image_id]._asdict() diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/annotated_objects_dataset.py b/sd/stablediffusion/src/taming-transformers/taming/data/annotated_objects_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..53cc346a1c76289a4964d7dc8a29582172f33dc0 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/annotated_objects_dataset.py @@ -0,0 +1,218 @@ +from pathlib import Path +from typing import Optional, List, Callable, Dict, Any, Union +import warnings + +import PIL.Image as pil_image +from torch import Tensor +from torch.utils.data import Dataset +from torchvision import transforms + +from taming.data.conditional_builder.objects_bbox import ObjectsBoundingBoxConditionalBuilder +from taming.data.conditional_builder.objects_center_points import ObjectsCenterPointsConditionalBuilder +from taming.data.conditional_builder.utils import load_object_from_string +from taming.data.helper_types import BoundingBox, CropMethodType, Image, Annotation, SplitType +from taming.data.image_transforms import CenterCropReturnCoordinates, RandomCrop1dReturnCoordinates, \ + Random2dCropReturnCoordinates, RandomHorizontalFlipReturn, convert_pil_to_tensor + + +class AnnotatedObjectsDataset(Dataset): + def __init__(self, data_path: Union[str, Path], split: SplitType, keys: List[str], target_image_size: int, + min_object_area: float, min_objects_per_image: int, max_objects_per_image: int, + crop_method: CropMethodType, random_flip: bool, no_tokens: int, use_group_parameter: bool, + encode_crop: bool, category_allow_list_target: str = "", category_mapping_target: str = "", + no_object_classes: Optional[int] = None): + self.data_path = data_path + self.split = split + self.keys = keys + self.target_image_size = target_image_size + self.min_object_area = min_object_area + self.min_objects_per_image = min_objects_per_image + self.max_objects_per_image = max_objects_per_image + self.crop_method = crop_method + self.random_flip = random_flip + self.no_tokens = no_tokens + self.use_group_parameter = use_group_parameter + self.encode_crop = encode_crop + + self.annotations = None + self.image_descriptions = None + self.categories = None + self.category_ids = None + self.category_number = None + self.image_ids = None + self.transform_functions: List[Callable] = self.setup_transform(target_image_size, crop_method, random_flip) + self.paths = self.build_paths(self.data_path) + self._conditional_builders = None + self.category_allow_list = None + if category_allow_list_target: + allow_list = load_object_from_string(category_allow_list_target) + self.category_allow_list = {name for name, _ in allow_list} + self.category_mapping = {} + if category_mapping_target: + self.category_mapping = load_object_from_string(category_mapping_target) + self.no_object_classes = no_object_classes + + def build_paths(self, top_level: Union[str, Path]) -> Dict[str, Path]: + top_level = Path(top_level) + sub_paths = {name: top_level.joinpath(sub_path) for name, sub_path in self.get_path_structure().items()} + for path in sub_paths.values(): + if not path.exists(): + raise FileNotFoundError(f'{type(self).__name__} data structure error: [{path}] does not exist.') + return sub_paths + + @staticmethod + def load_image_from_disk(path: Path) -> Image: + return pil_image.open(path).convert('RGB') + + @staticmethod + def setup_transform(target_image_size: int, crop_method: CropMethodType, random_flip: bool): + transform_functions = [] + if crop_method == 'none': + transform_functions.append(transforms.Resize((target_image_size, target_image_size))) + elif crop_method == 'center': + transform_functions.extend([ + transforms.Resize(target_image_size), + CenterCropReturnCoordinates(target_image_size) + ]) + elif crop_method == 'random-1d': + transform_functions.extend([ + transforms.Resize(target_image_size), + RandomCrop1dReturnCoordinates(target_image_size) + ]) + elif crop_method == 'random-2d': + transform_functions.extend([ + Random2dCropReturnCoordinates(target_image_size), + transforms.Resize(target_image_size) + ]) + elif crop_method is None: + return None + else: + raise ValueError(f'Received invalid crop method [{crop_method}].') + if random_flip: + transform_functions.append(RandomHorizontalFlipReturn()) + transform_functions.append(transforms.Lambda(lambda x: x / 127.5 - 1.)) + return transform_functions + + def image_transform(self, x: Tensor) -> (Optional[BoundingBox], Optional[bool], Tensor): + crop_bbox = None + flipped = None + for t in self.transform_functions: + if isinstance(t, (RandomCrop1dReturnCoordinates, CenterCropReturnCoordinates, Random2dCropReturnCoordinates)): + crop_bbox, x = t(x) + elif isinstance(t, RandomHorizontalFlipReturn): + flipped, x = t(x) + else: + x = t(x) + return crop_bbox, flipped, x + + @property + def no_classes(self) -> int: + return self.no_object_classes if self.no_object_classes else len(self.categories) + + @property + def conditional_builders(self) -> ObjectsCenterPointsConditionalBuilder: + # cannot set this up in init because no_classes is only known after loading data in init of superclass + if self._conditional_builders is None: + self._conditional_builders = { + 'objects_center_points': ObjectsCenterPointsConditionalBuilder( + self.no_classes, + self.max_objects_per_image, + self.no_tokens, + self.encode_crop, + self.use_group_parameter, + getattr(self, 'use_additional_parameters', False) + ), + 'objects_bbox': ObjectsBoundingBoxConditionalBuilder( + self.no_classes, + self.max_objects_per_image, + self.no_tokens, + self.encode_crop, + self.use_group_parameter, + getattr(self, 'use_additional_parameters', False) + ) + } + return self._conditional_builders + + def filter_categories(self) -> None: + if self.category_allow_list: + self.categories = {id_: cat for id_, cat in self.categories.items() if cat.name in self.category_allow_list} + if self.category_mapping: + self.categories = {id_: cat for id_, cat in self.categories.items() if cat.id not in self.category_mapping} + + def setup_category_id_and_number(self) -> None: + self.category_ids = list(self.categories.keys()) + self.category_ids.sort() + if '/m/01s55n' in self.category_ids: + self.category_ids.remove('/m/01s55n') + self.category_ids.append('/m/01s55n') + self.category_number = {category_id: i for i, category_id in enumerate(self.category_ids)} + if self.category_allow_list is not None and self.category_mapping is None \ + and len(self.category_ids) != len(self.category_allow_list): + warnings.warn('Unexpected number of categories: Mismatch with category_allow_list. ' + 'Make sure all names in category_allow_list exist.') + + def clean_up_annotations_and_image_descriptions(self) -> None: + image_id_set = set(self.image_ids) + self.annotations = {k: v for k, v in self.annotations.items() if k in image_id_set} + self.image_descriptions = {k: v for k, v in self.image_descriptions.items() if k in image_id_set} + + @staticmethod + def filter_object_number(all_annotations: Dict[str, List[Annotation]], min_object_area: float, + min_objects_per_image: int, max_objects_per_image: int) -> Dict[str, List[Annotation]]: + filtered = {} + for image_id, annotations in all_annotations.items(): + annotations_with_min_area = [a for a in annotations if a.area > min_object_area] + if min_objects_per_image <= len(annotations_with_min_area) <= max_objects_per_image: + filtered[image_id] = annotations_with_min_area + return filtered + + def __len__(self): + return len(self.image_ids) + + def __getitem__(self, n: int) -> Dict[str, Any]: + image_id = self.get_image_id(n) + sample = self.get_image_description(image_id) + sample['annotations'] = self.get_annotation(image_id) + + if 'image' in self.keys: + sample['image_path'] = str(self.get_image_path(image_id)) + sample['image'] = self.load_image_from_disk(sample['image_path']) + sample['image'] = convert_pil_to_tensor(sample['image']) + sample['crop_bbox'], sample['flipped'], sample['image'] = self.image_transform(sample['image']) + sample['image'] = sample['image'].permute(1, 2, 0) + + for conditional, builder in self.conditional_builders.items(): + if conditional in self.keys: + sample[conditional] = builder.build(sample['annotations'], sample['crop_bbox'], sample['flipped']) + + if self.keys: + # only return specified keys + sample = {key: sample[key] for key in self.keys} + return sample + + def get_image_id(self, no: int) -> str: + return self.image_ids[no] + + def get_annotation(self, image_id: str) -> str: + return self.annotations[image_id] + + def get_textual_label_for_category_id(self, category_id: str) -> str: + return self.categories[category_id].name + + def get_textual_label_for_category_no(self, category_no: int) -> str: + return self.categories[self.get_category_id(category_no)].name + + def get_category_number(self, category_id: str) -> int: + return self.category_number[category_id] + + def get_category_id(self, category_no: int) -> str: + return self.category_ids[category_no] + + def get_image_description(self, image_id: str) -> Dict[str, Any]: + raise NotImplementedError() + + def get_path_structure(self): + raise NotImplementedError + + def get_image_path(self, image_id: str) -> Path: + raise NotImplementedError diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/annotated_objects_open_images.py b/sd/stablediffusion/src/taming-transformers/taming/data/annotated_objects_open_images.py new file mode 100644 index 0000000000000000000000000000000000000000..aede6803d2cef7a74ca784e7907d35fba6c71239 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/annotated_objects_open_images.py @@ -0,0 +1,137 @@ +from collections import defaultdict +from csv import DictReader, reader as TupleReader +from pathlib import Path +from typing import Dict, List, Any +import warnings + +from taming.data.annotated_objects_dataset import AnnotatedObjectsDataset +from taming.data.helper_types import Annotation, Category +from tqdm import tqdm + +OPEN_IMAGES_STRUCTURE = { + 'train': { + 'top_level': '', + 'class_descriptions': 'class-descriptions-boxable.csv', + 'annotations': 'oidv6-train-annotations-bbox.csv', + 'file_list': 'train-images-boxable.csv', + 'files': 'train' + }, + 'validation': { + 'top_level': '', + 'class_descriptions': 'class-descriptions-boxable.csv', + 'annotations': 'validation-annotations-bbox.csv', + 'file_list': 'validation-images.csv', + 'files': 'validation' + }, + 'test': { + 'top_level': '', + 'class_descriptions': 'class-descriptions-boxable.csv', + 'annotations': 'test-annotations-bbox.csv', + 'file_list': 'test-images.csv', + 'files': 'test' + } +} + + +def load_annotations(descriptor_path: Path, min_object_area: float, category_mapping: Dict[str, str], + category_no_for_id: Dict[str, int]) -> Dict[str, List[Annotation]]: + annotations: Dict[str, List[Annotation]] = defaultdict(list) + with open(descriptor_path) as file: + reader = DictReader(file) + for i, row in tqdm(enumerate(reader), total=14620000, desc='Loading OpenImages annotations'): + width = float(row['XMax']) - float(row['XMin']) + height = float(row['YMax']) - float(row['YMin']) + area = width * height + category_id = row['LabelName'] + if category_id in category_mapping: + category_id = category_mapping[category_id] + if area >= min_object_area and category_id in category_no_for_id: + annotations[row['ImageID']].append( + Annotation( + id=i, + image_id=row['ImageID'], + source=row['Source'], + category_id=category_id, + category_no=category_no_for_id[category_id], + confidence=float(row['Confidence']), + bbox=(float(row['XMin']), float(row['YMin']), width, height), + area=area, + is_occluded=bool(int(row['IsOccluded'])), + is_truncated=bool(int(row['IsTruncated'])), + is_group_of=bool(int(row['IsGroupOf'])), + is_depiction=bool(int(row['IsDepiction'])), + is_inside=bool(int(row['IsInside'])) + ) + ) + if 'train' in str(descriptor_path) and i < 14000000: + warnings.warn(f'Running with subset of Open Images. Train dataset has length [{len(annotations)}].') + return dict(annotations) + + +def load_image_ids(csv_path: Path) -> List[str]: + with open(csv_path) as file: + reader = DictReader(file) + return [row['image_name'] for row in reader] + + +def load_categories(csv_path: Path) -> Dict[str, Category]: + with open(csv_path) as file: + reader = TupleReader(file) + return {row[0]: Category(id=row[0], name=row[1], super_category=None) for row in reader} + + +class AnnotatedObjectsOpenImages(AnnotatedObjectsDataset): + def __init__(self, use_additional_parameters: bool, **kwargs): + """ + @param data_path: is the path to the following folder structure: + open_images/ + │ oidv6-train-annotations-bbox.csv + ├── class-descriptions-boxable.csv + ├── oidv6-train-annotations-bbox.csv + ├── test + │ ├── 000026e7ee790996.jpg + │ ├── 000062a39995e348.jpg + │ └── ... + ├── test-annotations-bbox.csv + ├── test-images.csv + ├── train + │ ├── 000002b66c9c498e.jpg + │ ├── 000002b97e5471a0.jpg + │ └── ... + ├── train-images-boxable.csv + ├── validation + │ ├── 0001eeaf4aed83f9.jpg + │ ├── 0004886b7d043cfd.jpg + │ └── ... + ├── validation-annotations-bbox.csv + └── validation-images.csv + @param: split: one of 'train', 'validation' or 'test' + @param: desired image size (returns square images) + """ + + super().__init__(**kwargs) + self.use_additional_parameters = use_additional_parameters + + self.categories = load_categories(self.paths['class_descriptions']) + self.filter_categories() + self.setup_category_id_and_number() + + self.image_descriptions = {} + annotations = load_annotations(self.paths['annotations'], self.min_object_area, self.category_mapping, + self.category_number) + self.annotations = self.filter_object_number(annotations, self.min_object_area, self.min_objects_per_image, + self.max_objects_per_image) + self.image_ids = list(self.annotations.keys()) + self.clean_up_annotations_and_image_descriptions() + + def get_path_structure(self) -> Dict[str, str]: + if self.split not in OPEN_IMAGES_STRUCTURE: + raise ValueError(f'Split [{self.split} does not exist for Open Images data.]') + return OPEN_IMAGES_STRUCTURE[self.split] + + def get_image_path(self, image_id: str) -> Path: + return self.paths['files'].joinpath(f'{image_id:0>16}.jpg') + + def get_image_description(self, image_id: str) -> Dict[str, Any]: + image_path = self.get_image_path(image_id) + return {'file_path': str(image_path), 'file_name': image_path.name} diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/base.py b/sd/stablediffusion/src/taming-transformers/taming/data/base.py new file mode 100644 index 0000000000000000000000000000000000000000..e21667df4ce4baa6bb6aad9f8679bd756e2ffdb7 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/base.py @@ -0,0 +1,70 @@ +import bisect +import numpy as np +import albumentations +from PIL import Image +from torch.utils.data import Dataset, ConcatDataset + + +class ConcatDatasetWithIndex(ConcatDataset): + """Modified from original pytorch code to return dataset idx""" + def __getitem__(self, idx): + if idx < 0: + if -idx > len(self): + raise ValueError("absolute value of index should not exceed dataset length") + idx = len(self) + idx + dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx) + if dataset_idx == 0: + sample_idx = idx + else: + sample_idx = idx - self.cumulative_sizes[dataset_idx - 1] + return self.datasets[dataset_idx][sample_idx], dataset_idx + + +class ImagePaths(Dataset): + def __init__(self, paths, size=None, random_crop=False, labels=None): + self.size = size + self.random_crop = random_crop + + self.labels = dict() if labels is None else labels + self.labels["file_path_"] = paths + self._length = len(paths) + + if self.size is not None and self.size > 0: + self.rescaler = albumentations.SmallestMaxSize(max_size = self.size) + if not self.random_crop: + self.cropper = albumentations.CenterCrop(height=self.size,width=self.size) + else: + self.cropper = albumentations.RandomCrop(height=self.size,width=self.size) + self.preprocessor = albumentations.Compose([self.rescaler, self.cropper]) + else: + self.preprocessor = lambda **kwargs: kwargs + + def __len__(self): + return self._length + + def preprocess_image(self, image_path): + image = Image.open(image_path) + if not image.mode == "RGB": + image = image.convert("RGB") + image = np.array(image).astype(np.uint8) + image = self.preprocessor(image=image)["image"] + image = (image/127.5 - 1.0).astype(np.float32) + return image + + def __getitem__(self, i): + example = dict() + example["image"] = self.preprocess_image(self.labels["file_path_"][i]) + for k in self.labels: + example[k] = self.labels[k][i] + return example + + +class NumpyPaths(ImagePaths): + def preprocess_image(self, image_path): + image = np.load(image_path).squeeze(0) # 3 x 1024 x 1024 + image = np.transpose(image, (1,2,0)) + image = Image.fromarray(image, mode="RGB") + image = np.array(image).astype(np.uint8) + image = self.preprocessor(image=image)["image"] + image = (image/127.5 - 1.0).astype(np.float32) + return image diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/coco.py b/sd/stablediffusion/src/taming-transformers/taming/data/coco.py new file mode 100644 index 0000000000000000000000000000000000000000..2b2f7838448cb63dcf96daffe9470d58566d975a --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/coco.py @@ -0,0 +1,176 @@ +import os +import json +import albumentations +import numpy as np +from PIL import Image +from tqdm import tqdm +from torch.utils.data import Dataset + +from taming.data.sflckr import SegmentationBase # for examples included in repo + + +class Examples(SegmentationBase): + def __init__(self, size=256, random_crop=False, interpolation="bicubic"): + super().__init__(data_csv="data/coco_examples.txt", + data_root="data/coco_images", + segmentation_root="data/coco_segmentations", + size=size, random_crop=random_crop, + interpolation=interpolation, + n_labels=183, shift_segmentation=True) + + +class CocoBase(Dataset): + """needed for (image, caption, segmentation) pairs""" + def __init__(self, size=None, dataroot="", datajson="", onehot_segmentation=False, use_stuffthing=False, + crop_size=None, force_no_crop=False, given_files=None): + self.split = self.get_split() + self.size = size + if crop_size is None: + self.crop_size = size + else: + self.crop_size = crop_size + + self.onehot = onehot_segmentation # return segmentation as rgb or one hot + self.stuffthing = use_stuffthing # include thing in segmentation + if self.onehot and not self.stuffthing: + raise NotImplemented("One hot mode is only supported for the " + "stuffthings version because labels are stored " + "a bit different.") + + data_json = datajson + with open(data_json) as json_file: + self.json_data = json.load(json_file) + self.img_id_to_captions = dict() + self.img_id_to_filepath = dict() + self.img_id_to_segmentation_filepath = dict() + + assert data_json.split("/")[-1] in ["captions_train2017.json", + "captions_val2017.json"] + if self.stuffthing: + self.segmentation_prefix = ( + "data/cocostuffthings/val2017" if + data_json.endswith("captions_val2017.json") else + "data/cocostuffthings/train2017") + else: + self.segmentation_prefix = ( + "data/coco/annotations/stuff_val2017_pixelmaps" if + data_json.endswith("captions_val2017.json") else + "data/coco/annotations/stuff_train2017_pixelmaps") + + imagedirs = self.json_data["images"] + self.labels = {"image_ids": list()} + for imgdir in tqdm(imagedirs, desc="ImgToPath"): + self.img_id_to_filepath[imgdir["id"]] = os.path.join(dataroot, imgdir["file_name"]) + self.img_id_to_captions[imgdir["id"]] = list() + pngfilename = imgdir["file_name"].replace("jpg", "png") + self.img_id_to_segmentation_filepath[imgdir["id"]] = os.path.join( + self.segmentation_prefix, pngfilename) + if given_files is not None: + if pngfilename in given_files: + self.labels["image_ids"].append(imgdir["id"]) + else: + self.labels["image_ids"].append(imgdir["id"]) + + capdirs = self.json_data["annotations"] + for capdir in tqdm(capdirs, desc="ImgToCaptions"): + # there are in average 5 captions per image + self.img_id_to_captions[capdir["image_id"]].append(np.array([capdir["caption"]])) + + self.rescaler = albumentations.SmallestMaxSize(max_size=self.size) + if self.split=="validation": + self.cropper = albumentations.CenterCrop(height=self.crop_size, width=self.crop_size) + else: + self.cropper = albumentations.RandomCrop(height=self.crop_size, width=self.crop_size) + self.preprocessor = albumentations.Compose( + [self.rescaler, self.cropper], + additional_targets={"segmentation": "image"}) + if force_no_crop: + self.rescaler = albumentations.Resize(height=self.size, width=self.size) + self.preprocessor = albumentations.Compose( + [self.rescaler], + additional_targets={"segmentation": "image"}) + + def __len__(self): + return len(self.labels["image_ids"]) + + def preprocess_image(self, image_path, segmentation_path): + image = Image.open(image_path) + if not image.mode == "RGB": + image = image.convert("RGB") + image = np.array(image).astype(np.uint8) + + segmentation = Image.open(segmentation_path) + if not self.onehot and not segmentation.mode == "RGB": + segmentation = segmentation.convert("RGB") + segmentation = np.array(segmentation).astype(np.uint8) + if self.onehot: + assert self.stuffthing + # stored in caffe format: unlabeled==255. stuff and thing from + # 0-181. to be compatible with the labels in + # https://github.com/nightrome/cocostuff/blob/master/labels.txt + # we shift stuffthing one to the right and put unlabeled in zero + # as long as segmentation is uint8 shifting to right handles the + # latter too + assert segmentation.dtype == np.uint8 + segmentation = segmentation + 1 + + processed = self.preprocessor(image=image, segmentation=segmentation) + image, segmentation = processed["image"], processed["segmentation"] + image = (image / 127.5 - 1.0).astype(np.float32) + + if self.onehot: + assert segmentation.dtype == np.uint8 + # make it one hot + n_labels = 183 + flatseg = np.ravel(segmentation) + onehot = np.zeros((flatseg.size, n_labels), dtype=np.bool) + onehot[np.arange(flatseg.size), flatseg] = True + onehot = onehot.reshape(segmentation.shape + (n_labels,)).astype(int) + segmentation = onehot + else: + segmentation = (segmentation / 127.5 - 1.0).astype(np.float32) + return image, segmentation + + def __getitem__(self, i): + img_path = self.img_id_to_filepath[self.labels["image_ids"][i]] + seg_path = self.img_id_to_segmentation_filepath[self.labels["image_ids"][i]] + image, segmentation = self.preprocess_image(img_path, seg_path) + captions = self.img_id_to_captions[self.labels["image_ids"][i]] + # randomly draw one of all available captions per image + caption = captions[np.random.randint(0, len(captions))] + example = {"image": image, + "caption": [str(caption[0])], + "segmentation": segmentation, + "img_path": img_path, + "seg_path": seg_path, + "filename_": img_path.split(os.sep)[-1] + } + return example + + +class CocoImagesAndCaptionsTrain(CocoBase): + """returns a pair of (image, caption)""" + def __init__(self, size, onehot_segmentation=False, use_stuffthing=False, crop_size=None, force_no_crop=False): + super().__init__(size=size, + dataroot="data/coco/train2017", + datajson="data/coco/annotations/captions_train2017.json", + onehot_segmentation=onehot_segmentation, + use_stuffthing=use_stuffthing, crop_size=crop_size, force_no_crop=force_no_crop) + + def get_split(self): + return "train" + + +class CocoImagesAndCaptionsValidation(CocoBase): + """returns a pair of (image, caption)""" + def __init__(self, size, onehot_segmentation=False, use_stuffthing=False, crop_size=None, force_no_crop=False, + given_files=None): + super().__init__(size=size, + dataroot="data/coco/val2017", + datajson="data/coco/annotations/captions_val2017.json", + onehot_segmentation=onehot_segmentation, + use_stuffthing=use_stuffthing, crop_size=crop_size, force_no_crop=force_no_crop, + given_files=given_files) + + def get_split(self): + return "validation" diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/conditional_builder/objects_bbox.py b/sd/stablediffusion/src/taming-transformers/taming/data/conditional_builder/objects_bbox.py new file mode 100644 index 0000000000000000000000000000000000000000..15881e76b7ab2a914df8f2dfe08ae4f0c6c511b5 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/conditional_builder/objects_bbox.py @@ -0,0 +1,60 @@ +from itertools import cycle +from typing import List, Tuple, Callable, Optional + +from PIL import Image as pil_image, ImageDraw as pil_img_draw, ImageFont +from more_itertools.recipes import grouper +from taming.data.image_transforms import convert_pil_to_tensor +from torch import LongTensor, Tensor + +from taming.data.helper_types import BoundingBox, Annotation +from taming.data.conditional_builder.objects_center_points import ObjectsCenterPointsConditionalBuilder +from taming.data.conditional_builder.utils import COLOR_PALETTE, WHITE, GRAY_75, BLACK, additional_parameters_string, \ + pad_list, get_plot_font_size, absolute_bbox + + +class ObjectsBoundingBoxConditionalBuilder(ObjectsCenterPointsConditionalBuilder): + @property + def object_descriptor_length(self) -> int: + return 3 + + def _make_object_descriptors(self, annotations: List[Annotation]) -> List[Tuple[int, ...]]: + object_triples = [ + (self.object_representation(ann), *self.token_pair_from_bbox(ann.bbox)) + for ann in annotations + ] + empty_triple = (self.none, self.none, self.none) + object_triples = pad_list(object_triples, empty_triple, self.no_max_objects) + return object_triples + + def inverse_build(self, conditional: LongTensor) -> Tuple[List[Tuple[int, BoundingBox]], Optional[BoundingBox]]: + conditional_list = conditional.tolist() + crop_coordinates = None + if self.encode_crop: + crop_coordinates = self.bbox_from_token_pair(conditional_list[-2], conditional_list[-1]) + conditional_list = conditional_list[:-2] + object_triples = grouper(conditional_list, 3) + assert conditional.shape[0] == self.embedding_dim + return [ + (object_triple[0], self.bbox_from_token_pair(object_triple[1], object_triple[2])) + for object_triple in object_triples if object_triple[0] != self.none + ], crop_coordinates + + def plot(self, conditional: LongTensor, label_for_category_no: Callable[[int], str], figure_size: Tuple[int, int], + line_width: int = 3, font_size: Optional[int] = None) -> Tensor: + plot = pil_image.new('RGB', figure_size, WHITE) + draw = pil_img_draw.Draw(plot) + font = ImageFont.truetype( + "/usr/share/fonts/truetype/lato/Lato-Regular.ttf", + size=get_plot_font_size(font_size, figure_size) + ) + width, height = plot.size + description, crop_coordinates = self.inverse_build(conditional) + for (representation, bbox), color in zip(description, cycle(COLOR_PALETTE)): + annotation = self.representation_to_annotation(representation) + class_label = label_for_category_no(annotation.category_no) + ' ' + additional_parameters_string(annotation) + bbox = absolute_bbox(bbox, width, height) + draw.rectangle(bbox, outline=color, width=line_width) + draw.text((bbox[0] + line_width, bbox[1] + line_width), class_label, anchor='la', fill=BLACK, font=font) + if crop_coordinates is not None: + draw.rectangle(absolute_bbox(crop_coordinates, width, height), outline=GRAY_75, width=line_width) + return convert_pil_to_tensor(plot) / 127.5 - 1. diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/conditional_builder/objects_center_points.py b/sd/stablediffusion/src/taming-transformers/taming/data/conditional_builder/objects_center_points.py new file mode 100644 index 0000000000000000000000000000000000000000..9a480329cc47fb38a7b8729d424e092b77d40749 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/conditional_builder/objects_center_points.py @@ -0,0 +1,168 @@ +import math +import random +import warnings +from itertools import cycle +from typing import List, Optional, Tuple, Callable + +from PIL import Image as pil_image, ImageDraw as pil_img_draw, ImageFont +from more_itertools.recipes import grouper +from taming.data.conditional_builder.utils import COLOR_PALETTE, WHITE, GRAY_75, BLACK, FULL_CROP, filter_annotations, \ + additional_parameters_string, horizontally_flip_bbox, pad_list, get_circle_size, get_plot_font_size, \ + absolute_bbox, rescale_annotations +from taming.data.helper_types import BoundingBox, Annotation +from taming.data.image_transforms import convert_pil_to_tensor +from torch import LongTensor, Tensor + + +class ObjectsCenterPointsConditionalBuilder: + def __init__(self, no_object_classes: int, no_max_objects: int, no_tokens: int, encode_crop: bool, + use_group_parameter: bool, use_additional_parameters: bool): + self.no_object_classes = no_object_classes + self.no_max_objects = no_max_objects + self.no_tokens = no_tokens + self.encode_crop = encode_crop + self.no_sections = int(math.sqrt(self.no_tokens)) + self.use_group_parameter = use_group_parameter + self.use_additional_parameters = use_additional_parameters + + @property + def none(self) -> int: + return self.no_tokens - 1 + + @property + def object_descriptor_length(self) -> int: + return 2 + + @property + def embedding_dim(self) -> int: + extra_length = 2 if self.encode_crop else 0 + return self.no_max_objects * self.object_descriptor_length + extra_length + + def tokenize_coordinates(self, x: float, y: float) -> int: + """ + Express 2d coordinates with one number. + Example: assume self.no_tokens = 16, then no_sections = 4: + 0 0 0 0 + 0 0 # 0 + 0 0 0 0 + 0 0 0 x + Then the # position corresponds to token 6, the x position to token 15. + @param x: float in [0, 1] + @param y: float in [0, 1] + @return: discrete tokenized coordinate + """ + x_discrete = int(round(x * (self.no_sections - 1))) + y_discrete = int(round(y * (self.no_sections - 1))) + return y_discrete * self.no_sections + x_discrete + + def coordinates_from_token(self, token: int) -> (float, float): + x = token % self.no_sections + y = token // self.no_sections + return x / (self.no_sections - 1), y / (self.no_sections - 1) + + def bbox_from_token_pair(self, token1: int, token2: int) -> BoundingBox: + x0, y0 = self.coordinates_from_token(token1) + x1, y1 = self.coordinates_from_token(token2) + return x0, y0, x1 - x0, y1 - y0 + + def token_pair_from_bbox(self, bbox: BoundingBox) -> Tuple[int, int]: + return self.tokenize_coordinates(bbox[0], bbox[1]), \ + self.tokenize_coordinates(bbox[0] + bbox[2], bbox[1] + bbox[3]) + + def inverse_build(self, conditional: LongTensor) \ + -> Tuple[List[Tuple[int, Tuple[float, float]]], Optional[BoundingBox]]: + conditional_list = conditional.tolist() + crop_coordinates = None + if self.encode_crop: + crop_coordinates = self.bbox_from_token_pair(conditional_list[-2], conditional_list[-1]) + conditional_list = conditional_list[:-2] + table_of_content = grouper(conditional_list, self.object_descriptor_length) + assert conditional.shape[0] == self.embedding_dim + return [ + (object_tuple[0], self.coordinates_from_token(object_tuple[1])) + for object_tuple in table_of_content if object_tuple[0] != self.none + ], crop_coordinates + + def plot(self, conditional: LongTensor, label_for_category_no: Callable[[int], str], figure_size: Tuple[int, int], + line_width: int = 3, font_size: Optional[int] = None) -> Tensor: + plot = pil_image.new('RGB', figure_size, WHITE) + draw = pil_img_draw.Draw(plot) + circle_size = get_circle_size(figure_size) + font = ImageFont.truetype('/usr/share/fonts/truetype/lato/Lato-Regular.ttf', + size=get_plot_font_size(font_size, figure_size)) + width, height = plot.size + description, crop_coordinates = self.inverse_build(conditional) + for (representation, (x, y)), color in zip(description, cycle(COLOR_PALETTE)): + x_abs, y_abs = x * width, y * height + ann = self.representation_to_annotation(representation) + label = label_for_category_no(ann.category_no) + ' ' + additional_parameters_string(ann) + ellipse_bbox = [x_abs - circle_size, y_abs - circle_size, x_abs + circle_size, y_abs + circle_size] + draw.ellipse(ellipse_bbox, fill=color, width=0) + draw.text((x_abs, y_abs), label, anchor='md', fill=BLACK, font=font) + if crop_coordinates is not None: + draw.rectangle(absolute_bbox(crop_coordinates, width, height), outline=GRAY_75, width=line_width) + return convert_pil_to_tensor(plot) / 127.5 - 1. + + def object_representation(self, annotation: Annotation) -> int: + modifier = 0 + if self.use_group_parameter: + modifier |= 1 * (annotation.is_group_of is True) + if self.use_additional_parameters: + modifier |= 2 * (annotation.is_occluded is True) + modifier |= 4 * (annotation.is_depiction is True) + modifier |= 8 * (annotation.is_inside is True) + return annotation.category_no + self.no_object_classes * modifier + + def representation_to_annotation(self, representation: int) -> Annotation: + category_no = representation % self.no_object_classes + modifier = representation // self.no_object_classes + # noinspection PyTypeChecker + return Annotation( + area=None, image_id=None, bbox=None, category_id=None, id=None, source=None, confidence=None, + category_no=category_no, + is_group_of=bool((modifier & 1) * self.use_group_parameter), + is_occluded=bool((modifier & 2) * self.use_additional_parameters), + is_depiction=bool((modifier & 4) * self.use_additional_parameters), + is_inside=bool((modifier & 8) * self.use_additional_parameters) + ) + + def _crop_encoder(self, crop_coordinates: BoundingBox) -> List[int]: + return list(self.token_pair_from_bbox(crop_coordinates)) + + def _make_object_descriptors(self, annotations: List[Annotation]) -> List[Tuple[int, ...]]: + object_tuples = [ + (self.object_representation(a), + self.tokenize_coordinates(a.bbox[0] + a.bbox[2] / 2, a.bbox[1] + a.bbox[3] / 2)) + for a in annotations + ] + empty_tuple = (self.none, self.none) + object_tuples = pad_list(object_tuples, empty_tuple, self.no_max_objects) + return object_tuples + + def build(self, annotations: List, crop_coordinates: Optional[BoundingBox] = None, horizontal_flip: bool = False) \ + -> LongTensor: + if len(annotations) == 0: + warnings.warn('Did not receive any annotations.') + if len(annotations) > self.no_max_objects: + warnings.warn('Received more annotations than allowed.') + annotations = annotations[:self.no_max_objects] + + if not crop_coordinates: + crop_coordinates = FULL_CROP + + random.shuffle(annotations) + annotations = filter_annotations(annotations, crop_coordinates) + if self.encode_crop: + annotations = rescale_annotations(annotations, FULL_CROP, horizontal_flip) + if horizontal_flip: + crop_coordinates = horizontally_flip_bbox(crop_coordinates) + extra = self._crop_encoder(crop_coordinates) + else: + annotations = rescale_annotations(annotations, crop_coordinates, horizontal_flip) + extra = [] + + object_tuples = self._make_object_descriptors(annotations) + flattened = [token for tuple_ in object_tuples for token in tuple_] + extra + assert len(flattened) == self.embedding_dim + assert all(0 <= value < self.no_tokens for value in flattened) + return LongTensor(flattened) diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/conditional_builder/utils.py b/sd/stablediffusion/src/taming-transformers/taming/data/conditional_builder/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..d0ee175f2e05a80dbc71c22acbecb22dddadbb42 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/conditional_builder/utils.py @@ -0,0 +1,105 @@ +import importlib +from typing import List, Any, Tuple, Optional + +from taming.data.helper_types import BoundingBox, Annotation + +# source: seaborn, color palette tab10 +COLOR_PALETTE = [(30, 118, 179), (255, 126, 13), (43, 159, 43), (213, 38, 39), (147, 102, 188), + (139, 85, 74), (226, 118, 193), (126, 126, 126), (187, 188, 33), (22, 189, 206)] +BLACK = (0, 0, 0) +GRAY_75 = (63, 63, 63) +GRAY_50 = (127, 127, 127) +GRAY_25 = (191, 191, 191) +WHITE = (255, 255, 255) +FULL_CROP = (0., 0., 1., 1.) + + +def intersection_area(rectangle1: BoundingBox, rectangle2: BoundingBox) -> float: + """ + Give intersection area of two rectangles. + @param rectangle1: (x0, y0, w, h) of first rectangle + @param rectangle2: (x0, y0, w, h) of second rectangle + """ + rectangle1 = rectangle1[0], rectangle1[1], rectangle1[0] + rectangle1[2], rectangle1[1] + rectangle1[3] + rectangle2 = rectangle2[0], rectangle2[1], rectangle2[0] + rectangle2[2], rectangle2[1] + rectangle2[3] + x_overlap = max(0., min(rectangle1[2], rectangle2[2]) - max(rectangle1[0], rectangle2[0])) + y_overlap = max(0., min(rectangle1[3], rectangle2[3]) - max(rectangle1[1], rectangle2[1])) + return x_overlap * y_overlap + + +def horizontally_flip_bbox(bbox: BoundingBox) -> BoundingBox: + return 1 - (bbox[0] + bbox[2]), bbox[1], bbox[2], bbox[3] + + +def absolute_bbox(relative_bbox: BoundingBox, width: int, height: int) -> Tuple[int, int, int, int]: + bbox = relative_bbox + bbox = bbox[0] * width, bbox[1] * height, (bbox[0] + bbox[2]) * width, (bbox[1] + bbox[3]) * height + return int(bbox[0]), int(bbox[1]), int(bbox[2]), int(bbox[3]) + + +def pad_list(list_: List, pad_element: Any, pad_to_length: int) -> List: + return list_ + [pad_element for _ in range(pad_to_length - len(list_))] + + +def rescale_annotations(annotations: List[Annotation], crop_coordinates: BoundingBox, flip: bool) -> \ + List[Annotation]: + def clamp(x: float): + return max(min(x, 1.), 0.) + + def rescale_bbox(bbox: BoundingBox) -> BoundingBox: + x0 = clamp((bbox[0] - crop_coordinates[0]) / crop_coordinates[2]) + y0 = clamp((bbox[1] - crop_coordinates[1]) / crop_coordinates[3]) + w = min(bbox[2] / crop_coordinates[2], 1 - x0) + h = min(bbox[3] / crop_coordinates[3], 1 - y0) + if flip: + x0 = 1 - (x0 + w) + return x0, y0, w, h + + return [a._replace(bbox=rescale_bbox(a.bbox)) for a in annotations] + + +def filter_annotations(annotations: List[Annotation], crop_coordinates: BoundingBox) -> List: + return [a for a in annotations if intersection_area(a.bbox, crop_coordinates) > 0.0] + + +def additional_parameters_string(annotation: Annotation, short: bool = True) -> str: + sl = slice(1) if short else slice(None) + string = '' + if not (annotation.is_group_of or annotation.is_occluded or annotation.is_depiction or annotation.is_inside): + return string + if annotation.is_group_of: + string += 'group'[sl] + ',' + if annotation.is_occluded: + string += 'occluded'[sl] + ',' + if annotation.is_depiction: + string += 'depiction'[sl] + ',' + if annotation.is_inside: + string += 'inside'[sl] + return '(' + string.strip(",") + ')' + + +def get_plot_font_size(font_size: Optional[int], figure_size: Tuple[int, int]) -> int: + if font_size is None: + font_size = 10 + if max(figure_size) >= 256: + font_size = 12 + if max(figure_size) >= 512: + font_size = 15 + return font_size + + +def get_circle_size(figure_size: Tuple[int, int]) -> int: + circle_size = 2 + if max(figure_size) >= 256: + circle_size = 3 + if max(figure_size) >= 512: + circle_size = 4 + return circle_size + + +def load_object_from_string(object_string: str) -> Any: + """ + Source: https://stackoverflow.com/a/10773699 + """ + module_name, class_name = object_string.rsplit(".", 1) + return getattr(importlib.import_module(module_name), class_name) diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/custom.py b/sd/stablediffusion/src/taming-transformers/taming/data/custom.py new file mode 100644 index 0000000000000000000000000000000000000000..33f302a4b55ba1e8ec282ec3292b6263c06dfb91 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/custom.py @@ -0,0 +1,38 @@ +import os +import numpy as np +import albumentations +from torch.utils.data import Dataset + +from taming.data.base import ImagePaths, NumpyPaths, ConcatDatasetWithIndex + + +class CustomBase(Dataset): + def __init__(self, *args, **kwargs): + super().__init__() + self.data = None + + def __len__(self): + return len(self.data) + + def __getitem__(self, i): + example = self.data[i] + return example + + + +class CustomTrain(CustomBase): + def __init__(self, size, training_images_list_file): + super().__init__() + with open(training_images_list_file, "r") as f: + paths = f.read().splitlines() + self.data = ImagePaths(paths=paths, size=size, random_crop=False) + + +class CustomTest(CustomBase): + def __init__(self, size, test_images_list_file): + super().__init__() + with open(test_images_list_file, "r") as f: + paths = f.read().splitlines() + self.data = ImagePaths(paths=paths, size=size, random_crop=False) + + diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/faceshq.py b/sd/stablediffusion/src/taming-transformers/taming/data/faceshq.py new file mode 100644 index 0000000000000000000000000000000000000000..6912d04b66a6d464c1078e4b51d5da290f5e767e --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/faceshq.py @@ -0,0 +1,134 @@ +import os +import numpy as np +import albumentations +from torch.utils.data import Dataset + +from taming.data.base import ImagePaths, NumpyPaths, ConcatDatasetWithIndex + + +class FacesBase(Dataset): + def __init__(self, *args, **kwargs): + super().__init__() + self.data = None + self.keys = None + + def __len__(self): + return len(self.data) + + def __getitem__(self, i): + example = self.data[i] + ex = {} + if self.keys is not None: + for k in self.keys: + ex[k] = example[k] + else: + ex = example + return ex + + +class CelebAHQTrain(FacesBase): + def __init__(self, size, keys=None): + super().__init__() + root = "data/celebahq" + with open("data/celebahqtrain.txt", "r") as f: + relpaths = f.read().splitlines() + paths = [os.path.join(root, relpath) for relpath in relpaths] + self.data = NumpyPaths(paths=paths, size=size, random_crop=False) + self.keys = keys + + +class CelebAHQValidation(FacesBase): + def __init__(self, size, keys=None): + super().__init__() + root = "data/celebahq" + with open("data/celebahqvalidation.txt", "r") as f: + relpaths = f.read().splitlines() + paths = [os.path.join(root, relpath) for relpath in relpaths] + self.data = NumpyPaths(paths=paths, size=size, random_crop=False) + self.keys = keys + + +class FFHQTrain(FacesBase): + def __init__(self, size, keys=None): + super().__init__() + root = "data/ffhq" + with open("data/ffhqtrain.txt", "r") as f: + relpaths = f.read().splitlines() + paths = [os.path.join(root, relpath) for relpath in relpaths] + self.data = ImagePaths(paths=paths, size=size, random_crop=False) + self.keys = keys + + +class FFHQValidation(FacesBase): + def __init__(self, size, keys=None): + super().__init__() + root = "data/ffhq" + with open("data/ffhqvalidation.txt", "r") as f: + relpaths = f.read().splitlines() + paths = [os.path.join(root, relpath) for relpath in relpaths] + self.data = ImagePaths(paths=paths, size=size, random_crop=False) + self.keys = keys + + +class FacesHQTrain(Dataset): + # CelebAHQ [0] + FFHQ [1] + def __init__(self, size, keys=None, crop_size=None, coord=False): + d1 = CelebAHQTrain(size=size, keys=keys) + d2 = FFHQTrain(size=size, keys=keys) + self.data = ConcatDatasetWithIndex([d1, d2]) + self.coord = coord + if crop_size is not None: + self.cropper = albumentations.RandomCrop(height=crop_size,width=crop_size) + if self.coord: + self.cropper = albumentations.Compose([self.cropper], + additional_targets={"coord": "image"}) + + def __len__(self): + return len(self.data) + + def __getitem__(self, i): + ex, y = self.data[i] + if hasattr(self, "cropper"): + if not self.coord: + out = self.cropper(image=ex["image"]) + ex["image"] = out["image"] + else: + h,w,_ = ex["image"].shape + coord = np.arange(h*w).reshape(h,w,1)/(h*w) + out = self.cropper(image=ex["image"], coord=coord) + ex["image"] = out["image"] + ex["coord"] = out["coord"] + ex["class"] = y + return ex + + +class FacesHQValidation(Dataset): + # CelebAHQ [0] + FFHQ [1] + def __init__(self, size, keys=None, crop_size=None, coord=False): + d1 = CelebAHQValidation(size=size, keys=keys) + d2 = FFHQValidation(size=size, keys=keys) + self.data = ConcatDatasetWithIndex([d1, d2]) + self.coord = coord + if crop_size is not None: + self.cropper = albumentations.CenterCrop(height=crop_size,width=crop_size) + if self.coord: + self.cropper = albumentations.Compose([self.cropper], + additional_targets={"coord": "image"}) + + def __len__(self): + return len(self.data) + + def __getitem__(self, i): + ex, y = self.data[i] + if hasattr(self, "cropper"): + if not self.coord: + out = self.cropper(image=ex["image"]) + ex["image"] = out["image"] + else: + h,w,_ = ex["image"].shape + coord = np.arange(h*w).reshape(h,w,1)/(h*w) + out = self.cropper(image=ex["image"], coord=coord) + ex["image"] = out["image"] + ex["coord"] = out["coord"] + ex["class"] = y + return ex diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/helper_types.py b/sd/stablediffusion/src/taming-transformers/taming/data/helper_types.py new file mode 100644 index 0000000000000000000000000000000000000000..fb51e301da08602cfead5961c4f7e1d89f6aba79 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/helper_types.py @@ -0,0 +1,49 @@ +from typing import Dict, Tuple, Optional, NamedTuple, Union +from PIL.Image import Image as pil_image +from torch import Tensor + +try: + from typing import Literal +except ImportError: + from typing_extensions import Literal + +Image = Union[Tensor, pil_image] +BoundingBox = Tuple[float, float, float, float] # x0, y0, w, h +CropMethodType = Literal['none', 'random', 'center', 'random-2d'] +SplitType = Literal['train', 'validation', 'test'] + + +class ImageDescription(NamedTuple): + id: int + file_name: str + original_size: Tuple[int, int] # w, h + url: Optional[str] = None + license: Optional[int] = None + coco_url: Optional[str] = None + date_captured: Optional[str] = None + flickr_url: Optional[str] = None + flickr_id: Optional[str] = None + coco_id: Optional[str] = None + + +class Category(NamedTuple): + id: str + super_category: Optional[str] + name: str + + +class Annotation(NamedTuple): + area: float + image_id: str + bbox: BoundingBox + category_no: int + category_id: str + id: Optional[int] = None + source: Optional[str] = None + confidence: Optional[float] = None + is_group_of: Optional[bool] = None + is_truncated: Optional[bool] = None + is_occluded: Optional[bool] = None + is_depiction: Optional[bool] = None + is_inside: Optional[bool] = None + segmentation: Optional[Dict] = None diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/image_transforms.py b/sd/stablediffusion/src/taming-transformers/taming/data/image_transforms.py new file mode 100644 index 0000000000000000000000000000000000000000..657ac332174e0ac72f68315271ffbd757b771a0f --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/image_transforms.py @@ -0,0 +1,132 @@ +import random +import warnings +from typing import Union + +import torch +from torch import Tensor +from torchvision.transforms import RandomCrop, functional as F, CenterCrop, RandomHorizontalFlip, PILToTensor +from torchvision.transforms.functional import _get_image_size as get_image_size + +from taming.data.helper_types import BoundingBox, Image + +pil_to_tensor = PILToTensor() + + +def convert_pil_to_tensor(image: Image) -> Tensor: + with warnings.catch_warnings(): + # to filter PyTorch UserWarning as described here: https://github.com/pytorch/vision/issues/2194 + warnings.simplefilter("ignore") + return pil_to_tensor(image) + + +class RandomCrop1dReturnCoordinates(RandomCrop): + def forward(self, img: Image) -> (BoundingBox, Image): + """ + Additionally to cropping, returns the relative coordinates of the crop bounding box. + Args: + img (PIL Image or Tensor): Image to be cropped. + + Returns: + Bounding box: x0, y0, w, h + PIL Image or Tensor: Cropped image. + + Based on: + torchvision.transforms.RandomCrop, torchvision 1.7.0 + """ + if self.padding is not None: + img = F.pad(img, self.padding, self.fill, self.padding_mode) + + width, height = get_image_size(img) + # pad the width if needed + if self.pad_if_needed and width < self.size[1]: + padding = [self.size[1] - width, 0] + img = F.pad(img, padding, self.fill, self.padding_mode) + # pad the height if needed + if self.pad_if_needed and height < self.size[0]: + padding = [0, self.size[0] - height] + img = F.pad(img, padding, self.fill, self.padding_mode) + + i, j, h, w = self.get_params(img, self.size) + bbox = (j / width, i / height, w / width, h / height) # x0, y0, w, h + return bbox, F.crop(img, i, j, h, w) + + +class Random2dCropReturnCoordinates(torch.nn.Module): + """ + Additionally to cropping, returns the relative coordinates of the crop bounding box. + Args: + img (PIL Image or Tensor): Image to be cropped. + + Returns: + Bounding box: x0, y0, w, h + PIL Image or Tensor: Cropped image. + + Based on: + torchvision.transforms.RandomCrop, torchvision 1.7.0 + """ + + def __init__(self, min_size: int): + super().__init__() + self.min_size = min_size + + def forward(self, img: Image) -> (BoundingBox, Image): + width, height = get_image_size(img) + max_size = min(width, height) + if max_size <= self.min_size: + size = max_size + else: + size = random.randint(self.min_size, max_size) + top = random.randint(0, height - size) + left = random.randint(0, width - size) + bbox = left / width, top / height, size / width, size / height + return bbox, F.crop(img, top, left, size, size) + + +class CenterCropReturnCoordinates(CenterCrop): + @staticmethod + def get_bbox_of_center_crop(width: int, height: int) -> BoundingBox: + if width > height: + w = height / width + h = 1.0 + x0 = 0.5 - w / 2 + y0 = 0. + else: + w = 1.0 + h = width / height + x0 = 0. + y0 = 0.5 - h / 2 + return x0, y0, w, h + + def forward(self, img: Union[Image, Tensor]) -> (BoundingBox, Union[Image, Tensor]): + """ + Additionally to cropping, returns the relative coordinates of the crop bounding box. + Args: + img (PIL Image or Tensor): Image to be cropped. + + Returns: + Bounding box: x0, y0, w, h + PIL Image or Tensor: Cropped image. + Based on: + torchvision.transforms.RandomHorizontalFlip (version 1.7.0) + """ + width, height = get_image_size(img) + return self.get_bbox_of_center_crop(width, height), F.center_crop(img, self.size) + + +class RandomHorizontalFlipReturn(RandomHorizontalFlip): + def forward(self, img: Image) -> (bool, Image): + """ + Additionally to flipping, returns a boolean whether it was flipped or not. + Args: + img (PIL Image or Tensor): Image to be flipped. + + Returns: + flipped: whether the image was flipped or not + PIL Image or Tensor: Randomly flipped image. + + Based on: + torchvision.transforms.RandomHorizontalFlip (version 1.7.0) + """ + if torch.rand(1) < self.p: + return True, F.hflip(img) + return False, img diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/imagenet.py b/sd/stablediffusion/src/taming-transformers/taming/data/imagenet.py new file mode 100644 index 0000000000000000000000000000000000000000..9a02ec44ba4af9e993f58c91fa43482a4ecbe54c --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/imagenet.py @@ -0,0 +1,558 @@ +import os, tarfile, glob, shutil +import yaml +import numpy as np +from tqdm import tqdm +from PIL import Image +import albumentations +from omegaconf import OmegaConf +from torch.utils.data import Dataset + +from taming.data.base import ImagePaths +from taming.util import download, retrieve +import taming.data.utils as bdu + + +def give_synsets_from_indices(indices, path_to_yaml="data/imagenet_idx_to_synset.yaml"): + synsets = [] + with open(path_to_yaml) as f: + di2s = yaml.load(f) + for idx in indices: + synsets.append(str(di2s[idx])) + print("Using {} different synsets for construction of Restriced Imagenet.".format(len(synsets))) + return synsets + + +def str_to_indices(string): + """Expects a string in the format '32-123, 256, 280-321'""" + assert not string.endswith(","), "provided string '{}' ends with a comma, pls remove it".format(string) + subs = string.split(",") + indices = [] + for sub in subs: + subsubs = sub.split("-") + assert len(subsubs) > 0 + if len(subsubs) == 1: + indices.append(int(subsubs[0])) + else: + rang = [j for j in range(int(subsubs[0]), int(subsubs[1]))] + indices.extend(rang) + return sorted(indices) + + +class ImageNetBase(Dataset): + def __init__(self, config=None): + self.config = config or OmegaConf.create() + if not type(self.config)==dict: + self.config = OmegaConf.to_container(self.config) + self._prepare() + self._prepare_synset_to_human() + self._prepare_idx_to_synset() + self._load() + + def __len__(self): + return len(self.data) + + def __getitem__(self, i): + return self.data[i] + + def _prepare(self): + raise NotImplementedError() + + def _filter_relpaths(self, relpaths): + ignore = set([ + "n06596364_9591.JPEG", + ]) + relpaths = [rpath for rpath in relpaths if not rpath.split("/")[-1] in ignore] + if "sub_indices" in self.config: + indices = str_to_indices(self.config["sub_indices"]) + synsets = give_synsets_from_indices(indices, path_to_yaml=self.idx2syn) # returns a list of strings + files = [] + for rpath in relpaths: + syn = rpath.split("/")[0] + if syn in synsets: + files.append(rpath) + return files + else: + return relpaths + + def _prepare_synset_to_human(self): + SIZE = 2655750 + URL = "https://heibox.uni-heidelberg.de/f/9f28e956cd304264bb82/?dl=1" + self.human_dict = os.path.join(self.root, "synset_human.txt") + if (not os.path.exists(self.human_dict) or + not os.path.getsize(self.human_dict)==SIZE): + download(URL, self.human_dict) + + def _prepare_idx_to_synset(self): + URL = "https://heibox.uni-heidelberg.de/f/d835d5b6ceda4d3aa910/?dl=1" + self.idx2syn = os.path.join(self.root, "index_synset.yaml") + if (not os.path.exists(self.idx2syn)): + download(URL, self.idx2syn) + + def _load(self): + with open(self.txt_filelist, "r") as f: + self.relpaths = f.read().splitlines() + l1 = len(self.relpaths) + self.relpaths = self._filter_relpaths(self.relpaths) + print("Removed {} files from filelist during filtering.".format(l1 - len(self.relpaths))) + + self.synsets = [p.split("/")[0] for p in self.relpaths] + self.abspaths = [os.path.join(self.datadir, p) for p in self.relpaths] + + unique_synsets = np.unique(self.synsets) + class_dict = dict((synset, i) for i, synset in enumerate(unique_synsets)) + self.class_labels = [class_dict[s] for s in self.synsets] + + with open(self.human_dict, "r") as f: + human_dict = f.read().splitlines() + human_dict = dict(line.split(maxsplit=1) for line in human_dict) + + self.human_labels = [human_dict[s] for s in self.synsets] + + labels = { + "relpath": np.array(self.relpaths), + "synsets": np.array(self.synsets), + "class_label": np.array(self.class_labels), + "human_label": np.array(self.human_labels), + } + self.data = ImagePaths(self.abspaths, + labels=labels, + size=retrieve(self.config, "size", default=0), + random_crop=self.random_crop) + + +class ImageNetTrain(ImageNetBase): + NAME = "ILSVRC2012_train" + URL = "http://www.image-net.org/challenges/LSVRC/2012/" + AT_HASH = "a306397ccf9c2ead27155983c254227c0fd938e2" + FILES = [ + "ILSVRC2012_img_train.tar", + ] + SIZES = [ + 147897477120, + ] + + def _prepare(self): + self.random_crop = retrieve(self.config, "ImageNetTrain/random_crop", + default=True) + cachedir = os.environ.get("XDG_CACHE_HOME", os.path.expanduser("~/.cache")) + self.root = os.path.join(cachedir, "autoencoders/data", self.NAME) + self.datadir = os.path.join(self.root, "data") + self.txt_filelist = os.path.join(self.root, "filelist.txt") + self.expected_length = 1281167 + if not bdu.is_prepared(self.root): + # prep + print("Preparing dataset {} in {}".format(self.NAME, self.root)) + + datadir = self.datadir + if not os.path.exists(datadir): + path = os.path.join(self.root, self.FILES[0]) + if not os.path.exists(path) or not os.path.getsize(path)==self.SIZES[0]: + import academictorrents as at + atpath = at.get(self.AT_HASH, datastore=self.root) + assert atpath == path + + print("Extracting {} to {}".format(path, datadir)) + os.makedirs(datadir, exist_ok=True) + with tarfile.open(path, "r:") as tar: + tar.extractall(path=datadir) + + print("Extracting sub-tars.") + subpaths = sorted(glob.glob(os.path.join(datadir, "*.tar"))) + for subpath in tqdm(subpaths): + subdir = subpath[:-len(".tar")] + os.makedirs(subdir, exist_ok=True) + with tarfile.open(subpath, "r:") as tar: + tar.extractall(path=subdir) + + + filelist = glob.glob(os.path.join(datadir, "**", "*.JPEG")) + filelist = [os.path.relpath(p, start=datadir) for p in filelist] + filelist = sorted(filelist) + filelist = "\n".join(filelist)+"\n" + with open(self.txt_filelist, "w") as f: + f.write(filelist) + + bdu.mark_prepared(self.root) + + +class ImageNetValidation(ImageNetBase): + NAME = "ILSVRC2012_validation" + URL = "http://www.image-net.org/challenges/LSVRC/2012/" + AT_HASH = "5d6d0df7ed81efd49ca99ea4737e0ae5e3a5f2e5" + VS_URL = "https://heibox.uni-heidelberg.de/f/3e0f6e9c624e45f2bd73/?dl=1" + FILES = [ + "ILSVRC2012_img_val.tar", + "validation_synset.txt", + ] + SIZES = [ + 6744924160, + 1950000, + ] + + def _prepare(self): + self.random_crop = retrieve(self.config, "ImageNetValidation/random_crop", + default=False) + cachedir = os.environ.get("XDG_CACHE_HOME", os.path.expanduser("~/.cache")) + self.root = os.path.join(cachedir, "autoencoders/data", self.NAME) + self.datadir = os.path.join(self.root, "data") + self.txt_filelist = os.path.join(self.root, "filelist.txt") + self.expected_length = 50000 + if not bdu.is_prepared(self.root): + # prep + print("Preparing dataset {} in {}".format(self.NAME, self.root)) + + datadir = self.datadir + if not os.path.exists(datadir): + path = os.path.join(self.root, self.FILES[0]) + if not os.path.exists(path) or not os.path.getsize(path)==self.SIZES[0]: + import academictorrents as at + atpath = at.get(self.AT_HASH, datastore=self.root) + assert atpath == path + + print("Extracting {} to {}".format(path, datadir)) + os.makedirs(datadir, exist_ok=True) + with tarfile.open(path, "r:") as tar: + tar.extractall(path=datadir) + + vspath = os.path.join(self.root, self.FILES[1]) + if not os.path.exists(vspath) or not os.path.getsize(vspath)==self.SIZES[1]: + download(self.VS_URL, vspath) + + with open(vspath, "r") as f: + synset_dict = f.read().splitlines() + synset_dict = dict(line.split() for line in synset_dict) + + print("Reorganizing into synset folders") + synsets = np.unique(list(synset_dict.values())) + for s in synsets: + os.makedirs(os.path.join(datadir, s), exist_ok=True) + for k, v in synset_dict.items(): + src = os.path.join(datadir, k) + dst = os.path.join(datadir, v) + shutil.move(src, dst) + + filelist = glob.glob(os.path.join(datadir, "**", "*.JPEG")) + filelist = [os.path.relpath(p, start=datadir) for p in filelist] + filelist = sorted(filelist) + filelist = "\n".join(filelist)+"\n" + with open(self.txt_filelist, "w") as f: + f.write(filelist) + + bdu.mark_prepared(self.root) + + +def get_preprocessor(size=None, random_crop=False, additional_targets=None, + crop_size=None): + if size is not None and size > 0: + transforms = list() + rescaler = albumentations.SmallestMaxSize(max_size = size) + transforms.append(rescaler) + if not random_crop: + cropper = albumentations.CenterCrop(height=size,width=size) + transforms.append(cropper) + else: + cropper = albumentations.RandomCrop(height=size,width=size) + transforms.append(cropper) + flipper = albumentations.HorizontalFlip() + transforms.append(flipper) + preprocessor = albumentations.Compose(transforms, + additional_targets=additional_targets) + elif crop_size is not None and crop_size > 0: + if not random_crop: + cropper = albumentations.CenterCrop(height=crop_size,width=crop_size) + else: + cropper = albumentations.RandomCrop(height=crop_size,width=crop_size) + transforms = [cropper] + preprocessor = albumentations.Compose(transforms, + additional_targets=additional_targets) + else: + preprocessor = lambda **kwargs: kwargs + return preprocessor + + +def rgba_to_depth(x): + assert x.dtype == np.uint8 + assert len(x.shape) == 3 and x.shape[2] == 4 + y = x.copy() + y.dtype = np.float32 + y = y.reshape(x.shape[:2]) + return np.ascontiguousarray(y) + + +class BaseWithDepth(Dataset): + DEFAULT_DEPTH_ROOT="data/imagenet_depth" + + def __init__(self, config=None, size=None, random_crop=False, + crop_size=None, root=None): + self.config = config + self.base_dset = self.get_base_dset() + self.preprocessor = get_preprocessor( + size=size, + crop_size=crop_size, + random_crop=random_crop, + additional_targets={"depth": "image"}) + self.crop_size = crop_size + if self.crop_size is not None: + self.rescaler = albumentations.Compose( + [albumentations.SmallestMaxSize(max_size = self.crop_size)], + additional_targets={"depth": "image"}) + if root is not None: + self.DEFAULT_DEPTH_ROOT = root + + def __len__(self): + return len(self.base_dset) + + def preprocess_depth(self, path): + rgba = np.array(Image.open(path)) + depth = rgba_to_depth(rgba) + depth = (depth - depth.min())/max(1e-8, depth.max()-depth.min()) + depth = 2.0*depth-1.0 + return depth + + def __getitem__(self, i): + e = self.base_dset[i] + e["depth"] = self.preprocess_depth(self.get_depth_path(e)) + # up if necessary + h,w,c = e["image"].shape + if self.crop_size and min(h,w) < self.crop_size: + # have to upscale to be able to crop - this just uses bilinear + out = self.rescaler(image=e["image"], depth=e["depth"]) + e["image"] = out["image"] + e["depth"] = out["depth"] + transformed = self.preprocessor(image=e["image"], depth=e["depth"]) + e["image"] = transformed["image"] + e["depth"] = transformed["depth"] + return e + + +class ImageNetTrainWithDepth(BaseWithDepth): + # default to random_crop=True + def __init__(self, random_crop=True, sub_indices=None, **kwargs): + self.sub_indices = sub_indices + super().__init__(random_crop=random_crop, **kwargs) + + def get_base_dset(self): + if self.sub_indices is None: + return ImageNetTrain() + else: + return ImageNetTrain({"sub_indices": self.sub_indices}) + + def get_depth_path(self, e): + fid = os.path.splitext(e["relpath"])[0]+".png" + fid = os.path.join(self.DEFAULT_DEPTH_ROOT, "train", fid) + return fid + + +class ImageNetValidationWithDepth(BaseWithDepth): + def __init__(self, sub_indices=None, **kwargs): + self.sub_indices = sub_indices + super().__init__(**kwargs) + + def get_base_dset(self): + if self.sub_indices is None: + return ImageNetValidation() + else: + return ImageNetValidation({"sub_indices": self.sub_indices}) + + def get_depth_path(self, e): + fid = os.path.splitext(e["relpath"])[0]+".png" + fid = os.path.join(self.DEFAULT_DEPTH_ROOT, "val", fid) + return fid + + +class RINTrainWithDepth(ImageNetTrainWithDepth): + def __init__(self, config=None, size=None, random_crop=True, crop_size=None): + sub_indices = "30-32, 33-37, 151-268, 281-285, 80-100, 365-382, 389-397, 118-121, 300-319" + super().__init__(config=config, size=size, random_crop=random_crop, + sub_indices=sub_indices, crop_size=crop_size) + + +class RINValidationWithDepth(ImageNetValidationWithDepth): + def __init__(self, config=None, size=None, random_crop=False, crop_size=None): + sub_indices = "30-32, 33-37, 151-268, 281-285, 80-100, 365-382, 389-397, 118-121, 300-319" + super().__init__(config=config, size=size, random_crop=random_crop, + sub_indices=sub_indices, crop_size=crop_size) + + +class DRINExamples(Dataset): + def __init__(self): + self.preprocessor = get_preprocessor(size=256, additional_targets={"depth": "image"}) + with open("data/drin_examples.txt", "r") as f: + relpaths = f.read().splitlines() + self.image_paths = [os.path.join("data/drin_images", + relpath) for relpath in relpaths] + self.depth_paths = [os.path.join("data/drin_depth", + relpath.replace(".JPEG", ".png")) for relpath in relpaths] + + def __len__(self): + return len(self.image_paths) + + def preprocess_image(self, image_path): + image = Image.open(image_path) + if not image.mode == "RGB": + image = image.convert("RGB") + image = np.array(image).astype(np.uint8) + image = self.preprocessor(image=image)["image"] + image = (image/127.5 - 1.0).astype(np.float32) + return image + + def preprocess_depth(self, path): + rgba = np.array(Image.open(path)) + depth = rgba_to_depth(rgba) + depth = (depth - depth.min())/max(1e-8, depth.max()-depth.min()) + depth = 2.0*depth-1.0 + return depth + + def __getitem__(self, i): + e = dict() + e["image"] = self.preprocess_image(self.image_paths[i]) + e["depth"] = self.preprocess_depth(self.depth_paths[i]) + transformed = self.preprocessor(image=e["image"], depth=e["depth"]) + e["image"] = transformed["image"] + e["depth"] = transformed["depth"] + return e + + +def imscale(x, factor, keepshapes=False, keepmode="bicubic"): + if factor is None or factor==1: + return x + + dtype = x.dtype + assert dtype in [np.float32, np.float64] + assert x.min() >= -1 + assert x.max() <= 1 + + keepmode = {"nearest": Image.NEAREST, "bilinear": Image.BILINEAR, + "bicubic": Image.BICUBIC}[keepmode] + + lr = (x+1.0)*127.5 + lr = lr.clip(0,255).astype(np.uint8) + lr = Image.fromarray(lr) + + h, w, _ = x.shape + nh = h//factor + nw = w//factor + assert nh > 0 and nw > 0, (nh, nw) + + lr = lr.resize((nw,nh), Image.BICUBIC) + if keepshapes: + lr = lr.resize((w,h), keepmode) + lr = np.array(lr)/127.5-1.0 + lr = lr.astype(dtype) + + return lr + + +class ImageNetScale(Dataset): + def __init__(self, size=None, crop_size=None, random_crop=False, + up_factor=None, hr_factor=None, keep_mode="bicubic"): + self.base = self.get_base() + + self.size = size + self.crop_size = crop_size if crop_size is not None else self.size + self.random_crop = random_crop + self.up_factor = up_factor + self.hr_factor = hr_factor + self.keep_mode = keep_mode + + transforms = list() + + if self.size is not None and self.size > 0: + rescaler = albumentations.SmallestMaxSize(max_size = self.size) + self.rescaler = rescaler + transforms.append(rescaler) + + if self.crop_size is not None and self.crop_size > 0: + if len(transforms) == 0: + self.rescaler = albumentations.SmallestMaxSize(max_size = self.crop_size) + + if not self.random_crop: + cropper = albumentations.CenterCrop(height=self.crop_size,width=self.crop_size) + else: + cropper = albumentations.RandomCrop(height=self.crop_size,width=self.crop_size) + transforms.append(cropper) + + if len(transforms) > 0: + if self.up_factor is not None: + additional_targets = {"lr": "image"} + else: + additional_targets = None + self.preprocessor = albumentations.Compose(transforms, + additional_targets=additional_targets) + else: + self.preprocessor = lambda **kwargs: kwargs + + def __len__(self): + return len(self.base) + + def __getitem__(self, i): + example = self.base[i] + image = example["image"] + # adjust resolution + image = imscale(image, self.hr_factor, keepshapes=False) + h,w,c = image.shape + if self.crop_size and min(h,w) < self.crop_size: + # have to upscale to be able to crop - this just uses bilinear + image = self.rescaler(image=image)["image"] + if self.up_factor is None: + image = self.preprocessor(image=image)["image"] + example["image"] = image + else: + lr = imscale(image, self.up_factor, keepshapes=True, + keepmode=self.keep_mode) + + out = self.preprocessor(image=image, lr=lr) + example["image"] = out["image"] + example["lr"] = out["lr"] + + return example + +class ImageNetScaleTrain(ImageNetScale): + def __init__(self, random_crop=True, **kwargs): + super().__init__(random_crop=random_crop, **kwargs) + + def get_base(self): + return ImageNetTrain() + +class ImageNetScaleValidation(ImageNetScale): + def get_base(self): + return ImageNetValidation() + + +from skimage.feature import canny +from skimage.color import rgb2gray + + +class ImageNetEdges(ImageNetScale): + def __init__(self, up_factor=1, **kwargs): + super().__init__(up_factor=1, **kwargs) + + def __getitem__(self, i): + example = self.base[i] + image = example["image"] + h,w,c = image.shape + if self.crop_size and min(h,w) < self.crop_size: + # have to upscale to be able to crop - this just uses bilinear + image = self.rescaler(image=image)["image"] + + lr = canny(rgb2gray(image), sigma=2) + lr = lr.astype(np.float32) + lr = lr[:,:,None][:,:,[0,0,0]] + + out = self.preprocessor(image=image, lr=lr) + example["image"] = out["image"] + example["lr"] = out["lr"] + + return example + + +class ImageNetEdgesTrain(ImageNetEdges): + def __init__(self, random_crop=True, **kwargs): + super().__init__(random_crop=random_crop, **kwargs) + + def get_base(self): + return ImageNetTrain() + +class ImageNetEdgesValidation(ImageNetEdges): + def get_base(self): + return ImageNetValidation() diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/open_images_helper.py b/sd/stablediffusion/src/taming-transformers/taming/data/open_images_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..8feb7c6e705fc165d2983303192aaa88f579b243 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/open_images_helper.py @@ -0,0 +1,379 @@ +open_images_unify_categories_for_coco = { + '/m/03bt1vf': '/m/01g317', + '/m/04yx4': '/m/01g317', + '/m/05r655': '/m/01g317', + '/m/01bl7v': '/m/01g317', + '/m/0cnyhnx': '/m/01xq0k1', + '/m/01226z': '/m/018xm', + '/m/05ctyq': '/m/018xm', + '/m/058qzx': '/m/04ctx', + '/m/06pcq': '/m/0l515', + '/m/03m3pdh': '/m/02crq1', + '/m/046dlr': '/m/01x3z', + '/m/0h8mzrc': '/m/01x3z', +} + + +top_300_classes_plus_coco_compatibility = [ + ('Man', 1060962), + ('Clothing', 986610), + ('Tree', 748162), + ('Woman', 611896), + ('Person', 610294), + ('Human face', 442948), + ('Girl', 175399), + ('Building', 162147), + ('Car', 159135), + ('Plant', 155704), + ('Human body', 137073), + ('Flower', 133128), + ('Window', 127485), + ('Human arm', 118380), + ('House', 114365), + ('Wheel', 111684), + ('Suit', 99054), + ('Human hair', 98089), + ('Human head', 92763), + ('Chair', 88624), + ('Boy', 79849), + ('Table', 73699), + ('Jeans', 57200), + ('Tire', 55725), + ('Skyscraper', 53321), + ('Food', 52400), + ('Footwear', 50335), + ('Dress', 50236), + ('Human leg', 47124), + ('Toy', 46636), + ('Tower', 45605), + ('Boat', 43486), + ('Land vehicle', 40541), + ('Bicycle wheel', 34646), + ('Palm tree', 33729), + ('Fashion accessory', 32914), + ('Glasses', 31940), + ('Bicycle', 31409), + ('Furniture', 30656), + ('Sculpture', 29643), + ('Bottle', 27558), + ('Dog', 26980), + ('Snack', 26796), + ('Human hand', 26664), + ('Bird', 25791), + ('Book', 25415), + ('Guitar', 24386), + ('Jacket', 23998), + ('Poster', 22192), + ('Dessert', 21284), + ('Baked goods', 20657), + ('Drink', 19754), + ('Flag', 18588), + ('Houseplant', 18205), + ('Tableware', 17613), + ('Airplane', 17218), + ('Door', 17195), + ('Sports uniform', 17068), + ('Shelf', 16865), + ('Drum', 16612), + ('Vehicle', 16542), + ('Microphone', 15269), + ('Street light', 14957), + ('Cat', 14879), + ('Fruit', 13684), + ('Fast food', 13536), + ('Animal', 12932), + ('Vegetable', 12534), + ('Train', 12358), + ('Horse', 11948), + ('Flowerpot', 11728), + ('Motorcycle', 11621), + ('Fish', 11517), + ('Desk', 11405), + ('Helmet', 10996), + ('Truck', 10915), + ('Bus', 10695), + ('Hat', 10532), + ('Auto part', 10488), + ('Musical instrument', 10303), + ('Sunglasses', 10207), + ('Picture frame', 10096), + ('Sports equipment', 10015), + ('Shorts', 9999), + ('Wine glass', 9632), + ('Duck', 9242), + ('Wine', 9032), + ('Rose', 8781), + ('Tie', 8693), + ('Butterfly', 8436), + ('Beer', 7978), + ('Cabinetry', 7956), + ('Laptop', 7907), + ('Insect', 7497), + ('Goggles', 7363), + ('Shirt', 7098), + ('Dairy Product', 7021), + ('Marine invertebrates', 7014), + ('Cattle', 7006), + ('Trousers', 6903), + ('Van', 6843), + ('Billboard', 6777), + ('Balloon', 6367), + ('Human nose', 6103), + ('Tent', 6073), + ('Camera', 6014), + ('Doll', 6002), + ('Coat', 5951), + ('Mobile phone', 5758), + ('Swimwear', 5729), + ('Strawberry', 5691), + ('Stairs', 5643), + ('Goose', 5599), + ('Umbrella', 5536), + ('Cake', 5508), + ('Sun hat', 5475), + ('Bench', 5310), + ('Bookcase', 5163), + ('Bee', 5140), + ('Computer monitor', 5078), + ('Hiking equipment', 4983), + ('Office building', 4981), + ('Coffee cup', 4748), + ('Curtain', 4685), + ('Plate', 4651), + ('Box', 4621), + ('Tomato', 4595), + ('Coffee table', 4529), + ('Office supplies', 4473), + ('Maple', 4416), + ('Muffin', 4365), + ('Cocktail', 4234), + ('Castle', 4197), + ('Couch', 4134), + ('Pumpkin', 3983), + ('Computer keyboard', 3960), + ('Human mouth', 3926), + ('Christmas tree', 3893), + ('Mushroom', 3883), + ('Swimming pool', 3809), + ('Pastry', 3799), + ('Lavender (Plant)', 3769), + ('Football helmet', 3732), + ('Bread', 3648), + ('Traffic sign', 3628), + ('Common sunflower', 3597), + ('Television', 3550), + ('Bed', 3525), + ('Cookie', 3485), + ('Fountain', 3484), + ('Paddle', 3447), + ('Bicycle helmet', 3429), + ('Porch', 3420), + ('Deer', 3387), + ('Fedora', 3339), + ('Canoe', 3338), + ('Carnivore', 3266), + ('Bowl', 3202), + ('Human eye', 3166), + ('Ball', 3118), + ('Pillow', 3077), + ('Salad', 3061), + ('Beetle', 3060), + ('Orange', 3050), + ('Drawer', 2958), + ('Platter', 2937), + ('Elephant', 2921), + ('Seafood', 2921), + ('Monkey', 2915), + ('Countertop', 2879), + ('Watercraft', 2831), + ('Helicopter', 2805), + ('Kitchen appliance', 2797), + ('Personal flotation device', 2781), + ('Swan', 2739), + ('Lamp', 2711), + ('Boot', 2695), + ('Bronze sculpture', 2693), + ('Chicken', 2677), + ('Taxi', 2643), + ('Juice', 2615), + ('Cowboy hat', 2604), + ('Apple', 2600), + ('Tin can', 2590), + ('Necklace', 2564), + ('Ice cream', 2560), + ('Human beard', 2539), + ('Coin', 2536), + ('Candle', 2515), + ('Cart', 2512), + ('High heels', 2441), + ('Weapon', 2433), + ('Handbag', 2406), + ('Penguin', 2396), + ('Rifle', 2352), + ('Violin', 2336), + ('Skull', 2304), + ('Lantern', 2285), + ('Scarf', 2269), + ('Saucer', 2225), + ('Sheep', 2215), + ('Vase', 2189), + ('Lily', 2180), + ('Mug', 2154), + ('Parrot', 2140), + ('Human ear', 2137), + ('Sandal', 2115), + ('Lizard', 2100), + ('Kitchen & dining room table', 2063), + ('Spider', 1977), + ('Coffee', 1974), + ('Goat', 1926), + ('Squirrel', 1922), + ('Cello', 1913), + ('Sushi', 1881), + ('Tortoise', 1876), + ('Pizza', 1870), + ('Studio couch', 1864), + ('Barrel', 1862), + ('Cosmetics', 1841), + ('Moths and butterflies', 1841), + ('Convenience store', 1817), + ('Watch', 1792), + ('Home appliance', 1786), + ('Harbor seal', 1780), + ('Luggage and bags', 1756), + ('Vehicle registration plate', 1754), + ('Shrimp', 1751), + ('Jellyfish', 1730), + ('French fries', 1723), + ('Egg (Food)', 1698), + ('Football', 1697), + ('Musical keyboard', 1683), + ('Falcon', 1674), + ('Candy', 1660), + ('Medical equipment', 1654), + ('Eagle', 1651), + ('Dinosaur', 1634), + ('Surfboard', 1630), + ('Tank', 1628), + ('Grape', 1624), + ('Lion', 1624), + ('Owl', 1622), + ('Ski', 1613), + ('Waste container', 1606), + ('Frog', 1591), + ('Sparrow', 1585), + ('Rabbit', 1581), + ('Pen', 1546), + ('Sea lion', 1537), + ('Spoon', 1521), + ('Sink', 1512), + ('Teddy bear', 1507), + ('Bull', 1495), + ('Sofa bed', 1490), + ('Dragonfly', 1479), + ('Brassiere', 1478), + ('Chest of drawers', 1472), + ('Aircraft', 1466), + ('Human foot', 1463), + ('Pig', 1455), + ('Fork', 1454), + ('Antelope', 1438), + ('Tripod', 1427), + ('Tool', 1424), + ('Cheese', 1422), + ('Lemon', 1397), + ('Hamburger', 1393), + ('Dolphin', 1390), + ('Mirror', 1390), + ('Marine mammal', 1387), + ('Giraffe', 1385), + ('Snake', 1368), + ('Gondola', 1364), + ('Wheelchair', 1360), + ('Piano', 1358), + ('Cupboard', 1348), + ('Banana', 1345), + ('Trumpet', 1335), + ('Lighthouse', 1333), + ('Invertebrate', 1317), + ('Carrot', 1268), + ('Sock', 1260), + ('Tiger', 1241), + ('Camel', 1224), + ('Parachute', 1224), + ('Bathroom accessory', 1223), + ('Earrings', 1221), + ('Headphones', 1218), + ('Skirt', 1198), + ('Skateboard', 1190), + ('Sandwich', 1148), + ('Saxophone', 1141), + ('Goldfish', 1136), + ('Stool', 1104), + ('Traffic light', 1097), + ('Shellfish', 1081), + ('Backpack', 1079), + ('Sea turtle', 1078), + ('Cucumber', 1075), + ('Tea', 1051), + ('Toilet', 1047), + ('Roller skates', 1040), + ('Mule', 1039), + ('Bust', 1031), + ('Broccoli', 1030), + ('Crab', 1020), + ('Oyster', 1019), + ('Cannon', 1012), + ('Zebra', 1012), + ('French horn', 1008), + ('Grapefruit', 998), + ('Whiteboard', 997), + ('Zucchini', 997), + ('Crocodile', 992), + + ('Clock', 960), + ('Wall clock', 958), + + ('Doughnut', 869), + ('Snail', 868), + + ('Baseball glove', 859), + + ('Panda', 830), + ('Tennis racket', 830), + + ('Pear', 652), + + ('Bagel', 617), + ('Oven', 616), + ('Ladybug', 615), + ('Shark', 615), + ('Polar bear', 614), + ('Ostrich', 609), + + ('Hot dog', 473), + ('Microwave oven', 467), + ('Fire hydrant', 20), + ('Stop sign', 20), + ('Parking meter', 20), + ('Bear', 20), + ('Flying disc', 20), + ('Snowboard', 20), + ('Tennis ball', 20), + ('Kite', 20), + ('Baseball bat', 20), + ('Kitchen knife', 20), + ('Knife', 20), + ('Submarine sandwich', 20), + ('Computer mouse', 20), + ('Remote control', 20), + ('Toaster', 20), + ('Sink', 20), + ('Refrigerator', 20), + ('Alarm clock', 20), + ('Wall clock', 20), + ('Scissors', 20), + ('Hair dryer', 20), + ('Toothbrush', 20), + ('Suitcase', 20) +] diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/sflckr.py b/sd/stablediffusion/src/taming-transformers/taming/data/sflckr.py new file mode 100644 index 0000000000000000000000000000000000000000..91101be5953b113f1e58376af637e43f366b3dee --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/sflckr.py @@ -0,0 +1,91 @@ +import os +import numpy as np +import cv2 +import albumentations +from PIL import Image +from torch.utils.data import Dataset + + +class SegmentationBase(Dataset): + def __init__(self, + data_csv, data_root, segmentation_root, + size=None, random_crop=False, interpolation="bicubic", + n_labels=182, shift_segmentation=False, + ): + self.n_labels = n_labels + self.shift_segmentation = shift_segmentation + self.data_csv = data_csv + self.data_root = data_root + self.segmentation_root = segmentation_root + with open(self.data_csv, "r") as f: + self.image_paths = f.read().splitlines() + self._length = len(self.image_paths) + self.labels = { + "relative_file_path_": [l for l in self.image_paths], + "file_path_": [os.path.join(self.data_root, l) + for l in self.image_paths], + "segmentation_path_": [os.path.join(self.segmentation_root, l.replace(".jpg", ".png")) + for l in self.image_paths] + } + + size = None if size is not None and size<=0 else size + self.size = size + if self.size is not None: + self.interpolation = interpolation + self.interpolation = { + "nearest": cv2.INTER_NEAREST, + "bilinear": cv2.INTER_LINEAR, + "bicubic": cv2.INTER_CUBIC, + "area": cv2.INTER_AREA, + "lanczos": cv2.INTER_LANCZOS4}[self.interpolation] + self.image_rescaler = albumentations.SmallestMaxSize(max_size=self.size, + interpolation=self.interpolation) + self.segmentation_rescaler = albumentations.SmallestMaxSize(max_size=self.size, + interpolation=cv2.INTER_NEAREST) + self.center_crop = not random_crop + if self.center_crop: + self.cropper = albumentations.CenterCrop(height=self.size, width=self.size) + else: + self.cropper = albumentations.RandomCrop(height=self.size, width=self.size) + self.preprocessor = self.cropper + + def __len__(self): + return self._length + + def __getitem__(self, i): + example = dict((k, self.labels[k][i]) for k in self.labels) + image = Image.open(example["file_path_"]) + if not image.mode == "RGB": + image = image.convert("RGB") + image = np.array(image).astype(np.uint8) + if self.size is not None: + image = self.image_rescaler(image=image)["image"] + segmentation = Image.open(example["segmentation_path_"]) + assert segmentation.mode == "L", segmentation.mode + segmentation = np.array(segmentation).astype(np.uint8) + if self.shift_segmentation: + # used to support segmentations containing unlabeled==255 label + segmentation = segmentation+1 + if self.size is not None: + segmentation = self.segmentation_rescaler(image=segmentation)["image"] + if self.size is not None: + processed = self.preprocessor(image=image, + mask=segmentation + ) + else: + processed = {"image": image, + "mask": segmentation + } + example["image"] = (processed["image"]/127.5 - 1.0).astype(np.float32) + segmentation = processed["mask"] + onehot = np.eye(self.n_labels)[segmentation] + example["segmentation"] = onehot + return example + + +class Examples(SegmentationBase): + def __init__(self, size=None, random_crop=False, interpolation="bicubic"): + super().__init__(data_csv="data/sflckr_examples.txt", + data_root="data/sflckr_images", + segmentation_root="data/sflckr_segmentations", + size=size, random_crop=random_crop, interpolation=interpolation) diff --git a/sd/stablediffusion/src/taming-transformers/taming/data/utils.py b/sd/stablediffusion/src/taming-transformers/taming/data/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..2b3c3d53cd2b6c72b481b59834cf809d3735b394 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/data/utils.py @@ -0,0 +1,169 @@ +import collections +import os +import tarfile +import urllib +import zipfile +from pathlib import Path + +import numpy as np +import torch +from taming.data.helper_types import Annotation +from torch._six import string_classes +from torch.utils.data._utils.collate import np_str_obj_array_pattern, default_collate_err_msg_format +from tqdm import tqdm + + +def unpack(path): + if path.endswith("tar.gz"): + with tarfile.open(path, "r:gz") as tar: + tar.extractall(path=os.path.split(path)[0]) + elif path.endswith("tar"): + with tarfile.open(path, "r:") as tar: + tar.extractall(path=os.path.split(path)[0]) + elif path.endswith("zip"): + with zipfile.ZipFile(path, "r") as f: + f.extractall(path=os.path.split(path)[0]) + else: + raise NotImplementedError( + "Unknown file extension: {}".format(os.path.splitext(path)[1]) + ) + + +def reporthook(bar): + """tqdm progress bar for downloads.""" + + def hook(b=1, bsize=1, tsize=None): + if tsize is not None: + bar.total = tsize + bar.update(b * bsize - bar.n) + + return hook + + +def get_root(name): + base = "data/" + root = os.path.join(base, name) + os.makedirs(root, exist_ok=True) + return root + + +def is_prepared(root): + return Path(root).joinpath(".ready").exists() + + +def mark_prepared(root): + Path(root).joinpath(".ready").touch() + + +def prompt_download(file_, source, target_dir, content_dir=None): + targetpath = os.path.join(target_dir, file_) + while not os.path.exists(targetpath): + if content_dir is not None and os.path.exists( + os.path.join(target_dir, content_dir) + ): + break + print( + "Please download '{}' from '{}' to '{}'.".format(file_, source, targetpath) + ) + if content_dir is not None: + print( + "Or place its content into '{}'.".format( + os.path.join(target_dir, content_dir) + ) + ) + input("Press Enter when done...") + return targetpath + + +def download_url(file_, url, target_dir): + targetpath = os.path.join(target_dir, file_) + os.makedirs(target_dir, exist_ok=True) + with tqdm( + unit="B", unit_scale=True, unit_divisor=1024, miniters=1, desc=file_ + ) as bar: + urllib.request.urlretrieve(url, targetpath, reporthook=reporthook(bar)) + return targetpath + + +def download_urls(urls, target_dir): + paths = dict() + for fname, url in urls.items(): + outpath = download_url(fname, url, target_dir) + paths[fname] = outpath + return paths + + +def quadratic_crop(x, bbox, alpha=1.0): + """bbox is xmin, ymin, xmax, ymax""" + im_h, im_w = x.shape[:2] + bbox = np.array(bbox, dtype=np.float32) + bbox = np.clip(bbox, 0, max(im_h, im_w)) + center = 0.5 * (bbox[0] + bbox[2]), 0.5 * (bbox[1] + bbox[3]) + w = bbox[2] - bbox[0] + h = bbox[3] - bbox[1] + l = int(alpha * max(w, h)) + l = max(l, 2) + + required_padding = -1 * min( + center[0] - l, center[1] - l, im_w - (center[0] + l), im_h - (center[1] + l) + ) + required_padding = int(np.ceil(required_padding)) + if required_padding > 0: + padding = [ + [required_padding, required_padding], + [required_padding, required_padding], + ] + padding += [[0, 0]] * (len(x.shape) - 2) + x = np.pad(x, padding, "reflect") + center = center[0] + required_padding, center[1] + required_padding + xmin = int(center[0] - l / 2) + ymin = int(center[1] - l / 2) + return np.array(x[ymin : ymin + l, xmin : xmin + l, ...]) + + +def custom_collate(batch): + r"""source: pytorch 1.9.0, only one modification to original code """ + + elem = batch[0] + elem_type = type(elem) + if isinstance(elem, torch.Tensor): + out = None + if torch.utils.data.get_worker_info() is not None: + # If we're in a background process, concatenate directly into a + # shared memory tensor to avoid an extra copy + numel = sum([x.numel() for x in batch]) + storage = elem.storage()._new_shared(numel) + out = elem.new(storage) + return torch.stack(batch, 0, out=out) + elif elem_type.__module__ == 'numpy' and elem_type.__name__ != 'str_' \ + and elem_type.__name__ != 'string_': + if elem_type.__name__ == 'ndarray' or elem_type.__name__ == 'memmap': + # array of string classes and object + if np_str_obj_array_pattern.search(elem.dtype.str) is not None: + raise TypeError(default_collate_err_msg_format.format(elem.dtype)) + + return custom_collate([torch.as_tensor(b) for b in batch]) + elif elem.shape == (): # scalars + return torch.as_tensor(batch) + elif isinstance(elem, float): + return torch.tensor(batch, dtype=torch.float64) + elif isinstance(elem, int): + return torch.tensor(batch) + elif isinstance(elem, string_classes): + return batch + elif isinstance(elem, collections.abc.Mapping): + return {key: custom_collate([d[key] for d in batch]) for key in elem} + elif isinstance(elem, tuple) and hasattr(elem, '_fields'): # namedtuple + return elem_type(*(custom_collate(samples) for samples in zip(*batch))) + if isinstance(elem, collections.abc.Sequence) and isinstance(elem[0], Annotation): # added + return batch # added + elif isinstance(elem, collections.abc.Sequence): + # check to make sure that the elements in batch have consistent size + it = iter(batch) + elem_size = len(next(it)) + if not all(len(elem) == elem_size for elem in it): + raise RuntimeError('each element in list of batch should be of equal size') + transposed = zip(*batch) + return [custom_collate(samples) for samples in transposed] + + raise TypeError(default_collate_err_msg_format.format(elem_type)) diff --git a/sd/stablediffusion/src/taming-transformers/taming/lr_scheduler.py b/sd/stablediffusion/src/taming-transformers/taming/lr_scheduler.py new file mode 100644 index 0000000000000000000000000000000000000000..e598ed120159c53da6820a55ad86b89f5c70c82d --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/lr_scheduler.py @@ -0,0 +1,34 @@ +import numpy as np + + +class LambdaWarmUpCosineScheduler: + """ + note: use with a base_lr of 1.0 + """ + def __init__(self, warm_up_steps, lr_min, lr_max, lr_start, max_decay_steps, verbosity_interval=0): + self.lr_warm_up_steps = warm_up_steps + self.lr_start = lr_start + self.lr_min = lr_min + self.lr_max = lr_max + self.lr_max_decay_steps = max_decay_steps + self.last_lr = 0. + self.verbosity_interval = verbosity_interval + + def schedule(self, n): + if self.verbosity_interval > 0: + if n % self.verbosity_interval == 0: print(f"current step: {n}, recent lr-multiplier: {self.last_lr}") + if n < self.lr_warm_up_steps: + lr = (self.lr_max - self.lr_start) / self.lr_warm_up_steps * n + self.lr_start + self.last_lr = lr + return lr + else: + t = (n - self.lr_warm_up_steps) / (self.lr_max_decay_steps - self.lr_warm_up_steps) + t = min(t, 1.0) + lr = self.lr_min + 0.5 * (self.lr_max - self.lr_min) * ( + 1 + np.cos(t * np.pi)) + self.last_lr = lr + return lr + + def __call__(self, n): + return self.schedule(n) + diff --git a/sd/stablediffusion/src/taming-transformers/taming/models/cond_transformer.py b/sd/stablediffusion/src/taming-transformers/taming/models/cond_transformer.py new file mode 100644 index 0000000000000000000000000000000000000000..e4c63730fa86ac1b92b37af14c14fb696595b1ab --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/models/cond_transformer.py @@ -0,0 +1,352 @@ +import os, math +import torch +import torch.nn.functional as F +import pytorch_lightning as pl + +from main import instantiate_from_config +from taming.modules.util import SOSProvider + + +def disabled_train(self, mode=True): + """Overwrite model.train with this function to make sure train/eval mode + does not change anymore.""" + return self + + +class Net2NetTransformer(pl.LightningModule): + def __init__(self, + transformer_config, + first_stage_config, + cond_stage_config, + permuter_config=None, + ckpt_path=None, + ignore_keys=[], + first_stage_key="image", + cond_stage_key="depth", + downsample_cond_size=-1, + pkeep=1.0, + sos_token=0, + unconditional=False, + ): + super().__init__() + self.be_unconditional = unconditional + self.sos_token = sos_token + self.first_stage_key = first_stage_key + self.cond_stage_key = cond_stage_key + self.init_first_stage_from_ckpt(first_stage_config) + self.init_cond_stage_from_ckpt(cond_stage_config) + if permuter_config is None: + permuter_config = {"target": "taming.modules.transformer.permuter.Identity"} + self.permuter = instantiate_from_config(config=permuter_config) + self.transformer = instantiate_from_config(config=transformer_config) + + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys) + self.downsample_cond_size = downsample_cond_size + self.pkeep = pkeep + + def init_from_ckpt(self, path, ignore_keys=list()): + sd = torch.load(path, map_location="cpu")["state_dict"] + for k in sd.keys(): + for ik in ignore_keys: + if k.startswith(ik): + self.print("Deleting key {} from state_dict.".format(k)) + del sd[k] + self.load_state_dict(sd, strict=False) + print(f"Restored from {path}") + + def init_first_stage_from_ckpt(self, config): + model = instantiate_from_config(config) + model = model.eval() + model.train = disabled_train + self.first_stage_model = model + + def init_cond_stage_from_ckpt(self, config): + if config == "__is_first_stage__": + print("Using first stage also as cond stage.") + self.cond_stage_model = self.first_stage_model + elif config == "__is_unconditional__" or self.be_unconditional: + print(f"Using no cond stage. Assuming the training is intended to be unconditional. " + f"Prepending {self.sos_token} as a sos token.") + self.be_unconditional = True + self.cond_stage_key = self.first_stage_key + self.cond_stage_model = SOSProvider(self.sos_token) + else: + model = instantiate_from_config(config) + model = model.eval() + model.train = disabled_train + self.cond_stage_model = model + + def forward(self, x, c): + # one step to produce the logits + _, z_indices = self.encode_to_z(x) + _, c_indices = self.encode_to_c(c) + + if self.training and self.pkeep < 1.0: + mask = torch.bernoulli(self.pkeep*torch.ones(z_indices.shape, + device=z_indices.device)) + mask = mask.round().to(dtype=torch.int64) + r_indices = torch.randint_like(z_indices, self.transformer.config.vocab_size) + a_indices = mask*z_indices+(1-mask)*r_indices + else: + a_indices = z_indices + + cz_indices = torch.cat((c_indices, a_indices), dim=1) + + # target includes all sequence elements (no need to handle first one + # differently because we are conditioning) + target = z_indices + # make the prediction + logits, _ = self.transformer(cz_indices[:, :-1]) + # cut off conditioning outputs - output i corresponds to p(z_i | z_{ -1: + c = F.interpolate(c, size=(self.downsample_cond_size, self.downsample_cond_size)) + quant_c, _, [_,_,indices] = self.cond_stage_model.encode(c) + if len(indices.shape) > 2: + indices = indices.view(c.shape[0], -1) + return quant_c, indices + + @torch.no_grad() + def decode_to_img(self, index, zshape): + index = self.permuter(index, reverse=True) + bhwc = (zshape[0],zshape[2],zshape[3],zshape[1]) + quant_z = self.first_stage_model.quantize.get_codebook_entry( + index.reshape(-1), shape=bhwc) + x = self.first_stage_model.decode(quant_z) + return x + + @torch.no_grad() + def log_images(self, batch, temperature=None, top_k=None, callback=None, lr_interface=False, **kwargs): + log = dict() + + N = 4 + if lr_interface: + x, c = self.get_xc(batch, N, diffuse=False, upsample_factor=8) + else: + x, c = self.get_xc(batch, N) + x = x.to(device=self.device) + c = c.to(device=self.device) + + quant_z, z_indices = self.encode_to_z(x) + quant_c, c_indices = self.encode_to_c(c) + + # create a "half"" sample + z_start_indices = z_indices[:,:z_indices.shape[1]//2] + index_sample = self.sample(z_start_indices, c_indices, + steps=z_indices.shape[1]-z_start_indices.shape[1], + temperature=temperature if temperature is not None else 1.0, + sample=True, + top_k=top_k if top_k is not None else 100, + callback=callback if callback is not None else lambda k: None) + x_sample = self.decode_to_img(index_sample, quant_z.shape) + + # sample + z_start_indices = z_indices[:, :0] + index_sample = self.sample(z_start_indices, c_indices, + steps=z_indices.shape[1], + temperature=temperature if temperature is not None else 1.0, + sample=True, + top_k=top_k if top_k is not None else 100, + callback=callback if callback is not None else lambda k: None) + x_sample_nopix = self.decode_to_img(index_sample, quant_z.shape) + + # det sample + z_start_indices = z_indices[:, :0] + index_sample = self.sample(z_start_indices, c_indices, + steps=z_indices.shape[1], + sample=False, + callback=callback if callback is not None else lambda k: None) + x_sample_det = self.decode_to_img(index_sample, quant_z.shape) + + # reconstruction + x_rec = self.decode_to_img(z_indices, quant_z.shape) + + log["inputs"] = x + log["reconstructions"] = x_rec + + if self.cond_stage_key in ["objects_bbox", "objects_center_points"]: + figure_size = (x_rec.shape[2], x_rec.shape[3]) + dataset = kwargs["pl_module"].trainer.datamodule.datasets["validation"] + label_for_category_no = dataset.get_textual_label_for_category_no + plotter = dataset.conditional_builders[self.cond_stage_key].plot + log["conditioning"] = torch.zeros_like(log["reconstructions"]) + for i in range(quant_c.shape[0]): + log["conditioning"][i] = plotter(quant_c[i], label_for_category_no, figure_size) + log["conditioning_rec"] = log["conditioning"] + elif self.cond_stage_key != "image": + cond_rec = self.cond_stage_model.decode(quant_c) + if self.cond_stage_key == "segmentation": + # get image from segmentation mask + num_classes = cond_rec.shape[1] + + c = torch.argmax(c, dim=1, keepdim=True) + c = F.one_hot(c, num_classes=num_classes) + c = c.squeeze(1).permute(0, 3, 1, 2).float() + c = self.cond_stage_model.to_rgb(c) + + cond_rec = torch.argmax(cond_rec, dim=1, keepdim=True) + cond_rec = F.one_hot(cond_rec, num_classes=num_classes) + cond_rec = cond_rec.squeeze(1).permute(0, 3, 1, 2).float() + cond_rec = self.cond_stage_model.to_rgb(cond_rec) + log["conditioning_rec"] = cond_rec + log["conditioning"] = c + + log["samples_half"] = x_sample + log["samples_nopix"] = x_sample_nopix + log["samples_det"] = x_sample_det + return log + + def get_input(self, key, batch): + x = batch[key] + if len(x.shape) == 3: + x = x[..., None] + if len(x.shape) == 4: + x = x.permute(0, 3, 1, 2).to(memory_format=torch.contiguous_format) + if x.dtype == torch.double: + x = x.float() + return x + + def get_xc(self, batch, N=None): + x = self.get_input(self.first_stage_key, batch) + c = self.get_input(self.cond_stage_key, batch) + if N is not None: + x = x[:N] + c = c[:N] + return x, c + + def shared_step(self, batch, batch_idx): + x, c = self.get_xc(batch) + logits, target = self(x, c) + loss = F.cross_entropy(logits.reshape(-1, logits.size(-1)), target.reshape(-1)) + return loss + + def training_step(self, batch, batch_idx): + loss = self.shared_step(batch, batch_idx) + self.log("train/loss", loss, prog_bar=True, logger=True, on_step=True, on_epoch=True) + return loss + + def validation_step(self, batch, batch_idx): + loss = self.shared_step(batch, batch_idx) + self.log("val/loss", loss, prog_bar=True, logger=True, on_step=True, on_epoch=True) + return loss + + def configure_optimizers(self): + """ + Following minGPT: + This long function is unfortunately doing something very simple and is being very defensive: + We are separating out all parameters of the model into two buckets: those that will experience + weight decay for regularization and those that won't (biases, and layernorm/embedding weights). + We are then returning the PyTorch optimizer object. + """ + # separate out all parameters to those that will and won't experience regularizing weight decay + decay = set() + no_decay = set() + whitelist_weight_modules = (torch.nn.Linear, ) + blacklist_weight_modules = (torch.nn.LayerNorm, torch.nn.Embedding) + for mn, m in self.transformer.named_modules(): + for pn, p in m.named_parameters(): + fpn = '%s.%s' % (mn, pn) if mn else pn # full param name + + if pn.endswith('bias'): + # all biases will not be decayed + no_decay.add(fpn) + elif pn.endswith('weight') and isinstance(m, whitelist_weight_modules): + # weights of whitelist modules will be weight decayed + decay.add(fpn) + elif pn.endswith('weight') and isinstance(m, blacklist_weight_modules): + # weights of blacklist modules will NOT be weight decayed + no_decay.add(fpn) + + # special case the position embedding parameter in the root GPT module as not decayed + no_decay.add('pos_emb') + + # validate that we considered every parameter + param_dict = {pn: p for pn, p in self.transformer.named_parameters()} + inter_params = decay & no_decay + union_params = decay | no_decay + assert len(inter_params) == 0, "parameters %s made it into both decay/no_decay sets!" % (str(inter_params), ) + assert len(param_dict.keys() - union_params) == 0, "parameters %s were not separated into either decay/no_decay set!" \ + % (str(param_dict.keys() - union_params), ) + + # create the pytorch optimizer object + optim_groups = [ + {"params": [param_dict[pn] for pn in sorted(list(decay))], "weight_decay": 0.01}, + {"params": [param_dict[pn] for pn in sorted(list(no_decay))], "weight_decay": 0.0}, + ] + optimizer = torch.optim.AdamW(optim_groups, lr=self.learning_rate, betas=(0.9, 0.95)) + return optimizer diff --git a/sd/stablediffusion/src/taming-transformers/taming/models/dummy_cond_stage.py b/sd/stablediffusion/src/taming-transformers/taming/models/dummy_cond_stage.py new file mode 100644 index 0000000000000000000000000000000000000000..6e19938078752e09b926a3e749907ee99a258ca0 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/models/dummy_cond_stage.py @@ -0,0 +1,22 @@ +from torch import Tensor + + +class DummyCondStage: + def __init__(self, conditional_key): + self.conditional_key = conditional_key + self.train = None + + def eval(self): + return self + + @staticmethod + def encode(c: Tensor): + return c, None, (None, None, c) + + @staticmethod + def decode(c: Tensor): + return c + + @staticmethod + def to_rgb(c: Tensor): + return c diff --git a/sd/stablediffusion/src/taming-transformers/taming/models/vqgan.py b/sd/stablediffusion/src/taming-transformers/taming/models/vqgan.py new file mode 100644 index 0000000000000000000000000000000000000000..a6950baa5f739111cd64c17235dca8be3a5f8037 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/models/vqgan.py @@ -0,0 +1,404 @@ +import torch +import torch.nn.functional as F +import pytorch_lightning as pl + +from main import instantiate_from_config + +from taming.modules.diffusionmodules.model import Encoder, Decoder +from taming.modules.vqvae.quantize import VectorQuantizer2 as VectorQuantizer +from taming.modules.vqvae.quantize import GumbelQuantize +from taming.modules.vqvae.quantize import EMAVectorQuantizer + +class VQModel(pl.LightningModule): + def __init__(self, + ddconfig, + lossconfig, + n_embed, + embed_dim, + ckpt_path=None, + ignore_keys=[], + image_key="image", + colorize_nlabels=None, + monitor=None, + remap=None, + sane_index_shape=False, # tell vector quantizer to return indices as bhw + ): + super().__init__() + self.image_key = image_key + self.encoder = Encoder(**ddconfig) + self.decoder = Decoder(**ddconfig) + self.loss = instantiate_from_config(lossconfig) + self.quantize = VectorQuantizer(n_embed, embed_dim, beta=0.25, + remap=remap, sane_index_shape=sane_index_shape) + self.quant_conv = torch.nn.Conv2d(ddconfig["z_channels"], embed_dim, 1) + self.post_quant_conv = torch.nn.Conv2d(embed_dim, ddconfig["z_channels"], 1) + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys) + self.image_key = image_key + if colorize_nlabels is not None: + assert type(colorize_nlabels)==int + self.register_buffer("colorize", torch.randn(3, colorize_nlabels, 1, 1)) + if monitor is not None: + self.monitor = monitor + + def init_from_ckpt(self, path, ignore_keys=list()): + sd = torch.load(path, map_location="cpu")["state_dict"] + keys = list(sd.keys()) + for k in keys: + for ik in ignore_keys: + if k.startswith(ik): + print("Deleting key {} from state_dict.".format(k)) + del sd[k] + self.load_state_dict(sd, strict=False) + print(f"Restored from {path}") + + def encode(self, x): + h = self.encoder(x) + h = self.quant_conv(h) + quant, emb_loss, info = self.quantize(h) + return quant, emb_loss, info + + def decode(self, quant): + quant = self.post_quant_conv(quant) + dec = self.decoder(quant) + return dec + + def decode_code(self, code_b): + quant_b = self.quantize.embed_code(code_b) + dec = self.decode(quant_b) + return dec + + def forward(self, input): + quant, diff, _ = self.encode(input) + dec = self.decode(quant) + return dec, diff + + def get_input(self, batch, k): + x = batch[k] + if len(x.shape) == 3: + x = x[..., None] + x = x.permute(0, 3, 1, 2).to(memory_format=torch.contiguous_format) + return x.float() + + def training_step(self, batch, batch_idx, optimizer_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + + if optimizer_idx == 0: + # autoencode + aeloss, log_dict_ae = self.loss(qloss, x, xrec, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + + self.log("train/aeloss", aeloss, prog_bar=True, logger=True, on_step=True, on_epoch=True) + self.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return aeloss + + if optimizer_idx == 1: + # discriminator + discloss, log_dict_disc = self.loss(qloss, x, xrec, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + self.log("train/discloss", discloss, prog_bar=True, logger=True, on_step=True, on_epoch=True) + self.log_dict(log_dict_disc, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return discloss + + def validation_step(self, batch, batch_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + aeloss, log_dict_ae = self.loss(qloss, x, xrec, 0, self.global_step, + last_layer=self.get_last_layer(), split="val") + + discloss, log_dict_disc = self.loss(qloss, x, xrec, 1, self.global_step, + last_layer=self.get_last_layer(), split="val") + rec_loss = log_dict_ae["val/rec_loss"] + self.log("val/rec_loss", rec_loss, + prog_bar=True, logger=True, on_step=True, on_epoch=True, sync_dist=True) + self.log("val/aeloss", aeloss, + prog_bar=True, logger=True, on_step=True, on_epoch=True, sync_dist=True) + self.log_dict(log_dict_ae) + self.log_dict(log_dict_disc) + return self.log_dict + + def configure_optimizers(self): + lr = self.learning_rate + opt_ae = torch.optim.Adam(list(self.encoder.parameters())+ + list(self.decoder.parameters())+ + list(self.quantize.parameters())+ + list(self.quant_conv.parameters())+ + list(self.post_quant_conv.parameters()), + lr=lr, betas=(0.5, 0.9)) + opt_disc = torch.optim.Adam(self.loss.discriminator.parameters(), + lr=lr, betas=(0.5, 0.9)) + return [opt_ae, opt_disc], [] + + def get_last_layer(self): + return self.decoder.conv_out.weight + + def log_images(self, batch, **kwargs): + log = dict() + x = self.get_input(batch, self.image_key) + x = x.to(self.device) + xrec, _ = self(x) + if x.shape[1] > 3: + # colorize with random projection + assert xrec.shape[1] > 3 + x = self.to_rgb(x) + xrec = self.to_rgb(xrec) + log["inputs"] = x + log["reconstructions"] = xrec + return log + + def to_rgb(self, x): + assert self.image_key == "segmentation" + if not hasattr(self, "colorize"): + self.register_buffer("colorize", torch.randn(3, x.shape[1], 1, 1).to(x)) + x = F.conv2d(x, weight=self.colorize) + x = 2.*(x-x.min())/(x.max()-x.min()) - 1. + return x + + +class VQSegmentationModel(VQModel): + def __init__(self, n_labels, *args, **kwargs): + super().__init__(*args, **kwargs) + self.register_buffer("colorize", torch.randn(3, n_labels, 1, 1)) + + def configure_optimizers(self): + lr = self.learning_rate + opt_ae = torch.optim.Adam(list(self.encoder.parameters())+ + list(self.decoder.parameters())+ + list(self.quantize.parameters())+ + list(self.quant_conv.parameters())+ + list(self.post_quant_conv.parameters()), + lr=lr, betas=(0.5, 0.9)) + return opt_ae + + def training_step(self, batch, batch_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + aeloss, log_dict_ae = self.loss(qloss, x, xrec, split="train") + self.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return aeloss + + def validation_step(self, batch, batch_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + aeloss, log_dict_ae = self.loss(qloss, x, xrec, split="val") + self.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=True) + total_loss = log_dict_ae["val/total_loss"] + self.log("val/total_loss", total_loss, + prog_bar=True, logger=True, on_step=True, on_epoch=True, sync_dist=True) + return aeloss + + @torch.no_grad() + def log_images(self, batch, **kwargs): + log = dict() + x = self.get_input(batch, self.image_key) + x = x.to(self.device) + xrec, _ = self(x) + if x.shape[1] > 3: + # colorize with random projection + assert xrec.shape[1] > 3 + # convert logits to indices + xrec = torch.argmax(xrec, dim=1, keepdim=True) + xrec = F.one_hot(xrec, num_classes=x.shape[1]) + xrec = xrec.squeeze(1).permute(0, 3, 1, 2).float() + x = self.to_rgb(x) + xrec = self.to_rgb(xrec) + log["inputs"] = x + log["reconstructions"] = xrec + return log + + +class VQNoDiscModel(VQModel): + def __init__(self, + ddconfig, + lossconfig, + n_embed, + embed_dim, + ckpt_path=None, + ignore_keys=[], + image_key="image", + colorize_nlabels=None + ): + super().__init__(ddconfig=ddconfig, lossconfig=lossconfig, n_embed=n_embed, embed_dim=embed_dim, + ckpt_path=ckpt_path, ignore_keys=ignore_keys, image_key=image_key, + colorize_nlabels=colorize_nlabels) + + def training_step(self, batch, batch_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + # autoencode + aeloss, log_dict_ae = self.loss(qloss, x, xrec, self.global_step, split="train") + output = pl.TrainResult(minimize=aeloss) + output.log("train/aeloss", aeloss, + prog_bar=True, logger=True, on_step=True, on_epoch=True) + output.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return output + + def validation_step(self, batch, batch_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + aeloss, log_dict_ae = self.loss(qloss, x, xrec, self.global_step, split="val") + rec_loss = log_dict_ae["val/rec_loss"] + output = pl.EvalResult(checkpoint_on=rec_loss) + output.log("val/rec_loss", rec_loss, + prog_bar=True, logger=True, on_step=True, on_epoch=True) + output.log("val/aeloss", aeloss, + prog_bar=True, logger=True, on_step=True, on_epoch=True) + output.log_dict(log_dict_ae) + + return output + + def configure_optimizers(self): + optimizer = torch.optim.Adam(list(self.encoder.parameters())+ + list(self.decoder.parameters())+ + list(self.quantize.parameters())+ + list(self.quant_conv.parameters())+ + list(self.post_quant_conv.parameters()), + lr=self.learning_rate, betas=(0.5, 0.9)) + return optimizer + + +class GumbelVQ(VQModel): + def __init__(self, + ddconfig, + lossconfig, + n_embed, + embed_dim, + temperature_scheduler_config, + ckpt_path=None, + ignore_keys=[], + image_key="image", + colorize_nlabels=None, + monitor=None, + kl_weight=1e-8, + remap=None, + ): + + z_channels = ddconfig["z_channels"] + super().__init__(ddconfig, + lossconfig, + n_embed, + embed_dim, + ckpt_path=None, + ignore_keys=ignore_keys, + image_key=image_key, + colorize_nlabels=colorize_nlabels, + monitor=monitor, + ) + + self.loss.n_classes = n_embed + self.vocab_size = n_embed + + self.quantize = GumbelQuantize(z_channels, embed_dim, + n_embed=n_embed, + kl_weight=kl_weight, temp_init=1.0, + remap=remap) + + self.temperature_scheduler = instantiate_from_config(temperature_scheduler_config) # annealing of temp + + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys) + + def temperature_scheduling(self): + self.quantize.temperature = self.temperature_scheduler(self.global_step) + + def encode_to_prequant(self, x): + h = self.encoder(x) + h = self.quant_conv(h) + return h + + def decode_code(self, code_b): + raise NotImplementedError + + def training_step(self, batch, batch_idx, optimizer_idx): + self.temperature_scheduling() + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + + if optimizer_idx == 0: + # autoencode + aeloss, log_dict_ae = self.loss(qloss, x, xrec, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + + self.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=True) + self.log("temperature", self.quantize.temperature, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return aeloss + + if optimizer_idx == 1: + # discriminator + discloss, log_dict_disc = self.loss(qloss, x, xrec, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + self.log_dict(log_dict_disc, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return discloss + + def validation_step(self, batch, batch_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x, return_pred_indices=True) + aeloss, log_dict_ae = self.loss(qloss, x, xrec, 0, self.global_step, + last_layer=self.get_last_layer(), split="val") + + discloss, log_dict_disc = self.loss(qloss, x, xrec, 1, self.global_step, + last_layer=self.get_last_layer(), split="val") + rec_loss = log_dict_ae["val/rec_loss"] + self.log("val/rec_loss", rec_loss, + prog_bar=True, logger=True, on_step=False, on_epoch=True, sync_dist=True) + self.log("val/aeloss", aeloss, + prog_bar=True, logger=True, on_step=False, on_epoch=True, sync_dist=True) + self.log_dict(log_dict_ae) + self.log_dict(log_dict_disc) + return self.log_dict + + def log_images(self, batch, **kwargs): + log = dict() + x = self.get_input(batch, self.image_key) + x = x.to(self.device) + # encode + h = self.encoder(x) + h = self.quant_conv(h) + quant, _, _ = self.quantize(h) + # decode + x_rec = self.decode(quant) + log["inputs"] = x + log["reconstructions"] = x_rec + return log + + +class EMAVQ(VQModel): + def __init__(self, + ddconfig, + lossconfig, + n_embed, + embed_dim, + ckpt_path=None, + ignore_keys=[], + image_key="image", + colorize_nlabels=None, + monitor=None, + remap=None, + sane_index_shape=False, # tell vector quantizer to return indices as bhw + ): + super().__init__(ddconfig, + lossconfig, + n_embed, + embed_dim, + ckpt_path=None, + ignore_keys=ignore_keys, + image_key=image_key, + colorize_nlabels=colorize_nlabels, + monitor=monitor, + ) + self.quantize = EMAVectorQuantizer(n_embed=n_embed, + embedding_dim=embed_dim, + beta=0.25, + remap=remap) + def configure_optimizers(self): + lr = self.learning_rate + #Remove self.quantize from parameter list since it is updated via EMA + opt_ae = torch.optim.Adam(list(self.encoder.parameters())+ + list(self.decoder.parameters())+ + list(self.quant_conv.parameters())+ + list(self.post_quant_conv.parameters()), + lr=lr, betas=(0.5, 0.9)) + opt_disc = torch.optim.Adam(self.loss.discriminator.parameters(), + lr=lr, betas=(0.5, 0.9)) + return [opt_ae, opt_disc], [] \ No newline at end of file diff --git a/sd/stablediffusion/src/taming-transformers/taming/modules/diffusionmodules/model.py b/sd/stablediffusion/src/taming-transformers/taming/modules/diffusionmodules/model.py new file mode 100644 index 0000000000000000000000000000000000000000..d3a5db6aa2ef915e270f1ae135e4a9918fdd884c --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/modules/diffusionmodules/model.py @@ -0,0 +1,776 @@ +# pytorch_diffusion + derived encoder decoder +import math +import torch +import torch.nn as nn +import numpy as np + + +def get_timestep_embedding(timesteps, embedding_dim): + """ + This matches the implementation in Denoising Diffusion Probabilistic Models: + From Fairseq. + Build sinusoidal embeddings. + This matches the implementation in tensor2tensor, but differs slightly + from the description in Section 3.5 of "Attention Is All You Need". + """ + assert len(timesteps.shape) == 1 + + half_dim = embedding_dim // 2 + emb = math.log(10000) / (half_dim - 1) + emb = torch.exp(torch.arange(half_dim, dtype=torch.float32) * -emb) + emb = emb.to(device=timesteps.device) + emb = timesteps.float()[:, None] * emb[None, :] + emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=1) + if embedding_dim % 2 == 1: # zero pad + emb = torch.nn.functional.pad(emb, (0,1,0,0)) + return emb + + +def nonlinearity(x): + # swish + return x*torch.sigmoid(x) + + +def Normalize(in_channels): + return torch.nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True) + + +class Upsample(nn.Module): + def __init__(self, in_channels, with_conv): + super().__init__() + self.with_conv = with_conv + if self.with_conv: + self.conv = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + x = torch.nn.functional.interpolate(x, scale_factor=2.0, mode="nearest") + if self.with_conv: + x = self.conv(x) + return x + + +class Downsample(nn.Module): + def __init__(self, in_channels, with_conv): + super().__init__() + self.with_conv = with_conv + if self.with_conv: + # no asymmetric padding in torch conv, must do it ourselves + self.conv = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=3, + stride=2, + padding=0) + + def forward(self, x): + if self.with_conv: + pad = (0,1,0,1) + x = torch.nn.functional.pad(x, pad, mode="constant", value=0) + x = self.conv(x) + else: + x = torch.nn.functional.avg_pool2d(x, kernel_size=2, stride=2) + return x + + +class ResnetBlock(nn.Module): + def __init__(self, *, in_channels, out_channels=None, conv_shortcut=False, + dropout, temb_channels=512): + super().__init__() + self.in_channels = in_channels + out_channels = in_channels if out_channels is None else out_channels + self.out_channels = out_channels + self.use_conv_shortcut = conv_shortcut + + self.norm1 = Normalize(in_channels) + self.conv1 = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + if temb_channels > 0: + self.temb_proj = torch.nn.Linear(temb_channels, + out_channels) + self.norm2 = Normalize(out_channels) + self.dropout = torch.nn.Dropout(dropout) + self.conv2 = torch.nn.Conv2d(out_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + if self.in_channels != self.out_channels: + if self.use_conv_shortcut: + self.conv_shortcut = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + else: + self.nin_shortcut = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=1, + stride=1, + padding=0) + + def forward(self, x, temb): + h = x + h = self.norm1(h) + h = nonlinearity(h) + h = self.conv1(h) + + if temb is not None: + h = h + self.temb_proj(nonlinearity(temb))[:,:,None,None] + + h = self.norm2(h) + h = nonlinearity(h) + h = self.dropout(h) + h = self.conv2(h) + + if self.in_channels != self.out_channels: + if self.use_conv_shortcut: + x = self.conv_shortcut(x) + else: + x = self.nin_shortcut(x) + + return x+h + + +class AttnBlock(nn.Module): + def __init__(self, in_channels): + super().__init__() + self.in_channels = in_channels + + self.norm = Normalize(in_channels) + self.q = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.k = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.v = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.proj_out = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + + + def forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + # compute attention + b,c,h,w = q.shape + q = q.reshape(b,c,h*w) + q = q.permute(0,2,1) # b,hw,c + k = k.reshape(b,c,h*w) # b,c,hw + w_ = torch.bmm(q,k) # b,hw,hw w[b,i,j]=sum_c q[b,i,c]k[b,c,j] + w_ = w_ * (int(c)**(-0.5)) + w_ = torch.nn.functional.softmax(w_, dim=2) + + # attend to values + v = v.reshape(b,c,h*w) + w_ = w_.permute(0,2,1) # b,hw,hw (first hw of k, second of q) + h_ = torch.bmm(v,w_) # b, c,hw (hw of q) h_[b,c,j] = sum_i v[b,c,i] w_[b,i,j] + h_ = h_.reshape(b,c,h,w) + + h_ = self.proj_out(h_) + + return x+h_ + + +class Model(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, use_timestep=True): + super().__init__() + self.ch = ch + self.temb_ch = self.ch*4 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + + self.use_timestep = use_timestep + if self.use_timestep: + # timestep embedding + self.temb = nn.Module() + self.temb.dense = nn.ModuleList([ + torch.nn.Linear(self.ch, + self.temb_ch), + torch.nn.Linear(self.temb_ch, + self.temb_ch), + ]) + + # downsampling + self.conv_in = torch.nn.Conv2d(in_channels, + self.ch, + kernel_size=3, + stride=1, + padding=1) + + curr_res = resolution + in_ch_mult = (1,)+tuple(ch_mult) + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = ch*in_ch_mult[i_level] + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(AttnBlock(block_in)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions-1: + down.downsample = Downsample(block_in, resamp_with_conv) + curr_res = curr_res // 2 + self.down.append(down) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = AttnBlock(block_in) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = ch*ch_mult[i_level] + skip_in = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks+1): + if i_block == self.num_res_blocks: + skip_in = ch*in_ch_mult[i_level] + block.append(ResnetBlock(in_channels=block_in+skip_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(AttnBlock(block_in)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + up.upsample = Upsample(block_in, resamp_with_conv) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_ch, + kernel_size=3, + stride=1, + padding=1) + + + def forward(self, x, t=None): + #assert x.shape[2] == x.shape[3] == self.resolution + + if self.use_timestep: + # timestep embedding + assert t is not None + temb = get_timestep_embedding(t, self.ch) + temb = self.temb.dense[0](temb) + temb = nonlinearity(temb) + temb = self.temb.dense[1](temb) + else: + temb = None + + # downsampling + hs = [self.conv_in(x)] + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](hs[-1], temb) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + hs.append(h) + if i_level != self.num_resolutions-1: + hs.append(self.down[i_level].downsample(hs[-1])) + + # middle + h = hs[-1] + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # upsampling + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks+1): + h = self.up[i_level].block[i_block]( + torch.cat([h, hs.pop()], dim=1), temb) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class Encoder(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, z_channels, double_z=True, **ignore_kwargs): + super().__init__() + self.ch = ch + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + + # downsampling + self.conv_in = torch.nn.Conv2d(in_channels, + self.ch, + kernel_size=3, + stride=1, + padding=1) + + curr_res = resolution + in_ch_mult = (1,)+tuple(ch_mult) + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = ch*in_ch_mult[i_level] + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(AttnBlock(block_in)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions-1: + down.downsample = Downsample(block_in, resamp_with_conv) + curr_res = curr_res // 2 + self.down.append(down) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = AttnBlock(block_in) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + 2*z_channels if double_z else z_channels, + kernel_size=3, + stride=1, + padding=1) + + + def forward(self, x): + #assert x.shape[2] == x.shape[3] == self.resolution, "{}, {}, {}".format(x.shape[2], x.shape[3], self.resolution) + + # timestep embedding + temb = None + + # downsampling + hs = [self.conv_in(x)] + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](hs[-1], temb) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + hs.append(h) + if i_level != self.num_resolutions-1: + hs.append(self.down[i_level].downsample(hs[-1])) + + # middle + h = hs[-1] + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class Decoder(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, z_channels, give_pre_end=False, **ignorekwargs): + super().__init__() + self.ch = ch + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + self.give_pre_end = give_pre_end + + # compute in_ch_mult, block_in and curr_res at lowest res + in_ch_mult = (1,)+tuple(ch_mult) + block_in = ch*ch_mult[self.num_resolutions-1] + curr_res = resolution // 2**(self.num_resolutions-1) + self.z_shape = (1,z_channels,curr_res,curr_res) + print("Working with z of shape {} = {} dimensions.".format( + self.z_shape, np.prod(self.z_shape))) + + # z to block_in + self.conv_in = torch.nn.Conv2d(z_channels, + block_in, + kernel_size=3, + stride=1, + padding=1) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = AttnBlock(block_in) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks+1): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(AttnBlock(block_in)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + up.upsample = Upsample(block_in, resamp_with_conv) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_ch, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, z): + #assert z.shape[1:] == self.z_shape[1:] + self.last_z_shape = z.shape + + # timestep embedding + temb = None + + # z to block_in + h = self.conv_in(z) + + # middle + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # upsampling + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks+1): + h = self.up[i_level].block[i_block](h, temb) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + + # end + if self.give_pre_end: + return h + + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class VUNet(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, + in_channels, c_channels, + resolution, z_channels, use_timestep=False, **ignore_kwargs): + super().__init__() + self.ch = ch + self.temb_ch = self.ch*4 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + + self.use_timestep = use_timestep + if self.use_timestep: + # timestep embedding + self.temb = nn.Module() + self.temb.dense = nn.ModuleList([ + torch.nn.Linear(self.ch, + self.temb_ch), + torch.nn.Linear(self.temb_ch, + self.temb_ch), + ]) + + # downsampling + self.conv_in = torch.nn.Conv2d(c_channels, + self.ch, + kernel_size=3, + stride=1, + padding=1) + + curr_res = resolution + in_ch_mult = (1,)+tuple(ch_mult) + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = ch*in_ch_mult[i_level] + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(AttnBlock(block_in)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions-1: + down.downsample = Downsample(block_in, resamp_with_conv) + curr_res = curr_res // 2 + self.down.append(down) + + self.z_in = torch.nn.Conv2d(z_channels, + block_in, + kernel_size=1, + stride=1, + padding=0) + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=2*block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = AttnBlock(block_in) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = ch*ch_mult[i_level] + skip_in = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks+1): + if i_block == self.num_res_blocks: + skip_in = ch*in_ch_mult[i_level] + block.append(ResnetBlock(in_channels=block_in+skip_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(AttnBlock(block_in)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + up.upsample = Upsample(block_in, resamp_with_conv) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_ch, + kernel_size=3, + stride=1, + padding=1) + + + def forward(self, x, z): + #assert x.shape[2] == x.shape[3] == self.resolution + + if self.use_timestep: + # timestep embedding + assert t is not None + temb = get_timestep_embedding(t, self.ch) + temb = self.temb.dense[0](temb) + temb = nonlinearity(temb) + temb = self.temb.dense[1](temb) + else: + temb = None + + # downsampling + hs = [self.conv_in(x)] + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](hs[-1], temb) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + hs.append(h) + if i_level != self.num_resolutions-1: + hs.append(self.down[i_level].downsample(hs[-1])) + + # middle + h = hs[-1] + z = self.z_in(z) + h = torch.cat((h,z),dim=1) + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # upsampling + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks+1): + h = self.up[i_level].block[i_block]( + torch.cat([h, hs.pop()], dim=1), temb) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class SimpleDecoder(nn.Module): + def __init__(self, in_channels, out_channels, *args, **kwargs): + super().__init__() + self.model = nn.ModuleList([nn.Conv2d(in_channels, in_channels, 1), + ResnetBlock(in_channels=in_channels, + out_channels=2 * in_channels, + temb_channels=0, dropout=0.0), + ResnetBlock(in_channels=2 * in_channels, + out_channels=4 * in_channels, + temb_channels=0, dropout=0.0), + ResnetBlock(in_channels=4 * in_channels, + out_channels=2 * in_channels, + temb_channels=0, dropout=0.0), + nn.Conv2d(2*in_channels, in_channels, 1), + Upsample(in_channels, with_conv=True)]) + # end + self.norm_out = Normalize(in_channels) + self.conv_out = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + for i, layer in enumerate(self.model): + if i in [1,2,3]: + x = layer(x, None) + else: + x = layer(x) + + h = self.norm_out(x) + h = nonlinearity(h) + x = self.conv_out(h) + return x + + +class UpsampleDecoder(nn.Module): + def __init__(self, in_channels, out_channels, ch, num_res_blocks, resolution, + ch_mult=(2,2), dropout=0.0): + super().__init__() + # upsampling + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + block_in = in_channels + curr_res = resolution // 2 ** (self.num_resolutions - 1) + self.res_blocks = nn.ModuleList() + self.upsample_blocks = nn.ModuleList() + for i_level in range(self.num_resolutions): + res_block = [] + block_out = ch * ch_mult[i_level] + for i_block in range(self.num_res_blocks + 1): + res_block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + self.res_blocks.append(nn.ModuleList(res_block)) + if i_level != self.num_resolutions - 1: + self.upsample_blocks.append(Upsample(block_in, True)) + curr_res = curr_res * 2 + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + # upsampling + h = x + for k, i_level in enumerate(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks + 1): + h = self.res_blocks[i_level][i_block](h, None) + if i_level != self.num_resolutions - 1: + h = self.upsample_blocks[k](h) + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + diff --git a/sd/stablediffusion/src/taming-transformers/taming/modules/discriminator/model.py b/sd/stablediffusion/src/taming-transformers/taming/modules/discriminator/model.py new file mode 100644 index 0000000000000000000000000000000000000000..2aaa3110d0a7bcd05de7eca1e45101589ca5af05 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/modules/discriminator/model.py @@ -0,0 +1,67 @@ +import functools +import torch.nn as nn + + +from taming.modules.util import ActNorm + + +def weights_init(m): + classname = m.__class__.__name__ + if classname.find('Conv') != -1: + nn.init.normal_(m.weight.data, 0.0, 0.02) + elif classname.find('BatchNorm') != -1: + nn.init.normal_(m.weight.data, 1.0, 0.02) + nn.init.constant_(m.bias.data, 0) + + +class NLayerDiscriminator(nn.Module): + """Defines a PatchGAN discriminator as in Pix2Pix + --> see https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix/blob/master/models/networks.py + """ + def __init__(self, input_nc=3, ndf=64, n_layers=3, use_actnorm=False): + """Construct a PatchGAN discriminator + Parameters: + input_nc (int) -- the number of channels in input images + ndf (int) -- the number of filters in the last conv layer + n_layers (int) -- the number of conv layers in the discriminator + norm_layer -- normalization layer + """ + super(NLayerDiscriminator, self).__init__() + if not use_actnorm: + norm_layer = nn.BatchNorm2d + else: + norm_layer = ActNorm + if type(norm_layer) == functools.partial: # no need to use bias as BatchNorm2d has affine parameters + use_bias = norm_layer.func != nn.BatchNorm2d + else: + use_bias = norm_layer != nn.BatchNorm2d + + kw = 4 + padw = 1 + sequence = [nn.Conv2d(input_nc, ndf, kernel_size=kw, stride=2, padding=padw), nn.LeakyReLU(0.2, True)] + nf_mult = 1 + nf_mult_prev = 1 + for n in range(1, n_layers): # gradually increase the number of filters + nf_mult_prev = nf_mult + nf_mult = min(2 ** n, 8) + sequence += [ + nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult, kernel_size=kw, stride=2, padding=padw, bias=use_bias), + norm_layer(ndf * nf_mult), + nn.LeakyReLU(0.2, True) + ] + + nf_mult_prev = nf_mult + nf_mult = min(2 ** n_layers, 8) + sequence += [ + nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult, kernel_size=kw, stride=1, padding=padw, bias=use_bias), + norm_layer(ndf * nf_mult), + nn.LeakyReLU(0.2, True) + ] + + sequence += [ + nn.Conv2d(ndf * nf_mult, 1, kernel_size=kw, stride=1, padding=padw)] # output 1 channel prediction map + self.main = nn.Sequential(*sequence) + + def forward(self, input): + """Standard forward.""" + return self.main(input) diff --git a/sd/stablediffusion/src/taming-transformers/taming/modules/losses/__init__.py b/sd/stablediffusion/src/taming-transformers/taming/modules/losses/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d09caf9eb805f849a517f1b23503e1a4d6ea1ec5 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/modules/losses/__init__.py @@ -0,0 +1,2 @@ +from taming.modules.losses.vqperceptual import DummyLoss + diff --git a/sd/stablediffusion/src/taming-transformers/taming/modules/losses/lpips.py b/sd/stablediffusion/src/taming-transformers/taming/modules/losses/lpips.py new file mode 100644 index 0000000000000000000000000000000000000000..a7280447694ffc302a7636e7e4d6183408e0aa95 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/modules/losses/lpips.py @@ -0,0 +1,123 @@ +"""Stripped version of https://github.com/richzhang/PerceptualSimilarity/tree/master/models""" + +import torch +import torch.nn as nn +from torchvision import models +from collections import namedtuple + +from taming.util import get_ckpt_path + + +class LPIPS(nn.Module): + # Learned perceptual metric + def __init__(self, use_dropout=True): + super().__init__() + self.scaling_layer = ScalingLayer() + self.chns = [64, 128, 256, 512, 512] # vg16 features + self.net = vgg16(pretrained=True, requires_grad=False) + self.lin0 = NetLinLayer(self.chns[0], use_dropout=use_dropout) + self.lin1 = NetLinLayer(self.chns[1], use_dropout=use_dropout) + self.lin2 = NetLinLayer(self.chns[2], use_dropout=use_dropout) + self.lin3 = NetLinLayer(self.chns[3], use_dropout=use_dropout) + self.lin4 = NetLinLayer(self.chns[4], use_dropout=use_dropout) + self.load_from_pretrained() + for param in self.parameters(): + param.requires_grad = False + + def load_from_pretrained(self, name="vgg_lpips"): + ckpt = get_ckpt_path(name, "taming/modules/autoencoder/lpips") + self.load_state_dict(torch.load(ckpt, map_location=torch.device("cpu")), strict=False) + print("loaded pretrained LPIPS loss from {}".format(ckpt)) + + @classmethod + def from_pretrained(cls, name="vgg_lpips"): + if name != "vgg_lpips": + raise NotImplementedError + model = cls() + ckpt = get_ckpt_path(name) + model.load_state_dict(torch.load(ckpt, map_location=torch.device("cpu")), strict=False) + return model + + def forward(self, input, target): + in0_input, in1_input = (self.scaling_layer(input), self.scaling_layer(target)) + outs0, outs1 = self.net(in0_input), self.net(in1_input) + feats0, feats1, diffs = {}, {}, {} + lins = [self.lin0, self.lin1, self.lin2, self.lin3, self.lin4] + for kk in range(len(self.chns)): + feats0[kk], feats1[kk] = normalize_tensor(outs0[kk]), normalize_tensor(outs1[kk]) + diffs[kk] = (feats0[kk] - feats1[kk]) ** 2 + + res = [spatial_average(lins[kk].model(diffs[kk]), keepdim=True) for kk in range(len(self.chns))] + val = res[0] + for l in range(1, len(self.chns)): + val += res[l] + return val + + +class ScalingLayer(nn.Module): + def __init__(self): + super(ScalingLayer, self).__init__() + self.register_buffer('shift', torch.Tensor([-.030, -.088, -.188])[None, :, None, None]) + self.register_buffer('scale', torch.Tensor([.458, .448, .450])[None, :, None, None]) + + def forward(self, inp): + return (inp - self.shift) / self.scale + + +class NetLinLayer(nn.Module): + """ A single linear layer which does a 1x1 conv """ + def __init__(self, chn_in, chn_out=1, use_dropout=False): + super(NetLinLayer, self).__init__() + layers = [nn.Dropout(), ] if (use_dropout) else [] + layers += [nn.Conv2d(chn_in, chn_out, 1, stride=1, padding=0, bias=False), ] + self.model = nn.Sequential(*layers) + + +class vgg16(torch.nn.Module): + def __init__(self, requires_grad=False, pretrained=True): + super(vgg16, self).__init__() + vgg_pretrained_features = models.vgg16(pretrained=pretrained).features + self.slice1 = torch.nn.Sequential() + self.slice2 = torch.nn.Sequential() + self.slice3 = torch.nn.Sequential() + self.slice4 = torch.nn.Sequential() + self.slice5 = torch.nn.Sequential() + self.N_slices = 5 + for x in range(4): + self.slice1.add_module(str(x), vgg_pretrained_features[x]) + for x in range(4, 9): + self.slice2.add_module(str(x), vgg_pretrained_features[x]) + for x in range(9, 16): + self.slice3.add_module(str(x), vgg_pretrained_features[x]) + for x in range(16, 23): + self.slice4.add_module(str(x), vgg_pretrained_features[x]) + for x in range(23, 30): + self.slice5.add_module(str(x), vgg_pretrained_features[x]) + if not requires_grad: + for param in self.parameters(): + param.requires_grad = False + + def forward(self, X): + h = self.slice1(X) + h_relu1_2 = h + h = self.slice2(h) + h_relu2_2 = h + h = self.slice3(h) + h_relu3_3 = h + h = self.slice4(h) + h_relu4_3 = h + h = self.slice5(h) + h_relu5_3 = h + vgg_outputs = namedtuple("VggOutputs", ['relu1_2', 'relu2_2', 'relu3_3', 'relu4_3', 'relu5_3']) + out = vgg_outputs(h_relu1_2, h_relu2_2, h_relu3_3, h_relu4_3, h_relu5_3) + return out + + +def normalize_tensor(x,eps=1e-10): + norm_factor = torch.sqrt(torch.sum(x**2,dim=1,keepdim=True)) + return x/(norm_factor+eps) + + +def spatial_average(x, keepdim=True): + return x.mean([2,3],keepdim=keepdim) + diff --git a/sd/stablediffusion/src/taming-transformers/taming/modules/losses/segmentation.py b/sd/stablediffusion/src/taming-transformers/taming/modules/losses/segmentation.py new file mode 100644 index 0000000000000000000000000000000000000000..4ba77deb5159a6307ed2acba9945e4764a4ff0a5 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/modules/losses/segmentation.py @@ -0,0 +1,22 @@ +import torch.nn as nn +import torch.nn.functional as F + + +class BCELoss(nn.Module): + def forward(self, prediction, target): + loss = F.binary_cross_entropy_with_logits(prediction,target) + return loss, {} + + +class BCELossWithQuant(nn.Module): + def __init__(self, codebook_weight=1.): + super().__init__() + self.codebook_weight = codebook_weight + + def forward(self, qloss, target, prediction, split): + bce_loss = F.binary_cross_entropy_with_logits(prediction,target) + loss = bce_loss + self.codebook_weight*qloss + return loss, {"{}/total_loss".format(split): loss.clone().detach().mean(), + "{}/bce_loss".format(split): bce_loss.detach().mean(), + "{}/quant_loss".format(split): qloss.detach().mean() + } diff --git a/sd/stablediffusion/src/taming-transformers/taming/modules/losses/vqperceptual.py b/sd/stablediffusion/src/taming-transformers/taming/modules/losses/vqperceptual.py new file mode 100644 index 0000000000000000000000000000000000000000..c2febd445728479d4cd9aacdb2572cb1f1af04db --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/modules/losses/vqperceptual.py @@ -0,0 +1,136 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +from taming.modules.losses.lpips import LPIPS +from taming.modules.discriminator.model import NLayerDiscriminator, weights_init + + +class DummyLoss(nn.Module): + def __init__(self): + super().__init__() + + +def adopt_weight(weight, global_step, threshold=0, value=0.): + if global_step < threshold: + weight = value + return weight + + +def hinge_d_loss(logits_real, logits_fake): + loss_real = torch.mean(F.relu(1. - logits_real)) + loss_fake = torch.mean(F.relu(1. + logits_fake)) + d_loss = 0.5 * (loss_real + loss_fake) + return d_loss + + +def vanilla_d_loss(logits_real, logits_fake): + d_loss = 0.5 * ( + torch.mean(torch.nn.functional.softplus(-logits_real)) + + torch.mean(torch.nn.functional.softplus(logits_fake))) + return d_loss + + +class VQLPIPSWithDiscriminator(nn.Module): + def __init__(self, disc_start, codebook_weight=1.0, pixelloss_weight=1.0, + disc_num_layers=3, disc_in_channels=3, disc_factor=1.0, disc_weight=1.0, + perceptual_weight=1.0, use_actnorm=False, disc_conditional=False, + disc_ndf=64, disc_loss="hinge"): + super().__init__() + assert disc_loss in ["hinge", "vanilla"] + self.codebook_weight = codebook_weight + self.pixel_weight = pixelloss_weight + self.perceptual_loss = LPIPS().eval() + self.perceptual_weight = perceptual_weight + + self.discriminator = NLayerDiscriminator(input_nc=disc_in_channels, + n_layers=disc_num_layers, + use_actnorm=use_actnorm, + ndf=disc_ndf + ).apply(weights_init) + self.discriminator_iter_start = disc_start + if disc_loss == "hinge": + self.disc_loss = hinge_d_loss + elif disc_loss == "vanilla": + self.disc_loss = vanilla_d_loss + else: + raise ValueError(f"Unknown GAN loss '{disc_loss}'.") + print(f"VQLPIPSWithDiscriminator running with {disc_loss} loss.") + self.disc_factor = disc_factor + self.discriminator_weight = disc_weight + self.disc_conditional = disc_conditional + + def calculate_adaptive_weight(self, nll_loss, g_loss, last_layer=None): + if last_layer is not None: + nll_grads = torch.autograd.grad(nll_loss, last_layer, retain_graph=True)[0] + g_grads = torch.autograd.grad(g_loss, last_layer, retain_graph=True)[0] + else: + nll_grads = torch.autograd.grad(nll_loss, self.last_layer[0], retain_graph=True)[0] + g_grads = torch.autograd.grad(g_loss, self.last_layer[0], retain_graph=True)[0] + + d_weight = torch.norm(nll_grads) / (torch.norm(g_grads) + 1e-4) + d_weight = torch.clamp(d_weight, 0.0, 1e4).detach() + d_weight = d_weight * self.discriminator_weight + return d_weight + + def forward(self, codebook_loss, inputs, reconstructions, optimizer_idx, + global_step, last_layer=None, cond=None, split="train"): + rec_loss = torch.abs(inputs.contiguous() - reconstructions.contiguous()) + if self.perceptual_weight > 0: + p_loss = self.perceptual_loss(inputs.contiguous(), reconstructions.contiguous()) + rec_loss = rec_loss + self.perceptual_weight * p_loss + else: + p_loss = torch.tensor([0.0]) + + nll_loss = rec_loss + #nll_loss = torch.sum(nll_loss) / nll_loss.shape[0] + nll_loss = torch.mean(nll_loss) + + # now the GAN part + if optimizer_idx == 0: + # generator update + if cond is None: + assert not self.disc_conditional + logits_fake = self.discriminator(reconstructions.contiguous()) + else: + assert self.disc_conditional + logits_fake = self.discriminator(torch.cat((reconstructions.contiguous(), cond), dim=1)) + g_loss = -torch.mean(logits_fake) + + try: + d_weight = self.calculate_adaptive_weight(nll_loss, g_loss, last_layer=last_layer) + except RuntimeError: + assert not self.training + d_weight = torch.tensor(0.0) + + disc_factor = adopt_weight(self.disc_factor, global_step, threshold=self.discriminator_iter_start) + loss = nll_loss + d_weight * disc_factor * g_loss + self.codebook_weight * codebook_loss.mean() + + log = {"{}/total_loss".format(split): loss.clone().detach().mean(), + "{}/quant_loss".format(split): codebook_loss.detach().mean(), + "{}/nll_loss".format(split): nll_loss.detach().mean(), + "{}/rec_loss".format(split): rec_loss.detach().mean(), + "{}/p_loss".format(split): p_loss.detach().mean(), + "{}/d_weight".format(split): d_weight.detach(), + "{}/disc_factor".format(split): torch.tensor(disc_factor), + "{}/g_loss".format(split): g_loss.detach().mean(), + } + return loss, log + + if optimizer_idx == 1: + # second pass for discriminator update + if cond is None: + logits_real = self.discriminator(inputs.contiguous().detach()) + logits_fake = self.discriminator(reconstructions.contiguous().detach()) + else: + logits_real = self.discriminator(torch.cat((inputs.contiguous().detach(), cond), dim=1)) + logits_fake = self.discriminator(torch.cat((reconstructions.contiguous().detach(), cond), dim=1)) + + disc_factor = adopt_weight(self.disc_factor, global_step, threshold=self.discriminator_iter_start) + d_loss = disc_factor * self.disc_loss(logits_real, logits_fake) + + log = {"{}/disc_loss".format(split): d_loss.clone().detach().mean(), + "{}/logits_real".format(split): logits_real.detach().mean(), + "{}/logits_fake".format(split): logits_fake.detach().mean() + } + return d_loss, log diff --git a/sd/stablediffusion/src/taming-transformers/taming/modules/misc/coord.py b/sd/stablediffusion/src/taming-transformers/taming/modules/misc/coord.py new file mode 100644 index 0000000000000000000000000000000000000000..ee69b0c897b6b382ae673622e420f55e494f5b09 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/modules/misc/coord.py @@ -0,0 +1,31 @@ +import torch + +class CoordStage(object): + def __init__(self, n_embed, down_factor): + self.n_embed = n_embed + self.down_factor = down_factor + + def eval(self): + return self + + def encode(self, c): + """fake vqmodel interface""" + assert 0.0 <= c.min() and c.max() <= 1.0 + b,ch,h,w = c.shape + assert ch == 1 + + c = torch.nn.functional.interpolate(c, scale_factor=1/self.down_factor, + mode="area") + c = c.clamp(0.0, 1.0) + c = self.n_embed*c + c_quant = c.round() + c_ind = c_quant.to(dtype=torch.long) + + info = None, None, c_ind + return c_quant, None, info + + def decode(self, c): + c = c/self.n_embed + c = torch.nn.functional.interpolate(c, scale_factor=self.down_factor, + mode="nearest") + return c diff --git a/sd/stablediffusion/src/taming-transformers/taming/modules/transformer/mingpt.py b/sd/stablediffusion/src/taming-transformers/taming/modules/transformer/mingpt.py new file mode 100644 index 0000000000000000000000000000000000000000..d14b7b68117f4b9f297b2929397cd4f55089334c --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/modules/transformer/mingpt.py @@ -0,0 +1,415 @@ +""" +taken from: https://github.com/karpathy/minGPT/ +GPT model: +- the initial stem consists of a combination of token encoding and a positional encoding +- the meat of it is a uniform sequence of Transformer blocks + - each Transformer is a sequential combination of a 1-hidden-layer MLP block and a self-attention block + - all blocks feed into a central residual pathway similar to resnets +- the final decoder is a linear projection into a vanilla Softmax classifier +""" + +import math +import logging + +import torch +import torch.nn as nn +from torch.nn import functional as F +from transformers import top_k_top_p_filtering + +logger = logging.getLogger(__name__) + + +class GPTConfig: + """ base GPT config, params common to all GPT versions """ + embd_pdrop = 0.1 + resid_pdrop = 0.1 + attn_pdrop = 0.1 + + def __init__(self, vocab_size, block_size, **kwargs): + self.vocab_size = vocab_size + self.block_size = block_size + for k,v in kwargs.items(): + setattr(self, k, v) + + +class GPT1Config(GPTConfig): + """ GPT-1 like network roughly 125M params """ + n_layer = 12 + n_head = 12 + n_embd = 768 + + +class CausalSelfAttention(nn.Module): + """ + A vanilla multi-head masked self-attention layer with a projection at the end. + It is possible to use torch.nn.MultiheadAttention here but I am including an + explicit implementation here to show that there is nothing too scary here. + """ + + def __init__(self, config): + super().__init__() + assert config.n_embd % config.n_head == 0 + # key, query, value projections for all heads + self.key = nn.Linear(config.n_embd, config.n_embd) + self.query = nn.Linear(config.n_embd, config.n_embd) + self.value = nn.Linear(config.n_embd, config.n_embd) + # regularization + self.attn_drop = nn.Dropout(config.attn_pdrop) + self.resid_drop = nn.Dropout(config.resid_pdrop) + # output projection + self.proj = nn.Linear(config.n_embd, config.n_embd) + # causal mask to ensure that attention is only applied to the left in the input sequence + mask = torch.tril(torch.ones(config.block_size, + config.block_size)) + if hasattr(config, "n_unmasked"): + mask[:config.n_unmasked, :config.n_unmasked] = 1 + self.register_buffer("mask", mask.view(1, 1, config.block_size, config.block_size)) + self.n_head = config.n_head + + def forward(self, x, layer_past=None): + B, T, C = x.size() + + # calculate query, key, values for all heads in batch and move head forward to be the batch dim + k = self.key(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs) + q = self.query(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs) + v = self.value(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs) + + present = torch.stack((k, v)) + if layer_past is not None: + past_key, past_value = layer_past + k = torch.cat((past_key, k), dim=-2) + v = torch.cat((past_value, v), dim=-2) + + # causal self-attention; Self-attend: (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T) + att = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(k.size(-1))) + if layer_past is None: + att = att.masked_fill(self.mask[:,:,:T,:T] == 0, float('-inf')) + + att = F.softmax(att, dim=-1) + att = self.attn_drop(att) + y = att @ v # (B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs) + y = y.transpose(1, 2).contiguous().view(B, T, C) # re-assemble all head outputs side by side + + # output projection + y = self.resid_drop(self.proj(y)) + return y, present # TODO: check that this does not break anything + + +class Block(nn.Module): + """ an unassuming Transformer block """ + def __init__(self, config): + super().__init__() + self.ln1 = nn.LayerNorm(config.n_embd) + self.ln2 = nn.LayerNorm(config.n_embd) + self.attn = CausalSelfAttention(config) + self.mlp = nn.Sequential( + nn.Linear(config.n_embd, 4 * config.n_embd), + nn.GELU(), # nice + nn.Linear(4 * config.n_embd, config.n_embd), + nn.Dropout(config.resid_pdrop), + ) + + def forward(self, x, layer_past=None, return_present=False): + # TODO: check that training still works + if return_present: assert not self.training + # layer past: tuple of length two with B, nh, T, hs + attn, present = self.attn(self.ln1(x), layer_past=layer_past) + + x = x + attn + x = x + self.mlp(self.ln2(x)) + if layer_past is not None or return_present: + return x, present + return x + + +class GPT(nn.Module): + """ the full GPT language model, with a context size of block_size """ + def __init__(self, vocab_size, block_size, n_layer=12, n_head=8, n_embd=256, + embd_pdrop=0., resid_pdrop=0., attn_pdrop=0., n_unmasked=0): + super().__init__() + config = GPTConfig(vocab_size=vocab_size, block_size=block_size, + embd_pdrop=embd_pdrop, resid_pdrop=resid_pdrop, attn_pdrop=attn_pdrop, + n_layer=n_layer, n_head=n_head, n_embd=n_embd, + n_unmasked=n_unmasked) + # input embedding stem + self.tok_emb = nn.Embedding(config.vocab_size, config.n_embd) + self.pos_emb = nn.Parameter(torch.zeros(1, config.block_size, config.n_embd)) + self.drop = nn.Dropout(config.embd_pdrop) + # transformer + self.blocks = nn.Sequential(*[Block(config) for _ in range(config.n_layer)]) + # decoder head + self.ln_f = nn.LayerNorm(config.n_embd) + self.head = nn.Linear(config.n_embd, config.vocab_size, bias=False) + self.block_size = config.block_size + self.apply(self._init_weights) + self.config = config + logger.info("number of parameters: %e", sum(p.numel() for p in self.parameters())) + + def get_block_size(self): + return self.block_size + + def _init_weights(self, module): + if isinstance(module, (nn.Linear, nn.Embedding)): + module.weight.data.normal_(mean=0.0, std=0.02) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + + def forward(self, idx, embeddings=None, targets=None): + # forward the GPT model + token_embeddings = self.tok_emb(idx) # each index maps to a (learnable) vector + + if embeddings is not None: # prepend explicit embeddings + token_embeddings = torch.cat((embeddings, token_embeddings), dim=1) + + t = token_embeddings.shape[1] + assert t <= self.block_size, "Cannot forward, model block size is exhausted." + position_embeddings = self.pos_emb[:, :t, :] # each position maps to a (learnable) vector + x = self.drop(token_embeddings + position_embeddings) + x = self.blocks(x) + x = self.ln_f(x) + logits = self.head(x) + + # if we are given some desired targets also calculate the loss + loss = None + if targets is not None: + loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1)) + + return logits, loss + + def forward_with_past(self, idx, embeddings=None, targets=None, past=None, past_length=None): + # inference only + assert not self.training + token_embeddings = self.tok_emb(idx) # each index maps to a (learnable) vector + if embeddings is not None: # prepend explicit embeddings + token_embeddings = torch.cat((embeddings, token_embeddings), dim=1) + + if past is not None: + assert past_length is not None + past = torch.cat(past, dim=-2) # n_layer, 2, b, nh, len_past, dim_head + past_shape = list(past.shape) + expected_shape = [self.config.n_layer, 2, idx.shape[0], self.config.n_head, past_length, self.config.n_embd//self.config.n_head] + assert past_shape == expected_shape, f"{past_shape} =/= {expected_shape}" + position_embeddings = self.pos_emb[:, past_length, :] # each position maps to a (learnable) vector + else: + position_embeddings = self.pos_emb[:, :token_embeddings.shape[1], :] + + x = self.drop(token_embeddings + position_embeddings) + presents = [] # accumulate over layers + for i, block in enumerate(self.blocks): + x, present = block(x, layer_past=past[i, ...] if past is not None else None, return_present=True) + presents.append(present) + + x = self.ln_f(x) + logits = self.head(x) + # if we are given some desired targets also calculate the loss + loss = None + if targets is not None: + loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1)) + + return logits, loss, torch.stack(presents) # _, _, n_layer, 2, b, nh, 1, dim_head + + +class DummyGPT(nn.Module): + # for debugging + def __init__(self, add_value=1): + super().__init__() + self.add_value = add_value + + def forward(self, idx): + return idx + self.add_value, None + + +class CodeGPT(nn.Module): + """Takes in semi-embeddings""" + def __init__(self, vocab_size, block_size, in_channels, n_layer=12, n_head=8, n_embd=256, + embd_pdrop=0., resid_pdrop=0., attn_pdrop=0., n_unmasked=0): + super().__init__() + config = GPTConfig(vocab_size=vocab_size, block_size=block_size, + embd_pdrop=embd_pdrop, resid_pdrop=resid_pdrop, attn_pdrop=attn_pdrop, + n_layer=n_layer, n_head=n_head, n_embd=n_embd, + n_unmasked=n_unmasked) + # input embedding stem + self.tok_emb = nn.Linear(in_channels, config.n_embd) + self.pos_emb = nn.Parameter(torch.zeros(1, config.block_size, config.n_embd)) + self.drop = nn.Dropout(config.embd_pdrop) + # transformer + self.blocks = nn.Sequential(*[Block(config) for _ in range(config.n_layer)]) + # decoder head + self.ln_f = nn.LayerNorm(config.n_embd) + self.head = nn.Linear(config.n_embd, config.vocab_size, bias=False) + self.block_size = config.block_size + self.apply(self._init_weights) + self.config = config + logger.info("number of parameters: %e", sum(p.numel() for p in self.parameters())) + + def get_block_size(self): + return self.block_size + + def _init_weights(self, module): + if isinstance(module, (nn.Linear, nn.Embedding)): + module.weight.data.normal_(mean=0.0, std=0.02) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + + def forward(self, idx, embeddings=None, targets=None): + # forward the GPT model + token_embeddings = self.tok_emb(idx) # each index maps to a (learnable) vector + + if embeddings is not None: # prepend explicit embeddings + token_embeddings = torch.cat((embeddings, token_embeddings), dim=1) + + t = token_embeddings.shape[1] + assert t <= self.block_size, "Cannot forward, model block size is exhausted." + position_embeddings = self.pos_emb[:, :t, :] # each position maps to a (learnable) vector + x = self.drop(token_embeddings + position_embeddings) + x = self.blocks(x) + x = self.taming_cinln_f(x) + logits = self.head(x) + + # if we are given some desired targets also calculate the loss + loss = None + if targets is not None: + loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1)) + + return logits, loss + + + +#### sampling utils + +def top_k_logits(logits, k): + v, ix = torch.topk(logits, k) + out = logits.clone() + out[out < v[:, [-1]]] = -float('Inf') + return out + +@torch.no_grad() +def sample(model, x, steps, temperature=1.0, sample=False, top_k=None): + """ + take a conditioning sequence of indices in x (of shape (b,t)) and predict the next token in + the sequence, feeding the predictions back into the model each time. Clearly the sampling + has quadratic complexity unlike an RNN that is only linear, and has a finite context window + of block_size, unlike an RNN that has an infinite context window. + """ + block_size = model.get_block_size() + model.eval() + for k in range(steps): + x_cond = x if x.size(1) <= block_size else x[:, -block_size:] # crop context if needed + logits, _ = model(x_cond) + # pluck the logits at the final step and scale by temperature + logits = logits[:, -1, :] / temperature + # optionally crop probabilities to only the top k options + if top_k is not None: + logits = top_k_logits(logits, top_k) + # apply softmax to convert to probabilities + probs = F.softmax(logits, dim=-1) + # sample from the distribution or take the most likely + if sample: + ix = torch.multinomial(probs, num_samples=1) + else: + _, ix = torch.topk(probs, k=1, dim=-1) + # append to the sequence and continue + x = torch.cat((x, ix), dim=1) + + return x + + +@torch.no_grad() +def sample_with_past(x, model, steps, temperature=1., sample_logits=True, + top_k=None, top_p=None, callback=None): + # x is conditioning + sample = x + cond_len = x.shape[1] + past = None + for n in range(steps): + if callback is not None: + callback(n) + logits, _, present = model.forward_with_past(x, past=past, past_length=(n+cond_len-1)) + if past is None: + past = [present] + else: + past.append(present) + logits = logits[:, -1, :] / temperature + if top_k is not None: + logits = top_k_top_p_filtering(logits, top_k=top_k, top_p=top_p) + + probs = F.softmax(logits, dim=-1) + if not sample_logits: + _, x = torch.topk(probs, k=1, dim=-1) + else: + x = torch.multinomial(probs, num_samples=1) + # append to the sequence and continue + sample = torch.cat((sample, x), dim=1) + del past + sample = sample[:, cond_len:] # cut conditioning off + return sample + + +#### clustering utils + +class KMeans(nn.Module): + def __init__(self, ncluster=512, nc=3, niter=10): + super().__init__() + self.ncluster = ncluster + self.nc = nc + self.niter = niter + self.shape = (3,32,32) + self.register_buffer("C", torch.zeros(self.ncluster,nc)) + self.register_buffer('initialized', torch.tensor(0, dtype=torch.uint8)) + + def is_initialized(self): + return self.initialized.item() == 1 + + @torch.no_grad() + def initialize(self, x): + N, D = x.shape + assert D == self.nc, D + c = x[torch.randperm(N)[:self.ncluster]] # init clusters at random + for i in range(self.niter): + # assign all pixels to the closest codebook element + a = ((x[:, None, :] - c[None, :, :])**2).sum(-1).argmin(1) + # move each codebook element to be the mean of the pixels that assigned to it + c = torch.stack([x[a==k].mean(0) for k in range(self.ncluster)]) + # re-assign any poorly positioned codebook elements + nanix = torch.any(torch.isnan(c), dim=1) + ndead = nanix.sum().item() + print('done step %d/%d, re-initialized %d dead clusters' % (i+1, self.niter, ndead)) + c[nanix] = x[torch.randperm(N)[:ndead]] # re-init dead clusters + + self.C.copy_(c) + self.initialized.fill_(1) + + + def forward(self, x, reverse=False, shape=None): + if not reverse: + # flatten + bs,c,h,w = x.shape + assert c == self.nc + x = x.reshape(bs,c,h*w,1) + C = self.C.permute(1,0) + C = C.reshape(1,c,1,self.ncluster) + a = ((x-C)**2).sum(1).argmin(-1) # bs, h*w indices + return a + else: + # flatten + bs, HW = x.shape + """ + c = self.C.reshape( 1, self.nc, 1, self.ncluster) + c = c[bs*[0],:,:,:] + c = c[:,:,HW*[0],:] + x = x.reshape(bs, 1, HW, 1) + x = x[:,3*[0],:,:] + x = torch.gather(c, dim=3, index=x) + """ + x = self.C[x] + x = x.permute(0,2,1) + shape = shape if shape is not None else self.shape + x = x.reshape(bs, *shape) + + return x diff --git a/sd/stablediffusion/src/taming-transformers/taming/modules/transformer/permuter.py b/sd/stablediffusion/src/taming-transformers/taming/modules/transformer/permuter.py new file mode 100644 index 0000000000000000000000000000000000000000..0d43bb135adde38d94bf18a7e5edaa4523cd95cf --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/modules/transformer/permuter.py @@ -0,0 +1,248 @@ +import torch +import torch.nn as nn +import numpy as np + + +class AbstractPermuter(nn.Module): + def __init__(self, *args, **kwargs): + super().__init__() + def forward(self, x, reverse=False): + raise NotImplementedError + + +class Identity(AbstractPermuter): + def __init__(self): + super().__init__() + + def forward(self, x, reverse=False): + return x + + +class Subsample(AbstractPermuter): + def __init__(self, H, W): + super().__init__() + C = 1 + indices = np.arange(H*W).reshape(C,H,W) + while min(H, W) > 1: + indices = indices.reshape(C,H//2,2,W//2,2) + indices = indices.transpose(0,2,4,1,3) + indices = indices.reshape(C*4,H//2, W//2) + H = H//2 + W = W//2 + C = C*4 + assert H == W == 1 + idx = torch.tensor(indices.ravel()) + self.register_buffer('forward_shuffle_idx', + nn.Parameter(idx, requires_grad=False)) + self.register_buffer('backward_shuffle_idx', + nn.Parameter(torch.argsort(idx), requires_grad=False)) + + def forward(self, x, reverse=False): + if not reverse: + return x[:, self.forward_shuffle_idx] + else: + return x[:, self.backward_shuffle_idx] + + +def mortonify(i, j): + """(i,j) index to linear morton code""" + i = np.uint64(i) + j = np.uint64(j) + + z = np.uint(0) + + for pos in range(32): + z = (z | + ((j & (np.uint64(1) << np.uint64(pos))) << np.uint64(pos)) | + ((i & (np.uint64(1) << np.uint64(pos))) << np.uint64(pos+1)) + ) + return z + + +class ZCurve(AbstractPermuter): + def __init__(self, H, W): + super().__init__() + reverseidx = [np.int64(mortonify(i,j)) for i in range(H) for j in range(W)] + idx = np.argsort(reverseidx) + idx = torch.tensor(idx) + reverseidx = torch.tensor(reverseidx) + self.register_buffer('forward_shuffle_idx', + idx) + self.register_buffer('backward_shuffle_idx', + reverseidx) + + def forward(self, x, reverse=False): + if not reverse: + return x[:, self.forward_shuffle_idx] + else: + return x[:, self.backward_shuffle_idx] + + +class SpiralOut(AbstractPermuter): + def __init__(self, H, W): + super().__init__() + assert H == W + size = W + indices = np.arange(size*size).reshape(size,size) + + i0 = size//2 + j0 = size//2-1 + + i = i0 + j = j0 + + idx = [indices[i0, j0]] + step_mult = 0 + for c in range(1, size//2+1): + step_mult += 1 + # steps left + for k in range(step_mult): + i = i - 1 + j = j + idx.append(indices[i, j]) + + # step down + for k in range(step_mult): + i = i + j = j + 1 + idx.append(indices[i, j]) + + step_mult += 1 + if c < size//2: + # step right + for k in range(step_mult): + i = i + 1 + j = j + idx.append(indices[i, j]) + + # step up + for k in range(step_mult): + i = i + j = j - 1 + idx.append(indices[i, j]) + else: + # end reached + for k in range(step_mult-1): + i = i + 1 + idx.append(indices[i, j]) + + assert len(idx) == size*size + idx = torch.tensor(idx) + self.register_buffer('forward_shuffle_idx', idx) + self.register_buffer('backward_shuffle_idx', torch.argsort(idx)) + + def forward(self, x, reverse=False): + if not reverse: + return x[:, self.forward_shuffle_idx] + else: + return x[:, self.backward_shuffle_idx] + + +class SpiralIn(AbstractPermuter): + def __init__(self, H, W): + super().__init__() + assert H == W + size = W + indices = np.arange(size*size).reshape(size,size) + + i0 = size//2 + j0 = size//2-1 + + i = i0 + j = j0 + + idx = [indices[i0, j0]] + step_mult = 0 + for c in range(1, size//2+1): + step_mult += 1 + # steps left + for k in range(step_mult): + i = i - 1 + j = j + idx.append(indices[i, j]) + + # step down + for k in range(step_mult): + i = i + j = j + 1 + idx.append(indices[i, j]) + + step_mult += 1 + if c < size//2: + # step right + for k in range(step_mult): + i = i + 1 + j = j + idx.append(indices[i, j]) + + # step up + for k in range(step_mult): + i = i + j = j - 1 + idx.append(indices[i, j]) + else: + # end reached + for k in range(step_mult-1): + i = i + 1 + idx.append(indices[i, j]) + + assert len(idx) == size*size + idx = idx[::-1] + idx = torch.tensor(idx) + self.register_buffer('forward_shuffle_idx', idx) + self.register_buffer('backward_shuffle_idx', torch.argsort(idx)) + + def forward(self, x, reverse=False): + if not reverse: + return x[:, self.forward_shuffle_idx] + else: + return x[:, self.backward_shuffle_idx] + + +class Random(nn.Module): + def __init__(self, H, W): + super().__init__() + indices = np.random.RandomState(1).permutation(H*W) + idx = torch.tensor(indices.ravel()) + self.register_buffer('forward_shuffle_idx', idx) + self.register_buffer('backward_shuffle_idx', torch.argsort(idx)) + + def forward(self, x, reverse=False): + if not reverse: + return x[:, self.forward_shuffle_idx] + else: + return x[:, self.backward_shuffle_idx] + + +class AlternateParsing(AbstractPermuter): + def __init__(self, H, W): + super().__init__() + indices = np.arange(W*H).reshape(H,W) + for i in range(1, H, 2): + indices[i, :] = indices[i, ::-1] + idx = indices.flatten() + assert len(idx) == H*W + idx = torch.tensor(idx) + self.register_buffer('forward_shuffle_idx', idx) + self.register_buffer('backward_shuffle_idx', torch.argsort(idx)) + + def forward(self, x, reverse=False): + if not reverse: + return x[:, self.forward_shuffle_idx] + else: + return x[:, self.backward_shuffle_idx] + + +if __name__ == "__main__": + p0 = AlternateParsing(16, 16) + print(p0.forward_shuffle_idx) + print(p0.backward_shuffle_idx) + + x = torch.randint(0, 768, size=(11, 256)) + y = p0(x) + xre = p0(y, reverse=True) + assert torch.equal(x, xre) + + p1 = SpiralOut(2, 2) + print(p1.forward_shuffle_idx) + print(p1.backward_shuffle_idx) diff --git a/sd/stablediffusion/src/taming-transformers/taming/modules/util.py b/sd/stablediffusion/src/taming-transformers/taming/modules/util.py new file mode 100644 index 0000000000000000000000000000000000000000..9ee16385d8b1342a2d60a5f1aa5cadcfbe934bd8 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/modules/util.py @@ -0,0 +1,130 @@ +import torch +import torch.nn as nn + + +def count_params(model): + total_params = sum(p.numel() for p in model.parameters()) + return total_params + + +class ActNorm(nn.Module): + def __init__(self, num_features, logdet=False, affine=True, + allow_reverse_init=False): + assert affine + super().__init__() + self.logdet = logdet + self.loc = nn.Parameter(torch.zeros(1, num_features, 1, 1)) + self.scale = nn.Parameter(torch.ones(1, num_features, 1, 1)) + self.allow_reverse_init = allow_reverse_init + + self.register_buffer('initialized', torch.tensor(0, dtype=torch.uint8)) + + def initialize(self, input): + with torch.no_grad(): + flatten = input.permute(1, 0, 2, 3).contiguous().view(input.shape[1], -1) + mean = ( + flatten.mean(1) + .unsqueeze(1) + .unsqueeze(2) + .unsqueeze(3) + .permute(1, 0, 2, 3) + ) + std = ( + flatten.std(1) + .unsqueeze(1) + .unsqueeze(2) + .unsqueeze(3) + .permute(1, 0, 2, 3) + ) + + self.loc.data.copy_(-mean) + self.scale.data.copy_(1 / (std + 1e-6)) + + def forward(self, input, reverse=False): + if reverse: + return self.reverse(input) + if len(input.shape) == 2: + input = input[:,:,None,None] + squeeze = True + else: + squeeze = False + + _, _, height, width = input.shape + + if self.training and self.initialized.item() == 0: + self.initialize(input) + self.initialized.fill_(1) + + h = self.scale * (input + self.loc) + + if squeeze: + h = h.squeeze(-1).squeeze(-1) + + if self.logdet: + log_abs = torch.log(torch.abs(self.scale)) + logdet = height*width*torch.sum(log_abs) + logdet = logdet * torch.ones(input.shape[0]).to(input) + return h, logdet + + return h + + def reverse(self, output): + if self.training and self.initialized.item() == 0: + if not self.allow_reverse_init: + raise RuntimeError( + "Initializing ActNorm in reverse direction is " + "disabled by default. Use allow_reverse_init=True to enable." + ) + else: + self.initialize(output) + self.initialized.fill_(1) + + if len(output.shape) == 2: + output = output[:,:,None,None] + squeeze = True + else: + squeeze = False + + h = output / self.scale - self.loc + + if squeeze: + h = h.squeeze(-1).squeeze(-1) + return h + + +class AbstractEncoder(nn.Module): + def __init__(self): + super().__init__() + + def encode(self, *args, **kwargs): + raise NotImplementedError + + +class Labelator(AbstractEncoder): + """Net2Net Interface for Class-Conditional Model""" + def __init__(self, n_classes, quantize_interface=True): + super().__init__() + self.n_classes = n_classes + self.quantize_interface = quantize_interface + + def encode(self, c): + c = c[:,None] + if self.quantize_interface: + return c, None, [None, None, c.long()] + return c + + +class SOSProvider(AbstractEncoder): + # for unconditional training + def __init__(self, sos_token, quantize_interface=True): + super().__init__() + self.sos_token = sos_token + self.quantize_interface = quantize_interface + + def encode(self, x): + # get batch size from data and replicate sos_token + c = torch.ones(x.shape[0], 1)*self.sos_token + c = c.long().to(x.device) + if self.quantize_interface: + return c, None, [None, None, c] + return c diff --git a/sd/stablediffusion/src/taming-transformers/taming/modules/vqvae/__pycache__/quantize.cpython-39.pyc b/sd/stablediffusion/src/taming-transformers/taming/modules/vqvae/__pycache__/quantize.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..19b7c296fc3208706ff45701269683ea65516262 Binary files /dev/null and b/sd/stablediffusion/src/taming-transformers/taming/modules/vqvae/__pycache__/quantize.cpython-39.pyc differ diff --git a/sd/stablediffusion/src/taming-transformers/taming/modules/vqvae/quantize.py b/sd/stablediffusion/src/taming-transformers/taming/modules/vqvae/quantize.py new file mode 100644 index 0000000000000000000000000000000000000000..d75544e41fa01bce49dd822b1037963d62f79b51 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/modules/vqvae/quantize.py @@ -0,0 +1,445 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +import numpy as np +from torch import einsum +from einops import rearrange + + +class VectorQuantizer(nn.Module): + """ + see https://github.com/MishaLaskin/vqvae/blob/d761a999e2267766400dc646d82d3ac3657771d4/models/quantizer.py + ____________________________________________ + Discretization bottleneck part of the VQ-VAE. + Inputs: + - n_e : number of embeddings + - e_dim : dimension of embedding + - beta : commitment cost used in loss term, beta * ||z_e(x)-sg[e]||^2 + _____________________________________________ + """ + + # NOTE: this class contains a bug regarding beta; see VectorQuantizer2 for + # a fix and use legacy=False to apply that fix. VectorQuantizer2 can be + # used wherever VectorQuantizer has been used before and is additionally + # more efficient. + def __init__(self, n_e, e_dim, beta): + super(VectorQuantizer, self).__init__() + self.n_e = n_e + self.e_dim = e_dim + self.beta = beta + + self.embedding = nn.Embedding(self.n_e, self.e_dim) + self.embedding.weight.data.uniform_(-1.0 / self.n_e, 1.0 / self.n_e) + + def forward(self, z): + """ + Inputs the output of the encoder network z and maps it to a discrete + one-hot vector that is the index of the closest embedding vector e_j + z (continuous) -> z_q (discrete) + z.shape = (batch, channel, height, width) + quantization pipeline: + 1. get encoder input (B,C,H,W) + 2. flatten input to (B*H*W,C) + """ + # reshape z -> (batch, height, width, channel) and flatten + z = z.permute(0, 2, 3, 1).contiguous() + z_flattened = z.view(-1, self.e_dim) + # distances from z to embeddings e_j (z - e)^2 = z^2 + e^2 - 2 e * z + + d = torch.sum(z_flattened ** 2, dim=1, keepdim=True) + \ + torch.sum(self.embedding.weight**2, dim=1) - 2 * \ + torch.matmul(z_flattened, self.embedding.weight.t()) + + ## could possible replace this here + # #\start... + # find closest encodings + min_encoding_indices = torch.argmin(d, dim=1).unsqueeze(1) + + min_encodings = torch.zeros( + min_encoding_indices.shape[0], self.n_e).to(z) + min_encodings.scatter_(1, min_encoding_indices, 1) + + # dtype min encodings: torch.float32 + # min_encodings shape: torch.Size([2048, 512]) + # min_encoding_indices.shape: torch.Size([2048, 1]) + + # get quantized latent vectors + z_q = torch.matmul(min_encodings, self.embedding.weight).view(z.shape) + #.........\end + + # with: + # .........\start + #min_encoding_indices = torch.argmin(d, dim=1) + #z_q = self.embedding(min_encoding_indices) + # ......\end......... (TODO) + + # compute loss for embedding + loss = torch.mean((z_q.detach()-z)**2) + self.beta * \ + torch.mean((z_q - z.detach()) ** 2) + + # preserve gradients + z_q = z + (z_q - z).detach() + + # perplexity + e_mean = torch.mean(min_encodings, dim=0) + perplexity = torch.exp(-torch.sum(e_mean * torch.log(e_mean + 1e-10))) + + # reshape back to match original input shape + z_q = z_q.permute(0, 3, 1, 2).contiguous() + + return z_q, loss, (perplexity, min_encodings, min_encoding_indices) + + def get_codebook_entry(self, indices, shape): + # shape specifying (batch, height, width, channel) + # TODO: check for more easy handling with nn.Embedding + min_encodings = torch.zeros(indices.shape[0], self.n_e).to(indices) + min_encodings.scatter_(1, indices[:,None], 1) + + # get quantized latent vectors + z_q = torch.matmul(min_encodings.float(), self.embedding.weight) + + if shape is not None: + z_q = z_q.view(shape) + + # reshape back to match original input shape + z_q = z_q.permute(0, 3, 1, 2).contiguous() + + return z_q + + +class GumbelQuantize(nn.Module): + """ + credit to @karpathy: https://github.com/karpathy/deep-vector-quantization/blob/main/model.py (thanks!) + Gumbel Softmax trick quantizer + Categorical Reparameterization with Gumbel-Softmax, Jang et al. 2016 + https://arxiv.org/abs/1611.01144 + """ + def __init__(self, num_hiddens, embedding_dim, n_embed, straight_through=True, + kl_weight=5e-4, temp_init=1.0, use_vqinterface=True, + remap=None, unknown_index="random"): + super().__init__() + + self.embedding_dim = embedding_dim + self.n_embed = n_embed + + self.straight_through = straight_through + self.temperature = temp_init + self.kl_weight = kl_weight + + self.proj = nn.Conv2d(num_hiddens, n_embed, 1) + self.embed = nn.Embedding(n_embed, embedding_dim) + + self.use_vqinterface = use_vqinterface + + self.remap = remap + if self.remap is not None: + self.register_buffer("used", torch.tensor(np.load(self.remap))) + self.re_embed = self.used.shape[0] + self.unknown_index = unknown_index # "random" or "extra" or integer + if self.unknown_index == "extra": + self.unknown_index = self.re_embed + self.re_embed = self.re_embed+1 + print(f"Remapping {self.n_embed} indices to {self.re_embed} indices. " + f"Using {self.unknown_index} for unknown indices.") + else: + self.re_embed = n_embed + + def remap_to_used(self, inds): + ishape = inds.shape + assert len(ishape)>1 + inds = inds.reshape(ishape[0],-1) + used = self.used.to(inds) + match = (inds[:,:,None]==used[None,None,...]).long() + new = match.argmax(-1) + unknown = match.sum(2)<1 + if self.unknown_index == "random": + new[unknown]=torch.randint(0,self.re_embed,size=new[unknown].shape).to(device=new.device) + else: + new[unknown] = self.unknown_index + return new.reshape(ishape) + + def unmap_to_all(self, inds): + ishape = inds.shape + assert len(ishape)>1 + inds = inds.reshape(ishape[0],-1) + used = self.used.to(inds) + if self.re_embed > self.used.shape[0]: # extra token + inds[inds>=self.used.shape[0]] = 0 # simply set to zero + back=torch.gather(used[None,:][inds.shape[0]*[0],:], 1, inds) + return back.reshape(ishape) + + def forward(self, z, temp=None, return_logits=False): + # force hard = True when we are in eval mode, as we must quantize. actually, always true seems to work + hard = self.straight_through if self.training else True + temp = self.temperature if temp is None else temp + + logits = self.proj(z) + if self.remap is not None: + # continue only with used logits + full_zeros = torch.zeros_like(logits) + logits = logits[:,self.used,...] + + soft_one_hot = F.gumbel_softmax(logits, tau=temp, dim=1, hard=hard) + if self.remap is not None: + # go back to all entries but unused set to zero + full_zeros[:,self.used,...] = soft_one_hot + soft_one_hot = full_zeros + z_q = einsum('b n h w, n d -> b d h w', soft_one_hot, self.embed.weight) + + # + kl divergence to the prior loss + qy = F.softmax(logits, dim=1) + diff = self.kl_weight * torch.sum(qy * torch.log(qy * self.n_embed + 1e-10), dim=1).mean() + + ind = soft_one_hot.argmax(dim=1) + if self.remap is not None: + ind = self.remap_to_used(ind) + if self.use_vqinterface: + if return_logits: + return z_q, diff, (None, None, ind), logits + return z_q, diff, (None, None, ind) + return z_q, diff, ind + + def get_codebook_entry(self, indices, shape): + b, h, w, c = shape + assert b*h*w == indices.shape[0] + indices = rearrange(indices, '(b h w) -> b h w', b=b, h=h, w=w) + if self.remap is not None: + indices = self.unmap_to_all(indices) + one_hot = F.one_hot(indices, num_classes=self.n_embed).permute(0, 3, 1, 2).float() + z_q = einsum('b n h w, n d -> b d h w', one_hot, self.embed.weight) + return z_q + + +class VectorQuantizer2(nn.Module): + """ + Improved version over VectorQuantizer, can be used as a drop-in replacement. Mostly + avoids costly matrix multiplications and allows for post-hoc remapping of indices. + """ + # NOTE: due to a bug the beta term was applied to the wrong term. for + # backwards compatibility we use the buggy version by default, but you can + # specify legacy=False to fix it. + def __init__(self, n_e, e_dim, beta, remap=None, unknown_index="random", + sane_index_shape=False, legacy=True): + super().__init__() + self.n_e = n_e + self.e_dim = e_dim + self.beta = beta + self.legacy = legacy + + self.embedding = nn.Embedding(self.n_e, self.e_dim) + self.embedding.weight.data.uniform_(-1.0 / self.n_e, 1.0 / self.n_e) + + self.remap = remap + if self.remap is not None: + self.register_buffer("used", torch.tensor(np.load(self.remap))) + self.re_embed = self.used.shape[0] + self.unknown_index = unknown_index # "random" or "extra" or integer + if self.unknown_index == "extra": + self.unknown_index = self.re_embed + self.re_embed = self.re_embed+1 + print(f"Remapping {self.n_e} indices to {self.re_embed} indices. " + f"Using {self.unknown_index} for unknown indices.") + else: + self.re_embed = n_e + + self.sane_index_shape = sane_index_shape + + def remap_to_used(self, inds): + ishape = inds.shape + assert len(ishape)>1 + inds = inds.reshape(ishape[0],-1) + used = self.used.to(inds) + match = (inds[:,:,None]==used[None,None,...]).long() + new = match.argmax(-1) + unknown = match.sum(2)<1 + if self.unknown_index == "random": + new[unknown]=torch.randint(0,self.re_embed,size=new[unknown].shape).to(device=new.device) + else: + new[unknown] = self.unknown_index + return new.reshape(ishape) + + def unmap_to_all(self, inds): + ishape = inds.shape + assert len(ishape)>1 + inds = inds.reshape(ishape[0],-1) + used = self.used.to(inds) + if self.re_embed > self.used.shape[0]: # extra token + inds[inds>=self.used.shape[0]] = 0 # simply set to zero + back=torch.gather(used[None,:][inds.shape[0]*[0],:], 1, inds) + return back.reshape(ishape) + + def forward(self, z, temp=None, rescale_logits=False, return_logits=False): + assert temp is None or temp==1.0, "Only for interface compatible with Gumbel" + assert rescale_logits==False, "Only for interface compatible with Gumbel" + assert return_logits==False, "Only for interface compatible with Gumbel" + # reshape z -> (batch, height, width, channel) and flatten + z = rearrange(z, 'b c h w -> b h w c').contiguous() + z_flattened = z.view(-1, self.e_dim) + # distances from z to embeddings e_j (z - e)^2 = z^2 + e^2 - 2 e * z + + d = torch.sum(z_flattened ** 2, dim=1, keepdim=True) + \ + torch.sum(self.embedding.weight**2, dim=1) - 2 * \ + torch.einsum('bd,dn->bn', z_flattened, rearrange(self.embedding.weight, 'n d -> d n')) + + min_encoding_indices = torch.argmin(d, dim=1) + z_q = self.embedding(min_encoding_indices).view(z.shape) + perplexity = None + min_encodings = None + + # compute loss for embedding + if not self.legacy: + loss = self.beta * torch.mean((z_q.detach()-z)**2) + \ + torch.mean((z_q - z.detach()) ** 2) + else: + loss = torch.mean((z_q.detach()-z)**2) + self.beta * \ + torch.mean((z_q - z.detach()) ** 2) + + # preserve gradients + z_q = z + (z_q - z).detach() + + # reshape back to match original input shape + z_q = rearrange(z_q, 'b h w c -> b c h w').contiguous() + + if self.remap is not None: + min_encoding_indices = min_encoding_indices.reshape(z.shape[0],-1) # add batch axis + min_encoding_indices = self.remap_to_used(min_encoding_indices) + min_encoding_indices = min_encoding_indices.reshape(-1,1) # flatten + + if self.sane_index_shape: + min_encoding_indices = min_encoding_indices.reshape( + z_q.shape[0], z_q.shape[2], z_q.shape[3]) + + return z_q, loss, (perplexity, min_encodings, min_encoding_indices) + + def get_codebook_entry(self, indices, shape): + # shape specifying (batch, height, width, channel) + if self.remap is not None: + indices = indices.reshape(shape[0],-1) # add batch axis + indices = self.unmap_to_all(indices) + indices = indices.reshape(-1) # flatten again + + # get quantized latent vectors + z_q = self.embedding(indices) + + if shape is not None: + z_q = z_q.view(shape) + # reshape back to match original input shape + z_q = z_q.permute(0, 3, 1, 2).contiguous() + + return z_q + +class EmbeddingEMA(nn.Module): + def __init__(self, num_tokens, codebook_dim, decay=0.99, eps=1e-5): + super().__init__() + self.decay = decay + self.eps = eps + weight = torch.randn(num_tokens, codebook_dim) + self.weight = nn.Parameter(weight, requires_grad = False) + self.cluster_size = nn.Parameter(torch.zeros(num_tokens), requires_grad = False) + self.embed_avg = nn.Parameter(weight.clone(), requires_grad = False) + self.update = True + + def forward(self, embed_id): + return F.embedding(embed_id, self.weight) + + def cluster_size_ema_update(self, new_cluster_size): + self.cluster_size.data.mul_(self.decay).add_(new_cluster_size, alpha=1 - self.decay) + + def embed_avg_ema_update(self, new_embed_avg): + self.embed_avg.data.mul_(self.decay).add_(new_embed_avg, alpha=1 - self.decay) + + def weight_update(self, num_tokens): + n = self.cluster_size.sum() + smoothed_cluster_size = ( + (self.cluster_size + self.eps) / (n + num_tokens * self.eps) * n + ) + #normalize embedding average with smoothed cluster size + embed_normalized = self.embed_avg / smoothed_cluster_size.unsqueeze(1) + self.weight.data.copy_(embed_normalized) + + +class EMAVectorQuantizer(nn.Module): + def __init__(self, n_embed, embedding_dim, beta, decay=0.99, eps=1e-5, + remap=None, unknown_index="random"): + super().__init__() + self.codebook_dim = codebook_dim + self.num_tokens = num_tokens + self.beta = beta + self.embedding = EmbeddingEMA(self.num_tokens, self.codebook_dim, decay, eps) + + self.remap = remap + if self.remap is not None: + self.register_buffer("used", torch.tensor(np.load(self.remap))) + self.re_embed = self.used.shape[0] + self.unknown_index = unknown_index # "random" or "extra" or integer + if self.unknown_index == "extra": + self.unknown_index = self.re_embed + self.re_embed = self.re_embed+1 + print(f"Remapping {self.n_embed} indices to {self.re_embed} indices. " + f"Using {self.unknown_index} for unknown indices.") + else: + self.re_embed = n_embed + + def remap_to_used(self, inds): + ishape = inds.shape + assert len(ishape)>1 + inds = inds.reshape(ishape[0],-1) + used = self.used.to(inds) + match = (inds[:,:,None]==used[None,None,...]).long() + new = match.argmax(-1) + unknown = match.sum(2)<1 + if self.unknown_index == "random": + new[unknown]=torch.randint(0,self.re_embed,size=new[unknown].shape).to(device=new.device) + else: + new[unknown] = self.unknown_index + return new.reshape(ishape) + + def unmap_to_all(self, inds): + ishape = inds.shape + assert len(ishape)>1 + inds = inds.reshape(ishape[0],-1) + used = self.used.to(inds) + if self.re_embed > self.used.shape[0]: # extra token + inds[inds>=self.used.shape[0]] = 0 # simply set to zero + back=torch.gather(used[None,:][inds.shape[0]*[0],:], 1, inds) + return back.reshape(ishape) + + def forward(self, z): + # reshape z -> (batch, height, width, channel) and flatten + #z, 'b c h w -> b h w c' + z = rearrange(z, 'b c h w -> b h w c') + z_flattened = z.reshape(-1, self.codebook_dim) + + # distances from z to embeddings e_j (z - e)^2 = z^2 + e^2 - 2 e * z + d = z_flattened.pow(2).sum(dim=1, keepdim=True) + \ + self.embedding.weight.pow(2).sum(dim=1) - 2 * \ + torch.einsum('bd,nd->bn', z_flattened, self.embedding.weight) # 'n d -> d n' + + + encoding_indices = torch.argmin(d, dim=1) + + z_q = self.embedding(encoding_indices).view(z.shape) + encodings = F.one_hot(encoding_indices, self.num_tokens).type(z.dtype) + avg_probs = torch.mean(encodings, dim=0) + perplexity = torch.exp(-torch.sum(avg_probs * torch.log(avg_probs + 1e-10))) + + if self.training and self.embedding.update: + #EMA cluster size + encodings_sum = encodings.sum(0) + self.embedding.cluster_size_ema_update(encodings_sum) + #EMA embedding average + embed_sum = encodings.transpose(0,1) @ z_flattened + self.embedding.embed_avg_ema_update(embed_sum) + #normalize embed_avg and update weight + self.embedding.weight_update(self.num_tokens) + + # compute loss for embedding + loss = self.beta * F.mse_loss(z_q.detach(), z) + + # preserve gradients + z_q = z + (z_q - z).detach() + + # reshape back to match original input shape + #z_q, 'b h w c -> b c h w' + z_q = rearrange(z_q, 'b h w c -> b c h w') + return z_q, loss, (perplexity, encodings, encoding_indices) diff --git a/sd/stablediffusion/src/taming-transformers/taming/util.py b/sd/stablediffusion/src/taming-transformers/taming/util.py new file mode 100644 index 0000000000000000000000000000000000000000..06053e5defb87977f9ab07e69bf4da12201de9b7 --- /dev/null +++ b/sd/stablediffusion/src/taming-transformers/taming/util.py @@ -0,0 +1,157 @@ +import os, hashlib +import requests +from tqdm import tqdm + +URL_MAP = { + "vgg_lpips": "https://heibox.uni-heidelberg.de/f/607503859c864bc1b30b/?dl=1" +} + +CKPT_MAP = { + "vgg_lpips": "vgg.pth" +} + +MD5_MAP = { + "vgg_lpips": "d507d7349b931f0638a25a48a722f98a" +} + + +def download(url, local_path, chunk_size=1024): + os.makedirs(os.path.split(local_path)[0], exist_ok=True) + with requests.get(url, stream=True) as r: + total_size = int(r.headers.get("content-length", 0)) + with tqdm(total=total_size, unit="B", unit_scale=True) as pbar: + with open(local_path, "wb") as f: + for data in r.iter_content(chunk_size=chunk_size): + if data: + f.write(data) + pbar.update(chunk_size) + + +def md5_hash(path): + with open(path, "rb") as f: + content = f.read() + return hashlib.md5(content).hexdigest() + + +def get_ckpt_path(name, root, check=False): + assert name in URL_MAP + path = os.path.join(root, CKPT_MAP[name]) + if not os.path.exists(path) or (check and not md5_hash(path) == MD5_MAP[name]): + print("Downloading {} model from {} to {}".format(name, URL_MAP[name], path)) + download(URL_MAP[name], path) + md5 = md5_hash(path) + assert md5 == MD5_MAP[name], md5 + return path + + +class KeyNotFoundError(Exception): + def __init__(self, cause, keys=None, visited=None): + self.cause = cause + self.keys = keys + self.visited = visited + messages = list() + if keys is not None: + messages.append("Key not found: {}".format(keys)) + if visited is not None: + messages.append("Visited: {}".format(visited)) + messages.append("Cause:\n{}".format(cause)) + message = "\n".join(messages) + super().__init__(message) + + +def retrieve( + list_or_dict, key, splitval="/", default=None, expand=True, pass_success=False +): + """Given a nested list or dict return the desired value at key expanding + callable nodes if necessary and :attr:`expand` is ``True``. The expansion + is done in-place. + + Parameters + ---------- + list_or_dict : list or dict + Possibly nested list or dictionary. + key : str + key/to/value, path like string describing all keys necessary to + consider to get to the desired value. List indices can also be + passed here. + splitval : str + String that defines the delimiter between keys of the + different depth levels in `key`. + default : obj + Value returned if :attr:`key` is not found. + expand : bool + Whether to expand callable nodes on the path or not. + + Returns + ------- + The desired value or if :attr:`default` is not ``None`` and the + :attr:`key` is not found returns ``default``. + + Raises + ------ + Exception if ``key`` not in ``list_or_dict`` and :attr:`default` is + ``None``. + """ + + keys = key.split(splitval) + + success = True + try: + visited = [] + parent = None + last_key = None + for key in keys: + if callable(list_or_dict): + if not expand: + raise KeyNotFoundError( + ValueError( + "Trying to get past callable node with expand=False." + ), + keys=keys, + visited=visited, + ) + list_or_dict = list_or_dict() + parent[last_key] = list_or_dict + + last_key = key + parent = list_or_dict + + try: + if isinstance(list_or_dict, dict): + list_or_dict = list_or_dict[key] + else: + list_or_dict = list_or_dict[int(key)] + except (KeyError, IndexError, ValueError) as e: + raise KeyNotFoundError(e, keys=keys, visited=visited) + + visited += [key] + # final expansion of retrieved value + if expand and callable(list_or_dict): + list_or_dict = list_or_dict() + parent[last_key] = list_or_dict + except KeyNotFoundError as e: + if default is None: + raise e + else: + list_or_dict = default + success = False + + if not pass_success: + return list_or_dict + else: + return list_or_dict, success + + +if __name__ == "__main__": + config = {"keya": "a", + "keyb": "b", + "keyc": + {"cc1": 1, + "cc2": 2, + } + } + from omegaconf import OmegaConf + config = OmegaConf.create(config) + print(config) + retrieve(config, "keya") +