From b011d73ca880ae3d7e5c3036eef53e6b025c8583 Mon Sep 17 00:00:00 2001 From: flowtyone Date: Mon, 23 Oct 2023 12:50:09 +0300 Subject: [PATCH] initial commit --- .gitignore | 1 + LICENSE | 674 ++++++++++++++++++++++++++++++++++++++++++ README.md | 8 + __init__.py | 2 + lcm/__init__.py | 0 lcm/lcm_pipeline.py | 269 +++++++++++++++++ lcm/lcm_scheduler.py | 498 +++++++++++++++++++++++++++++++ nodes.py | 71 +++++ requirements.txt | 1 + sample.png | Bin 0 -> 85612 bytes scheduler_config.json | 19 ++ 11 files changed, 1543 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 __init__.py create mode 100644 lcm/__init__.py create mode 100644 lcm/lcm_pipeline.py create mode 100644 lcm/lcm_scheduler.py create mode 100644 nodes.py create mode 100644 requirements.txt create mode 100644 sample.png create mode 100644 scheduler_config.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + 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 General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is 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. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + 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. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + 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 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. Use with the GNU Affero General Public License. + + 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 Affero 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 special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 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 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 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + 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 GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8d8b709 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# comfyui-flowty-lcm +This is a comfyui early testing node for LCM, adapted from https://github.com/0xbitches/sd-webui-lcm + +It uses the diffusers backend unfortunately and not comfy's model loading mechanism. But the intention here is just to be able to execute lcm inside comfy. + +*You need to use a special lcm model for this - the only available checkpoint currently is [LCM_Dreamshaper_v7](https://huggingface.co/SimianLuo/LCM_Dreamshaper_v7).* + +![Context Node](./sample.png) \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..b0dbf9b --- /dev/null +++ b/__init__.py @@ -0,0 +1,2 @@ +from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS +__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] \ No newline at end of file diff --git a/lcm/__init__.py b/lcm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lcm/lcm_pipeline.py b/lcm/lcm_pipeline.py new file mode 100644 index 0000000..3011732 --- /dev/null +++ b/lcm/lcm_pipeline.py @@ -0,0 +1,269 @@ +import torch +from diffusers import DiffusionPipeline, AutoencoderKL, UNet2DConditionModel +from transformers import CLIPTokenizer, CLIPTextModel, CLIPImageProcessor +from diffusers.pipelines.stable_diffusion import StableDiffusionPipelineOutput +from diffusers.image_processor import VaeImageProcessor +from typing import List, Optional, Union, Dict, Any +from comfy.model_management import get_torch_device + +#from diffusers import logging +#logger = logging.get_logger(__name__) # pylint: disable=invalid-name + + +class LatentConsistencyModelPipeline(DiffusionPipeline): + def __init__( + self, + vae: AutoencoderKL, + text_encoder: CLIPTextModel, + tokenizer: CLIPTokenizer, + unet: UNet2DConditionModel, + scheduler: None, + safety_checker: None, + feature_extractor: CLIPImageProcessor + ): + super().__init__() + + self.register_modules( + vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + unet=unet, + scheduler=scheduler, + safety_checker=safety_checker, + feature_extractor=feature_extractor, + ) + self.vae_scale_factor = 2 ** ( + len(self.vae.config.block_out_channels) - 1) + self.image_processor = VaeImageProcessor( + vae_scale_factor=self.vae_scale_factor) + + def _encode_prompt( + self, + prompt, + device, + num_images_per_prompt, + prompt_embeds: None, + ): + r""" + Encodes the prompt into text encoder hidden states. + + Args: + prompt (`str` or `List[str]`, *optional*): + prompt to be encoded + device: (`torch.device`): + torch device + num_images_per_prompt (`int`): + number of images that should be generated per prompt + prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not + provided, text embeddings will be generated from `prompt` input argument. + """ + + if prompt is not None and isinstance(prompt, str): + batch_size = 1 + elif prompt is not None and isinstance(prompt, list): + batch_size = len(prompt) + else: + batch_size = prompt_embeds.shape[0] + + if prompt_embeds is None: + + text_inputs = self.tokenizer( + prompt, + padding="max_length", + max_length=self.tokenizer.model_max_length, + truncation=True, + return_tensors="pt", + ) + text_input_ids = text_inputs.input_ids + untruncated_ids = self.tokenizer( + prompt, padding="longest", return_tensors="pt").input_ids + + if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal( + text_input_ids, untruncated_ids + ): + removed_text = self.tokenizer.batch_decode( + untruncated_ids[:, self.tokenizer.model_max_length - 1: -1] + ) + print( + "The following part of your input was truncated because CLIP can only handle sequences up to" + f" {self.tokenizer.model_max_length} tokens: {removed_text}" + ) + + if hasattr(self.text_encoder.config, "use_attention_mask") and self.text_encoder.config.use_attention_mask: + attention_mask = text_inputs.attention_mask.to(device) + else: + attention_mask = None + + prompt_embeds = self.text_encoder( + text_input_ids.to(device), + attention_mask=attention_mask, + ) + prompt_embeds = prompt_embeds[0] + + if self.text_encoder is not None: + prompt_embeds_dtype = self.text_encoder.dtype + elif self.unet is not None: + prompt_embeds_dtype = self.unet.dtype + else: + prompt_embeds_dtype = prompt_embeds.dtype + + prompt_embeds = prompt_embeds.to( + dtype=prompt_embeds_dtype, device=device) + + bs_embed, seq_len, _ = prompt_embeds.shape + # duplicate text embeddings for each generation per prompt, using mps friendly method + prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1) + prompt_embeds = prompt_embeds.view( + bs_embed * num_images_per_prompt, seq_len, -1) + + # Don't need to get uncond prompt embedding because of LCM Guided Distillation + return prompt_embeds + + # ¯\_(ツ)_/¯ + def run_safety_checker(self, image, device, dtype): + return image, None + + def prepare_latents(self, batch_size, num_channels_latents, height, width, dtype, device, latents=None): + shape = (batch_size, num_channels_latents, height // + self.vae_scale_factor, width // self.vae_scale_factor) + if latents is None: + latents = torch.randn(shape, dtype=dtype).to(device) + else: + latents = latents.to(device) + # scale the initial noise by the standard deviation required by the scheduler + latents = latents * self.scheduler.init_noise_sigma + return latents + + def get_w_embedding(self, w, embedding_dim=512, dtype=torch.float32): + """ + see https://github.com/google-research/vdm/blob/dc27b98a554f65cdc654b800da5aa1846545d41b/model_vdm.py#L298 + Args: + timesteps: torch.Tensor: generate embedding vectors at these timesteps + embedding_dim: int: dimension of the embeddings to generate + dtype: data type of the generated embeddings + + Returns: + embedding vectors with shape `(len(timesteps), embedding_dim)` + """ + assert len(w.shape) == 1 + w = w * 1000. + + half_dim = embedding_dim // 2 + emb = torch.log(torch.tensor(10000.)) / (half_dim - 1) + emb = torch.exp(torch.arange(half_dim, dtype=dtype) * -emb) + emb = w.to(dtype)[:, 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)) + assert emb.shape == (w.shape[0], embedding_dim) + return emb + + @torch.no_grad() + def __call__( + self, + prompt: Union[str, List[str]] = None, + height: Optional[int] = 768, + width: Optional[int] = 768, + guidance_scale: float = 7.5, + num_images_per_prompt: Optional[int] = 1, + latents: Optional[torch.FloatTensor] = None, + num_inference_steps: int = 4, + lcm_origin_steps: int = 50, + prompt_embeds: Optional[torch.FloatTensor] = None, + output_type: Optional[str] = "pil", + return_dict: bool = True, + cross_attention_kwargs: Optional[Dict[str, Any]] = None, + ): + + # 0. Default height and width to unet + height = height or self.unet.config.sample_size * self.vae_scale_factor + width = width or self.unet.config.sample_size * self.vae_scale_factor + + # 2. Define call parameters + if prompt is not None and isinstance(prompt, str): + batch_size = 1 + elif prompt is not None and isinstance(prompt, list): + batch_size = len(prompt) + else: + batch_size = prompt_embeds.shape[0] + + device = get_torch_device() + # do_classifier_free_guidance = guidance_scale > 0.0 # In LCM Implementation: cfg_noise = noise_cond + cfg_scale * (noise_cond - noise_uncond) , (cfg_scale > 0.0 using CFG) + + # 3. Encode input prompt + prompt_embeds = self._encode_prompt( + prompt, + device, + num_images_per_prompt, + prompt_embeds=prompt_embeds, + ) + + # 4. Prepare timesteps + self.scheduler.set_timesteps(num_inference_steps, lcm_origin_steps) + timesteps = self.scheduler.timesteps + + # 5. Prepare latent variable + num_channels_latents = self.unet.config.in_channels + latents = self.prepare_latents( + batch_size * num_images_per_prompt, + num_channels_latents, + height, + width, + prompt_embeds.dtype, + device, + latents, + ) + bs = batch_size * num_images_per_prompt + + # 6. Get Guidance Scale Embedding + w = torch.tensor(guidance_scale).repeat(bs) + w_embedding = self.get_w_embedding(w, embedding_dim=256).to( + device=device, dtype=latents.dtype) + + # 7. LCM MultiStep Sampling Loop: + with self.progress_bar(total=num_inference_steps) as progress_bar: + for i, t in enumerate(timesteps): + + ts = torch.full((bs,), t, device=device, dtype=torch.long) + latents = latents.to(prompt_embeds.dtype) + + # model prediction (v-prediction, eps, x) + model_pred = self.unet( + latents, + ts, + timestep_cond=w_embedding, + encoder_hidden_states=prompt_embeds, + cross_attention_kwargs=cross_attention_kwargs, + return_dict=False)[0] + + # compute the previous noisy sample x_t -> x_t-1 + latents, denoised = self.scheduler.step( + model_pred, i, t, latents, return_dict=False) + + # # call the callback, if provided + # if i == len(timesteps) - 1: + progress_bar.update() + + denoised = denoised.to(prompt_embeds.dtype) + if not output_type == "latent": + image = self.vae.decode( + denoised / self.vae.config.scaling_factor, return_dict=False)[0] + image, has_nsfw_concept = self.run_safety_checker( + image, device, prompt_embeds.dtype) + else: + image = denoised + has_nsfw_concept = None + + if has_nsfw_concept is None: + do_denormalize = [True] * image.shape[0] + else: + do_denormalize = [not has_nsfw for has_nsfw in has_nsfw_concept] + + image = self.image_processor.postprocess( + image, output_type=output_type, do_denormalize=do_denormalize) + + if not return_dict: + return (image, has_nsfw_concept) + + return StableDiffusionPipelineOutput(images=image, nsfw_content_detected=has_nsfw_concept) diff --git a/lcm/lcm_scheduler.py b/lcm/lcm_scheduler.py new file mode 100644 index 0000000..9426cae --- /dev/null +++ b/lcm/lcm_scheduler.py @@ -0,0 +1,498 @@ +# Copyright 2023 Stanford University Team and The HuggingFace Team. All rights reserved. +# +# 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. + +# DISCLAIMER: This code is strongly influenced by https://github.com/pesser/pytorch_diffusion +# and https://github.com/hojonathanho/diffusion + +import math +from dataclasses import dataclass +from typing import List, Optional, Tuple, Union + +import numpy as np +import torch + +from diffusers import ConfigMixin, SchedulerMixin +from diffusers.configuration_utils import register_to_config +from diffusers.utils import BaseOutput + + +@dataclass +# Copied from diffusers.schedulers.scheduling_ddpm.DDPMSchedulerOutput with DDPM->DDIM +class LCMSchedulerOutput(BaseOutput): + """ + Output class for the scheduler's `step` function output. + + Args: + prev_sample (`torch.FloatTensor` of shape `(batch_size, num_channels, height, width)` for images): + Computed sample `(x_{t-1})` of previous timestep. `prev_sample` should be used as next model input in the + denoising loop. + pred_original_sample (`torch.FloatTensor` of shape `(batch_size, num_channels, height, width)` for images): + The predicted denoised sample `(x_{0})` based on the model output from the current timestep. + `pred_original_sample` can be used to preview progress or for guidance. + """ + + prev_sample: torch.FloatTensor + denoised: Optional[torch.FloatTensor] = None + + +# Copied from diffusers.schedulers.scheduling_ddpm.betas_for_alpha_bar +def betas_for_alpha_bar( + num_diffusion_timesteps, + max_beta=0.999, + alpha_transform_type="cosine", +): + """ + 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]. + + Contains a function alpha_bar that takes an argument t and transforms it to the cumulative product of (1-beta) up + to that part of the diffusion process. + + + Args: + num_diffusion_timesteps (`int`): the number of betas to produce. + max_beta (`float`): the maximum beta to use; use values lower than 1 to + prevent singularities. + alpha_transform_type (`str`, *optional*, default to `cosine`): the type of noise schedule for alpha_bar. + Choose from `cosine` or `exp` + + Returns: + betas (`np.ndarray`): the betas used by the scheduler to step the model outputs + """ + if alpha_transform_type == "cosine": + + def alpha_bar_fn(t): + return math.cos((t + 0.008) / 1.008 * math.pi / 2) ** 2 + + elif alpha_transform_type == "exp": + + def alpha_bar_fn(t): + return math.exp(t * -12.0) + + else: + raise ValueError( + f"Unsupported alpha_tranform_type: {alpha_transform_type}") + + 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_fn(t2) / alpha_bar_fn(t1), max_beta)) + return torch.tensor(betas, dtype=torch.float32) + + +def rescale_zero_terminal_snr(betas): + """ + Rescales betas to have zero terminal SNR Based on https://arxiv.org/pdf/2305.08891.pdf (Algorithm 1) + + + Args: + betas (`torch.FloatTensor`): + the betas that the scheduler is being initialized with. + + Returns: + `torch.FloatTensor`: rescaled betas with zero terminal SNR + """ + # Convert betas to alphas_bar_sqrt + alphas = 1.0 - betas + alphas_cumprod = torch.cumprod(alphas, dim=0) + alphas_bar_sqrt = alphas_cumprod.sqrt() + + # Store old values. + alphas_bar_sqrt_0 = alphas_bar_sqrt[0].clone() + alphas_bar_sqrt_T = alphas_bar_sqrt[-1].clone() + + # Shift so the last timestep is zero. + alphas_bar_sqrt -= alphas_bar_sqrt_T + + # Scale so the first timestep is back to the old value. + alphas_bar_sqrt *= alphas_bar_sqrt_0 / \ + (alphas_bar_sqrt_0 - alphas_bar_sqrt_T) + + # Convert alphas_bar_sqrt to betas + alphas_bar = alphas_bar_sqrt**2 # Revert sqrt + alphas = alphas_bar[1:] / alphas_bar[:-1] # Revert cumprod + alphas = torch.cat([alphas_bar[0:1], alphas]) + betas = 1 - alphas + + return betas + + +class LCMScheduler(SchedulerMixin, ConfigMixin): + """ + `LCMScheduler` extends the denoising procedure introduced in denoising diffusion probabilistic models (DDPMs) with + non-Markovian guidance. + + This model inherits from [`SchedulerMixin`] and [`ConfigMixin`]. Check the superclass documentation for the generic + methods the library implements for all schedulers such as loading and saving. + + Args: + num_train_timesteps (`int`, defaults to 1000): + The number of diffusion steps to train the model. + beta_start (`float`, defaults to 0.0001): + The starting `beta` value of inference. + beta_end (`float`, defaults to 0.02): + The final `beta` value. + beta_schedule (`str`, defaults to `"linear"`): + The beta schedule, a mapping from a beta range to a sequence of betas for stepping the model. Choose from + `linear`, `scaled_linear`, or `squaredcos_cap_v2`. + trained_betas (`np.ndarray`, *optional*): + Pass an array of betas directly to the constructor to bypass `beta_start` and `beta_end`. + clip_sample (`bool`, defaults to `True`): + Clip the predicted sample for numerical stability. + clip_sample_range (`float`, defaults to 1.0): + The maximum magnitude for sample clipping. Valid only when `clip_sample=True`. + set_alpha_to_one (`bool`, defaults to `True`): + Each diffusion step uses the alphas product value at that step and at the previous one. For the final step + there is no previous alpha. When this option is `True` the previous alpha product is fixed to `1`, + otherwise it uses the alpha value at step 0. + steps_offset (`int`, defaults to 0): + An offset added to the inference steps. You can use a combination of `offset=1` and + `set_alpha_to_one=False` to make the last step use step 0 for the previous alpha product like in Stable + Diffusion. + prediction_type (`str`, defaults to `epsilon`, *optional*): + Prediction type of the scheduler function; can be `epsilon` (predicts the noise of the diffusion process), + `sample` (directly predicts the noisy sample`) or `v_prediction` (see section 2.4 of [Imagen + Video](https://imagen.research.google/video/paper.pdf) paper). + thresholding (`bool`, defaults to `False`): + Whether to use the "dynamic thresholding" method. This is unsuitable for latent-space diffusion models such + as Stable Diffusion. + dynamic_thresholding_ratio (`float`, defaults to 0.995): + The ratio for the dynamic thresholding method. Valid only when `thresholding=True`. + sample_max_value (`float`, defaults to 1.0): + The threshold value for dynamic thresholding. Valid only when `thresholding=True`. + timestep_spacing (`str`, defaults to `"leading"`): + The way the timesteps should be scaled. Refer to Table 2 of the [Common Diffusion Noise Schedules and + Sample Steps are Flawed](https://huggingface.co/papers/2305.08891) for more information. + rescale_betas_zero_snr (`bool`, defaults to `False`): + Whether to rescale the betas to have zero terminal SNR. This enables the model to generate very bright and + dark samples instead of limiting it to samples with medium brightness. Loosely related to + [`--offset_noise`](https://github.com/huggingface/diffusers/blob/74fd735eb073eb1d774b1ab4154a0876eb82f055/examples/dreambooth/train_dreambooth.py#L506). + """ + + # _compatibles = [e.name for e in KarrasDiffusionSchedulers] + order = 1 + + @register_to_config + def __init__( + self, + num_train_timesteps: int = 1000, + beta_start: float = 0.0001, + beta_end: float = 0.02, + beta_schedule: str = "linear", + trained_betas: Optional[Union[np.ndarray, List[float]]] = None, + clip_sample: bool = True, + set_alpha_to_one: bool = True, + steps_offset: int = 0, + prediction_type: str = "epsilon", + thresholding: bool = False, + dynamic_thresholding_ratio: float = 0.995, + clip_sample_range: float = 1.0, + sample_max_value: float = 1.0, + timestep_spacing: str = "leading", + rescale_betas_zero_snr: bool = False, + ): + if trained_betas is not None: + self.betas = torch.tensor(trained_betas, dtype=torch.float32) + elif beta_schedule == "linear": + self.betas = torch.linspace( + beta_start, beta_end, num_train_timesteps, dtype=torch.float32) + elif beta_schedule == "scaled_linear": + # this schedule is very specific to the latent diffusion model. + self.betas = ( + torch.linspace(beta_start**0.5, beta_end**0.5, + num_train_timesteps, dtype=torch.float32) ** 2 + ) + elif beta_schedule == "squaredcos_cap_v2": + # Glide cosine schedule + self.betas = betas_for_alpha_bar(num_train_timesteps) + else: + raise NotImplementedError( + f"{beta_schedule} does is not implemented for {self.__class__}") + + # Rescale for zero SNR + if rescale_betas_zero_snr: + self.betas = rescale_zero_terminal_snr(self.betas) + + self.alphas = 1.0 - self.betas + self.alphas_cumprod = torch.cumprod(self.alphas, dim=0) + + # At every step in ddim, we are looking into the previous alphas_cumprod + # For the final step, there is no previous alphas_cumprod because we are already at 0 + # `set_alpha_to_one` decides whether we set this parameter simply to one or + # whether we use the final alpha of the "non-previous" one. + self.final_alpha_cumprod = torch.tensor( + 1.0) if set_alpha_to_one else self.alphas_cumprod[0] + + # standard deviation of the initial noise distribution + self.init_noise_sigma = 1.0 + + # setable values + self.num_inference_steps = None + self.timesteps = torch.from_numpy(np.arange(0, num_train_timesteps)[ + ::-1].copy().astype(np.int64)) + + def scale_model_input(self, sample: torch.FloatTensor, timestep: Optional[int] = None) -> torch.FloatTensor: + """ + Ensures interchangeability with schedulers that need to scale the denoising model input depending on the + current timestep. + + Args: + sample (`torch.FloatTensor`): + The input sample. + timestep (`int`, *optional*): + The current timestep in the diffusion chain. + + Returns: + `torch.FloatTensor`: + A scaled input sample. + """ + return sample + + def _get_variance(self, timestep, prev_timestep): + alpha_prod_t = self.alphas_cumprod[timestep] + alpha_prod_t_prev = self.alphas_cumprod[prev_timestep] if prev_timestep >= 0 else self.final_alpha_cumprod + beta_prod_t = 1 - alpha_prod_t + beta_prod_t_prev = 1 - alpha_prod_t_prev + + variance = (beta_prod_t_prev / beta_prod_t) * \ + (1 - alpha_prod_t / alpha_prod_t_prev) + + return variance + + # Copied from diffusers.schedulers.scheduling_ddpm.DDPMScheduler._threshold_sample + def _threshold_sample(self, sample: torch.FloatTensor) -> torch.FloatTensor: + """ + "Dynamic thresholding: At each sampling step we set s to a certain percentile absolute pixel value in xt0 (the + prediction of x_0 at timestep t), and if s > 1, then we threshold xt0 to the range [-s, s] and then divide by + s. Dynamic thresholding pushes saturated pixels (those near -1 and 1) inwards, thereby actively preventing + pixels from saturation at each step. We find that dynamic thresholding results in significantly better + photorealism as well as better image-text alignment, especially when using very large guidance weights." + + https://arxiv.org/abs/2205.11487 + """ + dtype = sample.dtype + batch_size, channels, height, width = sample.shape + + if dtype not in (torch.float32, torch.float64): + # upcast for quantile calculation, and clamp not implemented for cpu half + sample = sample.float() + + # Flatten sample for doing quantile calculation along each image + sample = sample.reshape(batch_size, channels * height * width) + + abs_sample = sample.abs() # "a certain percentile absolute pixel value" + + s = torch.quantile( + abs_sample, self.config.dynamic_thresholding_ratio, dim=1) + s = torch.clamp( + s, min=1, max=self.config.sample_max_value + ) # When clamped to min=1, equivalent to standard clipping to [-1, 1] + + # (batch_size, 1) because clamp will broadcast along dim=0 + s = s.unsqueeze(1) + # "we threshold xt0 to the range [-s, s] and then divide by s" + sample = torch.clamp(sample, -s, s) / s + + sample = sample.reshape(batch_size, channels, height, width) + sample = sample.to(dtype) + + return sample + + def set_timesteps(self, num_inference_steps: int, lcm_origin_steps: int, device: Union[str, torch.device] = None): + """ + Sets the discrete timesteps used for the diffusion chain (to be run before inference). + + Args: + num_inference_steps (`int`): + The number of diffusion steps used when generating samples with a pre-trained model. + """ + + if num_inference_steps > self.config.num_train_timesteps: + raise ValueError( + f"`num_inference_steps`: {num_inference_steps} cannot be larger than `self.config.train_timesteps`:" + f" {self.config.num_train_timesteps} as the unet model trained with this scheduler can only handle" + f" maximal {self.config.num_train_timesteps} timesteps." + ) + + self.num_inference_steps = num_inference_steps + + # LCM Timesteps Setting: # Linear Spacing + c = self.config.num_train_timesteps // lcm_origin_steps + lcm_origin_timesteps = np.asarray( + list(range(1, lcm_origin_steps + 1))) * c - 1 # LCM Training Steps Schedule + skipping_step = len(lcm_origin_timesteps) // num_inference_steps + # LCM Inference Steps Schedule + timesteps = lcm_origin_timesteps[::- + skipping_step][:num_inference_steps] + + self.timesteps = torch.from_numpy(timesteps.copy()).to(device) + + def get_scalings_for_boundary_condition_discrete(self, t): + self.sigma_data = 0.5 # Default: 0.5 + + # By dividing 0.1: This is almost a delta function at t=0. + c_skip = self.sigma_data**2 / ( + (t / 0.1) ** 2 + self.sigma_data**2 + ) + c_out = ((t / 0.1) / ((t / 0.1) ** 2 + self.sigma_data**2) ** 0.5) + return c_skip, c_out + + def step( + self, + model_output: torch.FloatTensor, + timeindex: int, + timestep: int, + sample: torch.FloatTensor, + eta: float = 0.0, + use_clipped_model_output: bool = False, + generator=None, + variance_noise: Optional[torch.FloatTensor] = None, + return_dict: bool = True, + ) -> Union[LCMSchedulerOutput, Tuple]: + """ + Predict the sample from the previous timestep by reversing the SDE. This function propagates the diffusion + process from the learned model outputs (most often the predicted noise). + + Args: + model_output (`torch.FloatTensor`): + The direct output from learned diffusion model. + timestep (`float`): + The current discrete timestep in the diffusion chain. + sample (`torch.FloatTensor`): + A current instance of a sample created by the diffusion process. + eta (`float`): + The weight of noise for added noise in diffusion step. + use_clipped_model_output (`bool`, defaults to `False`): + If `True`, computes "corrected" `model_output` from the clipped predicted original sample. Necessary + because predicted original sample is clipped to [-1, 1] when `self.config.clip_sample` is `True`. If no + clipping has happened, "corrected" `model_output` would coincide with the one provided as input and + `use_clipped_model_output` has no effect. + generator (`torch.Generator`, *optional*): + A random number generator. + variance_noise (`torch.FloatTensor`): + Alternative to generating noise with `generator` by directly providing the noise for the variance + itself. Useful for methods such as [`CycleDiffusion`]. + return_dict (`bool`, *optional*, defaults to `True`): + Whether or not to return a [`~schedulers.scheduling_lcm.LCMSchedulerOutput`] or `tuple`. + + Returns: + [`~schedulers.scheduling_utils.LCMSchedulerOutput`] or `tuple`: + If return_dict is `True`, [`~schedulers.scheduling_lcm.LCMSchedulerOutput`] is returned, otherwise a + tuple is returned where the first element is the sample tensor. + + """ + if self.num_inference_steps is None: + raise ValueError( + "Number of inference steps is 'None', you need to run 'set_timesteps' after creating the scheduler" + ) + + # 1. get previous step value + prev_timeindex = timeindex + 1 + if prev_timeindex < len(self.timesteps): + prev_timestep = self.timesteps[prev_timeindex] + else: + prev_timestep = timestep + + # 2. compute alphas, betas + alpha_prod_t = self.alphas_cumprod[timestep] + alpha_prod_t_prev = self.alphas_cumprod[prev_timestep] if prev_timestep >= 0 else self.final_alpha_cumprod + + beta_prod_t = 1 - alpha_prod_t + beta_prod_t_prev = 1 - alpha_prod_t_prev + + # 3. Get scalings for boundary conditions + c_skip, c_out = self.get_scalings_for_boundary_condition_discrete( + timestep) + + # 4. Different Parameterization: + parameterization = self.config.prediction_type + + if parameterization == "epsilon": # noise-prediction + pred_x0 = (sample - beta_prod_t.sqrt() * + model_output) / alpha_prod_t.sqrt() + + elif parameterization == "sample": # x-prediction + pred_x0 = model_output + + elif parameterization == "v_prediction": # v-prediction + pred_x0 = alpha_prod_t.sqrt() * sample - beta_prod_t.sqrt() * model_output + + # 4. Denoise model output using boundary conditions + denoised = c_out * pred_x0 + c_skip * sample + + # 5. Sample z ~ N(0, I), For MultiStep Inference + # Noise is not used for one-step sampling. + if len(self.timesteps) > 1: + noise = torch.randn(model_output.shape).to(model_output.device) + prev_sample = alpha_prod_t_prev.sqrt() * denoised + beta_prod_t_prev.sqrt() * noise + else: + prev_sample = denoised + + if not return_dict: + return (prev_sample, denoised) + + return LCMSchedulerOutput(prev_sample=prev_sample, denoised=denoised) + + # Copied from diffusers.schedulers.scheduling_ddpm.DDPMScheduler.add_noise + + def add_noise( + self, + original_samples: torch.FloatTensor, + noise: torch.FloatTensor, + timesteps: torch.IntTensor, + ) -> torch.FloatTensor: + # Make sure alphas_cumprod and timestep have same device and dtype as original_samples + alphas_cumprod = self.alphas_cumprod.to( + device=original_samples.device, dtype=original_samples.dtype) + timesteps = timesteps.to(original_samples.device) + + sqrt_alpha_prod = alphas_cumprod[timesteps] ** 0.5 + sqrt_alpha_prod = sqrt_alpha_prod.flatten() + while len(sqrt_alpha_prod.shape) < len(original_samples.shape): + sqrt_alpha_prod = sqrt_alpha_prod.unsqueeze(-1) + + sqrt_one_minus_alpha_prod = (1 - alphas_cumprod[timesteps]) ** 0.5 + sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.flatten() + while len(sqrt_one_minus_alpha_prod.shape) < len(original_samples.shape): + sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.unsqueeze(-1) + + noisy_samples = sqrt_alpha_prod * original_samples + \ + sqrt_one_minus_alpha_prod * noise + return noisy_samples + + # Copied from diffusers.schedulers.scheduling_ddpm.DDPMScheduler.get_velocity + def get_velocity( + self, sample: torch.FloatTensor, noise: torch.FloatTensor, timesteps: torch.IntTensor + ) -> torch.FloatTensor: + # Make sure alphas_cumprod and timestep have same device and dtype as sample + alphas_cumprod = self.alphas_cumprod.to( + device=sample.device, dtype=sample.dtype) + timesteps = timesteps.to(sample.device) + + sqrt_alpha_prod = alphas_cumprod[timesteps] ** 0.5 + sqrt_alpha_prod = sqrt_alpha_prod.flatten() + while len(sqrt_alpha_prod.shape) < len(sample.shape): + sqrt_alpha_prod = sqrt_alpha_prod.unsqueeze(-1) + + sqrt_one_minus_alpha_prod = (1 - alphas_cumprod[timesteps]) ** 0.5 + sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.flatten() + while len(sqrt_one_minus_alpha_prod.shape) < len(sample.shape): + sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.unsqueeze(-1) + + velocity = sqrt_alpha_prod * noise - sqrt_one_minus_alpha_prod * sample + return velocity + + def __len__(self): + return self.config.num_train_timesteps diff --git a/nodes.py b/nodes.py new file mode 100644 index 0000000..87fd410 --- /dev/null +++ b/nodes.py @@ -0,0 +1,71 @@ +from .lcm.lcm_scheduler import LCMScheduler +from .lcm.lcm_pipeline import LatentConsistencyModelPipeline +import folder_paths +from os import path +import time +import torch +import random +import numpy as np +from comfy.model_management import get_torch_device + +MAX_SEED = np.iinfo(np.int32).max + +def randomize_seed_fn(seed: int, randomize_seed: bool) -> int: + if randomize_seed: + seed = random.randint(0, MAX_SEED) + return seed + +class LCMSampler: + + def __init__(self): + self.scheduler = LCMScheduler.from_pretrained(path.join(path.dirname(__file__), "scheduler_config.json")) + self.pipe = None + + @classmethod + def INPUT_TYPES(s): + return {"required": + {"ckpt_name": (folder_paths.get_filename_list("checkpoints"), ), + "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + "steps": ("INT", {"default": 20, "min": 1, "max": 10000}), + "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.5, "round": 0.01}), + "size": ("INT", {"default": 512, "min": 512, "max": 768}), + "num_images": ("INT", {"default": 1, "min": 1, "max": 64}), + "positive_prompt": ("STRING", {"multiline": True}), + } + } + + RETURN_TYPES = ("LATENT",) + FUNCTION = "sample" + + CATEGORY = "sampling" + + def sample(self, ckpt_path, seed, steps, cfg, positive_prompt, size, num_images): + if self.pipe is None: + self.pipe = LatentConsistencyModelPipeline.from_pretrained(ckpt_path, scheduler=self.scheduler) + self.pipe.to(get_torch_device()) + + torch.manual_seed(seed) + start_time = time.time() + + result = self.pipe( + prompt=positive_prompt, + width=size, + height=size, + guidance_scale=cfg, + num_inference_steps=steps, + num_images_per_prompt=num_images, + lcm_origin_steps=50, + output_type="latent", + ).images + + print("LCM inference time: ", time.time() - start_time, "seconds") + + return (result,) + +NODE_CLASS_MAPPINGS = { + "LCMSampler": LCMSampler +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "LCMSampler" : "LCMSampler" +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..81caff4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +diffusers \ No newline at end of file diff --git a/sample.png b/sample.png new file mode 100644 index 0000000000000000000000000000000000000000..c050e1790495d8dea43ccc4a7b095b75d5b397f2 GIT binary patch literal 85612 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 Nbv)>h`{T(g{|mi_*{uKo literal 0 HcmV?d00001 diff --git a/scheduler_config.json b/scheduler_config.json new file mode 100644 index 0000000..b6dba68 --- /dev/null +++ b/scheduler_config.json @@ -0,0 +1,19 @@ +{ + "_class_name": "LCMScheduler", + "_diffusers_version": "0.22.0.dev0", + "beta_end": 0.012, + "beta_schedule": "scaled_linear", + "beta_start": 0.00085, + "clip_sample": true, + "clip_sample_range": 1.0, + "dynamic_thresholding_ratio": 0.995, + "num_train_timesteps": 1000, + "prediction_type": "epsilon", + "rescale_betas_zero_snr": false, + "sample_max_value": 1.0, + "set_alpha_to_one": true, + "steps_offset": 0, + "thresholding": false, + "timestep_spacing": "leading", + "trained_betas": null +} \ No newline at end of file