diff --git a/PixArt/LICENSE-PixArt b/PixArt/LICENSE-PixArt
deleted file mode 100644
index 0ad25db..0000000
--- a/PixArt/LICENSE-PixArt
+++ /dev/null
@@ -1,661 +0,0 @@
- GNU AFFERO GENERAL PUBLIC LICENSE
- Version 3, 19 November 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 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/PixArt/conf.py b/PixArt/conf.py
deleted file mode 100644
index 128146f..0000000
--- a/PixArt/conf.py
+++ /dev/null
@@ -1,140 +0,0 @@
-"""
-List of all PixArt model types / settings
-"""
-
-sampling_settings = {
- "beta_schedule" : "sqrt_linear",
- "linear_start" : 0.0001,
- "linear_end" : 0.02,
- "timesteps" : 1000,
-}
-
-pixart_conf = {
- "PixArtMS_XL_2": { # models/PixArtMS
- "target": "PixArtMS",
- "unet_config": {
- "input_size" : 1024//8,
- "depth" : 28,
- "num_heads" : 16,
- "patch_size" : 2,
- "hidden_size" : 1152,
- "pe_interpolation": 2,
- },
- "sampling_settings" : sampling_settings,
- },
- "PixArtMS_Sigma_XL_2": {
- "target": "PixArtMSSigma",
- "unet_config": {
- "input_size" : 1024//8,
- "token_num" : 300,
- "depth" : 28,
- "num_heads" : 16,
- "patch_size" : 2,
- "hidden_size" : 1152,
- "micro_condition": False,
- "pe_interpolation": 2,
- "model_max_length": 300,
- },
- "sampling_settings" : sampling_settings,
- },
- "PixArtMS_Sigma_XL_2_900M": {
- "target": "PixArtMSSigma",
- "unet_config": {
- "input_size": 1024 // 8,
- "token_num": 300,
- "depth": 42,
- "num_heads": 16,
- "patch_size": 2,
- "hidden_size": 1152,
- "micro_condition": False,
- "pe_interpolation": 2,
- "model_max_length": 300,
- },
- "sampling_settings": sampling_settings,
- },
- "PixArtMS_Sigma_XL_2_2K": {
- "target": "PixArtMSSigma",
- "unet_config": {
- "input_size" : 2048//8,
- "token_num" : 300,
- "depth" : 28,
- "num_heads" : 16,
- "patch_size" : 2,
- "hidden_size" : 1152,
- "micro_condition": False,
- "pe_interpolation": 4,
- "model_max_length": 300,
- },
- "sampling_settings" : sampling_settings,
- },
- "PixArt_XL_2": { # models/PixArt
- "target": "PixArt",
- "unet_config": {
- "input_size" : 512//8,
- "token_num" : 120,
- "depth" : 28,
- "num_heads" : 16,
- "patch_size" : 2,
- "hidden_size" : 1152,
- "pe_interpolation": 1,
- },
- "sampling_settings" : sampling_settings,
- },
-}
-
-pixart_conf.update({ # controlnet models
- "ControlPixArtHalf": {
- "target": "ControlPixArtHalf",
- "unet_config": pixart_conf["PixArt_XL_2"]["unet_config"],
- "sampling_settings": pixart_conf["PixArt_XL_2"]["sampling_settings"],
- },
- "ControlPixArtMSHalf": {
- "target": "ControlPixArtMSHalf",
- "unet_config": pixart_conf["PixArtMS_XL_2"]["unet_config"],
- "sampling_settings": pixart_conf["PixArtMS_XL_2"]["sampling_settings"],
- }
-})
-
-pixart_res = {
- "PixArtMS_XL_2": { # models/PixArtMS 1024x1024
- '0.25': [512, 2048], '0.26': [512, 1984], '0.27': [512, 1920], '0.28': [512, 1856],
- '0.32': [576, 1792], '0.33': [576, 1728], '0.35': [576, 1664], '0.40': [640, 1600],
- '0.42': [640, 1536], '0.48': [704, 1472], '0.50': [704, 1408], '0.52': [704, 1344],
- '0.57': [768, 1344], '0.60': [768, 1280], '0.68': [832, 1216], '0.72': [832, 1152],
- '0.78': [896, 1152], '0.82': [896, 1088], '0.88': [960, 1088], '0.94': [960, 1024],
- '1.00': [1024,1024], '1.07': [1024, 960], '1.13': [1088, 960], '1.21': [1088, 896],
- '1.29': [1152, 896], '1.38': [1152, 832], '1.46': [1216, 832], '1.67': [1280, 768],
- '1.75': [1344, 768], '2.00': [1408, 704], '2.09': [1472, 704], '2.40': [1536, 640],
- '2.50': [1600, 640], '2.89': [1664, 576], '3.00': [1728, 576], '3.11': [1792, 576],
- '3.62': [1856, 512], '3.75': [1920, 512], '3.88': [1984, 512], '4.00': [2048, 512],
- },
- "PixArt_XL_2": { # models/PixArt 512x512
- '0.25': [256,1024], '0.26': [256, 992], '0.27': [256, 960], '0.28': [256, 928],
- '0.32': [288, 896], '0.33': [288, 864], '0.35': [288, 832], '0.40': [320, 800],
- '0.42': [320, 768], '0.48': [352, 736], '0.50': [352, 704], '0.52': [352, 672],
- '0.57': [384, 672], '0.60': [384, 640], '0.68': [416, 608], '0.72': [416, 576],
- '0.78': [448, 576], '0.82': [448, 544], '0.88': [480, 544], '0.94': [480, 512],
- '1.00': [512, 512], '1.07': [512, 480], '1.13': [544, 480], '1.21': [544, 448],
- '1.29': [576, 448], '1.38': [576, 416], '1.46': [608, 416], '1.67': [640, 384],
- '1.75': [672, 384], '2.00': [704, 352], '2.09': [736, 352], '2.40': [768, 320],
- '2.50': [800, 320], '2.89': [832, 288], '3.00': [864, 288], '3.11': [896, 288],
- '3.62': [928, 256], '3.75': [960, 256], '3.88': [992, 256], '4.00': [1024,256]
- },
- "PixArtMS_Sigma_XL_2_2K": {
- '0.25': [1024, 4096], '0.26': [1024, 3968], '0.27': [1024, 3840], '0.28': [1024, 3712],
- '0.32': [1152, 3584], '0.33': [1152, 3456], '0.35': [1152, 3328], '0.40': [1280, 3200],
- '0.42': [1280, 3072], '0.48': [1408, 2944], '0.50': [1408, 2816], '0.52': [1408, 2688],
- '0.57': [1536, 2688], '0.60': [1536, 2560], '0.68': [1664, 2432], '0.72': [1664, 2304],
- '0.78': [1792, 2304], '0.82': [1792, 2176], '0.88': [1920, 2176], '0.94': [1920, 2048],
- '1.00': [2048, 2048], '1.07': [2048, 1920], '1.13': [2176, 1920], '1.21': [2176, 1792],
- '1.29': [2304, 1792], '1.38': [2304, 1664], '1.46': [2432, 1664], '1.67': [2560, 1536],
- '1.75': [2688, 1536], '2.00': [2816, 1408], '2.09': [2944, 1408], '2.40': [3072, 1280],
- '2.50': [3200, 1280], '2.89': [3328, 1152], '3.00': [3456, 1152], '3.11': [3584, 1152],
- '3.62': [3712, 1024], '3.75': [3840, 1024], '3.88': [3968, 1024], '4.00': [4096, 1024]
- }
-}
-# These should be the same
-pixart_res.update({
- "PixArtMS_Sigma_XL_2": pixart_res["PixArtMS_XL_2"],
- "PixArtMS_Sigma_XL_2_512": pixart_res["PixArt_XL_2"],
-})
diff --git a/PixArt/config.py b/PixArt/config.py
new file mode 100644
index 0000000..14975f7
--- /dev/null
+++ b/PixArt/config.py
@@ -0,0 +1,137 @@
+"""
+Model config and setting logic
+"""
+import math
+import logging
+
+import comfy.supported_models_base
+import comfy.supported_models
+import comfy.latent_formats
+
+from .model.pixart import PixArt
+from .model.pixartms import PixArtMS
+from ..text_encoders.pixart.tenc import PixArtTokenizer, PixArtT5XXL
+
+class PixArtConfig(comfy.supported_models_base.BASE):
+ unet_class = PixArtMS
+ unet_config = {}
+ unet_extra_config = {}
+
+ latent_format = comfy.latent_formats.SD15
+ sampling_settings = {
+ "beta_schedule" : "sqrt_linear",
+ "linear_start" : 0.0001,
+ "linear_end" : 0.02,
+ "timesteps" : 1000,
+ }
+
+ def model_type(self, state_dict, prefix=""):
+ return comfy.model_base.ModelType.EPS
+
+ def get_model(self, state_dict, prefix="", device=None):
+ return PixArtModel(model_config=self, unet_model=self.unet_class, device=device)
+
+ def clip_target(self, state_dict={}):
+ return comfy.supported_models_base.ClipTarget(PixArtTokenizer, PixArtT5XXL)
+
+class PixArtModel(comfy.model_base.BaseModel):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ def extra_conds(self, **kwargs):
+ out = super().extra_conds(**kwargs)
+ return out
+
+def model_config_from_unet(sd):
+ """
+ Guess config based on (converted) state dict.
+ """
+ # Shared settings based on DiT_XL_2 - could be enumerated
+ config = {
+ "num_heads" : 16, # get from attention
+ "patch_size" : 2, # final layer I guess?
+ "hidden_size" : 1152, # pos_embed.shape[2]
+ }
+ config["depth"] = sum([key.endswith(".attn.proj.weight") for key in sd.keys()]) or 28
+
+ try:
+ # this is not present in the diffusers version for sigma?
+ config["model_max_length"] = sd["y_embedder.y_embedding"].shape[0]
+ except KeyError:
+ # need better logic to guess this
+ config["model_max_length"] = 300
+
+ if "pos_embed" in sd:
+ config["input_size"] = int(math.sqrt(sd["pos_embed"].shape[1])) * config["patch_size"]
+ config["pe_interpolation"] = config["input_size"] // (512//8) # dumb guess
+
+ model_config = PixArtModel
+ if config["model_max_length"] == 300:
+ # Sigma
+ model_class = PixArtMS
+ model_config.latent_format = comfy.latent_formats.SDXL
+ config["micro_condition"] = False
+ if "input_size" not in config:
+ # The diffusers weights for 1K/2K are exactly the same...?
+ # replace patch embed logic with HyDiT?
+ logging.warn(f"PixArt: diffusers weights - 2K model will be broken, use manual loading!")
+ config["input_size"] = 1024//8
+ else:
+ # Alpha
+ if "csize_embedder.mlp.0.weight" in sd:
+ # MS (microconds)
+ model_class = PixArtMS
+ config["micro_condition"] = True
+ if "input_size" not in config:
+ config["input_size"] = 1024//8
+ config["pe_interpolation"] = 2
+ else:
+ # PixArt
+ model_class = PixArt
+ if "input_size" not in config:
+ config["input_size"] = 512//8
+ config["pe_interpolation"] = 1
+ model_config = PixArtConfig(config)
+ model_config.unet_class = model_class
+ logging.info(f"Detected PixArt model as [{model_class}]")
+ logging.info(f"PixArt config:\n{config}")
+ return model_config
+
+resolutions = {
+ "PixArt 512": {
+ 0.25: [256,1024], 0.26: [256, 992], 0.27: [256, 960], 0.28: [256, 928],
+ 0.32: [288, 896], 0.33: [288, 864], 0.35: [288, 832], 0.40: [320, 800],
+ 0.42: [320, 768], 0.48: [352, 736], 0.50: [352, 704], 0.52: [352, 672],
+ 0.57: [384, 672], 0.60: [384, 640], 0.68: [416, 608], 0.72: [416, 576],
+ 0.78: [448, 576], 0.82: [448, 544], 0.88: [480, 544], 0.94: [480, 512],
+ 1.00: [512, 512], 1.07: [512, 480], 1.13: [544, 480], 1.21: [544, 448],
+ 1.29: [576, 448], 1.38: [576, 416], 1.46: [608, 416], 1.67: [640, 384],
+ 1.75: [672, 384], 2.00: [704, 352], 2.09: [736, 352], 2.40: [768, 320],
+ 2.50: [800, 320], 2.89: [832, 288], 3.00: [864, 288], 3.11: [896, 288],
+ 3.62: [928, 256], 3.75: [960, 256], 3.88: [992, 256], 4.00: [1024,256]
+ },
+ "PixArt 1024": {
+ 0.25: [512, 2048], 0.26: [512, 1984], 0.27: [512, 1920], 0.28: [512, 1856],
+ 0.32: [576, 1792], 0.33: [576, 1728], 0.35: [576, 1664], 0.40: [640, 1600],
+ 0.42: [640, 1536], 0.48: [704, 1472], 0.50: [704, 1408], 0.52: [704, 1344],
+ 0.57: [768, 1344], 0.60: [768, 1280], 0.68: [832, 1216], 0.72: [832, 1152],
+ 0.78: [896, 1152], 0.82: [896, 1088], 0.88: [960, 1088], 0.94: [960, 1024],
+ 1.00: [1024,1024], 1.07: [1024, 960], 1.13: [1088, 960], 1.21: [1088, 896],
+ 1.29: [1152, 896], 1.38: [1152, 832], 1.46: [1216, 832], 1.67: [1280, 768],
+ 1.75: [1344, 768], 2.00: [1408, 704], 2.09: [1472, 704], 2.40: [1536, 640],
+ 2.50: [1600, 640], 2.89: [1664, 576], 3.00: [1728, 576], 3.11: [1792, 576],
+ 3.62: [1856, 512], 3.75: [1920, 512], 3.88: [1984, 512], 4.00: [2048, 512],
+ },
+ "PixArt 2K": {
+ 0.25: [1024, 4096], 0.26: [1024, 3968], 0.27: [1024, 3840], 0.28: [1024, 3712],
+ 0.32: [1152, 3584], 0.33: [1152, 3456], 0.35: [1152, 3328], 0.40: [1280, 3200],
+ 0.42: [1280, 3072], 0.48: [1408, 2944], 0.50: [1408, 2816], 0.52: [1408, 2688],
+ 0.57: [1536, 2688], 0.60: [1536, 2560], 0.68: [1664, 2432], 0.72: [1664, 2304],
+ 0.78: [1792, 2304], 0.82: [1792, 2176], 0.88: [1920, 2176], 0.94: [1920, 2048],
+ 1.00: [2048, 2048], 1.07: [2048, 1920], 1.13: [2176, 1920], 1.21: [2176, 1792],
+ 1.29: [2304, 1792], 1.38: [2304, 1664], 1.46: [2432, 1664], 1.67: [2560, 1536],
+ 1.75: [2688, 1536], 2.00: [2816, 1408], 2.09: [2944, 1408], 2.40: [3072, 1280],
+ 2.50: [3200, 1280], 2.89: [3328, 1152], 3.00: [3456, 1152], 3.11: [3584, 1152],
+ 3.62: [3712, 1024], 3.75: [3840, 1024], 3.88: [3968, 1024], 4.00: [4096, 1024]
+ }
+}
diff --git a/PixArt/diffusers_convert.py b/PixArt/diffusers_convert.py
index 312ea9d..6013d17 100644
--- a/PixArt/diffusers_convert.py
+++ b/PixArt/diffusers_convert.py
@@ -4,220 +4,116 @@
import torch
conversion_map_ms = [ # for multi_scale_train (MS)
- # Resolution
- ("csize_embedder.mlp.0.weight", "adaln_single.emb.resolution_embedder.linear_1.weight"),
- ("csize_embedder.mlp.0.bias", "adaln_single.emb.resolution_embedder.linear_1.bias"),
- ("csize_embedder.mlp.2.weight", "adaln_single.emb.resolution_embedder.linear_2.weight"),
- ("csize_embedder.mlp.2.bias", "adaln_single.emb.resolution_embedder.linear_2.bias"),
- # Aspect ratio
- ("ar_embedder.mlp.0.weight", "adaln_single.emb.aspect_ratio_embedder.linear_1.weight"),
- ("ar_embedder.mlp.0.bias", "adaln_single.emb.aspect_ratio_embedder.linear_1.bias"),
- ("ar_embedder.mlp.2.weight", "adaln_single.emb.aspect_ratio_embedder.linear_2.weight"),
- ("ar_embedder.mlp.2.bias", "adaln_single.emb.aspect_ratio_embedder.linear_2.bias"),
+ # Resolution
+ ("csize_embedder.mlp.0.weight", "adaln_single.emb.resolution_embedder.linear_1.weight"),
+ ("csize_embedder.mlp.0.bias", "adaln_single.emb.resolution_embedder.linear_1.bias"),
+ ("csize_embedder.mlp.2.weight", "adaln_single.emb.resolution_embedder.linear_2.weight"),
+ ("csize_embedder.mlp.2.bias", "adaln_single.emb.resolution_embedder.linear_2.bias"),
+ # Aspect ratio
+ ("ar_embedder.mlp.0.weight", "adaln_single.emb.aspect_ratio_embedder.linear_1.weight"),
+ ("ar_embedder.mlp.0.bias", "adaln_single.emb.aspect_ratio_embedder.linear_1.bias"),
+ ("ar_embedder.mlp.2.weight", "adaln_single.emb.aspect_ratio_embedder.linear_2.weight"),
+ ("ar_embedder.mlp.2.bias", "adaln_single.emb.aspect_ratio_embedder.linear_2.bias"),
]
def get_depth(state_dict):
- return sum(key.endswith('.attn1.to_k.bias') for key in state_dict.keys())
+ return sum(key.endswith('.attn1.to_k.bias') for key in state_dict.keys())
def get_lora_depth(state_dict):
- cnt = max([
- sum(key.endswith('.attn1.to_k.lora_A.weight') for key in state_dict.keys()),
- sum(key.endswith('_attn1_to_k.lora_A.weight') for key in state_dict.keys()),
- sum(key.endswith('.attn1.to_k.lora_up.weight') for key in state_dict.keys()),
- sum(key.endswith('_attn1_to_k.lora_up.weight') for key in state_dict.keys()),
- ])
- assert cnt > 0, "Unable to detect model depth!"
- return cnt
+ cnt = max([
+ sum(key.endswith('.attn1.to_k.lora_A.weight') for key in state_dict.keys()),
+ sum(key.endswith('_attn1_to_k.lora_A.weight') for key in state_dict.keys()),
+ sum(key.endswith('.attn1.to_k.lora_up.weight') for key in state_dict.keys()),
+ sum(key.endswith('_attn1_to_k.lora_up.weight') for key in state_dict.keys()),
+ ])
+ assert cnt > 0, "Unable to detect model depth!"
+ return cnt
def get_conversion_map(state_dict):
- conversion_map = [ # main SD conversion map (PixArt reference, HF Diffusers)
- # Patch embeddings
- ("x_embedder.proj.weight", "pos_embed.proj.weight"),
- ("x_embedder.proj.bias", "pos_embed.proj.bias"),
- # Caption projection
- ("y_embedder.y_embedding", "caption_projection.y_embedding"),
- ("y_embedder.y_proj.fc1.weight", "caption_projection.linear_1.weight"),
- ("y_embedder.y_proj.fc1.bias", "caption_projection.linear_1.bias"),
- ("y_embedder.y_proj.fc2.weight", "caption_projection.linear_2.weight"),
- ("y_embedder.y_proj.fc2.bias", "caption_projection.linear_2.bias"),
- # AdaLN-single LN
- ("t_embedder.mlp.0.weight", "adaln_single.emb.timestep_embedder.linear_1.weight"),
- ("t_embedder.mlp.0.bias", "adaln_single.emb.timestep_embedder.linear_1.bias"),
- ("t_embedder.mlp.2.weight", "adaln_single.emb.timestep_embedder.linear_2.weight"),
- ("t_embedder.mlp.2.bias", "adaln_single.emb.timestep_embedder.linear_2.bias"),
- # Shared norm
- ("t_block.1.weight", "adaln_single.linear.weight"),
- ("t_block.1.bias", "adaln_single.linear.bias"),
- # Final block
- ("final_layer.linear.weight", "proj_out.weight"),
- ("final_layer.linear.bias", "proj_out.bias"),
- ("final_layer.scale_shift_table", "scale_shift_table"),
- ]
+ conversion_map = [ # main SD conversion map (PixArt reference, HF Diffusers)
+ # Patch embeddings
+ ("x_embedder.proj.weight", "pos_embed.proj.weight"),
+ ("x_embedder.proj.bias", "pos_embed.proj.bias"),
+ # Caption projection
+ ("y_embedder.y_embedding", "caption_projection.y_embedding"),
+ ("y_embedder.y_proj.fc1.weight", "caption_projection.linear_1.weight"),
+ ("y_embedder.y_proj.fc1.bias", "caption_projection.linear_1.bias"),
+ ("y_embedder.y_proj.fc2.weight", "caption_projection.linear_2.weight"),
+ ("y_embedder.y_proj.fc2.bias", "caption_projection.linear_2.bias"),
+ # AdaLN-single LN
+ ("t_embedder.mlp.0.weight", "adaln_single.emb.timestep_embedder.linear_1.weight"),
+ ("t_embedder.mlp.0.bias", "adaln_single.emb.timestep_embedder.linear_1.bias"),
+ ("t_embedder.mlp.2.weight", "adaln_single.emb.timestep_embedder.linear_2.weight"),
+ ("t_embedder.mlp.2.bias", "adaln_single.emb.timestep_embedder.linear_2.bias"),
+ # Shared norm
+ ("t_block.1.weight", "adaln_single.linear.weight"),
+ ("t_block.1.bias", "adaln_single.linear.bias"),
+ # Final block
+ ("final_layer.linear.weight", "proj_out.weight"),
+ ("final_layer.linear.bias", "proj_out.bias"),
+ ("final_layer.scale_shift_table", "scale_shift_table"),
+ ]
- # Add actual transformer blocks
- for depth in range(get_depth(state_dict)):
- # Transformer blocks
- conversion_map += [
- (f"blocks.{depth}.scale_shift_table", f"transformer_blocks.{depth}.scale_shift_table"),
- # Projection
- (f"blocks.{depth}.attn.proj.weight", f"transformer_blocks.{depth}.attn1.to_out.0.weight"),
- (f"blocks.{depth}.attn.proj.bias", f"transformer_blocks.{depth}.attn1.to_out.0.bias"),
- # Feed-forward
- (f"blocks.{depth}.mlp.fc1.weight", f"transformer_blocks.{depth}.ff.net.0.proj.weight"),
- (f"blocks.{depth}.mlp.fc1.bias", f"transformer_blocks.{depth}.ff.net.0.proj.bias"),
- (f"blocks.{depth}.mlp.fc2.weight", f"transformer_blocks.{depth}.ff.net.2.weight"),
- (f"blocks.{depth}.mlp.fc2.bias", f"transformer_blocks.{depth}.ff.net.2.bias"),
- # Cross-attention (proj)
- (f"blocks.{depth}.cross_attn.proj.weight" ,f"transformer_blocks.{depth}.attn2.to_out.0.weight"),
- (f"blocks.{depth}.cross_attn.proj.bias" ,f"transformer_blocks.{depth}.attn2.to_out.0.bias"),
- ]
- return conversion_map
+ # Add actual transformer blocks
+ for depth in range(get_depth(state_dict)):
+ # Transformer blocks
+ conversion_map += [
+ (f"blocks.{depth}.scale_shift_table", f"transformer_blocks.{depth}.scale_shift_table"),
+ # Projection
+ (f"blocks.{depth}.attn.proj.weight", f"transformer_blocks.{depth}.attn1.to_out.0.weight"),
+ (f"blocks.{depth}.attn.proj.bias", f"transformer_blocks.{depth}.attn1.to_out.0.bias"),
+ # Feed-forward
+ (f"blocks.{depth}.mlp.fc1.weight", f"transformer_blocks.{depth}.ff.net.0.proj.weight"),
+ (f"blocks.{depth}.mlp.fc1.bias", f"transformer_blocks.{depth}.ff.net.0.proj.bias"),
+ (f"blocks.{depth}.mlp.fc2.weight", f"transformer_blocks.{depth}.ff.net.2.weight"),
+ (f"blocks.{depth}.mlp.fc2.bias", f"transformer_blocks.{depth}.ff.net.2.bias"),
+ # Cross-attention (proj)
+ (f"blocks.{depth}.cross_attn.proj.weight" ,f"transformer_blocks.{depth}.attn2.to_out.0.weight"),
+ (f"blocks.{depth}.cross_attn.proj.bias" ,f"transformer_blocks.{depth}.attn2.to_out.0.bias"),
+ ]
+ return conversion_map
def find_prefix(state_dict, target_key):
- prefix = ""
- for k in state_dict.keys():
- if k.endswith(target_key):
- prefix = k.split(target_key)[0]
- break
- return prefix
+ prefix = ""
+ for k in state_dict.keys():
+ if k.endswith(target_key):
+ prefix = k.split(target_key)[0]
+ break
+ return prefix
def convert_state_dict(state_dict):
- if "adaln_single.emb.resolution_embedder.linear_1.weight" in state_dict.keys():
- cmap = get_conversion_map(state_dict) + conversion_map_ms
- else:
- cmap = get_conversion_map(state_dict)
+ if "adaln_single.emb.resolution_embedder.linear_1.weight" in state_dict.keys():
+ cmap = get_conversion_map(state_dict) + conversion_map_ms
+ else:
+ cmap = get_conversion_map(state_dict)
- missing = [k for k,v in cmap if v not in state_dict]
- new_state_dict = {k: state_dict[v] for k,v in cmap if k not in missing}
- matched = list(v for k,v in cmap if v in state_dict.keys())
-
- for depth in range(get_depth(state_dict)):
- for wb in ["weight", "bias"]:
- # Self Attention
- key = lambda a: f"transformer_blocks.{depth}.attn1.to_{a}.{wb}"
- new_state_dict[f"blocks.{depth}.attn.qkv.{wb}"] = torch.cat((
- state_dict[key('q')], state_dict[key('k')], state_dict[key('v')]
- ), dim=0)
- matched += [key('q'), key('k'), key('v')]
+ missing = [k for k,v in cmap if v not in state_dict]
+ new_state_dict = {k: state_dict[v] for k,v in cmap if k not in missing}
+ matched = list(v for k,v in cmap if v in state_dict.keys())
+
+ for depth in range(get_depth(state_dict)):
+ for wb in ["weight", "bias"]:
+ # Self Attention
+ key = lambda a: f"transformer_blocks.{depth}.attn1.to_{a}.{wb}"
+ new_state_dict[f"blocks.{depth}.attn.qkv.{wb}"] = torch.cat((
+ state_dict[key('q')], state_dict[key('k')], state_dict[key('v')]
+ ), dim=0)
+ matched += [key('q'), key('k'), key('v')]
- # Cross-attention (linear)
- key = lambda a: f"transformer_blocks.{depth}.attn2.to_{a}.{wb}"
- new_state_dict[f"blocks.{depth}.cross_attn.q_linear.{wb}"] = state_dict[key('q')]
- new_state_dict[f"blocks.{depth}.cross_attn.kv_linear.{wb}"] = torch.cat((
- state_dict[key('k')], state_dict[key('v')]
- ), dim=0)
- matched += [key('q'), key('k'), key('v')]
+ # Cross-attention (linear)
+ key = lambda a: f"transformer_blocks.{depth}.attn2.to_{a}.{wb}"
+ new_state_dict[f"blocks.{depth}.cross_attn.q_linear.{wb}"] = state_dict[key('q')]
+ new_state_dict[f"blocks.{depth}.cross_attn.kv_linear.{wb}"] = torch.cat((
+ state_dict[key('k')], state_dict[key('v')]
+ ), dim=0)
+ matched += [key('q'), key('k'), key('v')]
- if len(matched) < len(state_dict):
- print(f"PixArt: UNET conversion has leftover keys! ({len(matched)} vs {len(state_dict)})")
- print(list( set(state_dict.keys()) - set(matched) ))
+ if len(matched) < len(state_dict):
+ print(f"PixArt: UNET conversion has leftover keys! ({len(matched)} vs {len(state_dict)})")
+ print(list( set(state_dict.keys()) - set(matched) ))
- if len(missing) > 0:
- print(f"PixArt: UNET conversion has missing keys!")
- print(missing)
+ if len(missing) > 0:
+ print(f"PixArt: UNET conversion has missing keys!")
+ print(missing)
- return new_state_dict
-
-# Same as above but for LoRA weights:
-def convert_lora_state_dict(state_dict, peft=True):
- # koyha
- rep_ak = lambda x: x.replace(".weight", ".lora_down.weight")
- rep_bk = lambda x: x.replace(".weight", ".lora_up.weight")
- rep_pk = lambda x: x.replace(".weight", ".alpha")
- if peft: # peft
- rep_ap = lambda x: x.replace(".weight", ".lora_A.weight")
- rep_bp = lambda x: x.replace(".weight", ".lora_B.weight")
- rep_pp = lambda x: x.replace(".weight", ".alpha")
-
- prefix = find_prefix(state_dict, "adaln_single.linear.lora_A.weight")
- state_dict = {k[len(prefix):]:v for k,v in state_dict.items()}
- else: # OneTrainer
- rep_ap = lambda x: x.replace(".", "_")[:-7] + ".lora_down.weight"
- rep_bp = lambda x: x.replace(".", "_")[:-7] + ".lora_up.weight"
- rep_pp = lambda x: x.replace(".", "_")[:-7] + ".alpha"
-
- prefix = "lora_transformer_"
- t5_marker = "lora_te_encoder"
- t5_keys = []
- for key in list(state_dict.keys()):
- if key.startswith(prefix):
- state_dict[key[len(prefix):]] = state_dict.pop(key)
- elif t5_marker in key:
- t5_keys.append(state_dict.pop(key))
- if len(t5_keys) > 0:
- print(f"Text Encoder not supported for PixArt LoRA, ignoring {len(t5_keys)} keys")
-
- cmap = []
- cmap_unet = get_conversion_map(state_dict) + conversion_map_ms # todo: 512 model
- for k, v in cmap_unet:
- if v.endswith(".weight"):
- cmap.append((rep_ak(k), rep_ap(v)))
- cmap.append((rep_bk(k), rep_bp(v)))
- if not peft:
- cmap.append((rep_pk(k), rep_pp(v)))
-
- missing = [k for k,v in cmap if v not in state_dict]
- new_state_dict = {k: state_dict[v] for k,v in cmap if k not in missing}
- matched = list(v for k,v in cmap if v in state_dict.keys())
-
- lora_depth = get_lora_depth(state_dict)
- for fp, fk in ((rep_ap, rep_ak),(rep_bp, rep_bk)):
- for depth in range(lora_depth):
- # Self Attention
- key = lambda a: fp(f"transformer_blocks.{depth}.attn1.to_{a}.weight")
- new_state_dict[fk(f"blocks.{depth}.attn.qkv.weight")] = torch.cat((
- state_dict[key('q')], state_dict[key('k')], state_dict[key('v')]
- ), dim=0)
-
- matched += [key('q'), key('k'), key('v')]
- if not peft:
- akey = lambda a: rep_pp(f"transformer_blocks.{depth}.attn1.to_{a}.weight")
- new_state_dict[rep_pk((f"blocks.{depth}.attn.qkv.weight"))] = state_dict[akey("q")]
- matched += [akey('q'), akey('k'), akey('v')]
-
- # Self Attention projection?
- key = lambda a: fp(f"transformer_blocks.{depth}.attn1.to_{a}.weight")
- new_state_dict[fk(f"blocks.{depth}.attn.proj.weight")] = state_dict[key('out.0')]
- matched += [key('out.0')]
-
- # Cross-attention (linear)
- key = lambda a: fp(f"transformer_blocks.{depth}.attn2.to_{a}.weight")
- new_state_dict[fk(f"blocks.{depth}.cross_attn.q_linear.weight")] = state_dict[key('q')]
- new_state_dict[fk(f"blocks.{depth}.cross_attn.kv_linear.weight")] = torch.cat((
- state_dict[key('k')], state_dict[key('v')]
- ), dim=0)
- matched += [key('q'), key('k'), key('v')]
- if not peft:
- akey = lambda a: rep_pp(f"transformer_blocks.{depth}.attn2.to_{a}.weight")
- new_state_dict[rep_pk((f"blocks.{depth}.cross_attn.q_linear.weight"))] = state_dict[akey("q")]
- new_state_dict[rep_pk((f"blocks.{depth}.cross_attn.kv_linear.weight"))] = state_dict[akey("k")]
- matched += [akey('q'), akey('k'), akey('v')]
-
- # Cross Attention projection?
- key = lambda a: fp(f"transformer_blocks.{depth}.attn2.to_{a}.weight")
- new_state_dict[fk(f"blocks.{depth}.cross_attn.proj.weight")] = state_dict[key('out.0')]
- matched += [key('out.0')]
-
- try:
- key = fp(f"transformer_blocks.{depth}.ff.net.0.proj.weight")
- new_state_dict[fk(f"blocks.{depth}.mlp.fc1.weight")] = state_dict[key]
- matched += [key]
- except KeyError:
- pass
-
- try:
- key = fp(f"transformer_blocks.{depth}.ff.net.2.weight")
- new_state_dict[fk(f"blocks.{depth}.mlp.fc2.weight")] = state_dict[key]
- matched += [key]
- except KeyError:
- pass
-
- if len(matched) < len(state_dict):
- print(f"PixArt: LoRA conversion has leftover keys! ({len(matched)} vs {len(state_dict)})")
- print(list( set(state_dict.keys()) - set(matched) ))
-
- if len(missing) > 0:
- print(f"PixArt: LoRA conversion has missing keys! (probably)")
- print(missing)
-
- return new_state_dict
+ return new_state_dict
diff --git a/PixArt/loader.py b/PixArt/loader.py
index cedb5fd..b3627d6 100644
--- a/PixArt/loader.py
+++ b/PixArt/loader.py
@@ -1,180 +1,76 @@
import comfy.supported_models_base
import comfy.latent_formats
+import comfy.model_detection
import comfy.model_patcher
import comfy.model_base
import comfy.utils
import comfy.conds
+import logging
import torch
-import math
+
from comfy import model_management
from .diffusers_convert import convert_state_dict
+from .config import model_config_from_unet
-class EXM_PixArt(comfy.supported_models_base.BASE):
- unet_config = {}
- unet_extra_config = {}
- latent_format = comfy.latent_formats.SD15
+class PixArtModel(comfy.model_base.BaseModel):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ def extra_conds(self, **kwargs):
+ out = super().extra_conds(**kwargs)
- def __init__(self, model_conf):
- self.model_target = model_conf.get("target")
- self.unet_config = model_conf.get("unet_config", {})
- self.sampling_settings = model_conf.get("sampling_settings", {})
- self.latent_format = self.latent_format()
- # UNET is handled by extension
- self.unet_config["disable_unet_model_creation"] = True
+ img_hw = kwargs.get("img_hw", None)
+ if img_hw is not None:
+ out["img_hw"] = comfy.conds.CONDRegular(torch.tensor(img_hw))
- def model_type(self, state_dict, prefix=""):
- return comfy.model_base.ModelType.EPS
+ aspect_ratio = kwargs.get("aspect_ratio", None)
+ if aspect_ratio is not None:
+ out["aspect_ratio"] = comfy.conds.CONDRegular(torch.tensor(aspect_ratio))
-class EXM_PixArt_Model(comfy.model_base.BaseModel):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
- def extra_conds(self, **kwargs):
- out = super().extra_conds(**kwargs)
+ return out
- img_hw = kwargs.get("img_hw", None)
- if img_hw is not None:
- out["img_hw"] = comfy.conds.CONDRegular(torch.tensor(img_hw))
-
- aspect_ratio = kwargs.get("aspect_ratio", None)
- if aspect_ratio is not None:
- out["aspect_ratio"] = comfy.conds.CONDRegular(torch.tensor(aspect_ratio))
+def load_pixart_state_dict(sd, model_options={}):
+ # prefix / format
+ sd = sd.get("model", sd) # ref ckpt
+ diffusion_model_prefix = comfy.model_detection.unet_prefix_from_state_dict(sd)
+ temp_sd = comfy.utils.state_dict_prefix_replace(sd, {diffusion_model_prefix: ""}, filter_keys=True)
+ if len(temp_sd) > 0:
+ sd = temp_sd
- cn_hint = kwargs.get("cn_hint", None)
- if cn_hint is not None:
- out["cn_hint"] = comfy.conds.CONDRegular(cn_hint)
+ # diffusers convert
+ if "adaln_single.linear.weight" in sd:
+ sd = convert_state_dict(sd)
- return out
+ # model config
+ model_config = model_config_from_unet(sd)
-def load_pixart(model_path, model_conf=None):
- state_dict = comfy.utils.load_torch_file(model_path)
- state_dict = state_dict.get("model", state_dict)
+ # TODO: move lines below to utils
+ parameters = comfy.utils.calculate_parameters(sd)
+ load_device = model_management.get_torch_device()
+ offload_device = comfy.model_management.unet_offload_device()
- # prefix
- for prefix in ["model.diffusion_model.",]:
- if any(True for x in state_dict if x.startswith(prefix)):
- state_dict = {k[len(prefix):]:v for k,v in state_dict.items()}
+ dtype = model_options.get("dtype", torch.float16) # TODO: fix this
+ weight_dtype = comfy.utils.weight_dtype(sd)
+ unet_weight_dtype = list(model_config.supported_inference_dtypes)
- # diffusers
- if "adaln_single.linear.weight" in state_dict:
- state_dict = convert_state_dict(state_dict) # Diffusers
+ if weight_dtype is not None and model_config.scaled_fp8 is None:
+ unet_weight_dtype.append(weight_dtype)
- # guess auto config
- if model_conf is None:
- model_conf = guess_pixart_config(state_dict)
+ if dtype is None:
+ unet_dtype = model_management.unet_dtype(model_params=parameters, supported_dtypes=unet_weight_dtype)
+ else:
+ unet_dtype = dtype
- parameters = comfy.utils.calculate_parameters(state_dict)
- unet_dtype = model_management.unet_dtype(model_params=parameters)
- load_device = comfy.model_management.get_torch_device()
- offload_device = comfy.model_management.unet_offload_device()
+ manual_cast_dtype = model_management.unet_manual_cast(unet_dtype, load_device, model_config.supported_inference_dtypes)
+ model_config.set_inference_dtype(unet_dtype, manual_cast_dtype)
+ model_config.custom_operations = model_options.get("custom_operations", model_config.custom_operations)
+ if model_options.get("fp8_optimizations", False):
+ model_config.optimizations["fp8"] = True
- # ignore fp8/etc and use directly for now
- manual_cast_dtype = model_management.unet_manual_cast(unet_dtype, load_device)
- if manual_cast_dtype:
- print(f"PixArt: falling back to {manual_cast_dtype}")
- unet_dtype = manual_cast_dtype
-
- model_conf = EXM_PixArt(model_conf) # convert to object
- model = EXM_PixArt_Model( # same as comfy.model_base.BaseModel
- model_conf,
- model_type=comfy.model_base.ModelType.EPS,
- device=model_management.get_torch_device()
- )
-
- if model_conf.model_target == "PixArtMS":
- from .models.PixArtMS import PixArtMS
- model.diffusion_model = PixArtMS(**model_conf.unet_config)
- elif model_conf.model_target == "PixArt":
- from .models.PixArt import PixArt
- model.diffusion_model = PixArt(**model_conf.unet_config)
- elif model_conf.model_target == "PixArtMSSigma":
- from .models.PixArtMS import PixArtMS
- model.diffusion_model = PixArtMS(**model_conf.unet_config)
- model.latent_format = comfy.latent_formats.SDXL()
- elif model_conf.model_target == "ControlPixArtMSHalf":
- from .models.PixArtMS import PixArtMS
- from .models.pixart_controlnet import ControlPixArtMSHalf
- model.diffusion_model = PixArtMS(**model_conf.unet_config)
- model.diffusion_model = ControlPixArtMSHalf(model.diffusion_model)
- elif model_conf.model_target == "ControlPixArtHalf":
- from .models.PixArt import PixArt
- from .models.pixart_controlnet import ControlPixArtHalf
- model.diffusion_model = PixArt(**model_conf.unet_config)
- model.diffusion_model = ControlPixArtHalf(model.diffusion_model)
- else:
- raise NotImplementedError(f"Unknown model target '{model_conf.model_target}'")
-
- m, u = model.diffusion_model.load_state_dict(state_dict, strict=False)
- if len(m) > 0: print("Missing UNET keys", m)
- if len(u) > 0: print("Leftover UNET keys", u)
- model.diffusion_model.dtype = unet_dtype
- model.diffusion_model.eval()
- model.diffusion_model.to(unet_dtype)
-
- model_patcher = comfy.model_patcher.ModelPatcher(
- model,
- load_device = load_device,
- offload_device = offload_device,
- )
- return model_patcher
-
-def guess_pixart_config(sd):
- """
- Guess config based on converted state dict.
- """
- # Shared settings based on DiT_XL_2 - could be enumerated
- config = {
- "num_heads" : 16, # get from attention
- "patch_size" : 2, # final layer I guess?
- "hidden_size" : 1152, # pos_embed.shape[2]
- }
- config["depth"] = sum([key.endswith(".attn.proj.weight") for key in sd.keys()]) or 28
-
- try:
- # this is not present in the diffusers version for sigma?
- config["model_max_length"] = sd["y_embedder.y_embedding"].shape[0]
- except KeyError:
- # need better logic to guess this
- config["model_max_length"] = 300
-
- if "pos_embed" in sd:
- config["input_size"] = int(math.sqrt(sd["pos_embed"].shape[1])) * config["patch_size"]
- config["pe_interpolation"] = config["input_size"] // (512//8) # dumb guess
-
- target_arch = "PixArtMS"
- if config["model_max_length"] == 300:
- # Sigma
- target_arch = "PixArtMSSigma"
- config["micro_condition"] = False
- if "input_size" not in config:
- # The diffusers weights for 1K/2K are exactly the same...?
- # replace patch embed logic with HyDiT?
- print(f"PixArt: diffusers weights - 2K model will be broken, use manual loading!")
- config["input_size"] = 1024//8
- else:
- # Alpha
- if "csize_embedder.mlp.0.weight" in sd:
- # MS (microconds)
- target_arch = "PixArtMS"
- config["micro_condition"] = True
- if "input_size" not in config:
- config["input_size"] = 1024//8
- config["pe_interpolation"] = 2
- else:
- # PixArt
- target_arch = "PixArt"
- if "input_size" not in config:
- config["input_size"] = 512//8
- config["pe_interpolation"] = 1
-
- print("PixArt guessed config:", target_arch, config)
- return {
- "target": target_arch,
- "unet_config": config,
- "sampling_settings": {
- "beta_schedule" : "sqrt_linear",
- "linear_start" : 0.0001,
- "linear_end" : 0.02,
- "timesteps" : 1000,
- }
- }
+ model = model_config.get_model(sd, "")
+ model = model.to(offload_device).eval()
+ model.load_model_weights(sd, "")
+ left_over = sd.keys()
+ if len(left_over) > 0:
+ logging.info("left over keys in unet: {}".format(left_over))
+ return comfy.model_patcher.ModelPatcher(model, load_device=load_device, offload_device=offload_device)
diff --git a/PixArt/lora.py b/PixArt/lora.py
deleted file mode 100644
index fca5931..0000000
--- a/PixArt/lora.py
+++ /dev/null
@@ -1,146 +0,0 @@
-import os
-import copy
-import json
-import torch
-import comfy.lora
-import comfy.model_management
-from comfy.model_patcher import ModelPatcher
-from .diffusers_convert import convert_lora_state_dict
-
-class EXM_PixArt_ModelPatcher(ModelPatcher):
- def calculate_weight(self, patches, weight, key):
- """
- This is almost the same as the comfy function, but stripped down to just the LoRA patch code.
- The problem with the original code is the q/k/v keys being combined into one for the attention.
- In the diffusers code, they're treated as separate keys, but in the reference code they're recombined (q+kv|qkv).
- This means, for example, that the [1152,1152] weights become [3456,1152] in the state dict.
- The issue with this is that the LoRA weights are [128,1152],[1152,128] and become [384,1162],[3456,128] instead.
-
- This is the best thing I could think of that would fix that, but it's very fragile.
- - Check key shape to determine if it needs the fallback logic
- - Cut the input into parts based on the shape (undoing the torch.cat)
- - Do the matrix multiplication logic
- - Recombine them to match the expected shape
- """
- for p in patches:
- alpha = p[0]
- v = p[1]
- strength_model = p[2]
- if strength_model != 1.0:
- weight *= strength_model
-
- if isinstance(v, list):
- v = (self.calculate_weight(v[1:], v[0].clone(), key), )
-
- if len(v) == 2:
- patch_type = v[0]
- v = v[1]
-
- if patch_type == "lora":
- mat1 = comfy.model_management.cast_to_device(v[0], weight.device, torch.float32)
- mat2 = comfy.model_management.cast_to_device(v[1], weight.device, torch.float32)
- if v[2] is not None:
- alpha *= v[2] / mat2.shape[0]
- try:
- mat1 = mat1.flatten(start_dim=1)
- mat2 = mat2.flatten(start_dim=1)
-
- ch1 = mat1.shape[0] // mat2.shape[1]
- ch2 = mat2.shape[0] // mat1.shape[1]
- ### Fallback logic for shape mismatch ###
- if mat1.shape[0] != mat2.shape[1] and ch1 == ch2 and (mat1.shape[0]/mat2.shape[1])%1 == 0:
- mat1 = mat1.chunk(ch1, dim=0)
- mat2 = mat2.chunk(ch1, dim=0)
- weight += torch.cat(
- [alpha * torch.mm(mat1[x], mat2[x]) for x in range(ch1)],
- dim=0,
- ).reshape(weight.shape).type(weight.dtype)
- else:
- weight += (alpha * torch.mm(mat1, mat2)).reshape(weight.shape).type(weight.dtype)
- except Exception as e:
- print("ERROR", key, e)
- return weight
-
- def clone(self):
- n = EXM_PixArt_ModelPatcher(self.model, self.load_device, self.offload_device, self.size, self.current_device, weight_inplace_update=self.weight_inplace_update)
- n.patches = {}
- for k in self.patches:
- n.patches[k] = self.patches[k][:]
-
- n.object_patches = self.object_patches.copy()
- n.model_options = copy.deepcopy(self.model_options)
- n.model_keys = self.model_keys
- return n
-
-def replace_model_patcher(model):
- n = EXM_PixArt_ModelPatcher(
- model = model.model,
- size = model.size,
- load_device = model.load_device,
- offload_device = model.offload_device,
- weight_inplace_update = model.weight_inplace_update,
- )
- n.patches = {}
- for k in model.patches:
- n.patches[k] = model.patches[k][:]
-
- n.object_patches = model.object_patches.copy()
- n.model_options = copy.deepcopy(model.model_options)
- return n
-
-def find_peft_alpha(path):
- def load_json(json_path):
- with open(json_path) as f:
- data = json.load(f)
- alpha = data.get("lora_alpha")
- alpha = alpha or data.get("alpha")
- if not alpha:
- print(" Found config but `lora_alpha` is missing!")
- else:
- print(f" Found config at {json_path} [alpha:{alpha}]")
- return alpha
-
- # For some weird reason peft doesn't include the alpha in the actual model
- print("PixArt: Warning! This is a PEFT LoRA. Trying to find config...")
- files = [
- f"{os.path.splitext(path)[0]}.json",
- f"{os.path.splitext(path)[0]}.config.json",
- os.path.join(os.path.dirname(path),"adapter_config.json"),
- ]
- for file in files:
- if os.path.isfile(file):
- return load_json(file)
-
- print(" Missing config/alpha! assuming alpha of 8. Consider converting it/adding a config json to it.")
- return 8.0
-
-def load_pixart_lora(model, lora, lora_path, strength):
- k_back = lambda x: x.replace(".lora_up.weight", "")
- # need to convert the actual weights for this to work.
- if any(True for x in lora.keys() if x.endswith("adaln_single.linear.lora_A.weight")):
- lora = convert_lora_state_dict(lora, peft=True)
- alpha = find_peft_alpha(lora_path)
- lora.update({f"{k_back(x)}.alpha":torch.tensor(alpha) for x in lora.keys() if "lora_up" in x})
- else: # OneTrainer
- lora = convert_lora_state_dict(lora, peft=False)
-
- key_map = {k_back(x):f"diffusion_model.{k_back(x)}.weight" for x in lora.keys() if "lora_up" in x} # fake
-
- loaded = comfy.lora.load_lora(lora, key_map)
- if model is not None:
- # switch to custom model patcher when using LoRAs
- if isinstance(model, EXM_PixArt_ModelPatcher):
- new_modelpatcher = model.clone()
- else:
- new_modelpatcher = replace_model_patcher(model)
- k = new_modelpatcher.add_patches(loaded, strength)
- else:
- k = ()
- new_modelpatcher = None
-
- k = set(k)
- for x in loaded:
- if (x not in k):
- print("NOT LOADED", x)
-
- return new_modelpatcher
diff --git a/T5/LICENSE-T5 b/PixArt/model/LICENSE
similarity index 99%
rename from T5/LICENSE-T5
rename to PixArt/model/LICENSE
index 261eeb9..fb524b1 100644
--- a/T5/LICENSE-T5
+++ b/PixArt/model/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2024 Junsong Chen, Jincheng Yu, Enze Xie
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/PixArt/model/__init__.py b/PixArt/model/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/PixArt/models/PixArt_blocks.py b/PixArt/model/blocks.py
similarity index 100%
rename from PixArt/models/PixArt_blocks.py
rename to PixArt/model/blocks.py
diff --git a/PixArt/models/PixArt.py b/PixArt/model/pixart.py
similarity index 98%
rename from PixArt/models/PixArt.py
rename to PixArt/model/pixart.py
index 4d6cf93..f200177 100644
--- a/PixArt/models/PixArt.py
+++ b/PixArt/model/pixart.py
@@ -18,7 +18,7 @@ from timm.models.vision_transformer import PatchEmbed, Mlp
from .utils import auto_grad_checkpoint, to_2tuple
-from .PixArt_blocks import t2i_modulate, CaptionEmbedder, AttentionKVCompress, MultiHeadCrossAttention, T2IFinalLayer, TimestepEmbedder, LabelEmbedder, FinalLayer
+from .blocks import t2i_modulate, CaptionEmbedder, AttentionKVCompress, MultiHeadCrossAttention, T2IFinalLayer, TimestepEmbedder, LabelEmbedder, FinalLayer
class PixArtBlock(nn.Module):
diff --git a/PixArt/models/PixArtMS.py b/PixArt/model/pixartms.py
similarity index 95%
rename from PixArt/models/PixArtMS.py
rename to PixArt/model/pixartms.py
index 908589c..0bb5b36 100644
--- a/PixArt/models/PixArtMS.py
+++ b/PixArt/model/pixartms.py
@@ -15,8 +15,8 @@ from timm.models.layers import DropPath
from timm.models.vision_transformer import Mlp
from .utils import auto_grad_checkpoint, to_2tuple
-from .PixArt_blocks import t2i_modulate, CaptionEmbedder, AttentionKVCompress, MultiHeadCrossAttention, T2IFinalLayer, TimestepEmbedder, SizeEmbedder
-from .PixArt import PixArt, get_2d_sincos_pos_embed
+from .blocks import t2i_modulate, CaptionEmbedder, AttentionKVCompress, MultiHeadCrossAttention, T2IFinalLayer, TimestepEmbedder, SizeEmbedder
+from .pixart import PixArt, get_2d_sincos_pos_embed
class PatchEmbed(nn.Module):
diff --git a/PixArt/models/utils.py b/PixArt/model/utils.py
similarity index 100%
rename from PixArt/models/utils.py
rename to PixArt/model/utils.py
diff --git a/PixArt/models/pixart_controlnet.py b/PixArt/models/pixart_controlnet.py
deleted file mode 100644
index 37fa4c1..0000000
--- a/PixArt/models/pixart_controlnet.py
+++ /dev/null
@@ -1,312 +0,0 @@
-import re
-import torch
-import torch.nn as nn
-
-from copy import deepcopy
-from torch import Tensor
-from torch.nn import Module, Linear, init
-from typing import Any, Mapping
-
-from .PixArt import PixArt, get_2d_sincos_pos_embed
-from .PixArtMS import PixArtMSBlock, PixArtMS
-from .utils import auto_grad_checkpoint
-
-# The implementation of ControlNet-Half architrecture
-# https://github.com/lllyasviel/ControlNet/discussions/188
-class ControlT2IDitBlockHalf(Module):
- def __init__(self, base_block: PixArtMSBlock, block_index: 0) -> None:
- super().__init__()
- self.copied_block = deepcopy(base_block)
- self.block_index = block_index
-
- for p in self.copied_block.parameters():
- p.requires_grad_(True)
-
- self.copied_block.load_state_dict(base_block.state_dict())
- self.copied_block.train()
-
- self.hidden_size = hidden_size = base_block.hidden_size
- if self.block_index == 0:
- self.before_proj = Linear(hidden_size, hidden_size)
- init.zeros_(self.before_proj.weight)
- init.zeros_(self.before_proj.bias)
- self.after_proj = Linear(hidden_size, hidden_size)
- init.zeros_(self.after_proj.weight)
- init.zeros_(self.after_proj.bias)
-
- def forward(self, x, y, t, mask=None, c=None):
-
- if self.block_index == 0:
- # the first block
- c = self.before_proj(c)
- c = self.copied_block(x + c, y, t, mask)
- c_skip = self.after_proj(c)
- else:
- # load from previous c and produce the c for skip connection
- c = self.copied_block(c, y, t, mask)
- c_skip = self.after_proj(c)
-
- return c, c_skip
-
-
-# The implementation of ControlPixArtHalf net
-class ControlPixArtHalf(Module):
- # only support single res model
- def __init__(self, base_model: PixArt, copy_blocks_num: int = 13) -> None:
- super().__init__()
- self.dtype = torch.get_default_dtype()
- self.base_model = base_model.eval()
- self.controlnet = []
- self.copy_blocks_num = copy_blocks_num
- self.total_blocks_num = len(base_model.blocks)
- for p in self.base_model.parameters():
- p.requires_grad_(False)
-
- # Copy first copy_blocks_num block
- for i in range(copy_blocks_num):
- self.controlnet.append(ControlT2IDitBlockHalf(base_model.blocks[i], i))
- self.controlnet = nn.ModuleList(self.controlnet)
-
- def __getattr__(self, name: str) -> Tensor or Module:
- if name in ['forward', 'forward_with_dpmsolver', 'forward_with_cfg', 'forward_c', 'load_state_dict']:
- return self.__dict__[name]
- elif name in ['base_model', 'controlnet']:
- return super().__getattr__(name)
- else:
- return getattr(self.base_model, name)
-
- def forward_c(self, c):
- self.h, self.w = c.shape[-2]//self.patch_size, c.shape[-1]//self.patch_size
- pos_embed = torch.from_numpy(get_2d_sincos_pos_embed(self.pos_embed.shape[-1], (self.h, self.w), lewei_scale=self.lewei_scale, base_size=self.base_size)).unsqueeze(0).to(c.device).to(self.dtype)
- return self.x_embedder(c) + pos_embed if c is not None else c
-
- # def forward(self, x, t, c, **kwargs):
- # return self.base_model(x, t, c=self.forward_c(c), **kwargs)
- def forward_raw(self, x, timestep, y, mask=None, data_info=None, c=None, **kwargs):
- # modify the original PixArtMS forward function
- if c is not None:
- c = c.to(self.dtype)
- c = self.forward_c(c)
- """
- Forward pass of PixArt.
- x: (N, C, H, W) tensor of spatial inputs (images or latent representations of images)
- t: (N,) tensor of diffusion timesteps
- y: (N, 1, 120, C) tensor of class labels
- """
- x = x.to(self.dtype)
- timestep = timestep.to(self.dtype)
- y = y.to(self.dtype)
- pos_embed = self.pos_embed.to(self.dtype)
- self.h, self.w = x.shape[-2]//self.patch_size, x.shape[-1]//self.patch_size
- x = self.x_embedder(x) + pos_embed # (N, T, D), where T = H * W / patch_size ** 2
- t = self.t_embedder(timestep.to(x.dtype)) # (N, D)
- t0 = self.t_block(t)
- y = self.y_embedder(y, self.training) # (N, 1, L, D)
- if mask is not None:
- if mask.shape[0] != y.shape[0]:
- mask = mask.repeat(y.shape[0] // mask.shape[0], 1)
- mask = mask.squeeze(1).squeeze(1)
- y = y.squeeze(1).masked_select(mask.unsqueeze(-1) != 0).view(1, -1, x.shape[-1])
- y_lens = mask.sum(dim=1).tolist()
- else:
- y_lens = [y.shape[2]] * y.shape[0]
- y = y.squeeze(1).view(1, -1, x.shape[-1])
-
- # define the first layer
- x = auto_grad_checkpoint(self.base_model.blocks[0], x, y, t0, y_lens, **kwargs) # (N, T, D) #support grad checkpoint
-
- if c is not None:
- # update c
- for index in range(1, self.copy_blocks_num + 1):
- c, c_skip = auto_grad_checkpoint(self.controlnet[index - 1], x, y, t0, y_lens, c, **kwargs)
- x = auto_grad_checkpoint(self.base_model.blocks[index], x + c_skip, y, t0, y_lens, **kwargs)
-
- # update x
- for index in range(self.copy_blocks_num + 1, self.total_blocks_num):
- x = auto_grad_checkpoint(self.base_model.blocks[index], x, y, t0, y_lens, **kwargs)
- else:
- for index in range(1, self.total_blocks_num):
- x = auto_grad_checkpoint(self.base_model.blocks[index], x, y, t0, y_lens, **kwargs)
-
- x = self.final_layer(x, t) # (N, T, patch_size ** 2 * out_channels)
- x = self.unpatchify(x) # (N, out_channels, H, W)
- return x
-
- def forward(self, x, timesteps, context, cn_hint=None, **kwargs):
- """
- Forward pass that adapts comfy input to original forward function
- x: (N, C, H, W) tensor of spatial inputs (images or latent representations of images)
- timesteps: (N,) tensor of diffusion timesteps
- context: (N, 1, 120, C) conditioning
- cn_hint: controlnet hint
- """
- ## Still accepts the input w/o that dim but returns garbage
- if len(context.shape) == 3:
- context = context.unsqueeze(1)
-
- ## run original forward pass
- out = self.forward_raw(
- x = x.to(self.dtype),
- timestep = timesteps.to(self.dtype),
- y = context.to(self.dtype),
- c = cn_hint,
- )
-
- ## only return EPS
- out = out.to(torch.float)
- eps, rest = out[:, :self.in_channels], out[:, self.in_channels:]
- return eps
-
- def forward_with_dpmsolver(self, x, t, y, data_info, c, **kwargs):
- model_out = self.forward_raw(x, t, y, data_info=data_info, c=c, **kwargs)
- return model_out.chunk(2, dim=1)[0]
-
- # def forward_with_dpmsolver(self, x, t, y, data_info, c, **kwargs):
- # return self.base_model.forward_with_dpmsolver(x, t, y, data_info=data_info, c=self.forward_c(c), **kwargs)
-
- def forward_with_cfg(self, x, t, y, cfg_scale, data_info, c, **kwargs):
- return self.base_model.forward_with_cfg(x, t, y, cfg_scale, data_info, c=self.forward_c(c), **kwargs)
-
- def load_state_dict(self, state_dict: Mapping[str, Any], strict: bool = True):
- if all((k.startswith('base_model') or k.startswith('controlnet')) for k in state_dict.keys()):
- return super().load_state_dict(state_dict, strict)
- else:
- new_key = {}
- for k in state_dict.keys():
- new_key[k] = re.sub(r"(blocks\.\d+)(.*)", r"\1.base_block\2", k)
- for k, v in new_key.items():
- if k != v:
- print(f"replace {k} to {v}")
- state_dict[v] = state_dict.pop(k)
-
- return self.base_model.load_state_dict(state_dict, strict)
-
- def unpatchify(self, x):
- """
- x: (N, T, patch_size**2 * C)
- imgs: (N, H, W, C)
- """
- c = self.out_channels
- p = self.x_embedder.patch_size[0]
- assert self.h * self.w == x.shape[1]
-
- x = x.reshape(shape=(x.shape[0], self.h, self.w, p, p, c))
- x = torch.einsum('nhwpqc->nchpwq', x)
- imgs = x.reshape(shape=(x.shape[0], c, self.h * p, self.w * p))
- return imgs
-
- # @property
- # def dtype(self):
- ## 返回模型参数的数据类型
- # return next(self.parameters()).dtype
-
-
-# The implementation for PixArtMS_Half + 1024 resolution
-class ControlPixArtMSHalf(ControlPixArtHalf):
- # support multi-scale res model (multi-scale model can also be applied to single reso training & inference)
- def __init__(self, base_model: PixArtMS, copy_blocks_num: int = 13) -> None:
- super().__init__(base_model=base_model, copy_blocks_num=copy_blocks_num)
-
- def forward_raw(self, x, timestep, y, mask=None, data_info=None, c=None, **kwargs):
- # modify the original PixArtMS forward function
- """
- Forward pass of PixArt.
- x: (N, C, H, W) tensor of spatial inputs (images or latent representations of images)
- t: (N,) tensor of diffusion timesteps
- y: (N, 1, 120, C) tensor of class labels
- """
- if c is not None:
- c = c.to(self.dtype)
- c = self.forward_c(c)
- bs = x.shape[0]
- x = x.to(self.dtype)
- timestep = timestep.to(self.dtype)
- y = y.to(self.dtype)
- c_size, ar = data_info['img_hw'].to(self.dtype), data_info['aspect_ratio'].to(self.dtype)
- self.h, self.w = x.shape[-2]//self.patch_size, x.shape[-1]//self.patch_size
-
- pos_embed = torch.from_numpy(get_2d_sincos_pos_embed(self.pos_embed.shape[-1], (self.h, self.w), lewei_scale=self.lewei_scale, base_size=self.base_size)).unsqueeze(0).to(x.device).to(self.dtype)
- x = self.x_embedder(x) + pos_embed # (N, T, D), where T = H * W / patch_size ** 2
- t = self.t_embedder(timestep) # (N, D)
- csize = self.csize_embedder(c_size, bs) # (N, D)
- ar = self.ar_embedder(ar, bs) # (N, D)
- t = t + torch.cat([csize, ar], dim=1)
- t0 = self.t_block(t)
- y = self.y_embedder(y, self.training) # (N, D)
- if mask is not None:
- if mask.shape[0] != y.shape[0]:
- mask = mask.repeat(y.shape[0] // mask.shape[0], 1)
- mask = mask.squeeze(1).squeeze(1)
- y = y.squeeze(1).masked_select(mask.unsqueeze(-1) != 0).view(1, -1, x.shape[-1])
- y_lens = mask.sum(dim=1).tolist()
- else:
- y_lens = [y.shape[2]] * y.shape[0]
- y = y.squeeze(1).view(1, -1, x.shape[-1])
-
- # define the first layer
- x = auto_grad_checkpoint(self.base_model.blocks[0], x, y, t0, y_lens, **kwargs) # (N, T, D) #support grad checkpoint
-
- if c is not None:
- # update c
- for index in range(1, self.copy_blocks_num + 1):
- c, c_skip = auto_grad_checkpoint(self.controlnet[index - 1], x, y, t0, y_lens, c, **kwargs)
- x = auto_grad_checkpoint(self.base_model.blocks[index], x + c_skip, y, t0, y_lens, **kwargs)
-
- # update x
- for index in range(self.copy_blocks_num + 1, self.total_blocks_num):
- x = auto_grad_checkpoint(self.base_model.blocks[index], x, y, t0, y_lens, **kwargs)
- else:
- for index in range(1, self.total_blocks_num):
- x = auto_grad_checkpoint(self.base_model.blocks[index], x, y, t0, y_lens, **kwargs)
-
- x = self.final_layer(x, t) # (N, T, patch_size ** 2 * out_channels)
- x = self.unpatchify(x) # (N, out_channels, H, W)
- return x
-
- def forward(self, x, timesteps, context, img_hw=None, aspect_ratio=None, cn_hint=None, **kwargs):
- """
- Forward pass that adapts comfy input to original forward function
- x: (N, C, H, W) tensor of spatial inputs (images or latent representations of images)
- timesteps: (N,) tensor of diffusion timesteps
- context: (N, 1, 120, C) conditioning
- img_hw: height|width conditioning
- aspect_ratio: aspect ratio conditioning
- cn_hint: controlnet hint
- """
- ## size/ar from cond with fallback based on the latent image shape.
- bs = x.shape[0]
- data_info = {}
- if img_hw is None:
- data_info["img_hw"] = torch.tensor(
- [[x.shape[2]*8, x.shape[3]*8]],
- dtype=self.dtype,
- device=x.device
- ).repeat(bs, 1)
- else:
- data_info["img_hw"] = img_hw.to(x.dtype)
- if aspect_ratio is None or True:
- data_info["aspect_ratio"] = torch.tensor(
- [[x.shape[2]/x.shape[3]]],
- dtype=self.dtype,
- device=x.device
- ).repeat(bs, 1)
- else:
- data_info["aspect_ratio"] = aspect_ratio.to(x.dtype)
-
- ## Still accepts the input w/o that dim but returns garbage
- if len(context.shape) == 3:
- context = context.unsqueeze(1)
-
- ## run original forward pass
- out = self.forward_raw(
- x = x.to(self.dtype),
- timestep = timesteps.to(self.dtype),
- y = context.to(self.dtype),
- c = cn_hint,
- data_info=data_info,
- )
-
- ## only return EPS
- out = out.to(torch.float)
- eps, rest = out[:, :self.in_channels], out[:, self.in_channels:]
- return eps
diff --git a/PixArt/nodes.py b/PixArt/nodes.py
index f027d89..6a1f2dd 100644
--- a/PixArt/nodes.py
+++ b/PixArt/nodes.py
@@ -1,278 +1,87 @@
-import os
-import json
-import torch
-import folder_paths
-
-from comfy import utils
-from .conf import pixart_conf, pixart_res
-from .lora import load_pixart_lora
-from .loader import load_pixart
-
-class PixArtCheckpointLoader:
- @classmethod
- def INPUT_TYPES(s):
- return {
- "required": {
- "ckpt_name": (folder_paths.get_filename_list("checkpoints"),),
- "model": (list(pixart_conf.keys()),),
- }
- }
- RETURN_TYPES = ("MODEL",)
- RETURN_NAMES = ("model",)
- FUNCTION = "load_checkpoint"
- CATEGORY = "ExtraModels/PixArt"
- TITLE = "PixArt Checkpoint Loader"
-
- def load_checkpoint(self, ckpt_name, model):
- ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name)
- model_conf = pixart_conf[model]
- model = load_pixart(
- model_path = ckpt_path,
- model_conf = model_conf,
- )
- return (model,)
-
-class PixArtCheckpointLoaderSimple(PixArtCheckpointLoader):
- @classmethod
- def INPUT_TYPES(s):
- return {
- "required": {
- "ckpt_name": (folder_paths.get_filename_list("checkpoints"),),
- }
- }
- TITLE = "PixArt Checkpoint Loader (auto)"
-
- def load_checkpoint(self, ckpt_name):
- ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name)
- model = load_pixart(model_path=ckpt_path)
- return (model,)
-
-class PixArtResolutionSelect():
- @classmethod
- def INPUT_TYPES(s):
- return {
- "required": {
- "model": (list(pixart_res.keys()),),
- # keys are the same for both
- "ratio": (list(pixart_res["PixArtMS_XL_2"].keys()),{"default":"1.00"}),
- }
- }
- RETURN_TYPES = ("INT","INT")
- RETURN_NAMES = ("width","height")
- FUNCTION = "get_res"
- CATEGORY = "ExtraModels/PixArt"
- TITLE = "PixArt Resolution Select"
-
- def get_res(self, model, ratio):
- width, height = pixart_res[model][ratio]
- return (width,height)
-
-class PixArtLoraLoader:
- def __init__(self):
- self.loaded_lora = None
-
- @classmethod
- def INPUT_TYPES(s):
- return {
- "required": {
- "model": ("MODEL",),
- "lora_name": (folder_paths.get_filename_list("loras"), ),
- "strength": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}),
- }
- }
- RETURN_TYPES = ("MODEL",)
- FUNCTION = "load_lora"
- CATEGORY = "ExtraModels/PixArt"
- TITLE = "PixArt Load LoRA"
-
- def load_lora(self, model, lora_name, strength,):
- if strength == 0:
- return (model)
-
- lora_path = folder_paths.get_full_path("loras", lora_name)
- lora = None
- if self.loaded_lora is not None:
- if self.loaded_lora[0] == lora_path:
- lora = self.loaded_lora[1]
- else:
- temp = self.loaded_lora
- self.loaded_lora = None
- del temp
-
- if lora is None:
- lora = utils.load_torch_file(lora_path, safe_load=True)
- self.loaded_lora = (lora_path, lora)
-
- model_lora = load_pixart_lora(model, lora, lora_path, strength,)
- return (model_lora,)
-
class PixArtResolutionCond:
- @classmethod
- def INPUT_TYPES(s):
- return {
- "required": {
- "cond": ("CONDITIONING", ),
- "width": ("INT", {"default": 1024.0, "min": 0, "max": 8192}),
- "height": ("INT", {"default": 1024.0, "min": 0, "max": 8192}),
- }
- }
+ @classmethod
+ def INPUT_TYPES(s):
+ return {
+ "required": {
+ "cond": ("CONDITIONING", ),
+ "width": ("INT", {"default": 1024.0, "min": 0, "max": 8192}),
+ "height": ("INT", {"default": 1024.0, "min": 0, "max": 8192}),
+ }
+ }
- RETURN_TYPES = ("CONDITIONING",)
- RETURN_NAMES = ("cond",)
- FUNCTION = "add_cond"
- CATEGORY = "ExtraModels/PixArt"
- TITLE = "PixArt Resolution Conditioning"
-
- def add_cond(self, cond, width, height):
- for c in range(len(cond)):
- cond[c][1].update({
- "img_hw": [[height, width]],
- "aspect_ratio": [[height/width]],
- })
- return (cond,)
-
-class PixArtControlNetCond:
- @classmethod
- def INPUT_TYPES(s):
- return {
- "required": {
- "cond": ("CONDITIONING",),
- "latent": ("LATENT",),
- # "image": ("IMAGE",),
- # "vae": ("VAE",),
- # "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01})
- }
- }
-
- RETURN_TYPES = ("CONDITIONING",)
- RETURN_NAMES = ("cond",)
- FUNCTION = "add_cond"
- CATEGORY = "ExtraModels/PixArt"
- TITLE = "PixArt ControlNet Conditioning"
-
- def add_cond(self, cond, latent):
- for c in range(len(cond)):
- cond[c][1]["cn_hint"] = latent["samples"] * 0.18215
- return (cond,)
-
-class PixArtT5TextEncode:
- """
- Reference code, mostly to verify compatibility.
- Once everything works, this should instead inherit from the
- T5 text encode node and simply add the extra conds (res/ar).
- """
- @classmethod
- def INPUT_TYPES(s):
- return {
- "required": {
- "text": ("STRING", {"multiline": True}),
- "T5": ("T5",),
- }
- }
-
- RETURN_TYPES = ("CONDITIONING",)
- FUNCTION = "encode"
- CATEGORY = "ExtraModels/PixArt"
- TITLE = "PixArt T5 Text Encode [Reference]"
-
- def mask_feature(self, emb, mask):
- if emb.shape[0] == 1:
- keep_index = mask.sum().item()
- return emb[:, :, :keep_index, :], keep_index
- else:
- masked_feature = emb * mask[:, None, :, None]
- return masked_feature, emb.shape[2]
-
- def encode(self, text, T5):
- text = text.lower().strip()
- tokenizer_out = T5.tokenizer.tokenizer(
- text,
- max_length = 120,
- padding = 'max_length',
- truncation = True,
- return_attention_mask = True,
- add_special_tokens = True,
- return_tensors = 'pt'
- )
- tokens = tokenizer_out["input_ids"]
- mask = tokenizer_out["attention_mask"]
- embs = T5.cond_stage_model.transformer(
- input_ids = tokens.to(T5.load_device),
- attention_mask = mask.to(T5.load_device),
- )['last_hidden_state'].float()[:, None]
- masked_embs, keep_index = self.mask_feature(
- embs.detach().to("cpu"),
- mask.detach().to("cpu")
- )
- masked_embs = masked_embs.squeeze(0) # match CLIP/internal
- print("Encoded T5:", masked_embs.shape)
- return ([[masked_embs, {}]], )
+ RETURN_TYPES = ("CONDITIONING",)
+ RETURN_NAMES = ("cond",)
+ FUNCTION = "add_cond"
+ CATEGORY = "ExtraModels/PixArt"
+ TITLE = "PixArt Resolution Conditioning"
+
+ def add_cond(self, cond, width, height):
+ for c in range(len(cond)):
+ cond[c][1].update({
+ "img_hw": [[height, width]],
+ "aspect_ratio": [[height/width]],
+ })
+ return (cond,)
class PixArtT5FromSD3CLIP:
- """
- Split the T5 text encoder away from SD3
- """
- @classmethod
- def INPUT_TYPES(s):
- return {
- "required": {
- "sd3_clip": ("CLIP",),
- "padding": ("INT", {"default": 1, "min": 1, "max": 300}),
- }
- }
+ """
+ Split the T5 text encoder away from SD3
+ """
+ @classmethod
+ def INPUT_TYPES(s):
+ return {
+ "required": {
+ "sd3_clip": ("CLIP",),
+ "padding": ("INT", {"default": 1, "min": 1, "max": 300}),
+ }
+ }
- RETURN_TYPES = ("CLIP",)
- RETURN_NAMES = ("t5",)
- FUNCTION = "split"
- CATEGORY = "ExtraModels/PixArt"
- TITLE = "PixArt T5 from SD3 CLIP"
+ RETURN_TYPES = ("CLIP",)
+ RETURN_NAMES = ("t5",)
+ FUNCTION = "split"
+ CATEGORY = "ExtraModels/PixArt"
+ TITLE = "PixArt T5 from SD3 CLIP"
- def split(self, sd3_clip, padding):
- try:
- from comfy.text_encoders.sd3_clip import SD3Tokenizer, SD3ClipModel
- except ImportError:
- # fallback for older ComfyUI versions
- from comfy.sd3_clip import SD3Tokenizer, SD3ClipModel
- import copy
-
- clip = sd3_clip.clone()
- assert clip.cond_stage_model.t5xxl is not None, "CLIP must have T5 loaded!"
+ def split(self, sd3_clip, padding):
+ try:
+ from comfy.text_encoders.sd3_clip import SD3Tokenizer, SD3ClipModel
+ except ImportError:
+ # fallback for older ComfyUI versions
+ from comfy.sd3_clip import SD3Tokenizer, SD3ClipModel # type: ignore
+ import copy
+
+ clip = sd3_clip.clone()
+ assert clip.cond_stage_model.t5xxl is not None, "CLIP must have T5 loaded!"
- # remove transformer
- transformer = clip.cond_stage_model.t5xxl.transformer
- clip.cond_stage_model.t5xxl.transformer = None
+ # remove transformer
+ transformer = clip.cond_stage_model.t5xxl.transformer
+ clip.cond_stage_model.t5xxl.transformer = None
- # clone object
- tmp = SD3ClipModel(clip_l=False, clip_g=False, t5=False)
- tmp.t5xxl = copy.deepcopy(clip.cond_stage_model.t5xxl)
- # put transformer back
- clip.cond_stage_model.t5xxl.transformer = transformer
- tmp.t5xxl.transformer = transformer
+ # clone object
+ tmp = SD3ClipModel(clip_l=False, clip_g=False, t5=False)
+ tmp.t5xxl = copy.deepcopy(clip.cond_stage_model.t5xxl)
+ # put transformer back
+ clip.cond_stage_model.t5xxl.transformer = transformer
+ tmp.t5xxl.transformer = transformer
- # override special tokens
- tmp.t5xxl.special_tokens = copy.deepcopy(clip.cond_stage_model.t5xxl.special_tokens)
- tmp.t5xxl.special_tokens.pop("end") # make sure empty tokens match
-
- # add attn mask opt if present in original
- if hasattr(sd3_clip.cond_stage_model, "t5_attention_mask"):
- tmp.t5_attention_mask = False
+ # override special tokens
+ tmp.t5xxl.special_tokens = copy.deepcopy(clip.cond_stage_model.t5xxl.special_tokens)
+ tmp.t5xxl.special_tokens.pop("end") # make sure empty tokens match
+
+ # add attn mask opt if present in original
+ if hasattr(sd3_clip.cond_stage_model, "t5_attention_mask"):
+ tmp.t5_attention_mask = False
- # tokenizer
- tok = SD3Tokenizer()
- tok.t5xxl.min_length = padding
-
- clip.cond_stage_model = tmp
- clip.tokenizer = tok
+ # tokenizer
+ tok = SD3Tokenizer()
+ tok.t5xxl.min_length = padding
+
+ clip.cond_stage_model = tmp
+ clip.tokenizer = tok
- return (clip, )
+ return (clip, )
NODE_CLASS_MAPPINGS = {
- "PixArtCheckpointLoader" : PixArtCheckpointLoader,
- "PixArtCheckpointLoaderSimple" : PixArtCheckpointLoaderSimple,
- "PixArtResolutionSelect" : PixArtResolutionSelect,
- "PixArtLoraLoader" : PixArtLoraLoader,
- "PixArtT5TextEncode" : PixArtT5TextEncode,
- "PixArtResolutionCond" : PixArtResolutionCond,
- "PixArtControlNetCond" : PixArtControlNetCond,
- "PixArtT5FromSD3CLIP": PixArtT5FromSD3CLIP,
+ "PixArtResolutionCond" : PixArtResolutionCond,
+ "PixArtT5FromSD3CLIP": PixArtT5FromSD3CLIP,
}
diff --git a/T5/LICENSE-ComfyUI b/T5/LICENSE-ComfyUI
deleted file mode 100644
index f288702..0000000
--- a/T5/LICENSE-ComfyUI
+++ /dev/null
@@ -1,674 +0,0 @@
- 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/T5/loader.py b/T5/loader.py
deleted file mode 100644
index db321ab..0000000
--- a/T5/loader.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import os
-import torch
-import comfy.utils
-import comfy.model_patcher
-from comfy import model_management
-import folder_paths
-
-from .t5v11 import T5v11Model, T5v11Tokenizer
-
-class EXM_T5v11:
- def __init__(self, textmodel_ver="xxl", embedding_directory=None, textmodel_path=None, no_init=False, device="cpu", dtype=None):
- if no_init:
- return
-
- if device == "auto":
- size = 0
- self.load_device = model_management.text_encoder_device()
- self.offload_device = model_management.text_encoder_offload_device()
- self.init_device = "cpu"
- elif dtype == "bnb8bit":
- # BNB doesn't support size enum
- size = 12.4 * (1024**3)
- # Or moving between devices
- self.load_device = model_management.get_torch_device()
- self.offload_device = self.load_device
- self.init_device = self.load_device
- elif dtype == "bnb4bit":
- # This seems to use the same VRAM as 8bit on Pascal?
- size = 6.2 * (1024**3)
- self.load_device = model_management.get_torch_device()
- self.offload_device = self.load_device
- self.init_device = self.load_device
- elif device == "cpu":
- size = 0
- self.load_device = "cpu"
- self.offload_device = "cpu"
- self.init_device="cpu"
- elif device.startswith("cuda"):
- print("Direct CUDA device override!\nVRAM will not be freed by default.")
- size = 0
- self.load_device = device
- self.offload_device = device
- self.init_device = device
- else:
- size = 0
- self.load_device = model_management.get_torch_device()
- self.offload_device = "cpu"
- self.init_device="cpu"
-
- self.cond_stage_model = T5v11Model(
- textmodel_ver = textmodel_ver,
- textmodel_path = textmodel_path,
- device = device,
- dtype = dtype,
- )
- self.tokenizer = T5v11Tokenizer(embedding_directory=embedding_directory)
- self.patcher = comfy.model_patcher.ModelPatcher(
- self.cond_stage_model,
- load_device = self.load_device,
- offload_device = self.offload_device,
- size = size,
- )
-
- def clone(self):
- n = T5(no_init=True)
- n.patcher = self.patcher.clone()
- n.cond_stage_model = self.cond_stage_model
- n.tokenizer = self.tokenizer
- return n
-
- def tokenize(self, text, return_word_ids=False):
- return self.tokenizer.tokenize_with_weights(text, return_word_ids)
-
- def encode_from_tokens(self, tokens):
- self.load_model()
- return self.cond_stage_model.encode_token_weights(tokens)
-
- def encode(self, text):
- tokens = self.tokenize(text)
- return self.encode_from_tokens(tokens)
-
- def load_sd(self, sd):
- return self.cond_stage_model.load_sd(sd)
-
- def get_sd(self):
- return self.cond_stage_model.state_dict()
-
- def load_model(self):
- if self.load_device != "cpu":
- model_management.load_model_gpu(self.patcher)
- return self.patcher
-
- def add_patches(self, patches, strength_patch=1.0, strength_model=1.0):
- return self.patcher.add_patches(patches, strength_patch, strength_model)
-
- def get_key_patches(self):
- return self.patcher.get_key_patches()
-
-
-def load_t5(model_type, model_ver, model_path, path_type="file", device="cpu", dtype=None):
- assert model_type in ["t5v11"] # Only supported model for now
- model_args = {
- "textmodel_ver" : model_ver,
- "device" : device,
- "dtype" : dtype,
- }
-
- if path_type == "folder":
- # pass directly to transformers and initialize there
- # this is to avoid having to handle multi-file state dict loading for now.
- model_args["textmodel_path"] = os.path.dirname(model_path)
- return EXM_T5v11(**model_args)
- else:
- # for some reason this returns garbage with torch.int8 weights, or just OOMs
- model = EXM_T5v11(**model_args)
- sd = comfy.utils.load_torch_file(model_path)
- model.load_sd(sd)
- return model
diff --git a/T5/nodes.py b/T5/nodes.py
deleted file mode 100644
index 021386e..0000000
--- a/T5/nodes.py
+++ /dev/null
@@ -1,95 +0,0 @@
-import os
-import json
-import torch
-import folder_paths
-
-from .loader import load_t5
-from ..utils.dtype import string_to_dtype
-
-# initialize custom folder path
-os.makedirs(
- os.path.join(folder_paths.models_dir,"t5"),
- exist_ok = True,
-)
-folder_paths.folder_names_and_paths["t5"] = (
- [
- os.path.join(folder_paths.models_dir,"t5"),
- *folder_paths.folder_names_and_paths.get("t5", [[],set()])[0]
- ],
- folder_paths.supported_pt_extensions
-)
-
-dtypes = [
- "default",
- "auto (comfy)",
- "FP32",
- "FP16",
- # Note: remove these at some point
- "bnb8bit",
- "bnb4bit",
-]
-try: torch.float8_e5m2
-except AttributeError: print("Torch version too old for FP8")
-else: dtypes += ["FP8 E4M3", "FP8 E5M2"]
-
-class T5v11Loader:
- @classmethod
- def INPUT_TYPES(s):
- devices = ["auto", "cpu", "gpu"]
- # hack for using second GPU as offload
- for k in range(1, torch.cuda.device_count()):
- devices.append(f"cuda:{k}")
- return {
- "required": {
- "t5v11_name": (folder_paths.get_filename_list("t5"),),
- "t5v11_ver": (["xxl"],),
- "path_type": (["folder", "file"],),
- "device": (devices, {"default":"cpu"}),
- "dtype": (dtypes,),
- }
- }
- RETURN_TYPES = ("T5",)
- FUNCTION = "load_model"
- CATEGORY = "ExtraModels/T5"
- TITLE = "T5v1.1 Loader"
-
- def load_model(self, t5v11_name, t5v11_ver, path_type, device, dtype):
- if "bnb" in dtype:
- assert device == "gpu" or device.startswith("cuda"), "BitsAndBytes only works on CUDA! Set device to 'gpu'."
- dtype = string_to_dtype(dtype, "text_encoder")
- if device == "cpu":
- assert dtype in [None, torch.float32], f"Can't use dtype '{dtype}' with CPU! Set dtype to 'default'."
-
- return (load_t5(
- model_type = "t5v11",
- model_ver = t5v11_ver,
- model_path = folder_paths.get_full_path("t5", t5v11_name),
- path_type = path_type,
- device = device,
- dtype = dtype,
- ),)
-
-class T5TextEncode:
- @classmethod
- def INPUT_TYPES(s):
- return {
- "required": {
- "text": ("STRING", {"multiline": True}),
- "T5": ("T5",),
- }
- }
-
- RETURN_TYPES = ("CONDITIONING",)
- FUNCTION = "encode"
- CATEGORY = "ExtraModels/T5"
- TITLE = "T5 Text Encode"
-
- def encode(self, text, T5=None):
- tokens = T5.tokenize(text)
- cond = T5.encode_from_tokens(tokens)
- return ([[cond, {}]], )
-
-NODE_CLASS_MAPPINGS = {
- "T5v11Loader" : T5v11Loader,
- "T5TextEncode" : T5TextEncode,
-}
diff --git a/T5/t5v11-xxl_config.json b/T5/t5v11-xxl_config.json
deleted file mode 100644
index d133daa..0000000
--- a/T5/t5v11-xxl_config.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "_name_or_path": "google/t5-v1_1-xxl",
- "architectures": [
- "T5EncoderModel"
- ],
- "d_ff": 10240,
- "d_kv": 64,
- "d_model": 4096,
- "decoder_start_token_id": 0,
- "dense_act_fn": "gelu_new",
- "dropout_rate": 0.1,
- "eos_token_id": 1,
- "feed_forward_proj": "gated-gelu",
- "initializer_factor": 1.0,
- "is_encoder_decoder": true,
- "is_gated_act": true,
- "layer_norm_epsilon": 1e-06,
- "model_type": "t5",
- "num_decoder_layers": 24,
- "num_heads": 64,
- "num_layers": 24,
- "output_past": true,
- "pad_token_id": 0,
- "relative_attention_max_distance": 128,
- "relative_attention_num_buckets": 32,
- "tie_word_embeddings": false,
- "torch_dtype": "float32",
- "transformers_version": "4.21.1",
- "use_cache": true,
- "vocab_size": 32128
-}
diff --git a/T5/t5v11.py b/T5/t5v11.py
deleted file mode 100644
index 76cec9c..0000000
--- a/T5/t5v11.py
+++ /dev/null
@@ -1,227 +0,0 @@
-"""
-Adapted from comfyui CLIP code.
-https://github.com/comfyanonymous/ComfyUI/blob/master/comfy/sd1_clip.py
-"""
-
-import os
-
-from transformers import T5Tokenizer, T5EncoderModel, T5Config, modeling_utils
-import torch
-import traceback
-import zipfile
-from comfy import model_management
-
-from comfy.sd1_clip import parse_parentheses, token_weights, escape_important, unescape_important, safe_load_embed_zip, expand_directory_list, load_embed
-
-class T5v11Model(torch.nn.Module):
- def __init__(self, textmodel_ver="xxl", textmodel_json_config=None, textmodel_path=None, device="cpu", max_length=120, freeze=True, dtype=None):
- super().__init__()
-
- self.num_layers = 24
- self.max_length = max_length
- self.bnb = False
-
- if textmodel_path is not None:
- model_args = {}
- model_args["low_cpu_mem_usage"] = True # Don't take 2x system ram on cpu
- if dtype == "bnb8bit":
- self.bnb = True
- model_args["load_in_8bit"] = True
- elif dtype == "bnb4bit":
- self.bnb = True
- model_args["load_in_4bit"] = True
- else:
- if dtype: model_args["torch_dtype"] = dtype
- self.bnb = False
- # second GPU offload hack part 2
- if device.startswith("cuda"):
- model_args["device_map"] = device
- print(f"Loading T5 from '{textmodel_path}'")
- self.transformer = T5EncoderModel.from_pretrained(textmodel_path, **model_args)
- else:
- if textmodel_json_config is None:
- textmodel_json_config = os.path.join(
- os.path.dirname(os.path.realpath(__file__)),
- f"t5v11-{textmodel_ver}_config.json"
- )
- config = T5Config.from_json_file(textmodel_json_config)
- self.num_layers = config.num_hidden_layers
- with modeling_utils.no_init_weights():
- self.transformer = T5EncoderModel(config)
-
- if freeze:
- self.freeze()
- self.empty_tokens = [[0] * self.max_length] # token
-
- def freeze(self):
- self.transformer = self.transformer.eval()
- for param in self.parameters():
- param.requires_grad = False
-
- def forward(self, tokens):
- device = self.transformer.get_input_embeddings().weight.device
- tokens = torch.LongTensor(tokens).to(device)
- attention_mask = torch.zeros_like(tokens)
- max_token = 1 # token
- for x in range(attention_mask.shape[0]):
- for y in range(attention_mask.shape[1]):
- attention_mask[x, y] = 1
- if tokens[x, y] == max_token:
- break
-
- outputs = self.transformer(input_ids=tokens, attention_mask=attention_mask)
-
- z = outputs['last_hidden_state']
- z.detach().cpu().float()
- return z
-
- def encode(self, tokens):
- return self(tokens)
-
- def load_sd(self, sd):
- return self.transformer.load_state_dict(sd, strict=False)
-
- def to(self, *args, **kwargs):
- """BNB complains if you try to change the device or dtype"""
- if self.bnb:
- print("Thanks to BitsAndBytes, T5 becomes an immovable rock.", args, kwargs)
- else:
- self.transformer.to(*args, **kwargs)
-
- def encode_token_weights(self, token_weight_pairs, return_padded=False):
- to_encode = list(self.empty_tokens)
- for x in token_weight_pairs:
- tokens = list(map(lambda a: a[0], x))
- to_encode.append(tokens)
-
- out = self.encode(to_encode)
- z_empty = out[0:1]
-
- output = []
- for k in range(1, out.shape[0]):
- z = out[k:k+1]
- for i in range(len(z)):
- for j in range(len(z[i])):
- weight = token_weight_pairs[k - 1][j][1]
- z[i][j] = (z[i][j] - z_empty[0][j]) * weight + z_empty[0][j]
- output.append(z)
-
- if (len(output) == 0):
- return z_empty.cpu()
-
- out = torch.cat(output, dim=-2)
- if not return_padded:
- # Count number of tokens that aren't , then use that number as an index.
- keep_index = sum([sum([1 for y in x if y[0] != 0]) for x in token_weight_pairs])
- out = out[:, :keep_index, :]
- return out
-
-
-class T5v11Tokenizer:
- """
- This is largely just based on the ComfyUI CLIP code.
- """
- def __init__(self, tokenizer_path=None, max_length=120, embedding_directory=None, embedding_size=4096, embedding_key='t5'):
- if tokenizer_path is None:
- tokenizer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "t5_tokenizer")
- self.tokenizer = T5Tokenizer.from_pretrained(tokenizer_path)
- self.max_length = max_length
- self.max_tokens_per_section = self.max_length - 1 # but no
-
- self.pad_token = self.tokenizer("", add_special_tokens=False)["input_ids"][0]
- self.end_token = self.tokenizer("", add_special_tokens=False)["input_ids"][0]
- vocab = self.tokenizer.get_vocab()
- self.inv_vocab = {v: k for k, v in vocab.items()}
- self.embedding_directory = embedding_directory
- self.max_word_length = 8 # haven't verified this
- self.embedding_identifier = "embedding:"
- self.embedding_size = embedding_size
- self.embedding_key = embedding_key
-
- def _try_get_embedding(self, embedding_name:str):
- '''
- Takes a potential embedding name and tries to retrieve it.
- Returns a Tuple consisting of the embedding and any leftover string, embedding can be None.
- '''
- embed = load_embed(embedding_name, self.embedding_directory, self.embedding_size, self.embedding_key)
- if embed is None:
- stripped = embedding_name.strip(',')
- if len(stripped) < len(embedding_name):
- embed = load_embed(stripped, self.embedding_directory, self.embedding_size, self.embedding_key)
- return (embed, embedding_name[len(stripped):])
- return (embed, "")
-
- def tokenize_with_weights(self, text:str, return_word_ids=False):
- '''
- Takes a prompt and converts it to a list of (token, weight, word id) elements.
- Tokens can both be integer tokens and pre computed T5 tensors.
- Word id values are unique per word and embedding, where the id 0 is reserved for non word tokens.
- Returned list has the dimensions NxM where M is the input size of T5
- '''
- pad_token = self.pad_token
- text = escape_important(text)
- parsed_weights = token_weights(text, 1.0)
-
- #tokenize words
- tokens = []
- for weighted_segment, weight in parsed_weights:
- to_tokenize = unescape_important(weighted_segment).replace("\n", " ").split(' ')
- to_tokenize = [x for x in to_tokenize if x != ""]
- for word in to_tokenize:
- #if we find an embedding, deal with the embedding
- if word.startswith(self.embedding_identifier) and self.embedding_directory is not None:
- embedding_name = word[len(self.embedding_identifier):].strip('\n')
- embed, leftover = self._try_get_embedding(embedding_name)
- if embed is None:
- print(f"warning, embedding:{embedding_name} does not exist, ignoring")
- else:
- if len(embed.shape) == 1:
- tokens.append([(embed, weight)])
- else:
- tokens.append([(embed[x], weight) for x in range(embed.shape[0])])
- #if we accidentally have leftover text, continue parsing using leftover, else move on to next word
- if leftover != "":
- word = leftover
- else:
- continue
- #parse word
- tokens.append([(t, weight) for t in self.tokenizer(word, add_special_tokens=False)["input_ids"]])
-
- #reshape token array to T5 input size
- batched_tokens = []
- batch = []
- batched_tokens.append(batch)
- for i, t_group in enumerate(tokens):
- #determine if we're going to try and keep the tokens in a single batch
- is_large = len(t_group) >= self.max_word_length
-
- while len(t_group) > 0:
- if len(t_group) + len(batch) > self.max_length - 1:
- remaining_length = self.max_length - len(batch) - 1
- #break word in two and add end token
- if is_large:
- batch.extend([(t,w,i+1) for t,w in t_group[:remaining_length]])
- batch.append((self.end_token, 1.0, 0))
- t_group = t_group[remaining_length:]
- #add end token and pad
- else:
- batch.append((self.end_token, 1.0, 0))
- batch.extend([(self.pad_token, 1.0, 0)] * (remaining_length))
- #start new batch
- batch = []
- batched_tokens.append(batch)
- else:
- batch.extend([(t,w,i+1) for t,w in t_group])
- t_group = []
-
- # fill last batch
- batch.extend([(self.end_token, 1.0, 0)] + [(self.pad_token, 1.0, 0)] * (self.max_length - len(batch) - 1))
- # instead of filling, just add EOS (DEBUG)
- # batch.extend([(self.end_token, 1.0, 0)])
-
- if not return_word_ids:
- batched_tokens = [[(t, w) for t, w,_ in x] for x in batched_tokens]
- return batched_tokens
-
- def untokenize(self, token_weight_pair):
- return list(map(lambda a: (a, self.inv_vocab[a[0]]), token_weight_pair))
diff --git a/__init__.py b/__init__.py
index 1fff84c..5dc119f 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,51 +1,50 @@
# only import if running as a custom node
try:
- import comfy.utils
+ import comfy.utils
except ImportError:
- pass
+ pass
else:
- NODE_CLASS_MAPPINGS = {}
+ NODE_CLASS_MAPPINGS = {}
- # Deci Diffusion
- # from .DeciDiffusion.nodes import NODE_CLASS_MAPPINGS as DeciDiffusion_Nodes
- # NODE_CLASS_MAPPINGS.update(DeciDiffusion_Nodes)
+ # All text encoders
+ from .text_encoders.nodes import NODE_CLASS_MAPPINGS as Tenc_Nodes
+ NODE_CLASS_MAPPINGS.update(Tenc_Nodes)
- # DiT
- from .DiT.nodes import NODE_CLASS_MAPPINGS as DiT_Nodes
- NODE_CLASS_MAPPINGS.update(DiT_Nodes)
+ # Generic nodes
+ from .nodes import NODE_CLASS_MAPPINGS as Base_Nodes
+ NODE_CLASS_MAPPINGS.update(Base_Nodes)
- # PixArt
- from .PixArt.nodes import NODE_CLASS_MAPPINGS as PixArt_Nodes
- NODE_CLASS_MAPPINGS.update(PixArt_Nodes)
+ # DiT
+ from .DiT.nodes import NODE_CLASS_MAPPINGS as DiT_Nodes
+ NODE_CLASS_MAPPINGS.update(DiT_Nodes)
- # T5
- from .T5.nodes import NODE_CLASS_MAPPINGS as T5_Nodes
- NODE_CLASS_MAPPINGS.update(T5_Nodes)
+ # PixArt
+ from .PixArt.nodes import NODE_CLASS_MAPPINGS as PixArt_Nodes
+ NODE_CLASS_MAPPINGS.update(PixArt_Nodes)
- # HYDiT
- from .HunYuanDiT.nodes import NODE_CLASS_MAPPINGS as HunYuanDiT_Nodes
- NODE_CLASS_MAPPINGS.update(HunYuanDiT_Nodes)
+ # HYDiT
+ from .HunYuanDiT.nodes import NODE_CLASS_MAPPINGS as HunYuanDiT_Nodes
+ NODE_CLASS_MAPPINGS.update(HunYuanDiT_Nodes)
- # VAE
- from .VAE.nodes import NODE_CLASS_MAPPINGS as VAE_Nodes
- NODE_CLASS_MAPPINGS.update(VAE_Nodes)
+ # VAE
+ from .VAE.nodes import NODE_CLASS_MAPPINGS as VAE_Nodes
+ NODE_CLASS_MAPPINGS.update(VAE_Nodes)
- # MiaoBi
- from .MiaoBi.nodes import NODE_CLASS_MAPPINGS as MiaoBi_Nodes
- NODE_CLASS_MAPPINGS.update(MiaoBi_Nodes)
+ # MiaoBi
+ from .MiaoBi.nodes import NODE_CLASS_MAPPINGS as MiaoBi_Nodes
+ NODE_CLASS_MAPPINGS.update(MiaoBi_Nodes)
- # Extra
- from .utils.nodes import NODE_CLASS_MAPPINGS as Extra_Nodes
- NODE_CLASS_MAPPINGS.update(Extra_Nodes)
+ # Extra
+ from .utils.nodes import NODE_CLASS_MAPPINGS as Extra_Nodes
+ NODE_CLASS_MAPPINGS.update(Extra_Nodes)
- # Sana
- from .Sana.nodes import NODE_CLASS_MAPPINGS as Sana_Nodes
- NODE_CLASS_MAPPINGS.update(Sana_Nodes)
+ # Sana
+ from .Sana.nodes import NODE_CLASS_MAPPINGS as Sana_Nodes
+ NODE_CLASS_MAPPINGS.update(Sana_Nodes)
- # Gemma
- from .Gemma.nodes import NODE_CLASS_MAPPINGS as Gemma_Nodes
- NODE_CLASS_MAPPINGS.update(Gemma_Nodes)
-
- NODE_DISPLAY_NAME_MAPPINGS = {k:v.TITLE for k,v in NODE_CLASS_MAPPINGS.items()}
- __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
+ # Gemma
+ from .Gemma.nodes import NODE_CLASS_MAPPINGS as Gemma_Nodes
+ NODE_CLASS_MAPPINGS.update(Gemma_Nodes)
+ NODE_DISPLAY_NAME_MAPPINGS = {k:v.TITLE for k,v in NODE_CLASS_MAPPINGS.items()}
+ __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
diff --git a/nodes.py b/nodes.py
new file mode 100644
index 0000000..eab0d3d
--- /dev/null
+++ b/nodes.py
@@ -0,0 +1,34 @@
+import folder_paths
+import comfy.utils
+
+from .PixArt.loader import load_pixart_state_dict
+
+loaders = {
+ "PixArt": load_pixart_state_dict,
+}
+
+class EXMUnetLoader:
+ @classmethod
+ def INPUT_TYPES(s):
+ return {
+ "required": {
+ "unet_name": (folder_paths.get_filename_list("unet"),),
+ "model_type": (list(loaders.keys()),)
+ }
+ }
+
+ RETURN_TYPES = ("MODEL",)
+ FUNCTION = "load_unet"
+ CATEGORY = "ExtraModels"
+ TITLE = "Load Diffusion Model (ExtraModels)"
+
+ def load_unet(self, unet_name, model_type):
+ model_options = {}
+ unet_path = folder_paths.get_full_path("diffusion_models", unet_name)
+ loader_fn = loaders[model_type]
+ sd = comfy.utils.load_torch_file(unet_path)
+ return (loader_fn(sd),)
+
+NODE_CLASS_MAPPINGS = {
+ "EXMUnetLoader": EXMUnetLoader,
+}
diff --git a/nodes/pixart.py b/nodes/pixart.py
new file mode 100644
index 0000000..e69de29
diff --git a/text_encoders/nodes.py b/text_encoders/nodes.py
new file mode 100644
index 0000000..5bda333
--- /dev/null
+++ b/text_encoders/nodes.py
@@ -0,0 +1,37 @@
+import folder_paths
+
+from .tenc import load_text_encoder, tenc_names
+
+class EXMCLIPLoader:
+ @classmethod
+ def INPUT_TYPES(s):
+ files = []
+ files += folder_paths.get_filename_list("clip")
+ # if "clip_gguf" in folder_paths.folder_names_and_paths:
+ # files += folder_paths.get_filename_list("clip_gguf")
+ return {
+ "required": {
+ "clip_name": (files, ),
+ "type": (["PixArt", "MiaoBi", "Sana"],),
+ }
+ }
+
+ RETURN_TYPES = ("CLIP",)
+ FUNCTION = "load_clip"
+ CATEGORY = "ExtraModels"
+ TITLE = "CLIPLoader (ExtraModels)"
+
+ def load_clip(self, clip_name, type):
+ clip_path = folder_paths.get_full_path("clip", clip_name)
+ clip_type = tenc_names.get(type, None)
+
+ clip = load_text_encoder(
+ ckpt_paths =[clip_path],
+ embedding_directory = folder_paths.get_folder_paths("embeddings"),
+ clip_type = clip_type
+ )
+ return (clip,)
+
+NODE_CLASS_MAPPINGS = {
+ "EXMCLIPLoader": EXMCLIPLoader,
+}
diff --git a/text_encoders/pixart/tenc.py b/text_encoders/pixart/tenc.py
new file mode 100644
index 0000000..456cd92
--- /dev/null
+++ b/text_encoders/pixart/tenc.py
@@ -0,0 +1,41 @@
+from comfy import sd1_clip
+import comfy.text_encoders.t5
+import comfy.text_encoders.sd3_clip
+import comfy.model_management
+from transformers import T5TokenizerFast
+import torch
+import os
+
+class T5XXLModel(comfy.text_encoders.sd3_clip.T5XXLModel):
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+ # make sure empty tokens match
+ self.special_tokens.pop("end")
+
+class PixArtT5XXL(sd1_clip.SD1ClipModel):
+ def __init__(self, device="cpu", dtype=None, model_options={}):
+ super().__init__(device=device, dtype=dtype, name="t5xxl", clip_model=T5XXLModel, model_options=model_options)
+
+class T5XXLTokenizer(sd1_clip.SDTokenizer):
+ def __init__(self, embedding_directory=None, tokenizer_data={}):
+ tokenizer_path = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
+ "tokenizers", "t5_tokenizer",
+ )
+ super().__init__(tokenizer_path, embedding_directory=embedding_directory, pad_with_end=False, embedding_size=4096, embedding_key='t5xxl', tokenizer_class=T5TokenizerFast, has_start_token=False, pad_to_max_length=False, max_length=99999999, min_length=1)
+
+class PixArtTokenizer(sd1_clip.SD1Tokenizer):
+ def __init__(self, embedding_directory=None, tokenizer_data={}):
+ super().__init__(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data, clip_name="t5xxl", tokenizer=T5XXLTokenizer)
+
+# TODO: don't duplicate this?
+def pixart_te(dtype_t5=None, t5xxl_scaled_fp8=None):
+ class PixArtTEModel_(PixArtT5XXL):
+ def __init__(self, device="cpu", dtype=None, model_options={}):
+ if t5xxl_scaled_fp8 is not None and "t5xxl_scaled_fp8" not in model_options:
+ model_options = model_options.copy()
+ model_options["t5xxl_scaled_fp8"] = t5xxl_scaled_fp8
+ if dtype is None:
+ dtype = dtype_t5
+ super().__init__(device=device, dtype=dtype, model_options=model_options)
+ return PixArtTEModel_
diff --git a/text_encoders/tenc.py b/text_encoders/tenc.py
new file mode 100644
index 0000000..239d97d
--- /dev/null
+++ b/text_encoders/tenc.py
@@ -0,0 +1,72 @@
+import logging
+from enum import Enum
+
+import comfy.sd
+import comfy.utils
+import comfy.text_encoders
+
+from .pixart.tenc import pixart_te, PixArtTokenizer
+
+class TencType(Enum):
+ # offset in case we ever integrate w/ original
+ PixArt = 1001
+ MiaoBi = 1002
+ # HunYuan = 1003 # deprecated
+ Sana = 1004
+
+tenc_names = {
+ # for node readout
+ "PixArt": TencType.PixArt,
+ "MiaoBi": TencType.MiaoBi,
+ # "HunYuan": TencType.HunYuan,
+ "Sana": TencType.Sana,
+}
+
+
+def load_text_encoder(ckpt_paths, embedding_directory=None, clip_type=TencType.PixArt, model_options={}):
+ # Partial duplicate of ComfyUI/comfy/sd:load_clip
+ clip_data = []
+ for p in ckpt_paths:
+ if p.lower().endswith(".gguf"):
+ # TODO: cross-node call w/o code duplication
+ raise NotImplementedError("Planned!")
+ else:
+ clip_data.append(comfy.utils.load_torch_file(p, safe_load=True))
+ return load_text_encoder_state_dicts(clip_data, embedding_directory=embedding_directory, clip_type=clip_type, model_options=model_options)
+
+def load_text_encoder_state_dicts(state_dicts=[], embedding_directory=None, clip_type=TencType.PixArt, model_options={}):
+ # Partial duplicate of ComfyUI/comfy/sd:load_text_encoder_state_dicts
+ clip_data = state_dicts
+
+ class EmptyClass:
+ pass
+
+ for i in range(len(clip_data)):
+ if "transformer.resblocks.0.ln_1.weight" in clip_data[i]:
+ clip_data[i] = comfy.utils.clip_text_transformers_convert(clip_data[i], "", "")
+ else:
+ if "text_projection" in clip_data[i]:
+ clip_data[i]["text_projection.weight"] = clip_data[i]["text_projection"].transpose(0, 1) #old models saved with the CLIPSave node
+
+ clip_target = EmptyClass()
+ clip_target.params = {}
+
+ if clip_type == TencType.PixArt:
+ clip_target.clip = pixart_te(**comfy.sd.t5xxl_detect(clip_data))
+ clip_target.tokenizer = PixArtTokenizer
+
+ parameters = 0
+ tokenizer_data = {}
+ for c in clip_data:
+ parameters += comfy.utils.calculate_parameters(c)
+ tokenizer_data, model_options = comfy.text_encoders.long_clipl.model_options_long_clip(c, tokenizer_data, model_options)
+
+ clip = comfy.sd.CLIP(clip_target, embedding_directory=embedding_directory, parameters=parameters, tokenizer_data=tokenizer_data, model_options=model_options)
+ for c in clip_data:
+ m, u = clip.load_sd(c)
+ if len(m) > 0:
+ logging.warning("clip missing: {}".format(m))
+
+ if len(u) > 0:
+ logging.debug("clip unexpected: {}".format(u))
+ return clip
diff --git a/T5/t5_tokenizer/special_tokens_map.json b/text_encoders/tokenizers/t5_tokenizer/special_tokens_map.json
similarity index 100%
rename from T5/t5_tokenizer/special_tokens_map.json
rename to text_encoders/tokenizers/t5_tokenizer/special_tokens_map.json
diff --git a/T5/t5_tokenizer/spiece.model b/text_encoders/tokenizers/t5_tokenizer/spiece.model
similarity index 100%
rename from T5/t5_tokenizer/spiece.model
rename to text_encoders/tokenizers/t5_tokenizer/spiece.model
diff --git a/T5/t5_tokenizer/tokenizer_config.json b/text_encoders/tokenizers/t5_tokenizer/tokenizer_config.json
similarity index 100%
rename from T5/t5_tokenizer/tokenizer_config.json
rename to text_encoders/tokenizers/t5_tokenizer/tokenizer_config.json