fffiloni commited on
Commit
a282717
·
verified ·
1 Parent(s): ef931f5

Migrated files batch 32

Browse files
Files changed (26) hide show
  1. diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_1/special_tokens_map.json +30 -0
  2. diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_1/tokenizer_config.json +30 -0
  3. diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_1/vocab.json +0 -0
  4. diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_2/merges.txt +0 -0
  5. diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_2/special_tokens_map.json +30 -0
  6. diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_2/tokenizer_config.json +38 -0
  7. diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_2/vocab.json +0 -0
  8. diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_3/special_tokens_map.json +125 -0
  9. diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_3/spiece.model +3 -0
  10. diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_3/tokenizer.json +0 -0
  11. diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_3/tokenizer_config.json +940 -0
  12. diffsynth/tokenizer_configs/stable_diffusion_xl/tokenizer_2/merges.txt +0 -0
  13. diffsynth/tokenizer_configs/stable_diffusion_xl/tokenizer_2/special_tokens_map.json +24 -0
  14. diffsynth/tokenizer_configs/stable_diffusion_xl/tokenizer_2/tokenizer_config.json +38 -0
  15. diffsynth/tokenizer_configs/stable_diffusion_xl/tokenizer_2/vocab.json +0 -0
  16. diffsynth/trainers/__init__.py +0 -0
  17. diffsynth/trainers/text_to_image.py +318 -0
  18. diffsynth/trainers/utils.py +506 -0
  19. diffsynth/utils/__init__.py +261 -0
  20. diffsynth/vram_management/__init__.py +2 -0
  21. diffsynth/vram_management/gradient_checkpointing.py +34 -0
  22. diffsynth/vram_management/layers.py +167 -0
  23. third_party/Optional.md +0 -0
  24. tools/full_inference_modules_gradio.sh +162 -0
  25. tools/process_env_maps.py +599 -0
  26. tools/reorg_gbuffer_from_dr_delighting.py +129 -0
diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_1/special_tokens_map.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": {
3
+ "content": "<|startoftext|>",
4
+ "lstrip": false,
5
+ "normalized": true,
6
+ "rstrip": false,
7
+ "single_word": false
8
+ },
9
+ "eos_token": {
10
+ "content": "<|endoftext|>",
11
+ "lstrip": false,
12
+ "normalized": false,
13
+ "rstrip": false,
14
+ "single_word": false
15
+ },
16
+ "pad_token": {
17
+ "content": "<|endoftext|>",
18
+ "lstrip": false,
19
+ "normalized": false,
20
+ "rstrip": false,
21
+ "single_word": false
22
+ },
23
+ "unk_token": {
24
+ "content": "<|endoftext|>",
25
+ "lstrip": false,
26
+ "normalized": false,
27
+ "rstrip": false,
28
+ "single_word": false
29
+ }
30
+ }
diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_1/tokenizer_config.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "added_tokens_decoder": {
4
+ "49406": {
5
+ "content": "<|startoftext|>",
6
+ "lstrip": false,
7
+ "normalized": true,
8
+ "rstrip": false,
9
+ "single_word": false,
10
+ "special": true
11
+ },
12
+ "49407": {
13
+ "content": "<|endoftext|>",
14
+ "lstrip": false,
15
+ "normalized": false,
16
+ "rstrip": false,
17
+ "single_word": false,
18
+ "special": true
19
+ }
20
+ },
21
+ "bos_token": "<|startoftext|>",
22
+ "clean_up_tokenization_spaces": true,
23
+ "do_lower_case": true,
24
+ "eos_token": "<|endoftext|>",
25
+ "errors": "replace",
26
+ "model_max_length": 77,
27
+ "pad_token": "<|endoftext|>",
28
+ "tokenizer_class": "CLIPTokenizer",
29
+ "unk_token": "<|endoftext|>"
30
+ }
diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_1/vocab.json ADDED
The diff for this file is too large to render. See raw diff
 
diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_2/merges.txt ADDED
The diff for this file is too large to render. See raw diff
 
diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_2/special_tokens_map.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": {
3
+ "content": "<|startoftext|>",
4
+ "lstrip": false,
5
+ "normalized": true,
6
+ "rstrip": false,
7
+ "single_word": false
8
+ },
9
+ "eos_token": {
10
+ "content": "<|endoftext|>",
11
+ "lstrip": false,
12
+ "normalized": false,
13
+ "rstrip": false,
14
+ "single_word": false
15
+ },
16
+ "pad_token": {
17
+ "content": "!",
18
+ "lstrip": false,
19
+ "normalized": false,
20
+ "rstrip": false,
21
+ "single_word": false
22
+ },
23
+ "unk_token": {
24
+ "content": "<|endoftext|>",
25
+ "lstrip": false,
26
+ "normalized": false,
27
+ "rstrip": false,
28
+ "single_word": false
29
+ }
30
+ }
diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_2/tokenizer_config.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "added_tokens_decoder": {
4
+ "0": {
5
+ "content": "!",
6
+ "lstrip": false,
7
+ "normalized": false,
8
+ "rstrip": false,
9
+ "single_word": false,
10
+ "special": true
11
+ },
12
+ "49406": {
13
+ "content": "<|startoftext|>",
14
+ "lstrip": false,
15
+ "normalized": true,
16
+ "rstrip": false,
17
+ "single_word": false,
18
+ "special": true
19
+ },
20
+ "49407": {
21
+ "content": "<|endoftext|>",
22
+ "lstrip": false,
23
+ "normalized": false,
24
+ "rstrip": false,
25
+ "single_word": false,
26
+ "special": true
27
+ }
28
+ },
29
+ "bos_token": "<|startoftext|>",
30
+ "clean_up_tokenization_spaces": true,
31
+ "do_lower_case": true,
32
+ "eos_token": "<|endoftext|>",
33
+ "errors": "replace",
34
+ "model_max_length": 77,
35
+ "pad_token": "!",
36
+ "tokenizer_class": "CLIPTokenizer",
37
+ "unk_token": "<|endoftext|>"
38
+ }
diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_2/vocab.json ADDED
The diff for this file is too large to render. See raw diff
 
diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_3/special_tokens_map.json ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "additional_special_tokens": [
3
+ "<extra_id_0>",
4
+ "<extra_id_1>",
5
+ "<extra_id_2>",
6
+ "<extra_id_3>",
7
+ "<extra_id_4>",
8
+ "<extra_id_5>",
9
+ "<extra_id_6>",
10
+ "<extra_id_7>",
11
+ "<extra_id_8>",
12
+ "<extra_id_9>",
13
+ "<extra_id_10>",
14
+ "<extra_id_11>",
15
+ "<extra_id_12>",
16
+ "<extra_id_13>",
17
+ "<extra_id_14>",
18
+ "<extra_id_15>",
19
+ "<extra_id_16>",
20
+ "<extra_id_17>",
21
+ "<extra_id_18>",
22
+ "<extra_id_19>",
23
+ "<extra_id_20>",
24
+ "<extra_id_21>",
25
+ "<extra_id_22>",
26
+ "<extra_id_23>",
27
+ "<extra_id_24>",
28
+ "<extra_id_25>",
29
+ "<extra_id_26>",
30
+ "<extra_id_27>",
31
+ "<extra_id_28>",
32
+ "<extra_id_29>",
33
+ "<extra_id_30>",
34
+ "<extra_id_31>",
35
+ "<extra_id_32>",
36
+ "<extra_id_33>",
37
+ "<extra_id_34>",
38
+ "<extra_id_35>",
39
+ "<extra_id_36>",
40
+ "<extra_id_37>",
41
+ "<extra_id_38>",
42
+ "<extra_id_39>",
43
+ "<extra_id_40>",
44
+ "<extra_id_41>",
45
+ "<extra_id_42>",
46
+ "<extra_id_43>",
47
+ "<extra_id_44>",
48
+ "<extra_id_45>",
49
+ "<extra_id_46>",
50
+ "<extra_id_47>",
51
+ "<extra_id_48>",
52
+ "<extra_id_49>",
53
+ "<extra_id_50>",
54
+ "<extra_id_51>",
55
+ "<extra_id_52>",
56
+ "<extra_id_53>",
57
+ "<extra_id_54>",
58
+ "<extra_id_55>",
59
+ "<extra_id_56>",
60
+ "<extra_id_57>",
61
+ "<extra_id_58>",
62
+ "<extra_id_59>",
63
+ "<extra_id_60>",
64
+ "<extra_id_61>",
65
+ "<extra_id_62>",
66
+ "<extra_id_63>",
67
+ "<extra_id_64>",
68
+ "<extra_id_65>",
69
+ "<extra_id_66>",
70
+ "<extra_id_67>",
71
+ "<extra_id_68>",
72
+ "<extra_id_69>",
73
+ "<extra_id_70>",
74
+ "<extra_id_71>",
75
+ "<extra_id_72>",
76
+ "<extra_id_73>",
77
+ "<extra_id_74>",
78
+ "<extra_id_75>",
79
+ "<extra_id_76>",
80
+ "<extra_id_77>",
81
+ "<extra_id_78>",
82
+ "<extra_id_79>",
83
+ "<extra_id_80>",
84
+ "<extra_id_81>",
85
+ "<extra_id_82>",
86
+ "<extra_id_83>",
87
+ "<extra_id_84>",
88
+ "<extra_id_85>",
89
+ "<extra_id_86>",
90
+ "<extra_id_87>",
91
+ "<extra_id_88>",
92
+ "<extra_id_89>",
93
+ "<extra_id_90>",
94
+ "<extra_id_91>",
95
+ "<extra_id_92>",
96
+ "<extra_id_93>",
97
+ "<extra_id_94>",
98
+ "<extra_id_95>",
99
+ "<extra_id_96>",
100
+ "<extra_id_97>",
101
+ "<extra_id_98>",
102
+ "<extra_id_99>"
103
+ ],
104
+ "eos_token": {
105
+ "content": "</s>",
106
+ "lstrip": false,
107
+ "normalized": false,
108
+ "rstrip": false,
109
+ "single_word": false
110
+ },
111
+ "pad_token": {
112
+ "content": "<pad>",
113
+ "lstrip": false,
114
+ "normalized": false,
115
+ "rstrip": false,
116
+ "single_word": false
117
+ },
118
+ "unk_token": {
119
+ "content": "<unk>",
120
+ "lstrip": false,
121
+ "normalized": false,
122
+ "rstrip": false,
123
+ "single_word": false
124
+ }
125
+ }
diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_3/spiece.model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d60acb128cf7b7f2536e8f38a5b18a05535c9e14c7a355904270e15b0945ea86
3
+ size 791656
diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_3/tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
diffsynth/tokenizer_configs/stable_diffusion_3/tokenizer_3/tokenizer_config.json ADDED
@@ -0,0 +1,940 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": true,
3
+ "added_tokens_decoder": {
4
+ "0": {
5
+ "content": "<pad>",
6
+ "lstrip": false,
7
+ "normalized": false,
8
+ "rstrip": false,
9
+ "single_word": false,
10
+ "special": true
11
+ },
12
+ "1": {
13
+ "content": "</s>",
14
+ "lstrip": false,
15
+ "normalized": false,
16
+ "rstrip": false,
17
+ "single_word": false,
18
+ "special": true
19
+ },
20
+ "2": {
21
+ "content": "<unk>",
22
+ "lstrip": false,
23
+ "normalized": false,
24
+ "rstrip": false,
25
+ "single_word": false,
26
+ "special": true
27
+ },
28
+ "32000": {
29
+ "content": "<extra_id_99>",
30
+ "lstrip": true,
31
+ "normalized": false,
32
+ "rstrip": true,
33
+ "single_word": false,
34
+ "special": true
35
+ },
36
+ "32001": {
37
+ "content": "<extra_id_98>",
38
+ "lstrip": true,
39
+ "normalized": false,
40
+ "rstrip": true,
41
+ "single_word": false,
42
+ "special": true
43
+ },
44
+ "32002": {
45
+ "content": "<extra_id_97>",
46
+ "lstrip": true,
47
+ "normalized": false,
48
+ "rstrip": true,
49
+ "single_word": false,
50
+ "special": true
51
+ },
52
+ "32003": {
53
+ "content": "<extra_id_96>",
54
+ "lstrip": true,
55
+ "normalized": false,
56
+ "rstrip": true,
57
+ "single_word": false,
58
+ "special": true
59
+ },
60
+ "32004": {
61
+ "content": "<extra_id_95>",
62
+ "lstrip": true,
63
+ "normalized": false,
64
+ "rstrip": true,
65
+ "single_word": false,
66
+ "special": true
67
+ },
68
+ "32005": {
69
+ "content": "<extra_id_94>",
70
+ "lstrip": true,
71
+ "normalized": false,
72
+ "rstrip": true,
73
+ "single_word": false,
74
+ "special": true
75
+ },
76
+ "32006": {
77
+ "content": "<extra_id_93>",
78
+ "lstrip": true,
79
+ "normalized": false,
80
+ "rstrip": true,
81
+ "single_word": false,
82
+ "special": true
83
+ },
84
+ "32007": {
85
+ "content": "<extra_id_92>",
86
+ "lstrip": true,
87
+ "normalized": false,
88
+ "rstrip": true,
89
+ "single_word": false,
90
+ "special": true
91
+ },
92
+ "32008": {
93
+ "content": "<extra_id_91>",
94
+ "lstrip": true,
95
+ "normalized": false,
96
+ "rstrip": true,
97
+ "single_word": false,
98
+ "special": true
99
+ },
100
+ "32009": {
101
+ "content": "<extra_id_90>",
102
+ "lstrip": true,
103
+ "normalized": false,
104
+ "rstrip": true,
105
+ "single_word": false,
106
+ "special": true
107
+ },
108
+ "32010": {
109
+ "content": "<extra_id_89>",
110
+ "lstrip": true,
111
+ "normalized": false,
112
+ "rstrip": true,
113
+ "single_word": false,
114
+ "special": true
115
+ },
116
+ "32011": {
117
+ "content": "<extra_id_88>",
118
+ "lstrip": true,
119
+ "normalized": false,
120
+ "rstrip": true,
121
+ "single_word": false,
122
+ "special": true
123
+ },
124
+ "32012": {
125
+ "content": "<extra_id_87>",
126
+ "lstrip": true,
127
+ "normalized": false,
128
+ "rstrip": true,
129
+ "single_word": false,
130
+ "special": true
131
+ },
132
+ "32013": {
133
+ "content": "<extra_id_86>",
134
+ "lstrip": true,
135
+ "normalized": false,
136
+ "rstrip": true,
137
+ "single_word": false,
138
+ "special": true
139
+ },
140
+ "32014": {
141
+ "content": "<extra_id_85>",
142
+ "lstrip": true,
143
+ "normalized": false,
144
+ "rstrip": true,
145
+ "single_word": false,
146
+ "special": true
147
+ },
148
+ "32015": {
149
+ "content": "<extra_id_84>",
150
+ "lstrip": true,
151
+ "normalized": false,
152
+ "rstrip": true,
153
+ "single_word": false,
154
+ "special": true
155
+ },
156
+ "32016": {
157
+ "content": "<extra_id_83>",
158
+ "lstrip": true,
159
+ "normalized": false,
160
+ "rstrip": true,
161
+ "single_word": false,
162
+ "special": true
163
+ },
164
+ "32017": {
165
+ "content": "<extra_id_82>",
166
+ "lstrip": true,
167
+ "normalized": false,
168
+ "rstrip": true,
169
+ "single_word": false,
170
+ "special": true
171
+ },
172
+ "32018": {
173
+ "content": "<extra_id_81>",
174
+ "lstrip": true,
175
+ "normalized": false,
176
+ "rstrip": true,
177
+ "single_word": false,
178
+ "special": true
179
+ },
180
+ "32019": {
181
+ "content": "<extra_id_80>",
182
+ "lstrip": true,
183
+ "normalized": false,
184
+ "rstrip": true,
185
+ "single_word": false,
186
+ "special": true
187
+ },
188
+ "32020": {
189
+ "content": "<extra_id_79>",
190
+ "lstrip": true,
191
+ "normalized": false,
192
+ "rstrip": true,
193
+ "single_word": false,
194
+ "special": true
195
+ },
196
+ "32021": {
197
+ "content": "<extra_id_78>",
198
+ "lstrip": true,
199
+ "normalized": false,
200
+ "rstrip": true,
201
+ "single_word": false,
202
+ "special": true
203
+ },
204
+ "32022": {
205
+ "content": "<extra_id_77>",
206
+ "lstrip": true,
207
+ "normalized": false,
208
+ "rstrip": true,
209
+ "single_word": false,
210
+ "special": true
211
+ },
212
+ "32023": {
213
+ "content": "<extra_id_76>",
214
+ "lstrip": true,
215
+ "normalized": false,
216
+ "rstrip": true,
217
+ "single_word": false,
218
+ "special": true
219
+ },
220
+ "32024": {
221
+ "content": "<extra_id_75>",
222
+ "lstrip": true,
223
+ "normalized": false,
224
+ "rstrip": true,
225
+ "single_word": false,
226
+ "special": true
227
+ },
228
+ "32025": {
229
+ "content": "<extra_id_74>",
230
+ "lstrip": true,
231
+ "normalized": false,
232
+ "rstrip": true,
233
+ "single_word": false,
234
+ "special": true
235
+ },
236
+ "32026": {
237
+ "content": "<extra_id_73>",
238
+ "lstrip": true,
239
+ "normalized": false,
240
+ "rstrip": true,
241
+ "single_word": false,
242
+ "special": true
243
+ },
244
+ "32027": {
245
+ "content": "<extra_id_72>",
246
+ "lstrip": true,
247
+ "normalized": false,
248
+ "rstrip": true,
249
+ "single_word": false,
250
+ "special": true
251
+ },
252
+ "32028": {
253
+ "content": "<extra_id_71>",
254
+ "lstrip": true,
255
+ "normalized": false,
256
+ "rstrip": true,
257
+ "single_word": false,
258
+ "special": true
259
+ },
260
+ "32029": {
261
+ "content": "<extra_id_70>",
262
+ "lstrip": true,
263
+ "normalized": false,
264
+ "rstrip": true,
265
+ "single_word": false,
266
+ "special": true
267
+ },
268
+ "32030": {
269
+ "content": "<extra_id_69>",
270
+ "lstrip": true,
271
+ "normalized": false,
272
+ "rstrip": true,
273
+ "single_word": false,
274
+ "special": true
275
+ },
276
+ "32031": {
277
+ "content": "<extra_id_68>",
278
+ "lstrip": true,
279
+ "normalized": false,
280
+ "rstrip": true,
281
+ "single_word": false,
282
+ "special": true
283
+ },
284
+ "32032": {
285
+ "content": "<extra_id_67>",
286
+ "lstrip": true,
287
+ "normalized": false,
288
+ "rstrip": true,
289
+ "single_word": false,
290
+ "special": true
291
+ },
292
+ "32033": {
293
+ "content": "<extra_id_66>",
294
+ "lstrip": true,
295
+ "normalized": false,
296
+ "rstrip": true,
297
+ "single_word": false,
298
+ "special": true
299
+ },
300
+ "32034": {
301
+ "content": "<extra_id_65>",
302
+ "lstrip": true,
303
+ "normalized": false,
304
+ "rstrip": true,
305
+ "single_word": false,
306
+ "special": true
307
+ },
308
+ "32035": {
309
+ "content": "<extra_id_64>",
310
+ "lstrip": true,
311
+ "normalized": false,
312
+ "rstrip": true,
313
+ "single_word": false,
314
+ "special": true
315
+ },
316
+ "32036": {
317
+ "content": "<extra_id_63>",
318
+ "lstrip": true,
319
+ "normalized": false,
320
+ "rstrip": true,
321
+ "single_word": false,
322
+ "special": true
323
+ },
324
+ "32037": {
325
+ "content": "<extra_id_62>",
326
+ "lstrip": true,
327
+ "normalized": false,
328
+ "rstrip": true,
329
+ "single_word": false,
330
+ "special": true
331
+ },
332
+ "32038": {
333
+ "content": "<extra_id_61>",
334
+ "lstrip": true,
335
+ "normalized": false,
336
+ "rstrip": true,
337
+ "single_word": false,
338
+ "special": true
339
+ },
340
+ "32039": {
341
+ "content": "<extra_id_60>",
342
+ "lstrip": true,
343
+ "normalized": false,
344
+ "rstrip": true,
345
+ "single_word": false,
346
+ "special": true
347
+ },
348
+ "32040": {
349
+ "content": "<extra_id_59>",
350
+ "lstrip": true,
351
+ "normalized": false,
352
+ "rstrip": true,
353
+ "single_word": false,
354
+ "special": true
355
+ },
356
+ "32041": {
357
+ "content": "<extra_id_58>",
358
+ "lstrip": true,
359
+ "normalized": false,
360
+ "rstrip": true,
361
+ "single_word": false,
362
+ "special": true
363
+ },
364
+ "32042": {
365
+ "content": "<extra_id_57>",
366
+ "lstrip": true,
367
+ "normalized": false,
368
+ "rstrip": true,
369
+ "single_word": false,
370
+ "special": true
371
+ },
372
+ "32043": {
373
+ "content": "<extra_id_56>",
374
+ "lstrip": true,
375
+ "normalized": false,
376
+ "rstrip": true,
377
+ "single_word": false,
378
+ "special": true
379
+ },
380
+ "32044": {
381
+ "content": "<extra_id_55>",
382
+ "lstrip": true,
383
+ "normalized": false,
384
+ "rstrip": true,
385
+ "single_word": false,
386
+ "special": true
387
+ },
388
+ "32045": {
389
+ "content": "<extra_id_54>",
390
+ "lstrip": true,
391
+ "normalized": false,
392
+ "rstrip": true,
393
+ "single_word": false,
394
+ "special": true
395
+ },
396
+ "32046": {
397
+ "content": "<extra_id_53>",
398
+ "lstrip": true,
399
+ "normalized": false,
400
+ "rstrip": true,
401
+ "single_word": false,
402
+ "special": true
403
+ },
404
+ "32047": {
405
+ "content": "<extra_id_52>",
406
+ "lstrip": true,
407
+ "normalized": false,
408
+ "rstrip": true,
409
+ "single_word": false,
410
+ "special": true
411
+ },
412
+ "32048": {
413
+ "content": "<extra_id_51>",
414
+ "lstrip": true,
415
+ "normalized": false,
416
+ "rstrip": true,
417
+ "single_word": false,
418
+ "special": true
419
+ },
420
+ "32049": {
421
+ "content": "<extra_id_50>",
422
+ "lstrip": true,
423
+ "normalized": false,
424
+ "rstrip": true,
425
+ "single_word": false,
426
+ "special": true
427
+ },
428
+ "32050": {
429
+ "content": "<extra_id_49>",
430
+ "lstrip": true,
431
+ "normalized": false,
432
+ "rstrip": true,
433
+ "single_word": false,
434
+ "special": true
435
+ },
436
+ "32051": {
437
+ "content": "<extra_id_48>",
438
+ "lstrip": true,
439
+ "normalized": false,
440
+ "rstrip": true,
441
+ "single_word": false,
442
+ "special": true
443
+ },
444
+ "32052": {
445
+ "content": "<extra_id_47>",
446
+ "lstrip": true,
447
+ "normalized": false,
448
+ "rstrip": true,
449
+ "single_word": false,
450
+ "special": true
451
+ },
452
+ "32053": {
453
+ "content": "<extra_id_46>",
454
+ "lstrip": true,
455
+ "normalized": false,
456
+ "rstrip": true,
457
+ "single_word": false,
458
+ "special": true
459
+ },
460
+ "32054": {
461
+ "content": "<extra_id_45>",
462
+ "lstrip": true,
463
+ "normalized": false,
464
+ "rstrip": true,
465
+ "single_word": false,
466
+ "special": true
467
+ },
468
+ "32055": {
469
+ "content": "<extra_id_44>",
470
+ "lstrip": true,
471
+ "normalized": false,
472
+ "rstrip": true,
473
+ "single_word": false,
474
+ "special": true
475
+ },
476
+ "32056": {
477
+ "content": "<extra_id_43>",
478
+ "lstrip": true,
479
+ "normalized": false,
480
+ "rstrip": true,
481
+ "single_word": false,
482
+ "special": true
483
+ },
484
+ "32057": {
485
+ "content": "<extra_id_42>",
486
+ "lstrip": true,
487
+ "normalized": false,
488
+ "rstrip": true,
489
+ "single_word": false,
490
+ "special": true
491
+ },
492
+ "32058": {
493
+ "content": "<extra_id_41>",
494
+ "lstrip": true,
495
+ "normalized": false,
496
+ "rstrip": true,
497
+ "single_word": false,
498
+ "special": true
499
+ },
500
+ "32059": {
501
+ "content": "<extra_id_40>",
502
+ "lstrip": true,
503
+ "normalized": false,
504
+ "rstrip": true,
505
+ "single_word": false,
506
+ "special": true
507
+ },
508
+ "32060": {
509
+ "content": "<extra_id_39>",
510
+ "lstrip": true,
511
+ "normalized": false,
512
+ "rstrip": true,
513
+ "single_word": false,
514
+ "special": true
515
+ },
516
+ "32061": {
517
+ "content": "<extra_id_38>",
518
+ "lstrip": true,
519
+ "normalized": false,
520
+ "rstrip": true,
521
+ "single_word": false,
522
+ "special": true
523
+ },
524
+ "32062": {
525
+ "content": "<extra_id_37>",
526
+ "lstrip": true,
527
+ "normalized": false,
528
+ "rstrip": true,
529
+ "single_word": false,
530
+ "special": true
531
+ },
532
+ "32063": {
533
+ "content": "<extra_id_36>",
534
+ "lstrip": true,
535
+ "normalized": false,
536
+ "rstrip": true,
537
+ "single_word": false,
538
+ "special": true
539
+ },
540
+ "32064": {
541
+ "content": "<extra_id_35>",
542
+ "lstrip": true,
543
+ "normalized": false,
544
+ "rstrip": true,
545
+ "single_word": false,
546
+ "special": true
547
+ },
548
+ "32065": {
549
+ "content": "<extra_id_34>",
550
+ "lstrip": true,
551
+ "normalized": false,
552
+ "rstrip": true,
553
+ "single_word": false,
554
+ "special": true
555
+ },
556
+ "32066": {
557
+ "content": "<extra_id_33>",
558
+ "lstrip": true,
559
+ "normalized": false,
560
+ "rstrip": true,
561
+ "single_word": false,
562
+ "special": true
563
+ },
564
+ "32067": {
565
+ "content": "<extra_id_32>",
566
+ "lstrip": true,
567
+ "normalized": false,
568
+ "rstrip": true,
569
+ "single_word": false,
570
+ "special": true
571
+ },
572
+ "32068": {
573
+ "content": "<extra_id_31>",
574
+ "lstrip": true,
575
+ "normalized": false,
576
+ "rstrip": true,
577
+ "single_word": false,
578
+ "special": true
579
+ },
580
+ "32069": {
581
+ "content": "<extra_id_30>",
582
+ "lstrip": true,
583
+ "normalized": false,
584
+ "rstrip": true,
585
+ "single_word": false,
586
+ "special": true
587
+ },
588
+ "32070": {
589
+ "content": "<extra_id_29>",
590
+ "lstrip": true,
591
+ "normalized": false,
592
+ "rstrip": true,
593
+ "single_word": false,
594
+ "special": true
595
+ },
596
+ "32071": {
597
+ "content": "<extra_id_28>",
598
+ "lstrip": true,
599
+ "normalized": false,
600
+ "rstrip": true,
601
+ "single_word": false,
602
+ "special": true
603
+ },
604
+ "32072": {
605
+ "content": "<extra_id_27>",
606
+ "lstrip": true,
607
+ "normalized": false,
608
+ "rstrip": true,
609
+ "single_word": false,
610
+ "special": true
611
+ },
612
+ "32073": {
613
+ "content": "<extra_id_26>",
614
+ "lstrip": true,
615
+ "normalized": false,
616
+ "rstrip": true,
617
+ "single_word": false,
618
+ "special": true
619
+ },
620
+ "32074": {
621
+ "content": "<extra_id_25>",
622
+ "lstrip": true,
623
+ "normalized": false,
624
+ "rstrip": true,
625
+ "single_word": false,
626
+ "special": true
627
+ },
628
+ "32075": {
629
+ "content": "<extra_id_24>",
630
+ "lstrip": true,
631
+ "normalized": false,
632
+ "rstrip": true,
633
+ "single_word": false,
634
+ "special": true
635
+ },
636
+ "32076": {
637
+ "content": "<extra_id_23>",
638
+ "lstrip": true,
639
+ "normalized": false,
640
+ "rstrip": true,
641
+ "single_word": false,
642
+ "special": true
643
+ },
644
+ "32077": {
645
+ "content": "<extra_id_22>",
646
+ "lstrip": true,
647
+ "normalized": false,
648
+ "rstrip": true,
649
+ "single_word": false,
650
+ "special": true
651
+ },
652
+ "32078": {
653
+ "content": "<extra_id_21>",
654
+ "lstrip": true,
655
+ "normalized": false,
656
+ "rstrip": true,
657
+ "single_word": false,
658
+ "special": true
659
+ },
660
+ "32079": {
661
+ "content": "<extra_id_20>",
662
+ "lstrip": true,
663
+ "normalized": false,
664
+ "rstrip": true,
665
+ "single_word": false,
666
+ "special": true
667
+ },
668
+ "32080": {
669
+ "content": "<extra_id_19>",
670
+ "lstrip": true,
671
+ "normalized": false,
672
+ "rstrip": true,
673
+ "single_word": false,
674
+ "special": true
675
+ },
676
+ "32081": {
677
+ "content": "<extra_id_18>",
678
+ "lstrip": true,
679
+ "normalized": false,
680
+ "rstrip": true,
681
+ "single_word": false,
682
+ "special": true
683
+ },
684
+ "32082": {
685
+ "content": "<extra_id_17>",
686
+ "lstrip": true,
687
+ "normalized": false,
688
+ "rstrip": true,
689
+ "single_word": false,
690
+ "special": true
691
+ },
692
+ "32083": {
693
+ "content": "<extra_id_16>",
694
+ "lstrip": true,
695
+ "normalized": false,
696
+ "rstrip": true,
697
+ "single_word": false,
698
+ "special": true
699
+ },
700
+ "32084": {
701
+ "content": "<extra_id_15>",
702
+ "lstrip": true,
703
+ "normalized": false,
704
+ "rstrip": true,
705
+ "single_word": false,
706
+ "special": true
707
+ },
708
+ "32085": {
709
+ "content": "<extra_id_14>",
710
+ "lstrip": true,
711
+ "normalized": false,
712
+ "rstrip": true,
713
+ "single_word": false,
714
+ "special": true
715
+ },
716
+ "32086": {
717
+ "content": "<extra_id_13>",
718
+ "lstrip": true,
719
+ "normalized": false,
720
+ "rstrip": true,
721
+ "single_word": false,
722
+ "special": true
723
+ },
724
+ "32087": {
725
+ "content": "<extra_id_12>",
726
+ "lstrip": true,
727
+ "normalized": false,
728
+ "rstrip": true,
729
+ "single_word": false,
730
+ "special": true
731
+ },
732
+ "32088": {
733
+ "content": "<extra_id_11>",
734
+ "lstrip": true,
735
+ "normalized": false,
736
+ "rstrip": true,
737
+ "single_word": false,
738
+ "special": true
739
+ },
740
+ "32089": {
741
+ "content": "<extra_id_10>",
742
+ "lstrip": true,
743
+ "normalized": false,
744
+ "rstrip": true,
745
+ "single_word": false,
746
+ "special": true
747
+ },
748
+ "32090": {
749
+ "content": "<extra_id_9>",
750
+ "lstrip": true,
751
+ "normalized": false,
752
+ "rstrip": true,
753
+ "single_word": false,
754
+ "special": true
755
+ },
756
+ "32091": {
757
+ "content": "<extra_id_8>",
758
+ "lstrip": true,
759
+ "normalized": false,
760
+ "rstrip": true,
761
+ "single_word": false,
762
+ "special": true
763
+ },
764
+ "32092": {
765
+ "content": "<extra_id_7>",
766
+ "lstrip": true,
767
+ "normalized": false,
768
+ "rstrip": true,
769
+ "single_word": false,
770
+ "special": true
771
+ },
772
+ "32093": {
773
+ "content": "<extra_id_6>",
774
+ "lstrip": true,
775
+ "normalized": false,
776
+ "rstrip": true,
777
+ "single_word": false,
778
+ "special": true
779
+ },
780
+ "32094": {
781
+ "content": "<extra_id_5>",
782
+ "lstrip": true,
783
+ "normalized": false,
784
+ "rstrip": true,
785
+ "single_word": false,
786
+ "special": true
787
+ },
788
+ "32095": {
789
+ "content": "<extra_id_4>",
790
+ "lstrip": true,
791
+ "normalized": false,
792
+ "rstrip": true,
793
+ "single_word": false,
794
+ "special": true
795
+ },
796
+ "32096": {
797
+ "content": "<extra_id_3>",
798
+ "lstrip": true,
799
+ "normalized": false,
800
+ "rstrip": true,
801
+ "single_word": false,
802
+ "special": true
803
+ },
804
+ "32097": {
805
+ "content": "<extra_id_2>",
806
+ "lstrip": true,
807
+ "normalized": false,
808
+ "rstrip": true,
809
+ "single_word": false,
810
+ "special": true
811
+ },
812
+ "32098": {
813
+ "content": "<extra_id_1>",
814
+ "lstrip": true,
815
+ "normalized": false,
816
+ "rstrip": true,
817
+ "single_word": false,
818
+ "special": true
819
+ },
820
+ "32099": {
821
+ "content": "<extra_id_0>",
822
+ "lstrip": true,
823
+ "normalized": false,
824
+ "rstrip": true,
825
+ "single_word": false,
826
+ "special": true
827
+ }
828
+ },
829
+ "additional_special_tokens": [
830
+ "<extra_id_0>",
831
+ "<extra_id_1>",
832
+ "<extra_id_2>",
833
+ "<extra_id_3>",
834
+ "<extra_id_4>",
835
+ "<extra_id_5>",
836
+ "<extra_id_6>",
837
+ "<extra_id_7>",
838
+ "<extra_id_8>",
839
+ "<extra_id_9>",
840
+ "<extra_id_10>",
841
+ "<extra_id_11>",
842
+ "<extra_id_12>",
843
+ "<extra_id_13>",
844
+ "<extra_id_14>",
845
+ "<extra_id_15>",
846
+ "<extra_id_16>",
847
+ "<extra_id_17>",
848
+ "<extra_id_18>",
849
+ "<extra_id_19>",
850
+ "<extra_id_20>",
851
+ "<extra_id_21>",
852
+ "<extra_id_22>",
853
+ "<extra_id_23>",
854
+ "<extra_id_24>",
855
+ "<extra_id_25>",
856
+ "<extra_id_26>",
857
+ "<extra_id_27>",
858
+ "<extra_id_28>",
859
+ "<extra_id_29>",
860
+ "<extra_id_30>",
861
+ "<extra_id_31>",
862
+ "<extra_id_32>",
863
+ "<extra_id_33>",
864
+ "<extra_id_34>",
865
+ "<extra_id_35>",
866
+ "<extra_id_36>",
867
+ "<extra_id_37>",
868
+ "<extra_id_38>",
869
+ "<extra_id_39>",
870
+ "<extra_id_40>",
871
+ "<extra_id_41>",
872
+ "<extra_id_42>",
873
+ "<extra_id_43>",
874
+ "<extra_id_44>",
875
+ "<extra_id_45>",
876
+ "<extra_id_46>",
877
+ "<extra_id_47>",
878
+ "<extra_id_48>",
879
+ "<extra_id_49>",
880
+ "<extra_id_50>",
881
+ "<extra_id_51>",
882
+ "<extra_id_52>",
883
+ "<extra_id_53>",
884
+ "<extra_id_54>",
885
+ "<extra_id_55>",
886
+ "<extra_id_56>",
887
+ "<extra_id_57>",
888
+ "<extra_id_58>",
889
+ "<extra_id_59>",
890
+ "<extra_id_60>",
891
+ "<extra_id_61>",
892
+ "<extra_id_62>",
893
+ "<extra_id_63>",
894
+ "<extra_id_64>",
895
+ "<extra_id_65>",
896
+ "<extra_id_66>",
897
+ "<extra_id_67>",
898
+ "<extra_id_68>",
899
+ "<extra_id_69>",
900
+ "<extra_id_70>",
901
+ "<extra_id_71>",
902
+ "<extra_id_72>",
903
+ "<extra_id_73>",
904
+ "<extra_id_74>",
905
+ "<extra_id_75>",
906
+ "<extra_id_76>",
907
+ "<extra_id_77>",
908
+ "<extra_id_78>",
909
+ "<extra_id_79>",
910
+ "<extra_id_80>",
911
+ "<extra_id_81>",
912
+ "<extra_id_82>",
913
+ "<extra_id_83>",
914
+ "<extra_id_84>",
915
+ "<extra_id_85>",
916
+ "<extra_id_86>",
917
+ "<extra_id_87>",
918
+ "<extra_id_88>",
919
+ "<extra_id_89>",
920
+ "<extra_id_90>",
921
+ "<extra_id_91>",
922
+ "<extra_id_92>",
923
+ "<extra_id_93>",
924
+ "<extra_id_94>",
925
+ "<extra_id_95>",
926
+ "<extra_id_96>",
927
+ "<extra_id_97>",
928
+ "<extra_id_98>",
929
+ "<extra_id_99>"
930
+ ],
931
+ "clean_up_tokenization_spaces": true,
932
+ "eos_token": "</s>",
933
+ "extra_ids": 100,
934
+ "legacy": true,
935
+ "model_max_length": 512,
936
+ "pad_token": "<pad>",
937
+ "sp_model_kwargs": {},
938
+ "tokenizer_class": "T5Tokenizer",
939
+ "unk_token": "<unk>"
940
+ }
diffsynth/tokenizer_configs/stable_diffusion_xl/tokenizer_2/merges.txt ADDED
The diff for this file is too large to render. See raw diff
 
diffsynth/tokenizer_configs/stable_diffusion_xl/tokenizer_2/special_tokens_map.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": {
3
+ "content": "<|startoftext|>",
4
+ "lstrip": false,
5
+ "normalized": true,
6
+ "rstrip": false,
7
+ "single_word": false
8
+ },
9
+ "eos_token": {
10
+ "content": "<|endoftext|>",
11
+ "lstrip": false,
12
+ "normalized": true,
13
+ "rstrip": false,
14
+ "single_word": false
15
+ },
16
+ "pad_token": "!",
17
+ "unk_token": {
18
+ "content": "<|endoftext|>",
19
+ "lstrip": false,
20
+ "normalized": true,
21
+ "rstrip": false,
22
+ "single_word": false
23
+ }
24
+ }
diffsynth/tokenizer_configs/stable_diffusion_xl/tokenizer_2/tokenizer_config.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "added_tokens_decoder": {
4
+ "0": {
5
+ "content": "!",
6
+ "lstrip": false,
7
+ "normalized": false,
8
+ "rstrip": false,
9
+ "single_word": false,
10
+ "special": true
11
+ },
12
+ "49406": {
13
+ "content": "<|startoftext|>",
14
+ "lstrip": false,
15
+ "normalized": true,
16
+ "rstrip": false,
17
+ "single_word": false,
18
+ "special": true
19
+ },
20
+ "49407": {
21
+ "content": "<|endoftext|>",
22
+ "lstrip": false,
23
+ "normalized": true,
24
+ "rstrip": false,
25
+ "single_word": false,
26
+ "special": true
27
+ }
28
+ },
29
+ "bos_token": "<|startoftext|>",
30
+ "clean_up_tokenization_spaces": true,
31
+ "do_lower_case": true,
32
+ "eos_token": "<|endoftext|>",
33
+ "errors": "replace",
34
+ "model_max_length": 77,
35
+ "pad_token": "!",
36
+ "tokenizer_class": "CLIPTokenizer",
37
+ "unk_token": "<|endoftext|>"
38
+ }
diffsynth/tokenizer_configs/stable_diffusion_xl/tokenizer_2/vocab.json ADDED
The diff for this file is too large to render. See raw diff
 
diffsynth/trainers/__init__.py ADDED
File without changes
diffsynth/trainers/text_to_image.py ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import lightning as pl
2
+ from peft import LoraConfig, inject_adapter_in_model
3
+ import torch, os
4
+ from ..data.simple_text_image import TextImageDataset
5
+ from modelscope.hub.api import HubApi
6
+ from ..models.utils import load_state_dict
7
+
8
+
9
+
10
+ class LightningModelForT2ILoRA(pl.LightningModule):
11
+ def __init__(
12
+ self,
13
+ learning_rate=1e-4,
14
+ use_gradient_checkpointing=True,
15
+ state_dict_converter=None,
16
+ ):
17
+ super().__init__()
18
+ # Set parameters
19
+ self.learning_rate = learning_rate
20
+ self.use_gradient_checkpointing = use_gradient_checkpointing
21
+ self.state_dict_converter = state_dict_converter
22
+ self.lora_alpha = None
23
+
24
+
25
+ def load_models(self):
26
+ # This function is implemented in other modules
27
+ self.pipe = None
28
+
29
+
30
+ def freeze_parameters(self):
31
+ # Freeze parameters
32
+ self.pipe.requires_grad_(False)
33
+ self.pipe.eval()
34
+ self.pipe.denoising_model().train()
35
+
36
+
37
+ def add_lora_to_model(self, model, lora_rank=4, lora_alpha=4, lora_target_modules="to_q,to_k,to_v,to_out", init_lora_weights="gaussian", pretrained_lora_path=None, state_dict_converter=None):
38
+ # Add LoRA to UNet
39
+ self.lora_alpha = lora_alpha
40
+ if init_lora_weights == "kaiming":
41
+ init_lora_weights = True
42
+
43
+ lora_config = LoraConfig(
44
+ r=lora_rank,
45
+ lora_alpha=lora_alpha,
46
+ init_lora_weights=init_lora_weights,
47
+ target_modules=lora_target_modules.split(","),
48
+ )
49
+ model = inject_adapter_in_model(lora_config, model)
50
+ for param in model.parameters():
51
+ # Upcast LoRA parameters into fp32
52
+ if param.requires_grad:
53
+ param.data = param.to(torch.float32)
54
+
55
+ # Lora pretrained lora weights
56
+ if pretrained_lora_path is not None:
57
+ state_dict = load_state_dict(pretrained_lora_path)
58
+ if state_dict_converter is not None:
59
+ state_dict = state_dict_converter(state_dict)
60
+ missing_keys, unexpected_keys = model.load_state_dict(state_dict, strict=False)
61
+ all_keys = [i for i, _ in model.named_parameters()]
62
+ num_updated_keys = len(all_keys) - len(missing_keys)
63
+ num_unexpected_keys = len(unexpected_keys)
64
+ print(f"{num_updated_keys} parameters are loaded from {pretrained_lora_path}. {num_unexpected_keys} parameters are unexpected.")
65
+
66
+
67
+ def training_step(self, batch, batch_idx):
68
+ # Data
69
+ text, image = batch["text"], batch["image"]
70
+
71
+ # Prepare input parameters
72
+ self.pipe.device = self.device
73
+ prompt_emb = self.pipe.encode_prompt(text, positive=True)
74
+ if "latents" in batch:
75
+ latents = batch["latents"].to(dtype=self.pipe.torch_dtype, device=self.device)
76
+ else:
77
+ latents = self.pipe.vae_encoder(image.to(dtype=self.pipe.torch_dtype, device=self.device))
78
+ noise = torch.randn_like(latents)
79
+ timestep_id = torch.randint(0, self.pipe.scheduler.num_train_timesteps, (1,))
80
+ timestep = self.pipe.scheduler.timesteps[timestep_id].to(self.device)
81
+ extra_input = self.pipe.prepare_extra_input(latents)
82
+ noisy_latents = self.pipe.scheduler.add_noise(latents, noise, timestep)
83
+ training_target = self.pipe.scheduler.training_target(latents, noise, timestep)
84
+
85
+ # Compute loss
86
+ noise_pred = self.pipe.denoising_model()(
87
+ noisy_latents, timestep=timestep, **prompt_emb, **extra_input,
88
+ use_gradient_checkpointing=self.use_gradient_checkpointing
89
+ )
90
+ loss = torch.nn.functional.mse_loss(noise_pred.float(), training_target.float())
91
+ loss = loss * self.pipe.scheduler.training_weight(timestep)
92
+
93
+ # Record log
94
+ self.log("train_loss", loss, prog_bar=True)
95
+ return loss
96
+
97
+
98
+ def configure_optimizers(self):
99
+ trainable_modules = filter(lambda p: p.requires_grad, self.pipe.denoising_model().parameters())
100
+ optimizer = torch.optim.AdamW(trainable_modules, lr=self.learning_rate)
101
+ return optimizer
102
+
103
+
104
+ def on_save_checkpoint(self, checkpoint):
105
+ checkpoint.clear()
106
+ trainable_param_names = list(filter(lambda named_param: named_param[1].requires_grad, self.pipe.denoising_model().named_parameters()))
107
+ trainable_param_names = set([named_param[0] for named_param in trainable_param_names])
108
+ state_dict = self.pipe.denoising_model().state_dict()
109
+ lora_state_dict = {}
110
+ for name, param in state_dict.items():
111
+ if name in trainable_param_names:
112
+ lora_state_dict[name] = param
113
+ if self.state_dict_converter is not None:
114
+ lora_state_dict = self.state_dict_converter(lora_state_dict, alpha=self.lora_alpha)
115
+ checkpoint.update(lora_state_dict)
116
+
117
+
118
+
119
+ def add_general_parsers(parser):
120
+ parser.add_argument(
121
+ "--dataset_path",
122
+ type=str,
123
+ default=None,
124
+ required=True,
125
+ help="The path of the Dataset.",
126
+ )
127
+ parser.add_argument(
128
+ "--output_path",
129
+ type=str,
130
+ default="./",
131
+ help="Path to save the model.",
132
+ )
133
+ parser.add_argument(
134
+ "--steps_per_epoch",
135
+ type=int,
136
+ default=500,
137
+ help="Number of steps per epoch.",
138
+ )
139
+ parser.add_argument(
140
+ "--height",
141
+ type=int,
142
+ default=1024,
143
+ help="Image height.",
144
+ )
145
+ parser.add_argument(
146
+ "--width",
147
+ type=int,
148
+ default=1024,
149
+ help="Image width.",
150
+ )
151
+ parser.add_argument(
152
+ "--center_crop",
153
+ default=False,
154
+ action="store_true",
155
+ help=(
156
+ "Whether to center crop the input images to the resolution. If not set, the images will be randomly"
157
+ " cropped. The images will be resized to the resolution first before cropping."
158
+ ),
159
+ )
160
+ parser.add_argument(
161
+ "--random_flip",
162
+ default=False,
163
+ action="store_true",
164
+ help="Whether to randomly flip images horizontally",
165
+ )
166
+ parser.add_argument(
167
+ "--batch_size",
168
+ type=int,
169
+ default=1,
170
+ help="Batch size (per device) for the training dataloader.",
171
+ )
172
+ parser.add_argument(
173
+ "--dataloader_num_workers",
174
+ type=int,
175
+ default=0,
176
+ help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.",
177
+ )
178
+ parser.add_argument(
179
+ "--precision",
180
+ type=str,
181
+ default="16-mixed",
182
+ choices=["32", "16", "16-mixed", "bf16"],
183
+ help="Training precision",
184
+ )
185
+ parser.add_argument(
186
+ "--learning_rate",
187
+ type=float,
188
+ default=1e-4,
189
+ help="Learning rate.",
190
+ )
191
+ parser.add_argument(
192
+ "--lora_rank",
193
+ type=int,
194
+ default=4,
195
+ help="The dimension of the LoRA update matrices.",
196
+ )
197
+ parser.add_argument(
198
+ "--lora_alpha",
199
+ type=float,
200
+ default=4.0,
201
+ help="The weight of the LoRA update matrices.",
202
+ )
203
+ parser.add_argument(
204
+ "--init_lora_weights",
205
+ type=str,
206
+ default="kaiming",
207
+ choices=["gaussian", "kaiming"],
208
+ help="The initializing method of LoRA weight.",
209
+ )
210
+ parser.add_argument(
211
+ "--use_gradient_checkpointing",
212
+ default=False,
213
+ action="store_true",
214
+ help="Whether to use gradient checkpointing.",
215
+ )
216
+ parser.add_argument(
217
+ "--accumulate_grad_batches",
218
+ type=int,
219
+ default=1,
220
+ help="The number of batches in gradient accumulation.",
221
+ )
222
+ parser.add_argument(
223
+ "--training_strategy",
224
+ type=str,
225
+ default="auto",
226
+ choices=["auto", "deepspeed_stage_1", "deepspeed_stage_2", "deepspeed_stage_3"],
227
+ help="Training strategy",
228
+ )
229
+ parser.add_argument(
230
+ "--max_epochs",
231
+ type=int,
232
+ default=1,
233
+ help="Number of epochs.",
234
+ )
235
+ parser.add_argument(
236
+ "--modelscope_model_id",
237
+ type=str,
238
+ default=None,
239
+ help="Model ID on ModelScope (https://www.modelscope.cn/). The model will be uploaded to ModelScope automatically if you provide a Model ID.",
240
+ )
241
+ parser.add_argument(
242
+ "--modelscope_access_token",
243
+ type=str,
244
+ default=None,
245
+ help="Access key on ModelScope (https://www.modelscope.cn/). Required if you want to upload the model to ModelScope.",
246
+ )
247
+ parser.add_argument(
248
+ "--pretrained_lora_path",
249
+ type=str,
250
+ default=None,
251
+ help="Pretrained LoRA path. Required if the training is resumed.",
252
+ )
253
+ parser.add_argument(
254
+ "--use_swanlab",
255
+ default=False,
256
+ action="store_true",
257
+ help="Whether to use SwanLab logger.",
258
+ )
259
+ parser.add_argument(
260
+ "--swanlab_mode",
261
+ default=None,
262
+ help="SwanLab mode (cloud or local).",
263
+ )
264
+ return parser
265
+
266
+
267
+ def launch_training_task(model, args):
268
+ # dataset and data loader
269
+ dataset = TextImageDataset(
270
+ args.dataset_path,
271
+ steps_per_epoch=args.steps_per_epoch * args.batch_size,
272
+ height=args.height,
273
+ width=args.width,
274
+ center_crop=args.center_crop,
275
+ random_flip=args.random_flip
276
+ )
277
+ train_loader = torch.utils.data.DataLoader(
278
+ dataset,
279
+ shuffle=True,
280
+ batch_size=args.batch_size,
281
+ num_workers=args.dataloader_num_workers
282
+ )
283
+ # train
284
+ if args.use_swanlab:
285
+ from swanlab.integration.pytorch_lightning import SwanLabLogger
286
+ swanlab_config = {"UPPERFRAMEWORK": "DiffSynth-Studio"}
287
+ swanlab_config.update(vars(args))
288
+ swanlab_logger = SwanLabLogger(
289
+ project="diffsynth_studio",
290
+ name="diffsynth_studio",
291
+ config=swanlab_config,
292
+ mode=args.swanlab_mode,
293
+ logdir=os.path.join(args.output_path, "swanlog"),
294
+ )
295
+ logger = [swanlab_logger]
296
+ else:
297
+ logger = None
298
+ trainer = pl.Trainer(
299
+ max_epochs=args.max_epochs,
300
+ accelerator="gpu",
301
+ devices="auto",
302
+ precision=args.precision,
303
+ strategy=args.training_strategy,
304
+ default_root_dir=args.output_path,
305
+ accumulate_grad_batches=args.accumulate_grad_batches,
306
+ callbacks=[pl.pytorch.callbacks.ModelCheckpoint(save_top_k=-1)],
307
+ logger=logger,
308
+ )
309
+ trainer.fit(model=model, train_dataloaders=train_loader)
310
+
311
+ # Upload models
312
+ if args.modelscope_model_id is not None and args.modelscope_access_token is not None:
313
+ print(f"Uploading models to modelscope. model_id: {args.modelscope_model_id} local_path: {trainer.log_dir}")
314
+ with open(os.path.join(trainer.log_dir, "configuration.json"), "w", encoding="utf-8") as f:
315
+ f.write('{"framework":"Pytorch","task":"text-to-image-synthesis"}\n')
316
+ api = HubApi()
317
+ api.login(args.modelscope_access_token)
318
+ api.push_model(model_id=args.modelscope_model_id, model_dir=trainer.log_dir)
diffsynth/trainers/utils.py ADDED
@@ -0,0 +1,506 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import imageio, os, torch, warnings, torchvision, argparse, json
2
+ from peft import LoraConfig, inject_adapter_in_model
3
+ from PIL import Image
4
+ import pandas as pd
5
+ from tqdm import tqdm
6
+ from accelerate import Accelerator
7
+
8
+
9
+
10
+ class ImageDataset(torch.utils.data.Dataset):
11
+ def __init__(
12
+ self,
13
+ base_path=None, metadata_path=None,
14
+ max_pixels=1920*1080, height=None, width=None,
15
+ height_division_factor=16, width_division_factor=16,
16
+ data_file_keys=("image",),
17
+ image_file_extension=("jpg", "jpeg", "png", "webp"),
18
+ repeat=1,
19
+ args=None,
20
+ ):
21
+ if args is not None:
22
+ base_path = args.dataset_base_path
23
+ metadata_path = args.dataset_metadata_path
24
+ height = args.height
25
+ width = args.width
26
+ max_pixels = args.max_pixels
27
+ data_file_keys = args.data_file_keys.split(",")
28
+ repeat = args.dataset_repeat
29
+
30
+ self.base_path = base_path
31
+ self.max_pixels = max_pixels
32
+ self.height = height
33
+ self.width = width
34
+ self.height_division_factor = height_division_factor
35
+ self.width_division_factor = width_division_factor
36
+ self.data_file_keys = data_file_keys
37
+ self.image_file_extension = image_file_extension
38
+ self.repeat = repeat
39
+
40
+ if height is not None and width is not None:
41
+ print("Height and width are fixed. Setting `dynamic_resolution` to False.")
42
+ self.dynamic_resolution = False
43
+ elif height is None and width is None:
44
+ print("Height and width are none. Setting `dynamic_resolution` to True.")
45
+ self.dynamic_resolution = True
46
+
47
+ if metadata_path is None:
48
+ print("No metadata. Trying to generate it.")
49
+ metadata = self.generate_metadata(base_path)
50
+ print(f"{len(metadata)} lines in metadata.")
51
+ self.data = [metadata.iloc[i].to_dict() for i in range(len(metadata))]
52
+ elif metadata_path.endswith(".json"):
53
+ with open(metadata_path, "r") as f:
54
+ metadata = json.load(f)
55
+ self.data = metadata
56
+ elif metadata_path.endswith(".jsonl"):
57
+ metadata = []
58
+ with open(metadata_path, 'r') as f:
59
+ for line in tqdm(f):
60
+ metadata.append(json.loads(line.strip()))
61
+ self.data = metadata
62
+ else:
63
+ metadata = pd.read_csv(metadata_path)
64
+ self.data = [metadata.iloc[i].to_dict() for i in range(len(metadata))]
65
+
66
+
67
+ def generate_metadata(self, folder):
68
+ image_list, prompt_list = [], []
69
+ file_set = set(os.listdir(folder))
70
+ for file_name in file_set:
71
+ if "." not in file_name:
72
+ continue
73
+ file_ext_name = file_name.split(".")[-1].lower()
74
+ file_base_name = file_name[:-len(file_ext_name)-1]
75
+ if file_ext_name not in self.image_file_extension:
76
+ continue
77
+ prompt_file_name = file_base_name + ".txt"
78
+ if prompt_file_name not in file_set:
79
+ continue
80
+ with open(os.path.join(folder, prompt_file_name), "r", encoding="utf-8") as f:
81
+ prompt = f.read().strip()
82
+ image_list.append(file_name)
83
+ prompt_list.append(prompt)
84
+ metadata = pd.DataFrame()
85
+ metadata["image"] = image_list
86
+ metadata["prompt"] = prompt_list
87
+ return metadata
88
+
89
+
90
+ def crop_and_resize(self, image, target_height, target_width):
91
+ width, height = image.size
92
+ scale = max(target_width / width, target_height / height)
93
+ image = torchvision.transforms.functional.resize(
94
+ image,
95
+ (round(height*scale), round(width*scale)),
96
+ interpolation=torchvision.transforms.InterpolationMode.BILINEAR
97
+ )
98
+ image = torchvision.transforms.functional.center_crop(image, (target_height, target_width))
99
+ return image
100
+
101
+
102
+ def get_height_width(self, image):
103
+ if self.dynamic_resolution:
104
+ width, height = image.size
105
+ if width * height > self.max_pixels:
106
+ scale = (width * height / self.max_pixels) ** 0.5
107
+ height, width = int(height / scale), int(width / scale)
108
+ height = height // self.height_division_factor * self.height_division_factor
109
+ width = width // self.width_division_factor * self.width_division_factor
110
+ else:
111
+ height, width = self.height, self.width
112
+ return height, width
113
+
114
+
115
+ def load_image(self, file_path):
116
+ image = Image.open(file_path).convert("RGB")
117
+ image = self.crop_and_resize(image, *self.get_height_width(image))
118
+ return image
119
+
120
+
121
+ def load_data(self, file_path):
122
+ return self.load_image(file_path)
123
+
124
+
125
+ def __getitem__(self, data_id):
126
+ data = self.data[data_id % len(self.data)].copy()
127
+ for key in self.data_file_keys:
128
+ if key in data:
129
+ if isinstance(data[key], list):
130
+ path = [os.path.join(self.base_path, p) for p in data[key]]
131
+ data[key] = [self.load_data(p) for p in path]
132
+ else:
133
+ path = os.path.join(self.base_path, data[key])
134
+ data[key] = self.load_data(path)
135
+ if data[key] is None:
136
+ warnings.warn(f"cannot load file {data[key]}.")
137
+ return None
138
+ return data
139
+
140
+
141
+ def __len__(self):
142
+ return len(self.data) * self.repeat
143
+
144
+
145
+
146
+ class VideoDataset(torch.utils.data.Dataset):
147
+ def __init__(
148
+ self,
149
+ base_path=None, metadata_path=None,
150
+ num_frames=81,
151
+ time_division_factor=4, time_division_remainder=1,
152
+ max_pixels=1920*1080, height=None, width=None,
153
+ height_division_factor=16, width_division_factor=16,
154
+ data_file_keys=("video",),
155
+ image_file_extension=("jpg", "jpeg", "png", "webp"),
156
+ video_file_extension=("mp4", "avi", "mov", "wmv", "mkv", "flv", "webm"),
157
+ repeat=1,
158
+ args=None,
159
+ ):
160
+ if args is not None:
161
+ base_path = args.dataset_base_path
162
+ metadata_path = args.dataset_metadata_path
163
+ height = args.height
164
+ width = args.width
165
+ max_pixels = args.max_pixels
166
+ num_frames = args.num_frames
167
+ data_file_keys = args.data_file_keys.split(",")
168
+ repeat = args.dataset_repeat
169
+
170
+ self.base_path = base_path
171
+ self.num_frames = num_frames
172
+ self.time_division_factor = time_division_factor
173
+ self.time_division_remainder = time_division_remainder
174
+ self.max_pixels = max_pixels
175
+ self.height = height
176
+ self.width = width
177
+ self.height_division_factor = height_division_factor
178
+ self.width_division_factor = width_division_factor
179
+ self.data_file_keys = data_file_keys
180
+ self.image_file_extension = image_file_extension
181
+ self.video_file_extension = video_file_extension
182
+ self.repeat = repeat
183
+
184
+ if height is not None and width is not None:
185
+ print("Height and width are fixed. Setting `dynamic_resolution` to False.")
186
+ self.dynamic_resolution = False
187
+ elif height is None and width is None:
188
+ print("Height and width are none. Setting `dynamic_resolution` to True.")
189
+ self.dynamic_resolution = True
190
+
191
+ if metadata_path is None:
192
+ print("No metadata. Trying to generate it.")
193
+ metadata = self.generate_metadata(base_path)
194
+ print(f"{len(metadata)} lines in metadata.")
195
+ self.data = [metadata.iloc[i].to_dict() for i in range(len(metadata))]
196
+ elif metadata_path.endswith(".json"):
197
+ with open(metadata_path, "r") as f:
198
+ metadata = json.load(f)
199
+ self.data = metadata
200
+ else:
201
+ metadata = pd.read_csv(metadata_path)
202
+ self.data = [metadata.iloc[i].to_dict() for i in range(len(metadata))]
203
+
204
+
205
+ def generate_metadata(self, folder):
206
+ video_list, prompt_list = [], []
207
+ file_set = set(os.listdir(folder))
208
+ for file_name in file_set:
209
+ if "." not in file_name:
210
+ continue
211
+ file_ext_name = file_name.split(".")[-1].lower()
212
+ file_base_name = file_name[:-len(file_ext_name)-1]
213
+ if file_ext_name not in self.image_file_extension and file_ext_name not in self.video_file_extension:
214
+ continue
215
+ prompt_file_name = file_base_name + ".txt"
216
+ if prompt_file_name not in file_set:
217
+ continue
218
+ with open(os.path.join(folder, prompt_file_name), "r", encoding="utf-8") as f:
219
+ prompt = f.read().strip()
220
+ video_list.append(file_name)
221
+ prompt_list.append(prompt)
222
+ metadata = pd.DataFrame()
223
+ metadata["video"] = video_list
224
+ metadata["prompt"] = prompt_list
225
+ return metadata
226
+
227
+
228
+ def crop_and_resize(self, image, target_height, target_width):
229
+ width, height = image.size
230
+ scale = max(target_width / width, target_height / height)
231
+ image = torchvision.transforms.functional.resize(
232
+ image,
233
+ (round(height*scale), round(width*scale)),
234
+ interpolation=torchvision.transforms.InterpolationMode.BILINEAR
235
+ )
236
+ image = torchvision.transforms.functional.center_crop(image, (target_height, target_width))
237
+ return image
238
+
239
+
240
+ def get_height_width(self, image):
241
+ if self.dynamic_resolution:
242
+ width, height = image.size
243
+ if width * height > self.max_pixels:
244
+ scale = (width * height / self.max_pixels) ** 0.5
245
+ height, width = int(height / scale), int(width / scale)
246
+ height = height // self.height_division_factor * self.height_division_factor
247
+ width = width // self.width_division_factor * self.width_division_factor
248
+ else:
249
+ height, width = self.height, self.width
250
+ return height, width
251
+
252
+
253
+ def get_num_frames(self, reader):
254
+ num_frames = self.num_frames
255
+ if int(reader.count_frames()) < num_frames:
256
+ num_frames = int(reader.count_frames())
257
+ while num_frames > 1 and num_frames % self.time_division_factor != self.time_division_remainder:
258
+ num_frames -= 1
259
+ return num_frames
260
+
261
+
262
+ def load_video(self, file_path):
263
+ reader = imageio.get_reader(file_path)
264
+ num_frames = self.get_num_frames(reader)
265
+ frames = []
266
+ for frame_id in range(num_frames):
267
+ frame = reader.get_data(frame_id)
268
+ frame = Image.fromarray(frame)
269
+ frame = self.crop_and_resize(frame, *self.get_height_width(frame))
270
+ frames.append(frame)
271
+ reader.close()
272
+ return frames
273
+
274
+
275
+ def load_image(self, file_path):
276
+ image = Image.open(file_path).convert("RGB")
277
+ image = self.crop_and_resize(image, *self.get_height_width(image))
278
+ frames = [image]
279
+ return frames
280
+
281
+
282
+ def is_image(self, file_path):
283
+ file_ext_name = file_path.split(".")[-1]
284
+ return file_ext_name.lower() in self.image_file_extension
285
+
286
+
287
+ def is_video(self, file_path):
288
+ file_ext_name = file_path.split(".")[-1]
289
+ return file_ext_name.lower() in self.video_file_extension
290
+
291
+
292
+ def load_data(self, file_path):
293
+ if self.is_image(file_path):
294
+ return self.load_image(file_path)
295
+ elif self.is_video(file_path):
296
+ return self.load_video(file_path)
297
+ else:
298
+ return None
299
+
300
+
301
+ def __getitem__(self, data_id):
302
+ data = self.data[data_id % len(self.data)].copy()
303
+ for key in self.data_file_keys:
304
+ if key in data:
305
+ path = os.path.join(self.base_path, data[key])
306
+ data[key] = self.load_data(path)
307
+ if data[key] is None:
308
+ warnings.warn(f"cannot load file {data[key]}.")
309
+ return None
310
+ return data
311
+
312
+
313
+ def __len__(self):
314
+ return len(self.data) * self.repeat
315
+
316
+
317
+
318
+ class DiffusionTrainingModule(torch.nn.Module):
319
+ def __init__(self):
320
+ super().__init__()
321
+
322
+
323
+ def to(self, *args, **kwargs):
324
+ for name, model in self.named_children():
325
+ model.to(*args, **kwargs)
326
+ return self
327
+
328
+
329
+ def trainable_modules(self):
330
+ trainable_modules = filter(lambda p: p.requires_grad, self.parameters())
331
+ return trainable_modules
332
+
333
+
334
+ def trainable_param_names(self):
335
+ trainable_param_names = list(filter(lambda named_param: named_param[1].requires_grad, self.named_parameters()))
336
+ trainable_param_names = set([named_param[0] for named_param in trainable_param_names])
337
+ return trainable_param_names
338
+
339
+
340
+ def add_lora_to_model(self, model, target_modules, lora_rank, lora_alpha=None):
341
+ if lora_alpha is None:
342
+ lora_alpha = lora_rank
343
+ lora_config = LoraConfig(r=lora_rank, lora_alpha=lora_alpha, target_modules=target_modules)
344
+ model = inject_adapter_in_model(lora_config, model)
345
+ return model
346
+
347
+
348
+ def export_trainable_state_dict(self, state_dict, remove_prefix=None):
349
+ trainable_param_names = self.trainable_param_names()
350
+ state_dict = {name: param for name, param in state_dict.items() if name in trainable_param_names}
351
+ if remove_prefix is not None:
352
+ state_dict_ = {}
353
+ for name, param in state_dict.items():
354
+ if name.startswith(remove_prefix):
355
+ name = name[len(remove_prefix):]
356
+ state_dict_[name] = param
357
+ state_dict = state_dict_
358
+ return state_dict
359
+
360
+
361
+
362
+ class ModelLogger:
363
+ def __init__(self, output_path, remove_prefix_in_ckpt=None, state_dict_converter=lambda x:x):
364
+ self.output_path = output_path
365
+ self.remove_prefix_in_ckpt = remove_prefix_in_ckpt
366
+ self.state_dict_converter = state_dict_converter
367
+
368
+
369
+ def on_step_end(self, loss):
370
+ pass
371
+
372
+
373
+ def on_epoch_end(self, accelerator, model, epoch_id):
374
+ accelerator.wait_for_everyone()
375
+ if accelerator.is_main_process:
376
+ state_dict = accelerator.get_state_dict(model)
377
+ state_dict = accelerator.unwrap_model(model).export_trainable_state_dict(state_dict, remove_prefix=self.remove_prefix_in_ckpt)
378
+ state_dict = self.state_dict_converter(state_dict)
379
+ os.makedirs(self.output_path, exist_ok=True)
380
+ path = os.path.join(self.output_path, f"epoch-{epoch_id}.safetensors")
381
+ accelerator.save(state_dict, path, safe_serialization=True)
382
+
383
+
384
+
385
+ def launch_training_task(
386
+ dataset: torch.utils.data.Dataset,
387
+ model: DiffusionTrainingModule,
388
+ model_logger: ModelLogger,
389
+ optimizer: torch.optim.Optimizer,
390
+ scheduler: torch.optim.lr_scheduler.LRScheduler,
391
+ num_epochs: int = 1,
392
+ gradient_accumulation_steps: int = 1,
393
+ ):
394
+ dataloader = torch.utils.data.DataLoader(dataset, shuffle=True, collate_fn=lambda x: x[0])
395
+ accelerator = Accelerator(gradient_accumulation_steps=gradient_accumulation_steps)
396
+ model, optimizer, dataloader, scheduler = accelerator.prepare(model, optimizer, dataloader, scheduler)
397
+
398
+ for epoch_id in range(num_epochs):
399
+ for data in tqdm(dataloader):
400
+ with accelerator.accumulate(model):
401
+ optimizer.zero_grad()
402
+ loss = model(data)
403
+ accelerator.backward(loss)
404
+ optimizer.step()
405
+ model_logger.on_step_end(loss)
406
+ scheduler.step()
407
+ model_logger.on_epoch_end(accelerator, model, epoch_id)
408
+
409
+
410
+
411
+ def launch_data_process_task(model: DiffusionTrainingModule, dataset, output_path="./models"):
412
+ dataloader = torch.utils.data.DataLoader(dataset, shuffle=False, collate_fn=lambda x: x[0])
413
+ accelerator = Accelerator()
414
+ model, dataloader = accelerator.prepare(model, dataloader)
415
+ os.makedirs(os.path.join(output_path, "data_cache"), exist_ok=True)
416
+ for data_id, data in enumerate(tqdm(dataloader)):
417
+ with torch.no_grad():
418
+ inputs = model.forward_preprocess(data)
419
+ inputs = {key: inputs[key] for key in model.model_input_keys if key in inputs}
420
+ torch.save(inputs, os.path.join(output_path, "data_cache", f"{data_id}.pth"))
421
+
422
+
423
+
424
+ def wan_parser():
425
+ parser = argparse.ArgumentParser(description="Simple example of a training script.")
426
+ parser.add_argument("--dataset_base_path", type=str, default="", required=True, help="Base path of the dataset.")
427
+ parser.add_argument("--dataset_metadata_path", type=str, default=None, help="Path to the metadata file of the dataset.")
428
+ parser.add_argument("--max_pixels", type=int, default=1280*720, help="Maximum number of pixels per frame, used for dynamic resolution..")
429
+ parser.add_argument("--height", type=int, default=None, help="Height of images or videos. Leave `height` and `width` empty to enable dynamic resolution.")
430
+ parser.add_argument("--width", type=int, default=None, help="Width of images or videos. Leave `height` and `width` empty to enable dynamic resolution.")
431
+ parser.add_argument("--num_frames", type=int, default=81, help="Number of frames per video. Frames are sampled from the video prefix.")
432
+ parser.add_argument("--data_file_keys", type=str, default="image,video", help="Data file keys in the metadata. Comma-separated.")
433
+ parser.add_argument("--dataset_repeat", type=int, default=1, help="Number of times to repeat the dataset per epoch.")
434
+ parser.add_argument("--model_paths", type=str, default=None, help="Paths to load models. In JSON format.")
435
+ parser.add_argument("--model_id_with_origin_paths", type=str, default=None, help="Model ID with origin paths, e.g., Wan-AI/Wan2.1-T2V-1.3B:diffusion_pytorch_model*.safetensors. Comma-separated.")
436
+ parser.add_argument("--learning_rate", type=float, default=1e-4, help="Learning rate.")
437
+ parser.add_argument("--num_epochs", type=int, default=1, help="Number of epochs.")
438
+ parser.add_argument("--output_path", type=str, default="./models", help="Output save path.")
439
+ parser.add_argument("--remove_prefix_in_ckpt", type=str, default="pipe.dit.", help="Remove prefix in ckpt.")
440
+ parser.add_argument("--trainable_models", type=str, default=None, help="Models to train, e.g., dit, vae, text_encoder.")
441
+ parser.add_argument("--lora_base_model", type=str, default=None, help="Which model LoRA is added to.")
442
+ parser.add_argument("--lora_target_modules", type=str, default="q,k,v,o,ffn.0,ffn.2", help="Which layers LoRA is added to.")
443
+ parser.add_argument("--lora_rank", type=int, default=32, help="Rank of LoRA.")
444
+ parser.add_argument("--extra_inputs", default=None, help="Additional model inputs, comma-separated.")
445
+ parser.add_argument("--use_gradient_checkpointing_offload", default=False, action="store_true", help="Whether to offload gradient checkpointing to CPU memory.")
446
+ parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Gradient accumulation steps.")
447
+ parser.add_argument("--max_timestep_boundary", type=float, default=1.0, help="Max timestep boundary (for mixed models, e.g., Wan-AI/Wan2.2-I2V-A14B).")
448
+ parser.add_argument("--min_timestep_boundary", type=float, default=0.0, help="Min timestep boundary (for mixed models, e.g., Wan-AI/Wan2.2-I2V-A14B).")
449
+ return parser
450
+
451
+
452
+
453
+ def flux_parser():
454
+ parser = argparse.ArgumentParser(description="Simple example of a training script.")
455
+ parser.add_argument("--dataset_base_path", type=str, default="", required=True, help="Base path of the dataset.")
456
+ parser.add_argument("--dataset_metadata_path", type=str, default=None, help="Path to the metadata file of the dataset.")
457
+ parser.add_argument("--max_pixels", type=int, default=1024*1024, help="Maximum number of pixels per frame, used for dynamic resolution..")
458
+ parser.add_argument("--height", type=int, default=None, help="Height of images. Leave `height` and `width` empty to enable dynamic resolution.")
459
+ parser.add_argument("--width", type=int, default=None, help="Width of images. Leave `height` and `width` empty to enable dynamic resolution.")
460
+ parser.add_argument("--data_file_keys", type=str, default="image", help="Data file keys in the metadata. Comma-separated.")
461
+ parser.add_argument("--dataset_repeat", type=int, default=1, help="Number of times to repeat the dataset per epoch.")
462
+ parser.add_argument("--model_paths", type=str, default=None, help="Paths to load models. In JSON format.")
463
+ parser.add_argument("--model_id_with_origin_paths", type=str, default=None, help="Model ID with origin paths, e.g., Wan-AI/Wan2.1-T2V-1.3B:diffusion_pytorch_model*.safetensors. Comma-separated.")
464
+ parser.add_argument("--learning_rate", type=float, default=1e-4, help="Learning rate.")
465
+ parser.add_argument("--num_epochs", type=int, default=1, help="Number of epochs.")
466
+ parser.add_argument("--output_path", type=str, default="./models", help="Output save path.")
467
+ parser.add_argument("--remove_prefix_in_ckpt", type=str, default="pipe.dit.", help="Remove prefix in ckpt.")
468
+ parser.add_argument("--trainable_models", type=str, default=None, help="Models to train, e.g., dit, vae, text_encoder.")
469
+ parser.add_argument("--lora_base_model", type=str, default=None, help="Which model LoRA is added to.")
470
+ parser.add_argument("--lora_target_modules", type=str, default="q,k,v,o,ffn.0,ffn.2", help="Which layers LoRA is added to.")
471
+ parser.add_argument("--lora_rank", type=int, default=32, help="Rank of LoRA.")
472
+ parser.add_argument("--extra_inputs", default=None, help="Additional model inputs, comma-separated.")
473
+ parser.add_argument("--align_to_opensource_format", default=False, action="store_true", help="Whether to align the lora format to opensource format. Only for DiT's LoRA.")
474
+ parser.add_argument("--use_gradient_checkpointing", default=False, action="store_true", help="Whether to use gradient checkpointing.")
475
+ parser.add_argument("--use_gradient_checkpointing_offload", default=False, action="store_true", help="Whether to offload gradient checkpointing to CPU memory.")
476
+ parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Gradient accumulation steps.")
477
+ return parser
478
+
479
+
480
+
481
+ def qwen_image_parser():
482
+ parser = argparse.ArgumentParser(description="Simple example of a training script.")
483
+ parser.add_argument("--dataset_base_path", type=str, default="", required=True, help="Base path of the dataset.")
484
+ parser.add_argument("--dataset_metadata_path", type=str, default=None, help="Path to the metadata file of the dataset.")
485
+ parser.add_argument("--max_pixels", type=int, default=1024*1024, help="Maximum number of pixels per frame, used for dynamic resolution..")
486
+ parser.add_argument("--height", type=int, default=None, help="Height of images. Leave `height` and `width` empty to enable dynamic resolution.")
487
+ parser.add_argument("--width", type=int, default=None, help="Width of images. Leave `height` and `width` empty to enable dynamic resolution.")
488
+ parser.add_argument("--data_file_keys", type=str, default="image", help="Data file keys in the metadata. Comma-separated.")
489
+ parser.add_argument("--dataset_repeat", type=int, default=1, help="Number of times to repeat the dataset per epoch.")
490
+ parser.add_argument("--model_paths", type=str, default=None, help="Paths to load models. In JSON format.")
491
+ parser.add_argument("--model_id_with_origin_paths", type=str, default=None, help="Model ID with origin paths, e.g., Wan-AI/Wan2.1-T2V-1.3B:diffusion_pytorch_model*.safetensors. Comma-separated.")
492
+ parser.add_argument("--tokenizer_path", type=str, default=None, help="Paths to tokenizer.")
493
+ parser.add_argument("--learning_rate", type=float, default=1e-4, help="Learning rate.")
494
+ parser.add_argument("--num_epochs", type=int, default=1, help="Number of epochs.")
495
+ parser.add_argument("--output_path", type=str, default="./models", help="Output save path.")
496
+ parser.add_argument("--remove_prefix_in_ckpt", type=str, default="pipe.dit.", help="Remove prefix in ckpt.")
497
+ parser.add_argument("--trainable_models", type=str, default=None, help="Models to train, e.g., dit, vae, text_encoder.")
498
+ parser.add_argument("--lora_base_model", type=str, default=None, help="Which model LoRA is added to.")
499
+ parser.add_argument("--lora_target_modules", type=str, default="q,k,v,o,ffn.0,ffn.2", help="Which layers LoRA is added to.")
500
+ parser.add_argument("--lora_rank", type=int, default=32, help="Rank of LoRA.")
501
+ parser.add_argument("--extra_inputs", default=None, help="Additional model inputs, comma-separated.")
502
+ parser.add_argument("--align_to_opensource_format", default=False, action="store_true", help="Whether to align the lora format to opensource format. Only for DiT's LoRA.")
503
+ parser.add_argument("--use_gradient_checkpointing", default=False, action="store_true", help="Whether to use gradient checkpointing.")
504
+ parser.add_argument("--use_gradient_checkpointing_offload", default=False, action="store_true", help="Whether to offload gradient checkpointing to CPU memory.")
505
+ parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Gradient accumulation steps.")
506
+ return parser
diffsynth/utils/__init__.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch, warnings, glob, os
2
+ import numpy as np
3
+ from PIL import Image
4
+ from einops import repeat, reduce
5
+ from typing import Optional, Union
6
+ from dataclasses import dataclass
7
+ from modelscope import snapshot_download
8
+ import numpy as np
9
+ from PIL import Image
10
+ from typing import Optional
11
+
12
+
13
+ class BasePipeline(torch.nn.Module):
14
+
15
+ def __init__(
16
+ self,
17
+ device="cuda", torch_dtype=torch.float16,
18
+ height_division_factor=64, width_division_factor=64,
19
+ time_division_factor=None, time_division_remainder=None,
20
+ ):
21
+ super().__init__()
22
+ # The device and torch_dtype is used for the storage of intermediate variables, not models.
23
+ self.device = device
24
+ self.torch_dtype = torch_dtype
25
+ # The following parameters are used for shape check.
26
+ self.height_division_factor = height_division_factor
27
+ self.width_division_factor = width_division_factor
28
+ self.time_division_factor = time_division_factor
29
+ self.time_division_remainder = time_division_remainder
30
+ self.vram_management_enabled = False
31
+
32
+
33
+ def to(self, *args, **kwargs):
34
+ device, dtype, non_blocking, convert_to_format = torch._C._nn._parse_to(*args, **kwargs)
35
+ if device is not None:
36
+ self.device = device
37
+ if dtype is not None:
38
+ self.torch_dtype = dtype
39
+ super().to(*args, **kwargs)
40
+ return self
41
+
42
+
43
+ def check_resize_height_width(self, height, width, num_frames=None):
44
+ # Shape check
45
+ if height % self.height_division_factor != 0:
46
+ height = (height + self.height_division_factor - 1) // self.height_division_factor * self.height_division_factor
47
+ print(f"height % {self.height_division_factor} != 0. We round it up to {height}.")
48
+ if width % self.width_division_factor != 0:
49
+ width = (width + self.width_division_factor - 1) // self.width_division_factor * self.width_division_factor
50
+ print(f"width % {self.width_division_factor} != 0. We round it up to {width}.")
51
+ if num_frames is None:
52
+ return height, width
53
+ else:
54
+ if num_frames % self.time_division_factor != self.time_division_remainder:
55
+ num_frames = (num_frames + self.time_division_factor - 1) // self.time_division_factor * self.time_division_factor + self.time_division_remainder
56
+ print(f"num_frames % {self.time_division_factor} != {self.time_division_remainder}. We round it up to {num_frames}.")
57
+ return height, width, num_frames
58
+
59
+
60
+ def preprocess_image(self, image, torch_dtype=None, device=None, pattern="B C H W", min_value=-1, max_value=1):
61
+ # Transform a PIL.Image to torch.Tensor
62
+ image = torch.Tensor(np.array(image, dtype=np.float32))
63
+ image = image.to(dtype=torch_dtype or self.torch_dtype, device=device or self.device)
64
+ image = image * ((max_value - min_value) / 255) + min_value
65
+ image = repeat(image, f"H W C -> {pattern}", **({"B": 1} if "B" in pattern else {}))
66
+ return image
67
+
68
+
69
+ def preprocess_video(self, video, torch_dtype=None, device=None, pattern="B C T H W", min_value=-1, max_value=1):
70
+ # Transform a list of PIL.Image to torch.Tensor
71
+ video = [self.preprocess_image(image, torch_dtype=torch_dtype, device=device, min_value=min_value, max_value=max_value) for image in video]
72
+ video = torch.stack(video, dim=pattern.index("T") // 2)
73
+ return video
74
+
75
+
76
+ def vae_output_to_image(self, vae_output, pattern="B C H W", min_value=-1, max_value=1):
77
+ # Transform a torch.Tensor to PIL.Image
78
+ if pattern != "H W C":
79
+ vae_output = reduce(vae_output, f"{pattern} -> H W C", reduction="mean")
80
+ image = ((vae_output - min_value) * (255 / (max_value - min_value))).clip(0, 255)
81
+ image = image.to(device="cpu", dtype=torch.uint8)
82
+ image = Image.fromarray(image.numpy())
83
+ return image
84
+
85
+
86
+ def vae_output_to_video(self, vae_output, pattern="B C T H W", min_value=-1, max_value=1):
87
+ # Transform a torch.Tensor to list of PIL.Image
88
+ if pattern != "T H W C":
89
+ vae_output = reduce(vae_output, f"{pattern} -> T H W C", reduction="mean")
90
+ video = [self.vae_output_to_image(image, pattern="H W C", min_value=min_value, max_value=max_value) for image in vae_output]
91
+ return video
92
+
93
+
94
+ def load_models_to_device(self, model_names=[]):
95
+ if self.vram_management_enabled:
96
+ # offload models
97
+ for name, model in self.named_children():
98
+ if name not in model_names:
99
+ if hasattr(model, "vram_management_enabled") and model.vram_management_enabled:
100
+ for module in model.modules():
101
+ if hasattr(module, "offload"):
102
+ module.offload()
103
+ else:
104
+ model.cpu()
105
+ torch.cuda.empty_cache()
106
+ # onload models
107
+ for name, model in self.named_children():
108
+ if name in model_names:
109
+ if hasattr(model, "vram_management_enabled") and model.vram_management_enabled:
110
+ for module in model.modules():
111
+ if hasattr(module, "onload"):
112
+ module.onload()
113
+ else:
114
+ model.to(self.device)
115
+
116
+
117
+ def generate_noise(self, shape, seed=None, rand_device="cpu", rand_torch_dtype=torch.float32, device=None, torch_dtype=None):
118
+ # Initialize Gaussian noise
119
+ generator = None if seed is None else torch.Generator(rand_device).manual_seed(seed)
120
+ noise = torch.randn(shape, generator=generator, device=rand_device, dtype=rand_torch_dtype)
121
+ noise = noise.to(dtype=torch_dtype or self.torch_dtype, device=device or self.device)
122
+ return noise
123
+
124
+
125
+ def enable_cpu_offload(self):
126
+ warnings.warn("`enable_cpu_offload` will be deprecated. Please use `enable_vram_management`.")
127
+ self.vram_management_enabled = True
128
+
129
+
130
+ def get_vram(self):
131
+ return torch.cuda.mem_get_info(self.device)[1] / (1024 ** 3)
132
+
133
+
134
+ def freeze_except(self, model_names):
135
+ for name, model in self.named_children():
136
+ if name in model_names:
137
+ model.train()
138
+ model.requires_grad_(True)
139
+ else:
140
+ model.eval()
141
+ model.requires_grad_(False)
142
+
143
+
144
+ @dataclass
145
+ class ModelConfig:
146
+ path: Union[str, list[str]] = None
147
+ model_id: str = None
148
+ origin_file_pattern: Union[str, list[str]] = None
149
+ download_resource: str = "ModelScope"
150
+ offload_device: Optional[Union[str, torch.device]] = None
151
+ offload_dtype: Optional[torch.dtype] = None
152
+ local_model_path: str = None
153
+ skip_download: bool = False
154
+
155
+ def download_if_necessary(self, use_usp=False):
156
+ if self.path is None:
157
+ # Check model_id and origin_file_pattern
158
+ if self.model_id is None:
159
+ raise ValueError(f"""No valid model files. Please use `ModelConfig(path="xxx")` or `ModelConfig(model_id="xxx/yyy", origin_file_pattern="zzz")`.""")
160
+
161
+ # Skip if not in rank 0
162
+ if use_usp:
163
+ import torch.distributed as dist
164
+ skip_download = self.skip_download or dist.get_rank() != 0
165
+ else:
166
+ skip_download = self.skip_download
167
+
168
+ # Check whether the origin path is a folder
169
+ if self.origin_file_pattern is None or self.origin_file_pattern == "":
170
+ self.origin_file_pattern = ""
171
+ allow_file_pattern = None
172
+ is_folder = True
173
+ elif isinstance(self.origin_file_pattern, str) and self.origin_file_pattern.endswith("/"):
174
+ allow_file_pattern = self.origin_file_pattern + "*"
175
+ is_folder = True
176
+ else:
177
+ allow_file_pattern = self.origin_file_pattern
178
+ is_folder = False
179
+
180
+ # Download
181
+ if self.local_model_path is None:
182
+ self.local_model_path = "./models"
183
+ if not skip_download:
184
+ downloaded_files = glob.glob(self.origin_file_pattern, root_dir=os.path.join(self.local_model_path, self.model_id))
185
+ snapshot_download(
186
+ self.model_id,
187
+ local_dir=os.path.join(self.local_model_path, self.model_id),
188
+ allow_file_pattern=allow_file_pattern,
189
+ ignore_file_pattern=downloaded_files,
190
+ local_files_only=False
191
+ )
192
+
193
+ # Let rank 1, 2, ... wait for rank 0
194
+ if use_usp:
195
+ import torch.distributed as dist
196
+ dist.barrier(device_ids=[dist.get_rank()])
197
+
198
+ # Return downloaded files
199
+ if is_folder:
200
+ self.path = os.path.join(self.local_model_path, self.model_id, self.origin_file_pattern)
201
+ else:
202
+ self.path = glob.glob(os.path.join(self.local_model_path, self.model_id, self.origin_file_pattern))
203
+ if isinstance(self.path, list) and len(self.path) == 1:
204
+ self.path = self.path[0]
205
+
206
+
207
+
208
+ class PipelineUnit:
209
+ def __init__(
210
+ self,
211
+ seperate_cfg: bool = False,
212
+ take_over: bool = False,
213
+ input_params: tuple[str] = None,
214
+ input_params_posi: dict[str, str] = None,
215
+ input_params_nega: dict[str, str] = None,
216
+ onload_model_names: tuple[str] = None
217
+ ):
218
+ self.seperate_cfg = seperate_cfg
219
+ self.take_over = take_over
220
+ self.input_params = input_params
221
+ self.input_params_posi = input_params_posi
222
+ self.input_params_nega = input_params_nega
223
+ self.onload_model_names = onload_model_names
224
+
225
+
226
+ def process(self, pipe: BasePipeline, inputs: dict, positive=True, **kwargs) -> dict:
227
+ raise NotImplementedError("`process` is not implemented.")
228
+
229
+
230
+
231
+ class PipelineUnitRunner:
232
+ def __init__(self):
233
+ pass
234
+
235
+ def __call__(self, unit: PipelineUnit, pipe: BasePipeline, inputs_shared: dict, inputs_posi: dict, inputs_nega: dict) -> tuple[dict, dict]:
236
+ if unit.take_over:
237
+ # Let the pipeline unit take over this function.
238
+ inputs_shared, inputs_posi, inputs_nega = unit.process(pipe, inputs_shared=inputs_shared, inputs_posi=inputs_posi, inputs_nega=inputs_nega)
239
+ elif unit.seperate_cfg:
240
+ # Positive side
241
+ processor_inputs = {name: inputs_posi.get(name_) for name, name_ in unit.input_params_posi.items()}
242
+ if unit.input_params is not None:
243
+ for name in unit.input_params:
244
+ processor_inputs[name] = inputs_shared.get(name)
245
+ processor_outputs = unit.process(pipe, **processor_inputs)
246
+ inputs_posi.update(processor_outputs)
247
+ # Negative side
248
+ if inputs_shared["cfg_scale"] != 1:
249
+ processor_inputs = {name: inputs_nega.get(name_) for name, name_ in unit.input_params_nega.items()}
250
+ if unit.input_params is not None:
251
+ for name in unit.input_params:
252
+ processor_inputs[name] = inputs_shared.get(name)
253
+ processor_outputs = unit.process(pipe, **processor_inputs)
254
+ inputs_nega.update(processor_outputs)
255
+ else:
256
+ inputs_nega.update(processor_outputs)
257
+ else:
258
+ processor_inputs = {name: inputs_shared.get(name) for name in unit.input_params}
259
+ processor_outputs = unit.process(pipe, **processor_inputs)
260
+ inputs_shared.update(processor_outputs)
261
+ return inputs_shared, inputs_posi, inputs_nega
diffsynth/vram_management/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ from .layers import *
2
+ from .gradient_checkpointing import *
diffsynth/vram_management/gradient_checkpointing.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+
4
+ def create_custom_forward(module):
5
+ def custom_forward(*inputs, **kwargs):
6
+ return module(*inputs, **kwargs)
7
+ return custom_forward
8
+
9
+
10
+ def gradient_checkpoint_forward(
11
+ model,
12
+ use_gradient_checkpointing,
13
+ use_gradient_checkpointing_offload,
14
+ *args,
15
+ **kwargs,
16
+ ):
17
+ if use_gradient_checkpointing_offload:
18
+ with torch.autograd.graph.save_on_cpu():
19
+ model_output = torch.utils.checkpoint.checkpoint(
20
+ create_custom_forward(model),
21
+ *args,
22
+ **kwargs,
23
+ use_reentrant=False,
24
+ )
25
+ elif use_gradient_checkpointing:
26
+ model_output = torch.utils.checkpoint.checkpoint(
27
+ create_custom_forward(model),
28
+ *args,
29
+ **kwargs,
30
+ use_reentrant=False,
31
+ )
32
+ else:
33
+ model_output = model(*args, **kwargs)
34
+ return model_output
diffsynth/vram_management/layers.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch, copy
2
+ from ..models.utils import init_weights_on_device
3
+
4
+
5
+ def cast_to(weight, dtype, device):
6
+ r = torch.empty_like(weight, dtype=dtype, device=device)
7
+ r.copy_(weight)
8
+ return r
9
+
10
+
11
+ class AutoTorchModule(torch.nn.Module):
12
+ def __init__(self):
13
+ super().__init__()
14
+
15
+ def check_free_vram(self):
16
+ gpu_mem_state = torch.cuda.mem_get_info(self.computation_device)
17
+ used_memory = (gpu_mem_state[1] - gpu_mem_state[0]) / (1024 ** 3)
18
+ return used_memory < self.vram_limit
19
+
20
+ def offload(self):
21
+ if self.state != 0:
22
+ self.to(dtype=self.offload_dtype, device=self.offload_device)
23
+ self.state = 0
24
+
25
+ def onload(self):
26
+ if self.state != 1:
27
+ self.to(dtype=self.onload_dtype, device=self.onload_device)
28
+ self.state = 1
29
+
30
+ def keep(self):
31
+ if self.state != 2:
32
+ self.to(dtype=self.computation_dtype, device=self.computation_device)
33
+ self.state = 2
34
+
35
+
36
+ class AutoWrappedModule(AutoTorchModule):
37
+ def __init__(self, module: torch.nn.Module, offload_dtype, offload_device, onload_dtype, onload_device, computation_dtype, computation_device, vram_limit, **kwargs):
38
+ super().__init__()
39
+ self.module = module.to(dtype=offload_dtype, device=offload_device)
40
+ self.offload_dtype = offload_dtype
41
+ self.offload_device = offload_device
42
+ self.onload_dtype = onload_dtype
43
+ self.onload_device = onload_device
44
+ self.computation_dtype = computation_dtype
45
+ self.computation_device = computation_device
46
+ self.vram_limit = vram_limit
47
+ self.state = 0
48
+
49
+ def forward(self, *args, **kwargs):
50
+ if self.state == 2:
51
+ module = self.module
52
+ else:
53
+ if self.onload_dtype == self.computation_dtype and self.onload_device == self.computation_device:
54
+ module = self.module
55
+ elif self.vram_limit is not None and self.check_free_vram():
56
+ self.keep()
57
+ module = self.module
58
+ else:
59
+ module = copy.deepcopy(self.module).to(dtype=self.computation_dtype, device=self.computation_device)
60
+ return module(*args, **kwargs)
61
+
62
+
63
+ class WanAutoCastLayerNorm(torch.nn.LayerNorm, AutoTorchModule):
64
+ def __init__(self, module: torch.nn.LayerNorm, offload_dtype, offload_device, onload_dtype, onload_device, computation_dtype, computation_device, vram_limit, **kwargs):
65
+ with init_weights_on_device(device=torch.device("meta")):
66
+ super().__init__(module.normalized_shape, eps=module.eps, elementwise_affine=module.elementwise_affine, bias=module.bias is not None, dtype=offload_dtype, device=offload_device)
67
+ self.weight = module.weight
68
+ self.bias = module.bias
69
+ self.offload_dtype = offload_dtype
70
+ self.offload_device = offload_device
71
+ self.onload_dtype = onload_dtype
72
+ self.onload_device = onload_device
73
+ self.computation_dtype = computation_dtype
74
+ self.computation_device = computation_device
75
+ self.vram_limit = vram_limit
76
+ self.state = 0
77
+
78
+ def forward(self, x, *args, **kwargs):
79
+ if self.state == 2:
80
+ weight, bias = self.weight, self.bias
81
+ else:
82
+ if self.onload_dtype == self.computation_dtype and self.onload_device == self.computation_device:
83
+ weight, bias = self.weight, self.bias
84
+ elif self.vram_limit is not None and self.check_free_vram():
85
+ self.keep()
86
+ weight, bias = self.weight, self.bias
87
+ else:
88
+ weight = None if self.weight is None else cast_to(self.weight, self.computation_dtype, self.computation_device)
89
+ bias = None if self.bias is None else cast_to(self.bias, self.computation_dtype, self.computation_device)
90
+ with torch.amp.autocast(device_type=x.device.type):
91
+ x = torch.nn.functional.layer_norm(x.float(), self.normalized_shape, weight, bias, self.eps).type_as(x)
92
+ return x
93
+
94
+
95
+ class AutoWrappedLinear(torch.nn.Linear, AutoTorchModule):
96
+ def __init__(self, module: torch.nn.Linear, offload_dtype, offload_device, onload_dtype, onload_device, computation_dtype, computation_device, vram_limit, name="", **kwargs):
97
+ with init_weights_on_device(device=torch.device("meta")):
98
+ super().__init__(in_features=module.in_features, out_features=module.out_features, bias=module.bias is not None, dtype=offload_dtype, device=offload_device)
99
+ self.weight = module.weight
100
+ self.bias = module.bias
101
+ self.offload_dtype = offload_dtype
102
+ self.offload_device = offload_device
103
+ self.onload_dtype = onload_dtype
104
+ self.onload_device = onload_device
105
+ self.computation_dtype = computation_dtype
106
+ self.computation_device = computation_device
107
+ self.vram_limit = vram_limit
108
+ self.state = 0
109
+ self.name = name
110
+ self.lora_A_weights = []
111
+ self.lora_B_weights = []
112
+ self.lora_merger = None
113
+
114
+ def forward(self, x, *args, **kwargs):
115
+ if self.state == 2:
116
+ weight, bias = self.weight, self.bias
117
+ else:
118
+ if self.onload_dtype == self.computation_dtype and self.onload_device == self.computation_device:
119
+ weight, bias = self.weight, self.bias
120
+ elif self.vram_limit is not None and self.check_free_vram():
121
+ self.keep()
122
+ weight, bias = self.weight, self.bias
123
+ else:
124
+ weight = cast_to(self.weight, self.computation_dtype, self.computation_device)
125
+ bias = None if self.bias is None else cast_to(self.bias, self.computation_dtype, self.computation_device)
126
+ out = torch.nn.functional.linear(x, weight, bias)
127
+
128
+ if len(self.lora_A_weights) == 0:
129
+ # No LoRA
130
+ return out
131
+ elif self.lora_merger is None:
132
+ # Native LoRA inference
133
+ for lora_A, lora_B in zip(self.lora_A_weights, self.lora_B_weights):
134
+ out = out + x @ lora_A.T @ lora_B.T
135
+ else:
136
+ # LoRA fusion
137
+ lora_output = []
138
+ for lora_A, lora_B in zip(self.lora_A_weights, self.lora_B_weights):
139
+ lora_output.append(x @ lora_A.T @ lora_B.T)
140
+ lora_output = torch.stack(lora_output)
141
+ out = self.lora_merger(out, lora_output)
142
+ return out
143
+
144
+
145
+ def enable_vram_management_recursively(model: torch.nn.Module, module_map: dict, module_config: dict, max_num_param=None, overflow_module_config: dict = None, total_num_param=0, vram_limit=None, name_prefix=""):
146
+ for name, module in model.named_children():
147
+ layer_name = name if name_prefix == "" else name_prefix + "." + name
148
+ for source_module, target_module in module_map.items():
149
+ if isinstance(module, source_module):
150
+ num_param = sum(p.numel() for p in module.parameters())
151
+ if max_num_param is not None and total_num_param + num_param > max_num_param:
152
+ module_config_ = overflow_module_config
153
+ else:
154
+ module_config_ = module_config
155
+ module_ = target_module(module, **module_config_, vram_limit=vram_limit, name=layer_name)
156
+ setattr(model, name, module_)
157
+ total_num_param += num_param
158
+ break
159
+ else:
160
+ total_num_param = enable_vram_management_recursively(module, module_map, module_config, max_num_param, overflow_module_config, total_num_param, vram_limit=vram_limit, name_prefix=layer_name)
161
+ return total_num_param
162
+
163
+
164
+ def enable_vram_management(model: torch.nn.Module, module_map: dict, module_config: dict, max_num_param=None, overflow_module_config: dict = None, vram_limit=None):
165
+ enable_vram_management_recursively(model, module_map, module_config, max_num_param, overflow_module_config, total_num_param=0, vram_limit=vram_limit)
166
+ model.vram_management_enabled = True
167
+
third_party/Optional.md ADDED
File without changes
tools/full_inference_modules_gradio.sh ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ which python
3
+ export CUDA_VISIBLE_DEVICES=${CUDA_VISIBLE_DEVICES:-0}
4
+
5
+ export CUDA_HOME=/usr/local/cuda-12.4
6
+ export PATH=$CUDA_HOME/bin:$PATH
7
+ export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH
8
+
9
+ # export HF_HUB_DOWNLOAD_TIMEOUT=30
10
+ # export HF_HOME=${HOME}/.cache/huggingface/
11
+
12
+ export CONDA_PATH=${HOME}/miniconda3 # ! Replace with your CONDA path
13
+ export CONDA_PREFIX_1=${CONDA_PATH}/envs/diffsynth # ! Replace with your diffsynth environment path
14
+ export CONDA_PREFIX_2=${CONDA_PATH}/envs/cosmos-predict1 # ! Replace with your cosmos-predict1 environment path
15
+ export DR_REPO_PATH=${REPO_PATH}/third_party/cosmos-transfer1-diffusion-renderer
16
+
17
+ # if input image, set FRAME=1
18
+ if [ "$TEST_TYPE" -eq 1 ]; then
19
+ export FRAME=1
20
+ fi
21
+
22
+ # -------------------------------------- module 1 -----------------------------------------
23
+ if [ "$INFER_1" -eq 1 ]; then
24
+ source ${CONDA_PATH}/bin/activate ${CONDA_PREFIX_2}
25
+ cd ${DR_REPO_PATH}
26
+ mkdir -p ${REPO_PATH}/datasets/gradio_data/results/$TEST_ID
27
+ rm -rf ${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/frames
28
+ if [ "$TEST_TYPE" -eq 0 ]; then
29
+ # If the input is a video, split it into multiple frames and save them in the output_folder/frames/$TEST_ID folder
30
+ CUDA_HOME=$CONDA_PREFIX_2 PYTHONPATH=$(pwd) python scripts/dataproc_extract_frames_from_video.py \
31
+ --input_folder ${REPO_PATH}/datasets/gradio_data/upload_data/$TEST_ID \
32
+ --output_folder ${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/frames/ --frame_rate=$FRAME_RATE --max_frames=$FRAME
33
+ elif [ "$TEST_TYPE" -eq 1 ]; then
34
+ # If the input is an image, directly copy it to the output_folder/frames/$TEST_ID folder
35
+ mkdir -p ${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/frames/$TEST_ID
36
+ cp ${REPO_PATH}/datasets/gradio_data/upload_data/$TEST_ID/$TEST_ID.jpg ${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/frames/$TEST_ID/frame_00000.jpg
37
+ fi
38
+
39
+ if [ "$FRAME" -eq 1 ]; then
40
+ export COSMOS_FRAME=$FRAME
41
+ else
42
+ export COSMOS_FRAME=57
43
+ fi
44
+ rm -rf ${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/frames_delighting
45
+ CUDA_HOME=$CONDA_PREFIX_2 PYTHONPATH=$(pwd) python cosmos_predict1/diffusion/inference/inference_inverse_renderer.py \
46
+ --checkpoint_dir checkpoints --diffusion_transformer_dir Diffusion_Renderer_Inverse_Cosmos_7B \
47
+ --dataset_path="${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/frames/" --num_video_frames $COSMOS_FRAME --group_mode folder \
48
+ --video_save_folder="${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/frames_delighting/" --chunk_mode 'first' --overlap_n_frames 0 --save_video 'False'
49
+
50
+ cd ${REPO_PATH}
51
+ CUDA_HOME=$CONDA_PREFIX_2 PYTHONPATH=$(pwd) python tools/reorg_gbuffer_from_dr_delighting.py \
52
+ --frame_dir "${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/frames/" \
53
+ --gbuffer_dir "${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/frames_delighting/gbuffer_frames/" \
54
+ --tgt_dir "${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/rego/"
55
+ fi
56
+
57
+ # -------------------------------------- module 2 -----------------------------------------
58
+ if [ "$INFER_2" -eq 1 ]; then
59
+ source ${CONDA_PATH}/bin/activate ${CONDA_PREFIX_2}
60
+ cd ${REPO_PATH}
61
+ mkdir -p ${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/envs
62
+ if [ "$USE_OFFICE_ENV" -eq 0 ]; then
63
+ CUDA_HOME=$CONDA_PREFIX_2 PYTHONPATH=$(pwd) python tools/process_env_maps.py \
64
+ --env_dir ${REPO_PATH}/datasets/gradio_data/upload_data/$TEST_ID/$TEST_ENV \
65
+ --save_path ${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/envs/$TEST_ENV \
66
+ --env_strength $ENV_STRENGTH
67
+ else
68
+ CUDA_HOME=$CONDA_PREFIX_2 PYTHONPATH=$(pwd) python tools/process_env_maps.py \
69
+ --env_dir ${REPO_PATH}/datasets/gradio_data/assets/envs_demo/$TEST_ENV \
70
+ --save_path ${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/envs/$TEST_ENV \
71
+ --env_strength $ENV_STRENGTH
72
+ fi
73
+ fi
74
+
75
+ # -------------------------------------- module 3 -----------------------------------------
76
+ if [ "$INFER_3" -eq 1 ]; then
77
+ source ${CONDA_PATH}/bin/activate ${CONDA_PREFIX_1}
78
+ cd ${REPO_PATH}
79
+
80
+ # LIGHT_TYPE non-zero forces FRAME ≠ 1
81
+ if [ "$LIGHT_TYPE" -ne 0 ] && [ "$FRAME" -eq 1 ]; then
82
+ export INFER_FRAME=25
83
+ else
84
+ export INFER_FRAME=$FRAME
85
+ fi
86
+
87
+ if [ "$INFER_FRAME" -eq 1 ]; then
88
+ SAVE_EXT='png'
89
+ else
90
+ SAVE_EXT='mp4'
91
+ fi
92
+
93
+ if [ "$INFER_FRAME" -eq 1 ]; then
94
+ export HEIGHT=1024
95
+ export WIDTH=1472
96
+ export CKT="${REPO_PATH}/checkpoints/model_frame1_1024_1472.ckpt"
97
+ elif [ "$INFER_FRAME" -eq 25 ]; then
98
+ export HEIGHT=480
99
+ export WIDTH=832
100
+ export CKT="${REPO_PATH}/checkpoints/model_frame25_480_832.ckpt"
101
+ else
102
+ export HEIGHT=480
103
+ export WIDTH=832
104
+ export CKT="${REPO_PATH}/checkpoints/model_frame57_480_832.ckpt"
105
+ fi
106
+
107
+
108
+ if [ "$LIGHT_TYPE" -eq 0 ]; then
109
+ CUDA_HOME=$CONDA_PREFIX_1 PYTHONPATH=$(pwd) python relit_inference.py \
110
+ --dataset_path "${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/rego" \
111
+ --ckpt_path=$CKT \
112
+ --output_dir "${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/relighting.$TEST_ENV" \
113
+ --output_path "${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/$TEST_ID.$TEST_ENV.$SAVE_EXT" \
114
+ --cfg_scale 1.0 \
115
+ --height $HEIGHT \
116
+ --width $WIDTH \
117
+ --num_frames $INFER_FRAME \
118
+ --padding_resolution \
119
+ --use_ref_image \
120
+ --env_map_path ${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/envs/$TEST_ENV \
121
+ --frame_interval 1 \
122
+ --num_inference_steps $NUM_INFER_STEPS \
123
+ --wo_ref_weight $WORW \
124
+ --quality 10
125
+ elif [ "$LIGHT_TYPE" -eq 1 ]; then
126
+ CUDA_HOME=$CONDA_PREFIX_1 PYTHONPATH=$(pwd) python relit_inference.py \
127
+ --dataset_path "${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/rego" \
128
+ --ckpt_path=$CKT \
129
+ --output_dir "${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/relighting.$TEST_ENV" \
130
+ --output_path "${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/$TEST_ID.$TEST_ENV.$SAVE_EXT" \
131
+ --cfg_scale 1.0 \
132
+ --height $HEIGHT \
133
+ --width $WIDTH \
134
+ --num_frames $INFER_FRAME \
135
+ --padding_resolution \
136
+ --use_ref_image \
137
+ --env_map_path ${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/envs/$TEST_ENV \
138
+ --frame_interval 1 \
139
+ --num_inference_steps $NUM_INFER_STEPS \
140
+ --wo_ref_weight $WORW \
141
+ --use_rotate_light \
142
+ --quality 10
143
+ elif [ "$LIGHT_TYPE" -eq 2 ]; then
144
+ CUDA_HOME=$CONDA_PREFIX_1 PYTHONPATH=$(pwd) python relit_inference.py \
145
+ --dataset_path "${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/rego" \
146
+ --ckpt_path=$CKT \
147
+ --output_dir "${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/relighting.$TEST_ENV" \
148
+ --output_path "${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/$TEST_ID.$TEST_ENV.$SAVE_EXT" \
149
+ --cfg_scale 1.0 \
150
+ --height $HEIGHT \
151
+ --width $WIDTH \
152
+ --num_frames $INFER_FRAME \
153
+ --padding_resolution \
154
+ --use_ref_image \
155
+ --env_map_path ${REPO_PATH}/datasets/gradio_data/results/$TEST_ID/envs/$TEST_ENV \
156
+ --frame_interval 1 \
157
+ --num_inference_steps $NUM_INFER_STEPS \
158
+ --wo_ref_weight $WORW \
159
+ --use_fixed_frame_and_w_rotate_light \
160
+ --quality 10
161
+ fi
162
+ fi
tools/process_env_maps.py ADDED
@@ -0,0 +1,599 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import glob
3
+ import json
4
+ import cv2
5
+ import numpy as np
6
+ import argparse
7
+ import torch
8
+ import imageio.v2 as imageio
9
+ import imageio.v3 as imageio_v3
10
+
11
+ import nvdiffrast.torch as dr
12
+
13
+ # Enable OpenEXR support in OpenCV
14
+ os.environ['OPENCV_IO_ENABLE_OPENEXR'] = '1'
15
+
16
+ def swap_yz_in_extrinsic_matrix(matrix):
17
+ assert matrix.shape == (4, 4), "Input must be a 4x4 matrix"
18
+
19
+ new_matrix = matrix.copy()
20
+ new_matrix[1, :], new_matrix[2, :] = new_matrix[2, :].copy(), new_matrix[1, :].copy()
21
+ new_matrix[:, 1], new_matrix[:, 2] = new_matrix[:, 2].copy(), new_matrix[:, 1].copy()
22
+
23
+ return new_matrix
24
+
25
+ def swap_yz_output_in_extrinsic_matrix(matrix):
26
+ assert matrix.shape == (4, 4), "Input must be a 4x4 matrix"
27
+
28
+ new_matrix = matrix.copy()
29
+ new_matrix[1, :], new_matrix[2, :] = new_matrix[2, :].copy(), new_matrix[1, :].copy()
30
+
31
+ return new_matrix
32
+
33
+ def euler_to_rotation_matrix(euler_angles, inverse_y=True, y_bias=0):
34
+ alpha, gamma, beta = euler_angles
35
+ beta += y_bias
36
+ if inverse_y:
37
+ beta = -beta
38
+
39
+ R_x = np.array([[1, 0, 0],
40
+ [0, np.cos(alpha), -np.sin(alpha)],
41
+ [0, np.sin(alpha), np.cos(alpha)]])
42
+
43
+ R_y = np.array([[np.cos(beta), 0, np.sin(beta)],
44
+ [0, 1, 0],
45
+ [-np.sin(beta), 0, np.cos(beta)]])
46
+
47
+ R_z = np.array([[np.cos(gamma), -np.sin(gamma), 0],
48
+ [np.sin(gamma), np.cos(gamma), 0],
49
+ [0, 0, 1]])
50
+
51
+ R = np.dot(R_z, np.dot(R_y, R_x))
52
+ return R
53
+
54
+ def remove_yaw_rotation(c2w_list):
55
+ c2w_0 = c2w_list[0]
56
+ rotation_0 = c2w_0[:3, :3]
57
+
58
+ yaw_0 = np.arctan2(rotation_0[2, 0], rotation_0[0, 0])
59
+
60
+ yaw_rotation_matrix = np.array([
61
+ [np.cos(-yaw_0), 0, np.sin(-yaw_0), 0],
62
+ [0, 1, 0, 0],
63
+ [-np.sin(-yaw_0), 0, np.cos(-yaw_0), 0],
64
+ [0, 0, 0, 1]
65
+ ])
66
+
67
+ c2w_list_adjusted = [yaw_rotation_matrix @ c2w_i for c2w_i in c2w_list]
68
+
69
+ return c2w_list_adjusted
70
+
71
+ def adjust_yaw_rotation(c2w_list, yaw_0):
72
+
73
+ yaw_rotation_matrix = np.array([
74
+ [np.cos(-yaw_0), 0, np.sin(-yaw_0), 0],
75
+ [0, 1, 0, 0],
76
+ [-np.sin(-yaw_0), 0, np.cos(-yaw_0), 0],
77
+ [0, 0, 0, 1]
78
+ ])
79
+
80
+ c2w_list_adjusted = [yaw_rotation_matrix @ c2w_i for c2w_i in c2w_list]
81
+
82
+ return c2w_list_adjusted
83
+
84
+ def reverse_yaw_rotation(c2w_list):
85
+ c2w_list_reversed = []
86
+
87
+ for c2w in c2w_list:
88
+ rotation = c2w[:3, :3]
89
+
90
+ yaw = np.arctan2(rotation[2, 0], rotation[0, 0])
91
+
92
+ reversed_yaw = 2 * yaw
93
+
94
+ reverse_yaw_rotation_matrix = np.array([
95
+ [np.cos(reversed_yaw), 0, np.sin(reversed_yaw), 0],
96
+ [0, 1, 0, 0],
97
+ [-np.sin(reversed_yaw), 0, np.cos(reversed_yaw), 0],
98
+ [0, 0, 0, 1]
99
+ ])
100
+
101
+ c2w_reversed = reverse_yaw_rotation_matrix @ c2w
102
+ c2w_list_reversed.append(c2w_reversed)
103
+
104
+ return c2w_list_reversed
105
+
106
+ def prepare_camera_poses(num_frames, fixed_pose, pose_file, pose_offset, pose_reset, device, ign_camera_pose=True, swap_type=0, load_w2c=False, remove_y_rotation=False, reverse_y_rotation=False, yaw_0=0, pose_list=None, rotation_euler=None):
107
+ """Prepare camera poses based on the provided arguments."""
108
+ if pose_list is not None:
109
+ c2w_list = pose_list
110
+ for frame_idx, transform_matrix in enumerate(c2w_list):
111
+ if swap_type == 0:
112
+ new_transform_matrix = transform_matrix
113
+ elif swap_type == 1:
114
+ new_transform_matrix = swap_yz_in_extrinsic_matrix(transform_matrix)
115
+ elif swap_type == 2:
116
+ new_transform_matrix = swap_yz_output_in_extrinsic_matrix(transform_matrix)
117
+ if load_w2c:
118
+ new_transform_matrix = np.linalg.inv(new_transform_matrix)
119
+ c2w_list[frame_idx] = new_transform_matrix
120
+
121
+ if pose_reset:
122
+ w2c_0 = np.linalg.inv(c2w_list[0])
123
+ c2w_list = [w2c_0 @ c2w_i for c2w_i in c2w_list]
124
+
125
+ if remove_y_rotation:
126
+ c2w_list = remove_yaw_rotation(c2w_list)
127
+
128
+ if reverse_y_rotation:
129
+ c2w_list = reverse_yaw_rotation(c2w_list)
130
+
131
+ if rotation_euler is not None:
132
+ for frame_idx, transform_matrix in enumerate(c2w_list):
133
+ rotation_matrix = euler_to_rotation_matrix(rotation_euler, inverse_y=True, y_bias=np.pi/2)
134
+
135
+ if ign_camera_pose:
136
+ transform_matrix = c2w_list[0]
137
+ else:
138
+ transform_matrix = c2w
139
+
140
+ rotation_matrix_4x4 = np.eye(4)
141
+ rotation_matrix_4x4[:3, :3] = rotation_matrix
142
+
143
+ new_transform_matrix = np.dot(rotation_matrix_4x4, transform_matrix)
144
+ c2w_list[frame_idx] = new_transform_matrix
145
+
146
+ return c2w_list
147
+
148
+ elif fixed_pose or pose_file is None:
149
+ return [np.eye(4) for _ in range(num_frames)]
150
+
151
+ with open(pose_file, 'r') as f:
152
+ meta = json.load(f)
153
+ frames = meta['frames'][pose_offset:pose_offset + num_frames]
154
+
155
+ for frame_idx, data in enumerate(frames):
156
+ transform_matrix = np.array(data["transform_matrix"])
157
+ if swap_type == 0:
158
+ new_transform_matrix = transform_matrix
159
+ elif swap_type == 1:
160
+ new_transform_matrix = swap_yz_in_extrinsic_matrix(transform_matrix)
161
+ if load_w2c:
162
+ new_transform_matrix = np.linalg.inv(new_transform_matrix)
163
+ data["transform_matrix"] = new_transform_matrix.tolist()
164
+ frames[frame_idx] = data
165
+
166
+ if ign_camera_pose:
167
+ c2w_list = [np.array(frames[0]["transform_matrix"]) for frame in frames]
168
+ else:
169
+ c2w_list = [np.array(frame['transform_matrix']) for frame in frames]
170
+
171
+ if pose_reset:
172
+ w2c_0 = np.linalg.inv(c2w_list[0])
173
+ c2w_list = [w2c_0 @ c2w_i for c2w_i in c2w_list] # compute c2c0
174
+
175
+ if remove_y_rotation:
176
+ c2w_list = remove_yaw_rotation(c2w_list)
177
+
178
+ if reverse_y_rotation:
179
+ c2w_list = reverse_yaw_rotation(c2w_list)
180
+
181
+ if yaw_0 != 0:
182
+ c2w_list = adjust_yaw_rotation(c2w_list, yaw_0)
183
+
184
+ for frame_idx, (data, c2w) in enumerate(zip(frames, c2w_list)):
185
+ if "hdri_euler" in data.keys():
186
+ rotation_matrix = euler_to_rotation_matrix(data["hdri_euler"], inverse_y=False)
187
+
188
+ if ign_camera_pose:
189
+ transform_matrix = c2w_list[0]
190
+ else:
191
+ transform_matrix = c2w
192
+
193
+ rotation_matrix_4x4 = np.eye(4)
194
+ rotation_matrix_4x4[:3, :3] = rotation_matrix
195
+ new_transform_matrix = np.dot(rotation_matrix_4x4, transform_matrix)
196
+ c2w_list[frame_idx] = new_transform_matrix
197
+ else:
198
+ print(f"Warning: 'hdri_euler' not found in frame {frame_idx} of {pose_file}. Using original transform matrix.")
199
+ break
200
+
201
+ return c2w_list
202
+
203
+ def latlong_vec(res, device=None):
204
+ gy, gx = torch.meshgrid(torch.linspace( 0.0 + 1.0 / res[0], 1.0 - 1.0 / res[0], res[0], device=device),
205
+ torch.linspace(-1.0 + 1.0 / res[1], 1.0 - 1.0 / res[1], res[1], device=device),
206
+ indexing='ij')
207
+
208
+ sintheta, costheta = torch.sin(gy*np.pi), torch.cos(gy*np.pi)
209
+ sinphi, cosphi = torch.sin(gx*np.pi), torch.cos(gx*np.pi)
210
+
211
+ dir_vec = torch.stack((
212
+ sintheta*sinphi,
213
+ costheta,
214
+ -sintheta*cosphi
215
+ ), dim=-1)
216
+ # return dr.texture(cubemap[None, ...], dir_vec[None, ...].contiguous(), filter_mode='linear', boundary_mode='cube')[0]
217
+ return dir_vec #[H, W, 3]
218
+
219
+ def envmap_vec(res, device=None):
220
+ return -latlong_vec(res, device).flip(0).flip(1) #[H, W, 3]
221
+
222
+ def dot(x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
223
+ return torch.sum(x*y, -1, keepdim=True)
224
+
225
+ def length(x: torch.Tensor, eps: float =1e-20) -> torch.Tensor:
226
+ return torch.sqrt(torch.clamp(dot(x,x), min=eps)) # Clamp to avoid nan gradients because grad(sqrt(0)) = NaN
227
+
228
+ def safe_normalize(x: torch.Tensor, eps: float =1e-20) -> torch.Tensor:
229
+ return x / length(x, eps)
230
+
231
+ def cube_to_dir(s, x, y):
232
+ if s == 0: rx, ry, rz = torch.ones_like(x), -y, -x
233
+ elif s == 1: rx, ry, rz = -torch.ones_like(x), -y, x
234
+ elif s == 2: rx, ry, rz = x, torch.ones_like(x), y
235
+ elif s == 3: rx, ry, rz = x, -torch.ones_like(x), -y
236
+ elif s == 4: rx, ry, rz = x, -y, torch.ones_like(x)
237
+ elif s == 5: rx, ry, rz = -x, -y, -torch.ones_like(x)
238
+ return torch.stack((rx, ry, rz), dim=-1)
239
+
240
+ def latlong_to_cubemap(latlong_map, res):
241
+ cubemap = torch.zeros(6, res[0], res[1], latlong_map.shape[-1], dtype=torch.float32, device='cuda')
242
+ for s in range(6):
243
+ gy, gx = torch.meshgrid(torch.linspace(-1.0 + 1.0 / res[0], 1.0 - 1.0 / res[0], res[0], device='cuda'),
244
+ torch.linspace(-1.0 + 1.0 / res[1], 1.0 - 1.0 / res[1], res[1], device='cuda'),
245
+ indexing='ij')
246
+ v = safe_normalize(cube_to_dir(s, gx, gy))
247
+
248
+ tu = torch.atan2(v[..., 0:1], -v[..., 2:3]) / (2 * np.pi) + 0.5
249
+ tv = torch.acos(torch.clamp(v[..., 1:2], min=-1, max=1)) / np.pi
250
+ texcoord = torch.cat((tu, tv), dim=-1)
251
+
252
+ cubemap[s, ...] = dr.texture(latlong_map[None, ...], texcoord[None, ...], filter_mode='linear')[0]
253
+ return cubemap
254
+
255
+ def load_and_preprocess_hdr(hdr_dir, env_strength, env_flip, env_rot, device, rotation180=False, inverse_env=False, flip_env=False):
256
+ """Load and preprocess the HDR environment map."""
257
+ if hdr_dir.endswith('.hdr') or hdr_dir.endswith('.exr'):
258
+ latlong_img = imageio_v3.imread(hdr_dir, flags=cv2.IMREAD_UNCHANGED, plugin='opencv')
259
+ elif hdr_dir.endswith('.jpg') or hdr_dir.endswith('.png'):
260
+ import skimage
261
+ latlong_img = skimage.io.imread(hdr_dir)[..., :3]
262
+ latlong_img = skimage.img_as_float(latlong_img)
263
+ latlong_img = np.power(latlong_img, 2.4).astype(np.float32)
264
+ latlong_img *= 2 # for mit dataset
265
+
266
+ if rotation180:
267
+ height, width, channels = latlong_img.shape
268
+ shift_amount = width // 2
269
+ shifted_hdr = np.zeros_like(latlong_img)
270
+ shifted_hdr[:, -shift_amount:, :] = latlong_img[:, :shift_amount, :]
271
+ shifted_hdr[:, :-shift_amount, :] = latlong_img[:, shift_amount:, :]
272
+ latlong_img = shifted_hdr
273
+
274
+ if inverse_env:
275
+ latlong_img = latlong_img[:, ::-1, :]
276
+ height, width, channels = latlong_img.shape
277
+ shift_amount = width // 2
278
+ shifted_hdr = np.zeros_like(latlong_img)
279
+ shifted_hdr[:, -shift_amount:, :] = latlong_img[:, :shift_amount, :]
280
+ shifted_hdr[:, :-shift_amount, :] = latlong_img[:, shift_amount:, :]
281
+ latlong_img = shifted_hdr
282
+
283
+ if flip_env:
284
+ latlong_img = latlong_img[:, ::-1, :].copy()
285
+
286
+ latlong_img = torch.tensor(latlong_img, dtype=torch.float32, device=device)
287
+ latlong_img *= env_strength
288
+
289
+ # Cleanup NaNs and Infs
290
+ latlong_img = torch.nan_to_num(latlong_img, nan=0.0, posinf=65504.0, neginf=0.0)
291
+ latlong_img = latlong_img.clamp(0.0, 65504.0)
292
+
293
+ if env_flip:
294
+ latlong_img = torch.flip(latlong_img, dims=[1])
295
+
296
+ if env_rot != 0:
297
+ lat_h, lat_w = latlong_img.shape[:2]
298
+ pixel_rot = int(lat_w * env_rot / 360)
299
+ latlong_img = torch.roll(latlong_img, shifts=pixel_rot, dims=1)
300
+
301
+ # Convert to cubemap
302
+ cubemap = latlong_to_cubemap(latlong_img, [512, 512])
303
+ return cubemap
304
+
305
+ def prepare_metadata(hdr_dir, env_rot, env_flip, env_strength, fixed_pose, rotate_envlight, save_dir, prefix):
306
+ """Prepare metadata about the environment map processing."""
307
+ env_meta = {
308
+ 'envmap': os.path.basename(hdr_dir),
309
+ 'envmap_rot': env_rot,
310
+ 'envmap_flip': env_flip,
311
+ 'envmap_strength': env_strength,
312
+ 'fixed_pose': fixed_pose,
313
+ 'rotate_envlight': rotate_envlight,
314
+ }
315
+
316
+ if save_dir:
317
+ os.makedirs(save_dir, exist_ok=True)
318
+ meta_path = os.path.join(save_dir, f'{prefix}.meta.json')
319
+ with open(meta_path, 'w') as f:
320
+ json.dump(env_meta, f, indent=4)
321
+
322
+ return env_meta
323
+
324
+ def rotate_y(a, device=None):
325
+ s, c = np.sin(a), np.cos(a)
326
+ return torch.tensor([[ c, 0, s, 0],
327
+ [ 0, 1, 0, 0],
328
+ [-s, 0, c, 0],
329
+ [ 0, 0, 0, 1]], dtype=torch.float32, device=device)
330
+
331
+ def process_projected_envmap(cubemap, vec, c2w, y_rot, H, W):
332
+ """Process the camera-oriented projected environment map."""
333
+ vec_cam = vec.view(-1, 3) @ c2w[:3, :3].T
334
+ vec_query = (vec_cam @ y_rot[:3, :3].T).view(1, H, W, 3)
335
+ env_proj = dr.texture(cubemap.unsqueeze(0), -vec_query.contiguous(),
336
+ filter_mode='linear', boundary_mode='cube')[0]
337
+ env_proj = torch.flip(env_proj, dims=[0, 1])
338
+ return env_proj
339
+
340
+ def rgb2srgb(rgb):
341
+ return torch.where(rgb <= 0.0031308, 12.92 * rgb, 1.055 * rgb**(1/2.4) - 0.055)
342
+
343
+ def reinhard(x, max_point=16):
344
+ y_rein = x * (1 + x / (max_point ** 2)) / (1 + x)
345
+ return y_rein
346
+
347
+ def hdr_mapping(env_hdr, log_scale):
348
+ """Map HDR environment maps to LDR and logarithmic representations."""
349
+ env_ev0 = rgb2srgb(reinhard(env_hdr, max_point=16).clamp(0, 1))
350
+ env_log = rgb2srgb(torch.log1p(env_hdr) / np.log1p(log_scale)).clamp(0, 1)
351
+ return {
352
+ 'env_hdr': env_hdr, # Original HDR image
353
+ 'env_ev0': env_ev0, # LDR image after tone mapping
354
+ 'env_log': env_log, # Logarithmic scaling
355
+ }
356
+
357
+ def process_environment_map(
358
+ hdr_dir,
359
+ resolution=(512, 512),
360
+ num_frames=1,
361
+ fixed_pose=True,
362
+ pose_file=None,
363
+ pose_list=None,
364
+ rotation_euler=None,
365
+ pose_offset=0,
366
+ pose_reset=False,
367
+ rotate_envlight=False,
368
+ env_format=['proj'],
369
+ log_scale=10000,
370
+ env_strength=1.0,
371
+ env_flip=True,
372
+ env_rot=180.0,
373
+ save_dir=None,
374
+ prefix='0000',
375
+ device=None,
376
+ rotation180=False,
377
+ ign_camera_pose=True,
378
+ inverse_env=False,
379
+ swap_type=0,
380
+ load_w2c=False,
381
+ flip_env=False,
382
+ remove_y_rotation=False,
383
+ reverse_y_rotation=False,
384
+ yaw_0=0,
385
+ ):
386
+ """
387
+ Preprocess HDR environment maps for rendering.
388
+ FIXME: Note that this function bakes in a flip and rotate operation for the environment light. Set to env_flip=True and env_rot=180 is considered as loading the original environment map.
389
+
390
+ Args:
391
+ hdr_dir (str): Path to the HDR environment map file.
392
+ resolution (tuple of int): Resolution of the output images (H, W).
393
+ num_frames (int): Number of frames to process.
394
+ fixed_pose (bool): Use a fixed camera pose (identity matrix) if True.
395
+ pose_file (str): Path to the camera pose file (JSON).
396
+ pose_offset (int): Offset for the pose frames in the pose file.
397
+ pose_reset (bool): Reset camera poses to be relative to the first frame.
398
+ rotate_envlight (bool): Rotate the environment light over frames if True.
399
+ env_format (list of str): Formats of the environment maps to generate ('proj', 'fixed', 'ball').
400
+ log_scale (int): Log scale factor for HDR mapping.
401
+ env_strength (float): Strength multiplier for the environment map.
402
+ env_flip (bool): Flip the environment map horizontally if True.
403
+ env_rot (float): Rotation angle for the environment map in degrees.
404
+ save_dir (str): Directory to save the processed images (optional).
405
+ prefix (str): Prefix for the output files (used if saving images).
406
+
407
+ Returns:
408
+ dict: A dictionary containing the processed environment maps and metadata.
409
+ {
410
+ 'metadata': env_meta,
411
+ 'fixed': mapping_results_for_fixed_envmap, # Only if 'fixed' in env_format
412
+ 'env_ldr': stacked_tensor_of_proj_env_ldr, # Only if 'proj' in env_format
413
+ 'env_log': stacked_tensor_of_proj_env_log, # Only if 'proj' in env_format
414
+ 'ball_env_ldr': stacked_tensor_of_ball_env_ldr, # Only if 'ball' in env_format
415
+ 'ball_env_log': stacked_tensor_of_ball_env_log, # Only if 'ball' in env_format
416
+ }
417
+ Tensors are with shape (T, H, W, 3) in [0, 1]
418
+ """
419
+ H, W = resolution # (704, 1280)
420
+ if device is None:
421
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
422
+ vec = latlong_vec((H, W), device=device)
423
+
424
+ # Prepare camera poses
425
+ poses = prepare_camera_poses(
426
+ num_frames=num_frames,
427
+ fixed_pose=fixed_pose,
428
+ pose_file=pose_file,
429
+ pose_offset=pose_offset,
430
+ pose_reset=pose_reset,
431
+ device=device,
432
+ ign_camera_pose=ign_camera_pose,
433
+ swap_type=swap_type,
434
+ load_w2c=load_w2c,
435
+ remove_y_rotation=remove_y_rotation,
436
+ reverse_y_rotation=reverse_y_rotation,
437
+ yaw_0=yaw_0,
438
+ pose_list=pose_list,
439
+ rotation_euler=rotation_euler,
440
+ )
441
+
442
+ # Prepare rotations for the environment light # 57 * rot
443
+ rots = np.linspace(0, 2 * np.pi, num_frames) if rotate_envlight else [0] * num_frames
444
+
445
+ # Load and preprocess the HDR environment map
446
+ cubemap = load_and_preprocess_hdr(
447
+ hdr_dir=hdr_dir,
448
+ env_strength=env_strength,
449
+ env_flip=env_flip,
450
+ env_rot=env_rot,
451
+ device=device,
452
+ rotation180=rotation180,
453
+ inverse_env=inverse_env,
454
+ flip_env=flip_env,
455
+ )
456
+
457
+ # Prepare metadata
458
+ env_meta = prepare_metadata(
459
+ hdr_dir=hdr_dir,
460
+ env_rot=env_rot,
461
+ env_flip=env_flip,
462
+ env_strength=env_strength,
463
+ fixed_pose=fixed_pose,
464
+ rotate_envlight=rotate_envlight,
465
+ save_dir=save_dir,
466
+ prefix=prefix
467
+ )
468
+
469
+ # Initialize result dictionary
470
+ results = {
471
+ 'metadata': env_meta,
472
+ }
473
+
474
+ # Prepare lists to collect per-frame tensors
475
+ if 'proj' in env_format:
476
+ proj_env_ldr_list = []
477
+ proj_env_log_list = []
478
+
479
+ # Process per-frame environment maps
480
+ for i in range(num_frames):
481
+ c2w = torch.from_numpy(poses[i]).float().to(device)
482
+ y_rot = rotate_y(rots[i], device=device)
483
+
484
+ if 'proj' in env_format:
485
+ env_proj = process_projected_envmap(cubemap, vec, c2w, y_rot, H, W)
486
+ mapping_results = hdr_mapping(env_proj, log_scale=log_scale)
487
+ proj_env_ldr_list.append(mapping_results['env_ev0'])
488
+ proj_env_log_list.append(mapping_results['env_log'])
489
+
490
+ if 'proj' in env_format:
491
+ results['env_ldr'] = torch.stack(proj_env_ldr_list, dim=0)
492
+ results['env_log'] = torch.stack(proj_env_log_list, dim=0)
493
+
494
+ return results
495
+
496
+ def save_array_as_video(video_array, output_path: str, fps: int = 24):
497
+ """
498
+ video_array: t h w c, np.array or tensors
499
+ """
500
+ if isinstance(video_array, torch.Tensor):
501
+ video_array = video_array.cpu().numpy()
502
+ if video_array.dtype != np.uint8:
503
+ print("float 2 uint8")
504
+ # If the data range is [-1, 1]
505
+ if video_array.min() < 0:
506
+ video_array = ((video_array + 1) * 127.5).clip(0, 255).astype(np.uint8)
507
+ # If the data range is [0, 1]
508
+ else:
509
+ video_array = (video_array * 255).clip(0, 255).astype(np.uint8)
510
+
511
+ try:
512
+ if not os.path.isfile(output_path):
513
+ imageio.mimsave(
514
+ output_path,
515
+ [frame for frame in video_array],
516
+ fps=fps,
517
+ codec='libx264'
518
+ )
519
+ print("succeed to save vide")
520
+ print(f"video already exists in {output_path}")
521
+ except Exception as e:
522
+ print(f"fail to save video: {e}")
523
+
524
+ def process_hdr(hdr_path, save_path, env_strength=1.0, inverse_env=False):
525
+ hdr_path = glob.glob(f'{hdr_path}*')[0]
526
+
527
+ if '.hdr' in hdr_path:
528
+ env_strength = env_strength / 3.0
529
+
530
+ num_of_frames = 57
531
+ ldr_list = []
532
+ hdr_log_list = []
533
+ env_dir_list = []
534
+
535
+ envlight_dict = process_environment_map(
536
+ hdr_dir=hdr_path,
537
+ resolution=(320, 576),
538
+ num_frames=num_of_frames, # 1 for mit dataset
539
+ fixed_pose=True,
540
+ rotate_envlight=False,
541
+ env_format=['proj', ],
542
+ device='cuda',
543
+ rotation180=False,
544
+ inverse_env=inverse_env, # True for mit dataset, False for others
545
+ log_scale=60000,
546
+ env_strength=env_strength, # 1.0 for mit dataset
547
+ ) # Tensors are with shape (T, H, W, 3) in [0, 1]
548
+ ldr_list = (envlight_dict['env_ldr'].cpu().numpy() * 255).astype(np.uint8)
549
+ hdr_log_list = (envlight_dict['env_log'].cpu().numpy() * 255).astype(np.uint8)
550
+ env_nrm = ((envmap_vec([320, 576], device='cpu').cpu().numpy()*0.5 + 0.5) * 255).astype(np.uint8)
551
+ for _ in range(num_of_frames):
552
+ env_dir_list.append(env_nrm)
553
+
554
+ os.makedirs(save_path, exist_ok=True)
555
+ ldr_video_path = os.path.join(save_path, "ldr_video_fix_first_frame.mp4")
556
+ hdr_log_video_path = os.path.join(save_path, "hdr_log_video_fix_first_frame.mp4")
557
+ env_dir_video_path = os.path.join(save_path, "env_dir_video_fix_first_frame.mp4")
558
+
559
+ if os.path.exists(ldr_video_path):
560
+ os.remove(ldr_video_path)
561
+ if os.path.exists(hdr_log_video_path):
562
+ os.remove(hdr_log_video_path)
563
+ if os.path.exists(env_dir_video_path):
564
+ os.remove(env_dir_video_path)
565
+
566
+ save_array_as_video(np.array(ldr_list),ldr_video_path)
567
+ save_array_as_video(np.array(hdr_log_list),hdr_log_video_path)
568
+ save_array_as_video(np.array(env_dir_list),env_dir_video_path)
569
+
570
+ def parse_arguments() -> argparse.Namespace:
571
+ parser = argparse.ArgumentParser(description="Env maps processing script")
572
+ # Specific arguments
573
+ parser.add_argument(
574
+ "--env_dir",
575
+ type=str,
576
+ default=None,
577
+ help="Path to the directory containing the environment map."
578
+ )
579
+ parser.add_argument(
580
+ "--save_path",
581
+ type=str,
582
+ default=None,
583
+ help="Path to the directory where the processed environment maps will be saved."
584
+ )
585
+ parser.add_argument(
586
+ "--env_strength",
587
+ type=float,
588
+ default=1.0,
589
+ help="Strength of the environment map."
590
+ )
591
+ return parser.parse_args()
592
+
593
+ if __name__ == "__main__":
594
+ args = parse_arguments()
595
+ process_hdr(args.env_dir, save_path=args.save_path, env_strength=args.env_strength)
596
+
597
+
598
+
599
+
tools/reorg_gbuffer_from_dr_delighting.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pdb
3
+ import json
4
+ from tqdm import tqdm
5
+ import shutil, glob
6
+ import argparse
7
+
8
+ def parse_arguments() -> argparse.Namespace:
9
+ parser = argparse.ArgumentParser(description="Text to world generation demo script")
10
+
11
+ parser.add_argument(
12
+ "--frame_dir",
13
+ type=str,
14
+ default=None,
15
+ help="."
16
+ )
17
+ parser.add_argument(
18
+ "--gbuffer_dir",
19
+ type=str,
20
+ default=None,
21
+ help="."
22
+ )
23
+ parser.add_argument(
24
+ "--tgt_dir",
25
+ type=str,
26
+ default=None,
27
+ help="."
28
+ )
29
+
30
+ return parser.parse_args()
31
+
32
+
33
+ def collect_available_chunk_ids(gbuffer_seq_dir):
34
+ chunk_ids = set()
35
+ for path in glob.glob(os.path.join(gbuffer_seq_dir, "*.jpg")):
36
+ name = os.path.basename(path)
37
+ parts = name.split(".")
38
+ if len(parts) < 4:
39
+ continue
40
+ if parts[0].isdigit():
41
+ chunk_ids.add(int(parts[0]))
42
+ return sorted(chunk_ids)
43
+
44
+
45
+ args = parse_arguments()
46
+ frame_dir = args.frame_dir
47
+ gbuffer_dir = args.gbuffer_dir
48
+ tgt_dir = args.tgt_dir
49
+ os.makedirs(tgt_dir, exist_ok=True)
50
+
51
+ seq_list = sorted(os.listdir(frame_dir))
52
+ for seq_name in tqdm(seq_list):
53
+ chunk_size = 57
54
+ overlap_n_frames = 0
55
+ step = chunk_size - overlap_n_frames
56
+
57
+ rgb_path_list = sorted(
58
+ glob.glob(os.path.join(frame_dir, f"{seq_name}/*.jpg")) +
59
+ glob.glob(os.path.join(frame_dir, f"{seq_name}/*.png"))
60
+ )
61
+ images_num = len(rgb_path_list)
62
+
63
+ gbuffer_file_path = f"{gbuffer_dir}/{seq_name}"
64
+ available_chunk_ids = collect_available_chunk_ids(gbuffer_file_path)
65
+
66
+ if len(available_chunk_ids) == 0:
67
+ print(f"Skip {seq_name}: no gbuffer files found in {gbuffer_file_path}")
68
+ continue
69
+
70
+ max_valid_chunk_idx = max(available_chunk_ids)
71
+
72
+ print("Processing", seq_name)
73
+ for t_gbuffer in ["Base Color", "normal", "depth", "Roughness", "Metallic", "env", "images_4"]:
74
+ os.system(f"rm -rf {tgt_dir}/{seq_name}*/{t_gbuffer}")
75
+
76
+ for image_idx in range(images_num):
77
+ raw_chunk_idx = image_idx // step
78
+ frame_in_chunk = image_idx % step
79
+
80
+ if raw_chunk_idx > max_valid_chunk_idx:
81
+ print(
82
+ f"Stop {seq_name} at image_idx={image_idx}: "
83
+ f"raw_chunk_idx={raw_chunk_idx} exceeds max_valid_chunk_idx={max_valid_chunk_idx}"
84
+ )
85
+ break
86
+
87
+ chunk_idx = raw_chunk_idx
88
+ frame_idx = frame_in_chunk
89
+ seq_name_now = seq_name + f".{chunk_idx}"
90
+
91
+ gbuffer_src_map = {}
92
+ missing_file = False
93
+ for gbuffer, t_gbuffer in [
94
+ ("basecolor", "Base Color"),
95
+ ("normal", "normal"),
96
+ ("depth", "depth"),
97
+ ("roughness", "Roughness"),
98
+ ("metallic", "Metallic"),
99
+ ]:
100
+ img_name = f"{chunk_idx:04d}.{frame_in_chunk:04d}.{gbuffer}.jpg"
101
+ src_path = os.path.join(gbuffer_file_path, img_name)
102
+ if not os.path.exists(src_path):
103
+ print(f"Stop {seq_name_now}: missing gbuffer file {src_path}")
104
+ missing_file = True
105
+ break
106
+ gbuffer_src_map[t_gbuffer] = src_path
107
+
108
+ if missing_file:
109
+ break
110
+
111
+ for t_gbuffer in ["Base Color", "normal", "depth", "Roughness", "Metallic", "env", "images_4"]:
112
+ if os.path.exists(f"{tgt_dir}/{seq_name_now}/{t_gbuffer}") and frame_idx == 0:
113
+ shutil.rmtree(f"{tgt_dir}/{seq_name_now}/{t_gbuffer}")
114
+ os.makedirs(f"{tgt_dir}/{seq_name_now}/{t_gbuffer}", exist_ok=True)
115
+
116
+ for t_gbuffer, src_path in gbuffer_src_map.items():
117
+ shutil.copy(
118
+ src_path,
119
+ f"{tgt_dir}/{seq_name_now}/{t_gbuffer}/frame_{frame_idx:04d}.jpg"
120
+ )
121
+
122
+ rgb_ext = os.path.basename(rgb_path_list[image_idx]).split(".")[-1]
123
+ shutil.copy(
124
+ rgb_path_list[image_idx],
125
+ f"{tgt_dir}/{seq_name_now}/images_4/frame_{frame_idx:04d}.{rgb_ext}"
126
+ )
127
+
128
+
129
+