Compare commits
2719 Commits
installer_
...
v3.0.2
Author | SHA1 | Date | |
---|---|---|---|
4bd89ab2e1 | |||
d4427b97ae | |||
4f336d9f25 | |||
1565530b0f | |||
a21b01a0cd | |||
1c7e90576d | |||
c8de1cd49b | |||
5eb36e131d | |||
b5d1adaa19 | |||
b89d152540 | |||
e49772030d | |||
b1cb03962c | |||
a7b0858b22 | |||
ad227ca190 | |||
a8360484b2 | |||
80c4a50ca1 | |||
768b88a0ac | |||
82607573fa | |||
d07e00cd74 | |||
dfdd2b32e0 | |||
844edbc865 | |||
2bc66cc640 | |||
f9f9aba92d | |||
3f278cf2ad | |||
cb7ba96dad | |||
31edce4a60 | |||
1b6aae9678 | |||
9572ddf1c1 | |||
3bbce82454 | |||
1f44cebd0e | |||
843ea58c15 | |||
1e13c4e808 | |||
ab8f10ae4a | |||
c62161770d | |||
15b828b0f5 | |||
faa83a87df | |||
796c12bc4c | |||
50da182e30 | |||
dba573bf1a | |||
6a0eef3fe4 | |||
98f58e8672 | |||
04274f5839 | |||
f387b9f464 | |||
b8f533d0ea | |||
5a49818a10 | |||
ad9d9e0b04 | |||
c92470ff7e | |||
1cd9c7fdac | |||
e607035c65 | |||
bde8113414 | |||
1fd011b1be | |||
061380742c | |||
8f9feb3ed9 | |||
0dc01cb974 | |||
55af328181 | |||
a8c0abfd5d | |||
4807744aa7 | |||
669d40a9d2 | |||
18049d529a | |||
f2b441d9fc | |||
d2078d4dde | |||
41d4ad2096 | |||
29ec8291ad | |||
b93a206a48 | |||
be83336cf7 | |||
19fdba7d73 | |||
2c2b3b75d5 | |||
47d5cb9e33 | |||
7b8e1bc919 | |||
77aa7a0148 | |||
bdd7d2599f | |||
ca8a96f956 | |||
8957250db8 | |||
1b6ec418a1 | |||
3759d77945 | |||
ab4d34e509 | |||
7f878f365b | |||
5efabfaea6 | |||
4cd8ae45e3 | |||
8999f9450f | |||
1d54943d71 | |||
767d8fc35d | |||
894f34678e | |||
1190bedafd | |||
e80db71d1c | |||
846bb2134e | |||
38b2eec4be | |||
8dafe486a2 | |||
c895a96a43 | |||
67cae9725e | |||
a2d06f87f6 | |||
8e4afc8374 | |||
afd879a692 | |||
83de2b8de7 | |||
4930f36a1a | |||
fa3f196add | |||
95004be0e9 | |||
281a849c8f | |||
5d82ce665c | |||
d632cfcde9 | |||
07f797a5e4 | |||
121107dd13 | |||
a2479b74be | |||
7ee1d3cd91 | |||
4b28ddd691 | |||
7270b5fe0c | |||
285792f692 | |||
23a0a48b81 | |||
2baad73bb9 | |||
097dc99e77 | |||
edd10bcfe7 | |||
ac1c65fba1 | |||
b4cc21ea89 | |||
3dfc3f5ff7 | |||
7c012df1d5 | |||
a1854d3734 | |||
074c566826 | |||
a2e7bfb30e | |||
01c1c77564 | |||
34de4fe8fe | |||
4975f8167e | |||
6777459e62 | |||
253d0dbd5e | |||
e98bd70871 | |||
6a216be5cb | |||
0adb7831e7 | |||
30ca98b597 | |||
e80001e8c8 | |||
b5490f7712 | |||
dc5748624f | |||
91fb82e9b6 | |||
84c5a759d4 | |||
ec43aa2f18 | |||
12fa08d7a7 | |||
50dea4cb52 | |||
20b06db359 | |||
b6e512e65f | |||
7d71c353b2 | |||
2adf43274c | |||
3216a68d63 | |||
df518f822c | |||
abdf0b6719 | |||
2d2a75f23c | |||
fcb59c68d4 | |||
d47816e7b9 | |||
21297d98f2 | |||
cc7452374d | |||
851aa7aaaf | |||
376d238ad8 | |||
e0998e227f | |||
07b584b3b4 | |||
d35a89bb01 | |||
22a6fe7721 | |||
404329f9b5 | |||
3929e88d87 | |||
83a5b5b46f | |||
b97c906128 | |||
b8328b6071 | |||
9a528496a3 | |||
6a95c602b1 | |||
f0f6578b9c | |||
83c93eb9ef | |||
befe8ad24e | |||
c5249e6144 | |||
9be3297c27 | |||
b6344ef6f9 | |||
76b7e32125 | |||
801a3dd598 | |||
d1fdf1766a | |||
35073adc1f | |||
d76930c7f4 | |||
7d496f4ad0 | |||
53b5ce6e2c | |||
38ab5b090f | |||
fa58996f37 | |||
56f92ccab0 | |||
4e444b418e | |||
3d9a9299dc | |||
ae34c9e84b | |||
eba7bab15e | |||
ee6db85768 | |||
05ed110519 | |||
9690fd1fa8 | |||
4cee1be99c | |||
d39e1da183 | |||
8538a684e7 | |||
47d7513dd8 | |||
432fd57581 | |||
9c06e2612a | |||
1d6742f463 | |||
2e849827d1 | |||
1e2c9ecb41 | |||
14679586a8 | |||
11fb83a2a7 | |||
4d3f55622a | |||
eedf6f0aad | |||
13592fae1a | |||
4dd05d3efe | |||
2e3059a7c8 | |||
3b53b5ebaf | |||
a9f1000af8 | |||
a9960ded01 | |||
ed84a23f36 | |||
8301cafb37 | |||
c906c5d14a | |||
6e52680fa8 | |||
7f32c531d7 | |||
17a11b94b2 | |||
e61549e0cd | |||
b93c624efa | |||
d118443a94 | |||
064a55f587 | |||
de3b43647a | |||
0b054b58d4 | |||
7b5e2b4a12 | |||
b408fc7cd2 | |||
a8ed1ebf52 | |||
357ab9596e | |||
cba1cfbbef | |||
c402867d45 | |||
158591bdcf | |||
8857249b48 | |||
c71d7ea14f | |||
721ab8a0c7 | |||
d0d9c185f1 | |||
ade0912ba1 | |||
192520eafe | |||
710208f376 | |||
9fd69b2519 | |||
636c3e5c02 | |||
0dbc195770 | |||
d03f16cbd3 | |||
eac0d49880 | |||
0ad97b8aa5 | |||
8ce5ebe885 | |||
fa2a929796 | |||
7debd2cd97 | |||
ca59866d52 | |||
4832c67167 | |||
6ec7b78d96 | |||
14c1d17632 | |||
5bd98d4aa0 | |||
a7b427c5ff | |||
de36489444 | |||
de6fec5fd7 | |||
47b157c24a | |||
e358a72925 | |||
6201ed30ff | |||
ace53d211f | |||
812fc20c39 | |||
08ff52235e | |||
66624f4011 | |||
b58b7660ab | |||
ef1e42dd7b | |||
6f065918e9 | |||
0f7f52fbc2 | |||
721f826376 | |||
74e7c35885 | |||
d61bc5958e | |||
3461bb669d | |||
8151bc57b1 | |||
0d610c5393 | |||
7c358c2842 | |||
f5b8044bad | |||
92ffbb5ed8 | |||
0cc2d26e97 | |||
bb5487efb8 | |||
0139111d49 | |||
fe6991c703 | |||
17c9d9b447 | |||
318136f4eb | |||
5344cb9723 | |||
959c1a196e | |||
c5a06eedbc | |||
025ee49680 | |||
935c75bcab | |||
7b632fe441 | |||
1e4cca54b6 | |||
4b89c3e7a5 | |||
837ad5b68c | |||
3980625be6 | |||
2242a76fed | |||
0661b9ba5b | |||
788404f66a | |||
fa457ad476 | |||
b005332840 | |||
ab52a95e45 | |||
ed59402e48 | |||
5e0839531e | |||
1c7acf7bcf | |||
d143e85760 | |||
2612c274d3 | |||
764ad1b8db | |||
b1dcfbd017 | |||
e43bf2b93a | |||
ab75527df2 | |||
b5a661eec8 | |||
df655eb2d7 | |||
db55064bb2 | |||
b4c3c4c650 | |||
6dfabb692d | |||
09f747a68e | |||
6d6a07f830 | |||
4313e7e701 | |||
2b9f5eb627 | |||
d4c1155ac3 | |||
37e8158175 | |||
c6c025353a | |||
21946ff824 | |||
2bd1cceb24 | |||
82561268ea | |||
99ab2d2a81 | |||
f9ff184b89 | |||
0118c7c808 | |||
d17ee88ced | |||
84574367b3 | |||
afdbcf267b | |||
4dd254263f | |||
2f9b492f5b | |||
0d9d01c9f5 | |||
36c4c0c8d7 | |||
c6d6446606 | |||
fa6716345d | |||
2dfa482b24 | |||
4959e52559 | |||
95334715ab | |||
5511d1090f | |||
9d319fd279 | |||
d023fd07b0 | |||
324226f87d | |||
3120b593c6 | |||
311ade1281 | |||
d98e4772ac | |||
cf87c34bef | |||
5a643c383b | |||
8618708fd1 | |||
f09c50ec90 | |||
75c57f646d | |||
45f99ab48a | |||
26042b1e26 | |||
656acafed3 | |||
ec353ba90d | |||
084ef5a28c | |||
81a24249e6 | |||
6df9a38a65 | |||
4c52dcb8a0 | |||
7306ac0168 | |||
9c34d42a50 | |||
fdc6a4d94b | |||
cc475f26f4 | |||
f252ca75e9 | |||
05b608831c | |||
7adf25ef97 | |||
837069648f | |||
1286e2d03c | |||
c425811b45 | |||
4b3de4c656 | |||
d6b996b28e | |||
3081a20bd0 | |||
fda30b1ecd | |||
74aa1a9db1 | |||
1f9a429a62 | |||
dbf9482303 | |||
845e6d1528 | |||
2e4807312a | |||
4bbb4b5e1e | |||
3a7281df3c | |||
800f275e91 | |||
ae930f3993 | |||
417daa264f | |||
19b42c91c0 | |||
aa53b868fc | |||
ab0d08b7a3 | |||
de0b082810 | |||
913550295c | |||
bce0373b11 | |||
95768cdb05 | |||
af7073d9b6 | |||
d56c23be9a | |||
5867baea35 | |||
f6bd05bcf1 | |||
13056f87d3 | |||
a72bae9cd2 | |||
df416a6a17 | |||
672571a36c | |||
848ff35e85 | |||
f05b815c5d | |||
e1e2a2a249 | |||
817436b65c | |||
c9a5ad9c3a | |||
c480b615ce | |||
5bc0d1f762 | |||
881fdc58ec | |||
569431dc72 | |||
07e30ae4ad | |||
c74be07c33 | |||
887d871d26 | |||
4dd1a46efa | |||
eb301a67d4 | |||
d9bddffc42 | |||
a5898aaf3b | |||
3dc62a8857 | |||
7811929b5b | |||
a43bd2fd3b | |||
aac9acf068 | |||
65bb01892f | |||
5b35c47360 | |||
4bf78521ce | |||
2a5b3040e2 | |||
ac4651c241 | |||
2c4cd21c8f | |||
31a7e178a1 | |||
ed59972b03 | |||
6ae4314b79 | |||
5e07432ae9 | |||
8ced5b7199 | |||
41d8847592 | |||
eb96bfe8a4 | |||
3037cceab3 | |||
324ffdefba | |||
9a81d17d33 | |||
3e34cdc884 | |||
e213f6cb95 | |||
0ba9f0549e | |||
f83af28e42 | |||
a2856b2b77 | |||
924fee394a | |||
e349fb1a23 | |||
4f799a2bf0 | |||
5398765fd7 | |||
48edce72a9 | |||
267c7b85ea | |||
e23f66a697 | |||
9a0031c47b | |||
0d8e73b206 | |||
9486c03a89 | |||
c09512bf12 | |||
05c2de9450 | |||
6ae5cb28cf | |||
cf6c1add1d | |||
d0184a1598 | |||
79d6ab9915 | |||
047390873c | |||
4b36ca75cb | |||
f7c52b700e | |||
c81d98ad0f | |||
046c00d844 | |||
b14653cb9e | |||
c72b287c82 | |||
a10aa92634 | |||
8a2c09c6de | |||
401fc30617 | |||
6ca7247c02 | |||
1d5309decb | |||
ab0218050c | |||
6dcf7539bb | |||
51d52d3a07 | |||
dd95df8f02 | |||
3045f5211f | |||
0860e35d17 | |||
32c4f10626 | |||
3e90eafafb | |||
16fcb4ed79 | |||
9be48b3fc5 | |||
7830ec7ca2 | |||
0ebf9df207 | |||
40682405cc | |||
9fdd482811 | |||
7202ffba6e | |||
30dcc7477f | |||
9ce076eb0d | |||
2080d6e27b | |||
6826435046 | |||
69d937e0b1 | |||
edd92b724f | |||
41ecc822df | |||
0990d8fc4d | |||
ce2a42ca13 | |||
1da35e89f6 | |||
d818107953 | |||
b3f65c0b3c | |||
59c322dc3b | |||
096f9ad3a6 | |||
5c8965b3ab | |||
090f8f6070 | |||
5f4fc63645 | |||
a0b3b5af53 | |||
351dd97500 | |||
b511000441 | |||
3a059bb919 | |||
a548c026b1 | |||
99c99ee9e3 | |||
523131de79 | |||
17e731dfe3 | |||
9dfa300083 | |||
3ea74af76d | |||
3d7e16cfd9 | |||
db265309a5 | |||
8554b0eab2 | |||
f641e6e69d | |||
30c07eab6b | |||
eba83386c1 | |||
d3334f9dfa | |||
a87dca1ef4 | |||
2bab4341a3 | |||
01fb2fde47 | |||
e93a49134a | |||
0127714929 | |||
29ec34169c | |||
d60cb61e58 | |||
d4582e9e6e | |||
a84d29c49c | |||
e76a91a78d | |||
ea9861d180 | |||
e48c73d277 | |||
0f6caaec33 | |||
a5a1d33589 | |||
cca6dd9230 | |||
7d936c72a4 | |||
cac4bd11d2 | |||
70a3beeaa2 | |||
566cb55f36 | |||
a6dbdf664b | |||
bdf36a8dab | |||
760909f495 | |||
40c9f1f51d | |||
4349c595b8 | |||
da27fc7782 | |||
11e1436e2e | |||
107323d8e7 | |||
83557d4b3c | |||
b08e9b7982 | |||
415213878d | |||
53b23756a4 | |||
063d14d2ac | |||
3d99f0dd9c | |||
f3ce5ed279 | |||
b77036443f | |||
00603ce124 | |||
d3dd15eb63 | |||
9d408a62bf | |||
e4a7537952 | |||
4313166dbf | |||
a25364732b | |||
0adaf6c0a0 | |||
9410879b73 | |||
1dc326cc41 | |||
1605c5fbcc | |||
7562a882f4 | |||
366bc72759 | |||
2f7990b9cf | |||
45db4bb036 | |||
4a04226cc0 | |||
8142fd0701 | |||
7240c91db7 | |||
a9318a9ba0 | |||
1cba62af24 | |||
add05228bd | |||
4bca739b3d | |||
566a83ce3f | |||
ca19a488a8 | |||
08f44472f8 | |||
2d1be6186e | |||
c0dcf1633c | |||
d8447ef1a9 | |||
ca362ef78d | |||
a255d74abf | |||
fec2140896 | |||
3f9ec378a0 | |||
64cfd55065 | |||
f9cfe1da45 | |||
b27a14b1b4 | |||
4dbdb802b6 | |||
cbd74e7510 | |||
8e416cef25 | |||
ae9afab6c1 | |||
06c990e94d | |||
4d31078579 | |||
01c7712961 | |||
c18bf3e413 | |||
5c95bcc65d | |||
75f0780bd1 | |||
843d22d0d4 | |||
33a49a57e6 | |||
eaba64a64a | |||
679b828cf5 | |||
d231c533ae | |||
5a9e74cef7 | |||
49599dc3ba | |||
c1e5c8dc86 | |||
35d36f9eb3 | |||
2b8c199e56 | |||
654749de40 | |||
dde51c0cef | |||
729f7eb24a | |||
51e067b050 | |||
4dc2a96d41 | |||
1b40a6baa3 | |||
50fdc32ff8 | |||
e7fd0b3a05 | |||
20d6e17d4d | |||
262a1464c3 | |||
31c54c4a41 | |||
6cf05df5ee | |||
f90a13571c | |||
a6fe023519 | |||
e550b15094 | |||
0ebad77083 | |||
3100fae118 | |||
01202c5c2e | |||
ae52d9ef22 | |||
70a37fda57 | |||
da3f894ed4 | |||
2e362d57eb | |||
03fedfd0d5 | |||
039395f221 | |||
1381be16ad | |||
9af511e457 | |||
d18cefc519 | |||
07f52c38ef | |||
a46ff731d8 | |||
469585ddda | |||
3000e53cc0 | |||
db0722aca7 | |||
af0058d2aa | |||
aad1afb70e | |||
228a5c4552 | |||
dc43eb29e1 | |||
400cb218ba | |||
0fbe3cfb8f | |||
9a01e917c6 | |||
e01d68fce3 | |||
60f2f5ea19 | |||
2333beda5f | |||
8174f94172 | |||
6a6ea5009a | |||
24d0e7566f | |||
fe8c208e7c | |||
ba7a49e834 | |||
216323fcf4 | |||
fb18c93bd6 | |||
382dee1fd1 | |||
9399fb5371 | |||
92d8dfe963 | |||
3ae851ab1f | |||
fb1e3de3c7 | |||
6fbb24ae3d | |||
943776dd14 | |||
bb607927d0 | |||
ce95072845 | |||
36344732ac | |||
1f4e4d8d82 | |||
4ef10222e1 | |||
d7b91db204 | |||
5acf5949a6 | |||
991f9cda42 | |||
6b075256e8 | |||
3d740555c3 | |||
49b2fc5b33 | |||
f7235cf82c | |||
56dbddd472 | |||
eb16296873 | |||
1967299417 | |||
c7d8164c48 | |||
1864921d1d | |||
75bdb214c7 | |||
0b19adba75 | |||
2e84a421f3 | |||
fea77e97a0 | |||
e1b6cc2a86 | |||
0921573644 | |||
5eec05c0c4 | |||
526fc989c1 | |||
023b78d1c9 | |||
670410b539 | |||
76e379d7e1 | |||
cde57109e4 | |||
bc142c9ecd | |||
6c148f1791 | |||
534bb2dd84 | |||
d0f4476ba5 | |||
6287bcd00a | |||
fcbcb7d471 | |||
cb527919a2 | |||
83c34ea52f | |||
35c75115de | |||
7c75a61700 | |||
34ea49147c | |||
c1e8637a9f | |||
becbef4fac | |||
f22ecc454a | |||
bf3df097b8 | |||
7fc2ed28b1 | |||
30a133bad9 | |||
d8d44c579c | |||
80384e6ee1 | |||
0898f98355 | |||
e7dc41e271 | |||
0f0f475241 | |||
127ee68486 | |||
b204b02b05 | |||
893b6d985c | |||
44824fb5f9 | |||
dc21cbe59d | |||
e16d9f4742 | |||
f2b5843e6c | |||
16229caa8e | |||
0c0525e11b | |||
11517b0969 | |||
1ba3a139d9 | |||
80bcfabc48 | |||
4192f87d6b | |||
03c8a0fca5 | |||
a3d2c71ed6 | |||
8ba0b34853 | |||
424ec40fa5 | |||
210429a259 | |||
df806d5dfa | |||
b07046f6a2 | |||
4cdb8a7d2a | |||
2f83f2bd48 | |||
8a6cf3cfae | |||
514e40569e | |||
f30f98abd8 | |||
c611d26306 | |||
9ee38d0b70 | |||
5e45f37232 | |||
4f2df2d188 | |||
ae470e35c8 | |||
0f4b62cb97 | |||
c086098af1 | |||
4c7b4c7592 | |||
9e244f758c | |||
e7c0b9bd76 | |||
d7317e8252 | |||
36a187d3c5 | |||
a6ec401440 | |||
7a2048b2cb | |||
37082ad430 | |||
c571521e87 | |||
c438cd47b9 | |||
3ce6c3dc61 | |||
17f60d3c7f | |||
7d4d85284b | |||
9c091a9edf | |||
60ca5641ae | |||
c115a9aa3d | |||
6529240808 | |||
0d570b3fae | |||
0778078350 | |||
888dc05cde | |||
687da5b64a | |||
5b00d54c76 | |||
38b4a7856e | |||
4f899bd83d | |||
7f80f7c46b | |||
d6cb0e48cc | |||
899125cc41 | |||
f38e8688b3 | |||
f3a0ab24c1 | |||
730424ebc4 | |||
84737eb271 | |||
e807bcdf90 | |||
73d947c4a6 | |||
f0dbd87ba9 | |||
88d415d3f9 | |||
3fc93e2c57 | |||
69380e3527 | |||
ec0b08e4d0 | |||
6aa048e3ad | |||
bc711414a8 | |||
07b467e4bc | |||
75445185c4 | |||
7772b6901a | |||
fad36d9c08 | |||
061e012cff | |||
02fdafc111 | |||
a03164f3bc | |||
8ab445bb31 | |||
1429a44f0e | |||
0b5c5b646f | |||
5ddf0bd164 | |||
ed60bab294 | |||
65348e1cb9 | |||
22ca01bbde | |||
c772347c09 | |||
afc18619db | |||
4f88939c77 | |||
5dd3f46cee | |||
11138a1d97 | |||
aea49bf739 | |||
8f877a2cee | |||
dc4344043e | |||
fe635db3ab | |||
7e53eb658c | |||
4836abb5bd | |||
67746981e7 | |||
8149f97388 | |||
e5a47ac964 | |||
40598c28af | |||
044e3746ca | |||
969eb5632f | |||
03800a45e0 | |||
9ee17ec5f1 | |||
72d991afaa | |||
a858d4d1ba | |||
590472cab2 | |||
d988c80874 | |||
8e705f0785 | |||
477a4fc0e3 | |||
77246aeab1 | |||
728e7c79fd | |||
a10dfd0386 | |||
d81be64711 | |||
e4550f7eb4 | |||
27fb8ccd92 | |||
811d3b916e | |||
a573e61d36 | |||
5a7675709b | |||
0730400bc1 | |||
e65ee76076 | |||
de02435015 | |||
48b233304f | |||
1c2e353fc5 | |||
356375677d | |||
2af1b5c064 | |||
e42ddbd652 | |||
d1f341678c | |||
696bb883d0 | |||
55da2988b3 | |||
d16783d0d1 | |||
d8cfd35a94 | |||
114363e22b | |||
23e7a6b8b0 | |||
20ad0d7f8c | |||
6ef72f03ff | |||
ac91a15aa9 | |||
872c09c220 | |||
8efde22580 | |||
9c693bd76f | |||
7a4002a17a | |||
fbaa11f08d | |||
3faaed9819 | |||
f5be941a4a | |||
eabe4cf58c | |||
593e9748b9 | |||
f8d260e0a7 | |||
f8bc50871a | |||
03a7e2e8bf | |||
3d2695c4ec | |||
4871f7fed7 | |||
0f3a3da5ed | |||
ec6922b885 | |||
c9a05e0290 | |||
856724b9a5 | |||
2b3e2b1de7 | |||
bcaa624b9b | |||
b5da4e2fa0 | |||
7917ac8ebc | |||
700bfc16bd | |||
8aead029a8 | |||
38aafd3577 | |||
a9da65c2cd | |||
a290fdd28c | |||
0324deec60 | |||
d7b3b5d87f | |||
28338612fa | |||
fe23cc558d | |||
394cb72e46 | |||
57c85a64c7 | |||
a3f357732c | |||
ca99b87319 | |||
3507f91090 | |||
aa3bc864ee | |||
db769dd995 | |||
a0f2097b1b | |||
6298df14e7 | |||
2d0a76c5a4 | |||
b7a7a7d31f | |||
841811e3bc | |||
0679fb2b20 | |||
230f7b478a | |||
c8f8f329e3 | |||
9c4d702434 | |||
ab7d74d2fa | |||
c9ddf4d15f | |||
7af620f66e | |||
24b631500a | |||
c27d57831c | |||
30eb729973 | |||
7f24241372 | |||
0ddcc98a57 | |||
9ca8cf810b | |||
3c2157111b | |||
d26e646a94 | |||
1fda12640f | |||
05316ae25b | |||
079402cb2f | |||
995bdc77b8 | |||
c52b72f500 | |||
a4e496abc1 | |||
6cb981655e | |||
2916a33fa2 | |||
ca555686ec | |||
03256f6bba | |||
257b14ee09 | |||
4e35580bd6 | |||
512b160fc0 | |||
250afd2355 | |||
7fa3e2d2f9 | |||
91f28fa38f | |||
a0dc82e1f9 | |||
fc3c2925e5 | |||
6b3511e15d | |||
59183aceec | |||
ec44a84915 | |||
1010837cfd | |||
aec7e6d32e | |||
bb0f7cd1cd | |||
5dd92b1d3f | |||
7548f7cdbb | |||
44da3d26f3 | |||
7c01c48297 | |||
7826870d99 | |||
bdb6649722 | |||
31ee73c5eb | |||
3b708e8d44 | |||
0fd706f392 | |||
8907dabd4c | |||
1496d6ec51 | |||
d1a45ed9ac | |||
f73d28ac10 | |||
1b7af75d4e | |||
ed0d78bf73 | |||
046e2acae1 | |||
b6efa71efc | |||
3bb835b5e1 | |||
fbeecda38c | |||
942904186a | |||
737a81570a | |||
3691aeb8e1 | |||
32d8f4d24b | |||
30ccd35dd3 | |||
11265c4034 | |||
8acff43028 | |||
660aa4f4ab | |||
1384c2f1bc | |||
459b9428d4 | |||
a82f16958b | |||
b9f436812b | |||
7c0ec9faaf | |||
88e3831bc6 | |||
2a597fcad7 | |||
6158f49400 | |||
9dd819e193 | |||
e706fae648 | |||
118a4862ab | |||
5e2f31e3bf | |||
f78b31b1bc | |||
8d698cb997 | |||
8945aac319 | |||
f2a960136e | |||
7a1170f1dd | |||
24cce08580 | |||
b425b43d3e | |||
353fe88226 | |||
1a3086230e | |||
0e57487774 | |||
3024465086 | |||
c95b43253a | |||
aedf7856e5 | |||
d83e034d5e | |||
05cafce1e8 | |||
b9676b51cb | |||
5698473891 | |||
de1d1ad961 | |||
bd82480fa3 | |||
fce8b96d3b | |||
37b47e7f05 | |||
a6f94959fe | |||
45a2c9f7ef | |||
c49ac6880d | |||
e0258d9e7b | |||
e3ff6f183b | |||
e6ec7393c6 | |||
f733b53c25 | |||
204a68b17d | |||
1379dde1a7 | |||
79eee62d42 | |||
7c1f18b6cd | |||
b59371988d | |||
30dbadb2ab | |||
a342de0207 | |||
6e6d236819 | |||
0e41483564 | |||
1023f5f7cc | |||
4bc7bca60d | |||
de7dbd27c0 | |||
14118f142c | |||
9b99be4c1d | |||
91c4b5865c | |||
1b4c14af71 | |||
7b85e50604 | |||
d64b2d8fbe | |||
f1a7aed1b6 | |||
75f758e792 | |||
e25e1bfe10 | |||
09deaefab0 | |||
f80ecbde71 | |||
5e1e198a1f | |||
bdbb741716 | |||
2f0e8a8a4a | |||
4f8424c544 | |||
ce3355d6aa | |||
fb67ef2df0 | |||
380e9aaf13 | |||
255e90d125 | |||
504f7f3799 | |||
9970e505de | |||
0ccacd5eca | |||
50e4683492 | |||
bc14bdc010 | |||
14b0dabfdf | |||
e140acd2a4 | |||
facfed07fe | |||
41a3309cbe | |||
bbda097aa8 | |||
4df9a22dd6 | |||
31a1c4b2b2 | |||
c2c33b7df1 | |||
6a2c2152e2 | |||
37f2755611 | |||
aa70f2849b | |||
e7a2dfa57f | |||
b43f9fc4ee | |||
51b6a2fd2a | |||
5fffb82b16 | |||
e051dbc2c7 | |||
c2fba39cc7 | |||
1050b13bbb | |||
92d3d9cd33 | |||
d8dec3e56a | |||
130f9678b2 | |||
29d13cb06d | |||
620f521e0c | |||
a36fb55b05 | |||
23f9bcb38b | |||
e73e820237 | |||
7e4735ae0f | |||
66ffcbbee6 | |||
4754743c84 | |||
09c1dfd92b | |||
7fc46f3672 | |||
df93fee034 | |||
fc2cf742c8 | |||
9bec441e94 | |||
1caab1da85 | |||
d612d7ab53 | |||
3d3994bbad | |||
d643ae0299 | |||
0a099434a3 | |||
16905a8999 | |||
282c4cca82 | |||
f36b7ce016 | |||
9fb5cac5d4 | |||
9f5f213cd3 | |||
932ee11c91 | |||
5d3b59b94e | |||
744c6e4725 | |||
c59745d346 | |||
9d1dd09a07 | |||
2eb317c6b6 | |||
0ad08c609d | |||
85f6f8b31d | |||
9799309db9 | |||
fa205f483a | |||
2df4286256 | |||
b89f689ea3 | |||
f58b21746e | |||
6971f9dcf1 | |||
3454a47f67 | |||
5922fd39c5 | |||
cdbddbae3b | |||
af4a26c1d0 | |||
d3f42e47a7 | |||
8821e471b5 | |||
d34aed0b14 | |||
b7391652ca | |||
074a14f056 | |||
b1db708af1 | |||
b2a66709b0 | |||
e3e43913ab | |||
c7fed0a42a | |||
c6c5e0734a | |||
73cbc58a50 | |||
8431395326 | |||
dd21c07d4a | |||
ce9591428e | |||
a801a5d8b6 | |||
04e8458ce2 | |||
4b4fa84879 | |||
1b3df8c4de | |||
7ce223771d | |||
ccf71ed445 | |||
aa7c031e8a | |||
8465bc1bc9 | |||
f2f3ed71d4 | |||
ab7ba35639 | |||
1cc09cbe5f | |||
fe7e398eb4 | |||
6ab3133b33 | |||
ef77c37a7e | |||
1dd165a9c9 | |||
3c74540615 | |||
ad249c4651 | |||
071a4d6f37 | |||
5f2fb19d71 | |||
ce61657f7a | |||
dc54e5bdce | |||
f7b8e000c5 | |||
73abf131a6 | |||
5741af2aba | |||
159af669f6 | |||
a517255653 | |||
573154633b | |||
baa8afd9eb | |||
9e718da70e | |||
4df442f169 | |||
1dc93c7a39 | |||
3d124986d3 | |||
a589d98cd4 | |||
ed9f18e22c | |||
14fb115fc8 | |||
c35a731a60 | |||
4f3d2bd120 | |||
69c8fc3236 | |||
840ff5c363 | |||
8386cd5cf7 | |||
666c2f8771 | |||
b342fa9661 | |||
63bf84fdd5 | |||
070e51fcab | |||
50fd64150e | |||
63c5de2612 | |||
c576d582e2 | |||
026a4b6c76 | |||
7bc95b68c8 | |||
0332cc8cb3 | |||
ce192f4ad7 | |||
cbdb715918 | |||
5537102fd3 | |||
1ea294f15c | |||
e7bf2ee58b | |||
a931aa59a3 | |||
4c8da67bb1 | |||
a0178e15b3 | |||
43a1c3901f | |||
a4c6f28a70 | |||
f8bca93170 | |||
f07d05a490 | |||
b3a988bc0b | |||
e0f22d29e8 | |||
07ee97b862 | |||
19b05659b5 | |||
7e5c7ca1b7 | |||
1156c159f9 | |||
5c6c2303ba | |||
a0a58bcfa8 | |||
8a28b265a3 | |||
86dc08130b | |||
5cd8a732c7 | |||
fafbbf68a4 | |||
0cbb553564 | |||
f4512bb291 | |||
99205b4d03 | |||
d48e6554d5 | |||
d0c4e95de3 | |||
0b3a35c4b6 | |||
ded6a41f86 | |||
f4063e63d3 | |||
23ba912db0 | |||
b99d9db8f9 | |||
b7047dafb2 | |||
368967fbcf | |||
a9d0fc9978 | |||
b6f3d2ec02 | |||
78e917a6fb | |||
96b45385e8 | |||
db47888a75 | |||
51443741b8 | |||
3e7f14af2c | |||
733439da07 | |||
6bff97d6fa | |||
efba81cb66 | |||
b2cc5dcf4b | |||
fab86ddf35 | |||
f3a90ce02d | |||
4886616c48 | |||
dcd8121009 | |||
59adaf6225 | |||
0055cd9b2e | |||
fe89d487f6 | |||
01368ac496 | |||
495064985e | |||
200f8fd245 | |||
64bf4356b4 | |||
8d4d409cd6 | |||
dd4937178f | |||
e12387a377 | |||
5d3fb9091a | |||
b044bc1791 | |||
409ec61be2 | |||
e2ae2715a3 | |||
52458ae273 | |||
79d112ca7b | |||
9b1a9cc7c8 | |||
42f9abdfe3 | |||
66d311258a | |||
0a1197055c | |||
649cbf07e3 | |||
5089ac5ad1 | |||
d99e3f7974 | |||
3d5133209b | |||
b5d1912c94 | |||
a8fba8f3fb | |||
9d9fc1683a | |||
8ee4364065 | |||
152aa7de09 | |||
85c90cbee1 | |||
7302927e4c | |||
df3d00ef94 | |||
bb47835256 | |||
037512ca5c | |||
a13713adaf | |||
ad073252e7 | |||
d24a7a5c5e | |||
192fd223b4 | |||
a671dd8e00 | |||
8b764a8fd3 | |||
aa576e68e3 | |||
ad5508a14d | |||
4fafc8aa67 | |||
0aab3d0f12 | |||
a5d88bdfcc | |||
5173957368 | |||
4b3e3d900d | |||
9ea51b174a | |||
80e265e547 | |||
c3e6e63023 | |||
9b5a262d63 | |||
1309f1480c | |||
12ba5b8096 | |||
156c5f4792 | |||
1da4b3d94a | |||
18aca98e41 | |||
a88afb0956 | |||
bfa1f57930 | |||
a5350eb3cc | |||
3ed4d792b3 | |||
fb0c9405cf | |||
a17a9044ad | |||
73af7f5481 | |||
57ead7f0c0 | |||
bf490c910a | |||
40f806efa8 | |||
226ba8b06e | |||
b11aa4833d | |||
8d9cd0e30b | |||
9532928998 | |||
420f7549a2 | |||
ed64b9bfed | |||
5d5ebfdef6 | |||
567c02bf5d | |||
60f7c73c8a | |||
ac4c5003f1 | |||
d5e76e662f | |||
23d5f85d17 | |||
f75adc1e22 | |||
15a1436c8b | |||
813edec808 | |||
21e3299b7a | |||
f7193966fb | |||
2d9853f1f4 | |||
ced79a187d | |||
7832524963 | |||
58c7f3ba15 | |||
90ec8f0575 | |||
64ced3b3f6 | |||
493526c478 | |||
b86617e3af | |||
f3db6d84fb | |||
f9b9ecf754 | |||
af43a92a2f | |||
4dbdc642f9 | |||
8f2c87ce94 | |||
5149040496 | |||
5b1078e0db | |||
ae31813239 | |||
f6b3cde286 | |||
0f05f9c32c | |||
89170af721 | |||
5fddae589b | |||
19c16af5fa | |||
019f8f69f4 | |||
ad8d1f77df | |||
e82a8a7f3d | |||
ad07aeb041 | |||
451ab7e84c | |||
083390da83 | |||
dc6d48580b | |||
27d69e2ac3 | |||
91274a4df8 | |||
6eafcdfafd | |||
5e44744ff7 | |||
37b293fe74 | |||
280f0be690 | |||
183bc8321c | |||
a973e4d1ef | |||
eed1066967 | |||
2859c94fea | |||
dbcce2ee5d | |||
25071c238c | |||
9995ffb5f3 | |||
c867c35e45 | |||
6f60e88ca6 | |||
11730dcbe4 | |||
e155bac445 | |||
15a4682665 | |||
08675b39f7 | |||
2c7d5adb80 | |||
51c7faee3c | |||
852e129f9c | |||
6eb2d800fa | |||
0a2c70595d | |||
f13e16af15 | |||
f364958c13 | |||
e65150647d | |||
3c435b9593 | |||
871b96a450 | |||
48a3254ad2 | |||
2c0bdd6377 | |||
8cedeb349d | |||
e241ef25e5 | |||
5e553dd958 | |||
19ee87d2cd | |||
72b3598687 | |||
33b120f6cd | |||
0bfb9d00c8 | |||
b1a2d36c2d | |||
517ddca22d | |||
41c7b08418 | |||
c7c1b5a570 | |||
87b6dfb1a9 | |||
46c56f3706 | |||
32bab80508 | |||
b6f1194c93 | |||
206f9b97bb | |||
13721f160e | |||
102e5623f7 | |||
9a975321db | |||
6743ec14f1 | |||
daec5e5426 | |||
a2b55c0df7 | |||
01320ac735 | |||
84bddee2ce | |||
e636dd3649 | |||
5f6b798e35 | |||
9137f3793e | |||
73e92a688f | |||
7a9f219037 | |||
a4728190c0 | |||
04d67a24b6 | |||
55049ba9d2 | |||
e0b33a4feb | |||
fb5c0a3db7 | |||
8154a5709b | |||
3a6780bd50 | |||
b7a76d4212 | |||
ba7cae683a | |||
243556656e | |||
6662dc66d5 | |||
107112d1c4 | |||
4eae540086 | |||
21108650f7 | |||
c5d343750c | |||
09b76dcd93 | |||
b87bc033f5 | |||
fb95d76e34 | |||
4e765a7948 | |||
cf2408013e | |||
d8543d1358 | |||
d8b79d8b5c | |||
c2bcf89f9a | |||
5cb24f992c | |||
21394b7d45 | |||
6d08082693 | |||
768fb2583a | |||
6e07b2354f | |||
00597879bc | |||
0cd0d6aadf | |||
9d201f82f1 | |||
d6c535c45c | |||
babdb5b718 | |||
0ea8d038be | |||
c804a9971e | |||
4d7f6e4236 | |||
5474d1786f | |||
7f36473544 | |||
9d19698bf3 | |||
582b2d936f | |||
6036ccdc1c | |||
5eeef41d8c | |||
bacf266f0d | |||
ba5c54043b | |||
e33c858829 | |||
e47e54de3f | |||
54f9e9bfe9 | |||
e1875c872c | |||
27b8e173e8 | |||
47e3884994 | |||
e483071894 | |||
af090cb289 | |||
9bbb25f16c | |||
3007f00c9b | |||
352dcfbe30 | |||
60b181a545 | |||
600482e2d7 | |||
39ccbbd72e | |||
6e69cbcdaf | |||
bf6c222a3b | |||
6afcf7570a | |||
c3126f7b4d | |||
cb3b542363 | |||
1a5e15608c | |||
64a751ad79 | |||
57efe31959 | |||
39350d554b | |||
8f4e03550c | |||
d03823fb20 | |||
00ec2b9d6f | |||
70e4bc4582 | |||
5e56a437ef | |||
22ffd25619 | |||
127949c56b | |||
cdfef16a0e | |||
1595f1ed05 | |||
1cae39b105 | |||
8189b38e6e | |||
c240d6932a | |||
c4548d9396 | |||
aea70e3dd4 | |||
3b01e65e11 | |||
341c810bbb | |||
85fd2dfaaa | |||
bf4bc38c6c | |||
aa8b50280b | |||
62553dc0fa | |||
25639cc3f8 | |||
7982a9ae25 | |||
aa01fd058e | |||
ef7e1575bd | |||
fb075a0013 | |||
d1738baf44 | |||
7eb29fa91b | |||
34c00fb77f | |||
7965318d9f | |||
e73a514e29 | |||
35ff4f439e | |||
12e0194c7f | |||
d1ac90e16d | |||
7dc7f70582 | |||
84d606408a | |||
d103693811 | |||
0dbce101ac | |||
cb81e2aacd | |||
6cd0b530c5 | |||
35571eb14d | |||
8e6102ad9a | |||
80bc80dc2c | |||
a483bd0800 | |||
47a39569bc | |||
f00e1a92d8 | |||
a289945e8e | |||
b750c0d7c3 | |||
a244a6873a | |||
ceff4f06c1 | |||
0307114c8e | |||
92030a3917 | |||
73ace121a4 | |||
44d5809e46 | |||
5c4e6f7e96 | |||
8c032579b8 | |||
b53935bfd4 | |||
d4db027cfa | |||
27963decc9 | |||
25f488c6e1 | |||
07bd580050 | |||
fb32a38d96 | |||
ac0961d7d4 | |||
6b943f88d1 | |||
4bbf683d15 | |||
d0e50584ea | |||
b57649828d | |||
1f44a283b3 | |||
9947c3bcfb | |||
8faf6b9f52 | |||
e45cbbf1ca | |||
1a5b6ef260 | |||
096556d8c9 | |||
97919c7e87 | |||
0aa7968503 | |||
bd1bc78953 | |||
6ce6dc3ff6 | |||
e6346775e7 | |||
d03eed3859 | |||
afb88616d8 | |||
543f13f9a3 | |||
af5c68051a | |||
5b7cd11de8 | |||
d3c3496e55 | |||
c08c8b2789 | |||
069315e434 | |||
7e4ad83a1c | |||
400f9fd680 | |||
38951f5581 | |||
b5329ee93d | |||
c568bca69e | |||
7b2be12587 | |||
099fde2652 | |||
83e5410945 | |||
b330c34b29 | |||
e3184622e8 | |||
28f822afe0 | |||
a2af811ad2 | |||
cde8c2d3bd | |||
79cc84b611 | |||
f1de0be679 | |||
854e3d3576 | |||
dbac2655f5 | |||
0f656dbf2f | |||
3fbb3f6773 | |||
8820814002 | |||
b40fb3a422 | |||
aa59575df3 | |||
accfec9007 | |||
16410d90b8 | |||
27c6113287 | |||
f4a6910ab4 | |||
bad89160cc | |||
5782966d63 | |||
ba2c966329 | |||
f8dee7e25f | |||
a8151176d7 | |||
9ee0b7fe2e | |||
fb6a7e04f5 | |||
bfdf487d52 | |||
b7aac1501d | |||
273525e6f9 | |||
064a4938c1 | |||
182236e742 | |||
75cb052cca | |||
d4a378827f | |||
592d5e8c40 | |||
733150111d | |||
cbe91251ac | |||
1283c6483d | |||
f24d3d69af | |||
7984327d81 | |||
ef90832aea | |||
9571b8addc | |||
9601f304a5 | |||
ff43dac2a7 | |||
0a43305455 | |||
54d8224de2 | |||
c9e34457cd | |||
47c8eb304f | |||
2dd39fa218 | |||
cb618efb98 | |||
e7ca8090fd | |||
7861c57317 | |||
f701b8dc29 | |||
bd10a850fa | |||
0f96688a54 | |||
8eeca90d55 | |||
367e7f7065 | |||
ee19eaae62 | |||
8eb3a3536b | |||
cfd50231e1 | |||
1c8ab9e1b4 | |||
6094cd8578 | |||
353c49a40b | |||
277140f218 | |||
ca9413ccf4 | |||
c9a0d090cb | |||
1cd783d3a3 | |||
1ead764a02 | |||
45f7b35954 | |||
6a41540749 | |||
5b47da67f6 | |||
292f68ff97 | |||
3b554d881a | |||
40ebf468d3 | |||
4bc6e51862 | |||
427861cf13 | |||
da3e7a2eb8 | |||
2979f04c82 | |||
1949d8a50c | |||
ee66c799e0 | |||
7c50b8bf94 | |||
141ff74ece | |||
321e5f1ed6 | |||
6d131d9d8e | |||
7e69b8eb31 | |||
4e0b33e6a4 | |||
54f7e6fcb8 | |||
529169c4da | |||
a2c8c99215 | |||
e8bf3fd009 | |||
465676e9ea | |||
af53b57047 | |||
54b5f75905 | |||
4348333497 | |||
cc31110bcf | |||
f7c04bf7a6 | |||
029509ebad | |||
65102bb64d | |||
b96b55c5ce | |||
1f5aba010e | |||
f0b3bea4e3 | |||
84fae2d9e0 | |||
0b96fa112d | |||
c64bcd23d3 | |||
efd9a22bb5 | |||
159c3edfe3 | |||
f74fa8657b | |||
648b142a4b | |||
426f92595e | |||
82a8d9b644 | |||
ff9430b8a2 | |||
2e69ffcb5e | |||
0ea38db7ef | |||
a69d4c279e | |||
2706149399 | |||
3d0cdc1cb6 | |||
ac605e9352 | |||
5432297691 | |||
e37be0f954 | |||
a99209b674 | |||
cb02b5ba18 | |||
69f14edd80 | |||
14714b950d | |||
13654cb8c0 | |||
00276228cf | |||
8583bb8d7b | |||
d48951fe00 | |||
99bdcfa0a5 | |||
e64e1a92e6 | |||
e278e639a3 | |||
c4bad5c454 | |||
da41a74efc | |||
0dc970562a | |||
2d8401473d | |||
9c91f57b19 | |||
f14afcd129 | |||
5c1a3d82d7 | |||
e02a917569 | |||
347fa0fda1 | |||
6510d4cb02 | |||
91e4ccf6f8 | |||
36249874bc | |||
d2b5d6cce9 | |||
b2922741c9 | |||
300f3e27db | |||
d7330b80a9 | |||
acdd7667b7 | |||
8114fa3f5d | |||
4bc5508f38 | |||
e503c6092e | |||
6a8985d8dd | |||
bee67fd883 | |||
a1d75d40aa | |||
29484867ca | |||
7fa983b971 | |||
617a8b2814 | |||
b924d323d4 | |||
a2efda41d3 | |||
642c114501 | |||
02dd3e457d | |||
ea7b28c9d5 | |||
472ab4a9ce | |||
fca84e3edf | |||
b70235ff92 | |||
6eff591df7 | |||
d0b2bf736e | |||
e5c11ea214 | |||
6b6443406d | |||
3452d7852a | |||
f1fa10badd | |||
1267621424 | |||
8a0ec95fe1 | |||
ba30a63407 | |||
c56a2adbcb | |||
2de96d4dc9 | |||
a486f20892 | |||
49535deb2e | |||
7cbf62cf12 | |||
3b0ace3410 | |||
5a9c8e1d87 | |||
daaa65dc0a | |||
ab4e371524 | |||
927fd304b0 | |||
5af84b8e90 | |||
d425dac499 | |||
d056459e76 | |||
3169485f33 | |||
d9b9f80a93 | |||
d429505b71 | |||
72ee708917 | |||
93bbfac29a | |||
040d7a6563 | |||
e8dd930a50 | |||
31c049ebfe | |||
d343a37fb2 | |||
7097175c6f | |||
8e57c49043 | |||
9f036ceefd | |||
ff3ca8b36b | |||
87a7b70a27 | |||
9c71c966ca | |||
6dc99e676e | |||
80ecb82cc2 | |||
7fc9509d4d | |||
3bf5e11f94 | |||
eef9af2266 | |||
fabdf5fe30 | |||
1cc27e524b | |||
8316a002da | |||
888c637f71 | |||
48b7d2587e | |||
923c889de8 | |||
8ae575d67a | |||
b51407486a | |||
a689b34ed1 | |||
aa98e60243 | |||
c3bf767024 | |||
b641f1a230 | |||
9499685dda | |||
80d23cbbbf | |||
efa684c5e8 | |||
2edf64985d | |||
5fe7807462 | |||
e96b9005ca | |||
8c29e735e7 | |||
497e073a8c | |||
d4ce54a3c2 | |||
de37a81902 | |||
d6b8cb718a | |||
ed435d2b72 | |||
2b1f8533b0 | |||
0a21a69a9f | |||
5ebc6b698c | |||
cbc48e31e1 | |||
577dd9048f | |||
ae409dd0ec | |||
cde855e1dc | |||
adcd4368e7 | |||
8bcdb205ed | |||
2cf8b2a453 | |||
6c156380f9 | |||
369d0ee502 | |||
4971a212e9 | |||
2111a81d18 | |||
6799b3d7da | |||
a3463274ee | |||
c10e773401 | |||
f7af259576 | |||
87c6a54634 | |||
d03521bf12 | |||
3eb1919c81 | |||
e53d6dbd5c | |||
01d2db8e96 | |||
b18c2aea05 | |||
a6e3c272e2 | |||
4000f98ba4 | |||
d06fd404ae | |||
c6f0e19e2f | |||
462af9989a | |||
eedea2fdcd | |||
9c3d946de0 | |||
ace3102601 | |||
48946100e9 | |||
0067e46192 | |||
921711a679 | |||
32dfb765dd | |||
8482f12909 | |||
306a56124c | |||
1f815d7562 | |||
f25d35fad8 | |||
f74c57449e | |||
a697bd935a | |||
ec294227bd | |||
f67758eaf3 | |||
f7ed65d749 | |||
7ffeb3964b | |||
025d4df774 | |||
45086a4b6e | |||
2db0023653 | |||
bfc21220a7 | |||
507491fbec | |||
c890ef6917 | |||
6756fb4fe7 | |||
6c089c0a78 | |||
b2ab3f987c | |||
c99b2edf98 | |||
e052610184 | |||
13f1725105 | |||
f2367932e1 | |||
7e94ec986e | |||
7bda3e6994 | |||
3a18606385 | |||
e25a94e815 | |||
c13f662e2d | |||
97ee085f30 | |||
1364fd5c45 | |||
cc3186a683 | |||
0c93c4754d | |||
7b4cfbeeaa | |||
8cebb53147 | |||
3e18f2f09c | |||
add09e52ef | |||
5429a509c6 | |||
3555fa36aa | |||
ee31519552 | |||
06b41aee58 | |||
cd19d50e1d | |||
a5e4eea5ca | |||
c6a6270e16 | |||
18d9d2602a | |||
f7ec9f2073 | |||
4c5f66185d | |||
105c1893cb | |||
061cee207f | |||
af4a925b54 | |||
a6f3e87921 | |||
9764d9109f | |||
46dfa57ee0 | |||
2c861c65d4 | |||
a59bac4b40 | |||
cf214bf367 | |||
75724797f7 | |||
d04aeb55ad | |||
47bd6dc6b8 | |||
5e0f525932 | |||
1f66daf2f3 | |||
ded9cb0358 | |||
04f201933b | |||
f5ec1cb3a4 | |||
6c23e3f534 | |||
e99d54d1f6 | |||
7f436061b8 | |||
3c71200eb4 | |||
f124cf8318 | |||
9d2b944063 | |||
8e1ec5903b | |||
5cf763d51f | |||
3546859fe5 | |||
6530e45178 | |||
07f0036b2b | |||
5237f55a71 | |||
a108e5067d | |||
a4a24b1a1a | |||
ffe0eb1544 | |||
288e8a65f3 | |||
0ebfbca93e | |||
f22f57495e | |||
8786a9d21d | |||
f06a97d30b | |||
2329c47faf | |||
2967261acb | |||
64ff1ecbb6 | |||
8707f88c07 | |||
36846618ec | |||
0cb2f19e29 | |||
125a50ae87 | |||
9d37ea23f8 | |||
31617ae340 | |||
950614fb81 | |||
14bbd7b7ae | |||
257cd34101 | |||
ab6ec3a9b7 | |||
39814a89b6 | |||
24fbbf8aa8 | |||
338ceffa6d | |||
371e104b00 | |||
d5aba8eaf1 | |||
1d2b3a4ed8 | |||
f904945d40 | |||
027b2e1b88 | |||
d79eb5e1a6 | |||
f6651b03b5 | |||
5f880a179c | |||
ea03fd22db | |||
e252c9ac05 | |||
a212fb35c1 | |||
e561e4de0b | |||
1c3d5cd851 | |||
e59fbac761 | |||
332f2b0678 | |||
745ea5fb05 | |||
fa16ca4eec | |||
d7757b8b03 | |||
98aefad249 | |||
a19ba40672 | |||
3983cb001f | |||
c17222dbe4 | |||
abd8c69395 | |||
a7fde73df4 | |||
78b464b404 | |||
aa21115e26 | |||
a39f845835 | |||
3fdd8d91e2 | |||
c13bccc7ae | |||
b4f7d6bf25 | |||
fa0c2f7138 | |||
453cc2a951 | |||
bd56795c62 | |||
2c54b7f289 | |||
cd5f847b55 | |||
a25544baea | |||
39b6c5d6f4 | |||
d1c9db874f | |||
f954542dda | |||
9fec7d236c | |||
67656accf8 | |||
64952a536c | |||
65e0d5f511 | |||
903acff924 | |||
5a06946469 | |||
baef31b2c7 | |||
b9a12d1562 | |||
3f26d03166 | |||
1fed3ad532 | |||
929b245f5f | |||
0da6354825 | |||
716a28891d | |||
93a2e91694 | |||
4913dc1aad | |||
087df18fea | |||
058ce6fe82 | |||
087c10d52d | |||
18292e447c | |||
6c1dda47c0 | |||
ad1fc8f3d8 | |||
bca98269bb | |||
1bebaf933d | |||
166eb996a9 | |||
10fae34754 | |||
aa4d97e8df | |||
dbbb9d7877 | |||
82fda5cb03 | |||
3ff213b3e8 | |||
65587536ab | |||
ad31be8344 | |||
25815c81bf | |||
852a22f86d | |||
69c7f22053 | |||
75a964167a | |||
c5768c81e1 | |||
4eb2b818e7 | |||
f742aad810 | |||
e22b171b7b | |||
d061eb2c64 | |||
69aa115178 | |||
e175b87384 | |||
f216ee739a | |||
16d6644573 | |||
38afc6e6f8 | |||
8f1d214b12 | |||
51fb1a43de | |||
a86b6bfbd6 | |||
1176ddcc85 | |||
fa080e380c | |||
57c3acd9d8 | |||
302cf5b10b | |||
e2a9e81dbc | |||
b1cf7391ce | |||
9bc7521de0 | |||
a68ebd2b76 | |||
47f7c938ae | |||
842e7e559e | |||
bd5a6e6fb3 | |||
67cca3bc00 | |||
90b1609d4e | |||
abbfae2fc0 | |||
b52b854270 | |||
58b759f652 | |||
74ca756a53 | |||
a62ee7850b | |||
d3a90ccc0d | |||
46b13ee664 | |||
cfa6dc7836 | |||
f969bfa7be | |||
3576214920 | |||
f964fe3750 | |||
e86a883d0a | |||
82d764000a | |||
749c72e6a6 | |||
c3129a40f1 | |||
d04aa89812 | |||
d5f854d376 | |||
63dcb8cfe1 | |||
6c57fa078b | |||
c3cc75feff | |||
d2e6011089 | |||
5a18144366 | |||
8a0a22bfb0 | |||
950b226374 | |||
74e64a4387 | |||
59e4c1cf79 | |||
9b89ede9c4 | |||
bf205de3a1 | |||
045ad78bb9 | |||
c0350e5be7 | |||
ea7006eec4 | |||
2b3e38f77e | |||
d04fe5d582 | |||
17ab4caa5e | |||
976bc727dd | |||
484e53cc08 | |||
b09b80933d | |||
8165086d02 | |||
82f14b087a | |||
93b3419737 | |||
19290fe467 | |||
d2f679030b | |||
053bce7a8e | |||
2f208832a9 | |||
268d7495cc | |||
ce16e61e63 | |||
f92bca58fa | |||
83d541b60d | |||
965efc3a13 | |||
d656c34bd4 | |||
7f151cbeba | |||
bc2f9204e9 | |||
a922a93016 | |||
eb596ba866 | |||
2208545612 | |||
f08a875cd2 | |||
d492d3f738 | |||
c687091ce9 | |||
eb994716e6 | |||
70acc8a7c0 | |||
bf97781232 | |||
099727d671 | |||
6229cdb1ba | |||
b7a663ed20 | |||
3bd97352ba | |||
33e25d9241 | |||
fc11018158 | |||
5e22360cb1 | |||
840348b4eb | |||
450fb2553c | |||
cf04738594 | |||
03757632cf | |||
e818f5a93f | |||
ab9b08770a | |||
40df8b68ad | |||
9f5202fee3 | |||
902ccbd203 | |||
4675da4d16 | |||
86da27a7a1 | |||
fc2a6567da | |||
7c611d9b62 | |||
784c7465d1 | |||
301af7bd7a | |||
09c11a385d | |||
ef6f491d94 | |||
9dcef00fbb | |||
e781e5dd43 | |||
d3e672d811 | |||
dad1554ec2 | |||
30bf96c6cd | |||
a8c16e39b8 | |||
79a7cd2938 | |||
26562e445f | |||
0b678b1f16 | |||
79b5e85b15 | |||
2432491bfc | |||
a09ce3e026 | |||
c52fc843f6 | |||
02240bda25 | |||
0185ef7c83 | |||
7d29b9901c | |||
ae553dfed3 | |||
71c6beadb4 | |||
d939629c09 | |||
0a569146a8 | |||
d5a012d49f | |||
22a11769fa | |||
7dc7ba9977 | |||
fa4059a4b9 | |||
7f4786f9dd | |||
5a6e7a46d1 | |||
9f90749f99 | |||
0dfaf9159d | |||
5d8bda1178 | |||
9ad1e0d529 | |||
389e3397ec | |||
284b95213e | |||
952854f64e | |||
554650c18d | |||
01a2fa7c2d | |||
1257e34487 | |||
a45743f443 | |||
7d03719816 | |||
7c5bbca2fa | |||
cf313939aa | |||
873d4bd3f2 | |||
f43f3fc84b | |||
0e1fed86ba | |||
3fb5d886dc | |||
b57cd8d5c2 | |||
5c1bbc08ca | |||
6ba32b95f3 | |||
d3df113fb0 | |||
06c2ab045a | |||
6d43e0951c | |||
ec14429238 | |||
1dc2c6f183 | |||
984b8f7e6f | |||
0f8448b2c0 | |||
088c546bee | |||
20fff378f0 | |||
4994a7ac85 | |||
a959c69d32 | |||
39244568be | |||
c8fc0bb4f5 | |||
654ad5c71f | |||
7b9d18caea | |||
8d347efec2 | |||
85de6dd52e | |||
c024b39c8b | |||
1f0db48487 | |||
2bc5f475f4 | |||
5abf4c99de | |||
137e519b66 | |||
dcb27e7de8 | |||
d4d5b5a75c | |||
a83b3f8408 | |||
da9af8673f | |||
1b4ba3b396 | |||
eb2f1cbc9e | |||
28f58f72dd | |||
81389401df | |||
c618c5c5f0 | |||
0eaff4c626 | |||
9783c1052d | |||
f732fa9736 | |||
0c2d227da1 | |||
a281efef04 | |||
538dcec348 | |||
0d38c8ae8f | |||
967c1a2da9 | |||
f3da326b77 | |||
153a6e2cb0 | |||
95f37b9d36 | |||
60c37a1fc7 | |||
615c61e230 | |||
ae40b6ba8c | |||
d482427e0d | |||
c41baf3aeb | |||
dd7cb74edc | |||
4eed2c7582 | |||
100e830e04 | |||
af28d82ebc | |||
6285980f98 | |||
a5d19cd31f | |||
9c9998b468 | |||
011eb55a53 | |||
189d31cc29 | |||
d178f3d1b9 | |||
6e9d73ec64 | |||
8d1adf4f80 | |||
d0b7f58e7c | |||
19d24e5644 | |||
461f618b8a | |||
fc875651d3 | |||
5ff14d1fed | |||
80c9c1bb05 | |||
a111d9b18a | |||
df14913c67 | |||
b6c6fef770 | |||
1a2f37b0ec | |||
6f3c662783 | |||
3772137c8f | |||
0d62123a0b | |||
28fed6281f | |||
1ec95d42ba | |||
8adf965d0b | |||
026dd38480 | |||
338c2243e3 | |||
e8d61225f5 | |||
cc356ce67d | |||
364e364429 | |||
46a46877ed | |||
5ee05e3aaa | |||
5568a09f49 | |||
1199c431ff | |||
2c1a897c4e | |||
305f2fa448 | |||
b051685727 | |||
344dd92c85 | |||
4167c65acf | |||
cd6d49860f | |||
8a10fcf7ea | |||
7580bb21c3 | |||
62102236a2 | |||
3b5f96a133 | |||
ce2b711b1f | |||
667fb438cb | |||
7befa94e6d | |||
32d7835119 | |||
726abf6e65 | |||
c154a4bdc8 | |||
88ef1a3c5b | |||
5453925e26 | |||
537e314b49 | |||
ccb7a553c2 | |||
1696a5c8e1 | |||
816cf8f702 | |||
e8167541af | |||
329360aa5b | |||
eb1a276e60 | |||
a53bac1a94 | |||
93bf93d3a1 | |||
4174c8c25c | |||
48a88a8624 | |||
1442748f58 | |||
d17e216f91 | |||
56ed4fe6f2 | |||
9a71e9ba86 | |||
ef478a4a9e | |||
1bd7d40716 | |||
807e9573fb | |||
849d1d7ebd | |||
3fe2545228 | |||
090dfff730 | |||
f94e9449d5 | |||
d8753adc4e | |||
2e17ea99e2 | |||
c4daf8524e | |||
9d16898926 | |||
f4bcc1f2e5 | |||
63e8614ace | |||
5d686b146d | |||
e85758dc5f | |||
d08f090800 | |||
8554473c21 | |||
01fb1bde8b | |||
29e32ffc42 | |||
88bd60a083 | |||
48b7b725b0 | |||
8d8c932d8c | |||
253d355bd2 | |||
e287df1320 | |||
bae0bec1cc | |||
602686a5d2 | |||
af05d94198 | |||
5fa3a7ca44 | |||
9609350789 | |||
9c1e73ffff | |||
50741c70c0 | |||
fc8660df78 | |||
4e5ddca3bd | |||
3bdc90451a | |||
a036b2981a | |||
8fae83dab7 | |||
083f9dd29b | |||
7d5fabbd25 | |||
105f071847 | |||
ef68e5b13d | |||
21afe077d7 | |||
48222ce44c | |||
0922ba938c | |||
3fc66ec525 | |||
44191cd908 | |||
0da0c6bd77 | |||
b5f6e9d01b | |||
6098b196dc | |||
0922349344 | |||
53cdeeff03 | |||
fcdb086daf | |||
d2d9c2dd0f | |||
4241fb9386 | |||
cfd6751777 | |||
5e461e9b6b | |||
946dfdf7b8 | |||
4da9843479 | |||
eccb3c643d | |||
bfa5a51ce8 | |||
9066ad6cdf | |||
f7b513dff2 | |||
3de5f10d52 | |||
07429a862c | |||
2f2bddf020 | |||
ad03adaebf | |||
ac7a5488ee | |||
6de93d4fbb | |||
8a312f76c5 | |||
4a956b5a55 | |||
83d6c3ba88 | |||
52daf6b864 | |||
c8420e152f | |||
1dff19af26 | |||
a1e2eca802 | |||
48b63a26c8 | |||
b23bc4a5b6 | |||
940236b4a4 | |||
b6ef18b0d8 | |||
372484f976 | |||
086cf67e93 | |||
3c8692d06c | |||
efffca83fe | |||
04fe81e001 | |||
d2215c2ba9 | |||
89b1b6e242 | |||
e476d68848 | |||
da8835bc77 | |||
f170c2611c | |||
351b17d1d9 | |||
14e88706df | |||
926e3e2712 | |||
c39043fb9d | |||
578b3ba4f4 | |||
5b0b582039 | |||
e24be913e5 | |||
4d3358ba66 | |||
ffe40fa3a3 | |||
87f93b34a3 | |||
03bd9a5731 | |||
cb82170187 | |||
5b9e16af83 | |||
7b1b2a4bef | |||
1f1a0b7b53 | |||
320acfae89 | |||
1c171d0f12 | |||
22bf3618ea | |||
5f1593f4d0 | |||
9af75bf9b2 | |||
344fa729a5 | |||
33d3d90a93 | |||
24dfc09f35 | |||
e533bc0847 | |||
306333ceba | |||
e96312b470 | |||
fb60b4bca7 | |||
0612e4429d | |||
ee80aa26db | |||
a45e667e9c | |||
1b4a2369bb | |||
224483f6ac | |||
c61574b782 | |||
c92129ac63 | |||
6c71d95932 | |||
e859f5c7a1 | |||
dc402f5f0e | |||
188894c837 | |||
70f99a70a5 | |||
fb4fbd23d8 | |||
f58b2383b9 | |||
05caf1fe28 | |||
704486abc2 | |||
1ec023b435 | |||
fd21eeb477 | |||
edf2b2df6f | |||
9ce338622c | |||
2b35529cbd | |||
554b67a2f0 | |||
012243a880 | |||
d4a348a2b2 | |||
1d4c5cc96f | |||
41bfb96b6b | |||
994d62ac65 | |||
7c72608e1c | |||
2edc06c662 | |||
cb4d66d6fe | |||
f80602b51a | |||
58d8a5ce46 | |||
72a65218be | |||
1b0d5b710e | |||
2a25ac0847 | |||
9aefdf35a1 | |||
231961c017 | |||
ee621fa091 | |||
a69a04cfb6 | |||
b1aed344c7 | |||
3e08d665c7 | |||
4a94c86433 | |||
d4878f6ed3 | |||
d94719ea02 | |||
982b5221b1 | |||
cbdf03450d | |||
7625e591fe | |||
8fdb1e7ec9 | |||
d3b28c42e6 | |||
1b32423881 | |||
7de699c7fa | |||
e9f9670eb5 | |||
3d4e961320 | |||
db2fb33d53 | |||
ff3db04ab7 | |||
c7f6763c48 | |||
b1dd4069db | |||
58c647d433 | |||
333ea4aa53 | |||
3ad59da2a9 | |||
2d9b211eeb | |||
4f5a352985 | |||
6ae3b77c2f | |||
4a7260b1be | |||
f91c77bdc6 | |||
476e938d23 | |||
1ec9d986bb | |||
4b88cfa51a | |||
bc56226a28 | |||
a6e5474fdb | |||
8c7ca2c34d | |||
91fccc6691 | |||
93d1737357 | |||
5ba1ae9ae4 | |||
8cb408bc6e | |||
197a89a37a | |||
d336ead3b1 | |||
662644663e | |||
4c7819effb | |||
8b5b9ee8f1 | |||
89b911a9dc | |||
b673e216b6 | |||
a1b2f0ccf1 | |||
f269facf9d | |||
5a36d280d7 | |||
c39563b123 | |||
548149de8e | |||
d6d4ce0ac4 | |||
83b0239791 | |||
d1fa13d67a | |||
3abd570678 | |||
b0b0781bd7 | |||
1aa28ddee1 | |||
09b50badb1 | |||
7060108a8b | |||
e6f0d5bf44 | |||
399642f958 | |||
781effc34e | |||
324c8f8146 | |||
27e372e38f | |||
87122ce211 | |||
c0c6675423 | |||
fa4aeb5261 | |||
4e51eeb998 | |||
a27c3f09b3 | |||
3e5f117066 | |||
3753fb3ea4 | |||
d3e49cf1e9 | |||
ffcf46a371 | |||
fc5eedbef5 | |||
d78b6c4445 | |||
6e056bb337 | |||
b5c2c1009c | |||
b54029a04a | |||
e30aca7531 | |||
866722b68f | |||
d93f3468d3 | |||
83032e858a | |||
6855e314b3 | |||
2c4a8619a8 | |||
3247d83252 | |||
109ff2d8a5 | |||
48797d12eb | |||
abe66d8af0 | |||
58c9b70b26 | |||
996643bde8 | |||
68dd9a29e8 | |||
68c4b55945 | |||
bab86c9ce2 | |||
5f24e4d705 | |||
66c7b3fcb2 | |||
1ffe29c657 | |||
6b7d4877e6 | |||
80826eb500 | |||
11962facde | |||
c7eccfd804 | |||
ad617e0deb | |||
7ae70d5a4d | |||
62d1a0291e | |||
883dc72fc6 | |||
65b2e9633c | |||
3b923e0d37 | |||
4938cb9bbc | |||
a208564f06 | |||
6c6ca4daf4 | |||
1024da601d | |||
55d05ee590 | |||
66f39e070b | |||
252681001e | |||
a39014b3b1 | |||
10bcfdd6b2 | |||
41ea9814e8 | |||
bccf7e3f69 | |||
06c8a004d8 | |||
4ab90fb14d | |||
54d8feeaf0 | |||
23d20c918f | |||
10012d7125 | |||
d70ea854b1 | |||
8c576ece28 | |||
e0c0935d3a | |||
ab987e73c6 | |||
cec203a6db | |||
cf8bb9efb0 | |||
201a053025 | |||
703f987825 | |||
4e3d677d8e | |||
aab90130d2 | |||
9321dfdd89 | |||
2607ef5fe0 | |||
84b291a57a | |||
3271c5a60b | |||
79bb66f535 | |||
1616885e7f | |||
85045c3e9c | |||
4710d18e33 | |||
3da75494b0 | |||
ac021f38da | |||
d0a2c36f6c | |||
9facc9379f | |||
a0f4e2659b | |||
4dfa3d8372 | |||
30563ee04c | |||
3fa7594fcc | |||
ccfa32ce93 | |||
fcb41e30dc | |||
5ed6bba412 | |||
50e929eb0c | |||
129839ce21 | |||
fd2961ecb9 | |||
e506d988e2 | |||
196649c0e9 | |||
12182ee04f | |||
5db64526cc | |||
5c2ec70eb4 | |||
24a2c6251f | |||
0d035d9ae9 | |||
a28f1294e2 | |||
a3b0cde59d | |||
c2dec9eac4 | |||
0fdb49f168 | |||
e86d02765f | |||
6e877aea02 | |||
330f1577fd | |||
ff980591c3 | |||
60992ae492 | |||
1b439c15f6 | |||
f58c16ab03 | |||
b560d2953f | |||
2c52c8efb7 | |||
5375166f04 | |||
5e85cae9ce | |||
0ae449fd44 | |||
4f5b4f387a | |||
a8d3d613b0 | |||
c680fbe834 | |||
286c77778f | |||
10c4bee1e5 | |||
f26b8ee224 | |||
1961567ebd | |||
094687717d | |||
e5844c926b | |||
96ad8c823a | |||
de2977284c | |||
c1dea44fa6 | |||
5a105eb2b3 | |||
7a076f7304 | |||
986b303f2f | |||
afd8717c21 | |||
6effd783c0 | |||
b8f47545ed | |||
a6394b2dce | |||
5ba802dc68 | |||
76c72c1a7f | |||
1c1cf58409 | |||
8155e3ef7a | |||
62048c68f0 | |||
f32df2ac9c | |||
22a0b3be45 | |||
3490d0d743 | |||
fbe829def7 | |||
161eea3e9e | |||
d1e792686e | |||
62fe380520 | |||
319f08c4c9 | |||
0d13fe67b0 | |||
a0c6e9e490 | |||
56960d6da9 | |||
2590dc690e | |||
e2545b3d34 | |||
bf4d920fb4 | |||
d84fb244d3 | |||
f04b5244fa | |||
6f3829fa91 | |||
a5ae3545d5 | |||
63b658ac50 | |||
e8544be30a | |||
39090451f0 | |||
12fc6e6354 | |||
154326061d | |||
0bbfd574b3 | |||
eff3887f35 | |||
34cb532e6b | |||
cf1d1a97f3 | |||
e0a4f0bc4c | |||
1ac8eba1b8 | |||
82f8c31b81 | |||
9f2ae75fa2 | |||
9d220c482d | |||
c4718bd302 | |||
e1e2a90498 | |||
40a48954c3 | |||
e81068f528 | |||
0591487cfd | |||
737ed7ba5d | |||
27fd1f39f0 | |||
1210313e7f | |||
4733c8fb75 | |||
8777e65996 | |||
330fa10935 | |||
1bd4a0f4ee | |||
bea8ae55de | |||
b99cd49091 | |||
35f3b70968 | |||
e33f3231d0 | |||
b6cf6ee94a | |||
6118518b1b | |||
f77d5ebfd2 | |||
fcf5c41709 | |||
7f3279f20f | |||
c23de50f3e | |||
51f1759323 | |||
cedc634933 | |||
c7b89a1126 | |||
bad015de9a | |||
21057759de | |||
d027352d79 | |||
fd8c568d75 | |||
85292ca1b4 | |||
1ab90e76d1 | |||
1e9e9daeb3 | |||
65682a8130 | |||
bca225e692 | |||
4fbc46b3b3 | |||
4b3806fb9a | |||
5abbec94c4 | |||
346d1dddba | |||
8b48ad77e8 | |||
64d97a3232 | |||
94a208bd91 | |||
b15ac8b13e | |||
0ddff9d551 | |||
7c31dbb3d6 | |||
6ee995afaa | |||
691acb647e | |||
80d9d88de1 | |||
cf12abfc7f | |||
b21ec7a302 | |||
972473f1af | |||
ee8e0b46a2 | |||
1c3d270402 | |||
9d09d2042a | |||
b8823ade29 | |||
0e5b01f897 | |||
87f221290f | |||
fd3b0c20b6 | |||
93e71027fd | |||
c6852c70fd | |||
953773aaa8 | |||
e0a139d083 | |||
867140df30 | |||
8a354e6187 | |||
6afd4b0dc7 | |||
e2517ef50e | |||
ad6798eaad | |||
2a5fcc0846 | |||
37592a876e | |||
4979e176c7 | |||
f3fdac2762 | |||
9a67617cef | |||
96e43cbb65 | |||
458dc5b232 | |||
28a7c6d3aa | |||
d3c241d283 | |||
445959abbd | |||
62842968cb | |||
2dda683aa6 | |||
616167e08c | |||
acd74c60c8 | |||
c1c4a2933e | |||
8158ead1a2 | |||
fcc196f452 | |||
d55e2492d4 | |||
45e05b0891 | |||
eb53739c95 | |||
4a35059711 | |||
a1ad1147e6 | |||
eefa7d53c5 | |||
716491a68e | |||
29e5263fcc | |||
4df61d7efc | |||
34aa60d47b |
2
.github/FUNDING.yml
vendored
@ -1,3 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
ko_fi: cmdr2_stablediffusion_ui
|
||||
ko_fi: easydiffusion
|
||||
|
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
||||
__pycache__
|
||||
installer
|
||||
installer.tar
|
||||
dist
|
||||
.idea/*
|
||||
node_modules/*
|
9
.prettierignore
Normal file
@ -0,0 +1,9 @@
|
||||
*.min.*
|
||||
*.py
|
||||
*.json
|
||||
*.html
|
||||
/*
|
||||
!/ui
|
||||
/ui/easydiffusion
|
||||
!/ui/plugins
|
||||
!/ui/media
|
7
.prettierrc.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"semi": false,
|
||||
"arrowParens": "always",
|
||||
"trailingComma": "es5"
|
||||
}
|
1122
3rd-PARTY-LICENSES
Normal file
245
CHANGES.md
Normal file
@ -0,0 +1,245 @@
|
||||
# What's new?
|
||||
|
||||
## v3.0
|
||||
### Major Changes
|
||||
- **ControlNet** - Full support for ControlNet, with native integration of the common ControlNet models. Just select a control image, then choose the ControlNet filter/model and run. No additional configuration or download necessary. Supports custom ControlNets as well.
|
||||
- **SDXL** - Full support for SDXL. No configuration necessary, just put the SDXL model in the `models/stable-diffusion` folder.
|
||||
- **Multiple LoRAs** - Use multiple LoRAs, including SDXL and SD2-compatible LoRAs. Put them in the `models/lora` folder.
|
||||
- **Embeddings** - Use textual inversion embeddings easily, by putting them in the `models/embeddings` folder and using their names in the prompt (or by clicking the `+ Embeddings` button to select embeddings visually). Thanks @JeLuf.
|
||||
- **Seamless Tiling** - Generate repeating textures that can be useful for games and other art projects. Works best in 512x512 resolution. Thanks @JeLuf.
|
||||
- **Inpainting Models** - Full support for inpainting models, including custom inpainting models. No configuration (or yaml files) necessary.
|
||||
- **Faster than v2.5** - Nearly 40% faster than Easy Diffusion v2.5, and can be even faster if you enable xFormers.
|
||||
- **Even less VRAM usage** - Less than 2 GB for 512x512 images on 'low' VRAM usage setting (SD 1.5). Can generate large images with SDXL.
|
||||
- **WebP images** - Supports saving images in the lossless webp format.
|
||||
- **Undo/Redo in the UI** - Remove tasks or images from the queue easily, and undo the action if you removed anything accidentally. Thanks @JeLuf.
|
||||
- **Three new samplers, and latent upscaler** - Added `DEIS`, `DDPM` and `DPM++ 2m SDE` as additional samplers. Thanks @ogmaresca and @rbertus2000.
|
||||
- **Significantly faster 'Upscale' and 'Fix Faces' buttons on the images**
|
||||
- **Major rewrite of the code** - We've switched to using diffusers under-the-hood, which allows us to release new features faster, and focus on making the UI and installer even easier to use.
|
||||
|
||||
### Detailed changelog
|
||||
* 3.0.2 - 29 Aug 2023 - Fixed incorrect matching of embeddings from prompts.
|
||||
* 3.0.2 - 24 Aug 2023 - Fix broken seamless tiling.
|
||||
* 3.0.2 - 23 Aug 2023 - Fix styling on mobile devices.
|
||||
* 3.0.2 - 22 Aug 2023 - Full support for inpainting models, including custom models. Support SD 1.x and SD 2.x inpainting models. Does not require you to specify a yaml config file.
|
||||
* 3.0.2 - 22 Aug 2023 - Reduce VRAM consumption of controlnet in 'low' VRAM mode, and allow accelerating controlnets using xformers.
|
||||
* 3.0.2 - 22 Aug 2023 - Improve auto-detection of SD 2.0 and 2.1 models, removing the need for custom yaml files for SD 2.x models. Improve the model load time by speeding-up the black image test.
|
||||
* 3.0.1 - 18 Aug 2023 - Rotate an image if EXIF rotation is present. For e.g. this is common in images taken with a smartphone.
|
||||
* 3.0.1 - 18 Aug 2023 - Resize control images to the task dimensions, to avoid memory errors with high-res control images.
|
||||
* 3.0.1 - 18 Aug 2023 - Show controlnet filter preview in the task entry.
|
||||
* 3.0.1 - 18 Aug 2023 - Fix drag-and-drop and 'Use these Settings' for LoRA and ControlNet.
|
||||
* 3.0.1 - 18 Aug 2023 - Auto-save LoRA models and strengths.
|
||||
* 3.0.1 - 17 Aug 2023 - Automatically use the correct yaml config file for custom SDXL models, even if a yaml file isn't present in the folder.
|
||||
* 3.0.1 - 17 Aug 2023 - Fix broken embeddings with SDXL.
|
||||
* 3.0.1 - 16 Aug 2023 - Fix broken LoRA with SDXL.
|
||||
* 3.0.1 - 15 Aug 2023 - Fix broken seamless tiling.
|
||||
* 3.0.1 - 15 Aug 2023 - Fix textual inversion embeddings not working in `low` VRAM usage mode.
|
||||
* 3.0.1 - 15 Aug 2023 - Fix for custom VAEs not working in `low` VRAM usage mode.
|
||||
* 3.0.1 - 14 Aug 2023 - Slider to change the image dimensions proportionally (in Image Settings). Thanks @JeLuf.
|
||||
* 3.0.1 - 14 Aug 2023 - Show an error to the user if an embedding isn't compatible with the model, instead of failing silently without informing the user. Thanks @JeLuf.
|
||||
* 3.0.1 - 14 Aug 2023 - Disable watermarking for SDXL img2img. Thanks @AvidGameFan.
|
||||
* 3.0.0 - 3 Aug 2023 - Enabled diffusers for everyone by default. The old v2 engine can be used by disabling the "Use v3 engine" option in the Settings tab.
|
||||
|
||||
## v2.5
|
||||
### Major Changes
|
||||
- **Nearly twice as fast** - significantly faster speed of image generation. Code contributions are welcome to make our project even faster: https://github.com/easydiffusion/sdkit/#is-it-fast
|
||||
- **Mac M1/M2 support** - Experimental support for Mac M1/M2. Thanks @michaelgallacher, @JeLuf and vishae.
|
||||
- **AMD support for Linux** - Experimental support for AMD GPUs on Linux. Thanks @DianaNites and @JeLuf.
|
||||
- **Full support for Stable Diffusion 2.1 (including CPU)** - supports loading v1.4 or v2.0 or v2.1 models seamlessly. No need to enable "Test SD2", and no need to add `sd2_` to your SD 2.0 model file names. Works on CPU as well.
|
||||
- **Memory optimized Stable Diffusion 2.1** - you can now use Stable Diffusion 2.1 models, with the same low VRAM optimizations that we've always had for SD 1.4. Please note, the SD 2.0 and 2.1 models require more GPU and System RAM, as compared to the SD 1.4 and 1.5 models.
|
||||
- **11 new samplers!** - explore the new samplers, some of which can generate great images in less than 10 inference steps! We've added the Karras and UniPC samplers. Thanks @Schorny for the UniPC samplers.
|
||||
- **Model Merging** - You can now merge two models (`.ckpt` or `.safetensors`) and output `.ckpt` or `.safetensors` models, optionally in `fp16` precision. Details: https://github.com/easydiffusion/easydiffusion/wiki/Model-Merging . Thanks @JeLuf.
|
||||
- **Fast loading/unloading of VAEs** - No longer needs to reload the entire Stable Diffusion model, each time you change the VAE
|
||||
- **Database of known models** - automatically picks the right configuration for known models. E.g. we automatically detect and apply "v" parameterization (required for some SD 2.0 models), and "fp32" attention precision (required for some SD 2.1 models).
|
||||
- **Color correction for img2img** - an option to preserve the color profile (histogram) of the initial image. This is especially useful if you're getting red-tinted images after inpainting/masking.
|
||||
- **Three GPU Memory Usage Settings** - `High` (fastest, maximum VRAM usage), `Balanced` (default - almost as fast, significantly lower VRAM usage), `Low` (slowest, very low VRAM usage). The `Low` setting is applied automatically for GPUs with less than 4 GB of VRAM.
|
||||
- **Find models in sub-folders** - This allows you to organize your models into sub-folders inside `models/stable-diffusion`, instead of keeping them all in a single folder. Thanks @patriceac and @ogmaresca.
|
||||
- **Custom Modifier Categories** - Ability to create custom modifiers with thumbnails, and custom categories (and hierarchy of categories). Details: https://github.com/easydiffusion/easydiffusion/wiki/Custom-Modifiers . Thanks @ogmaresca.
|
||||
- **Embed metadata, or save as TXT/JSON** - You can now embed the metadata directly into the images, or save them as text or json files (choose in the Settings tab). Thanks @patriceac.
|
||||
- **Major rewrite of the code** - Most of the codebase has been reorganized and rewritten, to make it more manageable and easier for new developers to contribute features. We've separated our core engine into a new project called `sdkit`, which allows anyone to easily integrate Stable Diffusion (and related modules like GFPGAN etc) into their programming projects (via a simple `pip install sdkit`): https://github.com/easydiffusion/sdkit/
|
||||
- **Name change** - Last, and probably the least, the UI is now called "Easy Diffusion". It indicates the focus of this project - an easy way for people to play with Stable Diffusion.
|
||||
|
||||
Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed.
|
||||
|
||||
### Detailed changelog
|
||||
* 2.5.48 - 1 Aug 2023 - (beta-only) Full support for ControlNets. You can select a control image to guide the AI. You can pick a filter to pre-process the image, and one of the known (or custom) controlnet models. Supports `OpenPose`, `Canny`, `Straight Lines`, `Depth`, `Line Art`, `Scribble`, `Soft Edge`, `Shuffle` and `Segment`.
|
||||
* 2.5.47 - 30 Jul 2023 - An option to use `Strict Mask Border` while inpainting, to avoid touching areas outside the mask. But this might show a slight outline of the mask, which you will have to touch up separately.
|
||||
* 2.5.47 - 29 Jul 2023 - (beta-only) Fix long prompts with SDXL.
|
||||
* 2.5.47 - 29 Jul 2023 - (beta-only) Fix red dots in some SDXL images.
|
||||
* 2.5.47 - 29 Jul 2023 - Significantly faster `Fix Faces` and `Upscale` buttons (on the image). They no longer need to generate the image from scratch, instead they just upscale/fix the generated image in-place.
|
||||
* 2.5.47 - 28 Jul 2023 - Lots of internal code reorganization, in preparation for supporting Controlnets. No user-facing changes.
|
||||
* 2.5.46 - 27 Jul 2023 - (beta-only) Full support for SD-XL models (base and refiner)!
|
||||
* 2.5.45 - 24 Jul 2023 - (beta-only) Hide the samplers that won't be supported in the new diffusers version.
|
||||
* 2.5.45 - 22 Jul 2023 - (beta-only) Fix the recently-broken inpainting models.
|
||||
* 2.5.45 - 16 Jul 2023 - (beta-only) Fix the image quality of LoRAs, which had degraded in v2.5.44.
|
||||
* 2.5.44 - 15 Jul 2023 - (beta-only) Support for multiple LoRA files.
|
||||
* 2.5.43 - 9 Jul 2023 - (beta-only) Support for loading Textual Inversion embeddings. You can find the option in the Image Settings panel. Thanks @JeLuf.
|
||||
* 2.5.43 - 9 Jul 2023 - Improve the startup time of the UI.
|
||||
* 2.5.42 - 4 Jul 2023 - Keyboard shortcuts for the Image Editor. Thanks @JeLuf.
|
||||
* 2.5.42 - 28 Jun 2023 - Allow dropping images from folders to use as an Initial Image.
|
||||
* 2.5.42 - 26 Jun 2023 - Show a popup for Image Modifiers, allowing a larger screen space, better UX on mobile screens, and more room for us to develop and improve the Image Modifiers panel. Thanks @Hakorr.
|
||||
* 2.5.42 - 26 Jun 2023 - (beta-only) Show a welcome screen for users of the diffusers beta, with instructions on how to use the new prompt syntax, and known bugs. Thanks @JeLuf.
|
||||
* 2.5.42 - 26 Jun 2023 - Use YAML files for config. You can now edit the `config.yaml` file (using a text editor, like Notepad). This file is present inside the Easy Diffusion folder, and is easier to read and edit (for humans) than JSON. Thanks @JeLuf.
|
||||
* 2.5.41 - 24 Jun 2023 - (beta-only) Fix broken inpainting in low VRAM usage mode.
|
||||
* 2.5.41 - 24 Jun 2023 - (beta-only) Fix a recent regression where the LoRA would not get applied when changing SD models.
|
||||
* 2.5.41 - 23 Jun 2023 - Fix a regression where latent upscaler stopped working on PCs without a graphics card.
|
||||
* 2.5.41 - 20 Jun 2023 - Automatically fix black images if fp32 attention precision is required in diffusers.
|
||||
* 2.5.41 - 19 Jun 2023 - Another fix for multi-gpu rendering (in all VRAM usage modes).
|
||||
* 2.5.41 - 13 Jun 2023 - Fix multi-gpu bug with "low" VRAM usage mode while generating images.
|
||||
* 2.5.41 - 12 Jun 2023 - Fix multi-gpu bug with CodeFormer.
|
||||
* 2.5.41 - 6 Jun 2023 - Allow changing the strength of CodeFormer, and slightly improved styling of the CodeFormer options.
|
||||
* 2.5.41 - 5 Jun 2023 - Allow sharing an Easy Diffusion instance via https://try.cloudflare.com/ . You can find this option at the bottom of the Settings tab. Thanks @JeLuf.
|
||||
* 2.5.41 - 5 Jun 2023 - Show an option to download for tiled images. Shows a button on the generated image. Creates larger images by tiling them with the image generated by Easy Diffusion. Thanks @JeLuf.
|
||||
* 2.5.41 - 5 Jun 2023 - (beta-only) Allow LoRA strengths between -2 and 2. Thanks @ogmaresca.
|
||||
* 2.5.40 - 5 Jun 2023 - Reduce the VRAM usage of Latent Upscaling when using "balanced" VRAM usage mode.
|
||||
* 2.5.40 - 5 Jun 2023 - Fix the "realesrgan" key error when using CodeFormer with more than 1 image in a batch.
|
||||
* 2.5.40 - 3 Jun 2023 - Added CodeFormer as another option for fixing faces and eyes. CodeFormer tends to perform better than GFPGAN for many images. Thanks @patriceac for the implementation, and for contacting the CodeFormer team (who were supportive of it being integrated into Easy Diffusion).
|
||||
* 2.5.39 - 25 May 2023 - (beta-only) Seamless Tiling - make seamlessly tiled images, e.g. rock and grass textures. Thanks @JeLuf.
|
||||
* 2.5.38 - 24 May 2023 - Better reporting of errors, and show an explanation if the user cannot disable the "Use CPU" setting.
|
||||
* 2.5.38 - 23 May 2023 - Add Latent Upscaler as another option for upscaling images. Thanks @JeLuf for the implementation of the Latent Upscaler model.
|
||||
* 2.5.37 - 19 May 2023 - (beta-only) Two more samplers: DDPM and DEIS. Also disables the samplers that aren't working yet in the Diffusers version. Thanks @ogmaresca.
|
||||
* 2.5.37 - 19 May 2023 - (beta-only) Support CLIP-Skip. You can set this option under the models dropdown. Thanks @JeLuf.
|
||||
* 2.5.37 - 19 May 2023 - (beta-only) More VRAM optimizations for all modes in diffusers. The VRAM usage for diffusers in "low" and "balanced" should now be equal or less than the non-diffusers version. Performs softmax in half precision, like sdkit does.
|
||||
* 2.5.36 - 16 May 2023 - (beta-only) More VRAM optimizations for "balanced" VRAM usage mode.
|
||||
* 2.5.36 - 11 May 2023 - (beta-only) More VRAM optimizations for "low" VRAM usage mode.
|
||||
* 2.5.36 - 10 May 2023 - (beta-only) Bug fix for "meta" error when using a LoRA in 'low' VRAM usage mode.
|
||||
* 2.5.35 - 8 May 2023 - Allow dragging a zoomed-in image (after opening an image with the "expand" button). Thanks @ogmaresca.
|
||||
* 2.5.35 - 3 May 2023 - (beta-only) First round of VRAM Optimizations for the "Test Diffusers" version. This change significantly reduces the amount of VRAM used by the diffusers version during image generation. The VRAM usage is still not equal to the "non-diffusers" version, but more optimizations are coming soon.
|
||||
* 2.5.34 - 22 Apr 2023 - Don't start the browser in an incognito new profile (on Windows). Thanks @JeLuf.
|
||||
* 2.5.33 - 21 Apr 2023 - Install PyTorch 2.0 on new installations (on Windows and Linux).
|
||||
* 2.5.32 - 19 Apr 2023 - Automatically check for black images, and set full-precision if necessary (for attn). This means custom models based on Stable Diffusion v2.1 will just work, without needing special command-line arguments or editing of yaml config files.
|
||||
* 2.5.32 - 18 Apr 2023 - Automatic support for AMD graphics cards on Linux. Thanks @DianaNites and @JeLuf.
|
||||
* 2.5.31 - 10 Apr 2023 - Reduce VRAM usage while upscaling.
|
||||
* 2.5.31 - 6 Apr 2023 - Allow seeds upto `4,294,967,295`. Thanks @ogmaresca.
|
||||
* 2.5.31 - 6 Apr 2023 - Buttons to show the previous/next image in the image popup. Thanks @ogmaresca.
|
||||
* 2.5.30 - 5 Apr 2023 - Fix a bug where the JPEG image quality wasn't being respected when embedding the metadata into it. Thanks @JeLuf.
|
||||
* 2.5.30 - 1 Apr 2023 - (beta-only) Slider to control the strength of the LoRA model.
|
||||
* 2.5.30 - 28 Mar 2023 - Refactor task entry config to use a generating method. Added ability for plugins to easily add to this. Removed confusing sentence from `contributing.md`
|
||||
* 2.5.30 - 28 Mar 2023 - Allow the user to undo the deletion of tasks or images, instead of showing a pop-up each time. The new `Undo` button will be present at the top of the UI. Thanks @JeLuf.
|
||||
* 2.5.30 - 28 Mar 2023 - Support saving lossless WEBP images. Thanks @ogmaresca.
|
||||
* 2.5.30 - 28 Mar 2023 - Lots of bug fixes for the UI (Read LoRA flag in metadata files, new prompt weight format with scrollwheel, fix overflow with lots of tabs, clear button in image editor, shorter filenames in download). Thanks @patriceac, @JeLuf and @ogmaresca.
|
||||
* 2.5.29 - 27 Mar 2023 - (beta-only) Fix a bug where some non-square images would fail while inpainting with a `The size of tensor a must match size of tensor b` error.
|
||||
* 2.5.29 - 27 Mar 2023 - (beta-only) Fix the `incorrect number of channels` error, when given a PNG image with an alpha channel in `Test Diffusers`.
|
||||
* 2.5.29 - 27 Mar 2023 - (beta-only) Fix broken inpainting in `Test Diffusers`.
|
||||
* 2.5.28 - 24 Mar 2023 - (beta-only) Support for weighted prompts and long prompt lengths (not limited to 77 tokens). This change requires enabling the `Test Diffusers` setting in beta (in the Settings tab), and restarting the program.
|
||||
* 2.5.27 - 21 Mar 2023 - (beta-only) LoRA support, accessible by enabling the `Test Diffusers` setting (in the Settings tab in the UI). This change switches the internal engine to diffusers (if the `Test Diffusers` setting is enabled). If the `Test Diffusers` flag is disabled, it'll have no impact for the user.
|
||||
* 2.5.26 - 15 Mar 2023 - Allow styling the buttons displayed on an image. Update the API to allow multiple buttons and text labels in a single row. Thanks @ogmaresca.
|
||||
* 2.5.26 - 15 Mar 2023 - View images in full-screen, by either clicking on the image, or clicking the "Full screen" icon next to the Seed number on the image. Thanks @ogmaresca for the internal API.
|
||||
* 2.5.25 - 14 Mar 2023 - Button to download all the images, and all the metadata as a zip file. This is available at the top of the UI, as well as on each image. Thanks @JeLuf.
|
||||
* 2.5.25 - 14 Mar 2023 - Lots of UI tweaks and bug fixes. Thanks @patriceac and @JeLuf.
|
||||
* 2.5.24 - 11 Mar 2023 - Button to load an image mask from a file.
|
||||
* 2.5.24 - 10 Mar 2023 - Logo change. Image credit: @lazlo_vii.
|
||||
* 2.5.23 - 8 Mar 2023 - Experimental support for Mac M1/M2. Thanks @michaelgallacher, @JeLuf and vishae!
|
||||
* 2.5.23 - 8 Mar 2023 - Ability to create custom modifiers with thumbnails, and custom categories (and hierarchy of categories). More details - https://github.com/easydiffusion/easydiffusion/wiki/Custom-Modifiers . Thanks @ogmaresca.
|
||||
* 2.5.22 - 28 Feb 2023 - Minor styling changes to UI buttons, and the models dropdown.
|
||||
* 2.5.22 - 28 Feb 2023 - Lots of UI-related bug fixes. Thanks @patriceac.
|
||||
* 2.5.21 - 22 Feb 2023 - An option to control the size of the image thumbnails. You can use the `Display options` in the top-right corner to change this. Thanks @JeLuf.
|
||||
* 2.5.20 - 20 Feb 2023 - Support saving images in WEBP format (which consumes less disk space, with similar quality). Thanks @ogmaresca.
|
||||
* 2.5.20 - 18 Feb 2023 - A setting to block NSFW images from being generated. You can enable this setting in the Settings tab.
|
||||
* 2.5.19 - 17 Feb 2023 - Initial support for server-side plugins. Currently supports overriding the `get_cond_and_uncond()` function.
|
||||
* 2.5.18 - 17 Feb 2023 - 5 new samplers! UniPC samplers, some of which produce images in less than 15 steps. Thanks @Schorny.
|
||||
* 2.5.16 - 13 Feb 2023 - Searchable dropdown for models. This is useful if you have a LOT of models. You can type part of the model name, to auto-search through your models. Thanks @patriceac for the feature, and @AssassinJN for help in UI tweaks!
|
||||
* 2.5.16 - 13 Feb 2023 - Lots of fixes and improvements to the installer. First round of changes to add Mac support. Thanks @JeLuf.
|
||||
* 2.5.16 - 13 Feb 2023 - UI bug fixes for the inpainter editor. Thanks @patriceac.
|
||||
* 2.5.16 - 13 Feb 2023 - Fix broken task reorder. Thanks @JeLuf.
|
||||
* 2.5.16 - 13 Feb 2023 - Remove a task if all the images inside it have been removed. Thanks @AssassinJN.
|
||||
* 2.5.16 - 10 Feb 2023 - Embed metadata into the JPG/PNG images, if selected in the "Settings" tab (under "Metadata format"). Thanks @patriceac.
|
||||
* 2.5.16 - 10 Feb 2023 - Sort models alphabetically in the models dropdown. Thanks @ogmaresca.
|
||||
* 2.5.16 - 10 Feb 2023 - Support multiple GFPGAN models. Download new GFPGAN models into the `models/gfpgan` folder, and refresh the UI to use it. Thanks @JeLuf.
|
||||
* 2.5.16 - 10 Feb 2023 - Allow a server to enforce a fixed directory path to save images. This is useful if the server is exposed to a lot of users. This can be set in the `config.json` file as `force_save_path: "/path/to/fixed/save/dir"`. E.g. `force_save_path: "D:/user_images"`. Thanks @JeLuf.
|
||||
* 2.5.16 - 10 Feb 2023 - The "Make Images" button now shows the correct amount of images it'll create when using operators like `{}` or `|`. For e.g. if the prompt is `Photo of a {woman, man}`, then the button will say `Make 2 Images`. Thanks @JeLuf.
|
||||
* 2.5.16 - 10 Feb 2023 - A bunch of UI-related bug fixes. Thanks @patriceac.
|
||||
* 2.5.15 - 8 Feb 2023 - Allow using 'balanced' VRAM usage mode on GPUs with 4 GB or less of VRAM. This mode used to be called 'Turbo' in the previous version.
|
||||
* 2.5.14 - 8 Feb 2023 - Fix broken auto-save settings. We renamed `sampler` to `sampler_name`, which caused old settings to fail.
|
||||
* 2.5.14 - 6 Feb 2023 - Simplify the UI for merging models, and some other minor UI tweaks. Better error reporting if a model failed to load.
|
||||
* 2.5.14 - 3 Feb 2023 - Fix the 'Make Similar Images' button, which was producing incorrect images (weren't very similar).
|
||||
* 2.5.13 - 1 Feb 2023 - Fix the remaining GPU memory leaks, including a better fix (more comprehensive) for the change in 2.5.12 (27 Jan).
|
||||
* 2.5.12 - 27 Jan 2023 - Fix a memory leak, which made the UI unresponsive after an out-of-memory error. The allocated memory is now freed-up after an error.
|
||||
* 2.5.11 - 25 Jan 2023 - UI for Merging Models. Thanks @JeLuf. More info: https://github.com/easydiffusion/easydiffusion/wiki/Model-Merging
|
||||
* 2.5.10 - 24 Jan 2023 - Reduce the VRAM usage for img2img in 'balanced' mode (without reducing the rendering speed), to make it similar to v2.4 of this UI.
|
||||
* 2.5.9 - 23 Jan 2023 - Fix a bug where img2img would produce poorer-quality images for the same settings, as compared to version 2.4 of this UI.
|
||||
* 2.5.9 - 23 Jan 2023 - Reduce the VRAM usage for 'balanced' mode (without reducing the rendering speed), to make it similar to v2.4 of the UI.
|
||||
* 2.5.8 - 17 Jan 2023 - Fix a bug where 'Low' VRAM usage would consume a LOT of VRAM (on higher-end GPUs). Also fixed a bug that caused out-of-memory errors on SD 2.1-768 models, on 'high' VRAM usage setting.
|
||||
* 2.5.7 - 16 Jan 2023 - Fix a bug where VAE files ending with .vae.pt weren't getting displayed. Thanks Madrang, rbertus2000 and JeLuf.
|
||||
* 2.5.6 - 10 Jan 2023 - `Fill` tool for the Image Editor, to allow filling areas with color (or the entire image). And some bug fixes to the Image Editor. Thanks @mdiller.
|
||||
* 2.5.6 - 10 Jan 2023 - Find Stable Diffusion models in sub-folders inside `models/stable-diffusion`. This allows you to organize your models into sub-folders, instead of keeping them all in a single folder. Thanks @JeLuf.
|
||||
* 2.5.5 - 9 Jan 2023 - Lots of bug fixes. Thanks @patriceac and @JeLuf.
|
||||
* 2.5.4 - 29 Dec 2022 - Press Esc key on the keyboard to close the Image Editor. Thanks @patriceac.
|
||||
* 2.5.4 - 29 Dec 2022 - Lots of bug fixes in the UI. Thanks @patriceac.
|
||||
* 2.5.4 - 28 Dec 2022 - Full support for running tasks in parallel on multiple GPUs. Warning: 'Euler Ancestral', 'DPM2 Ancestral' and 'DPM++ 2s Ancestral' may produce slight variations in the image (if run in parallel), so we recommend using the other samplers.
|
||||
* 2.5.3 - 27 Dec 2022 - Fix broken drag-and-drop for text metadata files (as well as paste in clipboard).
|
||||
* 2.5.3 - 27 Dec 2022 - Allow upscaling by 2x as well as 4x.
|
||||
* 2.5.3 - 27 Dec 2022 - Fix broken renders on a second GPU.
|
||||
* 2.5.3 - 26 Dec 2022 - Add a `Remove` button on each image. Thanks @JeLuf.
|
||||
* 2.5.2 - 26 Dec 2022 - Fix broken inpainting if using non-square target images.
|
||||
* 2.5.2 - 26 Dec 2022 - Fix a bug where an incorrect model config would get used for some SD 2.1 models.
|
||||
* 2.5.2 - 26 Dec 2022 - Slight performance and memory improvement while rendering using SD 2.1 models.
|
||||
* 2.5.1 - 25 Dec 2022 - Allow custom config yaml files for models. You can put a config file (`.yaml`) next to the model file, with the same name as the model. For e.g. if you put `robo-diffusion-v2-base.yaml` next to `robo-diffusion-v2-base.ckpt`, it'll automatically use that config file.
|
||||
* 2.5.1 - 25 Dec 2022 - Fix broken rendering for SD 2.1-768 models. Fix broken rendering SD 2.0 safetensor models.
|
||||
* 2.5.0 - 25 Dec 2022 - Major new release! Nearly twice as fast, Full support for SD 2.1 (including low GPU RAM optimizations), 6 new samplers, Model Merging, Fast loading/unloading of VAEs, Database of known models, Color correction for img2img, Three GPU Memory Usage Settings, Save metadata as JSON, Major rewrite of the code, Name change.
|
||||
|
||||
## v2.4
|
||||
### Major Changes
|
||||
- **Allow reordering the task queue** (by dragging and dropping tasks). Thanks @madrang
|
||||
- **Automatic scanning for malicious model files** - using `picklescan`, and support for `safetensor` model format. Thanks @JeLuf
|
||||
- **Image Editor** - for drawing simple images for guiding the AI. Thanks @mdiller
|
||||
- **Use pre-trained hypernetworks** - for improving the quality of images. Thanks @C0bra5
|
||||
- **Support for custom VAE models**. You can place your VAE files in the `models/vae` folder, and refresh the browser page to use them. More info: https://github.com/easydiffusion/easydiffusion/wiki/VAE-Variational-Auto-Encoder
|
||||
- **Experimental support for multiple GPUs!** It should work automatically. Just open one browser tab per GPU, and spread your tasks across your GPUs. For e.g. open our UI in two browser tabs if you have two GPUs. You can customize which GPUs it should use in the "Settings" tab, otherwise let it automatically pick the best GPUs. Thanks @madrang . More info: https://github.com/easydiffusion/easydiffusion/wiki/Run-on-Multiple-GPUs
|
||||
- **Cleaner UI design** - Show settings and help in new tabs, instead of dropdown popups (which were buggy). Thanks @mdiller
|
||||
- **Progress bar.** Thanks @mdiller
|
||||
- **Custom Image Modifiers** - You can now save your custom image modifiers! Your saved modifiers can include special characters like `{}, (), [], |`
|
||||
- Drag and Drop **text files generated from previously saved images**, and copy settings to clipboard. Thanks @madrang
|
||||
- Paste settings from clipboard. Thanks @JeLuf
|
||||
- Bug fixes to reduce the chances of tasks crashing during long multi-hour runs (chrome can put long-running background tabs to sleep). Thanks @JeLuf and @madrang
|
||||
- **Improved documentation.** Thanks @JeLuf and @jsuelwald
|
||||
- Improved the codebase for dealing with system settings and UI settings. Thanks @mdiller
|
||||
- Help instructions next to some setttings, and in the tab
|
||||
- Show system info in the settings tab
|
||||
- Keyboard shortcut: Ctrl+Enter to start a task
|
||||
- Configuration to prevent the browser from opening on startup
|
||||
- Lots of minor bug fixes
|
||||
- A `What's New?` tab in the UI
|
||||
- Ask for a confimation before clearing the results pane or stopping a render task. The dialog can be skipped by holding down the shift key while clicking on the button.
|
||||
- Show the network addresses of the server in the systems setting dialog
|
||||
- Support loading models in the safetensor format, for improved safety
|
||||
|
||||
### Detailed changelog
|
||||
* 2.4.24 - 9 Jan 2022 - Urgent fix for failures on old/long-term-support browsers. Thanks @JeLuf.
|
||||
* 2.4.23/22 - 29 Dec 2022 - Allow rolling back from the upcoming v2.5 change (in beta).
|
||||
* 2.4.21 - 23 Dec 2022 - Speed up image creation, by removing a delay (regression) of 4-5 seconds between clicking the `Make Image` button and calling the server.
|
||||
* 2.4.20 - 22 Dec 2022 - `Pause All` button to pause all the pending tasks. Thanks @JeLuf
|
||||
* 2.4.20 - 22 Dec 2022 - `Undo`/`Redo` buttons in the image editor. Thanks @JeLuf
|
||||
* 2.4.20 - 22 Dec 2022 - Drag handle to reorder the tasks. This fixed a bug where the metadata was no longer selectable (for copying). Thanks @JeLuf
|
||||
* 2.4.19 - 17 Dec 2022 - Add Undo/Redo buttons in the Image Editor. Thanks @JeLuf
|
||||
* 2.4.19 - 10 Dec 2022 - Show init img in task list
|
||||
* 2.4.19 - 7 Dec 2022 - Use pre-trained hypernetworks while generating images. Thanks @C0bra5
|
||||
* 2.4.19 - 6 Dec 2022 - Allow processing new tasks first. Thanks @madrang
|
||||
* 2.4.19 - 6 Dec 2022 - Allow reordering the task queue (by dragging tasks). Thanks @madrang
|
||||
* 2.4.19 - 6 Dec 2022 - Re-organize the code, to make it easier to write user plugins. Thanks @madrang
|
||||
* 2.4.18 - 5 Dec 2022 - Make JPEG Output quality user controllable. Thanks @JeLuf
|
||||
* 2.4.18 - 5 Dec 2022 - Support loading models in the safetensor format, for improved safety. Thanks @JeLuf
|
||||
* 2.4.18 - 1 Dec 2022 - Image Editor, for drawing simple images for guiding the AI. Thanks @mdiller
|
||||
* 2.4.18 - 1 Dec 2022 - Disable an image modifier temporarily by right-clicking it. Thanks @patriceac
|
||||
* 2.4.17 - 30 Nov 2022 - Scroll to generated image. Thanks @patriceac
|
||||
* 2.4.17 - 30 Nov 2022 - Show the network addresses of the server in the systems setting dialog. Thanks @JeLuf
|
||||
* 2.4.17 - 30 Nov 2022 - Fix a bug where GFPGAN wouldn't work properly when multiple GPUs tried to run it at the same time. Thanks @madrang
|
||||
* 2.4.17 - 30 Nov 2022 - Confirm before stopping or clearing all the tasks. Thanks @JeLuf
|
||||
* 2.4.16 - 29 Nov 2022 - Bug fixes for SD 2.0 - remove the need for patching, default to SD 1.4 model if trying to load an SD2 model in SD1.4.
|
||||
* 2.4.15 - 25 Nov 2022 - Experimental support for SD 2.0. Uses lots of memory, not optimized, probably GPU-only.
|
||||
* 2.4.14 - 22 Nov 2022 - Change the backend to a custom fork of Stable Diffusion
|
||||
* 2.4.13 - 21 Nov 2022 - Change the modifier weight via mouse wheel, drag to reorder selected modifiers, and some more modifier-related fixes. Thanks @patriceac
|
||||
* 2.4.12 - 21 Nov 2022 - Another fix for improving how long images take to generate. Reduces the time taken for an enqueued task to start processing.
|
||||
* 2.4.11 - 21 Nov 2022 - Installer improvements: avoid crashing if the username contains a space or special characters, allow moving/renaming the folder after installation on Windows, whitespace fix on git apply
|
||||
* 2.4.11 - 21 Nov 2022 - Validate inputs before submitting the Image request
|
||||
* 2.4.11 - 19 Nov 2022 - New system settings to manage the network config (port number and whether to only listen on localhost)
|
||||
* 2.4.11 - 19 Nov 2022 - Address a regression in how long images take to generate. Use the previous code for moving a model to CPU. This improves things by a second or two per image, but we still have a regression (investigating).
|
||||
* 2.4.10 - 18 Nov 2022 - Textarea for negative prompts. Thanks @JeLuf
|
||||
* 2.4.10 - 18 Nov 2022 - Improved design for Settings, and rounded toggle buttons instead of checkboxes for a more modern look. Thanks @mdiller
|
||||
* 2.4.9 - 18 Nov 2022 - Add Picklescan - a scanner for malicious model files. If it finds a malicious file, it will halt the web application and alert the user. Thanks @JeLuf
|
||||
* 2.4.8 - 18 Nov 2022 - A `Use these settings` button to use the settings from a previously generated image task. Thanks @patriceac
|
||||
* 2.4.7 - 18 Nov 2022 - Don't crash if a VAE file fails to load
|
||||
* 2.4.7 - 17 Nov 2022 - Fix a bug where Face Correction (GFPGAN) would fail on cuda:N (i.e. GPUs other than cuda:0), as well as fail on CPU if the system had an incompatible GPU.
|
||||
* 2.4.6 - 16 Nov 2022 - Fix a regression in VRAM usage during startup, which caused 'Out of Memory' errors when starting on GPUs with 4gb (or less) VRAM
|
||||
* 2.4.5 - 16 Nov 2022 - Add checkbox for "Open browser on startup".
|
||||
* 2.4.5 - 16 Nov 2022 - Add a directory for core plugins that ship with Stable Diffusion UI by default.
|
||||
* 2.4.5 - 16 Nov 2022 - Add a "What's New?" tab as a core plugin, which fetches the contents of CHANGES.md from the app's release branch.
|
@ -1,24 +1,23 @@
|
||||
Hi there, these instructions are meant for the developers of this project.
|
||||
|
||||
If you only want to use the Stable Diffusion UI, you've downloaded the wrong file. In that case, please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation
|
||||
If you only want to use the Stable Diffusion UI, you've downloaded the wrong file. In that case, please download and follow the instructions at https://github.com/easydiffusion/easydiffusion#installation
|
||||
|
||||
Thanks
|
||||
|
||||
# For developers:
|
||||
|
||||
If you would like to contribute to this project, there is a discord for dicussion:
|
||||
If you would like to contribute to this project, there is a discord for discussion:
|
||||
[](https://discord.com/invite/u9yhsFmEkB)
|
||||
|
||||
## Development environment for UI (frontend and server) changes
|
||||
This is in-flux, but one way to get a development environment running for editing the UI of this project is:
|
||||
(swap `.sh` or `.bat` in instructions depending on your environment, and be sure to adjust any paths to match where you're working)
|
||||
|
||||
1) `git clone` the repository, e.g. to `/projects/stable-diffusion-ui-repo`
|
||||
2) Download the pre-built end user archive from the link on github, and extract it, e.g. to `/projects/stable-diffusion-ui-archive`
|
||||
3) `cd /projects/stable-diffusion-ui-archive` and run the script to set up and start the project, e.g. `start.sh`
|
||||
4) Check you can view and generate images on `localhost:9000`
|
||||
5) Close the server, and edit `/projects/stable-diffusion-ui-archive/scripts/on_env_start.sh`
|
||||
6) Comment out the lines near the bottom that copies the `files/ui` folder, e.g:
|
||||
1) Install the project to a new location using the [usual installation process](https://github.com/easydiffusion/easydiffusion#installation), e.g. to `/projects/stable-diffusion-ui-archive`
|
||||
2) Start the newly installed project, and check that you can view and generate images on `localhost:9000`
|
||||
3) Next, please clone the project repository using `git clone` (e.g. to `/projects/stable-diffusion-ui-repo`)
|
||||
4) Close the server (started in step 2), and edit `/projects/stable-diffusion-ui-archive/scripts/on_env_start.sh` (or `on_env_start.bat`)
|
||||
5) Comment out the lines near the bottom that copies the `files/ui` folder, e.g:
|
||||
|
||||
for `.sh`
|
||||
```
|
||||
@ -33,23 +32,18 @@ REM @xcopy sd-ui-files\ui ui /s /i /Y
|
||||
REM @copy sd-ui-files\scripts\on_sd_start.bat scripts\ /Y
|
||||
REM @copy "sd-ui-files\scripts\Start Stable Diffusion UI.cmd" . /Y
|
||||
```
|
||||
7) Comment out the line at the top of `/projects/stable-diffusion-ui-archive/scripts/on_sd_start.sh` that copies `on_env_start`. For e.g. `@copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y`
|
||||
6) Next, comment out the line at the top of `/projects/stable-diffusion-ui-archive/scripts/on_sd_start.sh` (or `on_sd_start.bat`) that copies `on_env_start`. For e.g. `@rem @copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y`
|
||||
8) Delete the current `ui` folder at `/projects/stable-diffusion-ui-archive/ui`
|
||||
9) Now make a symlink between the repository clone (where you will be making changes) and this archive (where you will be running stable diffusion):
|
||||
`ln -s /projects/stable-diffusion-ui-repo/ui /projects/stable-diffusion-ui-archive/ui`
|
||||
or for Windows
|
||||
`mklink /D \projects\stable-diffusion-ui-archive\ui \projects\stable-diffusion-ui-repo\ui` (link name first, source repo dir second)
|
||||
9) Run the archive again `start.sh` and ensure you can still use the UI.
|
||||
`mklink /J \projects\stable-diffusion-ui-archive\ui \projects\stable-diffusion-ui-repo\ui` (link name first, source repo dir second)
|
||||
9) Run the project again (like in step 2) and ensure you can still use the UI.
|
||||
10) Congrats, now any changes you make in your repo `ui` folder are linked to this running archive of the app and can be previewed in the browser.
|
||||
|
||||
Check the `ui/frontend/build/README.md` for instructions on running and building the React code.
|
||||
11) Please update CHANGES.md in your pull requests.
|
||||
|
||||
## Development environment for Installer changes
|
||||
Build the Windows installer using Windows, and the Linux installer using Linux. Don't mix the two, and don't use WSL. An Ubuntu VM is fine for building the Linux installer on a Windows host.
|
||||
|
||||
1. Install Miniconda 3 or Anaconda.
|
||||
2. Install `conda install -c conda-forge -y conda-pack`
|
||||
3. Open the Anaconda Prompt. Do not use WSL if you're building for Windows.
|
||||
4. Run `build.bat` or `./build.sh` depending on whether you're in Windows or Linux.
|
||||
5. Compress the `stable-diffusion-ui` folder created inside the `dist` folder. Make a `zip` for Windows, and `tar.xz` for Linux (smaller files, and Linux users already have tar).
|
||||
6. Make a new GitHub release and upload the Windows and Linux installer builds.
|
||||
1. Run `build.bat` or `./build.sh` depending on whether you're in Windows or Linux.
|
||||
2. Make a new GitHub release and upload the Windows and Linux installer builds created inside the `dist` folder.
|
||||
|
@ -1,15 +0,0 @@
|
||||
@echo off
|
||||
|
||||
echo "Opening Stable Diffusion UI - Developer Console.." & echo.
|
||||
|
||||
set SD_BASE_DIR=%cd%
|
||||
set MAMBA_ROOT_PREFIX=%SD_BASE_DIR%\env\mamba
|
||||
set INSTALL_ENV_DIR=%SD_BASE_DIR%\env\installer_env
|
||||
set PROJECT_ENV_DIR=%SD_BASE_DIR%\env\project_env
|
||||
|
||||
call "%MAMBA_ROOT_PREFIX%\condabin\mamba_hook.bat"
|
||||
|
||||
call micromamba activate "%INSTALL_ENV_DIR%"
|
||||
call micromamba activate "%PROJECT_ENV_DIR%"
|
||||
|
||||
cmd /k
|
24
How to install and run.txt
Normal file
@ -0,0 +1,24 @@
|
||||
Congrats on downloading Easy Diffusion, version 3!
|
||||
|
||||
If you haven't downloaded Easy Diffusion yet, please download from https://github.com/easydiffusion/easydiffusion#installation
|
||||
|
||||
After downloading, to install please follow these instructions:
|
||||
|
||||
For Windows:
|
||||
- Please double-click the "Easy-Diffusion-Windows.exe" file and follow the instructions.
|
||||
|
||||
For Linux and Mac:
|
||||
- Please open a terminal, and go to the "easy-diffusion" directory. Then run ./start.sh
|
||||
|
||||
That file will automatically install everything. After that it will start the Easy Diffusion interface in a web browser.
|
||||
|
||||
To start Easy Diffusion in the future, please run the same command mentioned above.
|
||||
|
||||
|
||||
If you have any problems, please:
|
||||
1. Try the troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting
|
||||
2. Or, seek help from the community at https://discord.com/invite/u9yhsFmEkB
|
||||
3. Or, file an issue at https://github.com/easydiffusion/easydiffusion/issues
|
||||
|
||||
Thanks
|
||||
cmdr2 (and contributors to the project)
|
1
NSIS/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.exe
|
1
NSIS/README.md
Normal file
@ -0,0 +1 @@
|
||||
Scripts to be used with the Nullsoft Scriptable Installation System
|
BIN
NSIS/astro.bmp
Normal file
After Width: | Height: | Size: 288 KiB |
BIN
NSIS/cyborg_flower_girl.bmp
Normal file
After Width: | Height: | Size: 565 KiB |
BIN
NSIS/cyborg_flower_girl.ico
Normal file
After Width: | Height: | Size: 223 KiB |
BIN
NSIS/cyborg_flower_girl_icon.png
Normal file
After Width: | Height: | Size: 454 KiB |
BIN
NSIS/cyborg_flower_girl_orig.jpeg
Normal file
After Width: | Height: | Size: 46 KiB |
1
NSIS/nsisconf.nsh
Normal file
@ -0,0 +1 @@
|
||||
!define EXISTING_INSTALLATION_DIR "D:\path\to\installed\easy-diffusion"
|
BIN
NSIS/sd.ico
Normal file
After Width: | Height: | Size: 200 KiB |
305
NSIS/sdui.nsi
Normal file
@ -0,0 +1,305 @@
|
||||
; Script generated by the HM NIS Edit Script Wizard.
|
||||
|
||||
Target amd64-unicode
|
||||
Unicode True
|
||||
SetCompressor /FINAL lzma
|
||||
RequestExecutionLevel user
|
||||
!AddPluginDir /amd64-unicode "."
|
||||
; HM NIS Edit Wizard helper defines
|
||||
!define PRODUCT_NAME "Easy Diffusion"
|
||||
!define PRODUCT_VERSION "2.5"
|
||||
!define PRODUCT_PUBLISHER "cmdr2 and contributors"
|
||||
!define PRODUCT_WEB_SITE "https://stable-diffusion-ui.github.io"
|
||||
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Easy Diffusion\App Paths\installer.exe"
|
||||
|
||||
; MUI 1.67 compatible ------
|
||||
!include "MUI.nsh"
|
||||
!include "LogicLib.nsh"
|
||||
!include "nsDialogs.nsh"
|
||||
|
||||
!include "nsisconf.nsh"
|
||||
|
||||
Var Dialog
|
||||
Var Label
|
||||
Var Button
|
||||
|
||||
Var InstDirLen
|
||||
Var LongPathsEnabled
|
||||
Var AccountType
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; This function returns the number of spaces in a string.
|
||||
; The string is passed on the stack (using Push $STRING)
|
||||
; The result is also returned on the stack and can be consumed with Pop $var
|
||||
; https://nsis.sourceforge.io/Check_for_spaces_in_a_directory_path
|
||||
Function CheckForSpaces
|
||||
Exch $R0
|
||||
Push $R1
|
||||
Push $R2
|
||||
Push $R3
|
||||
StrCpy $R1 -1
|
||||
StrCpy $R3 $R0
|
||||
StrCpy $R0 0
|
||||
loop:
|
||||
StrCpy $R2 $R3 1 $R1
|
||||
IntOp $R1 $R1 - 1
|
||||
StrCmp $R2 "" done
|
||||
StrCmp $R2 " " 0 loop
|
||||
IntOp $R0 $R0 + 1
|
||||
Goto loop
|
||||
done:
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
Exch $R0
|
||||
FunctionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; The function DirectoryLeave is called after the user chose the installation directory.
|
||||
; If it calls "abort", the user is sent back to choose a different directory.
|
||||
Function DirectoryLeave
|
||||
; check whether the installation directory path is longer than 30 characters.
|
||||
; If yes, we suggest to the user to enable long filename support
|
||||
;----------------------------------------------------------------------------
|
||||
StrLen $InstDirLen "$INSTDIR"
|
||||
|
||||
; Check whether the registry key that allows for >260 characters in a path name is set
|
||||
ReadRegStr $LongPathsEnabled HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled"
|
||||
|
||||
${If} $InstDirLen > 30
|
||||
${AndIf} $LongPathsEnabled == "0"
|
||||
; Check whether we're in the Admin group
|
||||
UserInfo::GetAccountType
|
||||
Pop $AccountType
|
||||
|
||||
${If} $AccountType == "Admin"
|
||||
${AndIf} ${Cmd} `MessageBox MB_YESNO|MB_ICONQUESTION 'The path name is too long. $\n$\nYou can either enable long file name support in Windows,$\nor you can go back and choose a different path.$\n$\nFor details see: shorturl.at/auBD1$\n$\nEnable long path name support in Windows?' IDYES`
|
||||
; Enable long path names
|
||||
WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1
|
||||
${Else}
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Installation path name too long. The installation path must not have more than 30 characters."
|
||||
abort
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
; Check for spaces in the installation directory path.
|
||||
; ----------------------------------------------------
|
||||
|
||||
; $R0 = CheckForSpaces( $INSTDIR )
|
||||
Push $INSTDIR # Input string (install path).
|
||||
Call CheckForSpaces
|
||||
Pop $R0 # The function returns the number of spaces found in the input string.
|
||||
|
||||
; Check if any spaces exist in $INSTDIR.
|
||||
${If} $R0 != 0
|
||||
; Plural if more than 1 space in $INSTDIR.
|
||||
; If $R0 == 1: $R1 = ""; else: $R1 = "s"
|
||||
StrCmp $R0 1 0 +3
|
||||
StrCpy $R1 ""
|
||||
Goto +2
|
||||
StrCpy $R1 "s"
|
||||
|
||||
; Show message box then take the user back to the Directory page.
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Error: The Installaton directory \
|
||||
has $R0 space character$R1.$\nPlease choose an installation directory without space characters."
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
; Check for NTFS filesystem. Installations on FAT fail.
|
||||
; -----------------------------------------------------
|
||||
StrCpy $5 $INSTDIR 3
|
||||
System::Call 'Kernel32::GetVolumeInformation(t "$5",t,i ${NSIS_MAX_STRLEN},*i,*i,*i,t.r1,i ${NSIS_MAX_STRLEN})i.r0'
|
||||
${If} $0 <> 0
|
||||
${AndIf} $1 != "NTFS"
|
||||
MessageBox mb_ok "$5 has filesystem type '$1'.$\nOnly NTFS filesystems are supported.$\nPlease choose a different drive."
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
FunctionEnd
|
||||
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; Open the MS download page in a browser and enable the [Next] button
|
||||
Function MSMediaFeaturepack
|
||||
ExecShell "open" "https://www.microsoft.com/en-us/software-download/mediafeaturepack"
|
||||
|
||||
GetDlgItem $0 $HWNDPARENT 1
|
||||
EnableWindow $0 1
|
||||
FunctionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; Install the MS Media Feature Pack, if it is missing (e.g. on Windows 10 N)
|
||||
Function MediaPackDialog
|
||||
!insertmacro MUI_HEADER_TEXT "Windows Media Feature Pack" "Required software module is missing"
|
||||
|
||||
; Skip this dialog if mf.dll is installed
|
||||
${If} ${FileExists} "$WINDIR\system32\mf.dll"
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
nsDialogs::Create 1018
|
||||
Pop $Dialog
|
||||
|
||||
${If} $Dialog == error
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
${NSD_CreateLabel} 0 0 100% 48u "The Windows Media Feature Pack is missing on this computer. It is required for Easy Diffusion.$\nYou can continue the installation after installing the Windows Media Feature Pack."
|
||||
Pop $Label
|
||||
|
||||
${NSD_CreateButton} 10% 49u 80% 12u "Download Meda Feature Pack from Microsoft"
|
||||
Pop $Button
|
||||
|
||||
GetFunctionAddress $0 MSMediaFeaturePack
|
||||
nsDialogs::OnClick $Button $0
|
||||
GetDlgItem $0 $HWNDPARENT 1
|
||||
EnableWindow $0 0
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
Function FinishPageAction
|
||||
CreateShortCut "$DESKTOP\Easy Diffusion.lnk" "$INSTDIR\Start Stable Diffusion UI.cmd" "" "$INSTDIR\installer_files\cyborg_flower_girl.ico"
|
||||
FunctionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; MUI Settings
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_ICON "cyborg_flower_girl.ico"
|
||||
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "cyborg_flower_girl.bmp"
|
||||
|
||||
; Welcome page
|
||||
!define MUI_WELCOMEPAGE_TEXT "This installer will guide you through the installation of Easy Diffusion.$\n$\n\
|
||||
Click Next to continue."
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
Page custom MediaPackDialog
|
||||
|
||||
; License page
|
||||
!insertmacro MUI_PAGE_LICENSE "..\LICENSE"
|
||||
!insertmacro MUI_PAGE_LICENSE "..\CreativeML Open RAIL-M License"
|
||||
; Directory page
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE "DirectoryLeave"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
|
||||
; Instfiles page
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
||||
; Finish page
|
||||
!define MUI_FINISHPAGE_SHOWREADME ""
|
||||
!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED
|
||||
!define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Desktop Shortcut"
|
||||
!define MUI_FINISHPAGE_SHOWREADME_FUNCTION FinishPageAction
|
||||
|
||||
!define MUI_FINISHPAGE_RUN "$INSTDIR\Start Stable Diffusion UI.cmd"
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
; Language files
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; MUI end
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
|
||||
OutFile "Install Easy Diffusion.exe"
|
||||
InstallDir "C:\EasyDiffusion\"
|
||||
InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
|
||||
ShowInstDetails show
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; List of files to be installed
|
||||
Section "MainSection" SEC01
|
||||
SetOutPath "$INSTDIR"
|
||||
File "..\CreativeML Open RAIL-M License"
|
||||
File "..\How to install and run.txt"
|
||||
File "..\LICENSE"
|
||||
File "..\scripts\Start Stable Diffusion UI.cmd"
|
||||
File /r "${EXISTING_INSTALLATION_DIR}\installer_files"
|
||||
File /r "${EXISTING_INSTALLATION_DIR}\profile"
|
||||
File /r "${EXISTING_INSTALLATION_DIR}\sd-ui-files"
|
||||
SetOutPath "$INSTDIR\installer_files"
|
||||
File "cyborg_flower_girl.ico"
|
||||
SetOutPath "$INSTDIR\scripts"
|
||||
File "${EXISTING_INSTALLATION_DIR}\scripts\install_status.txt"
|
||||
File "..\scripts\on_env_start.bat"
|
||||
File "C:\windows\system32\curl.exe"
|
||||
CreateDirectory "$INSTDIR\models"
|
||||
CreateDirectory "$INSTDIR\models\stable-diffusion"
|
||||
CreateDirectory "$INSTDIR\models\gfpgan"
|
||||
CreateDirectory "$INSTDIR\models\realesrgan"
|
||||
CreateDirectory "$INSTDIR\models\vae"
|
||||
CreateDirectory "$SMPROGRAMS\Easy Diffusion"
|
||||
CreateShortCut "$SMPROGRAMS\Easy Diffusion\Easy Diffusion.lnk" "$INSTDIR\Start Stable Diffusion UI.cmd" "" "$INSTDIR\installer_files\cyborg_flower_girl.ico"
|
||||
|
||||
DetailPrint 'Downloading the Stable Diffusion 1.4 model...'
|
||||
NScurl::http get "https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt" "$INSTDIR\models\stable-diffusion\sd-v1-4.ckpt" /CANCEL /INSIST /END
|
||||
|
||||
DetailPrint 'Downloading the GFPGAN model...'
|
||||
NScurl::http get "https://github.com/TencentARC/GFPGAN/releases/download/v1.3.4/GFPGANv1.4.pth" "$INSTDIR\models\gfpgan\GFPGANv1.4.pth" /CANCEL /INSIST /END
|
||||
|
||||
DetailPrint 'Downloading the RealESRGAN_x4plus model...'
|
||||
NScurl::http get "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth" "$INSTDIR\models\realesrgan\RealESRGAN_x4plus.pth" /CANCEL /INSIST /END
|
||||
|
||||
DetailPrint 'Downloading the RealESRGAN_x4plus_anime model...'
|
||||
NScurl::http get "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth" "$INSTDIR\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth" /CANCEL /INSIST /END
|
||||
|
||||
DetailPrint 'Downloading the default VAE (sd-vae-ft-mse-original) model...'
|
||||
NScurl::http get "https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.ckpt" "$INSTDIR\models\vae\vae-ft-mse-840000-ema-pruned.ckpt" /CANCEL /INSIST /END
|
||||
|
||||
DetailPrint 'Downloading the CLIP model (clip-vit-large-patch14)...'
|
||||
NScurl::http get "https://huggingface.co/openai/clip-vit-large-patch14/resolve/8d052a0f05efbaefbc9e8786ba291cfdf93e5bff/pytorch_model.bin" "$INSTDIR\profile\.cache\huggingface\hub\models--openai--clip-vit-large-patch14\snapshots\8d052a0f05efbaefbc9e8786ba291cfdf93e5bff\pytorch_model.bin" /CANCEL /INSIST /END
|
||||
|
||||
SectionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; Our installer only needs 25 KB, but once it has run, we need 25 GB
|
||||
; So we need to overwrite the automatically detected space requirements.
|
||||
; https://nsis.sourceforge.io/Docs/Chapter4.html#4.9.13.7
|
||||
; The example in section 4.9.13.7 seems to be wrong: the number
|
||||
; needs to be provided in Kilobytes.
|
||||
Function .onInit
|
||||
; Set required size of section 'SEC01' to 25 Gigabytes
|
||||
SectionSetSize ${SEC01} 26214400
|
||||
|
||||
|
||||
; Check system meory size. We need at least 8GB
|
||||
; ----------------------------------------------------
|
||||
|
||||
; allocate a few bytes of memory
|
||||
System::Alloc 64
|
||||
Pop $1
|
||||
|
||||
; Retrieve HW info from the Windows Kernel
|
||||
System::Call "*$1(i64)"
|
||||
System::Call "Kernel32::GlobalMemoryStatusEx(i r1)"
|
||||
; unpack the data into $R2 - $R10
|
||||
System::Call "*$1(i.r2, i.r3, l.r4, l.r5, l.r6, l.r7, l.r8, l.r9, l.r10)"
|
||||
|
||||
# free up the memory
|
||||
System::Free $1
|
||||
|
||||
; Result mapping:
|
||||
; "Structure size: $2 bytes"
|
||||
; "Memory load: $3%"
|
||||
; "Total physical memory: $4 bytes"
|
||||
; "Free physical memory: $5 bytes"
|
||||
; "Total page file: $6 bytes"
|
||||
; "Free page file: $7 bytes"
|
||||
; "Total virtual: $8 bytes"
|
||||
; "Free virtual: $9 bytes"
|
||||
|
||||
; Mem size in MB
|
||||
System::Int64Op $4 / 1048576
|
||||
Pop $4
|
||||
|
||||
${If} $4 < "8000"
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Warning!$\n$\nYour system has less than 8GB of memory (RAM).$\n$\n\
|
||||
You can still try to install Easy Diffusion,$\nbut it might have problems to start, or run$\nvery slowly."
|
||||
${EndIf}
|
||||
|
||||
FunctionEnd
|
||||
|
||||
|
||||
;Section -Post
|
||||
; WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\installer.exe"
|
||||
;SectionEnd
|
9
PRIVACY.md
Normal file
@ -0,0 +1,9 @@
|
||||
// placeholder until a more formal and legal-sounding privacy policy document is written. but the information below is true.
|
||||
|
||||
This is a summary of whether Easy Diffusion uses your data or tracks you:
|
||||
* The short answer is - Easy Diffusion does *not* use your data, and does *not* track you.
|
||||
* Easy Diffusion does not send your prompts or usage or analytics to anyone. There is no tracking. We don't even know how many people use Easy Diffusion, let alone their prompts.
|
||||
* Easy Diffusion fetches updates to the code whenever it starts up. It does this by contacting GitHub directly, via SSL (secure connection). Only your computer and GitHub and [this repository](https://github.com/easydiffusion/easydiffusion) are involved, and no third party is involved. Some countries intercepts SSL connections, that's not something we can do much about. GitHub does *not* share statistics (even with me) about how many people fetched code updates.
|
||||
* Easy Diffusion fetches the models from huggingface.co and github.com, if they don't exist on your PC. For e.g. if the safety checker (NSFW) model doesn't exist, it'll try to download it.
|
||||
* Easy Diffusion fetches code packages from pypi.org, which is the standard hosting service for all Python projects. That's where packages installed via `pip install` are stored.
|
||||
* Occasionally, antivirus software are known to *incorrectly* flag and delete some model files, which will result in Easy Diffusion re-downloading `pytorch_model.bin`. This *incorrect deletion* affects other Stable Diffusion UIs as well, like Invoke AI - https://itch.io/post/7509488
|
8
README BEFORE YOU RUN THIS.txt
Normal file
@ -0,0 +1,8 @@
|
||||
Hi there,
|
||||
|
||||
What you have downloaded is meant for the developers of this project, not for users.
|
||||
|
||||
If you only want to use the Stable Diffusion UI, you've downloaded the wrong file.
|
||||
Please download and follow the instructions at https://github.com/easydiffusion/easydiffusion#installation
|
||||
|
||||
Thanks
|
214
README.md
@ -1,107 +1,147 @@
|
||||
# Stable Diffusion UI v2
|
||||
### A simple 1-click way to install and use [Stable Diffusion](https://github.com/CompVis/stable-diffusion) on your own computer. No dependencies or technical knowledge required.
|
||||
# Easy Diffusion 3.0
|
||||
### The easiest way to install and use [Stable Diffusion](https://github.com/CompVis/stable-diffusion) on your computer.
|
||||
|
||||
<p float="left">
|
||||
<a href="#installation"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/develop/media/download-win.png" width="200" /></a>
|
||||
<a href="#installation"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/develop/media/download-linux.png" width="200" /></a>
|
||||
</p>
|
||||
Does not require technical knowledge, does not require pre-installed software. 1-click install, powerful features, friendly community.
|
||||
|
||||
[](https://discord.com/invite/u9yhsFmEkB) (for support, and development discussion) | [Troubleshooting guide for common problems](Troubleshooting.md)
|
||||
[Installation guide](#installation) | [Troubleshooting guide](https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting) | <sub>[](https://discord.com/invite/u9yhsFmEkB)</sub> <sup>(for support queries, and development discussions)</sup>
|
||||
|
||||
️🔥🎉 **New!** Use Custom Weights, Task Queue, Negative Prompt, Live Preview, More Samplers, In-Painting, Face Correction (GFPGAN) and Upscaling (RealESRGAN) have been added!
|
||||
---
|
||||

|
||||
|
||||
This distribution currently uses Stable Diffusion 1.4. Once the model for 1.5 becomes publicly available, the model in this distribution will be updated.
|
||||
|
||||
# Features in the new v2 Version:
|
||||
- **No Dependencies or Technical Knowledge Required**: 1-click install for Windows 10/11 and Linux. *No dependencies*, no need for WSL or Docker or Conda or technical setup. Just download and run!
|
||||
- **Face Correction (GFPGAN) and Upscaling (RealESRGAN)**
|
||||
- **In-Painting**
|
||||
- **Live Preview**: See the image as the AI is drawing it
|
||||
- **Task Queue**: Queue up all your ideas, without waiting for the current task to finish
|
||||
- **Custom Weights**: Use your own `.ckpt` file, by placing it inside the `stable-diffusion` folder (rename it to `custom-model.ckpt`)
|
||||
- **Negative Prompt**: Specify aspects of the image to *remove*.
|
||||
- **Lots of Samplers:** ddim, plms, heun, euler, euler_a, dpm2, dpm2_a, lms
|
||||
- **Image Modifiers**: A library of *modifier tags* like *"Realistic"*, *"Pencil Sketch"*, *"ArtStation"* etc. Experiment with various styles quickly.
|
||||
- **New UI**: with cleaner design
|
||||
- **Waifu Model Support**: Just replace the `stable-diffusion\sd-v1-4.ckpt` file after installation with the Waifu model
|
||||
- Supports "*Text to Image*" and "*Image to Image*"
|
||||
- **NSFW Setting**: A setting in the UI to control *NSFW content*
|
||||
- **Use CPU setting**: If you don't have a compatible graphics card, but still want to run it on your CPU.
|
||||
- **Auto-updater**: Gets you the latest improvements and bug-fixes to a rapidly evolving project.
|
||||
- **Low Memory Usage**: Creates 512x512 images with less than 4GB of VRAM!
|
||||
|
||||

|
||||
|
||||
## Live Preview
|
||||

|
||||
|
||||
|
||||
# System Requirements
|
||||
1. Windows 10/11, or Linux. Experimental support for Mac is coming soon.
|
||||
2. An NVIDIA graphics card, preferably with 4GB or more of VRAM. But if you don't have a compatible graphics card, you can still use it with a "Use CPU" setting. It'll be very slow, but it should still work.
|
||||
|
||||
You do not need anything else. You do not need WSL, Docker or Conda. The installer will take care of it.
|
||||
|
||||
# Installation
|
||||
1. **Download** [for Windows](https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.16/stable-diffusion-ui-win64.zip) or [for Linux](https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.16/stable-diffusion-ui-linux.tar.xz).
|
||||
Click the download button for your operating system:
|
||||
|
||||
2. **Extract**:
|
||||
- For Windows: After unzipping the file, please move the `stable-diffusion-ui` folder to your `C:` (or any drive like D:, at the top root level), e.g. `C:\stable-diffusion-ui`. This will avoid a common problem with Windows (file path length limits).
|
||||
- For Linux: After extracting the .tar.xz file, please open a terminal, and go to the `stable-diffusion-ui` directory.
|
||||
<p float="left">
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/releases/latest/download/Easy-Diffusion-Linux.zip"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/main/media/download-linux.png" width="200" /></a>
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/releases/latest/download/Easy-Diffusion-Mac.zip"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/main/media/download-mac.png" width="200" /></a>
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/releases/latest/download/Easy-Diffusion-Windows.exe"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/main/media/download-win.png" width="200" /></a>
|
||||
</p>
|
||||
|
||||
3. **Run**:
|
||||
- For Windows: `Start Stable Diffusion UI.cmd` by double-clicking it.
|
||||
- For Linux: In the terminal, run `./start.sh` (or `bash start.sh`)
|
||||
**Hardware requirements:**
|
||||
- **Windows:** NVIDIA graphics card¹ (minimum 2 GB RAM), or run on your CPU.
|
||||
- **Linux:** NVIDIA¹ or AMD² graphics card (minimum 2 GB RAM), or run on your CPU.
|
||||
- **Mac:** M1 or M2, or run on your CPU.
|
||||
- Minimum 8 GB of system RAM.
|
||||
- Atleast 25 GB of space on the hard disk.
|
||||
|
||||
This will automatically install Stable Diffusion, set it up, and start the interface. No additional steps are needed.
|
||||
¹) [CUDA Compute capability](https://en.wikipedia.org/wiki/CUDA#GPUs_supported) level of 3.7 or higher required.
|
||||
|
||||
**To Uninstall:** Just delete the `stable-diffusion-ui` folder to uninstall all the downloaded packages.
|
||||
²) ROCm 5.2 support required.
|
||||
|
||||
The installer will take care of whatever is needed. If you face any problems, you can join the friendly [Discord community](https://discord.com/invite/u9yhsFmEkB) and ask for assistance.
|
||||
|
||||
## On Windows:
|
||||
1. Run the downloaded `Easy-Diffusion-Windows.exe` file.
|
||||
2. Run `Easy Diffusion` once the installation finishes. You can also start from your Start Menu, or from your desktop (if you created a shortcut).
|
||||
|
||||
If Windows SmartScreen prevents you from running the program click `More info` and then `Run anyway`.
|
||||
|
||||
**Tip:** On Windows 10, please install at the top level in your drive, e.g. `C:\EasyDiffusion` or `D:\EasyDiffusion`. This will avoid a common problem with Windows 10 (file path length limits).
|
||||
|
||||
## On Linux/Mac:
|
||||
1. Unzip/extract the folder `easy-diffusion` which should be in your downloads folder, unless you changed your default downloads destination.
|
||||
2. Open a terminal window, and navigate to the `easy-diffusion` directory.
|
||||
3. Run `./start.sh` (or `bash start.sh`) in a terminal.
|
||||
|
||||
# To remove/uninstall:
|
||||
Just delete the `EasyDiffusion` folder to uninstall all the downloaded packages.
|
||||
|
||||
----
|
||||
|
||||
# Easy for new users, powerful features for advanced users
|
||||
## Features:
|
||||
|
||||
### User experience
|
||||
- **Hassle-free installation**: Does not require technical knowledge, does not require pre-installed software. Just download and run!
|
||||
- **Clutter-free UI**: A friendly and simple UI, while providing a lot of powerful features.
|
||||
- **Task Queue**: Queue up all your ideas, without waiting for the current task to finish.
|
||||
- **Intelligent Model Detection**: Automatically figures out the YAML config file to use for the chosen model (via a models database).
|
||||
- **Live Preview**: See the image as the AI is drawing it.
|
||||
- **Image Modifiers**: A library of *modifier tags* like *"Realistic"*, *"Pencil Sketch"*, *"ArtStation"* etc. Experiment with various styles quickly.
|
||||
- **Multiple Prompts File**: Queue multiple prompts by entering one prompt per line, or by running a text file.
|
||||
- **Save generated images to disk**: Save your images to your PC!
|
||||
- **UI Themes**: Customize the program to your liking.
|
||||
- **Searchable models dropdown**: organize your models into sub-folders, and search through them in the UI.
|
||||
|
||||
### Powerful image generation
|
||||
- **Supports**: "*Text to Image*", "*Image to Image*" and "*InPainting*"
|
||||
- **ControlNet**: For advanced control over the image, e.g. by setting the pose or drawing the outline for the AI to fill in.
|
||||
- **16 Samplers**: `PLMS`, `DDIM`, `DEIS`, `Heun`, `Euler`, `Euler Ancestral`, `DPM2`, `DPM2 Ancestral`, `LMS`, `DPM Solver`, `DPM++ 2s Ancestral`, `DPM++ 2m`, `DPM++ 2m SDE`, `DPM++ SDE`, `DDPM`, `UniPC`.
|
||||
- **Stable Diffusion XL and 2.1**: Generate higher-quality images using the latest Stable Diffusion XL models.
|
||||
- **Textual Inversion Embeddings**: For guiding the AI strongly towards a particular concept.
|
||||
- **Simple Drawing Tool**: Draw basic images to guide the AI, without needing an external drawing program.
|
||||
- **Face Correction (GFPGAN)**
|
||||
- **Upscaling (RealESRGAN)**
|
||||
- **Loopback**: Use the output image as the input image for the next image task.
|
||||
- **Negative Prompt**: Specify aspects of the image to *remove*.
|
||||
- **Attention/Emphasis**: `+` in the prompt increases the model's attention to enclosed words, and `-` decreases it. E.g. `apple++ falling from a tree`.
|
||||
- **Weighted Prompts**: Use weights for specific words in your prompt to change their importance, e.g. `(red)2.4 (dragon)1.2`.
|
||||
- **Prompt Matrix**: Quickly create multiple variations of your prompt, e.g. `a photograph of an astronaut riding a horse | illustration | cinematic lighting`.
|
||||
- **Prompt Set**: Quickly create multiple variations of your prompt, e.g. `a photograph of an astronaut on the {moon,earth}`
|
||||
- **1-click Upscale/Face Correction**: Upscale or correct an image after it has been generated.
|
||||
- **Make Similar Images**: Click to generate multiple variations of a generated image.
|
||||
- **NSFW Setting**: A setting in the UI to control *NSFW content*.
|
||||
- **JPEG/PNG/WEBP output**: Multiple file formats.
|
||||
|
||||
### Advanced features
|
||||
- **Custom Models**: Use your own `.ckpt` or `.safetensors` file, by placing it inside the `models/stable-diffusion` folder!
|
||||
- **Stable Diffusion XL and 2.1 support**
|
||||
- **Merge Models**
|
||||
- **Use custom VAE models**
|
||||
- **Textual Inversion Embeddings**
|
||||
- **ControlNet**
|
||||
- **Use custom GFPGAN models**
|
||||
- **UI Plugins**: Choose from a growing list of [community-generated UI plugins](https://github.com/easydiffusion/easydiffusion/wiki/UI-Plugins), or write your own plugin to add features to the project!
|
||||
|
||||
### Performance and security
|
||||
- **Fast**: Creates a 512x512 image with euler_a in 5 seconds, on an NVIDIA 3060 12GB.
|
||||
- **Low Memory Usage**: Create 512x512 images with less than 2 GB of GPU RAM, and 768x768 images with less than 3 GB of GPU RAM!
|
||||
- **Use CPU setting**: If you don't have a compatible graphics card, but still want to run it on your CPU.
|
||||
- **Multi-GPU support**: Automatically spreads your tasks across multiple GPUs (if available), for faster performance!
|
||||
- **Auto scan for malicious models**: Uses picklescan to prevent malicious models.
|
||||
- **Safetensors support**: Support loading models in the safetensor format, for improved safety.
|
||||
- **Auto-updater**: Gets you the latest improvements and bug-fixes to a rapidly evolving project.
|
||||
- **Developer Console**: A developer-mode for those who want to modify their Stable Diffusion code, and edit the conda environment.
|
||||
|
||||
**(and a lot more)**
|
||||
|
||||
----
|
||||
|
||||
## Easy for new users, powerful features for advanced users:
|
||||

|
||||
|
||||
## Task Queue
|
||||

|
||||
|
||||
|
||||
# Usage
|
||||
Open http://localhost:9000 in your browser (after running step 3 previously). It may take a few moments for the back-end to be ready.
|
||||
----
|
||||
|
||||
## With a text description
|
||||
1. Enter a text prompt, like `a photograph of an astronaut riding a horse` in the textbox.
|
||||
2. Press `Make Image`. This will take some time, depending on your system's processing power.
|
||||
3. See the image generated using your prompt.
|
||||
|
||||
## With an image
|
||||
1. Click `Browse..` next to `Initial Image`. Select your desired image.
|
||||
2. An optional text prompt can help you further describe the kind of image you want to generate.
|
||||
3. Press `Make Image`. See the image generated using your prompt.
|
||||
|
||||
You can use Face Correction or Upscaling to improve the image further.
|
||||
|
||||
**Pro tip:** You can also click `Use as Input` on a generated image, to use it as the input image for your next generation. This can be useful for sequentially refining the generated image with a single click.
|
||||
|
||||
**Another tip:** Images with the same aspect ratio of your generated image work best. E.g. 1:1 if you're generating images sized 512x512.
|
||||
|
||||
## Problems? Troubleshooting
|
||||
Please try the common [troubleshooting](Troubleshooting.md) steps. If that doesn't fix it, please ask on the [discord server](https://discord.com/invite/u9yhsFmEkB), or [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
|
||||
|
||||
# Image Settings
|
||||
You can also set the configuration like `seed`, `width`, `height`, `num_outputs`, `num_inference_steps` and `guidance_scale` using the 'show' button next to 'Image settings'.
|
||||
|
||||
Use the same `seed` number to get the same image for a certain prompt. This is useful for refining a prompt without losing the basic image design. Enable the `random images` checkbox to get random images.
|
||||
|
||||

|
||||
|
||||
# System Settings
|
||||
The system settings are reachable via the cogwheel symbol on the top right. It can be used to configure whether all generated images should
|
||||
saved be automically, or to tune the Stable Diffusion image generation.
|
||||
|
||||

|
||||
|
||||
# Image Modifiers
|
||||

|
||||
# How to use?
|
||||
Please refer to our [guide](https://github.com/easydiffusion/easydiffusion/wiki/How-to-Use) to understand how to use the features in this UI.
|
||||
|
||||
# Bugs reports and code contributions welcome
|
||||
If there are any problems or suggestions, please feel free to ask on the [discord server](https://discord.com/invite/u9yhsFmEkB) or [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
|
||||
If there are any problems or suggestions, please feel free to ask on the [discord server](https://discord.com/invite/u9yhsFmEkB) or [file an issue](https://github.com/easydiffusion/easydiffusion/issues).
|
||||
|
||||
Also, please feel free to submit a pull request, if you have any code contributions in mind. Join the [discord server](https://discord.com/invite/u9yhsFmEkB) for development-related discussions, and for helping other users.
|
||||
If you have any code contributions in mind, please feel free to say Hi to us on the [discord server](https://discord.com/invite/u9yhsFmEkB). We use the Discord server for development-related discussions, and for helping users.
|
||||
|
||||
# Credits
|
||||
* Stable Diffusion: https://github.com/Stability-AI/stablediffusion
|
||||
* CodeFormer: https://github.com/sczhou/CodeFormer (license: https://github.com/sczhou/CodeFormer/blob/master/LICENSE)
|
||||
* GFPGAN: https://github.com/TencentARC/GFPGAN
|
||||
* RealESRGAN: https://github.com/xinntao/Real-ESRGAN
|
||||
* k-diffusion: https://github.com/crowsonkb/k-diffusion
|
||||
* Code contributors and artists on the cmdr2 UI: https://github.com/cmdr2/stable-diffusion-ui and Discord (https://discord.com/invite/u9yhsFmEkB)
|
||||
* Lots of contributors on the internet
|
||||
|
||||
# Disclaimer
|
||||
The authors of this project are not responsible for any content generated using this interface.
|
||||
|
||||
The license of this software forbids you from sharing any content that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, spread misinformation, or target vulnerable groups. For the full list of restrictions please read [the license](LICENSE). You agree to these terms by using this software.
|
||||
The license of this software forbids you from sharing any content that:
|
||||
- Violates any laws.
|
||||
- Produces any harm to a person or persons.
|
||||
- Disseminates (spreads) any personal information that would be meant for harm.
|
||||
- Spreads misinformation.
|
||||
- Target vulnerable groups.
|
||||
|
||||
For the full list of restrictions please read [the License](LICENSE). You agree to these terms by using this software.
|
||||
|
@ -1,25 +0,0 @@
|
||||
@echo off
|
||||
|
||||
echo. & echo "Stable Diffusion UI - v2.5" & echo.
|
||||
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
set SD_BASE_DIR=%cd%
|
||||
|
||||
@rem Confirm or change the installation dir
|
||||
call installer\bootstrap\check-install-dir.bat
|
||||
|
||||
@rem set the vars again, if the installer dir has changed
|
||||
set SD_BASE_DIR=%cd%
|
||||
|
||||
echo Working in %SD_BASE_DIR%
|
||||
|
||||
@rem Setup the packages required for the installer
|
||||
call installer\bootstrap\bootstrap.bat
|
||||
|
||||
@rem Test the bootstrap
|
||||
call git --version
|
||||
call python --version
|
||||
|
||||
@rem Download the rest of the installer and UI
|
||||
call installer\installer\start.bat
|
@ -1,75 +0,0 @@
|
||||
Common issues and their solutions. If these solutions don't work, please feel free to ask at the [discord server](https://discord.com/invite/u9yhsFmEkB) or [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
|
||||
|
||||
## RuntimeError: CUDA out of memory
|
||||
This can happen if your PC has less than 6GB of VRAM.
|
||||
|
||||
Try disabling the "Turbo mode" setting under "Advanced Settings", since that takes an additional 1 GB of VRAM (to increase the speed).
|
||||
|
||||
Additionally, a common reason for this error is that you're using an initial image larger than 768x768 pixels. Try using a smaller initial image.
|
||||
|
||||
Also try generating smaller sized images.
|
||||
|
||||
## basicsr module not found
|
||||
For Windows: Please download and extract basicsr from [here](https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.16/basicsr-win64.zip), and place the `basicsr` folder inside the `stable-diffusion-ui\stable-diffusion\env\lib\site-packages` folder. Then run the `Start Stable Diffusion UI.cmd` file again.
|
||||
|
||||
For Linux: Please contact on the [discord server](https://discord.com/invite/u9yhsFmEkB).
|
||||
|
||||
## No ldm found, or antlr4 or any other missing module, or ClobberError: This transaction has incompatible packages due to a shared path
|
||||
On Windows, please ensure that you had placed the `stable-diffusion-ui` folder after unzipping to the root of C: or D: (or any drive). For e.g. `C:\stable-diffusion-ui`. **Note:** This has to be done **before** you start the installation process. If you have already installed (and are facing this error), please delete the installed folder, and start fresh by unzipping and placing the folder at the top of your drive.
|
||||
|
||||
This error can also be caused if you already have conda/miniconda/anaconda installed, due to package conflicts. Please open your Anaconda Prompt, and run `conda clean --all` to clean up unused packages.
|
||||
|
||||
If nothing works, this could be due to a corrupted installation. Please try reinstalling this, by deleting the installed folder, and unzipping from the downloaded zip file.
|
||||
|
||||
## Killed uvicorn server:app --app-dir ... --port 9000 --host 0.0.0.0
|
||||
This happens if your PC ran out of RAM. Stable Diffusion requires a lot of RAM, and requires atleast 10 GB of RAM to work well. You can also try closing all other applications before running Stable Diffusion UI.
|
||||
|
||||
## Green image generated
|
||||
This usually happens if you're running NVIDIA 1650 or 1660 Super. To solve this, please close and run the Stable Diffusion command on your computer. If you're using the older Docker-based solution (v1), please upgrade to v2: https://github.com/cmdr2/stable-diffusion-ui/tree/v2#installation
|
||||
|
||||
If you're still seeing this error, please try enabling "Full Precision" under "Advanced Settings" in the Stable Diffusion UI.
|
||||
|
||||
## './docker-compose.yml' is invalid:
|
||||
> ERROR: The Compose file './docker-compose.yml' is invalid because:
|
||||
> services.stability-ai.deploy.resources.reservations value Additional properties are not allowed ('devices' was unexpected)
|
||||
|
||||
Please ensure you have `docker-compose` version 1.29 or higher. Check `docker-compose --version`, and if required [update it to 1.29](https://docs.docker.com/compose/install/). (Thanks [HVRyan](https://github.com/HVRyan))
|
||||
|
||||
## RuntimeError: Found no NVIDIA driver on your system:
|
||||
If you have an NVIDIA GPU and the latest [NVIDIA driver](http://www.nvidia.com/Download/index.aspx), please ensure that you've installed [nvidia-container-toolkit](https://stackoverflow.com/a/58432877). (Thanks [u/exintrovert420](https://www.reddit.com/user/exintrovert420/))
|
||||
|
||||
## Some other process is already running at port 9000 / port 9000 could not be bound
|
||||
You can override the port used. Please change `docker-compose.yml` inside the project directory, and update the line `9000:9000` to `1337:9000` (where 1337 is whichever port number you want).
|
||||
|
||||
After doing this, please restart your server, by running `./server restart`.
|
||||
|
||||
After this, you can access the server at `http://localhost:1337` (where 1337 is the new port you specified earlier).
|
||||
|
||||
## RuntimeError: CUDA error: unknown error
|
||||
Please ensure that you have an NVIDIA GPU and the latest [NVIDIA driver](http://www.nvidia.com/Download/index.aspx), and that you've installed [nvidia-container-toolkit](https://stackoverflow.com/a/58432877).
|
||||
|
||||
Also, if you are using WSL (Windows), please ensure you have the latest WSL kernel by running `wsl --shutdown` and then `wsl --update`. (Thanks [AndrWeisR](https://github.com/AndrWeisR))
|
||||
|
||||
# For support queries
|
||||
## Entering a conda environment in an existing installation
|
||||
This will give you an activated conda environment in the terminal, so you can run commands and force-install any packages, if required.
|
||||
|
||||
Users don't need to have the Anaconda Prompt installed to do this anymore, since the installer bundles a portable version of conda inside it. Just follow these steps.
|
||||
|
||||
**Windows:**
|
||||
1. Open the terminal: Press Win+R, type "cmd", and press "Run"
|
||||
2. Type `cd C:\stable-diffusion-ui` and press enter (or wherever you've installed it)
|
||||
3. Type `installer\Scripts\activate.bat` and press enter
|
||||
4. Type `cd stable-diffusion` and press enter
|
||||
5. Type `conda activate .\env` and press enter
|
||||
6. Type `python --version` and press enter. You should see 3.8.5.
|
||||
|
||||
**Linux:**
|
||||
1. Open the terminal
|
||||
2. Type `cd /path/to/stable-diffusion-ui` and press enter
|
||||
3. Type `installer/bin/activate` and press enter
|
||||
4. Type `cd stable-diffusion` and press enter
|
||||
5. Type `conda activate ./env` and press enter
|
||||
6. Type `python --version` and press enter. You should see 3.8.5.
|
||||
|
||||
This will give you an activated conda environment. To confirm, type `python --version` and press enter. You should see 3.8.5.
|
48
build.bat
Normal file
@ -0,0 +1,48 @@
|
||||
@echo off
|
||||
|
||||
@echo "Hi there, what you are running is meant for the developers of this project, not for users." & echo.
|
||||
@echo "If you only want to use the Stable Diffusion UI, you've downloaded the wrong file."
|
||||
@echo "Please download and follow the instructions at https://github.com/easydiffusion/easydiffusion#installation" & echo.
|
||||
@echo "If you are actually a developer of this project, please type Y and press enter" & echo.
|
||||
|
||||
set /p answer=Are you a developer of this project (Y/N)?
|
||||
if /i "%answer:~,1%" NEQ "Y" exit /b
|
||||
|
||||
mkdir dist\win\stable-diffusion-ui\scripts
|
||||
@REM mkdir dist\linux-mac\stable-diffusion-ui\scripts
|
||||
|
||||
@rem copy the installer files for Windows
|
||||
|
||||
copy scripts\on_env_start.bat dist\win\stable-diffusion-ui\scripts\
|
||||
copy scripts\bootstrap.bat dist\win\stable-diffusion-ui\scripts\
|
||||
copy scripts\config.yaml.sample dist\win\stable-diffusion-ui\scripts\config.yaml
|
||||
copy "scripts\Start Stable Diffusion UI.cmd" dist\win\stable-diffusion-ui\
|
||||
copy LICENSE dist\win\stable-diffusion-ui\
|
||||
copy "CreativeML Open RAIL-M License" dist\win\stable-diffusion-ui\
|
||||
copy "How to install and run.txt" dist\win\stable-diffusion-ui\
|
||||
echo. > dist\win\stable-diffusion-ui\scripts\install_status.txt
|
||||
|
||||
@rem copy the installer files for Linux and Mac
|
||||
|
||||
@REM copy scripts\on_env_start.sh dist\linux-mac\stable-diffusion-ui\scripts\
|
||||
@REM copy scripts\bootstrap.sh dist\linux-mac\stable-diffusion-ui\scripts\
|
||||
@REM copy scripts\start.sh dist\linux-mac\stable-diffusion-ui\
|
||||
@REM copy LICENSE dist\linux-mac\stable-diffusion-ui\
|
||||
@REM copy "CreativeML Open RAIL-M License" dist\linux-mac\stable-diffusion-ui\
|
||||
@REM copy "How to install and run.txt" dist\linux-mac\stable-diffusion-ui\
|
||||
@REM echo. > dist\linux-mac\stable-diffusion-ui\scripts\install_status.txt
|
||||
|
||||
@rem make the zip
|
||||
|
||||
cd dist\win
|
||||
call powershell Compress-Archive -Path stable-diffusion-ui -DestinationPath ..\stable-diffusion-ui-windows.zip
|
||||
cd ..\..
|
||||
|
||||
@REM cd dist\linux-mac
|
||||
@REM call powershell Compress-Archive -Path stable-diffusion-ui -DestinationPath ..\stable-diffusion-ui-linux.zip
|
||||
@REM call powershell Compress-Archive -Path stable-diffusion-ui -DestinationPath ..\stable-diffusion-ui-mac.zip
|
||||
@REM cd ..\..
|
||||
|
||||
echo "Build ready. Upload the zip files inside the 'dist' folder."
|
||||
|
||||
pause
|
50
build.sh
Executable file
@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
|
||||
printf "Hi there, what you are running is meant for the developers of this project, not for users.\n\n"
|
||||
printf "If you only want to use the Stable Diffusion UI, you've downloaded the wrong file.\n"
|
||||
printf "Please download and follow the instructions at https://github.com/easydiffusion/easydiffusion#installation \n\n"
|
||||
printf "If you are actually a developer of this project, please type Y and press enter\n\n"
|
||||
|
||||
read -p "Are you a developer of this project (Y/N) " yn
|
||||
case $yn in
|
||||
[Yy]* ) ;;
|
||||
* ) exit;;
|
||||
esac
|
||||
|
||||
# mkdir -p dist/win/stable-diffusion-ui/scripts
|
||||
mkdir -p dist/linux-mac/stable-diffusion-ui/scripts
|
||||
|
||||
# copy the installer files for Windows
|
||||
|
||||
# cp scripts/on_env_start.bat dist/win/stable-diffusion-ui/scripts/
|
||||
# cp scripts/bootstrap.bat dist/win/stable-diffusion-ui/scripts/
|
||||
# cp "scripts/Start Stable Diffusion UI.cmd" dist/win/stable-diffusion-ui/
|
||||
# cp LICENSE dist/win/stable-diffusion-ui/
|
||||
# cp "CreativeML Open RAIL-M License" dist/win/stable-diffusion-ui/
|
||||
# cp "How to install and run.txt" dist/win/stable-diffusion-ui/
|
||||
# echo "" > dist/win/stable-diffusion-ui/scripts/install_status.txt
|
||||
|
||||
# copy the installer files for Linux and Mac
|
||||
|
||||
cp scripts/on_env_start.sh dist/linux-mac/stable-diffusion-ui/scripts/
|
||||
cp scripts/bootstrap.sh dist/linux-mac/stable-diffusion-ui/scripts/
|
||||
cp scripts/functions.sh dist/linux-mac/stable-diffusion-ui/scripts/
|
||||
cp scripts/config.yaml.sample dist/linux-mac/stable-diffusion-ui/scripts/config.yaml
|
||||
cp scripts/start.sh dist/linux-mac/stable-diffusion-ui/
|
||||
cp LICENSE dist/linux-mac/stable-diffusion-ui/
|
||||
cp "CreativeML Open RAIL-M License" dist/linux-mac/stable-diffusion-ui/
|
||||
cp "How to install and run.txt" dist/linux-mac/stable-diffusion-ui/
|
||||
echo "" > dist/linux-mac/stable-diffusion-ui/scripts/install_status.txt
|
||||
|
||||
# make the zip
|
||||
|
||||
# cd dist/win
|
||||
# zip -r ../stable-diffusion-ui-windows.zip stable-diffusion-ui
|
||||
# cd ../..
|
||||
|
||||
cd dist/linux-mac
|
||||
zip -r ../stable-diffusion-ui-linux.zip stable-diffusion-ui
|
||||
zip -r ../stable-diffusion-ui-mac.zip stable-diffusion-ui
|
||||
cd ../..
|
||||
|
||||
echo "Build ready. Upload the zip files inside the 'dist' folder."
|
@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$0" == "bash" ]; then
|
||||
echo "Opening Stable Diffusion UI - Developer Console.."
|
||||
echo ""
|
||||
|
||||
export SD_BASE_DIR=`pwd`
|
||||
export MAMBA_ROOT_PREFIX="$SD_BASE_DIR/env/mamba"
|
||||
export INSTALL_ENV_DIR="$SD_BASE_DIR/env/installer_env"
|
||||
export PROJECT_ENV_DIR="$SD_BASE_DIR/env/project_env"
|
||||
|
||||
eval "$($MAMBA_ROOT_PREFIX/micromamba shell hook -s posix)"
|
||||
|
||||
micromamba activate "$INSTALL_ENV_DIR"
|
||||
micromamba activate "$PROJECT_ENV_DIR"
|
||||
else
|
||||
bash --init-file developer_console.sh
|
||||
fi
|
@ -1,101 +0,0 @@
|
||||
import json
|
||||
|
||||
class Request:
|
||||
session_id: str = "session"
|
||||
prompt: str = ""
|
||||
negative_prompt: str = ""
|
||||
init_image: str = None # base64
|
||||
mask: str = None # base64
|
||||
num_outputs: int = 1
|
||||
num_inference_steps: int = 50
|
||||
guidance_scale: float = 7.5
|
||||
width: int = 512
|
||||
height: int = 512
|
||||
seed: int = 42
|
||||
prompt_strength: float = 0.8
|
||||
sampler: str = None # "ddim", "plms", "heun", "euler", "euler_a", "dpm2", "dpm2_a", "lms"
|
||||
# allow_nsfw: bool = False
|
||||
precision: str = "autocast" # or "full"
|
||||
save_to_disk_path: str = None
|
||||
turbo: bool = True
|
||||
use_cpu: bool = False
|
||||
use_full_precision: bool = False
|
||||
use_face_correction: str = None # or "GFPGANv1.3"
|
||||
use_upscale: str = None # or "RealESRGAN_x4plus" or "RealESRGAN_x4plus_anime_6B"
|
||||
show_only_filtered_image: bool = False
|
||||
|
||||
stream_progress_updates: bool = False
|
||||
stream_image_progress: bool = False
|
||||
|
||||
def json(self):
|
||||
return {
|
||||
"session_id": self.session_id,
|
||||
"prompt": self.prompt,
|
||||
"negative_prompt": self.negative_prompt,
|
||||
"num_outputs": self.num_outputs,
|
||||
"num_inference_steps": self.num_inference_steps,
|
||||
"guidance_scale": self.guidance_scale,
|
||||
"width": self.width,
|
||||
"height": self.height,
|
||||
"seed": self.seed,
|
||||
"prompt_strength": self.prompt_strength,
|
||||
"sampler": self.sampler,
|
||||
"use_face_correction": self.use_face_correction,
|
||||
"use_upscale": self.use_upscale,
|
||||
}
|
||||
|
||||
def to_string(self):
|
||||
return f'''
|
||||
session_id: {self.session_id}
|
||||
prompt: {self.prompt}
|
||||
negative_prompt: {self.negative_prompt}
|
||||
seed: {self.seed}
|
||||
num_inference_steps: {self.num_inference_steps}
|
||||
sampler: {self.sampler}
|
||||
guidance_scale: {self.guidance_scale}
|
||||
w: {self.width}
|
||||
h: {self.height}
|
||||
precision: {self.precision}
|
||||
save_to_disk_path: {self.save_to_disk_path}
|
||||
turbo: {self.turbo}
|
||||
use_cpu: {self.use_cpu}
|
||||
use_full_precision: {self.use_full_precision}
|
||||
use_face_correction: {self.use_face_correction}
|
||||
use_upscale: {self.use_upscale}
|
||||
show_only_filtered_image: {self.show_only_filtered_image}
|
||||
|
||||
stream_progress_updates: {self.stream_progress_updates}
|
||||
stream_image_progress: {self.stream_image_progress}'''
|
||||
|
||||
class Image:
|
||||
data: str # base64
|
||||
seed: int
|
||||
is_nsfw: bool
|
||||
path_abs: str = None
|
||||
|
||||
def __init__(self, data, seed):
|
||||
self.data = data
|
||||
self.seed = seed
|
||||
|
||||
def json(self):
|
||||
return {
|
||||
"data": self.data,
|
||||
"seed": self.seed,
|
||||
"path_abs": self.path_abs,
|
||||
}
|
||||
|
||||
class Response:
|
||||
request: Request
|
||||
images: list
|
||||
|
||||
def json(self):
|
||||
res = {
|
||||
"status": 'succeeded',
|
||||
"request": self.request.json(),
|
||||
"output": [],
|
||||
}
|
||||
|
||||
for image in self.images:
|
||||
res["output"].append(image.json())
|
||||
|
||||
return res
|
@ -1,658 +0,0 @@
|
||||
import json
|
||||
import os, re
|
||||
import traceback
|
||||
import torch
|
||||
import numpy as np
|
||||
from omegaconf import OmegaConf
|
||||
from PIL import Image, ImageOps
|
||||
from tqdm import tqdm, trange
|
||||
from itertools import islice
|
||||
from einops import rearrange
|
||||
import time
|
||||
from pytorch_lightning import seed_everything
|
||||
from torch import autocast
|
||||
from contextlib import nullcontext
|
||||
from einops import rearrange, repeat
|
||||
from ldm.util import instantiate_from_config
|
||||
from optimizedSD.optimUtils import split_weighted_subprompts
|
||||
from transformers import logging
|
||||
|
||||
from gfpgan import GFPGANer
|
||||
from basicsr.archs.rrdbnet_arch import RRDBNet
|
||||
from realesrgan import RealESRGANer
|
||||
|
||||
import uuid
|
||||
|
||||
logging.set_verbosity_error()
|
||||
|
||||
# consts
|
||||
config_yaml = "optimizedSD/v1-inference.yaml"
|
||||
filename_regex = re.compile('[^a-zA-Z0-9]')
|
||||
|
||||
# api stuff
|
||||
from . import Request, Response, Image as ResponseImage
|
||||
import base64
|
||||
from io import BytesIO
|
||||
#from colorama import Fore
|
||||
|
||||
# local
|
||||
stop_processing = False
|
||||
temp_images = {}
|
||||
|
||||
ckpt_file = None
|
||||
gfpgan_file = None
|
||||
real_esrgan_file = None
|
||||
|
||||
model = None
|
||||
modelCS = None
|
||||
modelFS = None
|
||||
model_gfpgan = None
|
||||
model_real_esrgan = None
|
||||
|
||||
model_is_half = False
|
||||
model_fs_is_half = False
|
||||
device = None
|
||||
unet_bs = 1
|
||||
precision = 'autocast'
|
||||
sampler_plms = None
|
||||
sampler_ddim = None
|
||||
|
||||
has_valid_gpu = False
|
||||
force_full_precision = False
|
||||
try:
|
||||
gpu = torch.cuda.current_device()
|
||||
gpu_name = torch.cuda.get_device_name(gpu)
|
||||
print('GPU detected: ', gpu_name)
|
||||
|
||||
force_full_precision = ('nvidia' in gpu_name.lower() or 'geforce' in gpu_name.lower()) and (' 1660' in gpu_name or ' 1650' in gpu_name) # otherwise these NVIDIA cards create green images
|
||||
if force_full_precision:
|
||||
print('forcing full precision on NVIDIA 16xx cards, to avoid green images. GPU detected: ', gpu_name)
|
||||
|
||||
mem_free, mem_total = torch.cuda.mem_get_info(gpu)
|
||||
mem_total /= float(10**9)
|
||||
if mem_total < 3.0:
|
||||
print("GPUs with less than 3 GB of VRAM are not compatible with Stable Diffusion")
|
||||
raise Exception()
|
||||
|
||||
has_valid_gpu = True
|
||||
except:
|
||||
print('WARNING: No compatible GPU found. Using the CPU, but this will be very slow!')
|
||||
pass
|
||||
|
||||
def load_model_ckpt(ckpt_to_use, device_to_use='cuda', turbo=False, unet_bs_to_use=1, precision_to_use='autocast', half_model_fs=False):
|
||||
global ckpt_file, model, modelCS, modelFS, model_is_half, device, unet_bs, precision, model_fs_is_half
|
||||
|
||||
ckpt_file = ckpt_to_use
|
||||
device = device_to_use if has_valid_gpu else 'cpu'
|
||||
precision = precision_to_use if not force_full_precision else 'full'
|
||||
unet_bs = unet_bs_to_use
|
||||
|
||||
if device == 'cpu':
|
||||
precision = 'full'
|
||||
|
||||
sd = load_model_from_config(f"{ckpt_file}.ckpt")
|
||||
li, lo = [], []
|
||||
for key, value in sd.items():
|
||||
sp = key.split(".")
|
||||
if (sp[0]) == "model":
|
||||
if "input_blocks" in sp:
|
||||
li.append(key)
|
||||
elif "middle_block" in sp:
|
||||
li.append(key)
|
||||
elif "time_embed" in sp:
|
||||
li.append(key)
|
||||
else:
|
||||
lo.append(key)
|
||||
for key in li:
|
||||
sd["model1." + key[6:]] = sd.pop(key)
|
||||
for key in lo:
|
||||
sd["model2." + key[6:]] = sd.pop(key)
|
||||
|
||||
config = OmegaConf.load(f"{config_yaml}")
|
||||
|
||||
model = instantiate_from_config(config.modelUNet)
|
||||
_, _ = model.load_state_dict(sd, strict=False)
|
||||
model.eval()
|
||||
model.cdevice = device
|
||||
model.unet_bs = unet_bs
|
||||
model.turbo = turbo
|
||||
|
||||
modelCS = instantiate_from_config(config.modelCondStage)
|
||||
_, _ = modelCS.load_state_dict(sd, strict=False)
|
||||
modelCS.eval()
|
||||
modelCS.cond_stage_model.device = device
|
||||
|
||||
modelFS = instantiate_from_config(config.modelFirstStage)
|
||||
_, _ = modelFS.load_state_dict(sd, strict=False)
|
||||
modelFS.eval()
|
||||
del sd
|
||||
|
||||
if device != "cpu" and precision == "autocast":
|
||||
model.half()
|
||||
modelCS.half()
|
||||
model_is_half = True
|
||||
else:
|
||||
model_is_half = False
|
||||
|
||||
if half_model_fs:
|
||||
modelFS.half()
|
||||
model_fs_is_half = True
|
||||
else:
|
||||
model_fs_is_half = False
|
||||
|
||||
print('loaded ', ckpt_file, 'to', device, 'precision', precision)
|
||||
|
||||
def load_model_gfpgan(gfpgan_to_use):
|
||||
global gfpgan_file, model_gfpgan
|
||||
|
||||
if gfpgan_to_use is None:
|
||||
return
|
||||
|
||||
gfpgan_file = gfpgan_to_use
|
||||
model_path = gfpgan_to_use + ".pth"
|
||||
|
||||
if device == 'cpu':
|
||||
model_gfpgan = GFPGANer(model_path=model_path, upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None, device=torch.device('cpu'))
|
||||
else:
|
||||
model_gfpgan = GFPGANer(model_path=model_path, upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None, device=torch.device('cuda'))
|
||||
|
||||
print('loaded ', gfpgan_to_use, 'to', device, 'precision', precision)
|
||||
|
||||
def load_model_real_esrgan(real_esrgan_to_use):
|
||||
global real_esrgan_file, model_real_esrgan
|
||||
|
||||
if real_esrgan_to_use is None:
|
||||
return
|
||||
|
||||
real_esrgan_file = real_esrgan_to_use
|
||||
model_path = real_esrgan_to_use + ".pth"
|
||||
|
||||
RealESRGAN_models = {
|
||||
'RealESRGAN_x4plus': RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4),
|
||||
'RealESRGAN_x4plus_anime_6B': RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=4)
|
||||
}
|
||||
|
||||
model_to_use = RealESRGAN_models[real_esrgan_to_use]
|
||||
|
||||
if device == 'cpu':
|
||||
model_real_esrgan = RealESRGANer(scale=2, model_path=model_path, model=model_to_use, pre_pad=0, half=False) # cpu does not support half
|
||||
model_real_esrgan.device = torch.device('cpu')
|
||||
model_real_esrgan.model.to('cpu')
|
||||
else:
|
||||
model_real_esrgan = RealESRGANer(scale=2, model_path=model_path, model=model_to_use, pre_pad=0, half=model_is_half)
|
||||
|
||||
model_real_esrgan.model.name = real_esrgan_to_use
|
||||
|
||||
print('loaded ', real_esrgan_to_use, 'to', device, 'precision', precision)
|
||||
|
||||
def mk_img(req: Request):
|
||||
try:
|
||||
yield from do_mk_img(req)
|
||||
except Exception as e:
|
||||
print(traceback.format_exc())
|
||||
|
||||
gc()
|
||||
|
||||
if device != "cpu":
|
||||
modelFS.to("cpu")
|
||||
modelCS.to("cpu")
|
||||
|
||||
model.model1.to("cpu")
|
||||
model.model2.to("cpu")
|
||||
|
||||
gc()
|
||||
|
||||
yield json.dumps({
|
||||
"status": 'failed',
|
||||
"detail": str(e)
|
||||
})
|
||||
|
||||
def do_mk_img(req: Request):
|
||||
global model, modelCS, modelFS, device
|
||||
global model_gfpgan, model_real_esrgan
|
||||
global stop_processing
|
||||
|
||||
stop_processing = False
|
||||
|
||||
res = Response()
|
||||
res.request = req
|
||||
res.images = []
|
||||
|
||||
temp_images.clear()
|
||||
|
||||
model.turbo = req.turbo
|
||||
if req.use_cpu:
|
||||
if device != 'cpu':
|
||||
device = 'cpu'
|
||||
|
||||
if model_is_half:
|
||||
del model, modelCS, modelFS
|
||||
load_model_ckpt(ckpt_file, device)
|
||||
|
||||
load_model_gfpgan(gfpgan_file)
|
||||
load_model_real_esrgan(real_esrgan_file)
|
||||
else:
|
||||
if has_valid_gpu:
|
||||
prev_device = device
|
||||
device = 'cuda'
|
||||
|
||||
if (precision == 'autocast' and (req.use_full_precision or not model_is_half)) or \
|
||||
(precision == 'full' and not req.use_full_precision and not force_full_precision) or \
|
||||
(req.init_image is None and model_fs_is_half) or \
|
||||
(req.init_image is not None and not model_fs_is_half and not force_full_precision):
|
||||
|
||||
del model, modelCS, modelFS
|
||||
load_model_ckpt(ckpt_file, device, req.turbo, unet_bs, ('full' if req.use_full_precision else 'autocast'), half_model_fs=(req.init_image is not None and not req.use_full_precision))
|
||||
|
||||
if prev_device != device:
|
||||
load_model_gfpgan(gfpgan_file)
|
||||
load_model_real_esrgan(real_esrgan_file)
|
||||
|
||||
if req.use_face_correction != gfpgan_file:
|
||||
load_model_gfpgan(req.use_face_correction)
|
||||
|
||||
if req.use_upscale != real_esrgan_file:
|
||||
load_model_real_esrgan(req.use_upscale)
|
||||
|
||||
model.cdevice = device
|
||||
modelCS.cond_stage_model.device = device
|
||||
|
||||
opt_prompt = req.prompt
|
||||
opt_seed = req.seed
|
||||
opt_n_samples = req.num_outputs
|
||||
opt_n_iter = 1
|
||||
opt_scale = req.guidance_scale
|
||||
opt_C = 4
|
||||
opt_H = req.height
|
||||
opt_W = req.width
|
||||
opt_f = 8
|
||||
opt_ddim_steps = req.num_inference_steps
|
||||
opt_ddim_eta = 0.0
|
||||
opt_strength = req.prompt_strength
|
||||
opt_save_to_disk_path = req.save_to_disk_path
|
||||
opt_init_img = req.init_image
|
||||
opt_use_face_correction = req.use_face_correction
|
||||
opt_use_upscale = req.use_upscale
|
||||
opt_show_only_filtered = req.show_only_filtered_image
|
||||
opt_format = 'png'
|
||||
opt_sampler_name = req.sampler
|
||||
|
||||
print(req.to_string(), '\n device', device)
|
||||
|
||||
print('\n\n Using precision:', precision)
|
||||
|
||||
seed_everything(opt_seed)
|
||||
|
||||
batch_size = opt_n_samples
|
||||
prompt = opt_prompt
|
||||
assert prompt is not None
|
||||
data = [batch_size * [prompt]]
|
||||
|
||||
if precision == "autocast" and device != "cpu":
|
||||
precision_scope = autocast
|
||||
else:
|
||||
precision_scope = nullcontext
|
||||
|
||||
mask = None
|
||||
|
||||
if req.init_image is None:
|
||||
handler = _txt2img
|
||||
|
||||
init_latent = None
|
||||
t_enc = None
|
||||
else:
|
||||
handler = _img2img
|
||||
|
||||
init_image = load_img(req.init_image, opt_W, opt_H)
|
||||
init_image = init_image.to(device)
|
||||
|
||||
if device != "cpu" and precision == "autocast":
|
||||
init_image = init_image.half()
|
||||
|
||||
modelFS.to(device)
|
||||
|
||||
init_image = repeat(init_image, '1 ... -> b ...', b=batch_size)
|
||||
init_latent = modelFS.get_first_stage_encoding(modelFS.encode_first_stage(init_image)) # move to latent space
|
||||
|
||||
if req.mask is not None:
|
||||
mask = load_mask(req.mask, opt_W, opt_H, init_latent.shape[2], init_latent.shape[3], True).to(device)
|
||||
mask = mask[0][0].unsqueeze(0).repeat(4, 1, 1).unsqueeze(0)
|
||||
mask = repeat(mask, '1 ... -> b ...', b=batch_size)
|
||||
|
||||
if device != "cpu" and precision == "autocast":
|
||||
mask = mask.half()
|
||||
|
||||
move_fs_to_cpu()
|
||||
|
||||
assert 0. <= opt_strength <= 1., 'can only work with strength in [0.0, 1.0]'
|
||||
t_enc = int(opt_strength * opt_ddim_steps)
|
||||
print(f"target t_enc is {t_enc} steps")
|
||||
|
||||
if opt_save_to_disk_path is not None:
|
||||
session_out_path = os.path.join(opt_save_to_disk_path, req.session_id)
|
||||
os.makedirs(session_out_path, exist_ok=True)
|
||||
else:
|
||||
session_out_path = None
|
||||
|
||||
seeds = ""
|
||||
with torch.no_grad():
|
||||
for n in trange(opt_n_iter, desc="Sampling"):
|
||||
for prompts in tqdm(data, desc="data"):
|
||||
|
||||
with precision_scope("cuda"):
|
||||
modelCS.to(device)
|
||||
uc = None
|
||||
if opt_scale != 1.0:
|
||||
uc = modelCS.get_learned_conditioning(batch_size * [req.negative_prompt])
|
||||
if isinstance(prompts, tuple):
|
||||
prompts = list(prompts)
|
||||
|
||||
subprompts, weights = split_weighted_subprompts(prompts[0])
|
||||
if len(subprompts) > 1:
|
||||
c = torch.zeros_like(uc)
|
||||
totalWeight = sum(weights)
|
||||
# normalize each "sub prompt" and add it
|
||||
for i in range(len(subprompts)):
|
||||
weight = weights[i]
|
||||
# if not skip_normalize:
|
||||
weight = weight / totalWeight
|
||||
c = torch.add(c, modelCS.get_learned_conditioning(subprompts[i]), alpha=weight)
|
||||
else:
|
||||
c = modelCS.get_learned_conditioning(prompts)
|
||||
|
||||
modelFS.to(device)
|
||||
|
||||
partial_x_samples = None
|
||||
def img_callback(x_samples, i):
|
||||
nonlocal partial_x_samples
|
||||
|
||||
partial_x_samples = x_samples
|
||||
|
||||
if req.stream_progress_updates:
|
||||
n_steps = opt_ddim_steps if req.init_image is None else t_enc
|
||||
progress = {"step": i, "total_steps": n_steps}
|
||||
|
||||
if req.stream_image_progress and i % 5 == 0:
|
||||
partial_images = []
|
||||
|
||||
for i in range(batch_size):
|
||||
x_samples_ddim = modelFS.decode_first_stage(x_samples[i].unsqueeze(0))
|
||||
x_sample = torch.clamp((x_samples_ddim + 1.0) / 2.0, min=0.0, max=1.0)
|
||||
x_sample = 255.0 * rearrange(x_sample[0].cpu().numpy(), "c h w -> h w c")
|
||||
x_sample = x_sample.astype(np.uint8)
|
||||
img = Image.fromarray(x_sample)
|
||||
buf = BytesIO()
|
||||
img.save(buf, format='JPEG')
|
||||
buf.seek(0)
|
||||
|
||||
del img, x_sample, x_samples_ddim
|
||||
# don't delete x_samples, it is used in the code that called this callback
|
||||
|
||||
temp_images[str(req.session_id) + '/' + str(i)] = buf
|
||||
partial_images.append({'path': f'/image/tmp/{req.session_id}/{i}'})
|
||||
|
||||
progress['output'] = partial_images
|
||||
|
||||
yield json.dumps(progress)
|
||||
|
||||
if stop_processing:
|
||||
raise UserInitiatedStop("User requested that we stop processing")
|
||||
|
||||
# run the handler
|
||||
try:
|
||||
if handler == _txt2img:
|
||||
x_samples = _txt2img(opt_W, opt_H, opt_n_samples, opt_ddim_steps, opt_scale, None, opt_C, opt_f, opt_ddim_eta, c, uc, opt_seed, img_callback, mask, opt_sampler_name)
|
||||
else:
|
||||
x_samples = _img2img(init_latent, t_enc, batch_size, opt_scale, c, uc, opt_ddim_steps, opt_ddim_eta, opt_seed, img_callback, mask)
|
||||
|
||||
yield from x_samples
|
||||
|
||||
x_samples = partial_x_samples
|
||||
except UserInitiatedStop:
|
||||
if partial_x_samples is None:
|
||||
continue
|
||||
|
||||
x_samples = partial_x_samples
|
||||
|
||||
print("saving images")
|
||||
for i in range(batch_size):
|
||||
|
||||
x_samples_ddim = modelFS.decode_first_stage(x_samples[i].unsqueeze(0))
|
||||
x_sample = torch.clamp((x_samples_ddim + 1.0) / 2.0, min=0.0, max=1.0)
|
||||
x_sample = 255.0 * rearrange(x_sample[0].cpu().numpy(), "c h w -> h w c")
|
||||
x_sample = x_sample.astype(np.uint8)
|
||||
img = Image.fromarray(x_sample)
|
||||
|
||||
has_filters = (opt_use_face_correction is not None and opt_use_face_correction.startswith('GFPGAN')) or \
|
||||
(opt_use_upscale is not None and opt_use_upscale.startswith('RealESRGAN'))
|
||||
|
||||
return_orig_img = not has_filters or not opt_show_only_filtered
|
||||
|
||||
if stop_processing:
|
||||
return_orig_img = True
|
||||
|
||||
if opt_save_to_disk_path is not None:
|
||||
prompt_flattened = filename_regex.sub('_', prompts[0])
|
||||
prompt_flattened = prompt_flattened[:50]
|
||||
|
||||
img_id = str(uuid.uuid4())[-8:]
|
||||
|
||||
file_path = f"{prompt_flattened}_{img_id}"
|
||||
img_out_path = os.path.join(session_out_path, f"{file_path}.{opt_format}")
|
||||
meta_out_path = os.path.join(session_out_path, f"{file_path}.txt")
|
||||
|
||||
if return_orig_img:
|
||||
save_image(img, img_out_path)
|
||||
|
||||
save_metadata(meta_out_path, prompts, opt_seed, opt_W, opt_H, opt_ddim_steps, opt_scale, opt_strength, opt_use_face_correction, opt_use_upscale, opt_sampler_name, req.negative_prompt)
|
||||
|
||||
if return_orig_img:
|
||||
img_data = img_to_base64_str(img)
|
||||
res_image_orig = ResponseImage(data=img_data, seed=opt_seed)
|
||||
res.images.append(res_image_orig)
|
||||
|
||||
if opt_save_to_disk_path is not None:
|
||||
res_image_orig.path_abs = img_out_path
|
||||
|
||||
del img
|
||||
|
||||
if has_filters and not stop_processing:
|
||||
print('Applying filters..')
|
||||
|
||||
gc()
|
||||
filters_applied = []
|
||||
|
||||
if opt_use_face_correction:
|
||||
_, _, output = model_gfpgan.enhance(x_sample[:,:,::-1], has_aligned=False, only_center_face=False, paste_back=True)
|
||||
x_sample = output[:,:,::-1]
|
||||
filters_applied.append(opt_use_face_correction)
|
||||
|
||||
if opt_use_upscale:
|
||||
output, _ = model_real_esrgan.enhance(x_sample[:,:,::-1])
|
||||
x_sample = output[:,:,::-1]
|
||||
filters_applied.append(opt_use_upscale)
|
||||
|
||||
filtered_image = Image.fromarray(x_sample)
|
||||
|
||||
filtered_img_data = img_to_base64_str(filtered_image)
|
||||
res_image_filtered = ResponseImage(data=filtered_img_data, seed=opt_seed)
|
||||
res.images.append(res_image_filtered)
|
||||
|
||||
filters_applied = "_".join(filters_applied)
|
||||
|
||||
if opt_save_to_disk_path is not None:
|
||||
filtered_img_out_path = os.path.join(session_out_path, f"{file_path}_{filters_applied}.{opt_format}")
|
||||
save_image(filtered_image, filtered_img_out_path)
|
||||
res_image_filtered.path_abs = filtered_img_out_path
|
||||
|
||||
del filtered_image
|
||||
|
||||
seeds += str(opt_seed) + ","
|
||||
opt_seed += 1
|
||||
|
||||
move_fs_to_cpu()
|
||||
gc()
|
||||
del x_samples, x_samples_ddim, x_sample
|
||||
print("memory_final = ", torch.cuda.memory_allocated() / 1e6)
|
||||
|
||||
print('Task completed')
|
||||
|
||||
yield json.dumps(res.json())
|
||||
|
||||
def save_image(img, img_out_path):
|
||||
try:
|
||||
img.save(img_out_path)
|
||||
except:
|
||||
print('could not save the file', traceback.format_exc())
|
||||
|
||||
def save_metadata(meta_out_path, prompts, opt_seed, opt_W, opt_H, opt_ddim_steps, opt_scale, opt_prompt_strength, opt_correct_face, opt_upscale, sampler_name, negative_prompt):
|
||||
metadata = f"{prompts[0]}\nWidth: {opt_W}\nHeight: {opt_H}\nSeed: {opt_seed}\nSteps: {opt_ddim_steps}\nGuidance Scale: {opt_scale}\nPrompt Strength: {opt_prompt_strength}\nUse Face Correction: {opt_correct_face}\nUse Upscaling: {opt_upscale}\nSampler: {sampler_name}\nNegative Prompt: {negative_prompt}"
|
||||
|
||||
try:
|
||||
with open(meta_out_path, 'w') as f:
|
||||
f.write(metadata)
|
||||
except:
|
||||
print('could not save the file', traceback.format_exc())
|
||||
|
||||
def _txt2img(opt_W, opt_H, opt_n_samples, opt_ddim_steps, opt_scale, start_code, opt_C, opt_f, opt_ddim_eta, c, uc, opt_seed, img_callback, mask, sampler_name):
|
||||
shape = [opt_n_samples, opt_C, opt_H // opt_f, opt_W // opt_f]
|
||||
|
||||
if device != "cpu":
|
||||
mem = torch.cuda.memory_allocated() / 1e6
|
||||
modelCS.to("cpu")
|
||||
while torch.cuda.memory_allocated() / 1e6 >= mem:
|
||||
time.sleep(1)
|
||||
|
||||
if sampler_name == 'ddim':
|
||||
model.make_schedule(ddim_num_steps=opt_ddim_steps, ddim_eta=opt_ddim_eta, verbose=False)
|
||||
|
||||
samples_ddim = model.sample(
|
||||
S=opt_ddim_steps,
|
||||
conditioning=c,
|
||||
seed=opt_seed,
|
||||
shape=shape,
|
||||
verbose=False,
|
||||
unconditional_guidance_scale=opt_scale,
|
||||
unconditional_conditioning=uc,
|
||||
eta=opt_ddim_eta,
|
||||
x_T=start_code,
|
||||
img_callback=img_callback,
|
||||
mask=mask,
|
||||
sampler = sampler_name,
|
||||
)
|
||||
|
||||
yield from samples_ddim
|
||||
|
||||
def _img2img(init_latent, t_enc, batch_size, opt_scale, c, uc, opt_ddim_steps, opt_ddim_eta, opt_seed, img_callback, mask):
|
||||
# encode (scaled latent)
|
||||
z_enc = model.stochastic_encode(
|
||||
init_latent,
|
||||
torch.tensor([t_enc] * batch_size).to(device),
|
||||
opt_seed,
|
||||
opt_ddim_eta,
|
||||
opt_ddim_steps,
|
||||
)
|
||||
x_T = None if mask is None else init_latent
|
||||
|
||||
# decode it
|
||||
samples_ddim = model.sample(
|
||||
t_enc,
|
||||
c,
|
||||
z_enc,
|
||||
unconditional_guidance_scale=opt_scale,
|
||||
unconditional_conditioning=uc,
|
||||
img_callback=img_callback,
|
||||
mask=mask,
|
||||
x_T=x_T,
|
||||
sampler = 'ddim'
|
||||
)
|
||||
|
||||
yield from samples_ddim
|
||||
|
||||
def move_fs_to_cpu():
|
||||
if device != "cpu":
|
||||
mem = torch.cuda.memory_allocated() / 1e6
|
||||
modelFS.to("cpu")
|
||||
while torch.cuda.memory_allocated() / 1e6 >= mem:
|
||||
time.sleep(1)
|
||||
|
||||
def gc():
|
||||
if device == 'cpu':
|
||||
return
|
||||
|
||||
torch.cuda.empty_cache()
|
||||
torch.cuda.ipc_collect()
|
||||
|
||||
# internal
|
||||
|
||||
def chunk(it, size):
|
||||
it = iter(it)
|
||||
return iter(lambda: tuple(islice(it, size)), ())
|
||||
|
||||
|
||||
def load_model_from_config(ckpt, verbose=False):
|
||||
print(f"Loading model from {ckpt}")
|
||||
pl_sd = torch.load(ckpt, map_location="cpu")
|
||||
if "global_step" in pl_sd:
|
||||
print(f"Global Step: {pl_sd['global_step']}")
|
||||
sd = pl_sd["state_dict"]
|
||||
return sd
|
||||
|
||||
# utils
|
||||
class UserInitiatedStop(Exception):
|
||||
pass
|
||||
|
||||
def load_img(img_str, w0, h0):
|
||||
image = base64_str_to_img(img_str).convert("RGB")
|
||||
w, h = image.size
|
||||
print(f"loaded input image of size ({w}, {h}) from base64")
|
||||
if h0 is not None and w0 is not None:
|
||||
h, w = h0, w0
|
||||
|
||||
w, h = map(lambda x: x - x % 64, (w, h)) # resize to integer multiple of 64
|
||||
image = image.resize((w, h), resample=Image.Resampling.LANCZOS)
|
||||
image = np.array(image).astype(np.float32) / 255.0
|
||||
image = image[None].transpose(0, 3, 1, 2)
|
||||
image = torch.from_numpy(image)
|
||||
return 2.*image - 1.
|
||||
|
||||
def load_mask(mask_str, h0, w0, newH, newW, invert=False):
|
||||
image = base64_str_to_img(mask_str).convert("RGB")
|
||||
w, h = image.size
|
||||
print(f"loaded input mask of size ({w}, {h})")
|
||||
|
||||
if invert:
|
||||
print("inverted")
|
||||
image = ImageOps.invert(image)
|
||||
# where_0, where_1 = np.where(image == 0), np.where(image == 255)
|
||||
# image[where_0], image[where_1] = 255, 0
|
||||
|
||||
if h0 is not None and w0 is not None:
|
||||
h, w = h0, w0
|
||||
|
||||
w, h = map(lambda x: x - x % 64, (w, h)) # resize to integer multiple of 64
|
||||
|
||||
print(f"New mask size ({w}, {h})")
|
||||
image = image.resize((newW, newH), resample=Image.Resampling.LANCZOS)
|
||||
image = np.array(image)
|
||||
|
||||
image = image.astype(np.float32) / 255.0
|
||||
image = image[None].transpose(0, 3, 1, 2)
|
||||
image = torch.from_numpy(image)
|
||||
return image
|
||||
|
||||
# https://stackoverflow.com/a/61114178
|
||||
def img_to_base64_str(img):
|
||||
buffered = BytesIO()
|
||||
img.save(buffered, format="PNG")
|
||||
buffered.seek(0)
|
||||
img_byte = buffered.getvalue()
|
||||
img_str = "data:image/png;base64," + base64.b64encode(img_byte).decode()
|
||||
return img_str
|
||||
|
||||
def base64_str_to_img(img_str):
|
||||
img_str = img_str[len("data:image/png;base64,"):]
|
||||
data = base64.b64decode(img_str)
|
||||
buffered = BytesIO(data)
|
||||
img = Image.open(buffered)
|
||||
return img
|
237
engine/server.py
@ -1,237 +0,0 @@
|
||||
import json
|
||||
import traceback
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
SCRIPT_DIR = os.getcwd()
|
||||
print('started in ', SCRIPT_DIR)
|
||||
|
||||
SD_UI_DIR = os.getenv('SD_UI_PATH', None)
|
||||
sys.path.append(os.path.dirname(SD_UI_DIR))
|
||||
|
||||
CONFIG_DIR = os.path.join(SD_UI_DIR, '..', 'scripts')
|
||||
|
||||
OUTPUT_DIRNAME = "Stable Diffusion UI" # in the user's home folder
|
||||
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from starlette.responses import FileResponse, StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
import logging
|
||||
|
||||
from sd_internal import Request, Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
model_loaded = False
|
||||
model_is_loading = False
|
||||
|
||||
modifiers_cache = None
|
||||
outpath = os.path.join(os.path.expanduser("~"), OUTPUT_DIRNAME)
|
||||
|
||||
# don't show access log entries for URLs that start with the given prefix
|
||||
ACCESS_LOG_SUPPRESS_PATH_PREFIXES = ['/ping', '/modifier-thumbnails']
|
||||
|
||||
app.mount('/media', StaticFiles(directory=os.path.join(SD_UI_DIR, 'media/')), name="media")
|
||||
|
||||
# defaults from https://huggingface.co/blog/stable_diffusion
|
||||
class ImageRequest(BaseModel):
|
||||
session_id: str = "session"
|
||||
prompt: str = ""
|
||||
negative_prompt: str = ""
|
||||
init_image: str = None # base64
|
||||
mask: str = None # base64
|
||||
num_outputs: int = 1
|
||||
num_inference_steps: int = 50
|
||||
guidance_scale: float = 7.5
|
||||
width: int = 512
|
||||
height: int = 512
|
||||
seed: int = 42
|
||||
prompt_strength: float = 0.8
|
||||
sampler: str = None # "ddim", "plms", "heun", "euler", "euler_a", "dpm2", "dpm2_a", "lms"
|
||||
# allow_nsfw: bool = False
|
||||
save_to_disk_path: str = None
|
||||
turbo: bool = True
|
||||
use_cpu: bool = False
|
||||
use_full_precision: bool = False
|
||||
use_face_correction: str = None # or "GFPGANv1.3"
|
||||
use_upscale: str = None # or "RealESRGAN_x4plus" or "RealESRGAN_x4plus_anime_6B"
|
||||
show_only_filtered_image: bool = False
|
||||
|
||||
stream_progress_updates: bool = False
|
||||
stream_image_progress: bool = False
|
||||
|
||||
class SetAppConfigRequest(BaseModel):
|
||||
update_branch: str = "main"
|
||||
|
||||
@app.get('/')
|
||||
def read_root():
|
||||
headers = {"Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0"}
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'index.html'), headers=headers)
|
||||
|
||||
@app.get('/ping')
|
||||
async def ping():
|
||||
global model_loaded, model_is_loading
|
||||
|
||||
try:
|
||||
if model_loaded:
|
||||
return {'OK'}
|
||||
|
||||
if model_is_loading:
|
||||
return {'ERROR'}
|
||||
|
||||
model_is_loading = True
|
||||
|
||||
from sd_internal import runtime
|
||||
|
||||
custom_weight_path = os.path.join(SCRIPT_DIR, 'custom-model.ckpt')
|
||||
ckpt_to_use = "sd-v1-4" if not os.path.exists(custom_weight_path) else "custom-model"
|
||||
runtime.load_model_ckpt(ckpt_to_use=ckpt_to_use)
|
||||
|
||||
model_loaded = True
|
||||
model_is_loading = False
|
||||
|
||||
return {'OK'}
|
||||
except Exception as e:
|
||||
print(traceback.format_exc())
|
||||
return HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post('/image')
|
||||
def image(req : ImageRequest):
|
||||
from sd_internal import runtime
|
||||
|
||||
r = Request()
|
||||
r.session_id = req.session_id
|
||||
r.prompt = req.prompt
|
||||
r.negative_prompt = req.negative_prompt
|
||||
r.init_image = req.init_image
|
||||
r.mask = req.mask
|
||||
r.num_outputs = req.num_outputs
|
||||
r.num_inference_steps = req.num_inference_steps
|
||||
r.guidance_scale = req.guidance_scale
|
||||
r.width = req.width
|
||||
r.height = req.height
|
||||
r.seed = req.seed
|
||||
r.prompt_strength = req.prompt_strength
|
||||
r.sampler = req.sampler
|
||||
# r.allow_nsfw = req.allow_nsfw
|
||||
r.turbo = req.turbo
|
||||
r.use_cpu = req.use_cpu
|
||||
r.use_full_precision = req.use_full_precision
|
||||
r.save_to_disk_path = req.save_to_disk_path
|
||||
r.use_upscale: str = req.use_upscale
|
||||
r.use_face_correction = req.use_face_correction
|
||||
r.show_only_filtered_image = req.show_only_filtered_image
|
||||
|
||||
r.stream_progress_updates = True # the underlying implementation only supports streaming
|
||||
r.stream_image_progress = req.stream_image_progress
|
||||
|
||||
try:
|
||||
if not req.stream_progress_updates:
|
||||
r.stream_image_progress = False
|
||||
|
||||
res = runtime.mk_img(r)
|
||||
|
||||
if req.stream_progress_updates:
|
||||
return StreamingResponse(res, media_type='application/json')
|
||||
else: # compatibility mode: buffer the streaming responses, and return the last one
|
||||
last_result = None
|
||||
|
||||
for result in res:
|
||||
last_result = result
|
||||
|
||||
return json.loads(last_result)
|
||||
except Exception as e:
|
||||
print(traceback.format_exc())
|
||||
return HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.get('/image/stop')
|
||||
def stop():
|
||||
try:
|
||||
if model_is_loading:
|
||||
return {'ERROR'}
|
||||
|
||||
from sd_internal import runtime
|
||||
runtime.stop_processing = True
|
||||
|
||||
return {'OK'}
|
||||
except Exception as e:
|
||||
print(traceback.format_exc())
|
||||
return HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.get('/image/tmp/{session_id}/{img_id}')
|
||||
def get_image(session_id, img_id):
|
||||
from sd_internal import runtime
|
||||
buf = runtime.temp_images[session_id + '/' + img_id]
|
||||
buf.seek(0)
|
||||
return StreamingResponse(buf, media_type='image/jpeg')
|
||||
|
||||
@app.post('/app_config')
|
||||
async def setAppConfig(req : SetAppConfigRequest):
|
||||
try:
|
||||
config = {
|
||||
'update_branch': req.update_branch
|
||||
}
|
||||
|
||||
config_json_str = json.dumps(config)
|
||||
config_bat_str = f'@set update_branch={req.update_branch}'
|
||||
config_sh_str = f'export update_branch={req.update_branch}'
|
||||
|
||||
config_json_path = os.path.join(CONFIG_DIR, 'config.json')
|
||||
config_bat_path = os.path.join(CONFIG_DIR, 'config.bat')
|
||||
config_sh_path = os.path.join(CONFIG_DIR, 'config.sh')
|
||||
|
||||
with open(config_json_path, 'w') as f:
|
||||
f.write(config_json_str)
|
||||
|
||||
with open(config_bat_path, 'w') as f:
|
||||
f.write(config_bat_str)
|
||||
|
||||
with open(config_sh_path, 'w') as f:
|
||||
f.write(config_sh_str)
|
||||
|
||||
return {'OK'}
|
||||
except Exception as e:
|
||||
print(traceback.format_exc())
|
||||
return HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.get('/app_config')
|
||||
def getAppConfig():
|
||||
try:
|
||||
config_json_path = os.path.join(CONFIG_DIR, 'config.json')
|
||||
|
||||
if not os.path.exists(config_json_path):
|
||||
return HTTPException(status_code=500, detail="No config file")
|
||||
|
||||
with open(config_json_path, 'r') as f:
|
||||
config_json_str = f.read()
|
||||
config = json.loads(config_json_str)
|
||||
return config
|
||||
except Exception as e:
|
||||
print(traceback.format_exc())
|
||||
return HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.get('/modifiers.json')
|
||||
def read_modifiers():
|
||||
headers = {"Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0"}
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'modifiers.json'), headers=headers)
|
||||
|
||||
@app.get('/output_dir')
|
||||
def read_home_dir():
|
||||
return {outpath}
|
||||
|
||||
# don't log certain requests
|
||||
class LogSuppressFilter(logging.Filter):
|
||||
def filter(self, record: logging.LogRecord) -> bool:
|
||||
path = record.getMessage()
|
||||
for prefix in ACCESS_LOG_SUPPRESS_PATH_PREFIXES:
|
||||
if path.find(prefix) != -1:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
logging.getLogger('uvicorn.access').addFilter(LogSuppressFilter())
|
||||
|
||||
# start the browser ui
|
||||
import webbrowser; webbrowser.open('http://localhost:9000')
|
@ -3,5 +3,5 @@ channels:
|
||||
- defaults
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- conda
|
||||
- git
|
||||
- python=3.10.5
|
@ -1,34 +0,0 @@
|
||||
@echo off
|
||||
|
||||
@rem This file initializes micromamba and activates the env.
|
||||
@rem A similar bootstrap file needs to exist for each platform (win, linux, macOS)
|
||||
@rem Ready to hand-over to the platform-independent installer after this (written in python).
|
||||
|
||||
set MAMBA_ROOT_PREFIX=%SD_BASE_DIR%\env\mamba
|
||||
set INSTALL_ENV_DIR=%SD_BASE_DIR%\env\installer_env
|
||||
set INSTALLER_YAML_FILE=%SD_BASE_DIR%\installer\yaml\installer-environment.yaml
|
||||
set MICROMAMBA_BINARY_FILE=%SD_BASE_DIR%\installer\bin\micromamba_win_x64.exe
|
||||
|
||||
@rem initialize the mamba dir
|
||||
if not exist "%MAMBA_ROOT_PREFIX%" mkdir "%MAMBA_ROOT_PREFIX%"
|
||||
|
||||
copy "%MICROMAMBA_BINARY_FILE%" "%MAMBA_ROOT_PREFIX%\micromamba.exe"
|
||||
|
||||
@rem test the mamba binary
|
||||
echo Micromamba version:
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" --version
|
||||
|
||||
@rem run the shell hook
|
||||
if not exist "%MAMBA_ROOT_PREFIX%\Scripts" (
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" shell hook --log-level 4 -s cmd.exe
|
||||
)
|
||||
|
||||
call "%MAMBA_ROOT_PREFIX%\condabin\mamba_hook.bat"
|
||||
|
||||
@rem create the installer env
|
||||
if not exist "%INSTALL_ENV_DIR%" (
|
||||
call micromamba create -y --prefix "%INSTALL_ENV_DIR%" -f "%INSTALLER_YAML_FILE%"
|
||||
)
|
||||
|
||||
@rem activate
|
||||
call micromamba activate "%INSTALL_ENV_DIR%"
|
@ -1,44 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This file initializes micromamba and activates the env.
|
||||
# A similar bootstrap file needs to exist for each platform (win, linux, macOS)
|
||||
# Ready to hand-over to the platform-independent installer after this (written in python).
|
||||
|
||||
OS_NAME=$(uname -s)
|
||||
case "${OS_NAME}" in
|
||||
Linux*) OS_NAME="linux";;
|
||||
Darwin*) OS_NAME="mac";;
|
||||
*) echo "Unknown OS: $OS_NAME! This only runs on Linux or Mac" && exit
|
||||
esac
|
||||
|
||||
OS_ARCH=$(uname -m)
|
||||
case "${OS_ARCH}" in
|
||||
x86_64*) OS_ARCH="x64";;
|
||||
arm64*) OS_ARCH="arm64";;
|
||||
*) echo "Unknown system architecture: $OS_ARCH! This only runs on x86_64 or arm64" && exit
|
||||
esac
|
||||
|
||||
export MAMBA_ROOT_PREFIX=$SD_BASE_DIR/env/mamba
|
||||
INSTALL_ENV_DIR=$SD_BASE_DIR/env/installer_env
|
||||
INSTALLER_YAML_FILE=$SD_BASE_DIR/installer/yaml/installer-environment.yaml
|
||||
MICROMAMBA_BINARY_FILE=$SD_BASE_DIR/installer/bin/micromamba_${OS_NAME}_${OS_ARCH}
|
||||
|
||||
# initialize the mamba dir
|
||||
mkdir -p "$MAMBA_ROOT_PREFIX"
|
||||
|
||||
cp "$MICROMAMBA_BINARY_FILE" "$MAMBA_ROOT_PREFIX/micromamba"
|
||||
|
||||
# test the mamba binary
|
||||
echo "Micromamba version:"
|
||||
"$MAMBA_ROOT_PREFIX/micromamba" --version
|
||||
|
||||
# run the shell hook
|
||||
eval "$($MAMBA_ROOT_PREFIX/micromamba shell hook -s posix)"
|
||||
|
||||
# create the installer env
|
||||
if [ ! -e "$INSTALL_ENV_DIR" ]; then
|
||||
micromamba create -y --prefix "$INSTALL_ENV_DIR" -f "$INSTALLER_YAML_FILE"
|
||||
fi
|
||||
|
||||
# activate
|
||||
micromamba activate "$INSTALL_ENV_DIR"
|
@ -1,21 +0,0 @@
|
||||
@echo off
|
||||
|
||||
if exist "%SD_BASE_DIR%\env" exit /b
|
||||
|
||||
set suggested_dir=%~d0\stable-diffusion-ui
|
||||
|
||||
echo "Please install Stable Diffusion UI at the root of your drive. This avoids problems with path length limits in Windows." & echo.
|
||||
set /p answer="Press Enter to install at %suggested_dir%, or type 'c' (without quotes) to install at the current location (press enter or type 'c'): "
|
||||
|
||||
if /i "%answer:~,1%" NEQ "c" (
|
||||
if exist "%suggested_dir%" (
|
||||
echo. & echo "Sorry, %suggested_dir% already exists! Cannot overwrite that folder!" & echo.
|
||||
pause
|
||||
exit
|
||||
)
|
||||
|
||||
xcopy "%SD_BASE_DIR%" "%suggested_dir%" /s /i /Y /Q
|
||||
echo Please run the %START_CMD_FILENAME% file inside %suggested_dir% . Do not use this folder anymore > "%SD_BASE_DIR%/READ_ME - DO_NOT_USE_THIS_FOLDER.txt"
|
||||
|
||||
cd %suggested_dir%
|
||||
)
|
@ -1,78 +0,0 @@
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
|
||||
config_path = os.path.join('config.json')
|
||||
|
||||
if not os.path.exists('LICENSE'):
|
||||
print('Error: This script needs to be run from the root of the stable-diffusion-ui folder! Please cd to the correct folder, and run this again.')
|
||||
exit(1)
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument(
|
||||
"--symlink_dir", type=str, default=None, help="the absolute path to the project git repository (to link to)"
|
||||
)
|
||||
opt = parser.parse_args()
|
||||
|
||||
def run(cmd):
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
|
||||
|
||||
for c in iter(lambda: p.stdout.read(1), b""):
|
||||
sys.stdout.buffer.write(c)
|
||||
sys.stdout.flush()
|
||||
|
||||
p.wait()
|
||||
|
||||
return p.returncode == 0
|
||||
|
||||
def get_config():
|
||||
if not os.path.exists(config_path):
|
||||
return {}
|
||||
|
||||
with open(config_path, "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
def save_config(config):
|
||||
with open(config_path, "w") as f:
|
||||
json.dump(config, f)
|
||||
|
||||
# set the `is_developer_mode` flag to `true` in the config
|
||||
config = get_config()
|
||||
config['is_developer_mode'] = True
|
||||
save_config(config)
|
||||
|
||||
print('set is_developer_mode=true in config.json')
|
||||
|
||||
# make the symlink, if requested
|
||||
if opt.symlink_dir is not None:
|
||||
if not os.path.exists(opt.symlink_dir):
|
||||
print(f'Symlink directory "{opt.symlink_dir}" was not found! Are you sure it has been escaped correctly?')
|
||||
exit(1)
|
||||
|
||||
installer_target_path = os.path.join(opt.symlink_dir, 'installer')
|
||||
ui_target_path = os.path.join(opt.symlink_dir, 'ui')
|
||||
engine_target_path = os.path.join(opt.symlink_dir, 'engine')
|
||||
|
||||
shutil.rmtree('installer', ignore_errors=True)
|
||||
shutil.rmtree('ui', ignore_errors=True)
|
||||
shutil.rmtree('engine', ignore_errors=True)
|
||||
|
||||
if not os.path.exists(ui_target_path) or not os.path.exists(installer_target_path) or not os.path.exists(engine_target_path):
|
||||
print('The target symlink directory does not contain the required {ui, installer, engine} folders. Are you sure it is the correct git repo for the project?')
|
||||
exit(1)
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
run(f'mklink /J "installer" "{installer_target_path}"')
|
||||
run(f'mklink /J "ui" "{ui_target_path}"')
|
||||
run(f'mklink /J "engine" "{engine_target_path}"')
|
||||
elif platform.system() in ('Linux', 'Darwin'):
|
||||
run(f'ln -s "{installer_target_path}" "installer"')
|
||||
run(f'ln -s "{ui_target_path}" "ui"')
|
||||
run(f'ln -s "{engine_target_path}" "engine"')
|
||||
|
||||
print(f'Created symlinks! Your installation will now automatically use the files present in the repository at {opt.symlink_dir}')
|
@ -1,70 +0,0 @@
|
||||
import os
|
||||
import json
|
||||
import platform
|
||||
|
||||
# config
|
||||
PROJECT_REPO_URL = 'https://github.com/cmdr2/stable-diffusion-ui.git'
|
||||
DEFAULT_PROJECT_BRANCH = 'installer_new'
|
||||
PROJECT_REPO_DIR_NAME = 'project_repo'
|
||||
|
||||
STABLE_DIFFUSION_REPO_URL = 'https://github.com/basujindal/stable-diffusion.git'
|
||||
DEFAULT_STABLE_DIFFUSION_COMMIT = 'f6cfebffa752ee11a7b07497b8529d5971de916c'
|
||||
STABLE_DIFFUSION_REPO_DIR_NAME = 'stable-diffusion'
|
||||
|
||||
PROJECT_ENV_DIR_NAME = 'project_env'
|
||||
|
||||
START_CMD_FILE_NAME = "Start Stable Diffusion UI.cmd" if platform.system() == "Windows" else "start.sh"
|
||||
DEV_CONSOLE_CMD_FILE_NAME = "Developer Console.cmd" if platform.system() == "Windows" else "developer_console.sh"
|
||||
CONFIG_FILE_NAME = 'config.json'
|
||||
|
||||
# top-level folders
|
||||
ENV_DIR_NAME = 'env'
|
||||
MODELS_DIR_NAME = 'models'
|
||||
|
||||
INSTALLER_DIR_NAME = 'installer'
|
||||
UI_DIR_NAME = 'ui'
|
||||
ENGINE_DIR_NAME = 'engine'
|
||||
|
||||
|
||||
# env
|
||||
SD_BASE_DIR = os.environ['SD_BASE_DIR']
|
||||
|
||||
|
||||
# model folders
|
||||
STABLE_DIFFUSION_MODELS_DIR_NAME = "stable-diffusion"
|
||||
GFPGAN_MODELS_DIR_NAME = "gfpgan"
|
||||
RealESRGAN_MODELS_DIR_NAME = "realesrgan"
|
||||
|
||||
# create references to dirs
|
||||
env_dir_path = os.path.join(SD_BASE_DIR, ENV_DIR_NAME)
|
||||
|
||||
installer_dir_path = os.path.join(SD_BASE_DIR, INSTALLER_DIR_NAME)
|
||||
ui_dir_path = os.path.join(SD_BASE_DIR, UI_DIR_NAME)
|
||||
engine_dir_path = os.path.join(SD_BASE_DIR, ENGINE_DIR_NAME)
|
||||
|
||||
project_repo_dir_path = os.path.join(env_dir_path, PROJECT_REPO_DIR_NAME)
|
||||
stable_diffusion_repo_dir_path = os.path.join(env_dir_path, STABLE_DIFFUSION_REPO_DIR_NAME)
|
||||
|
||||
project_env_dir_path = os.path.join(env_dir_path, PROJECT_ENV_DIR_NAME)
|
||||
|
||||
patches_dir_path = os.path.join(installer_dir_path, 'patches')
|
||||
|
||||
models_dir_path = os.path.join(SD_BASE_DIR, MODELS_DIR_NAME)
|
||||
stable_diffusion_models_dir_path = os.path.join(models_dir_path, STABLE_DIFFUSION_MODELS_DIR_NAME)
|
||||
gfpgan_models_dir_path = os.path.join(models_dir_path, GFPGAN_MODELS_DIR_NAME)
|
||||
realesrgan_models_dir_path = os.path.join(models_dir_path, RealESRGAN_MODELS_DIR_NAME)
|
||||
|
||||
|
||||
# useful functions
|
||||
def get_config():
|
||||
config_path = os.path.join(SD_BASE_DIR, CONFIG_FILE_NAME)
|
||||
if not os.path.exists(config_path):
|
||||
return {}
|
||||
|
||||
with open(config_path, "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
# app context
|
||||
config = get_config()
|
||||
activated_env_dir_path = None
|
@ -1,18 +0,0 @@
|
||||
'''
|
||||
This script is run by the `installer.helpers.modules_exist_in_env()` function
|
||||
'''
|
||||
|
||||
import sys
|
||||
import pkgutil
|
||||
|
||||
modules = sys.argv[1:]
|
||||
missing_modules = []
|
||||
for m in modules:
|
||||
if pkgutil.find_loader(m) is None:
|
||||
missing_modules.append(m)
|
||||
|
||||
if len(missing_modules) == 0:
|
||||
print('42')
|
||||
exit()
|
||||
|
||||
print('Missing modules', missing_modules)
|
@ -1,80 +0,0 @@
|
||||
import os
|
||||
from os import path
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
from installer import app
|
||||
|
||||
def run(cmd, run_in_folder=None, env=None, get_output=False, log_the_cmd=False):
|
||||
if app.activated_env_dir_path is not None and 'micromamba activate' not in cmd:
|
||||
cmd = f'micromamba activate "{app.activated_env_dir_path}" && {cmd}'
|
||||
|
||||
if run_in_folder is not None:
|
||||
cmd = f'cd "{run_in_folder}" && {cmd}'
|
||||
|
||||
if log_the_cmd:
|
||||
log('running: ' + cmd)
|
||||
|
||||
if get_output:
|
||||
p = subprocess.Popen(cmd, shell=True, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
else:
|
||||
p = subprocess.Popen(cmd, shell=True, env=env)
|
||||
|
||||
out, err = p.communicate()
|
||||
|
||||
out = out.decode('utf-8') if isinstance(out, bytes) else out
|
||||
err = err.decode('utf-8') if isinstance(out, bytes) else err
|
||||
|
||||
if get_output:
|
||||
return out, err
|
||||
|
||||
def log(msg):
|
||||
print(msg)
|
||||
|
||||
def modules_exist_in_env(modules, env_dir_path=app.project_env_dir_path):
|
||||
if not path.exists(env_dir_path):
|
||||
return False
|
||||
|
||||
check_modules_script_path = path.join(app.installer_dir_path, 'installer', 'check_modules.py')
|
||||
module_args = ' '.join(modules)
|
||||
check_modules_cmd = f'python "{check_modules_script_path}" {module_args}'
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PYTHONPATH'] = app.stable_diffusion_repo_dir_path + ';' + os.path.join(app.project_env_dir_path, 'lib', 'site-packages')
|
||||
|
||||
if app.activated_env_dir_path != env_dir_path:
|
||||
activate_cmd = f'micromamba activate "{env_dir_path}"'
|
||||
check_modules_cmd = f'{activate_cmd} && {check_modules_cmd}'
|
||||
|
||||
# activate and run the modules checker
|
||||
output, _ = run(check_modules_cmd, get_output=True, env=env)
|
||||
if 'Missing' in output:
|
||||
log(output)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def fail_with_install_error(error_msg):
|
||||
try:
|
||||
log(traceback.format_stack())
|
||||
log(f'''
|
||||
|
||||
Error: {error_msg}. Sorry about that, please try to:
|
||||
1. Run this installer again.
|
||||
2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/blob/main/Troubleshooting.md
|
||||
3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB
|
||||
4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues
|
||||
Thanks!''')
|
||||
except:
|
||||
pass
|
||||
|
||||
exit(1)
|
||||
|
||||
def apply_git_patches(repo_dir_path, patch_file_names):
|
||||
is_developer_mode = app.config.get('is_developer_mode', False)
|
||||
if is_developer_mode:
|
||||
return
|
||||
|
||||
for patch_file_name in patch_file_names:
|
||||
patch_file_path = path.join(app.patches_dir_path, patch_file_name)
|
||||
run(f"git apply {patch_file_path}", run_in_folder=repo_dir_path)
|
@ -1,34 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
||||
|
||||
from installer import helpers
|
||||
from installer.tasks import (
|
||||
fetch_project_repo,
|
||||
apply_project_update,
|
||||
fetch_stable_diffusion_repo,
|
||||
install_stable_diffusion_packages,
|
||||
install_ui_packages,
|
||||
download_weights,
|
||||
start_ui_server,
|
||||
)
|
||||
|
||||
tasks = [
|
||||
fetch_project_repo,
|
||||
apply_project_update,
|
||||
fetch_stable_diffusion_repo,
|
||||
install_stable_diffusion_packages,
|
||||
install_ui_packages,
|
||||
download_weights,
|
||||
start_ui_server,
|
||||
]
|
||||
|
||||
helpers.log(f'Starting Stable Diffusion UI at {datetime.now().strftime("%d/%m/%Y %H:%M:%S")}')
|
||||
|
||||
def run_tasks():
|
||||
for task in tasks:
|
||||
task.run()
|
||||
|
||||
run_tasks()
|
@ -1,8 +0,0 @@
|
||||
@echo off
|
||||
rem Never edit this file. If you really, really have to, beware that a script doesn't like
|
||||
rem being overwritten while it is running (the auto-updater will do that).
|
||||
rem The trick is to update this file while another script is running, and vice versa.
|
||||
|
||||
call python %SD_BASE_DIR%\installer\installer\main.py
|
||||
|
||||
pause
|
@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Never edit this file. If you really, really have to, beware that a script doesn't like
|
||||
# being overwritten while it is running (the auto-updater will do that).
|
||||
# The trick is to update this file while another script is running, and vice versa.
|
||||
|
||||
python $SD_BASE_DIR/installer/installer/main.py
|
||||
|
||||
read -p "Press enter to continue"
|
@ -1,30 +0,0 @@
|
||||
from os import path
|
||||
import shutil
|
||||
|
||||
from installer import app
|
||||
|
||||
def run():
|
||||
is_developer_mode = app.config.get('is_developer_mode', False)
|
||||
if is_developer_mode:
|
||||
return
|
||||
|
||||
installer_src_path = path.join(app.project_repo_dir_path, 'installer')
|
||||
ui_src_path = path.join(app.project_repo_dir_path, 'ui')
|
||||
engine_src_path = path.join(app.project_repo_dir_path, 'engine')
|
||||
|
||||
start_cmd_src_path = path.join(app.project_repo_dir_path, app.START_CMD_FILE_NAME)
|
||||
start_cmd_dst_path = path.join(app.SD_BASE_DIR, app.START_CMD_FILE_NAME)
|
||||
|
||||
dev_console_cmd_src_path = path.join(app.project_repo_dir_path, app.DEV_CONSOLE_CMD_FILE_NAME)
|
||||
dev_console_cmd_dst_path = path.join(app.SD_BASE_DIR, app.DEV_CONSOLE_CMD_FILE_NAME)
|
||||
|
||||
shutil.rmtree(app.installer_dir_path, ignore_errors=True)
|
||||
shutil.rmtree(app.ui_dir_path, ignore_errors=True)
|
||||
shutil.rmtree(app.engine_dir_path, ignore_errors=True)
|
||||
|
||||
shutil.copytree(installer_src_path, app.installer_dir_path, dirs_exist_ok=True)
|
||||
shutil.copytree(ui_src_path, app.ui_dir_path, dirs_exist_ok=True)
|
||||
shutil.copytree(engine_src_path, app.engine_dir_path, dirs_exist_ok=True)
|
||||
|
||||
shutil.copy(start_cmd_src_path, start_cmd_dst_path)
|
||||
shutil.copy(dev_console_cmd_src_path, dev_console_cmd_dst_path)
|
@ -1,46 +0,0 @@
|
||||
import os
|
||||
|
||||
from installer import app, helpers
|
||||
|
||||
def run():
|
||||
fetch_model('Stable Diffusion', 'sd-v1-4.ckpt', model_dir_path=app.stable_diffusion_models_dir_path, download_url='https://me.cmdr2.org/stable-diffusion-ui/sd-v1-4.ckpt', expected_file_sizes=[4265380512, 7703807346, 7703810927])
|
||||
fetch_model('Face Correction (GFPGAN)', 'GFPGANv1.4.pth', model_dir_path=app.gfpgan_models_dir_path, download_url='https://github.com/TencentARC/GFPGAN/releases/download/v1.3.4/GFPGANv1.4.pth', expected_file_sizes=[348632874])
|
||||
fetch_model('Resolution Upscale (RealESRGAN x4)', 'RealESRGAN_x4plus.pth', model_dir_path=app.realesrgan_models_dir_path, download_url='https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth', expected_file_sizes=[67040989])
|
||||
fetch_model('Resolution Upscale (RealESRGAN x4_anime)', 'RealESRGAN_x4plus_anime_6B.pth', model_dir_path=app.realesrgan_models_dir_path, download_url='https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth', expected_file_sizes=[17938799])
|
||||
|
||||
def fetch_model(model_type, file_name, model_dir_path, download_url, expected_file_sizes):
|
||||
os.makedirs(model_dir_path, exist_ok=True)
|
||||
|
||||
file_path = os.path.join(model_dir_path, file_name)
|
||||
|
||||
if model_exists(file_name, file_path, expected_file_sizes):
|
||||
helpers.log(f'Data files (weights) necessary for {model_type} were already downloaded')
|
||||
return
|
||||
|
||||
helpers.log(f'Downloading data files (weights) for {model_type}..')
|
||||
|
||||
helpers.run(f'curl -L -k "{download_url}" > "{file_path}"', log_the_cmd=True)
|
||||
|
||||
def model_exists(file_name, file_path, expected_file_sizes):
|
||||
legacy_file_path = os.path.join(app.stable_diffusion_repo_dir_path, file_name)
|
||||
|
||||
file_exists = os.path.exists(file_path)
|
||||
legacy_file_exists = os.path.exists(legacy_file_path)
|
||||
|
||||
if legacy_file_exists:
|
||||
file_size = os.path.getsize(legacy_file_path)
|
||||
if file_size in expected_file_sizes:
|
||||
return True
|
||||
|
||||
helpers.log(f'{file_name} is invalid. Was only {file_size} bytes in size. Downloading again..')
|
||||
os.remove(legacy_file_path)
|
||||
|
||||
if file_exists:
|
||||
file_size = os.path.getsize(file_path)
|
||||
if file_size in expected_file_sizes:
|
||||
return True
|
||||
|
||||
helpers.log(f'{file_name} is invalid. Was only {file_size} bytes in size. Downloading again..')
|
||||
os.remove(file_path)
|
||||
|
||||
return False
|
@ -1,27 +0,0 @@
|
||||
from os import path
|
||||
|
||||
from installer import app, helpers
|
||||
|
||||
project_repo_git_path = path.join(app.project_repo_dir_path, '.git')
|
||||
|
||||
def run():
|
||||
branch_name = app.config.get('update_branch', app.DEFAULT_PROJECT_BRANCH)
|
||||
|
||||
if path.exists(project_repo_git_path):
|
||||
helpers.log(f"Stable Diffusion UI's git repository was already installed. Updating from {branch_name}..")
|
||||
|
||||
helpers.run("git reset --hard", run_in_folder=app.project_repo_dir_path)
|
||||
helpers.run(f'git -c advice.detachedHead=false checkout "{branch_name}"', run_in_folder=app.project_repo_dir_path)
|
||||
helpers.run("git pull", run_in_folder=app.project_repo_dir_path)
|
||||
else:
|
||||
helpers.log("\nDownloading Stable Diffusion UI..\n")
|
||||
helpers.log(f"Using the {branch_name} channel\n")
|
||||
|
||||
helpers.run(f'git clone {app.PROJECT_REPO_URL} "{app.project_repo_dir_path}"')
|
||||
|
||||
if path.exists(project_repo_git_path):
|
||||
helpers.log("Downloaded Stable Diffusion UI")
|
||||
else:
|
||||
helpers.fail_with_install_error(error_msg="Could not download Stable Diffusion UI")
|
||||
|
||||
helpers.run(f'git -c advice.detachedHead=false checkout "{branch_name}"', run_in_folder=app.project_repo_dir_path)
|
@ -1,37 +0,0 @@
|
||||
from os import path
|
||||
|
||||
from installer import app, helpers
|
||||
|
||||
stable_diffusion_repo_git_path = path.join(app.stable_diffusion_repo_dir_path, '.git')
|
||||
|
||||
is_developer_mode = app.config.get('is_developer_mode', False)
|
||||
|
||||
def run():
|
||||
fetch_repo()
|
||||
|
||||
helpers.apply_git_patches(app.stable_diffusion_repo_dir_path, patch_file_names=(
|
||||
"sd_custom.patch",
|
||||
))
|
||||
|
||||
def fetch_repo():
|
||||
commit_id = app.config.get('stable_diffusion_commit', app.DEFAULT_STABLE_DIFFUSION_COMMIT)
|
||||
|
||||
if path.exists(stable_diffusion_repo_git_path):
|
||||
helpers.log(f"Stable Diffusion's git repository was already installed. Using commit: {commit_id}..")
|
||||
|
||||
if not is_developer_mode:
|
||||
helpers.run("git reset --hard", run_in_folder=app.stable_diffusion_repo_dir_path)
|
||||
helpers.run("git fetch origin", run_in_folder=app.stable_diffusion_repo_dir_path)
|
||||
helpers.run(f'git -c advice.detachedHead=false checkout "{commit_id}"', run_in_folder=app.stable_diffusion_repo_dir_path)
|
||||
else:
|
||||
helpers.log("\nDownloading Stable Diffusion..\n")
|
||||
helpers.log(f"Using commit: {commit_id}\n")
|
||||
|
||||
helpers.run(f'git clone {app.STABLE_DIFFUSION_REPO_URL} "{app.stable_diffusion_repo_dir_path}"')
|
||||
|
||||
if path.exists(stable_diffusion_repo_git_path):
|
||||
helpers.log("Downloaded Stable Diffusion")
|
||||
else:
|
||||
helpers.fail_with_install_error(error_msg="Could not download Stable Diffusion")
|
||||
|
||||
helpers.run(f'git -c advice.detachedHead=false checkout "{commit_id}"', run_in_folder=app.stable_diffusion_repo_dir_path)
|
@ -1,59 +0,0 @@
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
|
||||
from installer import app, helpers
|
||||
|
||||
def run():
|
||||
environment_file_path = get_environment_file_path()
|
||||
local_env_file_path = os.path.join(app.stable_diffusion_repo_dir_path, 'environment.yaml')
|
||||
|
||||
shutil.copy(environment_file_path, local_env_file_path)
|
||||
|
||||
if is_valid_env():
|
||||
helpers.log("Packages necessary for Stable Diffusion were already installed")
|
||||
return
|
||||
|
||||
log_installing_header()
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PYTHONNOUSERSITE'] = '1'
|
||||
|
||||
if not os.path.exists(app.project_env_dir_path):
|
||||
helpers.run(f'micromamba create --prefix {app.project_env_dir_path}', log_the_cmd=True)
|
||||
|
||||
helpers.run(f'micromamba install -y --prefix {app.project_env_dir_path} -f {local_env_file_path}', env=env, log_the_cmd=True, run_in_folder=app.stable_diffusion_repo_dir_path)
|
||||
|
||||
if is_valid_env():
|
||||
helpers.log("Installed the packages necessary for Stable Diffusion")
|
||||
|
||||
app.activated_env_dir_path = app.project_env_dir_path # so that future `run()` invocations will run in the activated env
|
||||
else:
|
||||
helpers.fail_with_install_error(error_msg="Could not install the packages necessary for Stable Diffusion")
|
||||
|
||||
apply_patches()
|
||||
|
||||
def apply_patches():
|
||||
gfpgan_repo_dir_path = os.path.join(app.stable_diffusion_repo_dir_path, 'src', 'gfpgan')
|
||||
helpers.apply_git_patches(gfpgan_repo_dir_path, patch_file_names=(
|
||||
"gfpgan_custom.patch",
|
||||
))
|
||||
|
||||
def get_environment_file_path():
|
||||
environment_file_name = 'sd-environment-win-linux-nvidia.yaml'
|
||||
if platform.system() == 'Darwin':
|
||||
environment_file_name = 'sd-environment-mac-nvidia.yaml'
|
||||
|
||||
return os.path.join(app.installer_dir_path, 'yaml', environment_file_name)
|
||||
|
||||
def log_installing_header():
|
||||
helpers.log('''
|
||||
|
||||
Downloading packages necessary for Stable Diffusion..
|
||||
|
||||
***** !! This will take some time (depending on the speed of the Internet connection) and may appear to be stuck, but please be patient *****
|
||||
|
||||
''')
|
||||
|
||||
def is_valid_env():
|
||||
return helpers.modules_exist_in_env(('torch', 'antlr4', 'transformers', 'numpy', 'gfpgan', 'realesrgan', 'basicsr'))
|
@ -1,39 +0,0 @@
|
||||
import os
|
||||
import shutil
|
||||
import platform
|
||||
|
||||
from installer import app, helpers
|
||||
|
||||
def run():
|
||||
if is_valid_env():
|
||||
helpers.log("Packages necessary for Stable Diffusion UI were already installed")
|
||||
return
|
||||
|
||||
log_installing_header()
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PYTHONNOUSERSITE'] = '1'
|
||||
|
||||
helpers.run(f'micromamba install -y --prefix {app.project_env_dir_path} -c conda-forge uvicorn fastapi', env=env, log_the_cmd=True)
|
||||
|
||||
if is_valid_env():
|
||||
helpers.log("Installed the packages necessary for Stable Diffusion UI")
|
||||
else:
|
||||
helpers.fail_with_install_error(error_msg="Could not install the packages necessary for Stable Diffusion UI")
|
||||
|
||||
def log_installing_header():
|
||||
helpers.log('''
|
||||
|
||||
Downloading packages necessary for Stable Diffusion UI..
|
||||
|
||||
''')
|
||||
|
||||
def is_valid_env():
|
||||
path = os.environ['PATH']
|
||||
path += ';' + os.path.join(app.project_env_dir_path, 'Scripts' if platform.system() == 'Windows' else 'bin')
|
||||
|
||||
if shutil.which("uvicorn", path=path) is None:
|
||||
helpers.log("uvicorn not found!")
|
||||
return False
|
||||
|
||||
return helpers.modules_exist_in_env(('uvicorn', 'fastapi'))
|
@ -1,23 +0,0 @@
|
||||
import os
|
||||
import platform
|
||||
|
||||
from installer import app, helpers
|
||||
|
||||
def run():
|
||||
helpers.log("\nStable Diffusion is ready!\n")
|
||||
|
||||
env = os.environ.copy()
|
||||
env['SD_DIR'] = app.stable_diffusion_repo_dir_path
|
||||
env['PYTHONPATH'] = app.stable_diffusion_repo_dir_path + ';' + os.path.join(app.project_env_dir_path, 'lib', 'site-packages')
|
||||
env['SD_UI_PATH'] = app.ui_dir_path
|
||||
env['PATH'] += ';' + os.path.join(app.project_env_dir_path, 'Scripts' if platform.system() == 'Windows' else 'bin')
|
||||
|
||||
helpers.log(f'PYTHONPATH={env["PYTHONPATH"]}')
|
||||
helpers.run('python --version', log_the_cmd=True)
|
||||
|
||||
host = app.config.get('host', 'localhost')
|
||||
port = app.config.get('port', '9000')
|
||||
|
||||
ui_server_cmd = f'uvicorn server:app --app-dir "{app.ui_dir_path}" --port {port} --host {host}'
|
||||
|
||||
helpers.run(ui_server_cmd, run_in_folder=app.stable_diffusion_repo_dir_path, log_the_cmd=True, env=env)
|
@ -1,22 +0,0 @@
|
||||
diff --git a/gfpgan/utils.py b/gfpgan/utils.py
|
||||
index 74ee5a8..1357f48 100644
|
||||
--- a/gfpgan/utils.py
|
||||
+++ b/gfpgan/utils.py
|
||||
@@ -117,14 +117,14 @@ class GFPGANer():
|
||||
# face restoration
|
||||
for cropped_face in self.face_helper.cropped_faces:
|
||||
# prepare data
|
||||
- cropped_face_t = img2tensor(cropped_face / 255., bgr2rgb=True, float32=True)
|
||||
+ cropped_face_t = img2tensor(cropped_face / 255., bgr2rgb=False, float32=True)
|
||||
normalize(cropped_face_t, (0.5, 0.5, 0.5), (0.5, 0.5, 0.5), inplace=True)
|
||||
cropped_face_t = cropped_face_t.unsqueeze(0).to(self.device)
|
||||
|
||||
try:
|
||||
- output = self.gfpgan(cropped_face_t, return_rgb=False, weight=weight)[0]
|
||||
+ output = self.gfpgan(cropped_face_t, return_rgb=True, weight=weight)[0]
|
||||
# convert to image
|
||||
- restored_face = tensor2img(output.squeeze(0), rgb2bgr=True, min_max=(-1, 1))
|
||||
+ restored_face = tensor2img(output.squeeze(0), rgb2bgr=False, min_max=(-1, 1))
|
||||
except RuntimeError as error:
|
||||
print(f'\tFailed inference for GFPGAN: {error}.')
|
||||
restored_face = cropped_face
|
@ -1,171 +0,0 @@
|
||||
{
|
||||
"_name_or_path": "clip-vit-large-patch14/",
|
||||
"architectures": [
|
||||
"CLIPModel"
|
||||
],
|
||||
"initializer_factor": 1.0,
|
||||
"logit_scale_init_value": 2.6592,
|
||||
"model_type": "clip",
|
||||
"projection_dim": 768,
|
||||
"text_config": {
|
||||
"_name_or_path": "",
|
||||
"add_cross_attention": false,
|
||||
"architectures": null,
|
||||
"attention_dropout": 0.0,
|
||||
"bad_words_ids": null,
|
||||
"bos_token_id": 0,
|
||||
"chunk_size_feed_forward": 0,
|
||||
"cross_attention_hidden_size": null,
|
||||
"decoder_start_token_id": null,
|
||||
"diversity_penalty": 0.0,
|
||||
"do_sample": false,
|
||||
"dropout": 0.0,
|
||||
"early_stopping": false,
|
||||
"encoder_no_repeat_ngram_size": 0,
|
||||
"eos_token_id": 2,
|
||||
"finetuning_task": null,
|
||||
"forced_bos_token_id": null,
|
||||
"forced_eos_token_id": null,
|
||||
"hidden_act": "quick_gelu",
|
||||
"hidden_size": 768,
|
||||
"id2label": {
|
||||
"0": "LABEL_0",
|
||||
"1": "LABEL_1"
|
||||
},
|
||||
"initializer_factor": 1.0,
|
||||
"initializer_range": 0.02,
|
||||
"intermediate_size": 3072,
|
||||
"is_decoder": false,
|
||||
"is_encoder_decoder": false,
|
||||
"label2id": {
|
||||
"LABEL_0": 0,
|
||||
"LABEL_1": 1
|
||||
},
|
||||
"layer_norm_eps": 1e-05,
|
||||
"length_penalty": 1.0,
|
||||
"max_length": 20,
|
||||
"max_position_embeddings": 77,
|
||||
"min_length": 0,
|
||||
"model_type": "clip_text_model",
|
||||
"no_repeat_ngram_size": 0,
|
||||
"num_attention_heads": 12,
|
||||
"num_beam_groups": 1,
|
||||
"num_beams": 1,
|
||||
"num_hidden_layers": 12,
|
||||
"num_return_sequences": 1,
|
||||
"output_attentions": false,
|
||||
"output_hidden_states": false,
|
||||
"output_scores": false,
|
||||
"pad_token_id": 1,
|
||||
"prefix": null,
|
||||
"problem_type": null,
|
||||
"projection_dim" : 768,
|
||||
"pruned_heads": {},
|
||||
"remove_invalid_values": false,
|
||||
"repetition_penalty": 1.0,
|
||||
"return_dict": true,
|
||||
"return_dict_in_generate": false,
|
||||
"sep_token_id": null,
|
||||
"task_specific_params": null,
|
||||
"temperature": 1.0,
|
||||
"tie_encoder_decoder": false,
|
||||
"tie_word_embeddings": true,
|
||||
"tokenizer_class": null,
|
||||
"top_k": 50,
|
||||
"top_p": 1.0,
|
||||
"torch_dtype": null,
|
||||
"torchscript": false,
|
||||
"transformers_version": "4.16.0.dev0",
|
||||
"use_bfloat16": false,
|
||||
"vocab_size": 49408
|
||||
},
|
||||
"text_config_dict": {
|
||||
"hidden_size": 768,
|
||||
"intermediate_size": 3072,
|
||||
"num_attention_heads": 12,
|
||||
"num_hidden_layers": 12,
|
||||
"projection_dim": 768
|
||||
},
|
||||
"torch_dtype": "float32",
|
||||
"transformers_version": null,
|
||||
"vision_config": {
|
||||
"_name_or_path": "",
|
||||
"add_cross_attention": false,
|
||||
"architectures": null,
|
||||
"attention_dropout": 0.0,
|
||||
"bad_words_ids": null,
|
||||
"bos_token_id": null,
|
||||
"chunk_size_feed_forward": 0,
|
||||
"cross_attention_hidden_size": null,
|
||||
"decoder_start_token_id": null,
|
||||
"diversity_penalty": 0.0,
|
||||
"do_sample": false,
|
||||
"dropout": 0.0,
|
||||
"early_stopping": false,
|
||||
"encoder_no_repeat_ngram_size": 0,
|
||||
"eos_token_id": null,
|
||||
"finetuning_task": null,
|
||||
"forced_bos_token_id": null,
|
||||
"forced_eos_token_id": null,
|
||||
"hidden_act": "quick_gelu",
|
||||
"hidden_size": 1024,
|
||||
"id2label": {
|
||||
"0": "LABEL_0",
|
||||
"1": "LABEL_1"
|
||||
},
|
||||
"image_size": 224,
|
||||
"initializer_factor": 1.0,
|
||||
"initializer_range": 0.02,
|
||||
"intermediate_size": 4096,
|
||||
"is_decoder": false,
|
||||
"is_encoder_decoder": false,
|
||||
"label2id": {
|
||||
"LABEL_0": 0,
|
||||
"LABEL_1": 1
|
||||
},
|
||||
"layer_norm_eps": 1e-05,
|
||||
"length_penalty": 1.0,
|
||||
"max_length": 20,
|
||||
"min_length": 0,
|
||||
"model_type": "clip_vision_model",
|
||||
"no_repeat_ngram_size": 0,
|
||||
"num_attention_heads": 16,
|
||||
"num_beam_groups": 1,
|
||||
"num_beams": 1,
|
||||
"num_hidden_layers": 24,
|
||||
"num_return_sequences": 1,
|
||||
"output_attentions": false,
|
||||
"output_hidden_states": false,
|
||||
"output_scores": false,
|
||||
"pad_token_id": null,
|
||||
"patch_size": 14,
|
||||
"prefix": null,
|
||||
"problem_type": null,
|
||||
"projection_dim" : 768,
|
||||
"pruned_heads": {},
|
||||
"remove_invalid_values": false,
|
||||
"repetition_penalty": 1.0,
|
||||
"return_dict": true,
|
||||
"return_dict_in_generate": false,
|
||||
"sep_token_id": null,
|
||||
"task_specific_params": null,
|
||||
"temperature": 1.0,
|
||||
"tie_encoder_decoder": false,
|
||||
"tie_word_embeddings": true,
|
||||
"tokenizer_class": null,
|
||||
"top_k": 50,
|
||||
"top_p": 1.0,
|
||||
"torch_dtype": null,
|
||||
"torchscript": false,
|
||||
"transformers_version": "4.16.0.dev0",
|
||||
"use_bfloat16": false
|
||||
},
|
||||
"vision_config_dict": {
|
||||
"hidden_size": 1024,
|
||||
"intermediate_size": 4096,
|
||||
"num_attention_heads": 16,
|
||||
"num_hidden_layers": 24,
|
||||
"patch_size": 14,
|
||||
"projection_dim": 768
|
||||
}
|
||||
}
|
@ -1,332 +0,0 @@
|
||||
diff --git a/optimizedSD/ddpm.py b/optimizedSD/ddpm.py
|
||||
index b967b55..35ef520 100644
|
||||
--- a/optimizedSD/ddpm.py
|
||||
+++ b/optimizedSD/ddpm.py
|
||||
@@ -22,7 +22,7 @@ from ldm.util import exists, default, instantiate_from_config
|
||||
from ldm.modules.diffusionmodules.util import make_beta_schedule
|
||||
from ldm.modules.diffusionmodules.util import make_ddim_sampling_parameters, make_ddim_timesteps, noise_like
|
||||
from ldm.modules.diffusionmodules.util import make_beta_schedule, extract_into_tensor, noise_like
|
||||
-from samplers import CompVisDenoiser, get_ancestral_step, to_d, append_dims,linear_multistep_coeff
|
||||
+from .samplers import CompVisDenoiser, get_ancestral_step, to_d, append_dims,linear_multistep_coeff
|
||||
|
||||
def disabled_train(self):
|
||||
"""Overwrite model.train with this function to make sure train/eval mode
|
||||
@@ -506,6 +506,8 @@ class UNet(DDPM):
|
||||
|
||||
x_latent = noise if x0 is None else x0
|
||||
# sampling
|
||||
+ if sampler in ('ddim', 'dpm2', 'heun', 'dpm2_a', 'lms') and not hasattr(self, 'ddim_timesteps'):
|
||||
+ self.make_schedule(ddim_num_steps=S, ddim_eta=eta, verbose=False)
|
||||
|
||||
if sampler == "plms":
|
||||
self.make_schedule(ddim_num_steps=S, ddim_eta=eta, verbose=False)
|
||||
@@ -528,39 +530,46 @@ class UNet(DDPM):
|
||||
elif sampler == "ddim":
|
||||
samples = self.ddim_sampling(x_latent, conditioning, S, unconditional_guidance_scale=unconditional_guidance_scale,
|
||||
unconditional_conditioning=unconditional_conditioning,
|
||||
- mask = mask,init_latent=x_T,use_original_steps=False)
|
||||
+ mask = mask,init_latent=x_T,use_original_steps=False,
|
||||
+ callback=callback, img_callback=img_callback)
|
||||
|
||||
elif sampler == "euler":
|
||||
self.make_schedule(ddim_num_steps=S, ddim_eta=eta, verbose=False)
|
||||
samples = self.euler_sampling(self.alphas_cumprod,x_latent, S, conditioning, unconditional_conditioning=unconditional_conditioning,
|
||||
- unconditional_guidance_scale=unconditional_guidance_scale)
|
||||
+ unconditional_guidance_scale=unconditional_guidance_scale,
|
||||
+ img_callback=img_callback)
|
||||
elif sampler == "euler_a":
|
||||
self.make_schedule(ddim_num_steps=S, ddim_eta=eta, verbose=False)
|
||||
samples = self.euler_ancestral_sampling(self.alphas_cumprod,x_latent, S, conditioning, unconditional_conditioning=unconditional_conditioning,
|
||||
- unconditional_guidance_scale=unconditional_guidance_scale)
|
||||
+ unconditional_guidance_scale=unconditional_guidance_scale,
|
||||
+ img_callback=img_callback)
|
||||
|
||||
elif sampler == "dpm2":
|
||||
samples = self.dpm_2_sampling(self.alphas_cumprod,x_latent, S, conditioning, unconditional_conditioning=unconditional_conditioning,
|
||||
- unconditional_guidance_scale=unconditional_guidance_scale)
|
||||
+ unconditional_guidance_scale=unconditional_guidance_scale,
|
||||
+ img_callback=img_callback)
|
||||
elif sampler == "heun":
|
||||
samples = self.heun_sampling(self.alphas_cumprod,x_latent, S, conditioning, unconditional_conditioning=unconditional_conditioning,
|
||||
- unconditional_guidance_scale=unconditional_guidance_scale)
|
||||
+ unconditional_guidance_scale=unconditional_guidance_scale,
|
||||
+ img_callback=img_callback)
|
||||
|
||||
elif sampler == "dpm2_a":
|
||||
samples = self.dpm_2_ancestral_sampling(self.alphas_cumprod,x_latent, S, conditioning, unconditional_conditioning=unconditional_conditioning,
|
||||
- unconditional_guidance_scale=unconditional_guidance_scale)
|
||||
+ unconditional_guidance_scale=unconditional_guidance_scale,
|
||||
+ img_callback=img_callback)
|
||||
|
||||
|
||||
elif sampler == "lms":
|
||||
samples = self.lms_sampling(self.alphas_cumprod,x_latent, S, conditioning, unconditional_conditioning=unconditional_conditioning,
|
||||
- unconditional_guidance_scale=unconditional_guidance_scale)
|
||||
+ unconditional_guidance_scale=unconditional_guidance_scale,
|
||||
+ img_callback=img_callback)
|
||||
+
|
||||
+ yield from samples
|
||||
|
||||
if(self.turbo):
|
||||
self.model1.to("cpu")
|
||||
self.model2.to("cpu")
|
||||
|
||||
- return samples
|
||||
-
|
||||
@torch.no_grad()
|
||||
def plms_sampling(self, cond,b, img,
|
||||
ddim_use_original_steps=False,
|
||||
@@ -599,10 +608,10 @@ class UNet(DDPM):
|
||||
old_eps.append(e_t)
|
||||
if len(old_eps) >= 4:
|
||||
old_eps.pop(0)
|
||||
- if callback: callback(i)
|
||||
- if img_callback: img_callback(pred_x0, i)
|
||||
+ if callback: yield from callback(i)
|
||||
+ if img_callback: yield from img_callback(pred_x0, i)
|
||||
|
||||
- return img
|
||||
+ yield from img_callback(img, len(iterator)-1)
|
||||
|
||||
@torch.no_grad()
|
||||
def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False,
|
||||
@@ -706,7 +715,8 @@ class UNet(DDPM):
|
||||
|
||||
@torch.no_grad()
|
||||
def ddim_sampling(self, x_latent, cond, t_start, unconditional_guidance_scale=1.0, unconditional_conditioning=None,
|
||||
- mask = None,init_latent=None,use_original_steps=False):
|
||||
+ mask = None,init_latent=None,use_original_steps=False,
|
||||
+ callback=None, img_callback=None):
|
||||
|
||||
timesteps = self.ddim_timesteps
|
||||
timesteps = timesteps[:t_start]
|
||||
@@ -730,10 +740,13 @@ class UNet(DDPM):
|
||||
unconditional_guidance_scale=unconditional_guidance_scale,
|
||||
unconditional_conditioning=unconditional_conditioning)
|
||||
|
||||
+ if callback: yield from callback(i)
|
||||
+ if img_callback: yield from img_callback(x_dec, i)
|
||||
+
|
||||
if mask is not None:
|
||||
- return x0 * mask + (1. - mask) * x_dec
|
||||
+ x_dec = x0 * mask + (1. - mask) * x_dec
|
||||
|
||||
- return x_dec
|
||||
+ yield from img_callback(x_dec, len(iterator)-1)
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
@@ -779,13 +792,16 @@ class UNet(DDPM):
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
- def euler_sampling(self, ac, x, S, cond, unconditional_conditioning = None, unconditional_guidance_scale = 1,extra_args=None,callback=None, disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'), s_noise=1.):
|
||||
+ def euler_sampling(self, ac, x, S, cond, unconditional_conditioning = None, unconditional_guidance_scale = 1,extra_args=None,callback=None, disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'), s_noise=1.,
|
||||
+ img_callback=None):
|
||||
"""Implements Algorithm 2 (Euler steps) from Karras et al. (2022)."""
|
||||
extra_args = {} if extra_args is None else extra_args
|
||||
cvd = CompVisDenoiser(ac)
|
||||
sigmas = cvd.get_sigmas(S)
|
||||
x = x*sigmas[0]
|
||||
|
||||
+ print(f"Running Euler Sampling with {len(sigmas) - 1} timesteps")
|
||||
+
|
||||
s_in = x.new_ones([x.shape[0]]).half()
|
||||
for i in trange(len(sigmas) - 1, disable=disable):
|
||||
gamma = min(s_churn / (len(sigmas) - 1), 2 ** 0.5 - 1) if s_tmin <= sigmas[i] <= s_tmax else 0.
|
||||
@@ -807,13 +823,18 @@ class UNet(DDPM):
|
||||
d = to_d(x, sigma_hat, denoised)
|
||||
if callback is not None:
|
||||
callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigma_hat, 'denoised': denoised})
|
||||
+
|
||||
+ if img_callback: yield from img_callback(x, i)
|
||||
+
|
||||
dt = sigmas[i + 1] - sigma_hat
|
||||
# Euler method
|
||||
x = x + d * dt
|
||||
- return x
|
||||
+
|
||||
+ yield from img_callback(x, len(sigmas)-1)
|
||||
|
||||
@torch.no_grad()
|
||||
- def euler_ancestral_sampling(self,ac,x, S, cond, unconditional_conditioning = None, unconditional_guidance_scale = 1,extra_args=None, callback=None, disable=None):
|
||||
+ def euler_ancestral_sampling(self,ac,x, S, cond, unconditional_conditioning = None, unconditional_guidance_scale = 1,extra_args=None, callback=None, disable=None,
|
||||
+ img_callback=None):
|
||||
"""Ancestral sampling with Euler method steps."""
|
||||
extra_args = {} if extra_args is None else extra_args
|
||||
|
||||
@@ -822,6 +843,8 @@ class UNet(DDPM):
|
||||
sigmas = cvd.get_sigmas(S)
|
||||
x = x*sigmas[0]
|
||||
|
||||
+ print(f"Running Euler Ancestral Sampling with {len(sigmas) - 1} timesteps")
|
||||
+
|
||||
s_in = x.new_ones([x.shape[0]]).half()
|
||||
for i in trange(len(sigmas) - 1, disable=disable):
|
||||
|
||||
@@ -837,17 +860,22 @@ class UNet(DDPM):
|
||||
sigma_down, sigma_up = get_ancestral_step(sigmas[i], sigmas[i + 1])
|
||||
if callback is not None:
|
||||
callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised})
|
||||
+
|
||||
+ if img_callback: yield from img_callback(x, i)
|
||||
+
|
||||
d = to_d(x, sigmas[i], denoised)
|
||||
# Euler method
|
||||
dt = sigma_down - sigmas[i]
|
||||
x = x + d * dt
|
||||
x = x + torch.randn_like(x) * sigma_up
|
||||
- return x
|
||||
+
|
||||
+ yield from img_callback(x, len(sigmas)-1)
|
||||
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
- def heun_sampling(self, ac, x, S, cond, unconditional_conditioning = None, unconditional_guidance_scale = 1, extra_args=None, callback=None, disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'), s_noise=1.):
|
||||
+ def heun_sampling(self, ac, x, S, cond, unconditional_conditioning = None, unconditional_guidance_scale = 1, extra_args=None, callback=None, disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'), s_noise=1.,
|
||||
+ img_callback=None):
|
||||
"""Implements Algorithm 2 (Heun steps) from Karras et al. (2022)."""
|
||||
extra_args = {} if extra_args is None else extra_args
|
||||
|
||||
@@ -855,6 +883,8 @@ class UNet(DDPM):
|
||||
sigmas = cvd.get_sigmas(S)
|
||||
x = x*sigmas[0]
|
||||
|
||||
+ print(f"Running Heun Sampling with {len(sigmas) - 1} timesteps")
|
||||
+
|
||||
|
||||
s_in = x.new_ones([x.shape[0]]).half()
|
||||
for i in trange(len(sigmas) - 1, disable=disable):
|
||||
@@ -876,6 +906,9 @@ class UNet(DDPM):
|
||||
d = to_d(x, sigma_hat, denoised)
|
||||
if callback is not None:
|
||||
callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigma_hat, 'denoised': denoised})
|
||||
+
|
||||
+ if img_callback: yield from img_callback(x, i)
|
||||
+
|
||||
dt = sigmas[i + 1] - sigma_hat
|
||||
if sigmas[i + 1] == 0:
|
||||
# Euler method
|
||||
@@ -895,11 +928,13 @@ class UNet(DDPM):
|
||||
d_2 = to_d(x_2, sigmas[i + 1], denoised_2)
|
||||
d_prime = (d + d_2) / 2
|
||||
x = x + d_prime * dt
|
||||
- return x
|
||||
+
|
||||
+ yield from img_callback(x, len(sigmas)-1)
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
- def dpm_2_sampling(self,ac,x, S, cond, unconditional_conditioning = None, unconditional_guidance_scale = 1,extra_args=None, callback=None, disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'), s_noise=1.):
|
||||
+ def dpm_2_sampling(self,ac,x, S, cond, unconditional_conditioning = None, unconditional_guidance_scale = 1,extra_args=None, callback=None, disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'), s_noise=1.,
|
||||
+ img_callback=None):
|
||||
"""A sampler inspired by DPM-Solver-2 and Algorithm 2 from Karras et al. (2022)."""
|
||||
extra_args = {} if extra_args is None else extra_args
|
||||
|
||||
@@ -907,6 +942,8 @@ class UNet(DDPM):
|
||||
sigmas = cvd.get_sigmas(S)
|
||||
x = x*sigmas[0]
|
||||
|
||||
+ print(f"Running DPM2 Sampling with {len(sigmas) - 1} timesteps")
|
||||
+
|
||||
s_in = x.new_ones([x.shape[0]]).half()
|
||||
for i in trange(len(sigmas) - 1, disable=disable):
|
||||
gamma = min(s_churn / (len(sigmas) - 1), 2 ** 0.5 - 1) if s_tmin <= sigmas[i] <= s_tmax else 0.
|
||||
@@ -924,7 +961,7 @@ class UNet(DDPM):
|
||||
e_t_uncond, e_t = (x_in + eps * c_out).chunk(2)
|
||||
denoised = e_t_uncond + unconditional_guidance_scale * (e_t - e_t_uncond)
|
||||
|
||||
-
|
||||
+ if img_callback: yield from img_callback(x, i)
|
||||
|
||||
d = to_d(x, sigma_hat, denoised)
|
||||
# Midpoint method, where the midpoint is chosen according to a rho=3 Karras schedule
|
||||
@@ -945,11 +982,13 @@ class UNet(DDPM):
|
||||
|
||||
d_2 = to_d(x_2, sigma_mid, denoised_2)
|
||||
x = x + d_2 * dt_2
|
||||
- return x
|
||||
+
|
||||
+ yield from img_callback(x, len(sigmas)-1)
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
- def dpm_2_ancestral_sampling(self,ac,x, S, cond, unconditional_conditioning = None, unconditional_guidance_scale = 1, extra_args=None, callback=None, disable=None):
|
||||
+ def dpm_2_ancestral_sampling(self,ac,x, S, cond, unconditional_conditioning = None, unconditional_guidance_scale = 1, extra_args=None, callback=None, disable=None,
|
||||
+ img_callback=None):
|
||||
"""Ancestral sampling with DPM-Solver inspired second-order steps."""
|
||||
extra_args = {} if extra_args is None else extra_args
|
||||
|
||||
@@ -957,6 +996,8 @@ class UNet(DDPM):
|
||||
sigmas = cvd.get_sigmas(S)
|
||||
x = x*sigmas[0]
|
||||
|
||||
+ print(f"Running DPM2 Ancestral Sampling with {len(sigmas) - 1} timesteps")
|
||||
+
|
||||
s_in = x.new_ones([x.shape[0]]).half()
|
||||
for i in trange(len(sigmas) - 1, disable=disable):
|
||||
|
||||
@@ -973,6 +1014,9 @@ class UNet(DDPM):
|
||||
sigma_down, sigma_up = get_ancestral_step(sigmas[i], sigmas[i + 1])
|
||||
if callback is not None:
|
||||
callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised})
|
||||
+
|
||||
+ if img_callback: yield from img_callback(x, i)
|
||||
+
|
||||
d = to_d(x, sigmas[i], denoised)
|
||||
# Midpoint method, where the midpoint is chosen according to a rho=3 Karras schedule
|
||||
sigma_mid = ((sigmas[i] ** (1 / 3) + sigma_down ** (1 / 3)) / 2) ** 3
|
||||
@@ -993,11 +1037,13 @@ class UNet(DDPM):
|
||||
d_2 = to_d(x_2, sigma_mid, denoised_2)
|
||||
x = x + d_2 * dt_2
|
||||
x = x + torch.randn_like(x) * sigma_up
|
||||
- return x
|
||||
+
|
||||
+ yield from img_callback(x, len(sigmas)-1)
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
- def lms_sampling(self,ac,x, S, cond, unconditional_conditioning = None, unconditional_guidance_scale = 1, extra_args=None, callback=None, disable=None, order=4):
|
||||
+ def lms_sampling(self,ac,x, S, cond, unconditional_conditioning = None, unconditional_guidance_scale = 1, extra_args=None, callback=None, disable=None, order=4,
|
||||
+ img_callback=None):
|
||||
extra_args = {} if extra_args is None else extra_args
|
||||
s_in = x.new_ones([x.shape[0]])
|
||||
|
||||
@@ -1005,6 +1051,8 @@ class UNet(DDPM):
|
||||
sigmas = cvd.get_sigmas(S)
|
||||
x = x*sigmas[0]
|
||||
|
||||
+ print(f"Running LMS Sampling with {len(sigmas) - 1} timesteps")
|
||||
+
|
||||
ds = []
|
||||
for i in trange(len(sigmas) - 1, disable=disable):
|
||||
|
||||
@@ -1017,6 +1065,7 @@ class UNet(DDPM):
|
||||
e_t_uncond, e_t = (x_in + eps * c_out).chunk(2)
|
||||
denoised = e_t_uncond + unconditional_guidance_scale * (e_t - e_t_uncond)
|
||||
|
||||
+ if img_callback: yield from img_callback(x, i)
|
||||
|
||||
d = to_d(x, sigmas[i], denoised)
|
||||
ds.append(d)
|
||||
@@ -1027,4 +1076,5 @@ class UNet(DDPM):
|
||||
cur_order = min(i + 1, order)
|
||||
coeffs = [linear_multistep_coeff(cur_order, sigmas.cpu(), i, j) for j in range(cur_order)]
|
||||
x = x + sum(coeff * d for coeff, d in zip(coeffs, reversed(ds)))
|
||||
- return x
|
||||
+
|
||||
+ yield from img_callback(x, len(sigmas)-1)
|
||||
diff --git a/optimizedSD/openaimodelSplit.py b/optimizedSD/openaimodelSplit.py
|
||||
index abc3098..7a32ffe 100644
|
||||
--- a/optimizedSD/openaimodelSplit.py
|
||||
+++ b/optimizedSD/openaimodelSplit.py
|
||||
@@ -13,7 +13,7 @@ from ldm.modules.diffusionmodules.util import (
|
||||
normalization,
|
||||
timestep_embedding,
|
||||
)
|
||||
-from splitAttention import SpatialTransformer
|
||||
+from .splitAttention import SpatialTransformer
|
||||
|
||||
|
||||
class AttentionPool2d(nn.Module):
|
@ -1,13 +0,0 @@
|
||||
diff --git a/environment.yaml b/environment.yaml
|
||||
index 7f25da8..306750f 100644
|
||||
--- a/environment.yaml
|
||||
+++ b/environment.yaml
|
||||
@@ -23,6 +23,8 @@ dependencies:
|
||||
- torch-fidelity==0.3.0
|
||||
- transformers==4.19.2
|
||||
- torchmetrics==0.6.0
|
||||
+ - pywavelets==1.3.0
|
||||
+ - pandas==1.4.4
|
||||
- kornia==0.6
|
||||
- -e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers
|
||||
- -e git+https://github.com/openai/CLIP.git@main#egg=clip
|
@ -1,47 +0,0 @@
|
||||
name: ldm
|
||||
channels:
|
||||
- pytorch
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- python==3.10.5
|
||||
- pip==22.2.2
|
||||
|
||||
- pytorch
|
||||
- torchvision
|
||||
|
||||
- albumentations==1.2.1
|
||||
- coloredlogs==15.0.1
|
||||
- einops==0.4.1
|
||||
- grpcio==1.46.4
|
||||
- humanfriendly==10.0
|
||||
- imageio==2.21.2
|
||||
- imageio-ffmpeg==0.4.7
|
||||
- imgaug==0.4.0
|
||||
- kornia==0.6.7
|
||||
- mpmath==1.2.1
|
||||
- nomkl
|
||||
- numpy==1.23.2
|
||||
- omegaconf==2.1.1
|
||||
- onnx==1.12.0
|
||||
- onnxruntime==1.12.1
|
||||
- pudb==2022.1
|
||||
- pytorch-lightning==1.6.5
|
||||
- scipy==1.9.1
|
||||
- streamlit==1.12.2
|
||||
- sympy==1.10.1
|
||||
- tensorboard==2.9.0
|
||||
- torchmetrics==0.9.3
|
||||
- antlr4-python3-runtime=4.8
|
||||
- pip:
|
||||
- opencv-python==4.6.0.66
|
||||
- realesrgan==0.2.5.0
|
||||
- test-tube==0.7.5
|
||||
- transformers==4.21.2
|
||||
- torch-fidelity==0.3.0
|
||||
- -e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers
|
||||
- -e git+https://github.com/openai/CLIP.git@main#egg=clip
|
||||
- -e git+https://github.com/TencentARC/GFPGAN#egg=GFPGAN
|
||||
- -e git+https://github.com/xinntao/Real-ESRGAN#egg=realesrgan
|
||||
- -e .
|
||||
variables:
|
||||
PYTORCH_ENABLE_MPS_FALLBACK: 1
|
@ -1,33 +0,0 @@
|
||||
name: ldm
|
||||
channels:
|
||||
- pytorch
|
||||
- defaults
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- python=3.10.5
|
||||
- pip=20.3
|
||||
- cudatoolkit=11.3
|
||||
- pytorch=1.11.0
|
||||
- torchvision=0.12.0
|
||||
- numpy=1.23.2
|
||||
- antlr4-python3-runtime=4.8
|
||||
- pip:
|
||||
- albumentations==0.4.3
|
||||
- opencv-python==4.6.0.66
|
||||
- pudb==2019.2
|
||||
- imageio==2.9.0
|
||||
- imageio-ffmpeg==0.4.2
|
||||
- pytorch-lightning==1.4.2
|
||||
- omegaconf==2.1.1
|
||||
- test-tube>=0.7.5
|
||||
- streamlit>=0.73.1
|
||||
- einops==0.3.0
|
||||
- torch-fidelity==0.3.0
|
||||
- transformers==4.19.2
|
||||
- torchmetrics==0.6.0
|
||||
- kornia==0.6
|
||||
- -e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers
|
||||
- -e git+https://github.com/openai/CLIP.git@main#egg=clip
|
||||
- -e git+https://github.com/TencentARC/GFPGAN#egg=GFPGAN
|
||||
- -e git+https://github.com/xinntao/Real-ESRGAN#egg=realesrgan
|
||||
- -e .
|
BIN
media/config-v3.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
media/config-v4.jpg
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
media/config-v5.jpg
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
media/config-v6.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
media/config-v7.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
media/ding.mp3
Normal file
BIN
media/download-mac.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
media/shot-v10-simple.jpg
Normal file
After Width: | Height: | Size: 139 KiB |
BIN
media/shot-v10.jpg
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
media/shot-v3a.jpg
Normal file
After Width: | Height: | Size: 122 KiB |
BIN
media/shot-v6a.jpg
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
media/shot-v8.jpg
Normal file
After Width: | Height: | Size: 244 KiB |
BIN
media/task-queue-v1.jpg
Normal file
After Width: | Height: | Size: 155 KiB |
9
package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"scripts": {
|
||||
"prettier-fix": "npx prettier --write \"./**/*.js\"",
|
||||
"prettier-check": "npx prettier --check \"./**/*.js\""
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^1.19.1"
|
||||
}
|
||||
}
|
BIN
patch.patch
Normal file
51
scripts/Developer Console.cmd
Normal file
@ -0,0 +1,51 @@
|
||||
@echo off
|
||||
|
||||
echo "Opening Stable Diffusion UI - Developer Console.." & echo.
|
||||
|
||||
cd /d %~dp0
|
||||
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
@rem set legacy and new installer's PATH, if they exist
|
||||
if exist "installer" set PATH=%cd%\installer;%cd%\installer\Library\bin;%cd%\installer\Scripts;%cd%\installer\Library\usr\bin;%PATH%
|
||||
if exist "installer_files\env" set PATH=%cd%\installer_files\env;%cd%\installer_files\env\Library\bin;%cd%\installer_files\env\Scripts;%cd%\installer_files\Library\usr\bin;%PATH%
|
||||
|
||||
set PYTHONPATH=%cd%\installer;%cd%\installer_files\env
|
||||
|
||||
@rem activate the installer env
|
||||
call conda activate
|
||||
|
||||
@rem Test the environment
|
||||
echo "Environment Info:"
|
||||
call where git
|
||||
call git --version
|
||||
|
||||
call where conda
|
||||
call conda --version
|
||||
|
||||
echo.
|
||||
echo COMSPEC=%COMSPEC%
|
||||
echo.
|
||||
|
||||
@rem activate the legacy environment (if present) and set PYTHONPATH
|
||||
if exist "installer_files\env" (
|
||||
set PYTHONPATH=%cd%\installer_files\env\lib\site-packages
|
||||
)
|
||||
if exist "stable-diffusion\env" (
|
||||
call conda activate .\stable-diffusion\env
|
||||
set PYTHONPATH=%cd%\stable-diffusion\env\lib\site-packages
|
||||
)
|
||||
|
||||
call where python
|
||||
call python --version
|
||||
|
||||
echo PYTHONPATH=%PYTHONPATH%
|
||||
|
||||
if exist "%cd%\profile" (
|
||||
set HF_HOME=%cd%\profile\.cache\huggingface
|
||||
)
|
||||
|
||||
@rem done
|
||||
echo.
|
||||
|
||||
cmd /k
|
45
scripts/Start Stable Diffusion UI.cmd
Normal file
@ -0,0 +1,45 @@
|
||||
@echo off
|
||||
|
||||
cd /d %~dp0
|
||||
echo Install dir: %~dp0
|
||||
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
set PYTHONHOME=
|
||||
|
||||
if exist "on_sd_start.bat" (
|
||||
echo ================================================================================
|
||||
echo.
|
||||
echo !!!! WARNING !!!!
|
||||
echo.
|
||||
echo It looks like you're trying to run the installation script from a source code
|
||||
echo download. This will not work.
|
||||
echo.
|
||||
echo Recommended: Please close this window and download the installer from
|
||||
echo https://stable-diffusion-ui.github.io/docs/installation/
|
||||
echo.
|
||||
echo ================================================================================
|
||||
echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
@rem set legacy installer's PATH, if it exists
|
||||
if exist "installer" set PATH=%cd%\installer;%cd%\installer\Library\bin;%cd%\installer\Scripts;%cd%\installer\Library\usr\bin;%PATH%
|
||||
|
||||
@rem set new installer's PATH, if it downloaded any packages
|
||||
if exist "installer_files\env" set PATH=%cd%\installer_files\env;%cd%\installer_files\env\Library\bin;%cd%\installer_files\env\Scripts;%cd%\installer_files\Library\usr\bin;%PATH%
|
||||
|
||||
set PYTHONPATH=%cd%\installer;%cd%\installer_files\env
|
||||
|
||||
@rem Test the core requirements
|
||||
call where git
|
||||
call git --version
|
||||
|
||||
call where conda
|
||||
call conda --version
|
||||
echo .
|
||||
echo COMSPEC=%COMSPEC%
|
||||
|
||||
@rem Download the rest of the installer and UI
|
||||
call scripts\on_env_start.bat
|
||||
@pause
|
78
scripts/bootstrap.bat
Normal file
@ -0,0 +1,78 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
@rem This script will install git and conda (if not found on the PATH variable)
|
||||
@rem using micromamba (an 8mb static-linked single-file binary, conda replacement).
|
||||
@rem For users who already have git and conda, this step will be skipped.
|
||||
|
||||
@rem This enables a user to install this project without manually installing conda and git.
|
||||
|
||||
@rem config
|
||||
set MAMBA_ROOT_PREFIX=%cd%\installer_files\mamba
|
||||
set INSTALL_ENV_DIR=%cd%\installer_files\env
|
||||
set LEGACY_INSTALL_ENV_DIR=%cd%\installer
|
||||
set MICROMAMBA_DOWNLOAD_URL=https://github.com/easydiffusion/easydiffusion/releases/download/v1.1/micromamba.exe
|
||||
set umamba_exists=F
|
||||
|
||||
set OLD_APPDATA=%APPDATA%
|
||||
set OLD_USERPROFILE=%USERPROFILE%
|
||||
set APPDATA=%cd%\installer_files\appdata
|
||||
set USERPROFILE=%cd%\profile
|
||||
|
||||
@rem figure out whether git and conda needs to be installed
|
||||
if exist "%INSTALL_ENV_DIR%" set PATH=%INSTALL_ENV_DIR%;%INSTALL_ENV_DIR%\Library\bin;%INSTALL_ENV_DIR%\Scripts;%INSTALL_ENV_DIR%\Library\usr\bin;%PATH%
|
||||
|
||||
set PACKAGES_TO_INSTALL=
|
||||
|
||||
if not exist "%LEGACY_INSTALL_ENV_DIR%\etc\profile.d\conda.sh" (
|
||||
if not exist "%INSTALL_ENV_DIR%\etc\profile.d\conda.sh" set PACKAGES_TO_INSTALL=%PACKAGES_TO_INSTALL% conda python=3.8.5
|
||||
)
|
||||
|
||||
call git --version >.tmp1 2>.tmp2
|
||||
if "!ERRORLEVEL!" NEQ "0" set PACKAGES_TO_INSTALL=%PACKAGES_TO_INSTALL% git
|
||||
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" --version >.tmp1 2>.tmp2
|
||||
if "!ERRORLEVEL!" EQU "0" set umamba_exists=T
|
||||
|
||||
@rem (if necessary) install git and conda into a contained environment
|
||||
if "%PACKAGES_TO_INSTALL%" NEQ "" (
|
||||
@rem download micromamba
|
||||
if "%umamba_exists%" == "F" (
|
||||
echo "Downloading micromamba from %MICROMAMBA_DOWNLOAD_URL% to %MAMBA_ROOT_PREFIX%\micromamba.exe"
|
||||
|
||||
mkdir "%MAMBA_ROOT_PREFIX%"
|
||||
call curl -Lk "%MICROMAMBA_DOWNLOAD_URL%" > "%MAMBA_ROOT_PREFIX%\micromamba.exe"
|
||||
|
||||
if "!ERRORLEVEL!" NEQ "0" (
|
||||
echo "There was a problem downloading micromamba. Cannot continue."
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
mkdir "%APPDATA%"
|
||||
mkdir "%USERPROFILE%"
|
||||
|
||||
@rem test the mamba binary
|
||||
echo Micromamba version:
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" --version
|
||||
)
|
||||
|
||||
@rem create the installer env
|
||||
if not exist "%INSTALL_ENV_DIR%" (
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" create -y --prefix "%INSTALL_ENV_DIR%"
|
||||
)
|
||||
|
||||
echo "Packages to install:%PACKAGES_TO_INSTALL%"
|
||||
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" install -y --prefix "%INSTALL_ENV_DIR%" -c conda-forge %PACKAGES_TO_INSTALL%
|
||||
|
||||
if not exist "%INSTALL_ENV_DIR%" (
|
||||
echo "There was a problem while installing%PACKAGES_TO_INSTALL% using micromamba. Cannot continue."
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
@rem revert to the old APPDATA. only needed it for bypassing a bug in micromamba (with special characters)
|
||||
set APPDATA=%OLD_APPDATA%
|
||||
set USERPROFILE=%OLD_USERPROFILE%
|
93
scripts/bootstrap.sh
Executable file
@ -0,0 +1,93 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script will install git and conda (if not found on the PATH variable)
|
||||
# using micromamba (an 8mb static-linked single-file binary, conda replacement).
|
||||
# For users who already have git and conda, this step will be skipped.
|
||||
|
||||
# This enables a user to install this project without manually installing conda and git.
|
||||
|
||||
source ./scripts/functions.sh
|
||||
|
||||
set -o pipefail
|
||||
|
||||
OS_NAME=$(uname -s)
|
||||
case "${OS_NAME}" in
|
||||
Linux*) OS_NAME="linux";;
|
||||
Darwin*) OS_NAME="osx";;
|
||||
*) echo "Unknown OS: $OS_NAME! This script runs only on Linux or Mac" && exit
|
||||
esac
|
||||
|
||||
OS_ARCH=$(uname -m)
|
||||
case "${OS_ARCH}" in
|
||||
x86_64*) OS_ARCH="64";;
|
||||
arm64*) OS_ARCH="arm64";;
|
||||
aarch64*) OS_ARCH="arm64";;
|
||||
*) echo "Unknown system architecture: $OS_ARCH! This script runs only on x86_64 or arm64" && exit
|
||||
esac
|
||||
|
||||
if ! which curl; then fail "'curl' not found. Please install curl."; fi
|
||||
if ! which tar; then fail "'tar' not found. Please install tar."; fi
|
||||
if ! which bzip2; then fail "'bzip2' not found. Please install bzip2."; fi
|
||||
|
||||
if pwd | grep ' '; then fail "The installation directory's path contains a space character. Conda will fail to install. Please change the directory."; fi
|
||||
|
||||
# https://mamba.readthedocs.io/en/latest/installation.html
|
||||
if [ "$OS_NAME" == "linux" ] && [ "$OS_ARCH" == "arm64" ]; then OS_ARCH="aarch64"; fi
|
||||
|
||||
# config
|
||||
export MAMBA_ROOT_PREFIX="$(pwd)/installer_files/mamba"
|
||||
INSTALL_ENV_DIR="$(pwd)/installer_files/env"
|
||||
LEGACY_INSTALL_ENV_DIR="$(pwd)/installer"
|
||||
MICROMAMBA_DOWNLOAD_URL="https://micro.mamba.pm/api/micromamba/${OS_NAME}-${OS_ARCH}/latest"
|
||||
umamba_exists="F"
|
||||
|
||||
# figure out whether git and conda needs to be installed
|
||||
if [ -e "$INSTALL_ENV_DIR" ]; then export PATH="$INSTALL_ENV_DIR/bin:$PATH"; fi
|
||||
|
||||
PACKAGES_TO_INSTALL=""
|
||||
|
||||
if [ ! -e "$LEGACY_INSTALL_ENV_DIR/etc/profile.d/conda.sh" ] && [ ! -e "$INSTALL_ENV_DIR/etc/profile.d/conda.sh" ]; then PACKAGES_TO_INSTALL="$PACKAGES_TO_INSTALL conda python=3.8.5"; fi
|
||||
if ! hash "git" &>/dev/null; then PACKAGES_TO_INSTALL="$PACKAGES_TO_INSTALL git"; fi
|
||||
|
||||
if "$MAMBA_ROOT_PREFIX/micromamba" --version &>/dev/null; then umamba_exists="T"; fi
|
||||
|
||||
# (if necessary) install git and conda into a contained environment
|
||||
if [ "$PACKAGES_TO_INSTALL" != "" ]; then
|
||||
# download micromamba
|
||||
if [ "$umamba_exists" == "F" ]; then
|
||||
echo "Downloading micromamba from $MICROMAMBA_DOWNLOAD_URL to $MAMBA_ROOT_PREFIX/micromamba"
|
||||
|
||||
mkdir -p "$MAMBA_ROOT_PREFIX"
|
||||
curl -L "$MICROMAMBA_DOWNLOAD_URL" | tar -xvj -O bin/micromamba > "$MAMBA_ROOT_PREFIX/micromamba"
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
echo
|
||||
echo "EE micromamba download failed"
|
||||
echo "EE If the lines above contain 'bzip2: Cannot exec', your system doesn't have bzip2 installed"
|
||||
echo "EE If there are network errors, please check your internet setup"
|
||||
fail "micromamba download failed"
|
||||
fi
|
||||
|
||||
chmod u+x "$MAMBA_ROOT_PREFIX/micromamba"
|
||||
|
||||
# test the mamba binary
|
||||
echo "Micromamba version:"
|
||||
"$MAMBA_ROOT_PREFIX/micromamba" --version
|
||||
fi
|
||||
|
||||
# create the installer env
|
||||
if [ ! -e "$INSTALL_ENV_DIR" ]; then
|
||||
"$MAMBA_ROOT_PREFIX/micromamba" create -y --prefix "$INSTALL_ENV_DIR" || fail "unable to create the install environment"
|
||||
fi
|
||||
|
||||
if [ ! -e "$INSTALL_ENV_DIR" ]; then
|
||||
fail "There was a problem while installing$PACKAGES_TO_INSTALL using micromamba. Cannot continue."
|
||||
fi
|
||||
|
||||
echo "Packages to install:$PACKAGES_TO_INSTALL"
|
||||
|
||||
"$MAMBA_ROOT_PREFIX/micromamba" install -y --prefix "$INSTALL_ENV_DIR" -c conda-forge $PACKAGES_TO_INSTALL
|
||||
if [ "$?" != "0" ]; then
|
||||
fail "Installation of the packages '$PACKAGES_TO_INSTALL' failed."
|
||||
fi
|
||||
fi
|
165
scripts/check_modules.py
Normal file
@ -0,0 +1,165 @@
|
||||
"""
|
||||
This script checks and installs the required modules.
|
||||
|
||||
This script runs inside the legacy "stable-diffusion" folder
|
||||
|
||||
TODO - Maybe replace the bulk of this script with a call to `pip install -f requirements.txt`, with
|
||||
a custom index URL depending on the platform.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
from importlib.metadata import version as pkg_version
|
||||
import platform
|
||||
import traceback
|
||||
|
||||
os_name = platform.system()
|
||||
|
||||
modules_to_check = {
|
||||
"torch": ("1.11.0", "1.13.1", "2.0.0"),
|
||||
"torchvision": ("0.12.0", "0.14.1", "0.15.1"),
|
||||
"sdkit": "2.0.3",
|
||||
"stable-diffusion-sdkit": "2.1.4",
|
||||
"rich": "12.6.0",
|
||||
"uvicorn": "0.19.0",
|
||||
"fastapi": "0.85.1",
|
||||
"pycloudflared": "0.2.0",
|
||||
"ruamel.yaml": "0.17.21",
|
||||
"sqlalchemy": "2.0.19",
|
||||
"python-multipart": "0.0.6",
|
||||
# "xformers": "0.0.16",
|
||||
}
|
||||
modules_to_log = ["torch", "torchvision", "sdkit", "stable-diffusion-sdkit"]
|
||||
|
||||
|
||||
def version(module_name: str) -> str:
|
||||
try:
|
||||
return pkg_version(module_name)
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def install(module_name: str, module_version: str):
|
||||
if module_name == "xformers" and (os_name == "Darwin" or is_amd_on_linux()):
|
||||
return
|
||||
|
||||
index_url = None
|
||||
if module_name in ("torch", "torchvision"):
|
||||
module_version, index_url = apply_torch_install_overrides(module_version)
|
||||
|
||||
if is_amd_on_linux(): # hack until AMD works properly on torch 2.0 (avoids black images on some cards)
|
||||
if module_name == "torch":
|
||||
module_version = "1.13.1+rocm5.2"
|
||||
elif module_name == "torchvision":
|
||||
module_version = "0.14.1+rocm5.2"
|
||||
elif os_name == "Darwin":
|
||||
if module_name == "torch":
|
||||
module_version = "1.13.1"
|
||||
elif module_name == "torchvision":
|
||||
module_version = "0.14.1"
|
||||
|
||||
install_cmd = f"python -m pip install --upgrade {module_name}=={module_version}"
|
||||
|
||||
if index_url:
|
||||
install_cmd += f" --index-url {index_url}"
|
||||
if module_name == "sdkit" and version("sdkit") is not None:
|
||||
install_cmd += " -q"
|
||||
|
||||
print(">", install_cmd)
|
||||
os.system(install_cmd)
|
||||
|
||||
|
||||
def init():
|
||||
for module_name, allowed_versions in modules_to_check.items():
|
||||
if os.path.exists(f"../src/{module_name}"):
|
||||
print(f"Skipping {module_name} update, since it's in developer/editable mode")
|
||||
continue
|
||||
|
||||
allowed_versions, latest_version = get_allowed_versions(module_name, allowed_versions)
|
||||
|
||||
requires_install = False
|
||||
if module_name in ("torch", "torchvision"):
|
||||
if version(module_name) is None: # allow any torch version
|
||||
requires_install = True
|
||||
elif os_name == "Darwin" and ( # force mac to downgrade from torch 2.0
|
||||
version("torch").startswith("2.") or version("torchvision").startswith("0.15.")
|
||||
):
|
||||
requires_install = True
|
||||
elif version(module_name) not in allowed_versions:
|
||||
requires_install = True
|
||||
|
||||
if requires_install:
|
||||
try:
|
||||
install(module_name, latest_version)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
fail(module_name)
|
||||
|
||||
if module_name in modules_to_log:
|
||||
print(f"{module_name}: {version(module_name)}")
|
||||
|
||||
|
||||
### utilities
|
||||
|
||||
|
||||
def get_allowed_versions(module_name: str, allowed_versions: tuple):
|
||||
allowed_versions = (allowed_versions,) if isinstance(allowed_versions, str) else allowed_versions
|
||||
latest_version = allowed_versions[-1]
|
||||
|
||||
if module_name in ("torch", "torchvision"):
|
||||
allowed_versions = include_cuda_versions(allowed_versions)
|
||||
|
||||
return allowed_versions, latest_version
|
||||
|
||||
|
||||
def apply_torch_install_overrides(module_version: str):
|
||||
index_url = None
|
||||
if os_name == "Windows":
|
||||
module_version += "+cu117"
|
||||
index_url = "https://download.pytorch.org/whl/cu117"
|
||||
elif is_amd_on_linux():
|
||||
index_url = "https://download.pytorch.org/whl/rocm5.2"
|
||||
|
||||
return module_version, index_url
|
||||
|
||||
|
||||
def include_cuda_versions(module_versions: tuple) -> tuple:
|
||||
"Adds CUDA-specific versions to the list of allowed version numbers"
|
||||
|
||||
allowed_versions = tuple(module_versions)
|
||||
allowed_versions += tuple(f"{v}+cu116" for v in module_versions)
|
||||
allowed_versions += tuple(f"{v}+cu117" for v in module_versions)
|
||||
allowed_versions += tuple(f"{v}+rocm5.2" for v in module_versions)
|
||||
allowed_versions += tuple(f"{v}+rocm5.4.2" for v in module_versions)
|
||||
|
||||
return allowed_versions
|
||||
|
||||
|
||||
def is_amd_on_linux():
|
||||
if os_name == "Linux":
|
||||
try:
|
||||
with open("/proc/bus/pci/devices", "r") as f:
|
||||
device_info = f.read()
|
||||
if "amdgpu" in device_info and "nvidia" not in device_info:
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def fail(module_name):
|
||||
print(
|
||||
f"""Error installing {module_name}. Sorry about that, please try to:
|
||||
1. Run this installer again.
|
||||
2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting
|
||||
3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB
|
||||
4. If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues
|
||||
Thanks!"""
|
||||
)
|
||||
exit(1)
|
||||
|
||||
|
||||
### start
|
||||
|
||||
init()
|
24
scripts/config.yaml.sample
Normal file
@ -0,0 +1,24 @@
|
||||
# Change listen_port if port 9000 is already in use on your system
|
||||
# Set listen_to_network to true to make Easy Diffusion accessibble on your local network
|
||||
net:
|
||||
listen_port: 9000
|
||||
listen_to_network: false
|
||||
|
||||
# Multi GPU setup
|
||||
render_devices: auto
|
||||
|
||||
# Set open_browser_on_start to false to disable opening a new browser tab on each restart
|
||||
ui:
|
||||
open_browser_on_start: true
|
||||
|
||||
# set update_branch to main to use the stable version, or to beta to use the experimental
|
||||
# beta version.
|
||||
update_branch: main
|
||||
|
||||
# Set force_save_path to enforce an auto save path. Clients will not be able to change or
|
||||
# disable auto save when this option is set. Please adapt the path in the examples to your
|
||||
# needs.
|
||||
# Windows:
|
||||
# force_save_path: C:\\Easy Diffusion Images\\
|
||||
# Linux:
|
||||
# force_save_path: /data/easy-diffusion-images/
|
55
scripts/developer_console.sh
Executable file
@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
if [ "$0" == "bash" ]; then
|
||||
echo "Opening Stable Diffusion UI - Developer Console.."
|
||||
echo ""
|
||||
|
||||
# set legacy and new installer's PATH, if they exist
|
||||
if [ -e "installer" ]; then export PATH="$(pwd)/installer/bin:$PATH"; fi
|
||||
if [ -e "installer_files/env" ]; then export PATH="$(pwd)/installer_files/env/bin:$PATH"; fi
|
||||
|
||||
# activate the installer env
|
||||
CONDA_BASEPATH=$(conda info --base)
|
||||
source "$CONDA_BASEPATH/etc/profile.d/conda.sh" # avoids the 'shell not initialized' error
|
||||
|
||||
conda activate
|
||||
|
||||
# test the environment
|
||||
echo "Environment Info:"
|
||||
which git
|
||||
git --version
|
||||
|
||||
which conda
|
||||
conda --version
|
||||
|
||||
echo ""
|
||||
|
||||
# activate the legacy environment (if present) and set PYTHONPATH
|
||||
if [ -e "installer_files/env" ]; then
|
||||
export PYTHONPATH="$(pwd)/installer_files/env/lib/python3.8/site-packages"
|
||||
fi
|
||||
if [ -e "stable-diffusion/env" ]; then
|
||||
CONDA_BASEPATH=$(conda info --base)
|
||||
source "$CONDA_BASEPATH/etc/profile.d/conda.sh" # otherwise conda complains about 'shell not initialized' (needed when running in a script)
|
||||
|
||||
conda activate ./stable-diffusion/env
|
||||
|
||||
export PYTHONPATH="$(pwd)/stable-diffusion/env/lib/python3.8/site-packages"
|
||||
fi
|
||||
|
||||
export PYTHONNOUSERSITE=y
|
||||
|
||||
which python
|
||||
python --version
|
||||
|
||||
echo "PYTHONPATH=$PYTHONPATH"
|
||||
|
||||
# done
|
||||
|
||||
echo ""
|
||||
else
|
||||
file_name=$(basename "${BASH_SOURCE[0]}")
|
||||
bash --init-file "$file_name"
|
||||
fi
|
39
scripts/functions.sh
Normal file
@ -0,0 +1,39 @@
|
||||
#
|
||||
# utility functions for all scripts
|
||||
#
|
||||
|
||||
fail() {
|
||||
echo
|
||||
echo "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
|
||||
echo
|
||||
if [ "$1" != "" ]; then
|
||||
echo ERROR: $1
|
||||
else
|
||||
echo An error occurred.
|
||||
fi
|
||||
cat <<EOF
|
||||
|
||||
Error downloading Stable Diffusion UI. Sorry about that, please try to:
|
||||
1. Run this installer again.
|
||||
2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting
|
||||
3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB
|
||||
4. If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues
|
||||
|
||||
Thanks!
|
||||
|
||||
|
||||
EOF
|
||||
read -p "Press any key to continue"
|
||||
exit 1
|
||||
|
||||
}
|
||||
|
||||
filesize() {
|
||||
case "$(uname -s)" in
|
||||
Linux*) stat -c "%s" $1;;
|
||||
Darwin*) /usr/bin/stat -f "%z" $1;;
|
||||
*) echo "Unknown OS: $OS_NAME! This script runs only on Linux or Mac" && exit
|
||||
esac
|
||||
}
|
||||
|
||||
|
53
scripts/get_config.py
Normal file
@ -0,0 +1,53 @@
|
||||
import os
|
||||
import argparse
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
# The config file is in the same directory as this script
|
||||
config_directory = os.path.dirname(__file__)
|
||||
config_yaml = os.path.join(config_directory, "..", "config.yaml")
|
||||
config_json = os.path.join(config_directory, "config.json")
|
||||
|
||||
parser = argparse.ArgumentParser(description='Get values from config file')
|
||||
parser.add_argument('--default', dest='default', action='store',
|
||||
help='default value, to be used if the setting is not defined in the config file')
|
||||
parser.add_argument('key', metavar='key', nargs='+',
|
||||
help='config key to return')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
config = None
|
||||
|
||||
# migrate the old config yaml location
|
||||
config_legacy_yaml = os.path.join(config_directory, "config.yaml")
|
||||
if os.path.isfile(config_legacy_yaml):
|
||||
shutil.move(config_legacy_yaml, config_yaml)
|
||||
|
||||
if os.path.isfile(config_yaml):
|
||||
from ruamel.yaml import YAML
|
||||
yaml = YAML(typ='safe')
|
||||
with open(config_yaml, 'r') as configfile:
|
||||
try:
|
||||
config = yaml.load(configfile)
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
elif os.path.isfile(config_json):
|
||||
import json
|
||||
with open(config_json, 'r') as configfile:
|
||||
try:
|
||||
config = json.load(configfile)
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
|
||||
if config is None:
|
||||
config = {}
|
||||
|
||||
for k in args.key:
|
||||
if k in config:
|
||||
config = config[k]
|
||||
else:
|
||||
if args.default != None:
|
||||
print(args.default)
|
||||
exit()
|
||||
|
||||
print(config)
|
79
scripts/on_env_start.bat
Normal file
@ -0,0 +1,79 @@
|
||||
@echo off
|
||||
|
||||
@echo. & echo "Easy Diffusion - v3" & echo.
|
||||
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
if exist "scripts\config.bat" (
|
||||
@call scripts\config.bat
|
||||
)
|
||||
|
||||
if exist "scripts\user_config.bat" (
|
||||
@call scripts\user_config.bat
|
||||
)
|
||||
|
||||
if exist "stable-diffusion\env" (
|
||||
@set PYTHONPATH=%PYTHONPATH%;%cd%\stable-diffusion\env\lib\site-packages
|
||||
)
|
||||
|
||||
if exist "scripts\get_config.py" (
|
||||
@FOR /F "tokens=* USEBACKQ" %%F IN (`python scripts\get_config.py --default=main update_branch`) DO (
|
||||
@SET update_branch=%%F
|
||||
)
|
||||
)
|
||||
|
||||
if "%update_branch%"=="" (
|
||||
set update_branch=main
|
||||
)
|
||||
|
||||
@>nul findstr /m "conda_sd_ui_deps_installed" scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
for /f "tokens=*" %%a in ('python -c "import os; parts = os.getcwd().split(os.path.sep); print(len(parts))"') do if "%%a" NEQ "2" (
|
||||
echo. & echo "!!!! WARNING !!!!" & echo.
|
||||
echo "Your 'stable-diffusion-ui' folder is at %cd%" & echo.
|
||||
echo "The 'stable-diffusion-ui' folder needs to be at the top of your drive, for e.g. 'C:\stable-diffusion-ui' or 'D:\stable-diffusion-ui' etc."
|
||||
echo "Not placing this folder at the top of a drive can cause errors on some computers."
|
||||
echo. & echo "Recommended: Please close this window and move the 'stable-diffusion-ui' folder to the top of a drive. For e.g. 'C:\stable-diffusion-ui'. Then run the installer again." & echo.
|
||||
echo "Not Recommended: If you're sure that you want to install at the current location, please press any key to continue." & echo.
|
||||
|
||||
pause
|
||||
)
|
||||
)
|
||||
|
||||
@>nul findstr /m "sd_ui_git_cloned" scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" EQU "0" (
|
||||
@echo "Easy Diffusion's git repository was already installed. Updating from %update_branch%.."
|
||||
|
||||
@cd sd-ui-files
|
||||
|
||||
@call git add -A .
|
||||
@call git stash
|
||||
@call git reset --hard
|
||||
@call git -c advice.detachedHead=false checkout "%update_branch%"
|
||||
@call git pull
|
||||
|
||||
@cd ..
|
||||
) else (
|
||||
@echo. & echo "Downloading Easy Diffusion..." & echo.
|
||||
@echo "Using the %update_branch% channel" & echo.
|
||||
|
||||
@call git clone -b "%update_branch%" https://github.com/easydiffusion/easydiffusion.git sd-ui-files && (
|
||||
@echo sd_ui_git_cloned >> scripts\install_status.txt
|
||||
) || (
|
||||
@echo "Error downloading Easy Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues" & echo "Thanks!"
|
||||
pause
|
||||
@exit /b
|
||||
)
|
||||
)
|
||||
|
||||
@xcopy sd-ui-files\ui ui /s /i /Y /q
|
||||
@copy sd-ui-files\scripts\on_sd_start.bat scripts\ /Y
|
||||
@copy sd-ui-files\scripts\check_modules.py scripts\ /Y
|
||||
@copy sd-ui-files\scripts\get_config.py scripts\ /Y
|
||||
@copy sd-ui-files\scripts\config.yaml.sample scripts\ /Y
|
||||
@copy "sd-ui-files\scripts\Start Stable Diffusion UI.cmd" . /Y
|
||||
@copy "sd-ui-files\scripts\Developer Console.cmd" . /Y
|
||||
|
||||
@call scripts\on_sd_start.bat
|
||||
|
||||
@pause
|
61
scripts/on_env_start.sh
Executable file
@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
source ./scripts/functions.sh
|
||||
|
||||
printf "\n\nEasy Diffusion - v3\n\n"
|
||||
|
||||
export PYTHONNOUSERSITE=y
|
||||
|
||||
if [ -f "scripts/config.sh" ]; then
|
||||
source scripts/config.sh
|
||||
fi
|
||||
|
||||
if [ -f "scripts/user_config.sh" ]; then
|
||||
source scripts/user_config.sh
|
||||
fi
|
||||
|
||||
export PYTHONPATH=$(pwd)/installer_files/env/lib/python3.8/site-packages:$(pwd)/stable-diffusion/env/lib/python3.8/site-packages
|
||||
|
||||
if [ -f "scripts/get_config.py" ]; then
|
||||
export update_branch="$( python scripts/get_config.py --default=main update_branch )"
|
||||
fi
|
||||
|
||||
if [ "$update_branch" == "" ]; then
|
||||
export update_branch="main"
|
||||
fi
|
||||
|
||||
if [ -f "scripts/install_status.txt" ] && [ `grep -c sd_ui_git_cloned scripts/install_status.txt` -gt "0" ]; then
|
||||
echo "Easy Diffusion's git repository was already installed. Updating from $update_branch.."
|
||||
|
||||
cd sd-ui-files
|
||||
|
||||
git add -A .
|
||||
git stash
|
||||
git reset --hard
|
||||
git -c advice.detachedHead=false checkout "$update_branch"
|
||||
git pull
|
||||
|
||||
cd ..
|
||||
else
|
||||
printf "\n\nDownloading Easy Diffusion..\n\n"
|
||||
printf "Using the $update_branch channel\n\n"
|
||||
|
||||
if git clone -b "$update_branch" https://github.com/easydiffusion/easydiffusion.git sd-ui-files ; then
|
||||
echo sd_ui_git_cloned >> scripts/install_status.txt
|
||||
else
|
||||
fail "git clone failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf ui
|
||||
cp -Rf sd-ui-files/ui .
|
||||
cp sd-ui-files/scripts/on_sd_start.sh scripts/
|
||||
cp sd-ui-files/scripts/bootstrap.sh scripts/
|
||||
cp sd-ui-files/scripts/check_modules.py scripts/
|
||||
cp sd-ui-files/scripts/get_config.py scripts/
|
||||
cp sd-ui-files/scripts/config.yaml.sample scripts/
|
||||
cp sd-ui-files/scripts/start.sh .
|
||||
cp sd-ui-files/scripts/developer_console.sh .
|
||||
cp sd-ui-files/scripts/functions.sh scripts/
|
||||
|
||||
exec ./scripts/on_sd_start.sh
|
124
scripts/on_sd_start.bat
Normal file
@ -0,0 +1,124 @@
|
||||
@echo off
|
||||
|
||||
@REM Caution, this file will make your eyes and brain bleed. It's such an unholy mess.
|
||||
@REM Note to self: Please rewrite this in Python. For the sake of your own sanity.
|
||||
|
||||
@copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y
|
||||
@copy sd-ui-files\scripts\check_modules.py scripts\ /Y
|
||||
@copy sd-ui-files\scripts\get_config.py scripts\ /Y
|
||||
@copy sd-ui-files\scripts\config.yaml.sample scripts\ /Y
|
||||
|
||||
if exist "%cd%\profile" (
|
||||
set HF_HOME=%cd%\profile\.cache\huggingface
|
||||
)
|
||||
|
||||
@rem set the correct installer path (current vs legacy)
|
||||
if exist "%cd%\installer_files\env" (
|
||||
set INSTALL_ENV_DIR=%cd%\installer_files\env
|
||||
)
|
||||
if exist "%cd%\stable-diffusion\env" (
|
||||
set INSTALL_ENV_DIR=%cd%\stable-diffusion\env
|
||||
)
|
||||
|
||||
@mkdir tmp
|
||||
@set TMP=%cd%\tmp
|
||||
@set TEMP=%cd%\tmp
|
||||
|
||||
@rem activate the installer env
|
||||
call conda activate
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo. & echo "Error activating conda for Easy Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
@REM remove the old version of the dev console script, if it's still present
|
||||
if exist "Open Developer Console.cmd" del "Open Developer Console.cmd"
|
||||
|
||||
@rem create the stable-diffusion folder, to work with legacy installations
|
||||
if not exist "stable-diffusion" mkdir stable-diffusion
|
||||
cd stable-diffusion
|
||||
|
||||
@rem activate the old stable-diffusion env, if it exists
|
||||
if exist "env" (
|
||||
call conda activate .\env
|
||||
)
|
||||
|
||||
@rem disable the legacy src and ldm folder (otherwise this prevents installing gfpgan and realesrgan)
|
||||
if exist src rename src src-old
|
||||
if exist ldm rename ldm ldm-old
|
||||
|
||||
|
||||
if not exist "%INSTALL_ENV_DIR%\DLLs\libssl-1_1-x64.dll" copy "%INSTALL_ENV_DIR%\Library\bin\libssl-1_1-x64.dll" "%INSTALL_ENV_DIR%\DLLs\"
|
||||
if not exist "%INSTALL_ENV_DIR%\DLLs\libcrypto-1_1-x64.dll" copy "%INSTALL_ENV_DIR%\Library\bin\libcrypto-1_1-x64.dll" "%INSTALL_ENV_DIR%\DLLs\"
|
||||
|
||||
@rem install or upgrade the required modules
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
@REM prevent from using packages from the user's home directory, to avoid conflicts
|
||||
set PYTHONNOUSERSITE=1
|
||||
set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages
|
||||
|
||||
@rem Download the required packages
|
||||
call python ..\scripts\check_modules.py
|
||||
if "%ERRORLEVEL%" NEQ "0" (
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
call WHERE uvicorn > .tmp
|
||||
@>nul findstr /m "uvicorn" .tmp
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo. & echo "UI packages not found! Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
@>nul findstr /m "conda_sd_ui_deps_installed" ..\scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo conda_sd_ui_deps_installed >> ..\scripts\install_status.txt
|
||||
)
|
||||
|
||||
@>nul findstr /m "sd_install_complete" ..\scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo sd_weights_downloaded >> ..\scripts\install_status.txt
|
||||
@echo sd_install_complete >> ..\scripts\install_status.txt
|
||||
)
|
||||
|
||||
@echo. & echo "Easy Diffusion installation complete! Starting the server!" & echo.
|
||||
|
||||
@set SD_DIR=%cd%
|
||||
|
||||
set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages
|
||||
echo PYTHONPATH=%PYTHONPATH%
|
||||
|
||||
call where python
|
||||
call python --version
|
||||
|
||||
@cd ..
|
||||
@set SD_UI_PATH=%cd%\ui
|
||||
|
||||
@FOR /F "tokens=* USEBACKQ" %%F IN (`python scripts\get_config.py --default=9000 net listen_port`) DO (
|
||||
@SET ED_BIND_PORT=%%F
|
||||
)
|
||||
|
||||
@FOR /F "tokens=* USEBACKQ" %%F IN (`python scripts\get_config.py --default=False net listen_to_network`) DO (
|
||||
if "%%F" EQU "True" (
|
||||
@FOR /F "tokens=* USEBACKQ" %%G IN (`python scripts\get_config.py --default=0.0.0.0 net bind_ip`) DO (
|
||||
@SET ED_BIND_IP=%%G
|
||||
)
|
||||
) else (
|
||||
@SET ED_BIND_IP=127.0.0.1
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@cd stable-diffusion
|
||||
|
||||
@rem set any overrides
|
||||
set HF_HUB_DISABLE_SYMLINKS_WARNING=true
|
||||
|
||||
@python -m uvicorn main:server_api --app-dir "%SD_UI_PATH%" --port %ED_BIND_PORT% --host %ED_BIND_IP% --log-level error
|
||||
|
||||
|
||||
@pause
|
85
scripts/on_sd_start.sh
Executable file
@ -0,0 +1,85 @@
|
||||
#!/bin/bash
|
||||
|
||||
cp sd-ui-files/scripts/functions.sh scripts/
|
||||
cp sd-ui-files/scripts/on_env_start.sh scripts/
|
||||
cp sd-ui-files/scripts/bootstrap.sh scripts/
|
||||
cp sd-ui-files/scripts/check_modules.py scripts/
|
||||
cp sd-ui-files/scripts/get_config.py scripts/
|
||||
cp sd-ui-files/scripts/config.yaml.sample scripts/
|
||||
|
||||
source ./scripts/functions.sh
|
||||
|
||||
# activate the installer env
|
||||
CONDA_BASEPATH=$(conda info --base)
|
||||
source "$CONDA_BASEPATH/etc/profile.d/conda.sh" # avoids the 'shell not initialized' error
|
||||
|
||||
conda activate || fail "Failed to activate conda"
|
||||
|
||||
# remove the old version of the dev console script, if it's still present
|
||||
if [ -e "open_dev_console.sh" ]; then
|
||||
rm "open_dev_console.sh"
|
||||
fi
|
||||
|
||||
# set the correct installer path (current vs legacy)
|
||||
if [ -e "installer_files/env" ]; then
|
||||
export INSTALL_ENV_DIR="$(pwd)/installer_files/env"
|
||||
fi
|
||||
if [ -e "stable-diffusion/env" ]; then
|
||||
export INSTALL_ENV_DIR="$(pwd)/stable-diffusion/env"
|
||||
fi
|
||||
|
||||
# create the stable-diffusion folder, to work with legacy installations
|
||||
if [ ! -e "stable-diffusion" ]; then mkdir stable-diffusion; fi
|
||||
cd stable-diffusion
|
||||
|
||||
# activate the old stable-diffusion env, if it exists
|
||||
if [ -e "env" ]; then
|
||||
conda activate ./env || fail "conda activate failed"
|
||||
fi
|
||||
|
||||
# disable the legacy src and ldm folder (otherwise this prevents installing gfpgan and realesrgan)
|
||||
if [ -e "src" ]; then mv src src-old; fi
|
||||
if [ -e "ldm" ]; then mv ldm ldm-old; fi
|
||||
|
||||
# Download the required packages
|
||||
if ! python ../scripts/check_modules.py; then
|
||||
read -p "Press any key to continue"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v uvicorn &> /dev/null; then
|
||||
fail "UI packages not found!"
|
||||
fi
|
||||
|
||||
if [ `grep -c sd_install_complete ../scripts/install_status.txt` -gt "0" ]; then
|
||||
echo sd_weights_downloaded >> ../scripts/install_status.txt
|
||||
echo sd_install_complete >> ../scripts/install_status.txt
|
||||
fi
|
||||
|
||||
printf "\n\nEasy Diffusion installation complete, starting the server!\n\n"
|
||||
|
||||
SD_PATH=`pwd`
|
||||
|
||||
export PYTORCH_ENABLE_MPS_FALLBACK=1
|
||||
export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages"
|
||||
echo "PYTHONPATH=$PYTHONPATH"
|
||||
|
||||
which python
|
||||
python --version
|
||||
|
||||
cd ..
|
||||
export SD_UI_PATH=`pwd`/ui
|
||||
export ED_BIND_PORT="$( python scripts/get_config.py --default=9000 net listen_port )"
|
||||
case "$( python scripts/get_config.py --default=False net listen_to_network )" in
|
||||
"True")
|
||||
export ED_BIND_IP=$( python scripts/get_config.py --default=0.0.0.0 net bind_ip)
|
||||
;;
|
||||
"False")
|
||||
export ED_BIND_IP=127.0.0.1
|
||||
;;
|
||||
esac
|
||||
cd stable-diffusion
|
||||
|
||||
uvicorn main:server_api --app-dir "$SD_UI_PATH" --port "$ED_BIND_PORT" --host "$ED_BIND_IP" --log-level error
|
||||
|
||||
read -p "Press any key to continue"
|
42
scripts/start.sh
Executable file
@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
if [ -f "on_sd_start.bat" ]; then
|
||||
echo ================================================================================
|
||||
echo
|
||||
echo !!!! WARNING !!!!
|
||||
echo
|
||||
echo It looks like you\'re trying to run the installation script from a source code
|
||||
echo download. This will not work.
|
||||
echo
|
||||
echo Recommended: Please close this window and download the installer from
|
||||
echo https://stable-diffusion-ui.github.io/docs/installation/
|
||||
echo
|
||||
echo ================================================================================
|
||||
echo
|
||||
read
|
||||
exit 1
|
||||
fi
|
||||
|
||||
unset PYTHONHOME
|
||||
|
||||
# set legacy installer's PATH, if it exists
|
||||
if [ -e "installer" ]; then export PATH="$(pwd)/installer/bin:$PATH"; fi
|
||||
|
||||
# Setup the packages required for the installer
|
||||
scripts/bootstrap.sh || exit 1
|
||||
|
||||
# set new installer's PATH, if it downloaded any packages
|
||||
if [ -e "installer_files/env" ]; then export PATH="$(pwd)/installer_files/env/bin:$PATH"; fi
|
||||
|
||||
# Test the bootstrap
|
||||
which git
|
||||
git --version || exit 1
|
||||
|
||||
which conda
|
||||
conda --version || exit 1
|
||||
|
||||
# Download the rest of the installer and UI
|
||||
chmod +x scripts/*.sh
|
||||
scripts/on_env_start.sh
|
2
scripts/win_enable_long_filepaths.ps1
Normal file
@ -0,0 +1,2 @@
|
||||
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name LongPathsEnabled -Type DWord -Value 1
|
||||
pause
|
18
start.sh
@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Stable Diffusion UI - v2.5"
|
||||
echo ""
|
||||
|
||||
export SD_BASE_DIR=$(pwd)
|
||||
|
||||
echo "Working in $SD_BASE_DIR"
|
||||
|
||||
# Setup the packages required for the installer
|
||||
installer/bootstrap/bootstrap.sh
|
||||
|
||||
# Test the bootstrap
|
||||
git --version
|
||||
python --version
|
||||
|
||||
# Download the rest of the installer and UI
|
||||
installer/installer/start.sh
|
445
ui/easydiffusion/app.py
Normal file
@ -0,0 +1,445 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
import sys
|
||||
import traceback
|
||||
import copy
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
import urllib
|
||||
import warnings
|
||||
|
||||
from easydiffusion import task_manager
|
||||
from easydiffusion.utils import log
|
||||
from rich.logging import RichHandler
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from sdkit.utils import log as sdkit_log # hack, so we can overwrite the log config
|
||||
|
||||
# Remove all handlers associated with the root logger object.
|
||||
for handler in logging.root.handlers[:]:
|
||||
logging.root.removeHandler(handler)
|
||||
|
||||
LOG_FORMAT = "%(asctime)s.%(msecs)03d %(levelname)s %(threadName)s %(message)s"
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format=LOG_FORMAT,
|
||||
datefmt="%X",
|
||||
handlers=[RichHandler(markup=True, rich_tracebacks=False, show_time=False, show_level=False)],
|
||||
)
|
||||
|
||||
SD_DIR = os.getcwd()
|
||||
|
||||
ROOT_DIR = os.path.abspath(os.path.join(SD_DIR, ".."))
|
||||
|
||||
SD_UI_DIR = os.getenv("SD_UI_PATH", None)
|
||||
|
||||
CONFIG_DIR = os.path.abspath(os.path.join(SD_UI_DIR, "..", "scripts"))
|
||||
MODELS_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "models"))
|
||||
BUCKET_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "bucket"))
|
||||
|
||||
USER_PLUGINS_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "plugins"))
|
||||
CORE_PLUGINS_DIR = os.path.abspath(os.path.join(SD_UI_DIR, "plugins"))
|
||||
|
||||
USER_UI_PLUGINS_DIR = os.path.join(USER_PLUGINS_DIR, "ui")
|
||||
CORE_UI_PLUGINS_DIR = os.path.join(CORE_PLUGINS_DIR, "ui")
|
||||
USER_SERVER_PLUGINS_DIR = os.path.join(USER_PLUGINS_DIR, "server")
|
||||
UI_PLUGINS_SOURCES = ((CORE_UI_PLUGINS_DIR, "core"), (USER_UI_PLUGINS_DIR, "user"))
|
||||
|
||||
sys.path.append(os.path.dirname(SD_UI_DIR))
|
||||
sys.path.append(USER_SERVER_PLUGINS_DIR)
|
||||
|
||||
OUTPUT_DIRNAME = "Stable Diffusion UI" # in the user's home folder
|
||||
PRESERVE_CONFIG_VARS = ["FORCE_FULL_PRECISION"]
|
||||
TASK_TTL = 15 * 60 # Discard last session's task timeout
|
||||
APP_CONFIG_DEFAULTS = {
|
||||
# auto: selects the cuda device with the most free memory, cuda: use the currently active cuda device.
|
||||
"render_devices": "auto", # valid entries: 'auto', 'cpu' or 'cuda:N' (where N is a GPU index)
|
||||
"update_branch": "main",
|
||||
"ui": {
|
||||
"open_browser_on_start": True,
|
||||
},
|
||||
"test_diffusers": True,
|
||||
}
|
||||
|
||||
IMAGE_EXTENSIONS = [
|
||||
".png",
|
||||
".apng",
|
||||
".jpg",
|
||||
".jpeg",
|
||||
".jfif",
|
||||
".pjpeg",
|
||||
".pjp",
|
||||
".jxl",
|
||||
".gif",
|
||||
".webp",
|
||||
".avif",
|
||||
".svg",
|
||||
]
|
||||
CUSTOM_MODIFIERS_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "modifiers"))
|
||||
CUSTOM_MODIFIERS_PORTRAIT_EXTENSIONS = [
|
||||
".portrait",
|
||||
"_portrait",
|
||||
" portrait",
|
||||
"-portrait",
|
||||
]
|
||||
CUSTOM_MODIFIERS_LANDSCAPE_EXTENSIONS = [
|
||||
".landscape",
|
||||
"_landscape",
|
||||
" landscape",
|
||||
"-landscape",
|
||||
]
|
||||
|
||||
|
||||
def init():
|
||||
os.makedirs(USER_UI_PLUGINS_DIR, exist_ok=True)
|
||||
os.makedirs(USER_SERVER_PLUGINS_DIR, exist_ok=True)
|
||||
|
||||
# https://pytorch.org/docs/stable/storage.html
|
||||
warnings.filterwarnings("ignore", category=UserWarning, message="TypedStorage is deprecated")
|
||||
|
||||
|
||||
def init_render_threads():
|
||||
load_server_plugins()
|
||||
|
||||
update_render_threads()
|
||||
|
||||
|
||||
def getConfig(default_val=APP_CONFIG_DEFAULTS):
|
||||
config_yaml_path = os.path.join(CONFIG_DIR, "..", "config.yaml")
|
||||
|
||||
# migrate the old config yaml location
|
||||
config_legacy_yaml = os.path.join(CONFIG_DIR, "config.yaml")
|
||||
if os.path.isfile(config_legacy_yaml):
|
||||
shutil.move(config_legacy_yaml, config_yaml_path)
|
||||
|
||||
def set_config_on_startup(config: dict):
|
||||
if getConfig.__test_diffusers_on_startup is None:
|
||||
getConfig.__test_diffusers_on_startup = config.get("test_diffusers", True)
|
||||
config["config_on_startup"] = {"test_diffusers": getConfig.__test_diffusers_on_startup}
|
||||
|
||||
if os.path.isfile(config_yaml_path):
|
||||
try:
|
||||
yaml = YAML()
|
||||
with open(config_yaml_path, "r", encoding="utf-8") as f:
|
||||
config = yaml.load(f)
|
||||
if "net" not in config:
|
||||
config["net"] = {}
|
||||
if os.getenv("SD_UI_BIND_PORT") is not None:
|
||||
config["net"]["listen_port"] = int(os.getenv("SD_UI_BIND_PORT"))
|
||||
else:
|
||||
config["net"]["listen_port"] = 9000
|
||||
if os.getenv("SD_UI_BIND_IP") is not None:
|
||||
config["net"]["listen_to_network"] = os.getenv("SD_UI_BIND_IP") == "0.0.0.0"
|
||||
else:
|
||||
config["net"]["listen_to_network"] = True
|
||||
|
||||
set_config_on_startup(config)
|
||||
|
||||
return config
|
||||
except Exception as e:
|
||||
log.warn(traceback.format_exc())
|
||||
set_config_on_startup(default_val)
|
||||
return default_val
|
||||
else:
|
||||
try:
|
||||
config_json_path = os.path.join(CONFIG_DIR, "config.json")
|
||||
if not os.path.exists(config_json_path):
|
||||
return default_val
|
||||
|
||||
log.info("Converting old json config file to yaml")
|
||||
with open(config_json_path, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
# Save config in new format
|
||||
setConfig(config)
|
||||
|
||||
with open(config_json_path + ".txt", "w") as f:
|
||||
f.write("Moved to config.yaml inside the Easy Diffusion folder. You can open it in any text editor.")
|
||||
os.remove(config_json_path)
|
||||
|
||||
return getConfig(default_val)
|
||||
except Exception as e:
|
||||
log.warn(traceback.format_exc())
|
||||
set_config_on_startup(default_val)
|
||||
return default_val
|
||||
|
||||
|
||||
getConfig.__test_diffusers_on_startup = None
|
||||
|
||||
|
||||
def setConfig(config):
|
||||
try: # config.yaml
|
||||
config_yaml_path = os.path.join(CONFIG_DIR, "..", "config.yaml")
|
||||
yaml = YAML()
|
||||
|
||||
if not hasattr(config, "_yaml_comment"):
|
||||
config_yaml_sample_path = os.path.join(CONFIG_DIR, "config.yaml.sample")
|
||||
|
||||
if os.path.exists(config_yaml_sample_path):
|
||||
with open(config_yaml_sample_path, "r", encoding="utf-8") as f:
|
||||
commented_config = yaml.load(f)
|
||||
|
||||
for k in config:
|
||||
commented_config[k] = config[k]
|
||||
|
||||
config = commented_config
|
||||
yaml.indent(mapping=2, sequence=4, offset=2)
|
||||
|
||||
if "config_on_startup" in config:
|
||||
del config["config_on_startup"]
|
||||
|
||||
try:
|
||||
f = open(config_yaml_path + ".tmp", "w", encoding="utf-8")
|
||||
yaml.dump(config, f)
|
||||
finally:
|
||||
f.close() # do this explicitly to avoid NUL bytes (possible rare bug when using 'with')
|
||||
|
||||
# verify that the new file is valid, and only then overwrite the old config file
|
||||
# helps prevent the rare NUL bytes error from corrupting the config file
|
||||
yaml = YAML()
|
||||
with open(config_yaml_path + ".tmp", "r", encoding="utf-8") as f:
|
||||
yaml.load(f)
|
||||
shutil.move(config_yaml_path + ".tmp", config_yaml_path)
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
|
||||
|
||||
def save_to_config(ckpt_model_name, vae_model_name, hypernetwork_model_name, vram_usage_level):
|
||||
config = getConfig()
|
||||
if "model" not in config:
|
||||
config["model"] = {}
|
||||
|
||||
config["model"]["stable-diffusion"] = ckpt_model_name
|
||||
config["model"]["vae"] = vae_model_name
|
||||
config["model"]["hypernetwork"] = hypernetwork_model_name
|
||||
|
||||
if vae_model_name is None or vae_model_name == "":
|
||||
del config["model"]["vae"]
|
||||
if hypernetwork_model_name is None or hypernetwork_model_name == "":
|
||||
del config["model"]["hypernetwork"]
|
||||
|
||||
config["vram_usage_level"] = vram_usage_level
|
||||
|
||||
setConfig(config)
|
||||
|
||||
|
||||
def update_render_threads():
|
||||
config = getConfig()
|
||||
render_devices = config.get("render_devices", "auto")
|
||||
active_devices = task_manager.get_devices()["active"].keys()
|
||||
|
||||
log.debug(f"requesting for render_devices: {render_devices}")
|
||||
task_manager.update_render_threads(render_devices, active_devices)
|
||||
|
||||
|
||||
def getUIPlugins():
|
||||
plugins = []
|
||||
|
||||
file_names = set()
|
||||
for plugins_dir, dir_prefix in UI_PLUGINS_SOURCES:
|
||||
for file in os.listdir(plugins_dir):
|
||||
if file.endswith(".plugin.js") and file not in file_names:
|
||||
plugins.append(f"/plugins/{dir_prefix}/{file}")
|
||||
file_names.add(file)
|
||||
|
||||
return plugins
|
||||
|
||||
|
||||
def load_server_plugins():
|
||||
if not os.path.exists(USER_SERVER_PLUGINS_DIR):
|
||||
return
|
||||
|
||||
import importlib
|
||||
|
||||
def load_plugin(file):
|
||||
mod_path = file.replace(".py", "")
|
||||
return importlib.import_module(mod_path)
|
||||
|
||||
def apply_plugin(file, plugin):
|
||||
if hasattr(plugin, "get_cond_and_uncond"):
|
||||
import sdkit.generate.image_generator
|
||||
|
||||
sdkit.generate.image_generator.get_cond_and_uncond = plugin.get_cond_and_uncond
|
||||
log.info(f"Overridden get_cond_and_uncond with the one in the server plugin: {file}")
|
||||
|
||||
for file in os.listdir(USER_SERVER_PLUGINS_DIR):
|
||||
file_path = os.path.join(USER_SERVER_PLUGINS_DIR, file)
|
||||
if (not os.path.isdir(file_path) and not file_path.endswith("_plugin.py")) or (
|
||||
os.path.isdir(file_path) and not file_path.endswith("_plugin")
|
||||
):
|
||||
continue
|
||||
|
||||
try:
|
||||
log.info(f"Loading server plugin: {file}")
|
||||
mod = load_plugin(file)
|
||||
|
||||
log.info(f"Applying server plugin: {file}")
|
||||
apply_plugin(file, mod)
|
||||
except:
|
||||
log.warn(f"Error while loading a server plugin")
|
||||
log.warn(traceback.format_exc())
|
||||
|
||||
|
||||
def getIPConfig():
|
||||
try:
|
||||
ips = socket.gethostbyname_ex(socket.gethostname())
|
||||
ips[2].append(ips[0])
|
||||
return ips[2]
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
return []
|
||||
|
||||
|
||||
def open_browser():
|
||||
config = getConfig()
|
||||
ui = config.get("ui", {})
|
||||
net = config.get("net", {})
|
||||
port = net.get("listen_port", 9000)
|
||||
|
||||
if ui.get("open_browser_on_start", True):
|
||||
import webbrowser
|
||||
|
||||
log.info("Opening browser..")
|
||||
|
||||
webbrowser.open(f"http://localhost:{port}")
|
||||
|
||||
Console().print(
|
||||
Panel(
|
||||
"\n"
|
||||
+ "[white]Easy Diffusion is ready to serve requests.\n\n"
|
||||
+ "A new browser tab should have been opened by now.\n"
|
||||
+ f"If not, please open your web browser and navigate to [bold yellow underline]http://localhost:{port}/\n",
|
||||
title="Easy Diffusion is ready",
|
||||
style="bold yellow on blue",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def fail_and_die(fail_type: str, data: str):
|
||||
suggestions = [
|
||||
"Run this installer again.",
|
||||
"If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB",
|
||||
"If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues",
|
||||
]
|
||||
|
||||
if fail_type == "model_download":
|
||||
fail_label = f"Error downloading the {data} model"
|
||||
suggestions.insert(
|
||||
1,
|
||||
"If that doesn't fix it, please try to download the file manually. The address to download from, and the destination to save to are printed above this message.",
|
||||
)
|
||||
else:
|
||||
fail_label = "Error while installing Easy Diffusion"
|
||||
|
||||
msg = [f"{fail_label}. Sorry about that, please try to:"]
|
||||
for i, suggestion in enumerate(suggestions):
|
||||
msg.append(f"{i+1}. {suggestion}")
|
||||
msg.append("Thanks!")
|
||||
|
||||
print("\n".join(msg))
|
||||
exit(1)
|
||||
|
||||
|
||||
def get_image_modifiers():
|
||||
modifiers_json_path = os.path.join(SD_UI_DIR, "modifiers.json")
|
||||
|
||||
modifier_categories = {}
|
||||
original_category_order = []
|
||||
with open(modifiers_json_path, "r", encoding="utf-8") as f:
|
||||
modifiers_file = json.load(f)
|
||||
|
||||
# The trailing slash is needed to support symlinks
|
||||
if not os.path.isdir(f"{CUSTOM_MODIFIERS_DIR}/"):
|
||||
return modifiers_file
|
||||
|
||||
# convert modifiers from a list of objects to a dict of dicts
|
||||
for category_item in modifiers_file:
|
||||
category_name = category_item["category"]
|
||||
original_category_order.append(category_name)
|
||||
category = {}
|
||||
for modifier_item in category_item["modifiers"]:
|
||||
modifier = {}
|
||||
for preview_item in modifier_item["previews"]:
|
||||
modifier[preview_item["name"]] = preview_item["path"]
|
||||
category[modifier_item["modifier"]] = modifier
|
||||
modifier_categories[category_name] = category
|
||||
|
||||
def scan_directory(directory_path: str, category_name="Modifiers"):
|
||||
for entry in os.scandir(directory_path):
|
||||
if entry.is_file():
|
||||
file_extension = list(filter(lambda e: entry.name.endswith(e), IMAGE_EXTENSIONS))
|
||||
if len(file_extension) == 0:
|
||||
continue
|
||||
|
||||
modifier_name = entry.name[: -len(file_extension[0])]
|
||||
modifier_path = f"custom/{entry.path[len(CUSTOM_MODIFIERS_DIR) + 1:]}"
|
||||
# URL encode path segments
|
||||
modifier_path = "/".join(
|
||||
map(
|
||||
lambda segment: urllib.parse.quote(segment),
|
||||
modifier_path.split("/"),
|
||||
)
|
||||
)
|
||||
is_portrait = True
|
||||
is_landscape = True
|
||||
|
||||
portrait_extension = list(
|
||||
filter(
|
||||
lambda e: modifier_name.lower().endswith(e),
|
||||
CUSTOM_MODIFIERS_PORTRAIT_EXTENSIONS,
|
||||
)
|
||||
)
|
||||
landscape_extension = list(
|
||||
filter(
|
||||
lambda e: modifier_name.lower().endswith(e),
|
||||
CUSTOM_MODIFIERS_LANDSCAPE_EXTENSIONS,
|
||||
)
|
||||
)
|
||||
|
||||
if len(portrait_extension) > 0:
|
||||
is_landscape = False
|
||||
modifier_name = modifier_name[: -len(portrait_extension[0])]
|
||||
elif len(landscape_extension) > 0:
|
||||
is_portrait = False
|
||||
modifier_name = modifier_name[: -len(landscape_extension[0])]
|
||||
|
||||
if category_name not in modifier_categories:
|
||||
modifier_categories[category_name] = {}
|
||||
|
||||
category = modifier_categories[category_name]
|
||||
|
||||
if modifier_name not in category:
|
||||
category[modifier_name] = {}
|
||||
|
||||
if is_portrait or "portrait" not in category[modifier_name]:
|
||||
category[modifier_name]["portrait"] = modifier_path
|
||||
|
||||
if is_landscape or "landscape" not in category[modifier_name]:
|
||||
category[modifier_name]["landscape"] = modifier_path
|
||||
elif entry.is_dir():
|
||||
scan_directory(
|
||||
entry.path,
|
||||
entry.name if directory_path == CUSTOM_MODIFIERS_DIR else f"{category_name}/{entry.name}",
|
||||
)
|
||||
|
||||
scan_directory(CUSTOM_MODIFIERS_DIR)
|
||||
|
||||
custom_categories = sorted(
|
||||
[cn for cn in modifier_categories.keys() if cn not in original_category_order],
|
||||
key=str.casefold,
|
||||
)
|
||||
|
||||
# convert the modifiers back into a list of objects
|
||||
modifier_categories_list = []
|
||||
for category_name in [*original_category_order, *custom_categories]:
|
||||
category = {"category": category_name, "modifiers": []}
|
||||
for modifier_name in sorted(modifier_categories[category_name].keys(), key=str.casefold):
|
||||
modifier = {"modifier": modifier_name, "previews": []}
|
||||
for preview_name, preview_path in modifier_categories[category_name][modifier_name].items():
|
||||
modifier["previews"].append({"name": preview_name, "path": preview_path})
|
||||
category["modifiers"].append(modifier)
|
||||
modifier_categories_list.append(category)
|
||||
|
||||
return modifier_categories_list
|
102
ui/easydiffusion/bucket_manager.py
Normal file
@ -0,0 +1,102 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException, Response, File
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from easydiffusion.easydb import crud, models, schemas
|
||||
from easydiffusion.easydb.database import SessionLocal, engine
|
||||
|
||||
from requests.compat import urlparse
|
||||
|
||||
import base64, json
|
||||
|
||||
MIME_TYPES = {
|
||||
"jpg": "image/jpeg",
|
||||
"jpeg": "image/jpeg",
|
||||
"gif": "image/gif",
|
||||
"png": "image/png",
|
||||
"webp": "image/webp",
|
||||
"js": "text/javascript",
|
||||
"htm": "text/html",
|
||||
"html": "text/html",
|
||||
"css": "text/css",
|
||||
"json": "application/json",
|
||||
"mjs": "application/json",
|
||||
"yaml": "application/yaml",
|
||||
"svg": "image/svg+xml",
|
||||
"txt": "text/plain",
|
||||
}
|
||||
|
||||
def init():
|
||||
from easydiffusion.server import server_api
|
||||
|
||||
models.BucketBase.metadata.create_all(bind=engine)
|
||||
|
||||
|
||||
# Dependency
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@server_api.get("/bucket/{obj_path:path}")
|
||||
def bucket_get_object(obj_path: str, db: Session = Depends(get_db)):
|
||||
filename = get_filename_from_url(obj_path)
|
||||
path = get_path_from_url(obj_path)
|
||||
|
||||
if filename==None:
|
||||
bucket = crud.get_bucket_by_path(db, path=path)
|
||||
if bucket == None:
|
||||
raise HTTPException(status_code=404, detail="Bucket not found")
|
||||
bucketfiles = db.query(models.BucketFile).with_entities(models.BucketFile.filename).filter(models.BucketFile.bucket_id == bucket.id).all()
|
||||
bucketfiles = [ x.filename for x in bucketfiles ]
|
||||
return bucketfiles
|
||||
|
||||
else:
|
||||
bucket_id = crud.get_bucket_by_path(db, path).id
|
||||
bucketfile = db.query(models.BucketFile).filter(models.BucketFile.bucket_id == bucket_id, models.BucketFile.filename == filename).first()
|
||||
|
||||
suffix = get_suffix_from_filename(filename)
|
||||
|
||||
return Response(content=bucketfile.data, media_type=MIME_TYPES.get(suffix, "application/octet-stream"))
|
||||
|
||||
@server_api.post("/bucket/{obj_path:path}")
|
||||
def bucket_post_object(obj_path: str, file: bytes = File(), db: Session = Depends(get_db)):
|
||||
filename = get_filename_from_url(obj_path)
|
||||
path = get_path_from_url(obj_path)
|
||||
bucket = crud.get_bucket_by_path(db, path)
|
||||
|
||||
if bucket == None:
|
||||
bucket = crud.create_bucket(db=db, bucket=schemas.BucketCreate(path=path))
|
||||
bucket_id = bucket.id
|
||||
|
||||
bucketfile = schemas.BucketFileCreate(filename=filename, data=file)
|
||||
result = crud.create_bucketfile(db=db, bucketfile=bucketfile, bucket_id=bucket_id)
|
||||
result.data = base64.encodestring(result.data)
|
||||
return result
|
||||
|
||||
|
||||
@server_api.post("/buckets/{bucket_id}/items/", response_model=schemas.BucketFile)
|
||||
def create_bucketfile_in_bucket(
|
||||
bucket_id: int, bucketfile: schemas.BucketFileCreate, db: Session = Depends(get_db)
|
||||
):
|
||||
bucketfile.data = base64.decodestring(bucketfile.data)
|
||||
result = crud.create_bucketfile(db=db, bucketfile=bucketfile, bucket_id=bucket_id)
|
||||
result.data = base64.encodestring(result.data)
|
||||
return result
|
||||
|
||||
|
||||
def get_filename_from_url(url):
|
||||
path = urlparse(url).path
|
||||
name = path[path.rfind('/')+1:]
|
||||
return name or None
|
||||
|
||||
def get_path_from_url(url):
|
||||
path = urlparse(url).path
|
||||
path = path[0:path.rfind('/')]
|
||||
return path or None
|
||||
|
||||
def get_suffix_from_filename(filename):
|
||||
return filename[filename.rfind('.')+1:]
|
257
ui/easydiffusion/device_manager.py
Normal file
@ -0,0 +1,257 @@
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import traceback
|
||||
|
||||
import torch
|
||||
from easydiffusion.utils import log
|
||||
|
||||
"""
|
||||
Set `FORCE_FULL_PRECISION` in the environment variables, or in `config.bat`/`config.sh` to set full precision (i.e. float32).
|
||||
Otherwise the models will load at half-precision (i.e. float16).
|
||||
|
||||
Half-precision is fine most of the time. Full precision is only needed for working around GPU bugs (like NVIDIA 16xx GPUs).
|
||||
"""
|
||||
|
||||
COMPARABLE_GPU_PERCENTILE = (
|
||||
0.65 # if a GPU's free_mem is within this % of the GPU with the most free_mem, it will be picked
|
||||
)
|
||||
|
||||
mem_free_threshold = 0
|
||||
|
||||
|
||||
def get_device_delta(render_devices, active_devices):
|
||||
"""
|
||||
render_devices: 'cpu', or 'auto', or 'mps' or ['cuda:N'...]
|
||||
active_devices: ['cpu', 'mps', 'cuda:N'...]
|
||||
"""
|
||||
|
||||
if render_devices in ("cpu", "auto", "mps"):
|
||||
render_devices = [render_devices]
|
||||
elif render_devices is not None:
|
||||
if isinstance(render_devices, str):
|
||||
render_devices = [render_devices]
|
||||
if isinstance(render_devices, list) and len(render_devices) > 0:
|
||||
render_devices = list(filter(lambda x: x.startswith("cuda:") or x == "mps", render_devices))
|
||||
if len(render_devices) == 0:
|
||||
raise Exception(
|
||||
'Invalid render_devices value in config.json. Valid: {"render_devices": ["cuda:0", "cuda:1"...]}, or {"render_devices": "cpu"} or {"render_devices": "mps"} or {"render_devices": "auto"}'
|
||||
)
|
||||
|
||||
render_devices = list(filter(lambda x: is_device_compatible(x), render_devices))
|
||||
if len(render_devices) == 0:
|
||||
raise Exception(
|
||||
"Sorry, none of the render_devices configured in config.json are compatible with Stable Diffusion"
|
||||
)
|
||||
else:
|
||||
raise Exception(
|
||||
'Invalid render_devices value in config.json. Valid: {"render_devices": ["cuda:0", "cuda:1"...]}, or {"render_devices": "cpu"} or {"render_devices": "auto"}'
|
||||
)
|
||||
else:
|
||||
render_devices = ["auto"]
|
||||
|
||||
if "auto" in render_devices:
|
||||
render_devices = auto_pick_devices(active_devices)
|
||||
if "cpu" in render_devices:
|
||||
log.warn("WARNING: Could not find a compatible GPU. Using the CPU, but this will be very slow!")
|
||||
|
||||
active_devices = set(active_devices)
|
||||
render_devices = set(render_devices)
|
||||
|
||||
devices_to_start = render_devices - active_devices
|
||||
devices_to_stop = active_devices - render_devices
|
||||
|
||||
return devices_to_start, devices_to_stop
|
||||
|
||||
|
||||
def is_mps_available():
|
||||
return (
|
||||
platform.system() == "Darwin"
|
||||
and hasattr(torch.backends, "mps")
|
||||
and torch.backends.mps.is_available()
|
||||
and torch.backends.mps.is_built()
|
||||
)
|
||||
|
||||
|
||||
def is_cuda_available():
|
||||
return torch.cuda.is_available()
|
||||
|
||||
|
||||
def auto_pick_devices(currently_active_devices):
|
||||
global mem_free_threshold
|
||||
|
||||
if is_mps_available():
|
||||
return ["mps"]
|
||||
|
||||
if not is_cuda_available():
|
||||
return ["cpu"]
|
||||
|
||||
device_count = torch.cuda.device_count()
|
||||
if device_count == 1:
|
||||
return ["cuda:0"] if is_device_compatible("cuda:0") else ["cpu"]
|
||||
|
||||
log.debug("Autoselecting GPU. Using most free memory.")
|
||||
devices = []
|
||||
for device in range(device_count):
|
||||
device = f"cuda:{device}"
|
||||
if not is_device_compatible(device):
|
||||
continue
|
||||
|
||||
mem_free, mem_total = torch.cuda.mem_get_info(device)
|
||||
mem_free /= float(10**9)
|
||||
mem_total /= float(10**9)
|
||||
device_name = torch.cuda.get_device_name(device)
|
||||
log.debug(
|
||||
f"{device} detected: {device_name} - Memory (free/total): {round(mem_free, 2)}Gb / {round(mem_total, 2)}Gb"
|
||||
)
|
||||
devices.append({"device": device, "device_name": device_name, "mem_free": mem_free})
|
||||
|
||||
devices.sort(key=lambda x: x["mem_free"], reverse=True)
|
||||
max_mem_free = devices[0]["mem_free"]
|
||||
curr_mem_free_threshold = COMPARABLE_GPU_PERCENTILE * max_mem_free
|
||||
mem_free_threshold = max(curr_mem_free_threshold, mem_free_threshold)
|
||||
|
||||
# Auto-pick algorithm:
|
||||
# 1. Pick the top 75 percentile of the GPUs, sorted by free_mem.
|
||||
# 2. Also include already-running devices (GPU-only), otherwise their free_mem will
|
||||
# always be very low (since their VRAM contains the model).
|
||||
# These already-running devices probably aren't terrible, since they were picked in the past.
|
||||
# Worst case, the user can restart the program and that'll get rid of them.
|
||||
devices = list(
|
||||
filter(
|
||||
(lambda x: x["mem_free"] > mem_free_threshold or x["device"] in currently_active_devices),
|
||||
devices,
|
||||
)
|
||||
)
|
||||
devices = list(map(lambda x: x["device"], devices))
|
||||
return devices
|
||||
|
||||
|
||||
def device_init(context, device):
|
||||
"""
|
||||
This function assumes the 'device' has already been verified to be compatible.
|
||||
`get_device_delta()` has already filtered out incompatible devices.
|
||||
"""
|
||||
|
||||
validate_device_id(device, log_prefix="device_init")
|
||||
|
||||
if "cuda" not in device:
|
||||
context.device = device
|
||||
context.device_name = get_processor_name()
|
||||
context.half_precision = False
|
||||
log.debug(f"Render device available as {context.device_name}")
|
||||
return
|
||||
|
||||
context.device_name = torch.cuda.get_device_name(device)
|
||||
context.device = device
|
||||
|
||||
# Force full precision on 1660 and 1650 NVIDIA cards to avoid creating green images
|
||||
if needs_to_force_full_precision(context):
|
||||
log.warn(f"forcing full precision on this GPU, to avoid green images. GPU detected: {context.device_name}")
|
||||
# Apply force_full_precision now before models are loaded.
|
||||
context.half_precision = False
|
||||
|
||||
log.info(f'Setting {device} as active, with precision: {"half" if context.half_precision else "full"}')
|
||||
torch.cuda.device(device)
|
||||
|
||||
|
||||
def needs_to_force_full_precision(context):
|
||||
if "FORCE_FULL_PRECISION" in os.environ:
|
||||
return True
|
||||
|
||||
device_name = context.device_name.lower()
|
||||
return (
|
||||
("nvidia" in device_name or "geforce" in device_name or "quadro" in device_name)
|
||||
and (
|
||||
" 1660" in device_name
|
||||
or " 1650" in device_name
|
||||
or " 1630" in device_name
|
||||
or " t400" in device_name
|
||||
or " t550" in device_name
|
||||
or " t600" in device_name
|
||||
or " t1000" in device_name
|
||||
or " t1200" in device_name
|
||||
or " t2000" in device_name
|
||||
)
|
||||
) or ("tesla k40m" in device_name)
|
||||
|
||||
|
||||
def get_max_vram_usage_level(device):
|
||||
if "cuda" in device:
|
||||
_, mem_total = torch.cuda.mem_get_info(device)
|
||||
else:
|
||||
return "high"
|
||||
|
||||
mem_total /= float(10**9)
|
||||
if mem_total < 4.5:
|
||||
return "low"
|
||||
elif mem_total < 6.5:
|
||||
return "balanced"
|
||||
|
||||
return "high"
|
||||
|
||||
|
||||
def validate_device_id(device, log_prefix=""):
|
||||
def is_valid():
|
||||
if not isinstance(device, str):
|
||||
return False
|
||||
if device == "cpu" or device == "mps":
|
||||
return True
|
||||
if not device.startswith("cuda:") or not device[5:].isnumeric():
|
||||
return False
|
||||
return True
|
||||
|
||||
if not is_valid():
|
||||
raise EnvironmentError(
|
||||
f"{log_prefix}: device id should be 'cpu', 'mps', or 'cuda:N' (where N is an integer index for the GPU). Got: {device}"
|
||||
)
|
||||
|
||||
|
||||
def is_device_compatible(device):
|
||||
"""
|
||||
Returns True/False, and prints any compatibility errors
|
||||
"""
|
||||
# static variable "history".
|
||||
is_device_compatible.history = getattr(is_device_compatible, "history", {})
|
||||
try:
|
||||
validate_device_id(device, log_prefix="is_device_compatible")
|
||||
except:
|
||||
log.error(str(e))
|
||||
return False
|
||||
|
||||
if device in ("cpu", "mps"):
|
||||
return True
|
||||
# Memory check
|
||||
try:
|
||||
_, mem_total = torch.cuda.mem_get_info(device)
|
||||
mem_total /= float(10**9)
|
||||
if mem_total < 1.9:
|
||||
if is_device_compatible.history.get(device) == None:
|
||||
log.warn(f"GPU {device} with less than 2 GB of VRAM is not compatible with Stable Diffusion")
|
||||
is_device_compatible.history[device] = 1
|
||||
return False
|
||||
except RuntimeError as e:
|
||||
log.error(str(e))
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_processor_name():
|
||||
try:
|
||||
import subprocess
|
||||
|
||||
if platform.system() == "Windows":
|
||||
return platform.processor()
|
||||
elif platform.system() == "Darwin":
|
||||
os.environ["PATH"] = os.environ["PATH"] + os.pathsep + "/usr/sbin"
|
||||
command = "sysctl -n machdep.cpu.brand_string"
|
||||
return subprocess.check_output(command, shell=True).decode().strip()
|
||||
elif platform.system() == "Linux":
|
||||
command = "cat /proc/cpuinfo"
|
||||
all_info = subprocess.check_output(command, shell=True).decode().strip()
|
||||
for line in all_info.split("\n"):
|
||||
if "model name" in line:
|
||||
return re.sub(".*model name.*:", "", line, 1).strip()
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return "cpu"
|
24
ui/easydiffusion/easydb/crud.py
Normal file
@ -0,0 +1,24 @@
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from easydiffusion.easydb import models, schemas
|
||||
|
||||
|
||||
def get_bucket_by_path(db: Session, path: str):
|
||||
return db.query(models.Bucket).filter(models.Bucket.path == path).first()
|
||||
|
||||
|
||||
def create_bucket(db: Session, bucket: schemas.BucketCreate):
|
||||
db_bucket = models.Bucket(path=bucket.path)
|
||||
db.add(db_bucket)
|
||||
db.commit()
|
||||
db.refresh(db_bucket)
|
||||
return db_bucket
|
||||
|
||||
|
||||
def create_bucketfile(db: Session, bucketfile: schemas.BucketFileCreate, bucket_id: int):
|
||||
db_bucketfile = models.BucketFile(**bucketfile.dict(), bucket_id=bucket_id)
|
||||
db.merge(db_bucketfile)
|
||||
db.commit()
|
||||
db_bucketfile = db.query(models.BucketFile).filter(models.BucketFile.bucket_id==bucket_id, models.BucketFile.filename==bucketfile.filename).first()
|
||||
return db_bucketfile
|
||||
|
14
ui/easydiffusion/easydb/database.py
Normal file
@ -0,0 +1,14 @@
|
||||
import os
|
||||
from easydiffusion import app
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
os.makedirs(app.BUCKET_DIR, exist_ok=True)
|
||||
SQLALCHEMY_DATABASE_URL = "sqlite:///"+os.path.join(app.BUCKET_DIR, "bucket.db")
|
||||
|
||||
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
BucketBase = declarative_base()
|
25
ui/easydiffusion/easydb/models.py
Normal file
@ -0,0 +1,25 @@
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, BLOB
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from easydiffusion.easydb.database import BucketBase
|
||||
|
||||
|
||||
class Bucket(BucketBase):
|
||||
__tablename__ = "bucket"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
path = Column(String, unique=True, index=True)
|
||||
|
||||
bucketfiles = relationship("BucketFile", back_populates="bucket")
|
||||
|
||||
|
||||
class BucketFile(BucketBase):
|
||||
__tablename__ = "bucketfile"
|
||||
|
||||
filename = Column(String, index=True, primary_key=True)
|
||||
bucket_id = Column(Integer, ForeignKey("bucket.id"), primary_key=True)
|
||||
|
||||
data = Column(BLOB, index=False)
|
||||
|
||||
bucket = relationship("Bucket", back_populates="bucketfiles")
|
||||
|
36
ui/easydiffusion/easydb/schemas.py
Normal file
@ -0,0 +1,36 @@
|
||||
from typing import List, Union
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class BucketFileBase(BaseModel):
|
||||
filename: str
|
||||
data: bytes
|
||||
|
||||
|
||||
class BucketFileCreate(BucketFileBase):
|
||||
pass
|
||||
|
||||
|
||||
class BucketFile(BucketFileBase):
|
||||
bucket_id: int
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class BucketBase(BaseModel):
|
||||
path: str
|
||||
|
||||
|
||||
class BucketCreate(BucketBase):
|
||||
pass
|
||||
|
||||
|
||||
class Bucket(BucketBase):
|
||||
id: int
|
||||
bucketfiles: List[BucketFile] = []
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|