mambazjp commited on
Commit
8b79d57
·
1 Parent(s): 47162d0

Upload 57 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +4 -0
  2. RobustVideoMatting/LICENSE +674 -0
  3. RobustVideoMatting/README.md +241 -0
  4. RobustVideoMatting/README_zh_Hans.md +240 -0
  5. RobustVideoMatting/__pycache__/inference.cpython-38.pyc +0 -0
  6. RobustVideoMatting/__pycache__/inference_utils.cpython-38.pyc +0 -0
  7. RobustVideoMatting/checkpoint/rvm_mobilenetv3.pth +3 -0
  8. RobustVideoMatting/dataset/augmentation.py +260 -0
  9. RobustVideoMatting/dataset/coco.py +103 -0
  10. RobustVideoMatting/dataset/imagematte.py +98 -0
  11. RobustVideoMatting/dataset/spd.py +27 -0
  12. RobustVideoMatting/dataset/videomatte.py +125 -0
  13. RobustVideoMatting/dataset/youtubevis.py +123 -0
  14. RobustVideoMatting/documentation/image/showreel.gif +3 -0
  15. RobustVideoMatting/documentation/image/teaser.gif +3 -0
  16. RobustVideoMatting/documentation/inference.md +352 -0
  17. RobustVideoMatting/documentation/inference_zh_Hans.md +353 -0
  18. RobustVideoMatting/documentation/misc/aim_test.txt +11 -0
  19. RobustVideoMatting/documentation/misc/d646_test.txt +11 -0
  20. RobustVideoMatting/documentation/misc/dvm_background_test_clips.txt +162 -0
  21. RobustVideoMatting/documentation/misc/dvm_background_train_clips.txt +3117 -0
  22. RobustVideoMatting/documentation/misc/imagematte_train.txt +420 -0
  23. RobustVideoMatting/documentation/misc/imagematte_valid.txt +15 -0
  24. RobustVideoMatting/documentation/misc/spd_preprocess.py +45 -0
  25. RobustVideoMatting/documentation/training.md +158 -0
  26. RobustVideoMatting/evaluation/evaluate_hr.py +216 -0
  27. RobustVideoMatting/evaluation/evaluate_lr.py +254 -0
  28. RobustVideoMatting/evaluation/generate_imagematte_with_background_image.py +146 -0
  29. RobustVideoMatting/evaluation/generate_imagematte_with_background_video.py +189 -0
  30. RobustVideoMatting/evaluation/generate_videomatte_with_background_image.py +78 -0
  31. RobustVideoMatting/evaluation/generate_videomatte_with_background_video.py +118 -0
  32. RobustVideoMatting/hubconf.py +39 -0
  33. RobustVideoMatting/inference.py +207 -0
  34. RobustVideoMatting/inference_itw_rotate.py +78 -0
  35. RobustVideoMatting/inference_speed_test.py +50 -0
  36. RobustVideoMatting/inference_utils.py +88 -0
  37. RobustVideoMatting/model/__init__.py +1 -0
  38. RobustVideoMatting/model/__pycache__/__init__.cpython-38.pyc +0 -0
  39. RobustVideoMatting/model/__pycache__/decoder.cpython-38.pyc +0 -0
  40. RobustVideoMatting/model/__pycache__/deep_guided_filter.cpython-38.pyc +0 -0
  41. RobustVideoMatting/model/__pycache__/fast_guided_filter.cpython-38.pyc +0 -0
  42. RobustVideoMatting/model/__pycache__/lraspp.cpython-38.pyc +0 -0
  43. RobustVideoMatting/model/__pycache__/mobilenetv3.cpython-38.pyc +0 -0
  44. RobustVideoMatting/model/__pycache__/model.cpython-38.pyc +0 -0
  45. RobustVideoMatting/model/__pycache__/resnet.cpython-38.pyc +0 -0
  46. RobustVideoMatting/model/decoder.py +210 -0
  47. RobustVideoMatting/model/deep_guided_filter.py +61 -0
  48. RobustVideoMatting/model/fast_guided_filter.py +76 -0
  49. RobustVideoMatting/model/lraspp.py +29 -0
  50. RobustVideoMatting/model/mobilenetv3.py +72 -0
.gitattributes CHANGED
@@ -1,2 +1,6 @@
1
  Self-Correction-Human-Parsing/demo/lip-visualization.jpg filter=lfs diff=lfs merge=lfs -text
2
  Self-Correction-Human-Parsing/exp-schp-201908301523-atr.pth filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
1
  Self-Correction-Human-Parsing/demo/lip-visualization.jpg filter=lfs diff=lfs merge=lfs -text
2
  Self-Correction-Human-Parsing/exp-schp-201908301523-atr.pth filter=lfs diff=lfs merge=lfs -text
3
+ RobustVideoMatting/checkpoint/rvm_mobilenetv3.pth filter=lfs diff=lfs merge=lfs -text
4
+ RobustVideoMatting/documentation/image/showreel.gif filter=lfs diff=lfs merge=lfs -text
5
+ RobustVideoMatting/documentation/image/teaser.gif filter=lfs diff=lfs merge=lfs -text
6
+ RobustVideoMatting/rvm_mobilenetv3.pth filter=lfs diff=lfs merge=lfs -text
RobustVideoMatting/LICENSE ADDED
@@ -0,0 +1,674 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU General Public License is a free, copyleft license for
11
+ software and other kinds of works.
12
+
13
+ The licenses for most software and other practical works are designed
14
+ to take away your freedom to share and change the works. By contrast,
15
+ the GNU General Public License is intended to guarantee your freedom to
16
+ share and change all versions of a program--to make sure it remains free
17
+ software for all its users. We, the Free Software Foundation, use the
18
+ GNU General Public License for most of our software; it applies also to
19
+ any other work released this way by its authors. You can apply it to
20
+ your programs, too.
21
+
22
+ When we speak of free software, we are referring to freedom, not
23
+ price. Our General Public Licenses are designed to make sure that you
24
+ have the freedom to distribute copies of free software (and charge for
25
+ them if you wish), that you receive source code or can get it if you
26
+ want it, that you can change the software or use pieces of it in new
27
+ free programs, and that you know you can do these things.
28
+
29
+ To protect your rights, we need to prevent others from denying you
30
+ these rights or asking you to surrender the rights. Therefore, you have
31
+ certain responsibilities if you distribute copies of the software, or if
32
+ you modify it: responsibilities to respect the freedom of others.
33
+
34
+ For example, if you distribute copies of such a program, whether
35
+ gratis or for a fee, you must pass on to the recipients the same
36
+ freedoms that you received. You must make sure that they, too, receive
37
+ or can get the source code. And you must show them these terms so they
38
+ know their rights.
39
+
40
+ Developers that use the GNU GPL protect your rights with two steps:
41
+ (1) assert copyright on the software, and (2) offer you this License
42
+ giving you legal permission to copy, distribute and/or modify it.
43
+
44
+ For the developers' and authors' protection, the GPL clearly explains
45
+ that there is no warranty for this free software. For both users' and
46
+ authors' sake, the GPL requires that modified versions be marked as
47
+ changed, so that their problems will not be attributed erroneously to
48
+ authors of previous versions.
49
+
50
+ Some devices are designed to deny users access to install or run
51
+ modified versions of the software inside them, although the manufacturer
52
+ can do so. This is fundamentally incompatible with the aim of
53
+ protecting users' freedom to change the software. The systematic
54
+ pattern of such abuse occurs in the area of products for individuals to
55
+ use, which is precisely where it is most unacceptable. Therefore, we
56
+ have designed this version of the GPL to prohibit the practice for those
57
+ products. If such problems arise substantially in other domains, we
58
+ stand ready to extend this provision to those domains in future versions
59
+ of the GPL, as needed to protect the freedom of users.
60
+
61
+ Finally, every program is threatened constantly by software patents.
62
+ States should not allow patents to restrict development and use of
63
+ software on general-purpose computers, but in those that do, we wish to
64
+ avoid the special danger that patents applied to a free program could
65
+ make it effectively proprietary. To prevent this, the GPL assures that
66
+ patents cannot be used to render the program non-free.
67
+
68
+ The precise terms and conditions for copying, distribution and
69
+ modification follow.
70
+
71
+ TERMS AND CONDITIONS
72
+
73
+ 0. Definitions.
74
+
75
+ "This License" refers to version 3 of the GNU General Public License.
76
+
77
+ "Copyright" also means copyright-like laws that apply to other kinds of
78
+ works, such as semiconductor masks.
79
+
80
+ "The Program" refers to any copyrightable work licensed under this
81
+ License. Each licensee is addressed as "you". "Licensees" and
82
+ "recipients" may be individuals or organizations.
83
+
84
+ To "modify" a work means to copy from or adapt all or part of the work
85
+ in a fashion requiring copyright permission, other than the making of an
86
+ exact copy. The resulting work is called a "modified version" of the
87
+ earlier work or a work "based on" the earlier work.
88
+
89
+ A "covered work" means either the unmodified Program or a work based
90
+ on the Program.
91
+
92
+ To "propagate" a work means to do anything with it that, without
93
+ permission, would make you directly or secondarily liable for
94
+ infringement under applicable copyright law, except executing it on a
95
+ computer or modifying a private copy. Propagation includes copying,
96
+ distribution (with or without modification), making available to the
97
+ public, and in some countries other activities as well.
98
+
99
+ To "convey" a work means any kind of propagation that enables other
100
+ parties to make or receive copies. Mere interaction with a user through
101
+ a computer network, with no transfer of a copy, is not conveying.
102
+
103
+ An interactive user interface displays "Appropriate Legal Notices"
104
+ to the extent that it includes a convenient and prominently visible
105
+ feature that (1) displays an appropriate copyright notice, and (2)
106
+ tells the user that there is no warranty for the work (except to the
107
+ extent that warranties are provided), that licensees may convey the
108
+ work under this License, and how to view a copy of this License. If
109
+ the interface presents a list of user commands or options, such as a
110
+ menu, a prominent item in the list meets this criterion.
111
+
112
+ 1. Source Code.
113
+
114
+ The "source code" for a work means the preferred form of the work
115
+ for making modifications to it. "Object code" means any non-source
116
+ form of a work.
117
+
118
+ A "Standard Interface" means an interface that either is an official
119
+ standard defined by a recognized standards body, or, in the case of
120
+ interfaces specified for a particular programming language, one that
121
+ is widely used among developers working in that language.
122
+
123
+ The "System Libraries" of an executable work include anything, other
124
+ than the work as a whole, that (a) is included in the normal form of
125
+ packaging a Major Component, but which is not part of that Major
126
+ Component, and (b) serves only to enable use of the work with that
127
+ Major Component, or to implement a Standard Interface for which an
128
+ implementation is available to the public in source code form. A
129
+ "Major Component", in this context, means a major essential component
130
+ (kernel, window system, and so on) of the specific operating system
131
+ (if any) on which the executable work runs, or a compiler used to
132
+ produce the work, or an object code interpreter used to run it.
133
+
134
+ The "Corresponding Source" for a work in object code form means all
135
+ the source code needed to generate, install, and (for an executable
136
+ work) run the object code and to modify the work, including scripts to
137
+ control those activities. However, it does not include the work's
138
+ System Libraries, or general-purpose tools or generally available free
139
+ programs which are used unmodified in performing those activities but
140
+ which are not part of the work. For example, Corresponding Source
141
+ includes interface definition files associated with source files for
142
+ the work, and the source code for shared libraries and dynamically
143
+ linked subprograms that the work is specifically designed to require,
144
+ such as by intimate data communication or control flow between those
145
+ subprograms and other parts of the work.
146
+
147
+ The Corresponding Source need not include anything that users
148
+ can regenerate automatically from other parts of the Corresponding
149
+ Source.
150
+
151
+ The Corresponding Source for a work in source code form is that
152
+ same work.
153
+
154
+ 2. Basic Permissions.
155
+
156
+ All rights granted under this License are granted for the term of
157
+ copyright on the Program, and are irrevocable provided the stated
158
+ conditions are met. This License explicitly affirms your unlimited
159
+ permission to run the unmodified Program. The output from running a
160
+ covered work is covered by this License only if the output, given its
161
+ content, constitutes a covered work. This License acknowledges your
162
+ rights of fair use or other equivalent, as provided by copyright law.
163
+
164
+ You may make, run and propagate covered works that you do not
165
+ convey, without conditions so long as your license otherwise remains
166
+ in force. You may convey covered works to others for the sole purpose
167
+ of having them make modifications exclusively for you, or provide you
168
+ with facilities for running those works, provided that you comply with
169
+ the terms of this License in conveying all material for which you do
170
+ not control copyright. Those thus making or running the covered works
171
+ for you must do so exclusively on your behalf, under your direction
172
+ and control, on terms that prohibit them from making any copies of
173
+ your copyrighted material outside their relationship with you.
174
+
175
+ Conveying under any other circumstances is permitted solely under
176
+ the conditions stated below. Sublicensing is not allowed; section 10
177
+ makes it unnecessary.
178
+
179
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
+
181
+ No covered work shall be deemed part of an effective technological
182
+ measure under any applicable law fulfilling obligations under article
183
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184
+ similar laws prohibiting or restricting circumvention of such
185
+ measures.
186
+
187
+ When you convey a covered work, you waive any legal power to forbid
188
+ circumvention of technological measures to the extent such circumvention
189
+ is effected by exercising rights under this License with respect to
190
+ the covered work, and you disclaim any intention to limit operation or
191
+ modification of the work as a means of enforcing, against the work's
192
+ users, your or third parties' legal rights to forbid circumvention of
193
+ technological measures.
194
+
195
+ 4. Conveying Verbatim Copies.
196
+
197
+ You may convey verbatim copies of the Program's source code as you
198
+ receive it, in any medium, provided that you conspicuously and
199
+ appropriately publish on each copy an appropriate copyright notice;
200
+ keep intact all notices stating that this License and any
201
+ non-permissive terms added in accord with section 7 apply to the code;
202
+ keep intact all notices of the absence of any warranty; and give all
203
+ recipients a copy of this License along with the Program.
204
+
205
+ You may charge any price or no price for each copy that you convey,
206
+ and you may offer support or warranty protection for a fee.
207
+
208
+ 5. Conveying Modified Source Versions.
209
+
210
+ You may convey a work based on the Program, or the modifications to
211
+ produce it from the Program, in the form of source code under the
212
+ terms of section 4, provided that you also meet all of these conditions:
213
+
214
+ a) The work must carry prominent notices stating that you modified
215
+ it, and giving a relevant date.
216
+
217
+ b) The work must carry prominent notices stating that it is
218
+ released under this License and any conditions added under section
219
+ 7. This requirement modifies the requirement in section 4 to
220
+ "keep intact all notices".
221
+
222
+ c) You must license the entire work, as a whole, under this
223
+ License to anyone who comes into possession of a copy. This
224
+ License will therefore apply, along with any applicable section 7
225
+ additional terms, to the whole of the work, and all its parts,
226
+ regardless of how they are packaged. This License gives no
227
+ permission to license the work in any other way, but it does not
228
+ invalidate such permission if you have separately received it.
229
+
230
+ d) If the work has interactive user interfaces, each must display
231
+ Appropriate Legal Notices; however, if the Program has interactive
232
+ interfaces that do not display Appropriate Legal Notices, your
233
+ work need not make them do so.
234
+
235
+ A compilation of a covered work with other separate and independent
236
+ works, which are not by their nature extensions of the covered work,
237
+ and which are not combined with it such as to form a larger program,
238
+ in or on a volume of a storage or distribution medium, is called an
239
+ "aggregate" if the compilation and its resulting copyright are not
240
+ used to limit the access or legal rights of the compilation's users
241
+ beyond what the individual works permit. Inclusion of a covered work
242
+ in an aggregate does not cause this License to apply to the other
243
+ parts of the aggregate.
244
+
245
+ 6. Conveying Non-Source Forms.
246
+
247
+ You may convey a covered work in object code form under the terms
248
+ of sections 4 and 5, provided that you also convey the
249
+ machine-readable Corresponding Source under the terms of this License,
250
+ in one of these ways:
251
+
252
+ a) Convey the object code in, or embodied in, a physical product
253
+ (including a physical distribution medium), accompanied by the
254
+ Corresponding Source fixed on a durable physical medium
255
+ customarily used for software interchange.
256
+
257
+ b) Convey the object code in, or embodied in, a physical product
258
+ (including a physical distribution medium), accompanied by a
259
+ written offer, valid for at least three years and valid for as
260
+ long as you offer spare parts or customer support for that product
261
+ model, to give anyone who possesses the object code either (1) a
262
+ copy of the Corresponding Source for all the software in the
263
+ product that is covered by this License, on a durable physical
264
+ medium customarily used for software interchange, for a price no
265
+ more than your reasonable cost of physically performing this
266
+ conveying of source, or (2) access to copy the
267
+ Corresponding Source from a network server at no charge.
268
+
269
+ c) Convey individual copies of the object code with a copy of the
270
+ written offer to provide the Corresponding Source. This
271
+ alternative is allowed only occasionally and noncommercially, and
272
+ only if you received the object code with such an offer, in accord
273
+ with subsection 6b.
274
+
275
+ d) Convey the object code by offering access from a designated
276
+ place (gratis or for a charge), and offer equivalent access to the
277
+ Corresponding Source in the same way through the same place at no
278
+ further charge. You need not require recipients to copy the
279
+ Corresponding Source along with the object code. If the place to
280
+ copy the object code is a network server, the Corresponding Source
281
+ may be on a different server (operated by you or a third party)
282
+ that supports equivalent copying facilities, provided you maintain
283
+ clear directions next to the object code saying where to find the
284
+ Corresponding Source. Regardless of what server hosts the
285
+ Corresponding Source, you remain obligated to ensure that it is
286
+ available for as long as needed to satisfy these requirements.
287
+
288
+ e) Convey the object code using peer-to-peer transmission, provided
289
+ you inform other peers where the object code and Corresponding
290
+ Source of the work are being offered to the general public at no
291
+ charge under subsection 6d.
292
+
293
+ A separable portion of the object code, whose source code is excluded
294
+ from the Corresponding Source as a System Library, need not be
295
+ included in conveying the object code work.
296
+
297
+ A "User Product" is either (1) a "consumer product", which means any
298
+ tangible personal property which is normally used for personal, family,
299
+ or household purposes, or (2) anything designed or sold for incorporation
300
+ into a dwelling. In determining whether a product is a consumer product,
301
+ doubtful cases shall be resolved in favor of coverage. For a particular
302
+ product received by a particular user, "normally used" refers to a
303
+ typical or common use of that class of product, regardless of the status
304
+ of the particular user or of the way in which the particular user
305
+ actually uses, or expects or is expected to use, the product. A product
306
+ is a consumer product regardless of whether the product has substantial
307
+ commercial, industrial or non-consumer uses, unless such uses represent
308
+ the only significant mode of use of the product.
309
+
310
+ "Installation Information" for a User Product means any methods,
311
+ procedures, authorization keys, or other information required to install
312
+ and execute modified versions of a covered work in that User Product from
313
+ a modified version of its Corresponding Source. The information must
314
+ suffice to ensure that the continued functioning of the modified object
315
+ code is in no case prevented or interfered with solely because
316
+ modification has been made.
317
+
318
+ If you convey an object code work under this section in, or with, or
319
+ specifically for use in, a User Product, and the conveying occurs as
320
+ part of a transaction in which the right of possession and use of the
321
+ User Product is transferred to the recipient in perpetuity or for a
322
+ fixed term (regardless of how the transaction is characterized), the
323
+ Corresponding Source conveyed under this section must be accompanied
324
+ by the Installation Information. But this requirement does not apply
325
+ if neither you nor any third party retains the ability to install
326
+ modified object code on the User Product (for example, the work has
327
+ been installed in ROM).
328
+
329
+ The requirement to provide Installation Information does not include a
330
+ requirement to continue to provide support service, warranty, or updates
331
+ for a work that has been modified or installed by the recipient, or for
332
+ the User Product in which it has been modified or installed. Access to a
333
+ network may be denied when the modification itself materially and
334
+ adversely affects the operation of the network or violates the rules and
335
+ protocols for communication across the network.
336
+
337
+ Corresponding Source conveyed, and Installation Information provided,
338
+ in accord with this section must be in a format that is publicly
339
+ documented (and with an implementation available to the public in
340
+ source code form), and must require no special password or key for
341
+ unpacking, reading or copying.
342
+
343
+ 7. Additional Terms.
344
+
345
+ "Additional permissions" are terms that supplement the terms of this
346
+ License by making exceptions from one or more of its conditions.
347
+ Additional permissions that are applicable to the entire Program shall
348
+ be treated as though they were included in this License, to the extent
349
+ that they are valid under applicable law. If additional permissions
350
+ apply only to part of the Program, that part may be used separately
351
+ under those permissions, but the entire Program remains governed by
352
+ this License without regard to the additional permissions.
353
+
354
+ When you convey a copy of a covered work, you may at your option
355
+ remove any additional permissions from that copy, or from any part of
356
+ it. (Additional permissions may be written to require their own
357
+ removal in certain cases when you modify the work.) You may place
358
+ additional permissions on material, added by you to a covered work,
359
+ for which you have or can give appropriate copyright permission.
360
+
361
+ Notwithstanding any other provision of this License, for material you
362
+ add to a covered work, you may (if authorized by the copyright holders of
363
+ that material) supplement the terms of this License with terms:
364
+
365
+ a) Disclaiming warranty or limiting liability differently from the
366
+ terms of sections 15 and 16 of this License; or
367
+
368
+ b) Requiring preservation of specified reasonable legal notices or
369
+ author attributions in that material or in the Appropriate Legal
370
+ Notices displayed by works containing it; or
371
+
372
+ c) Prohibiting misrepresentation of the origin of that material, or
373
+ requiring that modified versions of such material be marked in
374
+ reasonable ways as different from the original version; or
375
+
376
+ d) Limiting the use for publicity purposes of names of licensors or
377
+ authors of the material; or
378
+
379
+ e) Declining to grant rights under trademark law for use of some
380
+ trade names, trademarks, or service marks; or
381
+
382
+ f) Requiring indemnification of licensors and authors of that
383
+ material by anyone who conveys the material (or modified versions of
384
+ it) with contractual assumptions of liability to the recipient, for
385
+ any liability that these contractual assumptions directly impose on
386
+ those licensors and authors.
387
+
388
+ All other non-permissive additional terms are considered "further
389
+ restrictions" within the meaning of section 10. If the Program as you
390
+ received it, or any part of it, contains a notice stating that it is
391
+ governed by this License along with a term that is a further
392
+ restriction, you may remove that term. If a license document contains
393
+ a further restriction but permits relicensing or conveying under this
394
+ License, you may add to a covered work material governed by the terms
395
+ of that license document, provided that the further restriction does
396
+ not survive such relicensing or conveying.
397
+
398
+ If you add terms to a covered work in accord with this section, you
399
+ must place, in the relevant source files, a statement of the
400
+ additional terms that apply to those files, or a notice indicating
401
+ where to find the applicable terms.
402
+
403
+ Additional terms, permissive or non-permissive, may be stated in the
404
+ form of a separately written license, or stated as exceptions;
405
+ the above requirements apply either way.
406
+
407
+ 8. Termination.
408
+
409
+ You may not propagate or modify a covered work except as expressly
410
+ provided under this License. Any attempt otherwise to propagate or
411
+ modify it is void, and will automatically terminate your rights under
412
+ this License (including any patent licenses granted under the third
413
+ paragraph of section 11).
414
+
415
+ However, if you cease all violation of this License, then your
416
+ license from a particular copyright holder is reinstated (a)
417
+ provisionally, unless and until the copyright holder explicitly and
418
+ finally terminates your license, and (b) permanently, if the copyright
419
+ holder fails to notify you of the violation by some reasonable means
420
+ prior to 60 days after the cessation.
421
+
422
+ Moreover, your license from a particular copyright holder is
423
+ reinstated permanently if the copyright holder notifies you of the
424
+ violation by some reasonable means, this is the first time you have
425
+ received notice of violation of this License (for any work) from that
426
+ copyright holder, and you cure the violation prior to 30 days after
427
+ your receipt of the notice.
428
+
429
+ Termination of your rights under this section does not terminate the
430
+ licenses of parties who have received copies or rights from you under
431
+ this License. If your rights have been terminated and not permanently
432
+ reinstated, you do not qualify to receive new licenses for the same
433
+ material under section 10.
434
+
435
+ 9. Acceptance Not Required for Having Copies.
436
+
437
+ You are not required to accept this License in order to receive or
438
+ run a copy of the Program. Ancillary propagation of a covered work
439
+ occurring solely as a consequence of using peer-to-peer transmission
440
+ to receive a copy likewise does not require acceptance. However,
441
+ nothing other than this License grants you permission to propagate or
442
+ modify any covered work. These actions infringe copyright if you do
443
+ not accept this License. Therefore, by modifying or propagating a
444
+ covered work, you indicate your acceptance of this License to do so.
445
+
446
+ 10. Automatic Licensing of Downstream Recipients.
447
+
448
+ Each time you convey a covered work, the recipient automatically
449
+ receives a license from the original licensors, to run, modify and
450
+ propagate that work, subject to this License. You are not responsible
451
+ for enforcing compliance by third parties with this License.
452
+
453
+ An "entity transaction" is a transaction transferring control of an
454
+ organization, or substantially all assets of one, or subdividing an
455
+ organization, or merging organizations. If propagation of a covered
456
+ work results from an entity transaction, each party to that
457
+ transaction who receives a copy of the work also receives whatever
458
+ licenses to the work the party's predecessor in interest had or could
459
+ give under the previous paragraph, plus a right to possession of the
460
+ Corresponding Source of the work from the predecessor in interest, if
461
+ the predecessor has it or can get it with reasonable efforts.
462
+
463
+ You may not impose any further restrictions on the exercise of the
464
+ rights granted or affirmed under this License. For example, you may
465
+ not impose a license fee, royalty, or other charge for exercise of
466
+ rights granted under this License, and you may not initiate litigation
467
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
468
+ any patent claim is infringed by making, using, selling, offering for
469
+ sale, or importing the Program or any portion of it.
470
+
471
+ 11. Patents.
472
+
473
+ A "contributor" is a copyright holder who authorizes use under this
474
+ License of the Program or a work on which the Program is based. The
475
+ work thus licensed is called the contributor's "contributor version".
476
+
477
+ A contributor's "essential patent claims" are all patent claims
478
+ owned or controlled by the contributor, whether already acquired or
479
+ hereafter acquired, that would be infringed by some manner, permitted
480
+ by this License, of making, using, or selling its contributor version,
481
+ but do not include claims that would be infringed only as a
482
+ consequence of further modification of the contributor version. For
483
+ purposes of this definition, "control" includes the right to grant
484
+ patent sublicenses in a manner consistent with the requirements of
485
+ this License.
486
+
487
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
488
+ patent license under the contributor's essential patent claims, to
489
+ make, use, sell, offer for sale, import and otherwise run, modify and
490
+ propagate the contents of its contributor version.
491
+
492
+ In the following three paragraphs, a "patent license" is any express
493
+ agreement or commitment, however denominated, not to enforce a patent
494
+ (such as an express permission to practice a patent or covenant not to
495
+ sue for patent infringement). To "grant" such a patent license to a
496
+ party means to make such an agreement or commitment not to enforce a
497
+ patent against the party.
498
+
499
+ If you convey a covered work, knowingly relying on a patent license,
500
+ and the Corresponding Source of the work is not available for anyone
501
+ to copy, free of charge and under the terms of this License, through a
502
+ publicly available network server or other readily accessible means,
503
+ then you must either (1) cause the Corresponding Source to be so
504
+ available, or (2) arrange to deprive yourself of the benefit of the
505
+ patent license for this particular work, or (3) arrange, in a manner
506
+ consistent with the requirements of this License, to extend the patent
507
+ license to downstream recipients. "Knowingly relying" means you have
508
+ actual knowledge that, but for the patent license, your conveying the
509
+ covered work in a country, or your recipient's use of the covered work
510
+ in a country, would infringe one or more identifiable patents in that
511
+ country that you have reason to believe are valid.
512
+
513
+ If, pursuant to or in connection with a single transaction or
514
+ arrangement, you convey, or propagate by procuring conveyance of, a
515
+ covered work, and grant a patent license to some of the parties
516
+ receiving the covered work authorizing them to use, propagate, modify
517
+ or convey a specific copy of the covered work, then the patent license
518
+ you grant is automatically extended to all recipients of the covered
519
+ work and works based on it.
520
+
521
+ A patent license is "discriminatory" if it does not include within
522
+ the scope of its coverage, prohibits the exercise of, or is
523
+ conditioned on the non-exercise of one or more of the rights that are
524
+ specifically granted under this License. You may not convey a covered
525
+ work if you are a party to an arrangement with a third party that is
526
+ in the business of distributing software, under which you make payment
527
+ to the third party based on the extent of your activity of conveying
528
+ the work, and under which the third party grants, to any of the
529
+ parties who would receive the covered work from you, a discriminatory
530
+ patent license (a) in connection with copies of the covered work
531
+ conveyed by you (or copies made from those copies), or (b) primarily
532
+ for and in connection with specific products or compilations that
533
+ contain the covered work, unless you entered into that arrangement,
534
+ or that patent license was granted, prior to 28 March 2007.
535
+
536
+ Nothing in this License shall be construed as excluding or limiting
537
+ any implied license or other defenses to infringement that may
538
+ otherwise be available to you under applicable patent law.
539
+
540
+ 12. No Surrender of Others' Freedom.
541
+
542
+ If conditions are imposed on you (whether by court order, agreement or
543
+ otherwise) that contradict the conditions of this License, they do not
544
+ excuse you from the conditions of this License. If you cannot convey a
545
+ covered work so as to satisfy simultaneously your obligations under this
546
+ License and any other pertinent obligations, then as a consequence you may
547
+ not convey it at all. For example, if you agree to terms that obligate you
548
+ to collect a royalty for further conveying from those to whom you convey
549
+ the Program, the only way you could satisfy both those terms and this
550
+ License would be to refrain entirely from conveying the Program.
551
+
552
+ 13. Use with the GNU Affero General Public License.
553
+
554
+ Notwithstanding any other provision of this License, you have
555
+ permission to link or combine any covered work with a work licensed
556
+ under version 3 of the GNU Affero General Public License into a single
557
+ combined work, and to convey the resulting work. The terms of this
558
+ License will continue to apply to the part which is the covered work,
559
+ but the special requirements of the GNU Affero General Public License,
560
+ section 13, concerning interaction through a network will apply to the
561
+ combination as such.
562
+
563
+ 14. Revised Versions of this License.
564
+
565
+ The Free Software Foundation may publish revised and/or new versions of
566
+ the GNU General Public License from time to time. Such new versions will
567
+ be similar in spirit to the present version, but may differ in detail to
568
+ address new problems or concerns.
569
+
570
+ Each version is given a distinguishing version number. If the
571
+ Program specifies that a certain numbered version of the GNU General
572
+ Public License "or any later version" applies to it, you have the
573
+ option of following the terms and conditions either of that numbered
574
+ version or of any later version published by the Free Software
575
+ Foundation. If the Program does not specify a version number of the
576
+ GNU General Public License, you may choose any version ever published
577
+ by the Free Software Foundation.
578
+
579
+ If the Program specifies that a proxy can decide which future
580
+ versions of the GNU General Public License can be used, that proxy's
581
+ public statement of acceptance of a version permanently authorizes you
582
+ to choose that version for the Program.
583
+
584
+ Later license versions may give you additional or different
585
+ permissions. However, no additional obligations are imposed on any
586
+ author or copyright holder as a result of your choosing to follow a
587
+ later version.
588
+
589
+ 15. Disclaimer of Warranty.
590
+
591
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
+
600
+ 16. Limitation of Liability.
601
+
602
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610
+ SUCH DAMAGES.
611
+
612
+ 17. Interpretation of Sections 15 and 16.
613
+
614
+ If the disclaimer of warranty and limitation of liability provided
615
+ above cannot be given local legal effect according to their terms,
616
+ reviewing courts shall apply local law that most closely approximates
617
+ an absolute waiver of all civil liability in connection with the
618
+ Program, unless a warranty or assumption of liability accompanies a
619
+ copy of the Program in return for a fee.
620
+
621
+ END OF TERMS AND CONDITIONS
622
+
623
+ How to Apply These Terms to Your New Programs
624
+
625
+ If you develop a new program, and you want it to be of the greatest
626
+ possible use to the public, the best way to achieve this is to make it
627
+ free software which everyone can redistribute and change under these terms.
628
+
629
+ To do so, attach the following notices to the program. It is safest
630
+ to attach them to the start of each source file to most effectively
631
+ state the exclusion of warranty; and each file should have at least
632
+ the "copyright" line and a pointer to where the full notice is found.
633
+
634
+ <one line to give the program's name and a brief idea of what it does.>
635
+ Copyright (C) <year> <name of author>
636
+
637
+ This program is free software: you can redistribute it and/or modify
638
+ it under the terms of the GNU General Public License as published by
639
+ the Free Software Foundation, either version 3 of the License, or
640
+ (at your option) any later version.
641
+
642
+ This program is distributed in the hope that it will be useful,
643
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
644
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645
+ GNU General Public License for more details.
646
+
647
+ You should have received a copy of the GNU General Public License
648
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
649
+
650
+ Also add information on how to contact you by electronic and paper mail.
651
+
652
+ If the program does terminal interaction, make it output a short
653
+ notice like this when it starts in an interactive mode:
654
+
655
+ <program> Copyright (C) <year> <name of author>
656
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657
+ This is free software, and you are welcome to redistribute it
658
+ under certain conditions; type `show c' for details.
659
+
660
+ The hypothetical commands `show w' and `show c' should show the appropriate
661
+ parts of the General Public License. Of course, your program's commands
662
+ might be different; for a GUI interface, you would use an "about box".
663
+
664
+ You should also get your employer (if you work as a programmer) or school,
665
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
666
+ For more information on this, and how to apply and follow the GNU GPL, see
667
+ <https://www.gnu.org/licenses/>.
668
+
669
+ The GNU General Public License does not permit incorporating your program
670
+ into proprietary programs. If your program is a subroutine library, you
671
+ may consider it more useful to permit linking proprietary applications with
672
+ the library. If this is what you want to do, use the GNU Lesser General
673
+ Public License instead of this License. But first, please read
674
+ <https://www.gnu.org/licenses/why-not-lgpl.html>.
RobustVideoMatting/README.md ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Robust Video Matting (RVM)
2
+
3
+ ![Teaser](/documentation/image/teaser.gif)
4
+
5
+ <p align="center">English | <a href="README_zh_Hans.md">中文</a></p>
6
+
7
+ Official repository for the paper [Robust High-Resolution Video Matting with Temporal Guidance](https://peterl1n.github.io/RobustVideoMatting/). RVM is specifically designed for robust human video matting. Unlike existing neural models that process frames as independent images, RVM uses a recurrent neural network to process videos with temporal memory. RVM can perform matting in real-time on any videos without additional inputs. It achieves **4K 76FPS** and **HD 104FPS** on an Nvidia GTX 1080 Ti GPU. The project was developed at [ByteDance Inc.](https://www.bytedance.com/)
8
+
9
+ <br>
10
+
11
+ ## News
12
+
13
+ * [Nov 03 2021] Fixed a bug in [train.py](https://github.com/PeterL1n/RobustVideoMatting/commit/48effc91576a9e0e7a8519f3da687c0d3522045f).
14
+ * [Sep 16 2021] Code is re-released under GPL-3.0 license.
15
+ * [Aug 25 2021] Source code and pretrained models are published.
16
+ * [Jul 27 2021] Paper is accepted by WACV 2022.
17
+
18
+ <br>
19
+
20
+ ## Showreel
21
+ Watch the showreel video ([YouTube](https://youtu.be/Jvzltozpbpk), [Bilibili](https://www.bilibili.com/video/BV1Z3411B7g7/)) to see the model's performance.
22
+
23
+ <p align="center">
24
+ <a href="https://youtu.be/Jvzltozpbpk">
25
+ <img src="documentation/image/showreel.gif">
26
+ </a>
27
+ </p>
28
+
29
+ All footage in the video are available in [Google Drive](https://drive.google.com/drive/folders/1VFnWwuu-YXDKG-N6vcjK_nL7YZMFapMU?usp=sharing).
30
+
31
+ <br>
32
+
33
+
34
+ ## Demo
35
+ * [Webcam Demo](https://peterl1n.github.io/RobustVideoMatting/#/demo): Run the model live in your browser. Visualize recurrent states.
36
+ * [Colab Demo](https://colab.research.google.com/drive/10z-pNKRnVNsp0Lq9tH1J_XPZ7CBC_uHm?usp=sharing): Test our model on your own videos with free GPU.
37
+
38
+ <br>
39
+
40
+ ## Download
41
+
42
+ We recommend MobileNetv3 models for most use cases. ResNet50 models are the larger variant with small performance improvements. Our model is available on various inference frameworks. See [inference documentation](documentation/inference.md) for more instructions.
43
+
44
+ <table>
45
+ <thead>
46
+ <tr>
47
+ <td>Framework</td>
48
+ <td>Download</td>
49
+ <td>Notes</td>
50
+ </tr>
51
+ </thead>
52
+ <tbody>
53
+ <tr>
54
+ <td>PyTorch</td>
55
+ <td>
56
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3.pth">rvm_mobilenetv3.pth</a><br>
57
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50.pth">rvm_resnet50.pth</a>
58
+ </td>
59
+ <td>
60
+ Official weights for PyTorch. <a href="documentation/inference.md#pytorch">Doc</a>
61
+ </td>
62
+ </tr>
63
+ <tr>
64
+ <td>TorchHub</td>
65
+ <td>
66
+ Nothing to Download.
67
+ </td>
68
+ <td>
69
+ Easiest way to use our model in your PyTorch project. <a href="documentation/inference.md#torchhub">Doc</a>
70
+ </td>
71
+ </tr>
72
+ <tr>
73
+ <td>TorchScript</td>
74
+ <td>
75
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_fp32.torchscript">rvm_mobilenetv3_fp32.torchscript</a><br>
76
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_fp16.torchscript">rvm_mobilenetv3_fp16.torchscript</a><br>
77
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50_fp32.torchscript">rvm_resnet50_fp32.torchscript</a><br>
78
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50_fp16.torchscript">rvm_resnet50_fp16.torchscript</a>
79
+ </td>
80
+ <td>
81
+ If inference on mobile, consider export int8 quantized models yourself. <a href="documentation/inference.md#torchscript">Doc</a>
82
+ </td>
83
+ </tr>
84
+ <tr>
85
+ <td>ONNX</td>
86
+ <td>
87
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_fp32.onnx">rvm_mobilenetv3_fp32.onnx</a><br>
88
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_fp16.onnx">rvm_mobilenetv3_fp16.onnx</a><br>
89
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50_fp32.onnx">rvm_resnet50_fp32.onnx</a><br>
90
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50_fp16.onnx">rvm_resnet50_fp16.onnx</a>
91
+ </td>
92
+ <td>
93
+ Tested on ONNX Runtime with CPU and CUDA backends. Provided models use opset 12. <a href="documentation/inference.md#onnx">Doc</a>, <a href="https://github.com/PeterL1n/RobustVideoMatting/tree/onnx">Exporter</a>.
94
+ </td>
95
+ </tr>
96
+ <tr>
97
+ <td>TensorFlow</td>
98
+ <td>
99
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_tf.zip">rvm_mobilenetv3_tf.zip</a><br>
100
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50_tf.zip">rvm_resnet50_tf.zip</a>
101
+ </td>
102
+ <td>
103
+ TensorFlow 2 SavedModel. <a href="documentation/inference.md#tensorflow">Doc</a>
104
+ </td>
105
+ </tr>
106
+ <tr>
107
+ <td>TensorFlow.js</td>
108
+ <td>
109
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_tfjs_int8.zip">rvm_mobilenetv3_tfjs_int8.zip</a><br>
110
+ </td>
111
+ <td>
112
+ Run the model on the web. <a href="https://peterl1n.github.io/RobustVideoMatting/#/demo">Demo</a>, <a href="https://github.com/PeterL1n/RobustVideoMatting/tree/tfjs">Starter Code</a>
113
+ </td>
114
+ </tr>
115
+ <tr>
116
+ <td>CoreML</td>
117
+ <td>
118
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_1280x720_s0.375_fp16.mlmodel">rvm_mobilenetv3_1280x720_s0.375_fp16.mlmodel</a><br>
119
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_1280x720_s0.375_int8.mlmodel">rvm_mobilenetv3_1280x720_s0.375_int8.mlmodel</a><br>
120
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_1920x1080_s0.25_fp16.mlmodel">rvm_mobilenetv3_1920x1080_s0.25_fp16.mlmodel</a><br>
121
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_1920x1080_s0.25_int8.mlmodel">rvm_mobilenetv3_1920x1080_s0.25_int8.mlmodel</a><br>
122
+ </td>
123
+ <td>
124
+ CoreML does not support dynamic resolution. Other resolutions can be exported yourself. Models require iOS 13+. <code>s</code> denotes <code>downsample_ratio</code>. <a href="documentation/inference.md#coreml">Doc</a>, <a href="https://github.com/PeterL1n/RobustVideoMatting/tree/coreml">Exporter</a>
125
+ </td>
126
+ </tr>
127
+ </tbody>
128
+ </table>
129
+
130
+ All models are available in [Google Drive](https://drive.google.com/drive/folders/1pBsG-SCTatv-95SnEuxmnvvlRx208VKj?usp=sharing) and [Baidu Pan](https://pan.baidu.com/s/1puPSxQqgBFOVpW4W7AolkA) (code: gym7).
131
+
132
+ <br>
133
+
134
+ ## PyTorch Example
135
+
136
+ 1. Install dependencies:
137
+ ```sh
138
+ pip install -r requirements_inference.txt
139
+ ```
140
+
141
+ 2. Load the model:
142
+
143
+ ```python
144
+ import torch
145
+ from model import MattingNetwork
146
+
147
+ model = MattingNetwork('mobilenetv3').eval().cuda() # or "resnet50"
148
+ model.load_state_dict(torch.load('rvm_mobilenetv3.pth'))
149
+ ```
150
+
151
+ 3. To convert videos, we provide a simple conversion API:
152
+
153
+ ```python
154
+ from inference import convert_video
155
+
156
+ convert_video(
157
+ model, # The model, can be on any device (cpu or cuda).
158
+ input_source='input.mp4', # A video file or an image sequence directory.
159
+ output_type='video', # Choose "video" or "png_sequence"
160
+ output_composition='com.mp4', # File path if video; directory path if png sequence.
161
+ output_alpha="pha.mp4", # [Optional] Output the raw alpha prediction.
162
+ output_foreground="fgr.mp4", # [Optional] Output the raw foreground prediction.
163
+ output_video_mbps=4, # Output video mbps. Not needed for png sequence.
164
+ downsample_ratio=None, # A hyperparameter to adjust or use None for auto.
165
+ seq_chunk=12, # Process n frames at once for better parallelism.
166
+ )
167
+ ```
168
+
169
+ 4. Or write your own inference code:
170
+ ```python
171
+ from torch.utils.data import DataLoader
172
+ from torchvision.transforms import ToTensor
173
+ from inference_utils import VideoReader, VideoWriter
174
+
175
+ reader = VideoReader('input.mp4', transform=ToTensor())
176
+ writer = VideoWriter('output.mp4', frame_rate=30)
177
+
178
+ bgr = torch.tensor([.47, 1, .6]).view(3, 1, 1).cuda() # Green background.
179
+ rec = [None] * 4 # Initial recurrent states.
180
+ downsample_ratio = 0.25 # Adjust based on your video.
181
+
182
+ with torch.no_grad():
183
+ for src in DataLoader(reader): # RGB tensor normalized to 0 ~ 1.
184
+ fgr, pha, *rec = model(src.cuda(), *rec, downsample_ratio) # Cycle the recurrent states.
185
+ com = fgr * pha + bgr * (1 - pha) # Composite to green background.
186
+ writer.write(com) # Write frame.
187
+ ```
188
+
189
+ 5. The models and converter API are also available through TorchHub.
190
+
191
+ ```python
192
+ # Load the model.
193
+ model = torch.hub.load("PeterL1n/RobustVideoMatting", "mobilenetv3") # or "resnet50"
194
+
195
+ # Converter API.
196
+ convert_video = torch.hub.load("PeterL1n/RobustVideoMatting", "converter")
197
+ ```
198
+
199
+ Please see [inference documentation](documentation/inference.md) for details on `downsample_ratio` hyperparameter, more converter arguments, and more advanced usage.
200
+
201
+ <br>
202
+
203
+ ## Training and Evaluation
204
+
205
+ Please refer to the [training documentation](documentation/training.md) to train and evaluate your own model.
206
+
207
+ <br>
208
+
209
+ ## Speed
210
+
211
+ Speed is measured with `inference_speed_test.py` for reference.
212
+
213
+ | GPU | dType | HD (1920x1080) | 4K (3840x2160) |
214
+ | -------------- | ----- | -------------- |----------------|
215
+ | RTX 3090 | FP16 | 172 FPS | 154 FPS |
216
+ | RTX 2060 Super | FP16 | 134 FPS | 108 FPS |
217
+ | GTX 1080 Ti | FP32 | 104 FPS | 74 FPS |
218
+
219
+ * Note 1: HD uses `downsample_ratio=0.25`, 4K uses `downsample_ratio=0.125`. All tests use batch size 1 and frame chunk 1.
220
+ * Note 2: GPUs before Turing architecture does not support FP16 inference, so GTX 1080 Ti uses FP32.
221
+ * Note 3: We only measure tensor throughput. The provided video conversion script in this repo is expected to be much slower, because it does not utilize hardware video encoding/decoding and does not have the tensor transfer done on parallel threads. If you are interested in implementing hardware video encoding/decoding in Python, please refer to [PyNvCodec](https://github.com/NVIDIA/VideoProcessingFramework).
222
+
223
+ <br>
224
+
225
+ ## Project Members
226
+ * [Shanchuan Lin](https://www.linkedin.com/in/shanchuanlin/)
227
+ * [Linjie Yang](https://sites.google.com/site/linjieyang89/)
228
+ * [Imran Saleemi](https://www.linkedin.com/in/imran-saleemi/)
229
+ * [Soumyadip Sengupta](https://homes.cs.washington.edu/~soumya91/)
230
+
231
+ <br>
232
+
233
+ ## Third-Party Projects
234
+
235
+ * [NCNN C++ Android](https://github.com/FeiGeChuanShu/ncnn_Android_RobustVideoMatting) ([@FeiGeChuanShu](https://github.com/FeiGeChuanShu))
236
+ * [lite.ai.toolkit](https://github.com/DefTruth/RobustVideoMatting.lite.ai.toolkit) ([@DefTruth](https://github.com/DefTruth))
237
+ * [Gradio Web Demo](https://huggingface.co/spaces/akhaliq/Robust-Video-Matting) ([@AK391](https://github.com/AK391))
238
+ * [Unity Engine demo with NatML](https://hub.natml.ai/@natsuite/robust-video-matting) ([@natsuite](https://github.com/natsuite))
239
+ * [MNN C++ Demo](https://github.com/DefTruth/lite.ai.toolkit/blob/main/lite/mnn/cv/mnn_rvm.cpp) ([@DefTruth](https://github.com/DefTruth))
240
+ * [TNN C++ Demo](https://github.com/DefTruth/lite.ai.toolkit/blob/main/lite/tnn/cv/tnn_rvm.cpp) ([@DefTruth](https://github.com/DefTruth))
241
+
RobustVideoMatting/README_zh_Hans.md ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 稳定视频抠像 (RVM)
2
+
3
+ ![Teaser](/documentation/image/teaser.gif)
4
+
5
+ <p align="center"><a href="README.md">English</a> | 中文</p>
6
+
7
+ 论文 [Robust High-Resolution Video Matting with Temporal Guidance](https://peterl1n.github.io/RobustVideoMatting/) 的官方 GitHub 库。RVM 专为稳定人物视频抠像设计。不同于现有神经网络将每一帧作为单独图片处理,RVM 使用循环神经网络,在处理视频流时有时间记忆。RVM 可在任意视频上做实时高清抠像。在 Nvidia GTX 1080Ti 上实现 **4K 76FPS** 和 **HD 104FPS**。此研究项目来自[字节跳动](https://www.bytedance.com/)。
8
+
9
+ <br>
10
+
11
+ ## 更新
12
+
13
+ * [2021年11月3日] 修复了 [train.py](https://github.com/PeterL1n/RobustVideoMatting/commit/48effc91576a9e0e7a8519f3da687c0d3522045f) 的 bug。
14
+ * [2021年9月16日] 代码重新以 GPL-3.0 许可发布。
15
+ * [2021年8月25日] 公开代码和模型。
16
+ * [2021年7月27日] 论文被 WACV 2022 收录。
17
+
18
+ <br>
19
+
20
+ ## 展示视频
21
+ 观看展示视频 ([YouTube](https://youtu.be/Jvzltozpbpk), [Bilibili](https://www.bilibili.com/video/BV1Z3411B7g7/)),了解模型能力。
22
+ <p align="center">
23
+ <a href="https://youtu.be/Jvzltozpbpk">
24
+ <img src="documentation/image/showreel.gif">
25
+ </a>
26
+ </p>
27
+
28
+ 视频中的所有素材都提供下载,可用于测试模型:[Google Drive](https://drive.google.com/drive/folders/1VFnWwuu-YXDKG-N6vcjK_nL7YZMFapMU?usp=sharing)
29
+
30
+ <br>
31
+
32
+
33
+ ## Demo
34
+ * [网页](https://peterl1n.github.io/RobustVideoMatting/#/demo): 在浏览器里看摄像头抠像效果,展示模型内部循环记忆值。
35
+ * [Colab](https://colab.research.google.com/drive/10z-pNKRnVNsp0Lq9tH1J_XPZ7CBC_uHm?usp=sharing): 用我们的模型转换你的视频。
36
+
37
+ <br>
38
+
39
+ ## 下载
40
+
41
+ 推荐在通常情况下使用 MobileNetV3 的模型。ResNet50 的模型大很多,效果稍有提高。我们的模型支持很多框架。详情请阅读[推断文档](documentation/inference_zh_Hans.md)。
42
+
43
+ <table>
44
+ <thead>
45
+ <tr>
46
+ <td>框架</td>
47
+ <td>下载</td>
48
+ <td>备注</td>
49
+ </tr>
50
+ </thead>
51
+ <tbody>
52
+ <tr>
53
+ <td>PyTorch</td>
54
+ <td>
55
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3.pth">rvm_mobilenetv3.pth</a><br>
56
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50.pth">rvm_resnet50.pth</a>
57
+ </td>
58
+ <td>
59
+ 官方 PyTorch 模型权值。<a href="documentation/inference_zh_Hans.md#pytorch">文档</a>
60
+ </td>
61
+ </tr>
62
+ <tr>
63
+ <td>TorchHub</td>
64
+ <td>
65
+ 无需手动下载。
66
+ </td>
67
+ <td>
68
+ 更方便地在你的 PyTorch 项目里使用此模型。<a href="documentation/inference_zh_Hans.md#torchhub">文档</a>
69
+ </td>
70
+ </tr>
71
+ <tr>
72
+ <td>TorchScript</td>
73
+ <td>
74
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_fp32.torchscript">rvm_mobilenetv3_fp32.torchscript</a><br>
75
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_fp16.torchscript">rvm_mobilenetv3_fp16.torchscript</a><br>
76
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50_fp32.torchscript">rvm_resnet50_fp32.torchscript</a><br>
77
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50_fp16.torchscript">rvm_resnet50_fp16.torchscript</a>
78
+ </td>
79
+ <td>
80
+ 若需在移动端推断,可以考虑自行导出 int8 量化的模型。<a href="documentation/inference_zh_Hans.md#torchscript">文档</a>
81
+ </td>
82
+ </tr>
83
+ <tr>
84
+ <td>ONNX</td>
85
+ <td>
86
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_fp32.onnx">rvm_mobilenetv3_fp32.onnx</a><br>
87
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_fp16.onnx">rvm_mobilenetv3_fp16.onnx</a><br>
88
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50_fp32.onnx">rvm_resnet50_fp32.onnx</a><br>
89
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50_fp16.onnx">rvm_resnet50_fp16.onnx</a>
90
+ </td>
91
+ <td>
92
+ 在 ONNX Runtime 的 CPU 和 CUDA backend 上测试过。提供的模型用 opset 12。<a href="documentation/inference_zh_Hans.md#onnx">文档</a>,<a href="https://github.com/PeterL1n/RobustVideoMatting/tree/onnx">导出</a>
93
+ </td>
94
+ </tr>
95
+ <tr>
96
+ <td>TensorFlow</td>
97
+ <td>
98
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_tf.zip">rvm_mobilenetv3_tf.zip</a><br>
99
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50_tf.zip">rvm_resnet50_tf.zip</a>
100
+ </td>
101
+ <td>
102
+ TensorFlow 2 SavedModel 格式。<a href="documentation/inference_zh_Hans.md#tensorflow">文档</a>
103
+ </td>
104
+ </tr>
105
+ <tr>
106
+ <td>TensorFlow.js</td>
107
+ <td>
108
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_tfjs_int8.zip">rvm_mobilenetv3_tfjs_int8.zip</a><br>
109
+ </td>
110
+ <td>
111
+ 在网页上跑模型。<a href="https://peterl1n.github.io/RobustVideoMatting/#/demo">展示</a>,<a href="https://github.com/PeterL1n/RobustVideoMatting/tree/tfjs">示范代码</a>
112
+ </td>
113
+ </tr>
114
+ <tr>
115
+ <td>CoreML</td>
116
+ <td>
117
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_1280x720_s0.375_fp16.mlmodel">rvm_mobilenetv3_1280x720_s0.375_fp16.mlmodel</a><br>
118
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_1280x720_s0.375_int8.mlmodel">rvm_mobilenetv3_1280x720_s0.375_int8.mlmodel</a><br>
119
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_1920x1080_s0.25_fp16.mlmodel">rvm_mobilenetv3_1920x1080_s0.25_fp16.mlmodel</a><br>
120
+ <a href="https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3_1920x1080_s0.25_int8.mlmodel">rvm_mobilenetv3_1920x1080_s0.25_int8.mlmodel</a><br>
121
+ </td>
122
+ <td>
123
+ CoreML 只能导出固定分辨率,其他分辨率可自行导出。支持 iOS 13+。<code>s</code> 代表下采样比。<a href="documentation/inference_zh_Hans.md#coreml">文档</a>,<a href="https://github.com/PeterL1n/RobustVideoMatting/tree/coreml">导出</a>
124
+ </td>
125
+ </tr>
126
+ </tbody>
127
+ </table>
128
+
129
+ 所有模型可在 [Google Drive](https://drive.google.com/drive/folders/1pBsG-SCTatv-95SnEuxmnvvlRx208VKj?usp=sharing) 或[百度网盘](https://pan.baidu.com/s/1puPSxQqgBFOVpW4W7AolkA)(密码: gym7)上下载。
130
+
131
+ <br>
132
+
133
+ ## PyTorch 范例
134
+
135
+ 1. 安装 Python 库:
136
+ ```sh
137
+ pip install -r requirements_inference.txt
138
+ ```
139
+
140
+ 2. 加载模型:
141
+
142
+ ```python
143
+ import torch
144
+ from model import MattingNetwork
145
+
146
+ model = MattingNetwork('mobilenetv3').eval().cuda() # 或 "resnet50"
147
+ model.load_state_dict(torch.load('rvm_mobilenetv3.pth'))
148
+ ```
149
+
150
+ 3. 若只需要做视频抠像处理,我们提供简单的 API:
151
+
152
+ ```python
153
+ from inference import convert_video
154
+
155
+ convert_video(
156
+ model, # 模型,可以加载到任何设备(cpu 或 cuda)
157
+ input_source='input.mp4', # 视频文件,或图片序列文件夹
158
+ output_type='video', # 可选 "video"(视频)或 "png_sequence"(PNG 序列)
159
+ output_composition='com.mp4', # 若导出视频,提供文件路径。若导出 PNG 序列,提供文件夹路径
160
+ output_alpha="pha.mp4", # [可选项] 输出透明度预测
161
+ output_foreground="fgr.mp4", # [可选项] 输出前景预测
162
+ output_video_mbps=4, # 若导出视频,提供视频码率
163
+ downsample_ratio=None, # 下采样比,可根据具体视频调节,或 None 选择自动
164
+ seq_chunk=12, # 设置多帧并行计算
165
+ )
166
+ ```
167
+
168
+ 4. 或自己写推断逻辑:
169
+ ```python
170
+ from torch.utils.data import DataLoader
171
+ from torchvision.transforms import ToTensor
172
+ from inference_utils import VideoReader, VideoWriter
173
+
174
+ reader = VideoReader('input.mp4', transform=ToTensor())
175
+ writer = VideoWriter('output.mp4', frame_rate=30)
176
+
177
+ bgr = torch.tensor([.47, 1, .6]).view(3, 1, 1).cuda() # 绿背景
178
+ rec = [None] * 4 # 初始循环记忆(Recurrent States)
179
+ downsample_ratio = 0.25 # 下采样比,根据视频调节
180
+
181
+ with torch.no_grad():
182
+ for src in DataLoader(reader): # 输入张量,RGB通道,范围为 0~1
183
+ fgr, pha, *rec = model(src.cuda(), *rec, downsample_ratio) # 将上一帧的记忆给下一帧
184
+ com = fgr * pha + bgr * (1 - pha) # 将前景合成到绿色背景
185
+ writer.write(com) # 输出帧
186
+ ```
187
+
188
+ 5. 模型和 API 也可通过 TorchHub 快速载入。
189
+
190
+ ```python
191
+ # 加载模型
192
+ model = torch.hub.load("PeterL1n/RobustVideoMatting", "mobilenetv3") # 或 "resnet50"
193
+
194
+ # 转换 API
195
+ convert_video = torch.hub.load("PeterL1n/RobustVideoMatting", "converter")
196
+ ```
197
+
198
+ [推断文档](documentation/inference_zh_Hans.md)里有对 `downsample_ratio` 参数,API 使用,和高阶使用的讲解。
199
+
200
+ <br>
201
+
202
+ ## 训练和评估
203
+
204
+ 请参照[训练文档(英文)](documentation/training.md)。
205
+
206
+ <br>
207
+
208
+ ## 速度
209
+
210
+ 速度用 `inference_speed_test.py` 测量以供参考。
211
+
212
+ | GPU | dType | HD (1920x1080) | 4K (3840x2160) |
213
+ | -------------- | ----- | -------------- |----------------|
214
+ | RTX 3090 | FP16 | 172 FPS | 154 FPS |
215
+ | RTX 2060 Super | FP16 | 134 FPS | 108 FPS |
216
+ | GTX 1080 Ti | FP32 | 104 FPS | 74 FPS |
217
+
218
+ * 注释1:HD 使用 `downsample_ratio=0.25`,4K 使用 `downsample_ratio=0.125`。 所有测试都使用 batch size 1 和 frame chunk 1。
219
+ * 注释2:图灵架构之前的 GPU 不支持 FP16 推理,所以 GTX 1080 Ti 使用 FP32。
220
+ * 注释3:我们只测量张量吞吐量(tensor throughput)。 提供的视频转换脚本会慢得多,因为它不使用硬件视频编码/解码,也没有在并行线程上完成张量传输。如果您有兴趣在 Python 中实现硬件视频编码/解码,请参考 [PyNvCodec](https://github.com/NVIDIA/VideoProcessingFramework)。
221
+
222
+ <br>
223
+
224
+ ## 项目成员
225
+ * [Shanchuan Lin](https://www.linkedin.com/in/shanchuanlin/)
226
+ * [Linjie Yang](https://sites.google.com/site/linjieyang89/)
227
+ * [Imran Saleemi](https://www.linkedin.com/in/imran-saleemi/)
228
+ * [Soumyadip Sengupta](https://homes.cs.washington.edu/~soumya91/)
229
+
230
+ <br>
231
+
232
+ ## 第三方资源
233
+
234
+ * [NCNN C++ Android](https://github.com/FeiGeChuanShu/ncnn_Android_RobustVideoMatting) ([@FeiGeChuanShu](https://github.com/FeiGeChuanShu))
235
+ * [lite.ai.toolkit](https://github.com/DefTruth/RobustVideoMatting.lite.ai.toolkit) ([@DefTruth](https://github.com/DefTruth))
236
+ * [Gradio Web Demo](https://huggingface.co/spaces/akhaliq/Robust-Video-Matting) ([@AK391](https://github.com/AK391))
237
+ * [带有 NatML 的 Unity 引擎](https://hub.natml.ai/@natsuite/robust-video-matting) ([@natsuite](https://github.com/natsuite))
238
+ * [MNN C++ Demo](https://github.com/DefTruth/lite.ai.toolkit/blob/main/lite/mnn/cv/mnn_rvm.cpp) ([@DefTruth](https://github.com/DefTruth))
239
+ * [TNN C++ Demo](https://github.com/DefTruth/lite.ai.toolkit/blob/main/lite/tnn/cv/tnn_rvm.cpp) ([@DefTruth](https://github.com/DefTruth))
240
+
RobustVideoMatting/__pycache__/inference.cpython-38.pyc ADDED
Binary file (6.44 kB). View file
 
RobustVideoMatting/__pycache__/inference_utils.cpython-38.pyc ADDED
Binary file (3.85 kB). View file
 
RobustVideoMatting/checkpoint/rvm_mobilenetv3.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3c7c1d92033f7c38d6577c481d13a195d7d80a159b960f4f3119ac7b534cf4f8
3
+ size 15217721
RobustVideoMatting/dataset/augmentation.py ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import easing_functions as ef
2
+ import random
3
+ import torch
4
+ from torchvision import transforms
5
+ from torchvision.transforms import functional as F
6
+
7
+
8
+ class MotionAugmentation:
9
+ def __init__(self,
10
+ size,
11
+ prob_fgr_affine,
12
+ prob_bgr_affine,
13
+ prob_noise,
14
+ prob_color_jitter,
15
+ prob_grayscale,
16
+ prob_sharpness,
17
+ prob_blur,
18
+ prob_hflip,
19
+ prob_pause,
20
+ static_affine=True,
21
+ aspect_ratio_range=(0.9, 1.1)):
22
+ self.size = size
23
+ self.prob_fgr_affine = prob_fgr_affine
24
+ self.prob_bgr_affine = prob_bgr_affine
25
+ self.prob_noise = prob_noise
26
+ self.prob_color_jitter = prob_color_jitter
27
+ self.prob_grayscale = prob_grayscale
28
+ self.prob_sharpness = prob_sharpness
29
+ self.prob_blur = prob_blur
30
+ self.prob_hflip = prob_hflip
31
+ self.prob_pause = prob_pause
32
+ self.static_affine = static_affine
33
+ self.aspect_ratio_range = aspect_ratio_range
34
+
35
+ def __call__(self, fgrs, phas, bgrs):
36
+ # Foreground affine
37
+ if random.random() < self.prob_fgr_affine:
38
+ fgrs, phas = self._motion_affine(fgrs, phas)
39
+
40
+ # Background affine
41
+ if random.random() < self.prob_bgr_affine / 2:
42
+ bgrs = self._motion_affine(bgrs)
43
+ if random.random() < self.prob_bgr_affine / 2:
44
+ fgrs, phas, bgrs = self._motion_affine(fgrs, phas, bgrs)
45
+
46
+ # Still Affine
47
+ if self.static_affine:
48
+ fgrs, phas = self._static_affine(fgrs, phas, scale_ranges=(0.5, 1))
49
+ bgrs = self._static_affine(bgrs, scale_ranges=(1, 1.5))
50
+
51
+ # To tensor
52
+ fgrs = torch.stack([F.to_tensor(fgr) for fgr in fgrs])
53
+ phas = torch.stack([F.to_tensor(pha) for pha in phas])
54
+ bgrs = torch.stack([F.to_tensor(bgr) for bgr in bgrs])
55
+
56
+ # Resize
57
+ params = transforms.RandomResizedCrop.get_params(fgrs, scale=(1, 1), ratio=self.aspect_ratio_range)
58
+ fgrs = F.resized_crop(fgrs, *params, self.size, interpolation=F.InterpolationMode.BILINEAR)
59
+ phas = F.resized_crop(phas, *params, self.size, interpolation=F.InterpolationMode.BILINEAR)
60
+ params = transforms.RandomResizedCrop.get_params(bgrs, scale=(1, 1), ratio=self.aspect_ratio_range)
61
+ bgrs = F.resized_crop(bgrs, *params, self.size, interpolation=F.InterpolationMode.BILINEAR)
62
+
63
+ # Horizontal flip
64
+ if random.random() < self.prob_hflip:
65
+ fgrs = F.hflip(fgrs)
66
+ phas = F.hflip(phas)
67
+ if random.random() < self.prob_hflip:
68
+ bgrs = F.hflip(bgrs)
69
+
70
+ # Noise
71
+ if random.random() < self.prob_noise:
72
+ fgrs, bgrs = self._motion_noise(fgrs, bgrs)
73
+
74
+ # Color jitter
75
+ if random.random() < self.prob_color_jitter:
76
+ fgrs = self._motion_color_jitter(fgrs)
77
+ if random.random() < self.prob_color_jitter:
78
+ bgrs = self._motion_color_jitter(bgrs)
79
+
80
+ # Grayscale
81
+ if random.random() < self.prob_grayscale:
82
+ fgrs = F.rgb_to_grayscale(fgrs, num_output_channels=3).contiguous()
83
+ bgrs = F.rgb_to_grayscale(bgrs, num_output_channels=3).contiguous()
84
+
85
+ # Sharpen
86
+ if random.random() < self.prob_sharpness:
87
+ sharpness = random.random() * 8
88
+ fgrs = F.adjust_sharpness(fgrs, sharpness)
89
+ phas = F.adjust_sharpness(phas, sharpness)
90
+ bgrs = F.adjust_sharpness(bgrs, sharpness)
91
+
92
+ # Blur
93
+ if random.random() < self.prob_blur / 3:
94
+ fgrs, phas = self._motion_blur(fgrs, phas)
95
+ if random.random() < self.prob_blur / 3:
96
+ bgrs = self._motion_blur(bgrs)
97
+ if random.random() < self.prob_blur / 3:
98
+ fgrs, phas, bgrs = self._motion_blur(fgrs, phas, bgrs)
99
+
100
+ # Pause
101
+ if random.random() < self.prob_pause:
102
+ fgrs, phas, bgrs = self._motion_pause(fgrs, phas, bgrs)
103
+
104
+ return fgrs, phas, bgrs
105
+
106
+ def _static_affine(self, *imgs, scale_ranges):
107
+ params = transforms.RandomAffine.get_params(
108
+ degrees=(-10, 10), translate=(0.1, 0.1), scale_ranges=scale_ranges,
109
+ shears=(-5, 5), img_size=imgs[0][0].size)
110
+ imgs = [[F.affine(t, *params, F.InterpolationMode.BILINEAR) for t in img] for img in imgs]
111
+ return imgs if len(imgs) > 1 else imgs[0]
112
+
113
+ def _motion_affine(self, *imgs):
114
+ config = dict(degrees=(-10, 10), translate=(0.1, 0.1),
115
+ scale_ranges=(0.9, 1.1), shears=(-5, 5), img_size=imgs[0][0].size)
116
+ angleA, (transXA, transYA), scaleA, (shearXA, shearYA) = transforms.RandomAffine.get_params(**config)
117
+ angleB, (transXB, transYB), scaleB, (shearXB, shearYB) = transforms.RandomAffine.get_params(**config)
118
+
119
+ T = len(imgs[0])
120
+ easing = random_easing_fn()
121
+ for t in range(T):
122
+ percentage = easing(t / (T - 1))
123
+ angle = lerp(angleA, angleB, percentage)
124
+ transX = lerp(transXA, transXB, percentage)
125
+ transY = lerp(transYA, transYB, percentage)
126
+ scale = lerp(scaleA, scaleB, percentage)
127
+ shearX = lerp(shearXA, shearXB, percentage)
128
+ shearY = lerp(shearYA, shearYB, percentage)
129
+ for img in imgs:
130
+ img[t] = F.affine(img[t], angle, (transX, transY), scale, (shearX, shearY), F.InterpolationMode.BILINEAR)
131
+ return imgs if len(imgs) > 1 else imgs[0]
132
+
133
+ def _motion_noise(self, *imgs):
134
+ grain_size = random.random() * 3 + 1 # range 1 ~ 4
135
+ monochrome = random.random() < 0.5
136
+ for img in imgs:
137
+ T, C, H, W = img.shape
138
+ noise = torch.randn((T, 1 if monochrome else C, round(H / grain_size), round(W / grain_size)))
139
+ noise.mul_(random.random() * 0.2 / grain_size)
140
+ if grain_size != 1:
141
+ noise = F.resize(noise, (H, W))
142
+ img.add_(noise).clamp_(0, 1)
143
+ return imgs if len(imgs) > 1 else imgs[0]
144
+
145
+ def _motion_color_jitter(self, *imgs):
146
+ brightnessA, brightnessB, contrastA, contrastB, saturationA, saturationB, hueA, hueB \
147
+ = torch.randn(8).mul(0.1).tolist()
148
+ strength = random.random() * 0.2
149
+ easing = random_easing_fn()
150
+ T = len(imgs[0])
151
+ for t in range(T):
152
+ percentage = easing(t / (T - 1)) * strength
153
+ for img in imgs:
154
+ img[t] = F.adjust_brightness(img[t], max(1 + lerp(brightnessA, brightnessB, percentage), 0.1))
155
+ img[t] = F.adjust_contrast(img[t], max(1 + lerp(contrastA, contrastB, percentage), 0.1))
156
+ img[t] = F.adjust_saturation(img[t], max(1 + lerp(brightnessA, brightnessB, percentage), 0.1))
157
+ img[t] = F.adjust_hue(img[t], min(0.5, max(-0.5, lerp(hueA, hueB, percentage) * 0.1)))
158
+ return imgs if len(imgs) > 1 else imgs[0]
159
+
160
+ def _motion_blur(self, *imgs):
161
+ blurA = random.random() * 10
162
+ blurB = random.random() * 10
163
+
164
+ T = len(imgs[0])
165
+ easing = random_easing_fn()
166
+ for t in range(T):
167
+ percentage = easing(t / (T - 1))
168
+ blur = max(lerp(blurA, blurB, percentage), 0)
169
+ if blur != 0:
170
+ kernel_size = int(blur * 2)
171
+ if kernel_size % 2 == 0:
172
+ kernel_size += 1 # Make kernel_size odd
173
+ for img in imgs:
174
+ img[t] = F.gaussian_blur(img[t], kernel_size, sigma=blur)
175
+
176
+ return imgs if len(imgs) > 1 else imgs[0]
177
+
178
+ def _motion_pause(self, *imgs):
179
+ T = len(imgs[0])
180
+ pause_frame = random.choice(range(T - 1))
181
+ pause_length = random.choice(range(T - pause_frame))
182
+ for img in imgs:
183
+ img[pause_frame + 1 : pause_frame + pause_length] = img[pause_frame]
184
+ return imgs if len(imgs) > 1 else imgs[0]
185
+
186
+
187
+ def lerp(a, b, percentage):
188
+ return a * (1 - percentage) + b * percentage
189
+
190
+
191
+ def random_easing_fn():
192
+ if random.random() < 0.2:
193
+ return ef.LinearInOut()
194
+ else:
195
+ return random.choice([
196
+ ef.BackEaseIn,
197
+ ef.BackEaseOut,
198
+ ef.BackEaseInOut,
199
+ ef.BounceEaseIn,
200
+ ef.BounceEaseOut,
201
+ ef.BounceEaseInOut,
202
+ ef.CircularEaseIn,
203
+ ef.CircularEaseOut,
204
+ ef.CircularEaseInOut,
205
+ ef.CubicEaseIn,
206
+ ef.CubicEaseOut,
207
+ ef.CubicEaseInOut,
208
+ ef.ExponentialEaseIn,
209
+ ef.ExponentialEaseOut,
210
+ ef.ExponentialEaseInOut,
211
+ ef.ElasticEaseIn,
212
+ ef.ElasticEaseOut,
213
+ ef.ElasticEaseInOut,
214
+ ef.QuadEaseIn,
215
+ ef.QuadEaseOut,
216
+ ef.QuadEaseInOut,
217
+ ef.QuarticEaseIn,
218
+ ef.QuarticEaseOut,
219
+ ef.QuarticEaseInOut,
220
+ ef.QuinticEaseIn,
221
+ ef.QuinticEaseOut,
222
+ ef.QuinticEaseInOut,
223
+ ef.SineEaseIn,
224
+ ef.SineEaseOut,
225
+ ef.SineEaseInOut,
226
+ Step,
227
+ ])()
228
+
229
+ class Step: # Custom easing function for sudden change.
230
+ def __call__(self, value):
231
+ return 0 if value < 0.5 else 1
232
+
233
+
234
+ # ---------------------------- Frame Sampler ----------------------------
235
+
236
+
237
+ class TrainFrameSampler:
238
+ def __init__(self, speed=[0.5, 1, 2, 3, 4, 5]):
239
+ self.speed = speed
240
+
241
+ def __call__(self, seq_length):
242
+ frames = list(range(seq_length))
243
+
244
+ # Speed up
245
+ speed = random.choice(self.speed)
246
+ frames = [int(f * speed) for f in frames]
247
+
248
+ # Shift
249
+ shift = random.choice(range(seq_length))
250
+ frames = [f + shift for f in frames]
251
+
252
+ # Reverse
253
+ if random.random() < 0.5:
254
+ frames = frames[::-1]
255
+
256
+ return frames
257
+
258
+ class ValidFrameSampler:
259
+ def __call__(self, seq_length):
260
+ return range(seq_length)
RobustVideoMatting/dataset/coco.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import numpy as np
3
+ import random
4
+ import json
5
+ import os
6
+ from torch.utils.data import Dataset
7
+ from torchvision import transforms
8
+ from torchvision.transforms import functional as F
9
+ from PIL import Image
10
+
11
+
12
+ class CocoPanopticDataset(Dataset):
13
+ def __init__(self,
14
+ imgdir: str,
15
+ anndir: str,
16
+ annfile: str,
17
+ transform=None):
18
+ with open(annfile) as f:
19
+ self.data = json.load(f)['annotations']
20
+ self.data = list(filter(lambda data: any(info['category_id'] == 1 for info in data['segments_info']), self.data))
21
+ self.imgdir = imgdir
22
+ self.anndir = anndir
23
+ self.transform = transform
24
+
25
+ def __len__(self):
26
+ return len(self.data)
27
+
28
+ def __getitem__(self, idx):
29
+ data = self.data[idx]
30
+ img = self._load_img(data)
31
+ seg = self._load_seg(data)
32
+
33
+ if self.transform is not None:
34
+ img, seg = self.transform(img, seg)
35
+
36
+ return img, seg
37
+
38
+ def _load_img(self, data):
39
+ with Image.open(os.path.join(self.imgdir, data['file_name'].replace('.png', '.jpg'))) as img:
40
+ return img.convert('RGB')
41
+
42
+ def _load_seg(self, data):
43
+ with Image.open(os.path.join(self.anndir, data['file_name'])) as ann:
44
+ ann.load()
45
+
46
+ ann = np.array(ann, copy=False).astype(np.int32)
47
+ ann = ann[:, :, 0] + 256 * ann[:, :, 1] + 256 * 256 * ann[:, :, 2]
48
+ seg = np.zeros(ann.shape, np.uint8)
49
+
50
+ for segments_info in data['segments_info']:
51
+ if segments_info['category_id'] in [1, 27, 32]: # person, backpack, tie
52
+ seg[ann == segments_info['id']] = 255
53
+
54
+ return Image.fromarray(seg)
55
+
56
+
57
+ class CocoPanopticTrainAugmentation:
58
+ def __init__(self, size):
59
+ self.size = size
60
+ self.jitter = transforms.ColorJitter(0.1, 0.1, 0.1, 0.1)
61
+
62
+ def __call__(self, img, seg):
63
+ # Affine
64
+ params = transforms.RandomAffine.get_params(degrees=(-20, 20), translate=(0.1, 0.1),
65
+ scale_ranges=(1, 1), shears=(-10, 10), img_size=img.size)
66
+ img = F.affine(img, *params, interpolation=F.InterpolationMode.BILINEAR)
67
+ seg = F.affine(seg, *params, interpolation=F.InterpolationMode.NEAREST)
68
+
69
+ # Resize
70
+ params = transforms.RandomResizedCrop.get_params(img, scale=(0.5, 1), ratio=(0.7, 1.3))
71
+ img = F.resized_crop(img, *params, self.size, interpolation=F.InterpolationMode.BILINEAR)
72
+ seg = F.resized_crop(seg, *params, self.size, interpolation=F.InterpolationMode.NEAREST)
73
+
74
+ # Horizontal flip
75
+ if random.random() < 0.5:
76
+ img = F.hflip(img)
77
+ seg = F.hflip(seg)
78
+
79
+ # Color jitter
80
+ img = self.jitter(img)
81
+
82
+ # To tensor
83
+ img = F.to_tensor(img)
84
+ seg = F.to_tensor(seg)
85
+
86
+ return img, seg
87
+
88
+
89
+ class CocoPanopticValidAugmentation:
90
+ def __init__(self, size):
91
+ self.size = size
92
+
93
+ def __call__(self, img, seg):
94
+ # Resize
95
+ params = transforms.RandomResizedCrop.get_params(img, scale=(1, 1), ratio=(1., 1.))
96
+ img = F.resized_crop(img, *params, self.size, interpolation=F.InterpolationMode.BILINEAR)
97
+ seg = F.resized_crop(seg, *params, self.size, interpolation=F.InterpolationMode.NEAREST)
98
+
99
+ # To tensor
100
+ img = F.to_tensor(img)
101
+ seg = F.to_tensor(seg)
102
+
103
+ return img, seg
RobustVideoMatting/dataset/imagematte.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import random
3
+ from torch.utils.data import Dataset
4
+ from PIL import Image
5
+
6
+ from .augmentation import MotionAugmentation
7
+
8
+
9
+ class ImageMatteDataset(Dataset):
10
+ def __init__(self,
11
+ imagematte_dir,
12
+ background_image_dir,
13
+ background_video_dir,
14
+ size,
15
+ seq_length,
16
+ seq_sampler,
17
+ transform):
18
+ self.imagematte_dir = imagematte_dir
19
+ self.imagematte_files = os.listdir(os.path.join(imagematte_dir, 'fgr'))
20
+ self.background_image_dir = background_image_dir
21
+ self.background_image_files = os.listdir(background_image_dir)
22
+ self.background_video_dir = background_video_dir
23
+ self.background_video_clips = os.listdir(background_video_dir)
24
+ self.background_video_frames = [sorted(os.listdir(os.path.join(background_video_dir, clip)))
25
+ for clip in self.background_video_clips]
26
+ self.seq_length = seq_length
27
+ self.seq_sampler = seq_sampler
28
+ self.size = size
29
+ self.transform = transform
30
+
31
+ def __len__(self):
32
+ return max(len(self.imagematte_files), len(self.background_image_files) + len(self.background_video_clips))
33
+
34
+ def __getitem__(self, idx):
35
+ if random.random() < 0.5:
36
+ bgrs = self._get_random_image_background()
37
+ else:
38
+ bgrs = self._get_random_video_background()
39
+
40
+ fgrs, phas = self._get_imagematte(idx)
41
+
42
+ if self.transform is not None:
43
+ return self.transform(fgrs, phas, bgrs)
44
+
45
+ return fgrs, phas, bgrs
46
+
47
+ def _get_imagematte(self, idx):
48
+ with Image.open(os.path.join(self.imagematte_dir, 'fgr', self.imagematte_files[idx % len(self.imagematte_files)])) as fgr, \
49
+ Image.open(os.path.join(self.imagematte_dir, 'pha', self.imagematte_files[idx % len(self.imagematte_files)])) as pha:
50
+ fgr = self._downsample_if_needed(fgr.convert('RGB'))
51
+ pha = self._downsample_if_needed(pha.convert('L'))
52
+ fgrs = [fgr] * self.seq_length
53
+ phas = [pha] * self.seq_length
54
+ return fgrs, phas
55
+
56
+ def _get_random_image_background(self):
57
+ with Image.open(os.path.join(self.background_image_dir, self.background_image_files[random.choice(range(len(self.background_image_files)))])) as bgr:
58
+ bgr = self._downsample_if_needed(bgr.convert('RGB'))
59
+ bgrs = [bgr] * self.seq_length
60
+ return bgrs
61
+
62
+ def _get_random_video_background(self):
63
+ clip_idx = random.choice(range(len(self.background_video_clips)))
64
+ frame_count = len(self.background_video_frames[clip_idx])
65
+ frame_idx = random.choice(range(max(1, frame_count - self.seq_length)))
66
+ clip = self.background_video_clips[clip_idx]
67
+ bgrs = []
68
+ for i in self.seq_sampler(self.seq_length):
69
+ frame_idx_t = frame_idx + i
70
+ frame = self.background_video_frames[clip_idx][frame_idx_t % frame_count]
71
+ with Image.open(os.path.join(self.background_video_dir, clip, frame)) as bgr:
72
+ bgr = self._downsample_if_needed(bgr.convert('RGB'))
73
+ bgrs.append(bgr)
74
+ return bgrs
75
+
76
+ def _downsample_if_needed(self, img):
77
+ w, h = img.size
78
+ if min(w, h) > self.size:
79
+ scale = self.size / min(w, h)
80
+ w = int(scale * w)
81
+ h = int(scale * h)
82
+ img = img.resize((w, h))
83
+ return img
84
+
85
+ class ImageMatteAugmentation(MotionAugmentation):
86
+ def __init__(self, size):
87
+ super().__init__(
88
+ size=size,
89
+ prob_fgr_affine=0.95,
90
+ prob_bgr_affine=0.3,
91
+ prob_noise=0.05,
92
+ prob_color_jitter=0.3,
93
+ prob_grayscale=0.03,
94
+ prob_sharpness=0.05,
95
+ prob_blur=0.02,
96
+ prob_hflip=0.5,
97
+ prob_pause=0.03,
98
+ )
RobustVideoMatting/dataset/spd.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from torch.utils.data import Dataset
3
+ from PIL import Image
4
+
5
+
6
+ class SuperviselyPersonDataset(Dataset):
7
+ def __init__(self, imgdir, segdir, transform=None):
8
+ self.img_dir = imgdir
9
+ self.img_files = sorted(os.listdir(imgdir))
10
+ self.seg_dir = segdir
11
+ self.seg_files = sorted(os.listdir(segdir))
12
+ assert len(self.img_files) == len(self.seg_files)
13
+ self.transform = transform
14
+
15
+ def __len__(self):
16
+ return len(self.img_files)
17
+
18
+ def __getitem__(self, idx):
19
+ with Image.open(os.path.join(self.img_dir, self.img_files[idx])) as img, \
20
+ Image.open(os.path.join(self.seg_dir, self.seg_files[idx])) as seg:
21
+ img = img.convert('RGB')
22
+ seg = seg.convert('L')
23
+
24
+ if self.transform is not None:
25
+ img, seg = self.transform(img, seg)
26
+
27
+ return img, seg
RobustVideoMatting/dataset/videomatte.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import random
3
+ from torch.utils.data import Dataset
4
+ from PIL import Image
5
+
6
+ from .augmentation import MotionAugmentation
7
+
8
+
9
+ class VideoMatteDataset(Dataset):
10
+ def __init__(self,
11
+ videomatte_dir,
12
+ background_image_dir,
13
+ background_video_dir,
14
+ size,
15
+ seq_length,
16
+ seq_sampler,
17
+ transform=None):
18
+ self.background_image_dir = background_image_dir
19
+ self.background_image_files = os.listdir(background_image_dir)
20
+ self.background_video_dir = background_video_dir
21
+ self.background_video_clips = sorted(os.listdir(background_video_dir))
22
+ self.background_video_frames = [sorted(os.listdir(os.path.join(background_video_dir, clip)))
23
+ for clip in self.background_video_clips]
24
+
25
+ self.videomatte_dir = videomatte_dir
26
+ self.videomatte_clips = sorted(os.listdir(os.path.join(videomatte_dir, 'fgr')))
27
+ self.videomatte_frames = [sorted(os.listdir(os.path.join(videomatte_dir, 'fgr', clip)))
28
+ for clip in self.videomatte_clips]
29
+ self.videomatte_idx = [(clip_idx, frame_idx)
30
+ for clip_idx in range(len(self.videomatte_clips))
31
+ for frame_idx in range(0, len(self.videomatte_frames[clip_idx]), seq_length)]
32
+ self.size = size
33
+ self.seq_length = seq_length
34
+ self.seq_sampler = seq_sampler
35
+ self.transform = transform
36
+
37
+ def __len__(self):
38
+ return len(self.videomatte_idx)
39
+
40
+ def __getitem__(self, idx):
41
+ if random.random() < 0.5:
42
+ bgrs = self._get_random_image_background()
43
+ else:
44
+ bgrs = self._get_random_video_background()
45
+
46
+ fgrs, phas = self._get_videomatte(idx)
47
+
48
+ if self.transform is not None:
49
+ return self.transform(fgrs, phas, bgrs)
50
+
51
+ return fgrs, phas, bgrs
52
+
53
+ def _get_random_image_background(self):
54
+ with Image.open(os.path.join(self.background_image_dir, random.choice(self.background_image_files))) as bgr:
55
+ bgr = self._downsample_if_needed(bgr.convert('RGB'))
56
+ bgrs = [bgr] * self.seq_length
57
+ return bgrs
58
+
59
+ def _get_random_video_background(self):
60
+ clip_idx = random.choice(range(len(self.background_video_clips)))
61
+ frame_count = len(self.background_video_frames[clip_idx])
62
+ frame_idx = random.choice(range(max(1, frame_count - self.seq_length)))
63
+ clip = self.background_video_clips[clip_idx]
64
+ bgrs = []
65
+ for i in self.seq_sampler(self.seq_length):
66
+ frame_idx_t = frame_idx + i
67
+ frame = self.background_video_frames[clip_idx][frame_idx_t % frame_count]
68
+ with Image.open(os.path.join(self.background_video_dir, clip, frame)) as bgr:
69
+ bgr = self._downsample_if_needed(bgr.convert('RGB'))
70
+ bgrs.append(bgr)
71
+ return bgrs
72
+
73
+ def _get_videomatte(self, idx):
74
+ clip_idx, frame_idx = self.videomatte_idx[idx]
75
+ clip = self.videomatte_clips[clip_idx]
76
+ frame_count = len(self.videomatte_frames[clip_idx])
77
+ fgrs, phas = [], []
78
+ for i in self.seq_sampler(self.seq_length):
79
+ frame = self.videomatte_frames[clip_idx][(frame_idx + i) % frame_count]
80
+ with Image.open(os.path.join(self.videomatte_dir, 'fgr', clip, frame)) as fgr, \
81
+ Image.open(os.path.join(self.videomatte_dir, 'pha', clip, frame)) as pha:
82
+ fgr = self._downsample_if_needed(fgr.convert('RGB'))
83
+ pha = self._downsample_if_needed(pha.convert('L'))
84
+ fgrs.append(fgr)
85
+ phas.append(pha)
86
+ return fgrs, phas
87
+
88
+ def _downsample_if_needed(self, img):
89
+ w, h = img.size
90
+ if min(w, h) > self.size:
91
+ scale = self.size / min(w, h)
92
+ w = int(scale * w)
93
+ h = int(scale * h)
94
+ img = img.resize((w, h))
95
+ return img
96
+
97
+ class VideoMatteTrainAugmentation(MotionAugmentation):
98
+ def __init__(self, size):
99
+ super().__init__(
100
+ size=size,
101
+ prob_fgr_affine=0.3,
102
+ prob_bgr_affine=0.3,
103
+ prob_noise=0.1,
104
+ prob_color_jitter=0.3,
105
+ prob_grayscale=0.02,
106
+ prob_sharpness=0.1,
107
+ prob_blur=0.02,
108
+ prob_hflip=0.5,
109
+ prob_pause=0.03,
110
+ )
111
+
112
+ class VideoMatteValidAugmentation(MotionAugmentation):
113
+ def __init__(self, size):
114
+ super().__init__(
115
+ size=size,
116
+ prob_fgr_affine=0,
117
+ prob_bgr_affine=0,
118
+ prob_noise=0,
119
+ prob_color_jitter=0,
120
+ prob_grayscale=0,
121
+ prob_sharpness=0,
122
+ prob_blur=0,
123
+ prob_hflip=0,
124
+ prob_pause=0,
125
+ )
RobustVideoMatting/dataset/youtubevis.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import os
3
+ import json
4
+ import numpy as np
5
+ import random
6
+ from torch.utils.data import Dataset
7
+ from PIL import Image
8
+ from torchvision import transforms
9
+ from torchvision.transforms import functional as F
10
+
11
+
12
+ class YouTubeVISDataset(Dataset):
13
+ def __init__(self, videodir, annfile, size, seq_length, seq_sampler, transform=None):
14
+ self.videodir = videodir
15
+ self.size = size
16
+ self.seq_length = seq_length
17
+ self.seq_sampler = seq_sampler
18
+ self.transform = transform
19
+
20
+ with open(annfile) as f:
21
+ data = json.load(f)
22
+
23
+ self.masks = {}
24
+ for ann in data['annotations']:
25
+ if ann['category_id'] == 26: # person
26
+ video_id = ann['video_id']
27
+ if video_id not in self.masks:
28
+ self.masks[video_id] = [[] for _ in range(len(ann['segmentations']))]
29
+ for frame, mask in zip(self.masks[video_id], ann['segmentations']):
30
+ if mask is not None:
31
+ frame.append(mask)
32
+
33
+ self.videos = {}
34
+ for video in data['videos']:
35
+ video_id = video['id']
36
+ if video_id in self.masks:
37
+ self.videos[video_id] = video
38
+
39
+ self.index = []
40
+ for video_id in self.videos.keys():
41
+ for frame in range(len(self.videos[video_id]['file_names'])):
42
+ self.index.append((video_id, frame))
43
+
44
+ def __len__(self):
45
+ return len(self.index)
46
+
47
+ def __getitem__(self, idx):
48
+ video_id, frame_id = self.index[idx]
49
+ video = self.videos[video_id]
50
+ frame_count = len(self.videos[video_id]['file_names'])
51
+ H, W = video['height'], video['width']
52
+
53
+ imgs, segs = [], []
54
+ for t in self.seq_sampler(self.seq_length):
55
+ frame = (frame_id + t) % frame_count
56
+
57
+ filename = video['file_names'][frame]
58
+ masks = self.masks[video_id][frame]
59
+
60
+ with Image.open(os.path.join(self.videodir, filename)) as img:
61
+ imgs.append(self._downsample_if_needed(img.convert('RGB'), Image.BILINEAR))
62
+
63
+ seg = np.zeros((H, W), dtype=np.uint8)
64
+ for mask in masks:
65
+ seg |= self._decode_rle(mask)
66
+ segs.append(self._downsample_if_needed(Image.fromarray(seg), Image.NEAREST))
67
+
68
+ if self.transform is not None:
69
+ imgs, segs = self.transform(imgs, segs)
70
+
71
+ return imgs, segs
72
+
73
+ def _decode_rle(self, rle):
74
+ H, W = rle['size']
75
+ msk = np.zeros(H * W, dtype=np.uint8)
76
+ encoding = rle['counts']
77
+ skip = 0
78
+ for i in range(0, len(encoding) - 1, 2):
79
+ skip += encoding[i]
80
+ draw = encoding[i + 1]
81
+ msk[skip : skip + draw] = 255
82
+ skip += draw
83
+ return msk.reshape(W, H).transpose()
84
+
85
+ def _downsample_if_needed(self, img, resample):
86
+ w, h = img.size
87
+ if min(w, h) > self.size:
88
+ scale = self.size / min(w, h)
89
+ w = int(scale * w)
90
+ h = int(scale * h)
91
+ img = img.resize((w, h), resample)
92
+ return img
93
+
94
+
95
+ class YouTubeVISAugmentation:
96
+ def __init__(self, size):
97
+ self.size = size
98
+ self.jitter = transforms.ColorJitter(0.3, 0.3, 0.3, 0.15)
99
+
100
+ def __call__(self, imgs, segs):
101
+
102
+ # To tensor
103
+ imgs = torch.stack([F.to_tensor(img) for img in imgs])
104
+ segs = torch.stack([F.to_tensor(seg) for seg in segs])
105
+
106
+ # Resize
107
+ params = transforms.RandomResizedCrop.get_params(imgs, scale=(0.8, 1), ratio=(0.9, 1.1))
108
+ imgs = F.resized_crop(imgs, *params, self.size, interpolation=F.InterpolationMode.BILINEAR)
109
+ segs = F.resized_crop(segs, *params, self.size, interpolation=F.InterpolationMode.BILINEAR)
110
+
111
+ # Color jitter
112
+ imgs = self.jitter(imgs)
113
+
114
+ # Grayscale
115
+ if random.random() < 0.05:
116
+ imgs = F.rgb_to_grayscale(imgs, num_output_channels=3)
117
+
118
+ # Horizontal flip
119
+ if random.random() < 0.5:
120
+ imgs = F.hflip(imgs)
121
+ segs = F.hflip(segs)
122
+
123
+ return imgs, segs
RobustVideoMatting/documentation/image/showreel.gif ADDED

Git LFS Details

  • SHA256: 5325432c8dab92e52eb9b4793556415d591f9168bf045ce76cb51e176233d01a
  • Pointer size: 132 Bytes
  • Size of remote file: 1.01 MB
RobustVideoMatting/documentation/image/teaser.gif ADDED

Git LFS Details

  • SHA256: 4e6ba86c7fb94b48e6122b88396625239aaa8d2464315ea96c1b0e0d62059759
  • Pointer size: 132 Bytes
  • Size of remote file: 3.5 MB
RobustVideoMatting/documentation/inference.md ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Inference
2
+
3
+ <p align="center">English | <a href="inference_zh_Hans.md">中文</a></p>
4
+
5
+ ## Content
6
+
7
+ * [Concepts](#concepts)
8
+ * [Downsample Ratio](#downsample-ratio)
9
+ * [Recurrent States](#recurrent-states)
10
+ * [PyTorch](#pytorch)
11
+ * [TorchHub](#torchhub)
12
+ * [TorchScript](#torchscript)
13
+ * [ONNX](#onnx)
14
+ * [TensorFlow](#tensorflow)
15
+ * [TensorFlow.js](#tensorflowjs)
16
+ * [CoreML](#coreml)
17
+
18
+ <br>
19
+
20
+
21
+ ## Concepts
22
+
23
+ ### Downsample Ratio
24
+
25
+ The table provides a general guideline. Please adjust based on your video content.
26
+
27
+ | Resolution | Portrait | Full-Body |
28
+ | ------------- | ------------- | -------------- |
29
+ | <= 512x512 | 1 | 1 |
30
+ | 1280x720 | 0.375 | 0.6 |
31
+ | 1920x1080 | 0.25 | 0.4 |
32
+ | 3840x2160 | 0.125 | 0.2 |
33
+
34
+ Internally, the model resizes down the input for stage 1. Then, it refines at high-resolution for stage 2.
35
+
36
+ Set `downsample_ratio` so that the downsampled resolution is between 256 and 512. For example, for `1920x1080` input with `downsample_ratio=0.25`, the resized resolution `480x270` is between 256 and 512.
37
+
38
+ Adjust `downsample_ratio` base on the video content. If the shot is portrait, a lower `downsample_ratio` is sufficient. If the shot contains the full human body, use high `downsample_ratio`. Note that higher `downsample_ratio` is not always better.
39
+
40
+
41
+ <br>
42
+
43
+ ### Recurrent States
44
+ The model is a recurrent neural network. You must process frames sequentially and recycle its recurrent states.
45
+
46
+ **Correct Way**
47
+
48
+ The recurrent outputs are recycled back as input when processing the next frame. The states are essentially the model's memory.
49
+
50
+ ```python
51
+ rec = [None] * 4 # Initial recurrent states are None
52
+
53
+ for frame in YOUR_VIDEO:
54
+ fgr, pha, *rec = model(frame, *rec, downsample_ratio)
55
+ ```
56
+
57
+ **Wrong Way**
58
+
59
+ The model does not utilize the recurrent states. Only use it to process independent images.
60
+
61
+ ```python
62
+ for frame in YOUR_VIDEO:
63
+ fgr, pha = model(frame, downsample_ratio)[:2]
64
+ ```
65
+
66
+ More technical details are in the [paper](https://peterl1n.github.io/RobustVideoMatting/).
67
+
68
+ <br><br><br>
69
+
70
+
71
+ ## PyTorch
72
+
73
+ Model loading:
74
+
75
+ ```python
76
+ import torch
77
+ from model import MattingNetwork
78
+
79
+ model = MattingNetwork(variant='mobilenetv3').eval().cuda() # Or variant="resnet50"
80
+ model.load_state_dict(torch.load('rvm_mobilenetv3.pth'))
81
+ ```
82
+
83
+ Example inference loop:
84
+ ```python
85
+ rec = [None] * 4 # Set initial recurrent states to None
86
+
87
+ for src in YOUR_VIDEO: # src can be [B, C, H, W] or [B, T, C, H, W]
88
+ fgr, pha, *rec = model(src, *rec, downsample_ratio=0.25)
89
+ ```
90
+
91
+ * `src`: Input frame.
92
+ * Can be of shape `[B, C, H, W]` or `[B, T, C, H, W]`.
93
+ * If `[B, T, C, H, W]`, a chunk of `T` frames can be given at once for better parallelism.
94
+ * RGB input is normalized to `0~1` range.
95
+
96
+ * `fgr, pha`: Foreground and alpha predictions.
97
+ * Can be of shape `[B, C, H, W]` or `[B, T, C, H, W]` depends on `src`.
98
+ * `fgr` has `C=3` for RGB, `pha` has `C=1`.
99
+ * Outputs normalized to `0~1` range.
100
+ * `rec`: Recurrent states.
101
+ * Type of `List[Tensor, Tensor, Tensor, Tensor]`.
102
+ * Initial `rec` can be `List[None, None, None, None]`.
103
+ * It has 4 recurrent states because the model has 4 ConvGRU layers.
104
+ * All tensors are rank 4 regardless of `src` rank.
105
+ * If a chunk of `T` frames is given, only the last frame's recurrent states will be returned.
106
+
107
+ To inference on video, here is a complete example:
108
+
109
+ ```python
110
+ from torch.utils.data import DataLoader
111
+ from torchvision.transforms import ToTensor
112
+ from inference_utils import VideoReader, VideoWriter
113
+
114
+ reader = VideoReader('input.mp4', transform=ToTensor())
115
+ writer = VideoWriter('output.mp4', frame_rate=30)
116
+
117
+ bgr = torch.tensor([.47, 1, .6]).view(3, 1, 1).cuda() # Green background.
118
+ rec = [None] * 4 # Initial recurrent states.
119
+
120
+ with torch.no_grad():
121
+ for src in DataLoader(reader):
122
+ fgr, pha, *rec = model(src.cuda(), *rec, downsample_ratio=0.25) # Cycle the recurrent states.
123
+ writer.write(fgr * pha + bgr * (1 - pha))
124
+ ```
125
+
126
+ Or you can use the provided video converter:
127
+
128
+ ```python
129
+ from inference import convert_video
130
+
131
+ convert_video(
132
+ model, # The loaded model, can be on any device (cpu or cuda).
133
+ input_source='input.mp4', # A video file or an image sequence directory.
134
+ input_resize=(1920, 1080), # [Optional] Resize the input (also the output).
135
+ downsample_ratio=0.25, # [Optional] If None, make downsampled max size be 512px.
136
+ output_type='video', # Choose "video" or "png_sequence"
137
+ output_composition='com.mp4', # File path if video; directory path if png sequence.
138
+ output_alpha="pha.mp4", # [Optional] Output the raw alpha prediction.
139
+ output_foreground="fgr.mp4", # [Optional] Output the raw foreground prediction.
140
+ output_video_mbps=4, # Output video mbps. Not needed for png sequence.
141
+ seq_chunk=12, # Process n frames at once for better parallelism.
142
+ num_workers=1, # Only for image sequence input. Reader threads.
143
+ progress=True # Print conversion progress.
144
+ )
145
+ ```
146
+
147
+ The converter can also be invoked in command line:
148
+
149
+ ```sh
150
+ python inference.py \
151
+ --variant mobilenetv3 \
152
+ --checkpoint "CHECKPOINT" \
153
+ --device cuda \
154
+ --input-source "input.mp4" \
155
+ --downsample-ratio 0.25 \
156
+ --output-type video \
157
+ --output-composition "composition.mp4" \
158
+ --output-alpha "alpha.mp4" \
159
+ --output-foreground "foreground.mp4" \
160
+ --output-video-mbps 4 \
161
+ --seq-chunk 12
162
+ ```
163
+
164
+ <br><br><br>
165
+
166
+ ## TorchHub
167
+
168
+ Model loading:
169
+
170
+ ```python
171
+ model = torch.hub.load("PeterL1n/RobustVideoMatting", "mobilenetv3") # or "resnet50"
172
+ ```
173
+
174
+ Use the conversion function. Refer to the documentation for `convert_video` function above.
175
+
176
+ ```python
177
+ convert_video = torch.hub.load("PeterL1n/RobustVideoMatting", "converter")
178
+
179
+ convert_video(model, ...args...)
180
+ ```
181
+
182
+ <br><br><br>
183
+
184
+ ## TorchScript
185
+
186
+ Model loading:
187
+
188
+ ```python
189
+ import torch
190
+ model = torch.jit.load('rvm_mobilenetv3.torchscript')
191
+ ```
192
+
193
+ Optionally, freeze the model. This will trigger graph optimization, such as BatchNorm fusion etc. Frozen models are faster.
194
+
195
+ ```python
196
+ model = torch.jit.freeze(model)
197
+ ```
198
+
199
+ Then, you can use the `model` exactly the same as a PyTorch model, with the exception that you must manually provide `device` and `dtype` to the converter API for frozen model. For example:
200
+
201
+ ```python
202
+ convert_video(frozen_model, ...args..., device='cuda', dtype=torch.float32)
203
+ ```
204
+
205
+ <br><br><br>
206
+
207
+ ## ONNX
208
+
209
+ Model spec:
210
+ * Inputs: [`src`, `r1i`, `r2i`, `r3i`, `r4i`, `downsample_ratio`].
211
+ * `src` is the RGB input frame of shape `[B, C, H, W]` normalized to `0~1` range.
212
+ * `rXi` are the recurrent state inputs. Initial recurrent states are zero value tensors of shape `[1, 1, 1, 1]`.
213
+ * `downsample_ratio` is a tensor of shape `[1]`.
214
+ * Only `downsample_ratio` must have `dtype=FP32`. Other inputs must have `dtype` matching the loaded model's precision.
215
+ * Outputs: [`fgr`, `pha`, `r1o`, `r2o`, `r3o`, `r4o`]
216
+ * `fgr, pha` are the foreground and alpha prediction. Normalized to `0~1` range.
217
+ * `rXo` are the recurrent state outputs.
218
+
219
+ We only show examples of using onnxruntime CUDA backend in Python.
220
+
221
+ Model loading
222
+
223
+ ```python
224
+ import onnxruntime as ort
225
+
226
+ sess = ort.InferenceSession('rvm_mobilenetv3_fp16.onnx')
227
+ ```
228
+
229
+ Naive inference loop
230
+
231
+ ```python
232
+ import numpy as np
233
+
234
+ rec = [ np.zeros([1, 1, 1, 1], dtype=np.float16) ] * 4 # Must match dtype of the model.
235
+ downsample_ratio = np.array([0.25], dtype=np.float32) # dtype always FP32
236
+
237
+ for src in YOUR_VIDEO: # src is of [B, C, H, W] with dtype of the model.
238
+ fgr, pha, *rec = sess.run([], {
239
+ 'src': src,
240
+ 'r1i': rec[0],
241
+ 'r2i': rec[1],
242
+ 'r3i': rec[2],
243
+ 'r4i': rec[3],
244
+ 'downsample_ratio': downsample_ratio
245
+ })
246
+ ```
247
+
248
+ If you use GPU version of ONNX Runtime, the above naive implementation has recurrent states transferred between CPU and GPU on every frame. They could have just stayed on the GPU for better performance. Below is an example using `iobinding` to eliminate useless transfers.
249
+
250
+ ```python
251
+ import onnxruntime as ort
252
+ import numpy as np
253
+
254
+ # Load model.
255
+ sess = ort.InferenceSession('rvm_mobilenetv3_fp16.onnx')
256
+
257
+ # Create an io binding.
258
+ io = sess.io_binding()
259
+
260
+ # Create tensors on CUDA.
261
+ rec = [ ort.OrtValue.ortvalue_from_numpy(np.zeros([1, 1, 1, 1], dtype=np.float16), 'cuda') ] * 4
262
+ downsample_ratio = ort.OrtValue.ortvalue_from_numpy(np.asarray([0.25], dtype=np.float32), 'cuda')
263
+
264
+ # Set output binding.
265
+ for name in ['fgr', 'pha', 'r1o', 'r2o', 'r3o', 'r4o']:
266
+ io.bind_output(name, 'cuda')
267
+
268
+ # Inference loop
269
+ for src in YOUR_VIDEO:
270
+ io.bind_cpu_input('src', src)
271
+ io.bind_ortvalue_input('r1i', rec[0])
272
+ io.bind_ortvalue_input('r2i', rec[1])
273
+ io.bind_ortvalue_input('r3i', rec[2])
274
+ io.bind_ortvalue_input('r4i', rec[3])
275
+ io.bind_ortvalue_input('downsample_ratio', downsample_ratio)
276
+
277
+ sess.run_with_iobinding(io)
278
+
279
+ fgr, pha, *rec = io.get_outputs()
280
+
281
+ # Only transfer `fgr` and `pha` to CPU.
282
+ fgr = fgr.numpy()
283
+ pha = pha.numpy()
284
+ ```
285
+
286
+ Note: depending on the inference tool you choose, it may not support all the operations in our official ONNX model. You are responsible for modifying the model code and exporting your own ONNX model. You can refer to our exporter code in the [onnx branch](https://github.com/PeterL1n/RobustVideoMatting/tree/onnx).
287
+
288
+ <br><br><br>
289
+
290
+ ### TensorFlow
291
+
292
+ An example usage:
293
+
294
+ ```python
295
+ import tensorflow as tf
296
+
297
+ model = tf.keras.models.load_model('rvm_mobilenetv3_tf')
298
+ model = tf.function(model)
299
+
300
+ rec = [ tf.constant(0.) ] * 4 # Initial recurrent states.
301
+ downsample_ratio = tf.constant(0.25) # Adjust based on your video.
302
+
303
+ for src in YOUR_VIDEO: # src is of shape [B, H, W, C], not [B, C, H, W]!
304
+ out = model([src, *rec, downsample_ratio])
305
+ fgr, pha, *rec = out['fgr'], out['pha'], out['r1o'], out['r2o'], out['r3o'], out['r4o']
306
+ ```
307
+
308
+ Note the the tensors are all channel last. Otherwise, the inputs and outputs are exactly the same as PyTorch.
309
+
310
+ We also provide the raw TensorFlow model code in the [tensorflow branch](https://github.com/PeterL1n/RobustVideoMatting/tree/tensorflow). You can transfer PyTorch checkpoint weights to TensorFlow models.
311
+
312
+ <br><br><br>
313
+
314
+ ### TensorFlow.js
315
+
316
+ We provide a starter code in the [tfjs branch](https://github.com/PeterL1n/RobustVideoMatting/tree/tfjs). The example is very self-explanatory. It shows how to properly use the model.
317
+
318
+ <br><br><br>
319
+
320
+ ### CoreML
321
+
322
+ We only show example usage of the CoreML models in Python API using `coremltools`. In production, the same logic can be applied in Swift. When processing the first frame, do not provide recurrent states. CoreML will internally construct zero tensors of the correct shapes as the initial recurrent states.
323
+
324
+ ```python
325
+ import coremltools as ct
326
+
327
+ model = ct.models.model.MLModel('rvm_mobilenetv3_1920x1080_s0.25_int8.mlmodel')
328
+
329
+ r1, r2, r3, r4 = None, None, None, None
330
+
331
+ for src in YOUR_VIDEO: # src is PIL.Image.
332
+
333
+ if r1 is None:
334
+ # Initial frame, do not provide recurrent states.
335
+ inputs = {'src': src}
336
+ else:
337
+ # Subsequent frames, provide recurrent states.
338
+ inputs = {'src': src, 'r1i': r1, 'r2i': r2, 'r3i': r3, 'r4i': r4}
339
+
340
+ outputs = model.predict(inputs)
341
+
342
+ fgr = outputs['fgr'] # PIL.Image.
343
+ pha = outputs['pha'] # PIL.Image.
344
+
345
+ r1 = outputs['r1o'] # Numpy array.
346
+ r2 = outputs['r2o'] # Numpy array.
347
+ r3 = outputs['r3o'] # Numpy array.
348
+ r4 = outputs['r4o'] # Numpy array.
349
+
350
+ ```
351
+
352
+ Our CoreML models only support fixed resolutions. If you need other resolutions, you can export them yourself. See [coreml branch](https://github.com/PeterL1n/RobustVideoMatting/tree/coreml) for model export.
RobustVideoMatting/documentation/inference_zh_Hans.md ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 推断文档
2
+
3
+ <p align="center"><a href="inference.md">English</a> | 中文</p>
4
+
5
+ ## 目录
6
+
7
+ * [概念](#概念)
8
+ * [下采样比](#下采样比)
9
+ * [循环记忆](#循环记忆)
10
+ * [PyTorch](#pytorch)
11
+ * [TorchHub](#torchhub)
12
+ * [TorchScript](#torchscript)
13
+ * [ONNX](#onnx)
14
+ * [TensorFlow](#tensorflow)
15
+ * [TensorFlow.js](#tensorflowjs)
16
+ * [CoreML](#coreml)
17
+
18
+ <br>
19
+
20
+
21
+ ## 概念
22
+
23
+ ### 下采样比
24
+
25
+ 该表仅供参考。可根据视频内容进行调节。
26
+
27
+ | 分辨率 | 人像 | 全身 |
28
+ | ------------- | ------------- | -------------- |
29
+ | <= 512x512 | 1 | 1 |
30
+ | 1280x720 | 0.375 | 0.6 |
31
+ | 1920x1080 | 0.25 | 0.4 |
32
+ | 3840x2160 | 0.125 | 0.2 |
33
+
34
+ 模型在内部将高分辨率输入缩小做初步的处理,然后再放大做细分处理。
35
+
36
+ 建议设置 `downsample_ratio` 使缩小后的分辨率维持在 256 到 512 像素之间. 例如,`1920x1080` 的输入用 `downsample_ratio=0.25`,缩小后的分辨率 `480x270` 在 256 到 512 像素之间。
37
+
38
+ 根据视频内容调整 `downsample_ratio`。若视频是上身人像,低 `downsample_ratio` 足矣。若视频是全身像,建议尝试更高的 `downsample_ratio`。但注意,过高的 `downsample_ratio` 反而会降低效果。
39
+
40
+
41
+ <br>
42
+
43
+ ### 循环记忆
44
+ 此模型是循环神经网络(Recurrent Neural Network)。必须按顺序处理视频每帧,并提供网络循环记忆。
45
+
46
+ **正确用法**
47
+
48
+ 循环记忆输出被传递到下一帧做输入。
49
+
50
+ ```python
51
+ rec = [None] * 4 # 初始值设置为 None
52
+
53
+ for frame in YOUR_VIDEO:
54
+ fgr, pha, *rec = model(frame, *rec, downsample_ratio)
55
+ ```
56
+
57
+ **错误用法**
58
+
59
+ 没有使用循环记忆。此方法仅可用于处理单独的图片。
60
+
61
+ ```python
62
+ for frame in YOUR_VIDEO:
63
+ fgr, pha = model(frame, downsample_ratio)[:2]
64
+ ```
65
+
66
+ 更多技术细节见[论文](https://peterl1n.github.io/RobustVideoMatting/)。
67
+
68
+ <br><br><br>
69
+
70
+
71
+ ## PyTorch
72
+
73
+ 载入模型:
74
+
75
+ ```python
76
+ import torch
77
+ from model import MattingNetwork
78
+
79
+ model = MattingNetwork(variant='mobilenetv3').eval().cuda() # 或 variant="resnet50"
80
+ model.load_state_dict(torch.load('rvm_mobilenetv3.pth'))
81
+ ```
82
+
83
+ 推断循环:
84
+ ```python
85
+ rec = [None] * 4 # 初始值设置为 None
86
+
87
+ for src in YOUR_VIDEO: # src 可以是 [B, C, H, W] 或 [B, T, C, H, W]
88
+ fgr, pha, *rec = model(src, *rec, downsample_ratio=0.25)
89
+ ```
90
+
91
+ * `src`: 输入帧(Source)。
92
+ * 可以是 `[B, C, H, W]` 或 `[B, T, C, H, W]` 的张量。
93
+ * 若是 `[B, T, C, H, W]`,可给模型一次 `T` 帧,做一小段一小段地处理,用于更好的并行计算。
94
+ * RGB 通道输入,范围为 `0~1`。
95
+
96
+ * `fgr, pha`: 前景(Foreground)和透明度通道(Alpha)的预测。
97
+ * 根据`src`,可为 `[B, C, H, W]` 或 `[B, T, C, H, W]` 的输出。
98
+ * `fgr` 是 RGB 三通道,`pha` 为一通道。
99
+ * 输出范围为 `0~1`。
100
+ * `rec`: 循环记忆(Recurrent States)。
101
+ * `List[Tensor, Tensor, Tensor, Tensor]` 类型。
102
+ * 初始 `rec` 为 `List[None, None, None, None]`。
103
+ * 有四个记忆,因为网络使用四个 `ConvGRU` 层。
104
+ * 无论 `src` 的 Rank,所有记忆张量的 Rank 为 4。
105
+ * 若一次给予 `T` 帧,只返回处理完最后一帧后的记忆。
106
+
107
+ 完整的推断例子:
108
+
109
+ ```python
110
+ from torch.utils.data import DataLoader
111
+ from torchvision.transforms import ToTensor
112
+ from inference_utils import VideoReader, VideoWriter
113
+
114
+ reader = VideoReader('input.mp4', transform=ToTensor())
115
+ writer = VideoWriter('output.mp4', frame_rate=30)
116
+
117
+ bgr = torch.tensor([.47, 1, .6]).view(3, 1, 1).cuda() # 绿背景
118
+ rec = [None] * 4 # 初始记忆
119
+
120
+ with torch.no_grad():
121
+ for src in DataLoader(reader):
122
+ fgr, pha, *rec = model(src.cuda(), *rec, downsample_ratio=0.25) # 将上一帧的记忆给下一帧
123
+ writer.write(fgr * pha + bgr * (1 - pha))
124
+ ```
125
+
126
+ 或者使用提供的视频转换 API:
127
+
128
+ ```python
129
+ from inference import convert_video
130
+
131
+ convert_video(
132
+ model, # 模型,可以加载到任何设备(cpu 或 cuda)
133
+ input_source='input.mp4', # 视频文件,或图片序列文件夹
134
+ input_resize=(1920, 1080), # [可选项] 缩放视频大小
135
+ downsample_ratio=0.25, # [可选项] 下采样比,若 None,自动下采样至 512px
136
+ output_type='video', # 可选 "video"(视频)或 "png_sequence"(PNG 序列)
137
+ output_composition='com.mp4', # 若导出视频,提供文件路径。若导出 PNG 序列,提供文件夹路径
138
+ output_alpha="pha.mp4", # [可选项] 输出透明度预测
139
+ output_foreground="fgr.mp4", # [可选项] 输出前景预测
140
+ output_video_mbps=4, # 若导出视频,提供视频码率
141
+ seq_chunk=12, # 设置多帧并行计算
142
+ num_workers=1, # 只适用于图片序列输入,读取线程
143
+ progress=True # 显示进度条
144
+ )
145
+ ```
146
+
147
+ 也可通过命令行调用转换 API:
148
+
149
+ ```sh
150
+ python inference.py \
151
+ --variant mobilenetv3 \
152
+ --checkpoint "CHECKPOINT" \
153
+ --device cuda \
154
+ --input-source "input.mp4" \
155
+ --downsample-ratio 0.25 \
156
+ --output-type video \
157
+ --output-composition "composition.mp4" \
158
+ --output-alpha "alpha.mp4" \
159
+ --output-foreground "foreground.mp4" \
160
+ --output-video-mbps 4 \
161
+ --seq-chunk 12
162
+ ```
163
+
164
+ <br><br><br>
165
+
166
+ ## TorchHub
167
+
168
+ 载入模型:
169
+
170
+ ```python
171
+ model = torch.hub.load("PeterL1n/RobustVideoMatting", "mobilenetv3") # or "resnet50"
172
+ ```
173
+
174
+ 使用转换 API,具体请参考之前对 `convert_video` 的文档。
175
+
176
+ ```python
177
+ convert_video = torch.hub.load("PeterL1n/RobustVideoMatting", "converter")
178
+
179
+ convert_video(model, ...args...)
180
+ ```
181
+
182
+ <br><br><br>
183
+
184
+ ## TorchScript
185
+
186
+ 载入模型:
187
+
188
+ ```python
189
+ import torch
190
+ model = torch.jit.load('rvm_mobilenetv3.torchscript')
191
+ ```
192
+
193
+ 也可以可选的将模型固化(Freeze)。这会对模型进行优化,例如 BatchNorm Fusion 等。固化的模型更快。
194
+
195
+ ```python
196
+ model = torch.jit.freeze(model)
197
+ ```
198
+
199
+ 然后,可以将 `model` 作为普通的 PyTorch 模型使用。但注意,若用固化模型调用转换 API,必须手动提供 `device` 和 `dtype`:
200
+
201
+ ```python
202
+ convert_video(frozen_model, ...args..., device='cuda', dtype=torch.float32)
203
+ ```
204
+
205
+ <br><br><br>
206
+
207
+ ## ONNX
208
+
209
+ 模型规格:
210
+ * 输入: [`src`, `r1i`, `r2i`, `r3i`, `r4i`, `downsample_ratio`].
211
+ * `src`:输入帧,RGB 通道,形状为 `[B, C, H, W]`,范围为`0~1`。
212
+ * `rXi`:记忆输入,初始值是是形状为 `[1, 1, 1, 1]` 的零张量。
213
+ * `downsample_ratio` 下采样比,张量形状为 `[1]`。
214
+ * 只有 `downsample_ratio` 必须是 `FP32`,其他输入必须和加载的模型使用一样的 `dtype`。
215
+ * 输出: [`fgr`, `pha`, `r1o`, `r2o`, `r3o`, `r4o`]
216
+ * `fgr, pha`:前景和透明度通道输出,范围为 `0~1`。
217
+ * `rXo`:记忆输出。
218
+
219
+ 我们只展示用 ONNX Runtime CUDA Backend 在 Python 上的使用范例。
220
+
221
+ 载入模型:
222
+
223
+ ```python
224
+ import onnxruntime as ort
225
+
226
+ sess = ort.InferenceSession('rvm_mobilenetv3_fp16.onnx')
227
+ ```
228
+
229
+ 简单推断循环,但此方法不是最优化的:
230
+
231
+ ```python
232
+ import numpy as np
233
+
234
+ rec = [ np.zeros([1, 1, 1, 1], dtype=np.float16) ] * 4 # 必须用模型一样的 dtype
235
+ downsample_ratio = np.array([0.25], dtype=np.float32) # 必须是 FP32
236
+
237
+ for src in YOUR_VIDEO: # src 张量是 [B, C, H, W] 形状,必须用模型一样的 dtype
238
+ fgr, pha, *rec = sess.run([], {
239
+ 'src': src,
240
+ 'r1i': rec[0],
241
+ 'r2i': rec[1],
242
+ 'r3i': rec[2],
243
+ 'r4i': rec[3],
244
+ 'downsample_ratio': downsample_ratio
245
+ })
246
+ ```
247
+
248
+ 若使用 GPU,上例会将记忆输出传回到 CPU,再在下一帧时传回到 GPU。这种传输是无意义的,因为记忆值可以留在 GPU 上。下例使用 `iobinding` 来杜绝无用的传输。
249
+
250
+ ```python
251
+ import onnxruntime as ort
252
+ import numpy as np
253
+
254
+ # 载入模型
255
+ sess = ort.InferenceSession('rvm_mobilenetv3_fp16.onnx')
256
+
257
+ # 创建 io binding.
258
+ io = sess.io_binding()
259
+
260
+ # 在 CUDA 上创建张量
261
+ rec = [ ort.OrtValue.ortvalue_from_numpy(np.zeros([1, 1, 1, 1], dtype=np.float16), 'cuda') ] * 4
262
+ downsample_ratio = ort.OrtValue.ortvalue_from_numpy(np.asarray([0.25], dtype=np.float32), 'cuda')
263
+
264
+ # 设置输出项
265
+ for name in ['fgr', 'pha', 'r1o', 'r2o', 'r3o', 'r4o']:
266
+ io.bind_output(name, 'cuda')
267
+
268
+ # 推断
269
+ for src in YOUR_VIDEO:
270
+ io.bind_cpu_input('src', src)
271
+ io.bind_ortvalue_input('r1i', rec[0])
272
+ io.bind_ortvalue_input('r2i', rec[1])
273
+ io.bind_ortvalue_input('r3i', rec[2])
274
+ io.bind_ortvalue_input('r4i', rec[3])
275
+ io.bind_ortvalue_input('downsample_ratio', downsample_ratio)
276
+
277
+ sess.run_with_iobinding(io)
278
+
279
+ fgr, pha, *rec = io.get_outputs()
280
+
281
+ # 只将 `fgr` 和 `pha` 回传到 CPU
282
+ fgr = fgr.numpy()
283
+ pha = pha.numpy()
284
+ ```
285
+
286
+ 注:若你使用其他推断框架,可能有些 ONNX ops 不被支持,需被替换。可以参考 [onnx](https://github.com/PeterL1n/RobustVideoMatting/tree/onnx) 分支的代码做自行导出。
287
+
288
+ <br><br><br>
289
+
290
+ ### TensorFlow
291
+
292
+ 范例:
293
+
294
+ ```python
295
+ import tensorflow as tf
296
+
297
+ model = tf.keras.models.load_model('rvm_mobilenetv3_tf')
298
+ model = tf.function(model)
299
+
300
+ rec = [ tf.constant(0.) ] * 4 # 初始记忆
301
+ downsample_ratio = tf.constant(0.25) # 下采样率,根据视频调整
302
+
303
+ for src in YOUR_VIDEO: # src 张量是 [B, H, W, C] 的形状,而不是 [B, C, H, W]!
304
+ out = model([src, *rec, downsample_ratio])
305
+ fgr, pha, *rec = out['fgr'], out['pha'], out['r1o'], out['r2o'], out['r3o'], out['r4o']
306
+ ```
307
+
308
+ 注意,在 TensorFlow 上,所有张量都是 Channal Last 的格式。
309
+
310
+ 我们提供 TensorFlow 的原始模型代码,请参考 [tensorflow](https://github.com/PeterL1n/RobustVideoMatting/tree/tensorflow) 分支。您可自行将 PyTorch 的权值转到 TensorFlow 模型上。
311
+
312
+
313
+ <br><br><br>
314
+
315
+ ### TensorFlow.js
316
+
317
+ 我们在 [tfjs](https://github.com/PeterL1n/RobustVideoMatting/tree/tfjs) 分支提供范例代码。代码简单易懂,解释如何正确使用模型。
318
+
319
+ <br><br><br>
320
+
321
+ ### CoreML
322
+
323
+ 我们只展示在 Python 下通过 `coremltools` 使用 CoreML 模型。在部署时,同样逻辑可用于 Swift。模型的循环记忆输入不需要在处理第一帧时提供。CoreML 内部会自动创建零张量作为初始记忆。
324
+
325
+ ```python
326
+ import coremltools as ct
327
+
328
+ model = ct.models.model.MLModel('rvm_mobilenetv3_1920x1080_s0.25_int8.mlmodel')
329
+
330
+ r1, r2, r3, r4 = None, None, None, None
331
+
332
+ for src in YOUR_VIDEO: # src 是 PIL.Image.
333
+
334
+ if r1 is None:
335
+ # 初始帧, 不用提供循环记忆
336
+ inputs = {'src': src}
337
+ else:
338
+ # 剩余帧,提供循环记忆
339
+ inputs = {'src': src, 'r1i': r1, 'r2i': r2, 'r3i': r3, 'r4i': r4}
340
+
341
+ outputs = model.predict(inputs)
342
+
343
+ fgr = outputs['fgr'] # PIL.Image
344
+ pha = outputs['pha'] # PIL.Image
345
+
346
+ r1 = outputs['r1o'] # Numpy array
347
+ r2 = outputs['r2o'] # Numpy array
348
+ r3 = outputs['r3o'] # Numpy array
349
+ r4 = outputs['r4o'] # Numpy array
350
+
351
+ ```
352
+
353
+ 我们的 CoreML 模型只支持固定分辨率。如果你需要其他分辨率,可自行导出。导出代码见 [coreml](https://github.com/PeterL1n/RobustVideoMatting/tree/coreml) 分支。
RobustVideoMatting/documentation/misc/aim_test.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ boy-1518482_1920.png
2
+ girl-1219339_1920.png
3
+ girl-1467820_1280.png
4
+ girl-beautiful-young-face-53000.png
5
+ long-1245787_1920.png
6
+ model-600238_1920.png
7
+ pexels-photo-58463.png
8
+ sea-sunny-person-beach.png
9
+ wedding-dresses-1486260_1280.png
10
+ woman-952506_1920 (1).png
11
+ woman-morning-bathrobe-bathroom.png
RobustVideoMatting/documentation/misc/d646_test.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ test_13.png
2
+ test_16.png
3
+ test_18.png
4
+ test_22.png
5
+ test_32.png
6
+ test_35.png
7
+ test_39.png
8
+ test_42.png
9
+ test_46.png
10
+ test_4.png
11
+ test_6.png
RobustVideoMatting/documentation/misc/dvm_background_test_clips.txt ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 0000
2
+ 0001
3
+ 0002
4
+ 0004
5
+ 0005
6
+ 0007
7
+ 0008
8
+ 0009
9
+ 0010
10
+ 0012
11
+ 0013
12
+ 0014
13
+ 0015
14
+ 0016
15
+ 0017
16
+ 0018
17
+ 0019
18
+ 0021
19
+ 0022
20
+ 0023
21
+ 0024
22
+ 0025
23
+ 0027
24
+ 0029
25
+ 0030
26
+ 0032
27
+ 0033
28
+ 0034
29
+ 0035
30
+ 0037
31
+ 0038
32
+ 0039
33
+ 0040
34
+ 0041
35
+ 0042
36
+ 0043
37
+ 0045
38
+ 0046
39
+ 0047
40
+ 0048
41
+ 0050
42
+ 0051
43
+ 0052
44
+ 0054
45
+ 0055
46
+ 0057
47
+ 0058
48
+ 0059
49
+ 0060
50
+ 0061
51
+ 0062
52
+ 0063
53
+ 0064
54
+ 0065
55
+ 0066
56
+ 0068
57
+ 0070
58
+ 0071
59
+ 0073
60
+ 0074
61
+ 0075
62
+ 0077
63
+ 0078
64
+ 0079
65
+ 0080
66
+ 0081
67
+ 0082
68
+ 0083
69
+ 0084
70
+ 0085
71
+ 0086
72
+ 0089
73
+ 0097
74
+ 0100
75
+ 0101
76
+ 0102
77
+ 0103
78
+ 0104
79
+ 0106
80
+ 0107
81
+ 0109
82
+ 0110
83
+ 0111
84
+ 0113
85
+ 0115
86
+ 0116
87
+ 0117
88
+ 0119
89
+ 0120
90
+ 0121
91
+ 0122
92
+ 0123
93
+ 0124
94
+ 0125
95
+ 0126
96
+ 0127
97
+ 0128
98
+ 0129
99
+ 0130
100
+ 0131
101
+ 0132
102
+ 0133
103
+ 0134
104
+ 0135
105
+ 0136
106
+ 0137
107
+ 0143
108
+ 0145
109
+ 0147
110
+ 0148
111
+ 0150
112
+ 0159
113
+ 0160
114
+ 0161
115
+ 0162
116
+ 0165
117
+ 0166
118
+ 0168
119
+ 0172
120
+ 0174
121
+ 0175
122
+ 0176
123
+ 0178
124
+ 0181
125
+ 0182
126
+ 0183
127
+ 0184
128
+ 0185
129
+ 0187
130
+ 0194
131
+ 0198
132
+ 0200
133
+ 0201
134
+ 0207
135
+ 0210
136
+ 0211
137
+ 0212
138
+ 0215
139
+ 0217
140
+ 0218
141
+ 0219
142
+ 0220
143
+ 0222
144
+ 0223
145
+ 0224
146
+ 0225
147
+ 0226
148
+ 0227
149
+ 0229
150
+ 0230
151
+ 0231
152
+ 0232
153
+ 0233
154
+ 0234
155
+ 0235
156
+ 0237
157
+ 0240
158
+ 0241
159
+ 0242
160
+ 0243
161
+ 0244
162
+ 0245
RobustVideoMatting/documentation/misc/dvm_background_train_clips.txt ADDED
@@ -0,0 +1,3117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 0000
2
+ 0002
3
+ 0003
4
+ 0004
5
+ 0005
6
+ 0006
7
+ 0007
8
+ 0009
9
+ 0010
10
+ 0012
11
+ 0013
12
+ 0014
13
+ 0015
14
+ 0016
15
+ 0019
16
+ 0021
17
+ 0022
18
+ 0023
19
+ 0024
20
+ 0025
21
+ 0028
22
+ 0029
23
+ 0030
24
+ 0031
25
+ 0032
26
+ 0034
27
+ 0035
28
+ 0036
29
+ 0037
30
+ 0039
31
+ 0040
32
+ 0041
33
+ 0042
34
+ 0043
35
+ 0044
36
+ 0046
37
+ 0047
38
+ 0048
39
+ 0049
40
+ 0050
41
+ 0051
42
+ 0052
43
+ 0053
44
+ 0054
45
+ 0055
46
+ 0056
47
+ 0057
48
+ 0058
49
+ 0060
50
+ 0061
51
+ 0062
52
+ 0063
53
+ 0064
54
+ 0065
55
+ 0066
56
+ 0067
57
+ 0068
58
+ 0069
59
+ 0070
60
+ 0071
61
+ 0073
62
+ 0074
63
+ 0075
64
+ 0076
65
+ 0077
66
+ 0078
67
+ 0079
68
+ 0081
69
+ 0082
70
+ 0087
71
+ 0088
72
+ 0099
73
+ 0100
74
+ 0101
75
+ 0104
76
+ 0105
77
+ 0107
78
+ 0108
79
+ 0109
80
+ 0110
81
+ 0111
82
+ 0112
83
+ 0113
84
+ 0114
85
+ 0115
86
+ 0117
87
+ 0118
88
+ 0119
89
+ 0120
90
+ 0122
91
+ 0123
92
+ 0124
93
+ 0125
94
+ 0127
95
+ 0128
96
+ 0129
97
+ 0130
98
+ 0131
99
+ 0132
100
+ 0133
101
+ 0134
102
+ 0135
103
+ 0136
104
+ 0137
105
+ 0138
106
+ 0139
107
+ 0140
108
+ 0141
109
+ 0142
110
+ 0144
111
+ 0146
112
+ 0147
113
+ 0148
114
+ 0150
115
+ 0151
116
+ 0152
117
+ 0153
118
+ 0154
119
+ 0155
120
+ 0156
121
+ 0157
122
+ 0158
123
+ 0159
124
+ 0160
125
+ 0161
126
+ 0163
127
+ 0164
128
+ 0165
129
+ 0167
130
+ 0168
131
+ 0169
132
+ 0170
133
+ 0171
134
+ 0172
135
+ 0174
136
+ 0175
137
+ 0176
138
+ 0177
139
+ 0178
140
+ 0180
141
+ 0181
142
+ 0182
143
+ 0184
144
+ 0185
145
+ 0187
146
+ 0188
147
+ 0189
148
+ 0190
149
+ 0192
150
+ 0193
151
+ 0194
152
+ 0195
153
+ 0196
154
+ 0197
155
+ 0198
156
+ 0199
157
+ 0200
158
+ 0202
159
+ 0203
160
+ 0204
161
+ 0205
162
+ 0206
163
+ 0207
164
+ 0208
165
+ 0209
166
+ 0210
167
+ 0211
168
+ 0212
169
+ 0213
170
+ 0214
171
+ 0215
172
+ 0217
173
+ 0218
174
+ 0219
175
+ 0220
176
+ 0221
177
+ 0222
178
+ 0223
179
+ 0224
180
+ 0225
181
+ 0226
182
+ 0227
183
+ 0229
184
+ 0230
185
+ 0231
186
+ 0233
187
+ 0234
188
+ 0235
189
+ 0236
190
+ 0237
191
+ 0238
192
+ 0240
193
+ 0241
194
+ 0242
195
+ 0243
196
+ 0244
197
+ 0245
198
+ 0246
199
+ 0247
200
+ 0248
201
+ 0249
202
+ 0250
203
+ 0251
204
+ 0252
205
+ 0253
206
+ 0254
207
+ 0255
208
+ 0256
209
+ 0257
210
+ 0258
211
+ 0259
212
+ 0260
213
+ 0261
214
+ 0262
215
+ 0263
216
+ 0264
217
+ 0265
218
+ 0266
219
+ 0267
220
+ 0268
221
+ 0269
222
+ 0270
223
+ 0271
224
+ 0272
225
+ 0273
226
+ 0274
227
+ 0275
228
+ 0276
229
+ 0277
230
+ 0278
231
+ 0279
232
+ 0280
233
+ 0281
234
+ 0282
235
+ 0283
236
+ 0284
237
+ 0285
238
+ 0286
239
+ 0287
240
+ 0288
241
+ 0289
242
+ 0290
243
+ 0291
244
+ 0292
245
+ 0293
246
+ 0294
247
+ 0297
248
+ 0298
249
+ 0299
250
+ 0300
251
+ 0301
252
+ 0302
253
+ 0303
254
+ 0304
255
+ 0305
256
+ 0306
257
+ 0308
258
+ 0309
259
+ 0310
260
+ 0311
261
+ 0312
262
+ 0313
263
+ 0314
264
+ 0315
265
+ 0316
266
+ 0317
267
+ 0319
268
+ 0320
269
+ 0321
270
+ 0322
271
+ 0323
272
+ 0324
273
+ 0325
274
+ 0326
275
+ 0327
276
+ 0328
277
+ 0329
278
+ 0330
279
+ 0331
280
+ 0332
281
+ 0333
282
+ 0335
283
+ 0336
284
+ 0337
285
+ 0338
286
+ 0339
287
+ 0341
288
+ 0342
289
+ 0344
290
+ 0345
291
+ 0346
292
+ 0348
293
+ 0349
294
+ 0352
295
+ 0353
296
+ 0356
297
+ 0357
298
+ 0358
299
+ 0359
300
+ 0360
301
+ 0361
302
+ 0362
303
+ 0363
304
+ 0364
305
+ 0365
306
+ 0366
307
+ 0368
308
+ 0369
309
+ 0370
310
+ 0371
311
+ 0372
312
+ 0373
313
+ 0374
314
+ 0375
315
+ 0376
316
+ 0377
317
+ 0378
318
+ 0379
319
+ 0380
320
+ 0381
321
+ 0382
322
+ 0383
323
+ 0384
324
+ 0385
325
+ 0386
326
+ 0387
327
+ 0388
328
+ 0389
329
+ 0391
330
+ 0392
331
+ 0393
332
+ 0394
333
+ 0395
334
+ 0397
335
+ 0398
336
+ 0399
337
+ 0400
338
+ 0401
339
+ 0402
340
+ 0403
341
+ 0404
342
+ 0405
343
+ 0406
344
+ 0407
345
+ 0408
346
+ 0409
347
+ 0410
348
+ 0411
349
+ 0413
350
+ 0414
351
+ 0415
352
+ 0416
353
+ 0417
354
+ 0419
355
+ 0420
356
+ 0421
357
+ 0422
358
+ 0423
359
+ 0424
360
+ 0425
361
+ 0426
362
+ 0427
363
+ 0428
364
+ 0429
365
+ 0431
366
+ 0433
367
+ 0434
368
+ 0435
369
+ 0436
370
+ 0437
371
+ 0438
372
+ 0439
373
+ 0440
374
+ 0441
375
+ 0442
376
+ 0443
377
+ 0445
378
+ 0446
379
+ 0447
380
+ 0448
381
+ 0449
382
+ 0450
383
+ 0451
384
+ 0452
385
+ 0453
386
+ 0454
387
+ 0456
388
+ 0457
389
+ 0458
390
+ 0459
391
+ 0462
392
+ 0463
393
+ 0464
394
+ 0465
395
+ 0466
396
+ 0467
397
+ 0468
398
+ 0469
399
+ 0470
400
+ 0471
401
+ 0472
402
+ 0473
403
+ 0474
404
+ 0475
405
+ 0476
406
+ 0477
407
+ 0478
408
+ 0479
409
+ 0480
410
+ 0481
411
+ 0482
412
+ 0483
413
+ 0484
414
+ 0485
415
+ 0486
416
+ 0487
417
+ 0488
418
+ 0489
419
+ 0490
420
+ 0491
421
+ 0492
422
+ 0493
423
+ 0494
424
+ 0499
425
+ 0501
426
+ 0502
427
+ 0503
428
+ 0504
429
+ 0505
430
+ 0506
431
+ 0507
432
+ 0509
433
+ 0510
434
+ 0511
435
+ 0512
436
+ 0513
437
+ 0514
438
+ 0515
439
+ 0517
440
+ 0518
441
+ 0519
442
+ 0520
443
+ 0521
444
+ 0522
445
+ 0524
446
+ 0526
447
+ 0527
448
+ 0529
449
+ 0530
450
+ 0534
451
+ 0535
452
+ 0536
453
+ 0538
454
+ 0539
455
+ 0541
456
+ 0542
457
+ 0543
458
+ 0544
459
+ 0545
460
+ 0546
461
+ 0548
462
+ 0549
463
+ 0550
464
+ 0552
465
+ 0554
466
+ 0555
467
+ 0556
468
+ 0557
469
+ 0558
470
+ 0559
471
+ 0560
472
+ 0561
473
+ 0562
474
+ 0563
475
+ 0564
476
+ 0565
477
+ 0566
478
+ 0567
479
+ 0568
480
+ 0571
481
+ 0572
482
+ 0573
483
+ 0574
484
+ 0575
485
+ 0576
486
+ 0577
487
+ 0578
488
+ 0579
489
+ 0580
490
+ 0581
491
+ 0582
492
+ 0583
493
+ 0584
494
+ 0586
495
+ 0587
496
+ 0589
497
+ 0590
498
+ 0591
499
+ 0592
500
+ 0594
501
+ 0595
502
+ 0596
503
+ 0597
504
+ 0598
505
+ 0600
506
+ 0601
507
+ 0602
508
+ 0603
509
+ 0604
510
+ 0605
511
+ 0606
512
+ 0608
513
+ 0609
514
+ 0610
515
+ 0611
516
+ 0612
517
+ 0613
518
+ 0614
519
+ 0615
520
+ 0616
521
+ 0617
522
+ 0618
523
+ 0619
524
+ 0620
525
+ 0624
526
+ 0625
527
+ 0626
528
+ 0627
529
+ 0628
530
+ 0629
531
+ 0630
532
+ 0631
533
+ 0634
534
+ 0635
535
+ 0636
536
+ 0637
537
+ 0638
538
+ 0639
539
+ 0640
540
+ 0641
541
+ 0642
542
+ 0643
543
+ 0644
544
+ 0645
545
+ 0646
546
+ 0647
547
+ 0648
548
+ 0650
549
+ 0651
550
+ 0652
551
+ 0654
552
+ 0655
553
+ 0656
554
+ 0658
555
+ 0659
556
+ 0660
557
+ 0661
558
+ 0662
559
+ 0663
560
+ 0664
561
+ 0665
562
+ 0666
563
+ 0667
564
+ 0669
565
+ 0670
566
+ 0671
567
+ 0672
568
+ 0673
569
+ 0674
570
+ 0675
571
+ 0676
572
+ 0677
573
+ 0678
574
+ 0679
575
+ 0680
576
+ 0681
577
+ 0682
578
+ 0683
579
+ 0684
580
+ 0685
581
+ 0686
582
+ 0687
583
+ 0689
584
+ 0690
585
+ 0691
586
+ 0692
587
+ 0693
588
+ 0694
589
+ 0695
590
+ 0696
591
+ 0697
592
+ 0698
593
+ 0699
594
+ 0700
595
+ 0701
596
+ 0702
597
+ 0703
598
+ 0704
599
+ 0705
600
+ 0706
601
+ 0707
602
+ 0708
603
+ 0709
604
+ 0710
605
+ 0711
606
+ 0712
607
+ 0713
608
+ 0714
609
+ 0715
610
+ 0716
611
+ 0717
612
+ 0718
613
+ 0719
614
+ 0720
615
+ 0721
616
+ 0723
617
+ 0724
618
+ 0725
619
+ 0726
620
+ 0727
621
+ 0729
622
+ 0730
623
+ 0731
624
+ 0732
625
+ 0733
626
+ 0734
627
+ 0735
628
+ 0736
629
+ 0738
630
+ 0740
631
+ 0741
632
+ 0742
633
+ 0743
634
+ 0744
635
+ 0746
636
+ 0747
637
+ 0748
638
+ 0749
639
+ 0750
640
+ 0752
641
+ 0753
642
+ 0754
643
+ 0755
644
+ 0756
645
+ 0757
646
+ 0758
647
+ 0759
648
+ 0760
649
+ 0762
650
+ 0763
651
+ 0764
652
+ 0765
653
+ 0766
654
+ 0767
655
+ 0768
656
+ 0770
657
+ 0771
658
+ 0772
659
+ 0773
660
+ 0774
661
+ 0775
662
+ 0776
663
+ 0777
664
+ 0778
665
+ 0779
666
+ 0780
667
+ 0781
668
+ 0782
669
+ 0783
670
+ 0784
671
+ 0786
672
+ 0787
673
+ 0788
674
+ 0789
675
+ 0790
676
+ 0791
677
+ 0792
678
+ 0793
679
+ 0794
680
+ 0795
681
+ 0796
682
+ 0797
683
+ 0798
684
+ 0800
685
+ 0801
686
+ 0804
687
+ 0806
688
+ 0808
689
+ 0809
690
+ 0811
691
+ 0812
692
+ 0813
693
+ 0814
694
+ 0815
695
+ 0816
696
+ 0817
697
+ 0819
698
+ 0823
699
+ 0824
700
+ 0825
701
+ 0827
702
+ 0828
703
+ 0829
704
+ 0830
705
+ 0831
706
+ 0832
707
+ 0833
708
+ 0834
709
+ 0835
710
+ 0836
711
+ 0837
712
+ 0840
713
+ 0841
714
+ 0842
715
+ 0847
716
+ 0848
717
+ 0850
718
+ 0851
719
+ 0852
720
+ 0853
721
+ 0854
722
+ 0855
723
+ 0856
724
+ 0857
725
+ 0858
726
+ 0859
727
+ 0860
728
+ 0861
729
+ 0862
730
+ 0864
731
+ 0867
732
+ 0868
733
+ 0869
734
+ 0870
735
+ 0871
736
+ 0872
737
+ 0873
738
+ 0874
739
+ 0876
740
+ 0877
741
+ 0878
742
+ 0879
743
+ 0880
744
+ 0881
745
+ 0882
746
+ 0883
747
+ 0885
748
+ 0886
749
+ 0887
750
+ 0889
751
+ 0890
752
+ 0891
753
+ 0892
754
+ 0893
755
+ 0894
756
+ 0895
757
+ 0896
758
+ 0899
759
+ 0900
760
+ 0901
761
+ 0902
762
+ 0903
763
+ 0904
764
+ 0905
765
+ 0906
766
+ 0907
767
+ 0908
768
+ 0909
769
+ 0910
770
+ 0911
771
+ 0912
772
+ 0913
773
+ 0914
774
+ 0915
775
+ 0916
776
+ 0917
777
+ 0918
778
+ 0919
779
+ 0921
780
+ 0922
781
+ 0923
782
+ 0924
783
+ 0925
784
+ 0926
785
+ 0927
786
+ 0929
787
+ 0930
788
+ 0931
789
+ 0932
790
+ 0933
791
+ 0934
792
+ 0935
793
+ 0936
794
+ 0937
795
+ 0939
796
+ 0940
797
+ 0941
798
+ 0942
799
+ 0945
800
+ 0946
801
+ 0947
802
+ 0948
803
+ 0949
804
+ 0950
805
+ 0951
806
+ 0952
807
+ 0953
808
+ 0954
809
+ 0955
810
+ 0956
811
+ 0957
812
+ 0958
813
+ 0960
814
+ 0962
815
+ 0963
816
+ 0964
817
+ 0965
818
+ 0966
819
+ 0967
820
+ 0968
821
+ 0969
822
+ 0970
823
+ 0971
824
+ 0973
825
+ 0974
826
+ 0976
827
+ 0977
828
+ 0978
829
+ 0979
830
+ 0980
831
+ 0981
832
+ 0982
833
+ 0983
834
+ 0984
835
+ 0985
836
+ 0986
837
+ 0987
838
+ 0988
839
+ 0989
840
+ 0990
841
+ 0991
842
+ 0992
843
+ 0993
844
+ 0994
845
+ 0995
846
+ 0996
847
+ 0997
848
+ 0998
849
+ 0999
850
+ 1000
851
+ 1001
852
+ 1002
853
+ 1003
854
+ 1005
855
+ 1006
856
+ 1008
857
+ 1009
858
+ 1010
859
+ 1011
860
+ 1012
861
+ 1013
862
+ 1014
863
+ 1016
864
+ 1017
865
+ 1018
866
+ 1019
867
+ 1020
868
+ 1021
869
+ 1022
870
+ 1023
871
+ 1024
872
+ 1025
873
+ 1026
874
+ 1028
875
+ 1029
876
+ 1030
877
+ 1032
878
+ 1035
879
+ 1036
880
+ 1037
881
+ 1038
882
+ 1039
883
+ 1040
884
+ 1041
885
+ 1042
886
+ 1043
887
+ 1044
888
+ 1045
889
+ 1046
890
+ 1047
891
+ 1048
892
+ 1049
893
+ 1050
894
+ 1052
895
+ 1053
896
+ 1054
897
+ 1055
898
+ 1056
899
+ 1057
900
+ 1058
901
+ 1061
902
+ 1062
903
+ 1063
904
+ 1064
905
+ 1065
906
+ 1066
907
+ 1067
908
+ 1068
909
+ 1069
910
+ 1072
911
+ 1075
912
+ 1076
913
+ 1077
914
+ 1078
915
+ 1079
916
+ 1081
917
+ 1082
918
+ 1083
919
+ 1084
920
+ 1087
921
+ 1088
922
+ 1089
923
+ 1090
924
+ 1096
925
+ 1097
926
+ 1098
927
+ 1099
928
+ 1101
929
+ 1102
930
+ 1103
931
+ 1104
932
+ 1105
933
+ 1106
934
+ 1107
935
+ 1108
936
+ 1109
937
+ 1110
938
+ 1111
939
+ 1112
940
+ 1113
941
+ 1115
942
+ 1116
943
+ 1117
944
+ 1118
945
+ 1119
946
+ 1120
947
+ 1121
948
+ 1122
949
+ 1123
950
+ 1124
951
+ 1128
952
+ 1129
953
+ 1130
954
+ 1131
955
+ 1132
956
+ 1134
957
+ 1137
958
+ 1138
959
+ 1139
960
+ 1140
961
+ 1141
962
+ 1142
963
+ 1143
964
+ 1144
965
+ 1145
966
+ 1146
967
+ 1147
968
+ 1148
969
+ 1149
970
+ 1150
971
+ 1151
972
+ 1152
973
+ 1153
974
+ 1154
975
+ 1155
976
+ 1156
977
+ 1157
978
+ 1158
979
+ 1159
980
+ 1160
981
+ 1161
982
+ 1162
983
+ 1163
984
+ 1165
985
+ 1168
986
+ 1169
987
+ 1170
988
+ 1171
989
+ 1172
990
+ 1173
991
+ 1174
992
+ 1175
993
+ 1176
994
+ 1177
995
+ 1178
996
+ 1179
997
+ 1180
998
+ 1181
999
+ 1182
1000
+ 1183
1001
+ 1184
1002
+ 1185
1003
+ 1186
1004
+ 1187
1005
+ 1188
1006
+ 1189
1007
+ 1190
1008
+ 1191
1009
+ 1192
1010
+ 1193
1011
+ 1194
1012
+ 1195
1013
+ 1196
1014
+ 1197
1015
+ 1199
1016
+ 1200
1017
+ 1201
1018
+ 1202
1019
+ 1203
1020
+ 1204
1021
+ 1206
1022
+ 1207
1023
+ 1208
1024
+ 1211
1025
+ 1212
1026
+ 1213
1027
+ 1215
1028
+ 1216
1029
+ 1217
1030
+ 1219
1031
+ 1220
1032
+ 1221
1033
+ 1222
1034
+ 1223
1035
+ 1224
1036
+ 1225
1037
+ 1226
1038
+ 1227
1039
+ 1228
1040
+ 1229
1041
+ 1230
1042
+ 1231
1043
+ 1232
1044
+ 1233
1045
+ 1234
1046
+ 1235
1047
+ 1237
1048
+ 1238
1049
+ 1239
1050
+ 1240
1051
+ 1241
1052
+ 1242
1053
+ 1245
1054
+ 1246
1055
+ 1248
1056
+ 1249
1057
+ 1252
1058
+ 1253
1059
+ 1255
1060
+ 1256
1061
+ 1257
1062
+ 1258
1063
+ 1259
1064
+ 1260
1065
+ 1261
1066
+ 1262
1067
+ 1263
1068
+ 1264
1069
+ 1265
1070
+ 1266
1071
+ 1267
1072
+ 1268
1073
+ 1269
1074
+ 1270
1075
+ 1271
1076
+ 1272
1077
+ 1273
1078
+ 1274
1079
+ 1275
1080
+ 1277
1081
+ 1278
1082
+ 1279
1083
+ 1280
1084
+ 1281
1085
+ 1282
1086
+ 1283
1087
+ 1284
1088
+ 1287
1089
+ 1288
1090
+ 1289
1091
+ 1290
1092
+ 1291
1093
+ 1293
1094
+ 1294
1095
+ 1295
1096
+ 1296
1097
+ 1297
1098
+ 1299
1099
+ 1300
1100
+ 1301
1101
+ 1302
1102
+ 1303
1103
+ 1304
1104
+ 1305
1105
+ 1306
1106
+ 1307
1107
+ 1308
1108
+ 1309
1109
+ 1310
1110
+ 1311
1111
+ 1312
1112
+ 1313
1113
+ 1316
1114
+ 1317
1115
+ 1318
1116
+ 1319
1117
+ 1320
1118
+ 1321
1119
+ 1322
1120
+ 1323
1121
+ 1324
1122
+ 1325
1123
+ 1326
1124
+ 1327
1125
+ 1328
1126
+ 1329
1127
+ 1331
1128
+ 1332
1129
+ 1333
1130
+ 1334
1131
+ 1335
1132
+ 1336
1133
+ 1337
1134
+ 1338
1135
+ 1339
1136
+ 1340
1137
+ 1341
1138
+ 1342
1139
+ 1343
1140
+ 1344
1141
+ 1345
1142
+ 1346
1143
+ 1347
1144
+ 1348
1145
+ 1350
1146
+ 1351
1147
+ 1352
1148
+ 1353
1149
+ 1354
1150
+ 1355
1151
+ 1356
1152
+ 1357
1153
+ 1359
1154
+ 1360
1155
+ 1361
1156
+ 1362
1157
+ 1363
1158
+ 1364
1159
+ 1365
1160
+ 1366
1161
+ 1367
1162
+ 1368
1163
+ 1369
1164
+ 1370
1165
+ 1371
1166
+ 1372
1167
+ 1373
1168
+ 1374
1169
+ 1375
1170
+ 1376
1171
+ 1378
1172
+ 1379
1173
+ 1380
1174
+ 1381
1175
+ 1382
1176
+ 1383
1177
+ 1385
1178
+ 1386
1179
+ 1387
1180
+ 1389
1181
+ 1390
1182
+ 1391
1183
+ 1392
1184
+ 1393
1185
+ 1394
1186
+ 1395
1187
+ 1396
1188
+ 1397
1189
+ 1398
1190
+ 1399
1191
+ 1400
1192
+ 1402
1193
+ 1403
1194
+ 1405
1195
+ 1406
1196
+ 1409
1197
+ 1410
1198
+ 1411
1199
+ 1412
1200
+ 1413
1201
+ 1414
1202
+ 1415
1203
+ 1416
1204
+ 1417
1205
+ 1418
1206
+ 1420
1207
+ 1421
1208
+ 1422
1209
+ 1423
1210
+ 1424
1211
+ 1425
1212
+ 1426
1213
+ 1427
1214
+ 1428
1215
+ 1429
1216
+ 1430
1217
+ 1431
1218
+ 1432
1219
+ 1433
1220
+ 1434
1221
+ 1435
1222
+ 1436
1223
+ 1437
1224
+ 1438
1225
+ 1439
1226
+ 1441
1227
+ 1442
1228
+ 1443
1229
+ 1444
1230
+ 1445
1231
+ 1446
1232
+ 1447
1233
+ 1448
1234
+ 1449
1235
+ 1450
1236
+ 1451
1237
+ 1452
1238
+ 1453
1239
+ 1454
1240
+ 1455
1241
+ 1456
1242
+ 1457
1243
+ 1458
1244
+ 1459
1245
+ 1460
1246
+ 1461
1247
+ 1462
1248
+ 1465
1249
+ 1466
1250
+ 1467
1251
+ 1468
1252
+ 1469
1253
+ 1470
1254
+ 1471
1255
+ 1472
1256
+ 1473
1257
+ 1474
1258
+ 1475
1259
+ 1476
1260
+ 1477
1261
+ 1478
1262
+ 1479
1263
+ 1480
1264
+ 1481
1265
+ 1482
1266
+ 1483
1267
+ 1484
1268
+ 1485
1269
+ 1486
1270
+ 1487
1271
+ 1488
1272
+ 1489
1273
+ 1490
1274
+ 1491
1275
+ 1493
1276
+ 1494
1277
+ 1495
1278
+ 1496
1279
+ 1497
1280
+ 1499
1281
+ 1500
1282
+ 1501
1283
+ 1502
1284
+ 1503
1285
+ 1504
1286
+ 1505
1287
+ 1506
1288
+ 1507
1289
+ 1508
1290
+ 1509
1291
+ 1510
1292
+ 1511
1293
+ 1512
1294
+ 1513
1295
+ 1514
1296
+ 1515
1297
+ 1516
1298
+ 1519
1299
+ 1520
1300
+ 1521
1301
+ 1522
1302
+ 1523
1303
+ 1524
1304
+ 1525
1305
+ 1526
1306
+ 1527
1307
+ 1528
1308
+ 1529
1309
+ 1530
1310
+ 1531
1311
+ 1532
1312
+ 1534
1313
+ 1535
1314
+ 1536
1315
+ 1537
1316
+ 1538
1317
+ 1539
1318
+ 1540
1319
+ 1541
1320
+ 1542
1321
+ 1543
1322
+ 1544
1323
+ 1545
1324
+ 1546
1325
+ 1547
1326
+ 1548
1327
+ 1549
1328
+ 1550
1329
+ 1551
1330
+ 1552
1331
+ 1553
1332
+ 1554
1333
+ 1555
1334
+ 1556
1335
+ 1557
1336
+ 1558
1337
+ 1560
1338
+ 1561
1339
+ 1562
1340
+ 1563
1341
+ 1565
1342
+ 1566
1343
+ 1567
1344
+ 1568
1345
+ 1569
1346
+ 1570
1347
+ 1571
1348
+ 1572
1349
+ 1573
1350
+ 1574
1351
+ 1575
1352
+ 1576
1353
+ 1577
1354
+ 1578
1355
+ 1579
1356
+ 1580
1357
+ 1581
1358
+ 1582
1359
+ 1583
1360
+ 1584
1361
+ 1585
1362
+ 1586
1363
+ 1587
1364
+ 1588
1365
+ 1589
1366
+ 1590
1367
+ 1591
1368
+ 1592
1369
+ 1593
1370
+ 1594
1371
+ 1595
1372
+ 1596
1373
+ 1597
1374
+ 1598
1375
+ 1599
1376
+ 1600
1377
+ 1601
1378
+ 1602
1379
+ 1603
1380
+ 1604
1381
+ 1605
1382
+ 1606
1383
+ 1608
1384
+ 1609
1385
+ 1611
1386
+ 1612
1387
+ 1613
1388
+ 1614
1389
+ 1615
1390
+ 1616
1391
+ 1617
1392
+ 1618
1393
+ 1619
1394
+ 1620
1395
+ 1621
1396
+ 1622
1397
+ 1623
1398
+ 1624
1399
+ 1625
1400
+ 1626
1401
+ 1627
1402
+ 1628
1403
+ 1629
1404
+ 1630
1405
+ 1631
1406
+ 1632
1407
+ 1633
1408
+ 1634
1409
+ 1635
1410
+ 1636
1411
+ 1637
1412
+ 1638
1413
+ 1639
1414
+ 1640
1415
+ 1641
1416
+ 1642
1417
+ 1643
1418
+ 1644
1419
+ 1645
1420
+ 1646
1421
+ 1649
1422
+ 1650
1423
+ 1651
1424
+ 1652
1425
+ 1653
1426
+ 1654
1427
+ 1655
1428
+ 1656
1429
+ 1657
1430
+ 1658
1431
+ 1659
1432
+ 1660
1433
+ 1661
1434
+ 1662
1435
+ 1663
1436
+ 1664
1437
+ 1665
1438
+ 1666
1439
+ 1667
1440
+ 1668
1441
+ 1669
1442
+ 1670
1443
+ 1671
1444
+ 1672
1445
+ 1673
1446
+ 1675
1447
+ 1676
1448
+ 1677
1449
+ 1678
1450
+ 1679
1451
+ 1680
1452
+ 1681
1453
+ 1682
1454
+ 1683
1455
+ 1684
1456
+ 1685
1457
+ 1686
1458
+ 1687
1459
+ 1688
1460
+ 1689
1461
+ 1690
1462
+ 1691
1463
+ 1692
1464
+ 1693
1465
+ 1694
1466
+ 1695
1467
+ 1696
1468
+ 1697
1469
+ 1698
1470
+ 1699
1471
+ 1700
1472
+ 1701
1473
+ 1702
1474
+ 1703
1475
+ 1704
1476
+ 1705
1477
+ 1706
1478
+ 1707
1479
+ 1708
1480
+ 1709
1481
+ 1710
1482
+ 1712
1483
+ 1713
1484
+ 1714
1485
+ 1715
1486
+ 1716
1487
+ 1717
1488
+ 1718
1489
+ 1719
1490
+ 1720
1491
+ 1721
1492
+ 1722
1493
+ 1723
1494
+ 1724
1495
+ 1725
1496
+ 1726
1497
+ 1727
1498
+ 1728
1499
+ 1729
1500
+ 1730
1501
+ 1731
1502
+ 1732
1503
+ 1733
1504
+ 1734
1505
+ 1735
1506
+ 1736
1507
+ 1737
1508
+ 1738
1509
+ 1739
1510
+ 1740
1511
+ 1741
1512
+ 1742
1513
+ 1743
1514
+ 1744
1515
+ 1745
1516
+ 1746
1517
+ 1747
1518
+ 1748
1519
+ 1749
1520
+ 1750
1521
+ 1751
1522
+ 1752
1523
+ 1753
1524
+ 1754
1525
+ 1755
1526
+ 1756
1527
+ 1757
1528
+ 1758
1529
+ 1759
1530
+ 1760
1531
+ 1761
1532
+ 1762
1533
+ 1763
1534
+ 1764
1535
+ 1765
1536
+ 1766
1537
+ 1767
1538
+ 1768
1539
+ 1769
1540
+ 1770
1541
+ 1771
1542
+ 1772
1543
+ 1773
1544
+ 1774
1545
+ 1775
1546
+ 1776
1547
+ 1778
1548
+ 1779
1549
+ 1780
1550
+ 1781
1551
+ 1782
1552
+ 1784
1553
+ 1785
1554
+ 1786
1555
+ 1787
1556
+ 1788
1557
+ 1789
1558
+ 1790
1559
+ 1791
1560
+ 1792
1561
+ 1793
1562
+ 1794
1563
+ 1795
1564
+ 1796
1565
+ 1797
1566
+ 1798
1567
+ 1799
1568
+ 1800
1569
+ 1804
1570
+ 1806
1571
+ 1807
1572
+ 1808
1573
+ 1809
1574
+ 1811
1575
+ 1812
1576
+ 1813
1577
+ 1814
1578
+ 1815
1579
+ 1816
1580
+ 1817
1581
+ 1818
1582
+ 1819
1583
+ 1820
1584
+ 1821
1585
+ 1822
1586
+ 1823
1587
+ 1825
1588
+ 1826
1589
+ 1827
1590
+ 1828
1591
+ 1829
1592
+ 1831
1593
+ 1833
1594
+ 1834
1595
+ 1835
1596
+ 1836
1597
+ 1837
1598
+ 1838
1599
+ 1839
1600
+ 1840
1601
+ 1842
1602
+ 1843
1603
+ 1844
1604
+ 1845
1605
+ 1846
1606
+ 1847
1607
+ 1848
1608
+ 1849
1609
+ 1850
1610
+ 1851
1611
+ 1852
1612
+ 1853
1613
+ 1854
1614
+ 1855
1615
+ 1856
1616
+ 1857
1617
+ 1858
1618
+ 1859
1619
+ 1861
1620
+ 1862
1621
+ 1863
1622
+ 1864
1623
+ 1865
1624
+ 1866
1625
+ 1867
1626
+ 1868
1627
+ 1869
1628
+ 1870
1629
+ 1871
1630
+ 1872
1631
+ 1873
1632
+ 1874
1633
+ 1875
1634
+ 1876
1635
+ 1877
1636
+ 1879
1637
+ 1880
1638
+ 1881
1639
+ 1882
1640
+ 1886
1641
+ 1887
1642
+ 1889
1643
+ 1891
1644
+ 1892
1645
+ 1893
1646
+ 1894
1647
+ 1896
1648
+ 1897
1649
+ 1898
1650
+ 1899
1651
+ 1900
1652
+ 1901
1653
+ 1902
1654
+ 1903
1655
+ 1904
1656
+ 1905
1657
+ 1906
1658
+ 1907
1659
+ 1908
1660
+ 1909
1661
+ 1910
1662
+ 1911
1663
+ 1912
1664
+ 1913
1665
+ 1914
1666
+ 1915
1667
+ 1916
1668
+ 1917
1669
+ 1918
1670
+ 1919
1671
+ 1920
1672
+ 1921
1673
+ 1922
1674
+ 1923
1675
+ 1924
1676
+ 1925
1677
+ 1926
1678
+ 1927
1679
+ 1928
1680
+ 1929
1681
+ 1930
1682
+ 1931
1683
+ 1932
1684
+ 1933
1685
+ 1934
1686
+ 1935
1687
+ 1936
1688
+ 1937
1689
+ 1938
1690
+ 1939
1691
+ 1940
1692
+ 1941
1693
+ 1942
1694
+ 1943
1695
+ 1944
1696
+ 1945
1697
+ 1946
1698
+ 1947
1699
+ 1948
1700
+ 1949
1701
+ 1950
1702
+ 1952
1703
+ 1953
1704
+ 1954
1705
+ 1955
1706
+ 1956
1707
+ 1958
1708
+ 1959
1709
+ 1960
1710
+ 1961
1711
+ 1962
1712
+ 1963
1713
+ 1964
1714
+ 1965
1715
+ 1966
1716
+ 1967
1717
+ 1968
1718
+ 1969
1719
+ 1970
1720
+ 1971
1721
+ 1972
1722
+ 1973
1723
+ 1974
1724
+ 1975
1725
+ 1976
1726
+ 1977
1727
+ 1978
1728
+ 1980
1729
+ 1981
1730
+ 1982
1731
+ 1983
1732
+ 1984
1733
+ 1985
1734
+ 1986
1735
+ 1988
1736
+ 1989
1737
+ 1990
1738
+ 1991
1739
+ 1992
1740
+ 1993
1741
+ 1994
1742
+ 1995
1743
+ 1997
1744
+ 1998
1745
+ 1999
1746
+ 2000
1747
+ 2002
1748
+ 2003
1749
+ 2004
1750
+ 2005
1751
+ 2007
1752
+ 2008
1753
+ 2010
1754
+ 2011
1755
+ 2012
1756
+ 2013
1757
+ 2014
1758
+ 2015
1759
+ 2016
1760
+ 2017
1761
+ 2018
1762
+ 2019
1763
+ 2020
1764
+ 2021
1765
+ 2022
1766
+ 2024
1767
+ 2025
1768
+ 2026
1769
+ 2027
1770
+ 2028
1771
+ 2029
1772
+ 2030
1773
+ 2031
1774
+ 2032
1775
+ 2035
1776
+ 2036
1777
+ 2037
1778
+ 2038
1779
+ 2039
1780
+ 2040
1781
+ 2041
1782
+ 2042
1783
+ 2043
1784
+ 2045
1785
+ 2046
1786
+ 2047
1787
+ 2049
1788
+ 2050
1789
+ 2052
1790
+ 2053
1791
+ 2054
1792
+ 2056
1793
+ 2057
1794
+ 2059
1795
+ 2060
1796
+ 2061
1797
+ 2064
1798
+ 2066
1799
+ 2068
1800
+ 2069
1801
+ 2070
1802
+ 2071
1803
+ 2072
1804
+ 2073
1805
+ 2074
1806
+ 2075
1807
+ 2077
1808
+ 2078
1809
+ 2079
1810
+ 2080
1811
+ 2081
1812
+ 2082
1813
+ 2083
1814
+ 2084
1815
+ 2085
1816
+ 2086
1817
+ 2087
1818
+ 2088
1819
+ 2089
1820
+ 2090
1821
+ 2091
1822
+ 2092
1823
+ 2093
1824
+ 2094
1825
+ 2096
1826
+ 2097
1827
+ 2098
1828
+ 2099
1829
+ 2100
1830
+ 2101
1831
+ 2102
1832
+ 2103
1833
+ 2104
1834
+ 2105
1835
+ 2107
1836
+ 2108
1837
+ 2109
1838
+ 2111
1839
+ 2112
1840
+ 2113
1841
+ 2114
1842
+ 2115
1843
+ 2116
1844
+ 2117
1845
+ 2119
1846
+ 2120
1847
+ 2121
1848
+ 2122
1849
+ 2123
1850
+ 2124
1851
+ 2125
1852
+ 2126
1853
+ 2127
1854
+ 2128
1855
+ 2129
1856
+ 2130
1857
+ 2131
1858
+ 2132
1859
+ 2133
1860
+ 2134
1861
+ 2135
1862
+ 2136
1863
+ 2137
1864
+ 2138
1865
+ 2139
1866
+ 2140
1867
+ 2141
1868
+ 2143
1869
+ 2144
1870
+ 2145
1871
+ 2146
1872
+ 2147
1873
+ 2148
1874
+ 2149
1875
+ 2152
1876
+ 2153
1877
+ 2155
1878
+ 2158
1879
+ 2159
1880
+ 2160
1881
+ 2161
1882
+ 2162
1883
+ 2163
1884
+ 2164
1885
+ 2165
1886
+ 2166
1887
+ 2167
1888
+ 2168
1889
+ 2169
1890
+ 2170
1891
+ 2171
1892
+ 2174
1893
+ 2176
1894
+ 2177
1895
+ 2178
1896
+ 2179
1897
+ 2180
1898
+ 2181
1899
+ 2182
1900
+ 2183
1901
+ 2184
1902
+ 2186
1903
+ 2187
1904
+ 2188
1905
+ 2189
1906
+ 2190
1907
+ 2191
1908
+ 2192
1909
+ 2193
1910
+ 2195
1911
+ 2197
1912
+ 2198
1913
+ 2199
1914
+ 2200
1915
+ 2201
1916
+ 2202
1917
+ 2203
1918
+ 2204
1919
+ 2205
1920
+ 2206
1921
+ 2207
1922
+ 2208
1923
+ 2209
1924
+ 2210
1925
+ 2211
1926
+ 2212
1927
+ 2213
1928
+ 2214
1929
+ 2215
1930
+ 2216
1931
+ 2217
1932
+ 2218
1933
+ 2219
1934
+ 2220
1935
+ 2221
1936
+ 2222
1937
+ 2223
1938
+ 2225
1939
+ 2226
1940
+ 2227
1941
+ 2228
1942
+ 2229
1943
+ 2230
1944
+ 2231
1945
+ 2232
1946
+ 2233
1947
+ 2234
1948
+ 2235
1949
+ 2236
1950
+ 2238
1951
+ 2239
1952
+ 2240
1953
+ 2241
1954
+ 2242
1955
+ 2243
1956
+ 2244
1957
+ 2245
1958
+ 2246
1959
+ 2247
1960
+ 2248
1961
+ 2249
1962
+ 2250
1963
+ 2251
1964
+ 2252
1965
+ 2253
1966
+ 2255
1967
+ 2256
1968
+ 2257
1969
+ 2258
1970
+ 2259
1971
+ 2260
1972
+ 2261
1973
+ 2263
1974
+ 2264
1975
+ 2265
1976
+ 2267
1977
+ 2268
1978
+ 2269
1979
+ 2270
1980
+ 2271
1981
+ 2272
1982
+ 2273
1983
+ 2274
1984
+ 2275
1985
+ 2276
1986
+ 2277
1987
+ 2278
1988
+ 2279
1989
+ 2280
1990
+ 2281
1991
+ 2282
1992
+ 2283
1993
+ 2284
1994
+ 2285
1995
+ 2286
1996
+ 2287
1997
+ 2288
1998
+ 2290
1999
+ 2291
2000
+ 2292
2001
+ 2293
2002
+ 2294
2003
+ 2295
2004
+ 2297
2005
+ 2299
2006
+ 2300
2007
+ 2301
2008
+ 2302
2009
+ 2303
2010
+ 2304
2011
+ 2305
2012
+ 2311
2013
+ 2312
2014
+ 2313
2015
+ 2314
2016
+ 2315
2017
+ 2316
2018
+ 2317
2019
+ 2318
2020
+ 2319
2021
+ 2320
2022
+ 2322
2023
+ 2324
2024
+ 2325
2025
+ 2326
2026
+ 2329
2027
+ 2331
2028
+ 2332
2029
+ 2334
2030
+ 2335
2031
+ 2336
2032
+ 2337
2033
+ 2338
2034
+ 2339
2035
+ 2340
2036
+ 2341
2037
+ 2342
2038
+ 2343
2039
+ 2344
2040
+ 2345
2041
+ 2347
2042
+ 2349
2043
+ 2350
2044
+ 2351
2045
+ 2352
2046
+ 2353
2047
+ 2355
2048
+ 2356
2049
+ 2358
2050
+ 2359
2051
+ 2360
2052
+ 2361
2053
+ 2362
2054
+ 2363
2055
+ 2364
2056
+ 2365
2057
+ 2367
2058
+ 2368
2059
+ 2369
2060
+ 2370
2061
+ 2372
2062
+ 2373
2063
+ 2374
2064
+ 2375
2065
+ 2376
2066
+ 2377
2067
+ 2378
2068
+ 2379
2069
+ 2380
2070
+ 2381
2071
+ 2382
2072
+ 2383
2073
+ 2384
2074
+ 2386
2075
+ 2389
2076
+ 2390
2077
+ 2391
2078
+ 2392
2079
+ 2393
2080
+ 2394
2081
+ 2395
2082
+ 2396
2083
+ 2397
2084
+ 2398
2085
+ 2399
2086
+ 2400
2087
+ 2401
2088
+ 2402
2089
+ 2403
2090
+ 2404
2091
+ 2406
2092
+ 2407
2093
+ 2408
2094
+ 2409
2095
+ 2410
2096
+ 2411
2097
+ 2412
2098
+ 2413
2099
+ 2414
2100
+ 2416
2101
+ 2417
2102
+ 2418
2103
+ 2419
2104
+ 2420
2105
+ 2421
2106
+ 2422
2107
+ 2423
2108
+ 2424
2109
+ 2425
2110
+ 2426
2111
+ 2427
2112
+ 2428
2113
+ 2429
2114
+ 2431
2115
+ 2432
2116
+ 2433
2117
+ 2434
2118
+ 2437
2119
+ 2439
2120
+ 2440
2121
+ 2441
2122
+ 2442
2123
+ 2443
2124
+ 2444
2125
+ 2445
2126
+ 2446
2127
+ 2448
2128
+ 2449
2129
+ 2450
2130
+ 2451
2131
+ 2452
2132
+ 2453
2133
+ 2454
2134
+ 2455
2135
+ 2456
2136
+ 2457
2137
+ 2460
2138
+ 2461
2139
+ 2462
2140
+ 2463
2141
+ 2464
2142
+ 2465
2143
+ 2466
2144
+ 2467
2145
+ 2468
2146
+ 2469
2147
+ 2470
2148
+ 2472
2149
+ 2474
2150
+ 2475
2151
+ 2479
2152
+ 2480
2153
+ 2481
2154
+ 2482
2155
+ 2483
2156
+ 2484
2157
+ 2485
2158
+ 2486
2159
+ 2487
2160
+ 2488
2161
+ 2489
2162
+ 2490
2163
+ 2491
2164
+ 2492
2165
+ 2493
2166
+ 2494
2167
+ 2495
2168
+ 2496
2169
+ 2497
2170
+ 2499
2171
+ 2500
2172
+ 2501
2173
+ 2502
2174
+ 2503
2175
+ 2504
2176
+ 2505
2177
+ 2506
2178
+ 2507
2179
+ 2508
2180
+ 2509
2181
+ 2510
2182
+ 2511
2183
+ 2512
2184
+ 2513
2185
+ 2514
2186
+ 2515
2187
+ 2516
2188
+ 2517
2189
+ 2518
2190
+ 2519
2191
+ 2520
2192
+ 2521
2193
+ 2522
2194
+ 2523
2195
+ 2524
2196
+ 2525
2197
+ 2530
2198
+ 2534
2199
+ 2536
2200
+ 2537
2201
+ 2539
2202
+ 2540
2203
+ 2541
2204
+ 2542
2205
+ 2543
2206
+ 2544
2207
+ 2545
2208
+ 2546
2209
+ 2548
2210
+ 2549
2211
+ 2550
2212
+ 2551
2213
+ 2553
2214
+ 2554
2215
+ 2555
2216
+ 2556
2217
+ 2559
2218
+ 2560
2219
+ 2562
2220
+ 2567
2221
+ 2570
2222
+ 2573
2223
+ 2575
2224
+ 2576
2225
+ 2578
2226
+ 2579
2227
+ 2582
2228
+ 2585
2229
+ 2588
2230
+ 2590
2231
+ 2593
2232
+ 2594
2233
+ 2595
2234
+ 2596
2235
+ 2597
2236
+ 2598
2237
+ 2600
2238
+ 2601
2239
+ 2602
2240
+ 2603
2241
+ 2604
2242
+ 2605
2243
+ 2606
2244
+ 2607
2245
+ 2612
2246
+ 2613
2247
+ 2615
2248
+ 2616
2249
+ 2617
2250
+ 2618
2251
+ 2619
2252
+ 2620
2253
+ 2622
2254
+ 2623
2255
+ 2624
2256
+ 2625
2257
+ 2629
2258
+ 2630
2259
+ 2633
2260
+ 2634
2261
+ 2635
2262
+ 2637
2263
+ 2638
2264
+ 2639
2265
+ 2641
2266
+ 2643
2267
+ 2644
2268
+ 2646
2269
+ 2648
2270
+ 2649
2271
+ 2650
2272
+ 2652
2273
+ 2654
2274
+ 2656
2275
+ 2659
2276
+ 2661
2277
+ 2662
2278
+ 2663
2279
+ 2664
2280
+ 2665
2281
+ 2667
2282
+ 2669
2283
+ 2670
2284
+ 2671
2285
+ 2672
2286
+ 2674
2287
+ 2675
2288
+ 2677
2289
+ 2679
2290
+ 2680
2291
+ 2682
2292
+ 2684
2293
+ 2685
2294
+ 2687
2295
+ 2689
2296
+ 2691
2297
+ 2692
2298
+ 2693
2299
+ 2694
2300
+ 2695
2301
+ 2696
2302
+ 2697
2303
+ 2698
2304
+ 2699
2305
+ 2700
2306
+ 2701
2307
+ 2702
2308
+ 2703
2309
+ 2705
2310
+ 2706
2311
+ 2707
2312
+ 2710
2313
+ 2713
2314
+ 2714
2315
+ 2715
2316
+ 2717
2317
+ 2718
2318
+ 2720
2319
+ 2721
2320
+ 2722
2321
+ 2726
2322
+ 2727
2323
+ 2729
2324
+ 2733
2325
+ 2734
2326
+ 2737
2327
+ 2739
2328
+ 2740
2329
+ 2741
2330
+ 2742
2331
+ 2743
2332
+ 2745
2333
+ 2747
2334
+ 2748
2335
+ 2749
2336
+ 2755
2337
+ 2756
2338
+ 2757
2339
+ 2758
2340
+ 2759
2341
+ 2761
2342
+ 2762
2343
+ 2763
2344
+ 2765
2345
+ 2767
2346
+ 2768
2347
+ 2769
2348
+ 2770
2349
+ 2773
2350
+ 2775
2351
+ 2776
2352
+ 2780
2353
+ 2781
2354
+ 2782
2355
+ 2785
2356
+ 2787
2357
+ 2790
2358
+ 2791
2359
+ 2793
2360
+ 2794
2361
+ 2795
2362
+ 2796
2363
+ 2797
2364
+ 2798
2365
+ 2800
2366
+ 2801
2367
+ 2802
2368
+ 2803
2369
+ 2806
2370
+ 2808
2371
+ 2810
2372
+ 2812
2373
+ 2813
2374
+ 2814
2375
+ 2815
2376
+ 2816
2377
+ 2818
2378
+ 2819
2379
+ 2820
2380
+ 2821
2381
+ 2822
2382
+ 2823
2383
+ 2825
2384
+ 2827
2385
+ 2828
2386
+ 2829
2387
+ 2830
2388
+ 2831
2389
+ 2832
2390
+ 2833
2391
+ 2834
2392
+ 2835
2393
+ 2837
2394
+ 2838
2395
+ 2842
2396
+ 2843
2397
+ 2844
2398
+ 2845
2399
+ 2847
2400
+ 2848
2401
+ 2849
2402
+ 2850
2403
+ 2851
2404
+ 2852
2405
+ 2853
2406
+ 2854
2407
+ 2856
2408
+ 2857
2409
+ 2858
2410
+ 2860
2411
+ 2861
2412
+ 2862
2413
+ 2863
2414
+ 2864
2415
+ 2865
2416
+ 2866
2417
+ 2868
2418
+ 2869
2419
+ 2871
2420
+ 2874
2421
+ 2875
2422
+ 2877
2423
+ 2879
2424
+ 2881
2425
+ 2882
2426
+ 2884
2427
+ 2885
2428
+ 2887
2429
+ 2888
2430
+ 2889
2431
+ 2890
2432
+ 2891
2433
+ 2893
2434
+ 2894
2435
+ 2895
2436
+ 2896
2437
+ 2900
2438
+ 2902
2439
+ 2903
2440
+ 2904
2441
+ 2905
2442
+ 2906
2443
+ 2907
2444
+ 2908
2445
+ 2911
2446
+ 2912
2447
+ 2914
2448
+ 2915
2449
+ 2916
2450
+ 2917
2451
+ 2918
2452
+ 2919
2453
+ 2921
2454
+ 2923
2455
+ 2924
2456
+ 2925
2457
+ 2926
2458
+ 2928
2459
+ 2929
2460
+ 2930
2461
+ 2931
2462
+ 2932
2463
+ 2933
2464
+ 2934
2465
+ 2935
2466
+ 2936
2467
+ 2938
2468
+ 2939
2469
+ 2940
2470
+ 2941
2471
+ 2944
2472
+ 2945
2473
+ 2946
2474
+ 2947
2475
+ 2952
2476
+ 2954
2477
+ 2957
2478
+ 2958
2479
+ 2960
2480
+ 2962
2481
+ 2963
2482
+ 2966
2483
+ 2967
2484
+ 2968
2485
+ 2969
2486
+ 2970
2487
+ 2971
2488
+ 2972
2489
+ 2973
2490
+ 2974
2491
+ 2975
2492
+ 2976
2493
+ 2981
2494
+ 2982
2495
+ 2984
2496
+ 2985
2497
+ 2986
2498
+ 2988
2499
+ 2993
2500
+ 2994
2501
+ 2996
2502
+ 2998
2503
+ 2999
2504
+ 3000
2505
+ 3001
2506
+ 3002
2507
+ 3004
2508
+ 3006
2509
+ 3007
2510
+ 3008
2511
+ 3010
2512
+ 3015
2513
+ 3016
2514
+ 3017
2515
+ 3018
2516
+ 3019
2517
+ 3020
2518
+ 3021
2519
+ 3022
2520
+ 3024
2521
+ 3026
2522
+ 3027
2523
+ 3029
2524
+ 3033
2525
+ 3034
2526
+ 3035
2527
+ 3036
2528
+ 3037
2529
+ 3040
2530
+ 3041
2531
+ 3042
2532
+ 3043
2533
+ 3044
2534
+ 3045
2535
+ 3046
2536
+ 3047
2537
+ 3048
2538
+ 3049
2539
+ 3050
2540
+ 3051
2541
+ 3053
2542
+ 3056
2543
+ 3057
2544
+ 3058
2545
+ 3059
2546
+ 3060
2547
+ 3061
2548
+ 3063
2549
+ 3065
2550
+ 3066
2551
+ 3068
2552
+ 3069
2553
+ 3070
2554
+ 3074
2555
+ 3077
2556
+ 3078
2557
+ 3079
2558
+ 3080
2559
+ 3081
2560
+ 3082
2561
+ 3083
2562
+ 3084
2563
+ 3085
2564
+ 3086
2565
+ 3087
2566
+ 3094
2567
+ 3095
2568
+ 3097
2569
+ 3098
2570
+ 3100
2571
+ 3101
2572
+ 3102
2573
+ 3103
2574
+ 3105
2575
+ 3106
2576
+ 3108
2577
+ 3109
2578
+ 3110
2579
+ 3111
2580
+ 3112
2581
+ 3113
2582
+ 3114
2583
+ 3115
2584
+ 3116
2585
+ 3117
2586
+ 3118
2587
+ 3119
2588
+ 3121
2589
+ 3123
2590
+ 3124
2591
+ 3125
2592
+ 3127
2593
+ 3128
2594
+ 3130
2595
+ 3131
2596
+ 3133
2597
+ 3134
2598
+ 3136
2599
+ 3137
2600
+ 3140
2601
+ 3142
2602
+ 3143
2603
+ 3144
2604
+ 3145
2605
+ 3147
2606
+ 3148
2607
+ 3152
2608
+ 3153
2609
+ 3155
2610
+ 3156
2611
+ 3157
2612
+ 3159
2613
+ 3160
2614
+ 3162
2615
+ 3164
2616
+ 3165
2617
+ 3166
2618
+ 3168
2619
+ 3170
2620
+ 3171
2621
+ 3173
2622
+ 3176
2623
+ 3178
2624
+ 3179
2625
+ 3180
2626
+ 3181
2627
+ 3184
2628
+ 3187
2629
+ 3188
2630
+ 3190
2631
+ 3191
2632
+ 3194
2633
+ 3197
2634
+ 3199
2635
+ 3200
2636
+ 3201
2637
+ 3202
2638
+ 3204
2639
+ 3209
2640
+ 3210
2641
+ 3211
2642
+ 3212
2643
+ 3214
2644
+ 3215
2645
+ 3217
2646
+ 3218
2647
+ 3219
2648
+ 3221
2649
+ 3222
2650
+ 3223
2651
+ 3224
2652
+ 3226
2653
+ 3228
2654
+ 3229
2655
+ 3230
2656
+ 3232
2657
+ 3237
2658
+ 3238
2659
+ 3239
2660
+ 3240
2661
+ 3241
2662
+ 3242
2663
+ 3243
2664
+ 3245
2665
+ 3247
2666
+ 3248
2667
+ 3250
2668
+ 3251
2669
+ 3252
2670
+ 3253
2671
+ 3254
2672
+ 3257
2673
+ 3258
2674
+ 3259
2675
+ 3260
2676
+ 3262
2677
+ 3264
2678
+ 3266
2679
+ 3267
2680
+ 3269
2681
+ 3270
2682
+ 3275
2683
+ 3278
2684
+ 3279
2685
+ 3280
2686
+ 3282
2687
+ 3284
2688
+ 3285
2689
+ 3286
2690
+ 3287
2691
+ 3288
2692
+ 3289
2693
+ 3292
2694
+ 3293
2695
+ 3295
2696
+ 3296
2697
+ 3298
2698
+ 3299
2699
+ 3300
2700
+ 3301
2701
+ 3302
2702
+ 3303
2703
+ 3304
2704
+ 3309
2705
+ 3311
2706
+ 3312
2707
+ 3315
2708
+ 3316
2709
+ 3317
2710
+ 3318
2711
+ 3319
2712
+ 3322
2713
+ 3325
2714
+ 3328
2715
+ 3332
2716
+ 3334
2717
+ 3339
2718
+ 3340
2719
+ 3342
2720
+ 3346
2721
+ 3348
2722
+ 3349
2723
+ 3350
2724
+ 3351
2725
+ 3352
2726
+ 3354
2727
+ 3355
2728
+ 3357
2729
+ 3358
2730
+ 3361
2731
+ 3363
2732
+ 3364
2733
+ 3365
2734
+ 3366
2735
+ 3367
2736
+ 3368
2737
+ 3369
2738
+ 3370
2739
+ 3373
2740
+ 3374
2741
+ 3377
2742
+ 3378
2743
+ 3380
2744
+ 3381
2745
+ 3382
2746
+ 3383
2747
+ 3384
2748
+ 3385
2749
+ 3386
2750
+ 3391
2751
+ 3393
2752
+ 3395
2753
+ 3396
2754
+ 3397
2755
+ 3398
2756
+ 3399
2757
+ 3400
2758
+ 3401
2759
+ 3402
2760
+ 3403
2761
+ 3404
2762
+ 3405
2763
+ 3407
2764
+ 3408
2765
+ 3409
2766
+ 3410
2767
+ 3411
2768
+ 3415
2769
+ 3416
2770
+ 3418
2771
+ 3420
2772
+ 3422
2773
+ 3423
2774
+ 3424
2775
+ 3427
2776
+ 3428
2777
+ 3431
2778
+ 3433
2779
+ 3435
2780
+ 3437
2781
+ 3438
2782
+ 3439
2783
+ 3440
2784
+ 3441
2785
+ 3442
2786
+ 3443
2787
+ 3444
2788
+ 3446
2789
+ 3449
2790
+ 3450
2791
+ 3451
2792
+ 3452
2793
+ 3453
2794
+ 3454
2795
+ 3455
2796
+ 3456
2797
+ 3457
2798
+ 3460
2799
+ 3462
2800
+ 3463
2801
+ 3464
2802
+ 3465
2803
+ 3466
2804
+ 3467
2805
+ 3468
2806
+ 3470
2807
+ 3475
2808
+ 3476
2809
+ 3477
2810
+ 3483
2811
+ 3484
2812
+ 3486
2813
+ 3487
2814
+ 3489
2815
+ 3492
2816
+ 3496
2817
+ 3497
2818
+ 3498
2819
+ 3500
2820
+ 3501
2821
+ 3502
2822
+ 3505
2823
+ 3507
2824
+ 3508
2825
+ 3509
2826
+ 3510
2827
+ 3511
2828
+ 3512
2829
+ 3513
2830
+ 3514
2831
+ 3515
2832
+ 3517
2833
+ 3518
2834
+ 3519
2835
+ 3521
2836
+ 3524
2837
+ 3525
2838
+ 3526
2839
+ 3528
2840
+ 3529
2841
+ 3532
2842
+ 3536
2843
+ 3541
2844
+ 3542
2845
+ 3543
2846
+ 3544
2847
+ 3545
2848
+ 3546
2849
+ 3549
2850
+ 3550
2851
+ 3551
2852
+ 3552
2853
+ 3554
2854
+ 3556
2855
+ 3557
2856
+ 3558
2857
+ 3559
2858
+ 3560
2859
+ 3562
2860
+ 3563
2861
+ 3564
2862
+ 3566
2863
+ 3567
2864
+ 3568
2865
+ 3571
2866
+ 3572
2867
+ 3573
2868
+ 3574
2869
+ 3575
2870
+ 3576
2871
+ 3578
2872
+ 3579
2873
+ 3580
2874
+ 3582
2875
+ 3584
2876
+ 3585
2877
+ 3588
2878
+ 3590
2879
+ 3592
2880
+ 3593
2881
+ 3594
2882
+ 3599
2883
+ 3602
2884
+ 3605
2885
+ 3606
2886
+ 3608
2887
+ 3611
2888
+ 3612
2889
+ 3615
2890
+ 3617
2891
+ 3620
2892
+ 3621
2893
+ 3622
2894
+ 3623
2895
+ 3624
2896
+ 3625
2897
+ 3626
2898
+ 3629
2899
+ 3630
2900
+ 3632
2901
+ 3633
2902
+ 3636
2903
+ 3637
2904
+ 3638
2905
+ 3641
2906
+ 3644
2907
+ 3646
2908
+ 3647
2909
+ 3648
2910
+ 3649
2911
+ 3654
2912
+ 3655
2913
+ 3656
2914
+ 3657
2915
+ 3660
2916
+ 3662
2917
+ 3667
2918
+ 3671
2919
+ 3672
2920
+ 3673
2921
+ 3674
2922
+ 3675
2923
+ 3676
2924
+ 3678
2925
+ 3679
2926
+ 3680
2927
+ 3681
2928
+ 3682
2929
+ 3683
2930
+ 3685
2931
+ 3687
2932
+ 3691
2933
+ 3692
2934
+ 3694
2935
+ 3695
2936
+ 3697
2937
+ 3698
2938
+ 3699
2939
+ 3700
2940
+ 3701
2941
+ 3703
2942
+ 3704
2943
+ 3705
2944
+ 3707
2945
+ 3709
2946
+ 3711
2947
+ 3712
2948
+ 3715
2949
+ 3717
2950
+ 3718
2951
+ 3719
2952
+ 3720
2953
+ 3721
2954
+ 3723
2955
+ 3724
2956
+ 3725
2957
+ 3726
2958
+ 3728
2959
+ 3729
2960
+ 3733
2961
+ 3734
2962
+ 3735
2963
+ 3737
2964
+ 3738
2965
+ 3739
2966
+ 3741
2967
+ 3743
2968
+ 3746
2969
+ 3748
2970
+ 3750
2971
+ 3753
2972
+ 3754
2973
+ 3756
2974
+ 3757
2975
+ 3759
2976
+ 3760
2977
+ 3762
2978
+ 3764
2979
+ 3765
2980
+ 3766
2981
+ 3768
2982
+ 3772
2983
+ 3773
2984
+ 3774
2985
+ 3776
2986
+ 3777
2987
+ 3779
2988
+ 3780
2989
+ 3782
2990
+ 3783
2991
+ 3784
2992
+ 3785
2993
+ 3786
2994
+ 3790
2995
+ 3792
2996
+ 3793
2997
+ 3794
2998
+ 3798
2999
+ 3799
3000
+ 3800
3001
+ 3801
3002
+ 3802
3003
+ 3803
3004
+ 3804
3005
+ 3807
3006
+ 3809
3007
+ 3813
3008
+ 3814
3009
+ 3816
3010
+ 3819
3011
+ 3822
3012
+ 3823
3013
+ 3824
3014
+ 3826
3015
+ 3827
3016
+ 3828
3017
+ 3829
3018
+ 3830
3019
+ 3831
3020
+ 3832
3021
+ 3833
3022
+ 3834
3023
+ 3836
3024
+ 3837
3025
+ 3838
3026
+ 3839
3027
+ 3840
3028
+ 3841
3029
+ 3842
3030
+ 3844
3031
+ 3845
3032
+ 3847
3033
+ 3848
3034
+ 3849
3035
+ 3850
3036
+ 3852
3037
+ 3854
3038
+ 3855
3039
+ 3856
3040
+ 3857
3041
+ 3858
3042
+ 3861
3043
+ 3862
3044
+ 3863
3045
+ 3865
3046
+ 3869
3047
+ 3872
3048
+ 3873
3049
+ 3874
3050
+ 3879
3051
+ 3880
3052
+ 3883
3053
+ 3885
3054
+ 3886
3055
+ 3887
3056
+ 3888
3057
+ 3889
3058
+ 3890
3059
+ 3891
3060
+ 3893
3061
+ 3894
3062
+ 3895
3063
+ 3897
3064
+ 3898
3065
+ 3899
3066
+ 3900
3067
+ 3901
3068
+ 3903
3069
+ 3904
3070
+ 3906
3071
+ 3914
3072
+ 3915
3073
+ 3916
3074
+ 3920
3075
+ 3923
3076
+ 3924
3077
+ 3925
3078
+ 3926
3079
+ 3928
3080
+ 3929
3081
+ 3931
3082
+ 3932
3083
+ 3934
3084
+ 3935
3085
+ 3937
3086
+ 3939
3087
+ 3942
3088
+ 3943
3089
+ 3945
3090
+ 3949
3091
+ 3950
3092
+ 3952
3093
+ 3955
3094
+ 3956
3095
+ 3963
3096
+ 3965
3097
+ 3966
3098
+ 3967
3099
+ 3969
3100
+ 3970
3101
+ 3971
3102
+ 3974
3103
+ 3976
3104
+ 3977
3105
+ 3978
3106
+ 3980
3107
+ 3981
3108
+ 3982
3109
+ 3983
3110
+ 3984
3111
+ 3987
3112
+ 3988
3113
+ 3992
3114
+ 3995
3115
+ 3996
3116
+ 3997
3117
+ 3999
RobustVideoMatting/documentation/misc/imagematte_train.txt ADDED
@@ -0,0 +1,420 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 10743257206_18e7f44f2e_b.jpg
2
+ 10845279884_d2d4c7b4d1_b.jpg
3
+ 1-1252426161dfXY.jpg
4
+ 1-1255621189mTnS.jpg
5
+ 1-1259162624NMFK.jpg
6
+ 1-1259245823Un3j.jpg
7
+ 11363165393_05d7a21d76_b.jpg
8
+ 131686738165901828.jpg
9
+ 13564741125_753939e9ce_o.jpg
10
+ 14731860273_5b40b19b51_o.jpg
11
+ 16087-a-young-woman-showing-a-bitten-green-apple-pv.jpg
12
+ 1609484818_b9bb12b.jpg
13
+ 17620-a-beautiful-woman-in-a-bikini-pv.jpg
14
+ 20672673163_20c8467827_b.jpg
15
+ 3262986095_2d5afe583c_b.jpg
16
+ 3588101233_f91aa5e3a3.jpg
17
+ 3858897226_cae5b75963_o.jpg
18
+ 4889657410_2d503ca287_o.jpg
19
+ 4981835627_c4e6c4ffa8_o.jpg
20
+ 5025666458_576b974455_o.jpg
21
+ 5149410930_3a943dc43f_b.jpg
22
+ 539641011387760661.jpg
23
+ 5892503248_4b882863c7_o.jpg
24
+ 604673748289192179.jpg
25
+ 606189768665464996.jpg
26
+ 624753897218113578.jpg
27
+ 657454154710122500.jpg
28
+ 664308724952072193.jpg
29
+ 7669262460_e4be408343_b.jpg
30
+ 8244818049_dfa59a3eb8_b.jpg
31
+ 8688417335_01f3bafbe5_o.jpg
32
+ 9434599749_e7ccfc7812_b.jpg
33
+ Aaron_Friedman_Headshot.jpg
34
+ arrgh___r___28_by_mjranum_stock.jpg
35
+ arrgh___r___29_by_mjranum_stock.jpg
36
+ arrgh___r___30_by_mjranum_stock.jpg
37
+ a-single-person-1084191_960_720.jpg
38
+ ballerina-855652_1920.jpg
39
+ beautiful-19075_960_720.jpg
40
+ boy-454633_1920.jpg
41
+ bride-2819673_1920.jpg
42
+ bride-442894_1920.jpg
43
+ face-1223346_960_720.jpg
44
+ fashion-model-portrait.jpg
45
+ fashion-model-pose.jpg
46
+ girl-1535859_1920.jpg
47
+ Girl_in_front_of_a_green_background.jpg
48
+ goth_by_bugidifino-d4w7zms.jpg
49
+ h_0.jpg
50
+ h_100.jpg
51
+ h_101.jpg
52
+ h_102.jpg
53
+ h_103.jpg
54
+ h_104.jpg
55
+ h_105.jpg
56
+ h_106.jpg
57
+ h_107.jpg
58
+ h_108.jpg
59
+ h_109.jpg
60
+ h_10.jpg
61
+ h_111.jpg
62
+ h_112.jpg
63
+ h_113.jpg
64
+ h_114.jpg
65
+ h_115.jpg
66
+ h_116.jpg
67
+ h_117.jpg
68
+ h_118.jpg
69
+ h_119.jpg
70
+ h_11.jpg
71
+ h_120.jpg
72
+ h_121.jpg
73
+ h_122.jpg
74
+ h_123.jpg
75
+ h_124.jpg
76
+ h_125.jpg
77
+ h_126.jpg
78
+ h_127.jpg
79
+ h_128.jpg
80
+ h_129.jpg
81
+ h_12.jpg
82
+ h_130.jpg
83
+ h_131.jpg
84
+ h_132.jpg
85
+ h_133.jpg
86
+ h_134.jpg
87
+ h_135.jpg
88
+ h_136.jpg
89
+ h_137.jpg
90
+ h_138.jpg
91
+ h_139.jpg
92
+ h_13.jpg
93
+ h_140.jpg
94
+ h_141.jpg
95
+ h_142.jpg
96
+ h_143.jpg
97
+ h_144.jpg
98
+ h_145.jpg
99
+ h_146.jpg
100
+ h_147.jpg
101
+ h_148.jpg
102
+ h_149.jpg
103
+ h_14.jpg
104
+ h_151.jpg
105
+ h_152.jpg
106
+ h_153.jpg
107
+ h_154.jpg
108
+ h_155.jpg
109
+ h_156.jpg
110
+ h_157.jpg
111
+ h_158.jpg
112
+ h_159.jpg
113
+ h_15.jpg
114
+ h_160.jpg
115
+ h_161.jpg
116
+ h_162.jpg
117
+ h_163.jpg
118
+ h_164.jpg
119
+ h_165.jpg
120
+ h_166.jpg
121
+ h_167.jpg
122
+ h_168.jpg
123
+ h_169.jpg
124
+ h_170.jpg
125
+ h_171.jpg
126
+ h_172.jpg
127
+ h_173.jpg
128
+ h_174.jpg
129
+ h_175.jpg
130
+ h_176.jpg
131
+ h_177.jpg
132
+ h_178.jpg
133
+ h_179.jpg
134
+ h_17.jpg
135
+ h_180.jpg
136
+ h_181.jpg
137
+ h_182.jpg
138
+ h_183.jpg
139
+ h_184.jpg
140
+ h_185.jpg
141
+ h_186.jpg
142
+ h_187.jpg
143
+ h_188.jpg
144
+ h_189.jpg
145
+ h_18.jpg
146
+ h_190.jpg
147
+ h_191.jpg
148
+ h_192.jpg
149
+ h_193.jpg
150
+ h_194.jpg
151
+ h_195.jpg
152
+ h_196.jpg
153
+ h_197.jpg
154
+ h_198.jpg
155
+ h_199.jpg
156
+ h_19.jpg
157
+ h_1.jpg
158
+ h_200.jpg
159
+ h_201.jpg
160
+ h_202.jpg
161
+ h_204.jpg
162
+ h_205.jpg
163
+ h_206.jpg
164
+ h_207.jpg
165
+ h_208.jpg
166
+ h_209.jpg
167
+ h_20.jpg
168
+ h_210.jpg
169
+ h_211.jpg
170
+ h_212.jpg
171
+ h_213.jpg
172
+ h_214.jpg
173
+ h_215.jpg
174
+ h_216.jpg
175
+ h_217.jpg
176
+ h_218.jpg
177
+ h_219.jpg
178
+ h_21.jpg
179
+ h_220.jpg
180
+ h_221.jpg
181
+ h_222.jpg
182
+ h_223.jpg
183
+ h_224.jpg
184
+ h_225.jpg
185
+ h_226.jpg
186
+ h_227.jpg
187
+ h_228.jpg
188
+ h_229.jpg
189
+ h_22.jpg
190
+ h_230.jpg
191
+ h_231.jpg
192
+ h_232.jpg
193
+ h_233.jpg
194
+ h_234.jpg
195
+ h_235.jpg
196
+ h_236.jpg
197
+ h_237.jpg
198
+ h_238.jpg
199
+ h_239.jpg
200
+ h_23.jpg
201
+ h_240.jpg
202
+ h_241.jpg
203
+ h_242.jpg
204
+ h_243.jpg
205
+ h_244.jpg
206
+ h_245.jpg
207
+ h_247.jpg
208
+ h_248.jpg
209
+ h_249.jpg
210
+ h_24.jpg
211
+ h_250.jpg
212
+ h_251.jpg
213
+ h_252.jpg
214
+ h_253.jpg
215
+ h_254.jpg
216
+ h_255.jpg
217
+ h_256.jpg
218
+ h_257.jpg
219
+ h_258.jpg
220
+ h_259.jpg
221
+ h_25.jpg
222
+ h_260.jpg
223
+ h_261.jpg
224
+ h_262.jpg
225
+ h_263.jpg
226
+ h_264.jpg
227
+ h_265.jpg
228
+ h_266.jpg
229
+ h_268.jpg
230
+ h_269.jpg
231
+ h_26.jpg
232
+ h_270.jpg
233
+ h_271.jpg
234
+ h_272.jpg
235
+ h_273.jpg
236
+ h_274.jpg
237
+ h_276.jpg
238
+ h_277.jpg
239
+ h_278.jpg
240
+ h_279.jpg
241
+ h_27.jpg
242
+ h_280.jpg
243
+ h_281.jpg
244
+ h_282.jpg
245
+ h_283.jpg
246
+ h_284.jpg
247
+ h_285.jpg
248
+ h_286.jpg
249
+ h_287.jpg
250
+ h_288.jpg
251
+ h_289.jpg
252
+ h_28.jpg
253
+ h_290.jpg
254
+ h_291.jpg
255
+ h_292.jpg
256
+ h_293.jpg
257
+ h_294.jpg
258
+ h_295.jpg
259
+ h_296.jpg
260
+ h_297.jpg
261
+ h_298.jpg
262
+ h_299.jpg
263
+ h_29.jpg
264
+ h_300.jpg
265
+ h_301.jpg
266
+ h_302.jpg
267
+ h_303.jpg
268
+ h_304.jpg
269
+ h_305.jpg
270
+ h_307.jpg
271
+ h_308.jpg
272
+ h_309.jpg
273
+ h_30.jpg
274
+ h_310.jpg
275
+ h_311.jpg
276
+ h_312.jpg
277
+ h_313.jpg
278
+ h_314.jpg
279
+ h_315.jpg
280
+ h_316.jpg
281
+ h_317.jpg
282
+ h_318.jpg
283
+ h_319.jpg
284
+ h_31.jpg
285
+ h_320.jpg
286
+ h_321.jpg
287
+ h_322.jpg
288
+ h_323.jpg
289
+ h_324.jpg
290
+ h_325.jpg
291
+ h_326.jpg
292
+ h_327.jpg
293
+ h_329.jpg
294
+ h_32.jpg
295
+ h_33.jpg
296
+ h_34.jpg
297
+ h_35.jpg
298
+ h_36.jpg
299
+ h_37.jpg
300
+ h_38.jpg
301
+ h_39.jpg
302
+ h_3.jpg
303
+ h_40.jpg
304
+ h_41.jpg
305
+ h_42.jpg
306
+ h_43.jpg
307
+ h_44.jpg
308
+ h_45.jpg
309
+ h_46.jpg
310
+ h_47.jpg
311
+ h_48.jpg
312
+ h_49.jpg
313
+ h_4.jpg
314
+ h_50.jpg
315
+ h_51.jpg
316
+ h_52.jpg
317
+ h_53.jpg
318
+ h_54.jpg
319
+ h_55.jpg
320
+ h_56.jpg
321
+ h_57.jpg
322
+ h_58.jpg
323
+ h_59.jpg
324
+ h_5.jpg
325
+ h_60.jpg
326
+ h_61.jpg
327
+ h_62.jpg
328
+ h_63.jpg
329
+ h_65.jpg
330
+ h_67.jpg
331
+ h_68.jpg
332
+ h_69.jpg
333
+ h_6.jpg
334
+ h_70.jpg
335
+ h_71.jpg
336
+ h_72.jpg
337
+ h_73.jpg
338
+ h_74.jpg
339
+ h_75.jpg
340
+ h_76.jpg
341
+ h_77.jpg
342
+ h_78.jpg
343
+ h_79.jpg
344
+ h_7.jpg
345
+ h_80.jpg
346
+ h_81.jpg
347
+ h_82.jpg
348
+ h_83.jpg
349
+ h_84.jpg
350
+ h_85.jpg
351
+ h_86.jpg
352
+ h_87.jpg
353
+ h_88.jpg
354
+ h_89.jpg
355
+ h_8.jpg
356
+ h_90.jpg
357
+ h_91.jpg
358
+ h_92.jpg
359
+ h_93.jpg
360
+ h_94.jpg
361
+ h_95.jpg
362
+ h_96.jpg
363
+ h_97.jpg
364
+ h_98.jpg
365
+ h_99.jpg
366
+ h_9.jpg
367
+ hair-flying-142210_1920.jpg
368
+ headshotid_by_bokogreat_stock-d355xf3.jpg
369
+ lil_white_goth_grl___23_by_mjranum_stock.jpg
370
+ lil_white_goth_grl___26_by_mjranum_stock.jpg
371
+ man-388104_960_720.jpg
372
+ man_headshot.jpg
373
+ MFettes-headshot.jpg
374
+ model-429733_960_720.jpg
375
+ model-610352_960_720.jpg
376
+ model-858753_960_720.jpg
377
+ model-858755_960_720.jpg
378
+ model-873675_960_720.jpg
379
+ model-873678_960_720.jpg
380
+ model-873690_960_720.jpg
381
+ model-881425_960_720.jpg
382
+ model-881431_960_720.jpg
383
+ model-female-girl-beautiful-51969.jpg
384
+ Model_in_green_dress_3.jpg
385
+ Modern_shingle_bob_haircut.jpg
386
+ Motivate_(Fitness_model).jpg
387
+ Official_portrait_of_Barack_Obama.jpg
388
+ person-woman-eyes-face.jpg
389
+ pink-hair-855660_960_720.jpg
390
+ portrait-750774_1920.jpg
391
+ Professor_Steven_Chu_ForMemRS_headshot.jpg
392
+ sailor_flying_4_by_senshistock-d4k2wmr.jpg
393
+ skin-care-937667_960_720.jpg
394
+ sorcery___8_by_mjranum_stock.jpg
395
+ t_62.jpg
396
+ t_65.jpg
397
+ test_32.jpg
398
+ test_8.jpg
399
+ train_245.jpg
400
+ train_246.jpg
401
+ train_255.jpg
402
+ train_304.jpg
403
+ train_333.jpg
404
+ train_361.jpg
405
+ train_395.jpg
406
+ train_480.jpg
407
+ train_488.jpg
408
+ train_539.jpg
409
+ wedding-846926_1920.jpg
410
+ Wild_hair.jpg
411
+ with_wings___pose_reference_by_senshistock-d6by42n_2.jpg
412
+ with_wings___pose_reference_by_senshistock-d6by42n.jpg
413
+ woman-1138435_960_720.jpg
414
+ woman1.jpg
415
+ woman2.jpg
416
+ woman-659354_960_720.jpg
417
+ woman-804072_960_720.jpg
418
+ woman-868519_960_720.jpg
419
+ Woman_in_white_shirt_on_August_2009_02.jpg
420
+ women-878869_1920.jpg
RobustVideoMatting/documentation/misc/imagematte_valid.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 13564741125_753939e9ce_o.jpg
2
+ 3858897226_cae5b75963_o.jpg
3
+ 538724499685900405.jpg
4
+ ballerina-855652_1920.jpg
5
+ boy-454633_1920.jpg
6
+ h_110.jpg
7
+ h_150.jpg
8
+ h_16.jpg
9
+ h_246.jpg
10
+ h_267.jpg
11
+ h_275.jpg
12
+ h_306.jpg
13
+ h_328.jpg
14
+ model-610352_960_720.jpg
15
+ t_66.jpg
RobustVideoMatting/documentation/misc/spd_preprocess.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # pip install supervisely
2
+ import supervisely_lib as sly
3
+ import numpy as np
4
+ import os
5
+ from PIL import Image
6
+ from tqdm import tqdm
7
+
8
+ # Download dataset from <https://supervise.ly/explore/projects/supervisely-person-dataset-23304/datasets>
9
+ project_root = 'PATH_TO/Supervisely Person Dataset' # <-- Configure input
10
+ project = sly.Project(project_root, sly.OpenMode.READ)
11
+
12
+ output_path = 'OUTPUT_DIR' # <-- Configure output
13
+ os.makedirs(os.path.join(output_path, 'train', 'src'))
14
+ os.makedirs(os.path.join(output_path, 'train', 'msk'))
15
+ os.makedirs(os.path.join(output_path, 'valid', 'src'))
16
+ os.makedirs(os.path.join(output_path, 'valid', 'msk'))
17
+
18
+ max_size = 2048 # <-- Configure max size
19
+
20
+ for dataset in project.datasets:
21
+ for item in tqdm(dataset):
22
+ ann = sly.Annotation.load_json_file(dataset.get_ann_path(item), project.meta)
23
+ msk = np.zeros(ann.img_size, dtype=np.uint8)
24
+ for label in ann.labels:
25
+ label.geometry.draw(msk, color=[255])
26
+ msk = Image.fromarray(msk)
27
+
28
+ img = Image.open(dataset.get_img_path(item)).convert('RGB')
29
+ if img.size[0] > max_size or img.size[1] > max_size:
30
+ scale = max_size / max(img.size)
31
+ img = img.resize((int(img.size[0] * scale), int(img.size[1] * scale)), Image.BILINEAR)
32
+ msk = msk.resize((int(msk.size[0] * scale), int(msk.size[1] * scale)), Image.NEAREST)
33
+
34
+ img.save(os.path.join(output_path, 'train', 'src', item.replace('.png', '.jpg')))
35
+ msk.save(os.path.join(output_path, 'train', 'msk', item.replace('.png', '.jpg')))
36
+
37
+ # Move first 100 to validation set
38
+ names = os.listdir(os.path.join(output_path, 'train', 'src'))
39
+ for name in tqdm(names[:100]):
40
+ os.rename(
41
+ os.path.join(output_path, 'train', 'src', name),
42
+ os.path.join(output_path, 'valid', 'src', name))
43
+ os.rename(
44
+ os.path.join(output_path, 'train', 'msk', name),
45
+ os.path.join(output_path, 'valid', 'msk', name))
RobustVideoMatting/documentation/training.md ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Training Documentation
2
+
3
+ This documentation only shows the way to re-produce our [paper](https://peterl1n.github.io/RobustVideoMatting/). If you would like to remove or add a dataset to the training, you are responsible for adapting the training code yourself.
4
+
5
+ ## Datasets
6
+
7
+ The following datasets are used during our training.
8
+
9
+ **IMPORTANT: If you choose to download our preprocessed versions. Please avoid repeated downloads and cache the data locally. All traffics cost our expense. Please be responsible. We may only provide the preprocessed version of a limited time.**
10
+
11
+ ### Matting Datasets
12
+ * [VideoMatte240K](https://grail.cs.washington.edu/projects/background-matting-v2/#/datasets)
13
+ * Download JPEG SD version (6G) for stage 1 and 2.
14
+ * Download JPEG HD version (60G) for stage 3 and 4.
15
+ * Manually move clips `0000`, `0100`, `0200`, `0300` from the training set to a validation set.
16
+ * ImageMatte
17
+ * ImageMatte consists of [Distinctions-646](https://wukaoliu.github.io/HAttMatting/) and [Adobe Image Matting](https://sites.google.com/view/deepimagematting) datasets.
18
+ * Only needed for stage 4.
19
+ * You need to contact their authors to acquire.
20
+ * After downloading both datasets, merge their samples together to form ImageMatte dataset.
21
+ * Only keep samples of humans.
22
+ * Full list of images we used in ImageMatte for training:
23
+ * [imagematte_train.txt](/documentation/misc/imagematte_train.txt)
24
+ * [imagematte_valid.txt](/documentation/misc/imagematte_valid.txt)
25
+ * Full list of images we used for evaluation.
26
+ * [aim_test.txt](/documentation/misc/aim_test.txt)
27
+ * [d646_test.txt](/documentation/misc/d646_test.txt)
28
+ ### Background Datasets
29
+ * Video Backgrounds
30
+ * We process from [DVM Background Set](https://github.com/nowsyn/DVM) by selecting clips without humans and extract only the first 100 frames as JPEG sequence.
31
+ * Full list of clips we used:
32
+ * [dvm_background_train_clips.txt](/documentation/misc/dvm_background_train_clips.txt)
33
+ * [dvm_background_test_clips.txt](/documentation/misc/dvm_background_test_clips.txt)
34
+ * You can download our preprocessed versions:
35
+ * [Train set (14.6G)](https://robustvideomatting.blob.core.windows.net/data/BackgroundVideosTrain.tar) (Manually move some clips to validation set)
36
+ * [Test set (936M)](https://robustvideomatting.blob.core.windows.net/data/BackgroundVideosTest.tar) (Not needed for training. Only used for making synthetic test samples for evaluation)
37
+ * Image Backgrounds
38
+ * Train set:
39
+ * We crawled 8000 suitable images from Google and Flicker.
40
+ * We will not publish these images.
41
+ * [Test set](https://grail.cs.washington.edu/projects/background-matting-v2/#/datasets)
42
+ * We use the validation background set from [BGMv2](https://grail.cs.washington.edu/projects/background-matting-v2/) project.
43
+ * It contains about 200 images.
44
+ * It is not used in our training. Only used for making synthetic test samples for evaluation.
45
+ * But if you just want to quickly tryout training, you may use this as a temporary subsitute for the train set.
46
+
47
+ ### Segmentation Datasets
48
+
49
+ * [COCO](https://cocodataset.org/#download)
50
+ * Download [train2017.zip (18G)](http://images.cocodataset.org/zips/train2017.zip)
51
+ * Download [panoptic_annotations_trainval2017.zip (821M)](http://images.cocodataset.org/annotations/panoptic_annotations_trainval2017.zip)
52
+ * Note that our train script expects the panopitc version.
53
+ * [YouTubeVIS 2021](https://youtube-vos.org/dataset/vis/)
54
+ * Download the train set. No preprocessing needed.
55
+ * [Supervisely Person Dataset](https://supervise.ly/explore/projects/supervisely-person-dataset-23304/datasets)
56
+ * We used the supervisedly library to convert their encoding to bitmaps masks before using our script. We also resized down some of the large images to avoid disk loading bottleneck.
57
+ * You can refer to [spd_preprocess.py](/documentation/misc/spd_preprocess.py)
58
+ * Or, you can download our [preprocessed version (800M)](https://robustvideomatting.blob.core.windows.net/data/SuperviselyPersonDataset.tar)
59
+
60
+ ## Training
61
+
62
+ For reference, our training was done on data center machines with 48 CPU cores, 300G CPU memory, and 4 Nvidia V100 32G GPUs.
63
+
64
+ During our official training, the code contains custom logics for our infrastructure. For release, the script has been cleaned up. There may be bugs existing in this version of the code but not in our official training. If you find problems, please file an issue.
65
+
66
+ After you have downloaded the datasets. Please configure `train_config.py` to provide paths to your datasets.
67
+
68
+ The training consists of 4 stages. For detail, please refer to the [paper](https://peterl1n.github.io/RobustVideoMatting/).
69
+
70
+ ### Stage 1
71
+ ```sh
72
+ python train.py \
73
+ --model-variant mobilenetv3 \
74
+ --dataset videomatte \
75
+ --resolution-lr 512 \
76
+ --seq-length-lr 15 \
77
+ --learning-rate-backbone 0.0001 \
78
+ --learning-rate-aspp 0.0002 \
79
+ --learning-rate-decoder 0.0002 \
80
+ --learning-rate-refiner 0 \
81
+ --checkpoint-dir checkpoint/stage1 \
82
+ --log-dir log/stage1 \
83
+ --epoch-start 0 \
84
+ --epoch-end 20
85
+ ```
86
+
87
+ ### Stage 2
88
+ ```sh
89
+ python train.py \
90
+ --model-variant mobilenetv3 \
91
+ --dataset videomatte \
92
+ --resolution-lr 512 \
93
+ --seq-length-lr 50 \
94
+ --learning-rate-backbone 0.00005 \
95
+ --learning-rate-aspp 0.0001 \
96
+ --learning-rate-decoder 0.0001 \
97
+ --learning-rate-refiner 0 \
98
+ --checkpoint checkpoint/stage1/epoch-19.pth \
99
+ --checkpoint-dir checkpoint/stage2 \
100
+ --log-dir log/stage2 \
101
+ --epoch-start 20 \
102
+ --epoch-end 22
103
+ ```
104
+
105
+ ### Stage 3
106
+ ```sh
107
+ python train.py \
108
+ --model-variant mobilenetv3 \
109
+ --dataset videomatte \
110
+ --train-hr \
111
+ --resolution-lr 512 \
112
+ --resolution-hr 2048 \
113
+ --seq-length-lr 40 \
114
+ --seq-length-hr 6 \
115
+ --learning-rate-backbone 0.00001 \
116
+ --learning-rate-aspp 0.00001 \
117
+ --learning-rate-decoder 0.00001 \
118
+ --learning-rate-refiner 0.0002 \
119
+ --checkpoint checkpoint/stage2/epoch-21.pth \
120
+ --checkpoint-dir checkpoint/stage3 \
121
+ --log-dir log/stage3 \
122
+ --epoch-start 22 \
123
+ --epoch-end 23
124
+ ```
125
+
126
+ ### Stage 4
127
+ ```sh
128
+ python train.py \
129
+ --model-variant mobilenetv3 \
130
+ --dataset imagematte \
131
+ --train-hr \
132
+ --resolution-lr 512 \
133
+ --resolution-hr 2048 \
134
+ --seq-length-lr 40 \
135
+ --seq-length-hr 6 \
136
+ --learning-rate-backbone 0.00001 \
137
+ --learning-rate-aspp 0.00001 \
138
+ --learning-rate-decoder 0.00005 \
139
+ --learning-rate-refiner 0.0002 \
140
+ --checkpoint checkpoint/stage3/epoch-22.pth \
141
+ --checkpoint-dir checkpoint/stage4 \
142
+ --log-dir log/stage4 \
143
+ --epoch-start 23 \
144
+ --epoch-end 28
145
+ ```
146
+
147
+ <br><br><br>
148
+
149
+ ## Evaluation
150
+
151
+ We synthetically composite test samples to both image and video backgrounds. Image samples (from D646, AIM) are augmented with synthetic motion.
152
+
153
+ We only provide the composited VideoMatte240K test set. They are used in our paper evaluation. For D646 and AIM, you need to acquire the data from their authors and composite them yourself. The composition scripts we used are saved in `/evaluation` folder as reference backup. You need to modify them based on your setup.
154
+
155
+ * [videomatte_512x512.tar (PNG 1.8G)](https://robustvideomatting.blob.core.windows.net/eval/videomatte_512x288.tar)
156
+ * [videomatte_1920x1080.tar (JPG 2.2G)](https://robustvideomatting.blob.core.windows.net/eval/videomatte_1920x1080.tar)
157
+
158
+ Evaluation scripts are provided in `/evaluation` folder.
RobustVideoMatting/evaluation/evaluate_hr.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ HR (High-Resolution) evaluation. We found using numpy is very slow for high resolution, so we moved it to PyTorch using CUDA.
3
+
4
+ Note, the script only does evaluation. You will need to first inference yourself and save the results to disk
5
+ Expected directory format for both prediction and ground-truth is:
6
+
7
+ videomatte_1920x1080
8
+ ├── videomatte_motion
9
+ ├── pha
10
+ ├── 0000
11
+ ├── 0000.png
12
+ ├── fgr
13
+ ├── 0000
14
+ ├── 0000.png
15
+ ├── videomatte_static
16
+ ├── pha
17
+ ├── 0000
18
+ ├── 0000.png
19
+ ├── fgr
20
+ ├── 0000
21
+ ├── 0000.png
22
+
23
+ Prediction must have the exact file structure and file name as the ground-truth,
24
+ meaning that if the ground-truth is png/jpg, prediction should be png/jpg.
25
+
26
+ Example usage:
27
+
28
+ python evaluate.py \
29
+ --pred-dir pred/videomatte_1920x1080 \
30
+ --true-dir true/videomatte_1920x1080
31
+
32
+ An excel sheet with evaluation results will be written to "pred/videomatte_1920x1080/videomatte_1920x1080.xlsx"
33
+ """
34
+
35
+
36
+ import argparse
37
+ import os
38
+ import cv2
39
+ import kornia
40
+ import numpy as np
41
+ import xlsxwriter
42
+ import torch
43
+ from concurrent.futures import ThreadPoolExecutor
44
+ from tqdm import tqdm
45
+
46
+
47
+ class Evaluator:
48
+ def __init__(self):
49
+ self.parse_args()
50
+ self.init_metrics()
51
+ self.evaluate()
52
+ self.write_excel()
53
+
54
+ def parse_args(self):
55
+ parser = argparse.ArgumentParser()
56
+ parser.add_argument('--pred-dir', type=str, required=True)
57
+ parser.add_argument('--true-dir', type=str, required=True)
58
+ parser.add_argument('--num-workers', type=int, default=48)
59
+ parser.add_argument('--metrics', type=str, nargs='+', default=[
60
+ 'pha_mad', 'pha_mse', 'pha_grad', 'pha_dtssd', 'fgr_mse'])
61
+ self.args = parser.parse_args()
62
+
63
+ def init_metrics(self):
64
+ self.mad = MetricMAD()
65
+ self.mse = MetricMSE()
66
+ self.grad = MetricGRAD()
67
+ self.dtssd = MetricDTSSD()
68
+
69
+ def evaluate(self):
70
+ tasks = []
71
+ position = 0
72
+
73
+ with ThreadPoolExecutor(max_workers=self.args.num_workers) as executor:
74
+ for dataset in sorted(os.listdir(self.args.pred_dir)):
75
+ if os.path.isdir(os.path.join(self.args.pred_dir, dataset)):
76
+ for clip in sorted(os.listdir(os.path.join(self.args.pred_dir, dataset))):
77
+ future = executor.submit(self.evaluate_worker, dataset, clip, position)
78
+ tasks.append((dataset, clip, future))
79
+ position += 1
80
+
81
+ self.results = [(dataset, clip, future.result()) for dataset, clip, future in tasks]
82
+
83
+ def write_excel(self):
84
+ workbook = xlsxwriter.Workbook(os.path.join(self.args.pred_dir, f'{os.path.basename(self.args.pred_dir)}.xlsx'))
85
+ summarysheet = workbook.add_worksheet('summary')
86
+ metricsheets = [workbook.add_worksheet(metric) for metric in self.results[0][2].keys()]
87
+
88
+ for i, metric in enumerate(self.results[0][2].keys()):
89
+ summarysheet.write(i, 0, metric)
90
+ summarysheet.write(i, 1, f'={metric}!B2')
91
+
92
+ for row, (dataset, clip, metrics) in enumerate(self.results):
93
+ for metricsheet, metric in zip(metricsheets, metrics.values()):
94
+ # Write the header
95
+ if row == 0:
96
+ metricsheet.write(1, 0, 'Average')
97
+ metricsheet.write(1, 1, f'=AVERAGE(C2:ZZ2)')
98
+ for col in range(len(metric)):
99
+ metricsheet.write(0, col + 2, col)
100
+ colname = xlsxwriter.utility.xl_col_to_name(col + 2)
101
+ metricsheet.write(1, col + 2, f'=AVERAGE({colname}3:{colname}9999)')
102
+
103
+ metricsheet.write(row + 2, 0, dataset)
104
+ metricsheet.write(row + 2, 1, clip)
105
+ metricsheet.write_row(row + 2, 2, metric)
106
+
107
+ workbook.close()
108
+
109
+ def evaluate_worker(self, dataset, clip, position):
110
+ framenames = sorted(os.listdir(os.path.join(self.args.pred_dir, dataset, clip, 'pha')))
111
+ metrics = {metric_name : [] for metric_name in self.args.metrics}
112
+
113
+ pred_pha_tm1 = None
114
+ true_pha_tm1 = None
115
+
116
+ for i, framename in enumerate(tqdm(framenames, desc=f'{dataset} {clip}', position=position, dynamic_ncols=True)):
117
+ true_pha = cv2.imread(os.path.join(self.args.true_dir, dataset, clip, 'pha', framename), cv2.IMREAD_GRAYSCALE)
118
+ pred_pha = cv2.imread(os.path.join(self.args.pred_dir, dataset, clip, 'pha', framename), cv2.IMREAD_GRAYSCALE)
119
+
120
+ true_pha = torch.from_numpy(true_pha).cuda(non_blocking=True).float().div_(255)
121
+ pred_pha = torch.from_numpy(pred_pha).cuda(non_blocking=True).float().div_(255)
122
+
123
+ if 'pha_mad' in self.args.metrics:
124
+ metrics['pha_mad'].append(self.mad(pred_pha, true_pha))
125
+ if 'pha_mse' in self.args.metrics:
126
+ metrics['pha_mse'].append(self.mse(pred_pha, true_pha))
127
+ if 'pha_grad' in self.args.metrics:
128
+ metrics['pha_grad'].append(self.grad(pred_pha, true_pha))
129
+ if 'pha_conn' in self.args.metrics:
130
+ metrics['pha_conn'].append(self.conn(pred_pha, true_pha))
131
+ if 'pha_dtssd' in self.args.metrics:
132
+ if i == 0:
133
+ metrics['pha_dtssd'].append(0)
134
+ else:
135
+ metrics['pha_dtssd'].append(self.dtssd(pred_pha, pred_pha_tm1, true_pha, true_pha_tm1))
136
+
137
+ pred_pha_tm1 = pred_pha
138
+ true_pha_tm1 = true_pha
139
+
140
+ if 'fgr_mse' in self.args.metrics:
141
+ true_fgr = cv2.imread(os.path.join(self.args.true_dir, dataset, clip, 'fgr', framename), cv2.IMREAD_COLOR)
142
+ pred_fgr = cv2.imread(os.path.join(self.args.pred_dir, dataset, clip, 'fgr', framename), cv2.IMREAD_COLOR)
143
+
144
+ true_fgr = torch.from_numpy(true_fgr).float().div_(255)
145
+ pred_fgr = torch.from_numpy(pred_fgr).float().div_(255)
146
+
147
+ true_msk = true_pha > 0
148
+ metrics['fgr_mse'].append(self.mse(pred_fgr[true_msk], true_fgr[true_msk]))
149
+
150
+ return metrics
151
+
152
+
153
+ class MetricMAD:
154
+ def __call__(self, pred, true):
155
+ return (pred - true).abs_().mean() * 1e3
156
+
157
+
158
+ class MetricMSE:
159
+ def __call__(self, pred, true):
160
+ return ((pred - true) ** 2).mean() * 1e3
161
+
162
+
163
+ class MetricGRAD:
164
+ def __init__(self, sigma=1.4):
165
+ self.filter_x, self.filter_y = self.gauss_filter(sigma)
166
+ self.filter_x = torch.from_numpy(self.filter_x).unsqueeze(0).cuda()
167
+ self.filter_y = torch.from_numpy(self.filter_y).unsqueeze(0).cuda()
168
+
169
+ def __call__(self, pred, true):
170
+ true_grad = self.gauss_gradient(true)
171
+ pred_grad = self.gauss_gradient(pred)
172
+ return ((true_grad - pred_grad) ** 2).sum() / 1000
173
+
174
+ def gauss_gradient(self, img):
175
+ img_filtered_x = kornia.filters.filter2D(img[None, None, :, :], self.filter_x, border_type='replicate')[0, 0]
176
+ img_filtered_y = kornia.filters.filter2D(img[None, None, :, :], self.filter_y, border_type='replicate')[0, 0]
177
+ return (img_filtered_x**2 + img_filtered_y**2).sqrt()
178
+
179
+ @staticmethod
180
+ def gauss_filter(sigma, epsilon=1e-2):
181
+ half_size = np.ceil(sigma * np.sqrt(-2 * np.log(np.sqrt(2 * np.pi) * sigma * epsilon)))
182
+ size = np.int(2 * half_size + 1)
183
+
184
+ # create filter in x axis
185
+ filter_x = np.zeros((size, size))
186
+ for i in range(size):
187
+ for j in range(size):
188
+ filter_x[i, j] = MetricGRAD.gaussian(i - half_size, sigma) * MetricGRAD.dgaussian(
189
+ j - half_size, sigma)
190
+
191
+ # normalize filter
192
+ norm = np.sqrt((filter_x**2).sum())
193
+ filter_x = filter_x / norm
194
+ filter_y = np.transpose(filter_x)
195
+
196
+ return filter_x, filter_y
197
+
198
+ @staticmethod
199
+ def gaussian(x, sigma):
200
+ return np.exp(-x**2 / (2 * sigma**2)) / (sigma * np.sqrt(2 * np.pi))
201
+
202
+ @staticmethod
203
+ def dgaussian(x, sigma):
204
+ return -x * MetricGRAD.gaussian(x, sigma) / sigma**2
205
+
206
+
207
+ class MetricDTSSD:
208
+ def __call__(self, pred_t, pred_tm1, true_t, true_tm1):
209
+ dtSSD = ((pred_t - pred_tm1) - (true_t - true_tm1)) ** 2
210
+ dtSSD = dtSSD.sum() / true_t.numel()
211
+ dtSSD = dtSSD.sqrt()
212
+ return dtSSD * 1e2
213
+
214
+
215
+ if __name__ == '__main__':
216
+ Evaluator()
RobustVideoMatting/evaluation/evaluate_lr.py ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ LR (Low-Resolution) evaluation.
3
+
4
+ Note, the script only does evaluation. You will need to first inference yourself and save the results to disk
5
+ Expected directory format for both prediction and ground-truth is:
6
+
7
+ videomatte_512x288
8
+ ├── videomatte_motion
9
+ ├── pha
10
+ ├── 0000
11
+ ├── 0000.png
12
+ ├── fgr
13
+ ├── 0000
14
+ ├── 0000.png
15
+ ├── videomatte_static
16
+ ├── pha
17
+ ├── 0000
18
+ ├── 0000.png
19
+ ├── fgr
20
+ ├── 0000
21
+ ├── 0000.png
22
+
23
+ Prediction must have the exact file structure and file name as the ground-truth,
24
+ meaning that if the ground-truth is png/jpg, prediction should be png/jpg.
25
+
26
+ Example usage:
27
+
28
+ python evaluate.py \
29
+ --pred-dir PATH_TO_PREDICTIONS/videomatte_512x288 \
30
+ --true-dir PATH_TO_GROUNDTURTH/videomatte_512x288
31
+
32
+ An excel sheet with evaluation results will be written to "PATH_TO_PREDICTIONS/videomatte_512x288/videomatte_512x288.xlsx"
33
+ """
34
+
35
+
36
+ import argparse
37
+ import os
38
+ import cv2
39
+ import numpy as np
40
+ import xlsxwriter
41
+ from concurrent.futures import ThreadPoolExecutor
42
+ from tqdm import tqdm
43
+
44
+
45
+ class Evaluator:
46
+ def __init__(self):
47
+ self.parse_args()
48
+ self.init_metrics()
49
+ self.evaluate()
50
+ self.write_excel()
51
+
52
+ def parse_args(self):
53
+ parser = argparse.ArgumentParser()
54
+ parser.add_argument('--pred-dir', type=str, required=True)
55
+ parser.add_argument('--true-dir', type=str, required=True)
56
+ parser.add_argument('--num-workers', type=int, default=48)
57
+ parser.add_argument('--metrics', type=str, nargs='+', default=[
58
+ 'pha_mad', 'pha_mse', 'pha_grad', 'pha_conn', 'pha_dtssd', 'fgr_mad', 'fgr_mse'])
59
+ self.args = parser.parse_args()
60
+
61
+ def init_metrics(self):
62
+ self.mad = MetricMAD()
63
+ self.mse = MetricMSE()
64
+ self.grad = MetricGRAD()
65
+ self.conn = MetricCONN()
66
+ self.dtssd = MetricDTSSD()
67
+
68
+ def evaluate(self):
69
+ tasks = []
70
+ position = 0
71
+
72
+ with ThreadPoolExecutor(max_workers=self.args.num_workers) as executor:
73
+ for dataset in sorted(os.listdir(self.args.pred_dir)):
74
+ if os.path.isdir(os.path.join(self.args.pred_dir, dataset)):
75
+ for clip in sorted(os.listdir(os.path.join(self.args.pred_dir, dataset))):
76
+ future = executor.submit(self.evaluate_worker, dataset, clip, position)
77
+ tasks.append((dataset, clip, future))
78
+ position += 1
79
+
80
+ self.results = [(dataset, clip, future.result()) for dataset, clip, future in tasks]
81
+
82
+ def write_excel(self):
83
+ workbook = xlsxwriter.Workbook(os.path.join(self.args.pred_dir, f'{os.path.basename(self.args.pred_dir)}.xlsx'))
84
+ summarysheet = workbook.add_worksheet('summary')
85
+ metricsheets = [workbook.add_worksheet(metric) for metric in self.results[0][2].keys()]
86
+
87
+ for i, metric in enumerate(self.results[0][2].keys()):
88
+ summarysheet.write(i, 0, metric)
89
+ summarysheet.write(i, 1, f'={metric}!B2')
90
+
91
+ for row, (dataset, clip, metrics) in enumerate(self.results):
92
+ for metricsheet, metric in zip(metricsheets, metrics.values()):
93
+ # Write the header
94
+ if row == 0:
95
+ metricsheet.write(1, 0, 'Average')
96
+ metricsheet.write(1, 1, f'=AVERAGE(C2:ZZ2)')
97
+ for col in range(len(metric)):
98
+ metricsheet.write(0, col + 2, col)
99
+ colname = xlsxwriter.utility.xl_col_to_name(col + 2)
100
+ metricsheet.write(1, col + 2, f'=AVERAGE({colname}3:{colname}9999)')
101
+
102
+ metricsheet.write(row + 2, 0, dataset)
103
+ metricsheet.write(row + 2, 1, clip)
104
+ metricsheet.write_row(row + 2, 2, metric)
105
+
106
+ workbook.close()
107
+
108
+ def evaluate_worker(self, dataset, clip, position):
109
+ framenames = sorted(os.listdir(os.path.join(self.args.pred_dir, dataset, clip, 'pha')))
110
+ metrics = {metric_name : [] for metric_name in self.args.metrics}
111
+
112
+ pred_pha_tm1 = None
113
+ true_pha_tm1 = None
114
+
115
+ for i, framename in enumerate(tqdm(framenames, desc=f'{dataset} {clip}', position=position, dynamic_ncols=True)):
116
+ true_pha = cv2.imread(os.path.join(self.args.true_dir, dataset, clip, 'pha', framename), cv2.IMREAD_GRAYSCALE).astype(np.float32) / 255
117
+ pred_pha = cv2.imread(os.path.join(self.args.pred_dir, dataset, clip, 'pha', framename), cv2.IMREAD_GRAYSCALE).astype(np.float32) / 255
118
+ if 'pha_mad' in self.args.metrics:
119
+ metrics['pha_mad'].append(self.mad(pred_pha, true_pha))
120
+ if 'pha_mse' in self.args.metrics:
121
+ metrics['pha_mse'].append(self.mse(pred_pha, true_pha))
122
+ if 'pha_grad' in self.args.metrics:
123
+ metrics['pha_grad'].append(self.grad(pred_pha, true_pha))
124
+ if 'pha_conn' in self.args.metrics:
125
+ metrics['pha_conn'].append(self.conn(pred_pha, true_pha))
126
+ if 'pha_dtssd' in self.args.metrics:
127
+ if i == 0:
128
+ metrics['pha_dtssd'].append(0)
129
+ else:
130
+ metrics['pha_dtssd'].append(self.dtssd(pred_pha, pred_pha_tm1, true_pha, true_pha_tm1))
131
+
132
+ pred_pha_tm1 = pred_pha
133
+ true_pha_tm1 = true_pha
134
+
135
+ if 'fgr_mse' in self.args.metrics or 'fgr_mad' in self.args.metrics:
136
+ true_fgr = cv2.imread(os.path.join(self.args.true_dir, dataset, clip, 'fgr', framename), cv2.IMREAD_COLOR).astype(np.float32) / 255
137
+ pred_fgr = cv2.imread(os.path.join(self.args.pred_dir, dataset, clip, 'fgr', framename), cv2.IMREAD_COLOR).astype(np.float32) / 255
138
+ true_msk = true_pha > 0
139
+
140
+ if 'fgr_mse' in self.args.metrics:
141
+ metrics['fgr_mse'].append(self.mse(pred_fgr[true_msk], true_fgr[true_msk]))
142
+ if 'fgr_mad' in self.args.metrics:
143
+ metrics['fgr_mad'].append(self.mad(pred_fgr[true_msk], true_fgr[true_msk]))
144
+
145
+ return metrics
146
+
147
+
148
+ class MetricMAD:
149
+ def __call__(self, pred, true):
150
+ return np.abs(pred - true).mean() * 1e3
151
+
152
+
153
+ class MetricMSE:
154
+ def __call__(self, pred, true):
155
+ return ((pred - true) ** 2).mean() * 1e3
156
+
157
+
158
+ class MetricGRAD:
159
+ def __init__(self, sigma=1.4):
160
+ self.filter_x, self.filter_y = self.gauss_filter(sigma)
161
+
162
+ def __call__(self, pred, true):
163
+ pred_normed = np.zeros_like(pred)
164
+ true_normed = np.zeros_like(true)
165
+ cv2.normalize(pred, pred_normed, 1., 0., cv2.NORM_MINMAX)
166
+ cv2.normalize(true, true_normed, 1., 0., cv2.NORM_MINMAX)
167
+
168
+ true_grad = self.gauss_gradient(true_normed).astype(np.float32)
169
+ pred_grad = self.gauss_gradient(pred_normed).astype(np.float32)
170
+
171
+ grad_loss = ((true_grad - pred_grad) ** 2).sum()
172
+ return grad_loss / 1000
173
+
174
+ def gauss_gradient(self, img):
175
+ img_filtered_x = cv2.filter2D(img, -1, self.filter_x, borderType=cv2.BORDER_REPLICATE)
176
+ img_filtered_y = cv2.filter2D(img, -1, self.filter_y, borderType=cv2.BORDER_REPLICATE)
177
+ return np.sqrt(img_filtered_x**2 + img_filtered_y**2)
178
+
179
+ @staticmethod
180
+ def gauss_filter(sigma, epsilon=1e-2):
181
+ half_size = np.ceil(sigma * np.sqrt(-2 * np.log(np.sqrt(2 * np.pi) * sigma * epsilon)))
182
+ size = np.int(2 * half_size + 1)
183
+
184
+ # create filter in x axis
185
+ filter_x = np.zeros((size, size))
186
+ for i in range(size):
187
+ for j in range(size):
188
+ filter_x[i, j] = MetricGRAD.gaussian(i - half_size, sigma) * MetricGRAD.dgaussian(
189
+ j - half_size, sigma)
190
+
191
+ # normalize filter
192
+ norm = np.sqrt((filter_x**2).sum())
193
+ filter_x = filter_x / norm
194
+ filter_y = np.transpose(filter_x)
195
+
196
+ return filter_x, filter_y
197
+
198
+ @staticmethod
199
+ def gaussian(x, sigma):
200
+ return np.exp(-x**2 / (2 * sigma**2)) / (sigma * np.sqrt(2 * np.pi))
201
+
202
+ @staticmethod
203
+ def dgaussian(x, sigma):
204
+ return -x * MetricGRAD.gaussian(x, sigma) / sigma**2
205
+
206
+
207
+ class MetricCONN:
208
+ def __call__(self, pred, true):
209
+ step=0.1
210
+ thresh_steps = np.arange(0, 1 + step, step)
211
+ round_down_map = -np.ones_like(true)
212
+ for i in range(1, len(thresh_steps)):
213
+ true_thresh = true >= thresh_steps[i]
214
+ pred_thresh = pred >= thresh_steps[i]
215
+ intersection = (true_thresh & pred_thresh).astype(np.uint8)
216
+
217
+ # connected components
218
+ _, output, stats, _ = cv2.connectedComponentsWithStats(
219
+ intersection, connectivity=4)
220
+ # start from 1 in dim 0 to exclude background
221
+ size = stats[1:, -1]
222
+
223
+ # largest connected component of the intersection
224
+ omega = np.zeros_like(true)
225
+ if len(size) != 0:
226
+ max_id = np.argmax(size)
227
+ # plus one to include background
228
+ omega[output == max_id + 1] = 1
229
+
230
+ mask = (round_down_map == -1) & (omega == 0)
231
+ round_down_map[mask] = thresh_steps[i - 1]
232
+ round_down_map[round_down_map == -1] = 1
233
+
234
+ true_diff = true - round_down_map
235
+ pred_diff = pred - round_down_map
236
+ # only calculate difference larger than or equal to 0.15
237
+ true_phi = 1 - true_diff * (true_diff >= 0.15)
238
+ pred_phi = 1 - pred_diff * (pred_diff >= 0.15)
239
+
240
+ connectivity_error = np.sum(np.abs(true_phi - pred_phi))
241
+ return connectivity_error / 1000
242
+
243
+
244
+ class MetricDTSSD:
245
+ def __call__(self, pred_t, pred_tm1, true_t, true_tm1):
246
+ dtSSD = ((pred_t - pred_tm1) - (true_t - true_tm1)) ** 2
247
+ dtSSD = np.sum(dtSSD) / true_t.size
248
+ dtSSD = np.sqrt(dtSSD)
249
+ return dtSSD * 1e2
250
+
251
+
252
+
253
+ if __name__ == '__main__':
254
+ Evaluator()
RobustVideoMatting/evaluation/generate_imagematte_with_background_image.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ python generate_imagematte_with_background_image.py \
3
+ --imagematte-dir ../matting-data/Distinctions/test \
4
+ --background-dir ../matting-data/Backgrounds/valid \
5
+ --resolution 512 \
6
+ --out-dir ../matting-data/evaluation/distinction_static_sd/ \
7
+ --random-seed 10
8
+
9
+ Seed:
10
+ 10 - distinction-static
11
+ 11 - distinction-motion
12
+ 12 - adobe-static
13
+ 13 - adobe-motion
14
+
15
+ """
16
+
17
+ import argparse
18
+ import os
19
+ import pims
20
+ import numpy as np
21
+ import random
22
+ from PIL import Image
23
+ from tqdm import tqdm
24
+ from tqdm.contrib.concurrent import process_map
25
+ from torchvision import transforms
26
+ from torchvision.transforms import functional as F
27
+
28
+ parser = argparse.ArgumentParser()
29
+ parser.add_argument('--imagematte-dir', type=str, required=True)
30
+ parser.add_argument('--background-dir', type=str, required=True)
31
+ parser.add_argument('--num-samples', type=int, default=20)
32
+ parser.add_argument('--num-frames', type=int, default=100)
33
+ parser.add_argument('--resolution', type=int, required=True)
34
+ parser.add_argument('--out-dir', type=str, required=True)
35
+ parser.add_argument('--random-seed', type=int)
36
+ parser.add_argument('--extension', type=str, default='.png')
37
+ args = parser.parse_args()
38
+
39
+ random.seed(args.random_seed)
40
+
41
+ imagematte_filenames = os.listdir(os.path.join(args.imagematte_dir, 'fgr'))
42
+ background_filenames = os.listdir(args.background_dir)
43
+ random.shuffle(imagematte_filenames)
44
+ random.shuffle(background_filenames)
45
+
46
+
47
+ def lerp(a, b, percentage):
48
+ return a * (1 - percentage) + b * percentage
49
+
50
+ def motion_affine(*imgs):
51
+ config = dict(degrees=(-10, 10), translate=(0.1, 0.1),
52
+ scale_ranges=(0.9, 1.1), shears=(-5, 5), img_size=imgs[0][0].size)
53
+ angleA, (transXA, transYA), scaleA, (shearXA, shearYA) = transforms.RandomAffine.get_params(**config)
54
+ angleB, (transXB, transYB), scaleB, (shearXB, shearYB) = transforms.RandomAffine.get_params(**config)
55
+
56
+ T = len(imgs[0])
57
+ variation_over_time = random.random()
58
+ for t in range(T):
59
+ percentage = (t / (T - 1)) * variation_over_time
60
+ angle = lerp(angleA, angleB, percentage)
61
+ transX = lerp(transXA, transXB, percentage)
62
+ transY = lerp(transYA, transYB, percentage)
63
+ scale = lerp(scaleA, scaleB, percentage)
64
+ shearX = lerp(shearXA, shearXB, percentage)
65
+ shearY = lerp(shearYA, shearYB, percentage)
66
+ for img in imgs:
67
+ img[t] = F.affine(img[t], angle, (transX, transY), scale, (shearX, shearY), F.InterpolationMode.BILINEAR)
68
+ return imgs
69
+
70
+
71
+
72
+ def process(i):
73
+ imagematte_filename = imagematte_filenames[i % len(imagematte_filenames)]
74
+ background_filename = background_filenames[i % len(background_filenames)]
75
+
76
+ out_path = os.path.join(args.out_dir, str(i).zfill(4))
77
+ os.makedirs(os.path.join(out_path, 'fgr'), exist_ok=True)
78
+ os.makedirs(os.path.join(out_path, 'pha'), exist_ok=True)
79
+ os.makedirs(os.path.join(out_path, 'com'), exist_ok=True)
80
+ os.makedirs(os.path.join(out_path, 'bgr'), exist_ok=True)
81
+
82
+ with Image.open(os.path.join(args.background_dir, background_filename)) as bgr:
83
+ bgr = bgr.convert('RGB')
84
+
85
+ w, h = bgr.size
86
+ scale = args.resolution / min(h, w)
87
+ w, h = int(w * scale), int(h * scale)
88
+ bgr = bgr.resize((w, h))
89
+ bgr = F.center_crop(bgr, (args.resolution, args.resolution))
90
+
91
+ with Image.open(os.path.join(args.imagematte_dir, 'fgr', imagematte_filename)) as fgr, \
92
+ Image.open(os.path.join(args.imagematte_dir, 'pha', imagematte_filename)) as pha:
93
+ fgr = fgr.convert('RGB')
94
+ pha = pha.convert('L')
95
+
96
+ fgrs = [fgr] * args.num_frames
97
+ phas = [pha] * args.num_frames
98
+ fgrs, phas = motion_affine(fgrs, phas)
99
+
100
+ for t in tqdm(range(args.num_frames), desc=str(i).zfill(4)):
101
+ fgr = fgrs[t]
102
+ pha = phas[t]
103
+
104
+ w, h = fgr.size
105
+ scale = args.resolution / max(h, w)
106
+ w, h = int(w * scale), int(h * scale)
107
+
108
+ fgr = fgr.resize((w, h))
109
+ pha = pha.resize((w, h))
110
+
111
+ if h < args.resolution:
112
+ pt = (args.resolution - h) // 2
113
+ pb = args.resolution - h - pt
114
+ else:
115
+ pt = 0
116
+ pb = 0
117
+
118
+ if w < args.resolution:
119
+ pl = (args.resolution - w) // 2
120
+ pr = args.resolution - w - pl
121
+ else:
122
+ pl = 0
123
+ pr = 0
124
+
125
+ fgr = F.pad(fgr, [pl, pt, pr, pb])
126
+ pha = F.pad(pha, [pl, pt, pr, pb])
127
+
128
+ if i // len(imagematte_filenames) % 2 == 1:
129
+ fgr = fgr.transpose(Image.FLIP_LEFT_RIGHT)
130
+ pha = pha.transpose(Image.FLIP_LEFT_RIGHT)
131
+
132
+ fgr.save(os.path.join(out_path, 'fgr', str(t).zfill(4) + args.extension))
133
+ pha.save(os.path.join(out_path, 'pha', str(t).zfill(4) + args.extension))
134
+
135
+ if t == 0:
136
+ bgr.save(os.path.join(out_path, 'bgr', str(t).zfill(4) + args.extension))
137
+ else:
138
+ os.symlink(str(0).zfill(4) + args.extension, os.path.join(out_path, 'bgr', str(t).zfill(4) + args.extension))
139
+
140
+ pha = np.asarray(pha).astype(float)[:, :, None] / 255
141
+ com = Image.fromarray(np.uint8(np.asarray(fgr) * pha + np.asarray(bgr) * (1 - pha)))
142
+ com.save(os.path.join(out_path, 'com', str(t).zfill(4) + args.extension))
143
+
144
+
145
+ if __name__ == '__main__':
146
+ r = process_map(process, range(args.num_samples), max_workers=32)
RobustVideoMatting/evaluation/generate_imagematte_with_background_video.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ python generate_imagematte_with_background_video.py \
3
+ --imagematte-dir ../matting-data/Distinctions/test \
4
+ --background-dir ../matting-data/BackgroundVideos_mp4/test \
5
+ --resolution 512 \
6
+ --out-dir ../matting-data/evaluation/distinction_motion_sd/ \
7
+ --random-seed 11
8
+
9
+ Seed:
10
+ 10 - distinction-static
11
+ 11 - distinction-motion
12
+ 12 - adobe-static
13
+ 13 - adobe-motion
14
+
15
+ """
16
+
17
+ import argparse
18
+ import os
19
+ import pims
20
+ import numpy as np
21
+ import random
22
+ from multiprocessing import Pool
23
+ from PIL import Image
24
+ # from tqdm import tqdm
25
+ from tqdm.contrib.concurrent import process_map
26
+ from torchvision import transforms
27
+ from torchvision.transforms import functional as F
28
+
29
+ parser = argparse.ArgumentParser()
30
+ parser.add_argument('--imagematte-dir', type=str, required=True)
31
+ parser.add_argument('--background-dir', type=str, required=True)
32
+ parser.add_argument('--num-samples', type=int, default=20)
33
+ parser.add_argument('--num-frames', type=int, default=100)
34
+ parser.add_argument('--resolution', type=int, required=True)
35
+ parser.add_argument('--out-dir', type=str, required=True)
36
+ parser.add_argument('--random-seed', type=int)
37
+ parser.add_argument('--extension', type=str, default='.png')
38
+ args = parser.parse_args()
39
+
40
+ random.seed(args.random_seed)
41
+
42
+ imagematte_filenames = os.listdir(os.path.join(args.imagematte_dir, 'fgr'))
43
+ random.shuffle(imagematte_filenames)
44
+
45
+ background_filenames = [
46
+ "0000.mp4",
47
+ "0007.mp4",
48
+ "0008.mp4",
49
+ "0010.mp4",
50
+ "0013.mp4",
51
+ "0015.mp4",
52
+ "0016.mp4",
53
+ "0018.mp4",
54
+ "0021.mp4",
55
+ "0029.mp4",
56
+ "0033.mp4",
57
+ "0035.mp4",
58
+ "0039.mp4",
59
+ "0050.mp4",
60
+ "0052.mp4",
61
+ "0055.mp4",
62
+ "0060.mp4",
63
+ "0063.mp4",
64
+ "0087.mp4",
65
+ "0086.mp4",
66
+ "0090.mp4",
67
+ "0101.mp4",
68
+ "0110.mp4",
69
+ "0117.mp4",
70
+ "0120.mp4",
71
+ "0122.mp4",
72
+ "0123.mp4",
73
+ "0125.mp4",
74
+ "0128.mp4",
75
+ "0131.mp4",
76
+ "0172.mp4",
77
+ "0176.mp4",
78
+ "0181.mp4",
79
+ "0187.mp4",
80
+ "0193.mp4",
81
+ "0198.mp4",
82
+ "0220.mp4",
83
+ "0221.mp4",
84
+ "0224.mp4",
85
+ "0229.mp4",
86
+ "0233.mp4",
87
+ "0238.mp4",
88
+ "0241.mp4",
89
+ "0245.mp4",
90
+ "0246.mp4"
91
+ ]
92
+
93
+ random.shuffle(background_filenames)
94
+
95
+ def lerp(a, b, percentage):
96
+ return a * (1 - percentage) + b * percentage
97
+
98
+ def motion_affine(*imgs):
99
+ config = dict(degrees=(-10, 10), translate=(0.1, 0.1),
100
+ scale_ranges=(0.9, 1.1), shears=(-5, 5), img_size=imgs[0][0].size)
101
+ angleA, (transXA, transYA), scaleA, (shearXA, shearYA) = transforms.RandomAffine.get_params(**config)
102
+ angleB, (transXB, transYB), scaleB, (shearXB, shearYB) = transforms.RandomAffine.get_params(**config)
103
+
104
+ T = len(imgs[0])
105
+ variation_over_time = random.random()
106
+ for t in range(T):
107
+ percentage = (t / (T - 1)) * variation_over_time
108
+ angle = lerp(angleA, angleB, percentage)
109
+ transX = lerp(transXA, transXB, percentage)
110
+ transY = lerp(transYA, transYB, percentage)
111
+ scale = lerp(scaleA, scaleB, percentage)
112
+ shearX = lerp(shearXA, shearXB, percentage)
113
+ shearY = lerp(shearYA, shearYB, percentage)
114
+ for img in imgs:
115
+ img[t] = F.affine(img[t], angle, (transX, transY), scale, (shearX, shearY), F.InterpolationMode.BILINEAR)
116
+ return imgs
117
+
118
+
119
+ def process(i):
120
+ imagematte_filename = imagematte_filenames[i % len(imagematte_filenames)]
121
+ background_filename = background_filenames[i % len(background_filenames)]
122
+
123
+ bgrs = pims.PyAVVideoReader(os.path.join(args.background_dir, background_filename))
124
+
125
+ out_path = os.path.join(args.out_dir, str(i).zfill(4))
126
+ os.makedirs(os.path.join(out_path, 'fgr'), exist_ok=True)
127
+ os.makedirs(os.path.join(out_path, 'pha'), exist_ok=True)
128
+ os.makedirs(os.path.join(out_path, 'com'), exist_ok=True)
129
+ os.makedirs(os.path.join(out_path, 'bgr'), exist_ok=True)
130
+
131
+ with Image.open(os.path.join(args.imagematte_dir, 'fgr', imagematte_filename)) as fgr, \
132
+ Image.open(os.path.join(args.imagematte_dir, 'pha', imagematte_filename)) as pha:
133
+ fgr = fgr.convert('RGB')
134
+ pha = pha.convert('L')
135
+
136
+ fgrs = [fgr] * args.num_frames
137
+ phas = [pha] * args.num_frames
138
+ fgrs, phas = motion_affine(fgrs, phas)
139
+
140
+ for t in range(args.num_frames):
141
+ fgr = fgrs[t]
142
+ pha = phas[t]
143
+
144
+ w, h = fgr.size
145
+ scale = args.resolution / max(h, w)
146
+ w, h = int(w * scale), int(h * scale)
147
+
148
+ fgr = fgr.resize((w, h))
149
+ pha = pha.resize((w, h))
150
+
151
+ if h < args.resolution:
152
+ pt = (args.resolution - h) // 2
153
+ pb = args.resolution - h - pt
154
+ else:
155
+ pt = 0
156
+ pb = 0
157
+
158
+ if w < args.resolution:
159
+ pl = (args.resolution - w) // 2
160
+ pr = args.resolution - w - pl
161
+ else:
162
+ pl = 0
163
+ pr = 0
164
+
165
+ fgr = F.pad(fgr, [pl, pt, pr, pb])
166
+ pha = F.pad(pha, [pl, pt, pr, pb])
167
+
168
+ if i // len(imagematte_filenames) % 2 == 1:
169
+ fgr = fgr.transpose(Image.FLIP_LEFT_RIGHT)
170
+ pha = pha.transpose(Image.FLIP_LEFT_RIGHT)
171
+
172
+ fgr.save(os.path.join(out_path, 'fgr', str(t).zfill(4) + args.extension))
173
+ pha.save(os.path.join(out_path, 'pha', str(t).zfill(4) + args.extension))
174
+
175
+ bgr = Image.fromarray(bgrs[t]).convert('RGB')
176
+ w, h = bgr.size
177
+ scale = args.resolution / min(h, w)
178
+ w, h = int(w * scale), int(h * scale)
179
+ bgr = bgr.resize((w, h))
180
+ bgr = F.center_crop(bgr, (args.resolution, args.resolution))
181
+ bgr.save(os.path.join(out_path, 'bgr', str(t).zfill(4) + args.extension))
182
+
183
+ pha = np.asarray(pha).astype(float)[:, :, None] / 255
184
+ com = Image.fromarray(np.uint8(np.asarray(fgr) * pha + np.asarray(bgr) * (1 - pha)))
185
+ com.save(os.path.join(out_path, 'com', str(t).zfill(4) + args.extension))
186
+
187
+ if __name__ == '__main__':
188
+ r = process_map(process, range(args.num_samples), max_workers=10)
189
+
RobustVideoMatting/evaluation/generate_videomatte_with_background_image.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ python generate_videomatte_with_background_image.py \
3
+ --videomatte-dir ../matting-data/VideoMatte240K_JPEG_HD/test \
4
+ --background-dir ../matting-data/Backgrounds/valid \
5
+ --num-samples 25 \
6
+ --resize 512 288 \
7
+ --out-dir ../matting-data/evaluation/vidematte_static_sd/
8
+ """
9
+
10
+ import argparse
11
+ import os
12
+ import pims
13
+ import numpy as np
14
+ import random
15
+ from PIL import Image
16
+ from tqdm import tqdm
17
+
18
+ parser = argparse.ArgumentParser()
19
+ parser.add_argument('--videomatte-dir', type=str, required=True)
20
+ parser.add_argument('--background-dir', type=str, required=True)
21
+ parser.add_argument('--num-samples', type=int, default=20)
22
+ parser.add_argument('--num-frames', type=int, default=100)
23
+ parser.add_argument('--resize', type=int, default=None, nargs=2)
24
+ parser.add_argument('--out-dir', type=str, required=True)
25
+ parser.add_argument('--extension', type=str, default='.png')
26
+ args = parser.parse_args()
27
+
28
+ random.seed(10)
29
+
30
+ videomatte_filenames = [(clipname, sorted(os.listdir(os.path.join(args.videomatte_dir, 'fgr', clipname))))
31
+ for clipname in sorted(os.listdir(os.path.join(args.videomatte_dir, 'fgr')))]
32
+
33
+ background_filenames = os.listdir(args.background_dir)
34
+ random.shuffle(background_filenames)
35
+
36
+ for i in range(args.num_samples):
37
+
38
+ clipname, framenames = videomatte_filenames[i % len(videomatte_filenames)]
39
+
40
+ out_path = os.path.join(args.out_dir, str(i).zfill(4))
41
+ os.makedirs(os.path.join(out_path, 'fgr'), exist_ok=True)
42
+ os.makedirs(os.path.join(out_path, 'pha'), exist_ok=True)
43
+ os.makedirs(os.path.join(out_path, 'com'), exist_ok=True)
44
+ os.makedirs(os.path.join(out_path, 'bgr'), exist_ok=True)
45
+
46
+ with Image.open(os.path.join(args.background_dir, background_filenames[i])) as bgr:
47
+ bgr = bgr.convert('RGB')
48
+
49
+
50
+ base_t = random.choice(range(len(framenames) - args.num_frames))
51
+
52
+ for t in tqdm(range(args.num_frames), desc=str(i).zfill(4)):
53
+ with Image.open(os.path.join(args.videomatte_dir, 'fgr', clipname, framenames[base_t + t])) as fgr, \
54
+ Image.open(os.path.join(args.videomatte_dir, 'pha', clipname, framenames[base_t + t])) as pha:
55
+ fgr = fgr.convert('RGB')
56
+ pha = pha.convert('L')
57
+
58
+ if args.resize is not None:
59
+ fgr = fgr.resize(args.resize, Image.BILINEAR)
60
+ pha = pha.resize(args.resize, Image.BILINEAR)
61
+
62
+
63
+ if i // len(videomatte_filenames) % 2 == 1:
64
+ fgr = fgr.transpose(Image.FLIP_LEFT_RIGHT)
65
+ pha = pha.transpose(Image.FLIP_LEFT_RIGHT)
66
+
67
+ fgr.save(os.path.join(out_path, 'fgr', str(t).zfill(4) + args.extension))
68
+ pha.save(os.path.join(out_path, 'pha', str(t).zfill(4) + args.extension))
69
+
70
+ if t == 0:
71
+ bgr = bgr.resize(fgr.size, Image.BILINEAR)
72
+ bgr.save(os.path.join(out_path, 'bgr', str(t).zfill(4) + args.extension))
73
+ else:
74
+ os.symlink(str(0).zfill(4) + args.extension, os.path.join(out_path, 'bgr', str(t).zfill(4) + args.extension))
75
+
76
+ pha = np.asarray(pha).astype(float)[:, :, None] / 255
77
+ com = Image.fromarray(np.uint8(np.asarray(fgr) * pha + np.asarray(bgr) * (1 - pha)))
78
+ com.save(os.path.join(out_path, 'com', str(t).zfill(4) + args.extension))
RobustVideoMatting/evaluation/generate_videomatte_with_background_video.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ python generate_videomatte_with_background_video.py \
3
+ --videomatte-dir ../matting-data/VideoMatte240K_JPEG_HD/test \
4
+ --background-dir ../matting-data/BackgroundVideos_mp4/test \
5
+ --resize 512 288 \
6
+ --out-dir ../matting-data/evaluation/vidematte_motion_sd/
7
+ """
8
+
9
+ import argparse
10
+ import os
11
+ import pims
12
+ import numpy as np
13
+ import random
14
+ from PIL import Image
15
+ from tqdm import tqdm
16
+
17
+ parser = argparse.ArgumentParser()
18
+ parser.add_argument('--videomatte-dir', type=str, required=True)
19
+ parser.add_argument('--background-dir', type=str, required=True)
20
+ parser.add_argument('--num-samples', type=int, default=20)
21
+ parser.add_argument('--num-frames', type=int, default=100)
22
+ parser.add_argument('--resize', type=int, default=None, nargs=2)
23
+ parser.add_argument('--out-dir', type=str, required=True)
24
+ args = parser.parse_args()
25
+
26
+ # Hand selected a list of videos
27
+ background_filenames = [
28
+ "0000.mp4",
29
+ "0007.mp4",
30
+ "0008.mp4",
31
+ "0010.mp4",
32
+ "0013.mp4",
33
+ "0015.mp4",
34
+ "0016.mp4",
35
+ "0018.mp4",
36
+ "0021.mp4",
37
+ "0029.mp4",
38
+ "0033.mp4",
39
+ "0035.mp4",
40
+ "0039.mp4",
41
+ "0050.mp4",
42
+ "0052.mp4",
43
+ "0055.mp4",
44
+ "0060.mp4",
45
+ "0063.mp4",
46
+ "0087.mp4",
47
+ "0086.mp4",
48
+ "0090.mp4",
49
+ "0101.mp4",
50
+ "0110.mp4",
51
+ "0117.mp4",
52
+ "0120.mp4",
53
+ "0122.mp4",
54
+ "0123.mp4",
55
+ "0125.mp4",
56
+ "0128.mp4",
57
+ "0131.mp4",
58
+ "0172.mp4",
59
+ "0176.mp4",
60
+ "0181.mp4",
61
+ "0187.mp4",
62
+ "0193.mp4",
63
+ "0198.mp4",
64
+ "0220.mp4",
65
+ "0221.mp4",
66
+ "0224.mp4",
67
+ "0229.mp4",
68
+ "0233.mp4",
69
+ "0238.mp4",
70
+ "0241.mp4",
71
+ "0245.mp4",
72
+ "0246.mp4"
73
+ ]
74
+
75
+ random.seed(10)
76
+
77
+ videomatte_filenames = [(clipname, sorted(os.listdir(os.path.join(args.videomatte_dir, 'fgr', clipname))))
78
+ for clipname in sorted(os.listdir(os.path.join(args.videomatte_dir, 'fgr')))]
79
+
80
+ random.shuffle(background_filenames)
81
+
82
+ for i in range(args.num_samples):
83
+ bgrs = pims.PyAVVideoReader(os.path.join(args.background_dir, background_filenames[i % len(background_filenames)]))
84
+ clipname, framenames = videomatte_filenames[i % len(videomatte_filenames)]
85
+
86
+ out_path = os.path.join(args.out_dir, str(i).zfill(4))
87
+ os.makedirs(os.path.join(out_path, 'fgr'), exist_ok=True)
88
+ os.makedirs(os.path.join(out_path, 'pha'), exist_ok=True)
89
+ os.makedirs(os.path.join(out_path, 'com'), exist_ok=True)
90
+ os.makedirs(os.path.join(out_path, 'bgr'), exist_ok=True)
91
+
92
+ base_t = random.choice(range(len(framenames) - args.num_frames))
93
+
94
+ for t in tqdm(range(args.num_frames), desc=str(i).zfill(4)):
95
+ with Image.open(os.path.join(args.videomatte_dir, 'fgr', clipname, framenames[base_t + t])) as fgr, \
96
+ Image.open(os.path.join(args.videomatte_dir, 'pha', clipname, framenames[base_t + t])) as pha:
97
+ fgr = fgr.convert('RGB')
98
+ pha = pha.convert('L')
99
+
100
+ if args.resize is not None:
101
+ fgr = fgr.resize(args.resize, Image.BILINEAR)
102
+ pha = pha.resize(args.resize, Image.BILINEAR)
103
+
104
+
105
+ if i // len(videomatte_filenames) % 2 == 1:
106
+ fgr = fgr.transpose(Image.FLIP_LEFT_RIGHT)
107
+ pha = pha.transpose(Image.FLIP_LEFT_RIGHT)
108
+
109
+ fgr.save(os.path.join(out_path, 'fgr', str(t).zfill(4) + '.png'))
110
+ pha.save(os.path.join(out_path, 'pha', str(t).zfill(4) + '.png'))
111
+
112
+ bgr = Image.fromarray(bgrs[t])
113
+ bgr = bgr.resize(fgr.size, Image.BILINEAR)
114
+ bgr.save(os.path.join(out_path, 'bgr', str(t).zfill(4) + '.png'))
115
+
116
+ pha = np.asarray(pha).astype(float)[:, :, None] / 255
117
+ com = Image.fromarray(np.uint8(np.asarray(fgr) * pha + np.asarray(bgr) * (1 - pha)))
118
+ com.save(os.path.join(out_path, 'com', str(t).zfill(4) + '.png'))
RobustVideoMatting/hubconf.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Loading model
3
+ model = torch.hub.load("PeterL1n/RobustVideoMatting", "mobilenetv3")
4
+ model = torch.hub.load("PeterL1n/RobustVideoMatting", "resnet50")
5
+
6
+ Converter API
7
+ convert_video = torch.hub.load("PeterL1n/RobustVideoMatting", "converter")
8
+ """
9
+
10
+
11
+ dependencies = ['torch', 'torchvision']
12
+
13
+ import torch
14
+ from model import MattingNetwork
15
+
16
+
17
+ def mobilenetv3(pretrained: bool = True, progress: bool = True):
18
+ model = MattingNetwork('mobilenetv3')
19
+ if pretrained:
20
+ url = 'https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_mobilenetv3.pth'
21
+ model.load_state_dict(torch.hub.load_state_dict_from_url(url, map_location='cpu', progress=progress))
22
+ return model
23
+
24
+
25
+ def resnet50(pretrained: bool = True, progress: bool = True):
26
+ model = MattingNetwork('resnet50')
27
+ if pretrained:
28
+ url = 'https://github.com/PeterL1n/RobustVideoMatting/releases/download/v1.0.0/rvm_resnet50.pth'
29
+ model.load_state_dict(torch.hub.load_state_dict_from_url(url, map_location='cpu', progress=progress))
30
+ return model
31
+
32
+
33
+ def converter():
34
+ try:
35
+ from inference import convert_video
36
+ return convert_video
37
+ except ModuleNotFoundError as error:
38
+ print(error)
39
+ print('Please run "pip install av tqdm pims"')
RobustVideoMatting/inference.py ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ python inference.py \
3
+ --variant mobilenetv3 \
4
+ --checkpoint "CHECKPOINT" \
5
+ --device cuda \
6
+ --input-source "input.mp4" \
7
+ --output-type video \
8
+ --output-composition "composition.mp4" \
9
+ --output-alpha "alpha.mp4" \
10
+ --output-foreground "foreground.mp4" \
11
+ --output-video-mbps 4 \
12
+ --seq-chunk 1
13
+ """
14
+
15
+ import torch
16
+ import os
17
+ from torch.utils.data import DataLoader
18
+ from torchvision import transforms
19
+ from typing import Optional, Tuple
20
+ from tqdm.auto import tqdm
21
+
22
+ from inference_utils import VideoReader, VideoWriter, ImageSequenceReader, ImageSequenceWriter
23
+
24
+ def convert_video(model,
25
+ input_source: str,
26
+ input_resize: Optional[Tuple[int, int]] = None,
27
+ downsample_ratio: Optional[float] = None,
28
+ output_type: str = 'video',
29
+ output_composition: Optional[str] = None,
30
+ output_alpha: Optional[str] = None,
31
+ output_foreground: Optional[str] = None,
32
+ output_video_mbps: Optional[float] = None,
33
+ seq_chunk: int = 1,
34
+ num_workers: int = 0,
35
+ progress: bool = True,
36
+ device: Optional[str] = None,
37
+ dtype: Optional[torch.dtype] = None):
38
+
39
+ """
40
+ Args:
41
+ input_source:A video file, or an image sequence directory. Images must be sorted in accending order, support png and jpg.
42
+ input_resize: If provided, the input are first resized to (w, h).
43
+ downsample_ratio: The model's downsample_ratio hyperparameter. If not provided, model automatically set one.
44
+ output_type: Options: ["video", "png_sequence"].
45
+ output_composition:
46
+ The composition output path. File path if output_type == 'video'. Directory path if output_type == 'png_sequence'.
47
+ If output_type == 'video', the composition has green screen background.
48
+ If output_type == 'png_sequence'. the composition is RGBA png images.
49
+ output_alpha: The alpha output from the model.
50
+ output_foreground: The foreground output from the model.
51
+ seq_chunk: Number of frames to process at once. Increase it for better parallelism.
52
+ num_workers: PyTorch's DataLoader workers. Only use >0 for image input.
53
+ progress: Show progress bar.
54
+ device: Only need to manually provide if model is a TorchScript freezed model.
55
+ dtype: Only need to manually provide if model is a TorchScript freezed model.
56
+ """
57
+
58
+ assert downsample_ratio is None or (downsample_ratio > 0 and downsample_ratio <= 1), 'Downsample ratio must be between 0 (exclusive) and 1 (inclusive).'
59
+ assert any([output_composition, output_alpha, output_foreground]), 'Must provide at least one output.'
60
+ assert output_type in ['video', 'png_sequence'], 'Only support "video" and "png_sequence" output modes.'
61
+ assert seq_chunk >= 1, 'Sequence chunk must be >= 1'
62
+ assert num_workers >= 0, 'Number of workers must be >= 0'
63
+
64
+ # Initialize transform
65
+ if input_resize is not None:
66
+ transform = transforms.Compose([
67
+ transforms.Resize(input_resize[::-1]),
68
+ transforms.ToTensor()
69
+ ])
70
+ else:
71
+ transform = transforms.ToTensor()
72
+
73
+ # Initialize reader
74
+ if os.path.isfile(input_source):
75
+ source = VideoReader(input_source, transform)
76
+ else:
77
+ source = ImageSequenceReader(input_source, transform)
78
+ reader = DataLoader(source, batch_size=seq_chunk, pin_memory=True, num_workers=num_workers)
79
+
80
+ # Initialize writers
81
+ if output_type == 'video':
82
+ frame_rate = source.frame_rate if isinstance(source, VideoReader) else 30
83
+ output_video_mbps = 1 if output_video_mbps is None else output_video_mbps
84
+ if output_composition is not None:
85
+ writer_com = VideoWriter(
86
+ path=output_composition,
87
+ frame_rate=frame_rate,
88
+ bit_rate=int(output_video_mbps * 1000000))
89
+ if output_alpha is not None:
90
+ writer_pha = VideoWriter(
91
+ path=output_alpha,
92
+ frame_rate=frame_rate,
93
+ bit_rate=int(output_video_mbps * 1000000))
94
+ if output_foreground is not None:
95
+ writer_fgr = VideoWriter(
96
+ path=output_foreground,
97
+ frame_rate=frame_rate,
98
+ bit_rate=int(output_video_mbps * 1000000))
99
+ else:
100
+ if output_composition is not None:
101
+ writer_com = ImageSequenceWriter(output_composition, 'png')
102
+ if output_alpha is not None:
103
+ writer_pha = ImageSequenceWriter(output_alpha, 'png')
104
+ if output_foreground is not None:
105
+ writer_fgr = ImageSequenceWriter(output_foreground, 'png')
106
+
107
+ # Inference
108
+ model = model.eval()
109
+ if device is None or dtype is None:
110
+ param = next(model.parameters())
111
+ dtype = param.dtype
112
+ device = param.device
113
+
114
+ if (output_composition is not None) and (output_type == 'video'):
115
+ bgr = torch.tensor([120, 255, 155], device=device, dtype=dtype).div(255).view(1, 1, 3, 1, 1)
116
+
117
+ try:
118
+ with torch.no_grad():
119
+ bar = tqdm(total=len(source), disable=not progress, dynamic_ncols=True)
120
+ rec = [None] * 4
121
+ for src in reader:
122
+
123
+ if downsample_ratio is None:
124
+ downsample_ratio = auto_downsample_ratio(*src.shape[2:])
125
+
126
+ src = src.to(device, dtype, non_blocking=True).unsqueeze(0) # [B, T, C, H, W]
127
+ fgr, pha, *rec = model(src, *rec, downsample_ratio)
128
+
129
+ if output_foreground is not None:
130
+ writer_fgr.write(fgr[0])
131
+ if output_alpha is not None:
132
+ writer_pha.write(pha[0])
133
+ if output_composition is not None:
134
+ if output_type == 'video':
135
+ com = fgr * pha + bgr * (1 - pha)
136
+ else:
137
+ fgr = fgr * pha.gt(0)
138
+ com = torch.cat([fgr, pha], dim=-3)
139
+ writer_com.write(com[0])
140
+
141
+ bar.update(src.size(1))
142
+
143
+ finally:
144
+ # Clean up
145
+ if output_composition is not None:
146
+ writer_com.close()
147
+ if output_alpha is not None:
148
+ writer_pha.close()
149
+ if output_foreground is not None:
150
+ writer_fgr.close()
151
+
152
+
153
+ def auto_downsample_ratio(h, w):
154
+ """
155
+ Automatically find a downsample ratio so that the largest side of the resolution be 512px.
156
+ """
157
+ return min(512 / max(h, w), 1)
158
+
159
+
160
+ class Converter:
161
+ def __init__(self, variant: str, checkpoint: str, device: str):
162
+ self.model = MattingNetwork(variant).eval().to(device)
163
+ self.model.load_state_dict(torch.load(checkpoint, map_location=device))
164
+ self.model = torch.jit.script(self.model)
165
+ self.model = torch.jit.freeze(self.model)
166
+ self.device = device
167
+
168
+ def convert(self, *args, **kwargs):
169
+ convert_video(self.model, device=self.device, dtype=torch.float32, *args, **kwargs)
170
+
171
+ if __name__ == '__main__':
172
+ import argparse
173
+ from model import MattingNetwork
174
+
175
+ parser = argparse.ArgumentParser()
176
+ parser.add_argument('--variant', type=str, required=True, choices=['mobilenetv3', 'resnet50'])
177
+ parser.add_argument('--checkpoint', type=str, required=True)
178
+ parser.add_argument('--device', type=str, required=True)
179
+ parser.add_argument('--input-source', type=str, required=True)
180
+ parser.add_argument('--input-resize', type=int, default=None, nargs=2)
181
+ parser.add_argument('--downsample-ratio', type=float)
182
+ parser.add_argument('--output-composition', type=str)
183
+ parser.add_argument('--output-alpha', type=str)
184
+ parser.add_argument('--output-foreground', type=str)
185
+ parser.add_argument('--output-type', type=str, required=True, choices=['video', 'png_sequence'])
186
+ parser.add_argument('--output-video-mbps', type=int, default=1)
187
+ parser.add_argument('--seq-chunk', type=int, default=1)
188
+ parser.add_argument('--num-workers', type=int, default=0)
189
+ parser.add_argument('--disable-progress', action='store_true')
190
+ args = parser.parse_args()
191
+
192
+ converter = Converter(args.variant, args.checkpoint, args.device)
193
+ converter.convert(
194
+ input_source=args.input_source,
195
+ input_resize=args.input_resize,
196
+ downsample_ratio=args.downsample_ratio,
197
+ output_type=args.output_type,
198
+ output_composition=args.output_composition,
199
+ output_alpha=args.output_alpha,
200
+ output_foreground=args.output_foreground,
201
+ output_video_mbps=args.output_video_mbps,
202
+ seq_chunk=args.seq_chunk,
203
+ num_workers=args.num_workers,
204
+ progress=not args.disable_progress
205
+ )
206
+
207
+
RobustVideoMatting/inference_itw_rotate.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from model import MattingNetwork
3
+ from torch.utils.data import DataLoader
4
+ from torch.utils.data.dataset import Dataset
5
+ import glob
6
+ import os
7
+ import cv2
8
+ import pdb
9
+ import argparse
10
+
11
+ class ItwDataset(Dataset):
12
+ def __init__(self, input_pth, step, rotate):
13
+
14
+ self.input_pth_list = glob.glob(os.path.join(input_pth, '*.png')) + \
15
+ glob.glob(os.path.join(input_pth, '*.jpg'))
16
+ self.input_pth_list.sort()
17
+ self.input_pth_list = self.input_pth_list[::step]
18
+ self.rotate = rotate
19
+ # pdb.set_trace()
20
+ def __len__(self):
21
+ return len(self.input_pth_list)
22
+
23
+ def __getitem__(self, index):
24
+
25
+ render_path = self.input_pth_list[index]
26
+ # pdb.set_trace()
27
+ img = cv2.imread(render_path)
28
+ if self.rotate == '+90':
29
+ img = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
30
+ elif self.rotate == '-90':
31
+ img = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
32
+ elif self.rotate == '180':
33
+ img = cv2.rotate(img, cv2.ROTATE_180)
34
+ img = torch.from_numpy(img)
35
+ img = img.permute(2,0,1)/255.
36
+ img = img.unsqueeze(0)
37
+ # img = torch.flip(img, dims = [0])
38
+ # print(img.shape)
39
+ # img = img[::-1,...]
40
+ # img = img.unsqueeze(0)
41
+
42
+ return {
43
+ 'img': img,
44
+ 'file_name': os.path.basename(render_path)[:-4]
45
+ }
46
+
47
+ if __name__ == '__main__':
48
+
49
+ parser = argparse.ArgumentParser()
50
+ parser.add_argument('--input_pth', type = str)
51
+ parser.add_argument('--output_pth', type = str)
52
+ parser.add_argument('--device', type = str, default = 'cpu')
53
+ parser.add_argument('--step', type = int, default = 1)
54
+ parser.add_argument('--rotate', type = str, default = '')
55
+ args = parser.parse_args()
56
+ device = torch.device(f'cuda:{args.device}')
57
+ downsample_ratio = 0.4
58
+ model = MattingNetwork(variant='mobilenetv3').eval().to(device) # Or variant="resnet50"
59
+ model.load_state_dict(torch.load('./checkpoint/rvm_mobilenetv3.pth'))
60
+ rec = [None] * 4 # Initial recurrent states are None
61
+ frame_dataset = ItwDataset(args.input_pth, args.step, args.rotate)
62
+ # pdb.set_trace()
63
+ if not os.path.exists(args.output_pth):
64
+ os.makedirs(args.output_pth)
65
+ for data in frame_dataset:
66
+ save_img_pth = os.path.join(args.output_pth, data['file_name'] + '.png')
67
+ if os.path.exists(save_img_pth):
68
+ print(save_img_pth + ' exists!')
69
+ continue
70
+ # print('in')
71
+ with torch.no_grad():
72
+ fgr, pha, *rec = model(data['img'].to(device), *rec, downsample_ratio)
73
+ # pdb.set_trace()
74
+ mask_infer = torch.round(pha.repeat(1,3,1,1))*255
75
+ mask_infer = mask_infer.squeeze(0).permute(1,2,0).detach().cpu().numpy()
76
+ # pdb.set_trace()
77
+ cv2.imwrite(save_img_pth, mask_infer)
78
+ print(data['file_name'])
RobustVideoMatting/inference_speed_test.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ python inference_speed_test.py \
3
+ --model-variant mobilenetv3 \
4
+ --resolution 1920 1080 \
5
+ --downsample-ratio 0.25 \
6
+ --precision float32
7
+ """
8
+
9
+ import argparse
10
+ import torch
11
+ from tqdm import tqdm
12
+
13
+ from model.model import MattingNetwork
14
+
15
+ torch.backends.cudnn.benchmark = True
16
+
17
+ class InferenceSpeedTest:
18
+ def __init__(self):
19
+ self.parse_args()
20
+ self.init_model()
21
+ self.loop()
22
+
23
+ def parse_args(self):
24
+ parser = argparse.ArgumentParser()
25
+ parser.add_argument('--model-variant', type=str, required=True)
26
+ parser.add_argument('--resolution', type=int, required=True, nargs=2)
27
+ parser.add_argument('--downsample-ratio', type=float, required=True)
28
+ parser.add_argument('--precision', type=str, default='float32')
29
+ parser.add_argument('--disable-refiner', action='store_true')
30
+ self.args = parser.parse_args()
31
+
32
+ def init_model(self):
33
+ self.device = 'cuda'
34
+ self.precision = {'float32': torch.float32, 'float16': torch.float16}[self.args.precision]
35
+ self.model = MattingNetwork(self.args.model_variant)
36
+ self.model = self.model.to(device=self.device, dtype=self.precision).eval()
37
+ self.model = torch.jit.script(self.model)
38
+ self.model = torch.jit.freeze(self.model)
39
+
40
+ def loop(self):
41
+ w, h = self.args.resolution
42
+ src = torch.randn((1, 3, h, w), device=self.device, dtype=self.precision)
43
+ with torch.no_grad():
44
+ rec = None, None, None, None
45
+ for _ in tqdm(range(1000)):
46
+ fgr, pha, *rec = self.model(src, *rec, self.args.downsample_ratio)
47
+ torch.cuda.synchronize()
48
+
49
+ if __name__ == '__main__':
50
+ InferenceSpeedTest()
RobustVideoMatting/inference_utils.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import av
2
+ import os
3
+ import pims
4
+ import numpy as np
5
+ from torch.utils.data import Dataset
6
+ from torchvision.transforms.functional import to_pil_image
7
+ from PIL import Image
8
+
9
+
10
+ class VideoReader(Dataset):
11
+ def __init__(self, path, transform=None):
12
+ self.video = pims.PyAVVideoReader(path)
13
+ self.rate = self.video.frame_rate
14
+ self.transform = transform
15
+
16
+ @property
17
+ def frame_rate(self):
18
+ return self.rate
19
+
20
+ def __len__(self):
21
+ return len(self.video)
22
+
23
+ def __getitem__(self, idx):
24
+ frame = self.video[idx]
25
+ frame = Image.fromarray(np.asarray(frame))
26
+ if self.transform is not None:
27
+ frame = self.transform(frame)
28
+ return frame
29
+
30
+
31
+ class VideoWriter:
32
+ def __init__(self, path, frame_rate, bit_rate=1000000):
33
+ self.container = av.open(path, mode='w')
34
+ self.stream = self.container.add_stream('h264', rate=f'{frame_rate:.4f}')
35
+ self.stream.pix_fmt = 'yuv420p'
36
+ self.stream.bit_rate = bit_rate
37
+
38
+ def write(self, frames):
39
+ # frames: [T, C, H, W]
40
+ self.stream.width = frames.size(3)
41
+ self.stream.height = frames.size(2)
42
+ if frames.size(1) == 1:
43
+ frames = frames.repeat(1, 3, 1, 1) # convert grayscale to RGB
44
+ frames = frames.mul(255).byte().cpu().permute(0, 2, 3, 1).numpy()
45
+ for t in range(frames.shape[0]):
46
+ frame = frames[t]
47
+ frame = av.VideoFrame.from_ndarray(frame, format='rgb24')
48
+ self.container.mux(self.stream.encode(frame))
49
+
50
+ def close(self):
51
+ self.container.mux(self.stream.encode())
52
+ self.container.close()
53
+
54
+
55
+ class ImageSequenceReader(Dataset):
56
+ def __init__(self, path, transform=None):
57
+ self.path = path
58
+ self.files = sorted(os.listdir(path))
59
+ self.transform = transform
60
+
61
+ def __len__(self):
62
+ return len(self.files)
63
+
64
+ def __getitem__(self, idx):
65
+ with Image.open(os.path.join(self.path, self.files[idx])) as img:
66
+ img.load()
67
+ if self.transform is not None:
68
+ return self.transform(img)
69
+ return img
70
+
71
+
72
+ class ImageSequenceWriter:
73
+ def __init__(self, path, extension='jpg'):
74
+ self.path = path
75
+ self.extension = extension
76
+ self.counter = 0
77
+ os.makedirs(path, exist_ok=True)
78
+
79
+ def write(self, frames):
80
+ # frames: [T, C, H, W]
81
+ for t in range(frames.shape[0]):
82
+ to_pil_image(frames[t]).save(os.path.join(
83
+ self.path, str(self.counter).zfill(4) + '.' + self.extension))
84
+ self.counter += 1
85
+
86
+ def close(self):
87
+ pass
88
+
RobustVideoMatting/model/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .model import MattingNetwork
RobustVideoMatting/model/__pycache__/__init__.cpython-38.pyc ADDED
Binary file (204 Bytes). View file
 
RobustVideoMatting/model/__pycache__/decoder.cpython-38.pyc ADDED
Binary file (8.16 kB). View file
 
RobustVideoMatting/model/__pycache__/deep_guided_filter.cpython-38.pyc ADDED
Binary file (2.28 kB). View file
 
RobustVideoMatting/model/__pycache__/fast_guided_filter.cpython-38.pyc ADDED
Binary file (3.23 kB). View file
 
RobustVideoMatting/model/__pycache__/lraspp.cpython-38.pyc ADDED
Binary file (1.33 kB). View file
 
RobustVideoMatting/model/__pycache__/mobilenetv3.cpython-38.pyc ADDED
Binary file (2.78 kB). View file
 
RobustVideoMatting/model/__pycache__/model.cpython-38.pyc ADDED
Binary file (2.7 kB). View file
 
RobustVideoMatting/model/__pycache__/resnet.cpython-38.pyc ADDED
Binary file (1.91 kB). View file
 
RobustVideoMatting/model/decoder.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import Tensor
3
+ from torch import nn
4
+ from torch.nn import functional as F
5
+ from typing import Tuple, Optional
6
+
7
+ class RecurrentDecoder(nn.Module):
8
+ def __init__(self, feature_channels, decoder_channels):
9
+ super().__init__()
10
+ self.avgpool = AvgPool()
11
+ self.decode4 = BottleneckBlock(feature_channels[3])
12
+ self.decode3 = UpsamplingBlock(feature_channels[3], feature_channels[2], 3, decoder_channels[0])
13
+ self.decode2 = UpsamplingBlock(decoder_channels[0], feature_channels[1], 3, decoder_channels[1])
14
+ self.decode1 = UpsamplingBlock(decoder_channels[1], feature_channels[0], 3, decoder_channels[2])
15
+ self.decode0 = OutputBlock(decoder_channels[2], 3, decoder_channels[3])
16
+
17
+ def forward(self,
18
+ s0: Tensor, f1: Tensor, f2: Tensor, f3: Tensor, f4: Tensor,
19
+ r1: Optional[Tensor], r2: Optional[Tensor],
20
+ r3: Optional[Tensor], r4: Optional[Tensor]):
21
+ s1, s2, s3 = self.avgpool(s0)
22
+ x4, r4 = self.decode4(f4, r4)
23
+ x3, r3 = self.decode3(x4, f3, s3, r3)
24
+ x2, r2 = self.decode2(x3, f2, s2, r2)
25
+ x1, r1 = self.decode1(x2, f1, s1, r1)
26
+ x0 = self.decode0(x1, s0)
27
+ return x0, r1, r2, r3, r4
28
+
29
+
30
+ class AvgPool(nn.Module):
31
+ def __init__(self):
32
+ super().__init__()
33
+ self.avgpool = nn.AvgPool2d(2, 2, count_include_pad=False, ceil_mode=True)
34
+
35
+ def forward_single_frame(self, s0):
36
+ s1 = self.avgpool(s0)
37
+ s2 = self.avgpool(s1)
38
+ s3 = self.avgpool(s2)
39
+ return s1, s2, s3
40
+
41
+ def forward_time_series(self, s0):
42
+ B, T = s0.shape[:2]
43
+ s0 = s0.flatten(0, 1)
44
+ s1, s2, s3 = self.forward_single_frame(s0)
45
+ s1 = s1.unflatten(0, (B, T))
46
+ s2 = s2.unflatten(0, (B, T))
47
+ s3 = s3.unflatten(0, (B, T))
48
+ return s1, s2, s3
49
+
50
+ def forward(self, s0):
51
+ if s0.ndim == 5:
52
+ return self.forward_time_series(s0)
53
+ else:
54
+ return self.forward_single_frame(s0)
55
+
56
+
57
+ class BottleneckBlock(nn.Module):
58
+ def __init__(self, channels):
59
+ super().__init__()
60
+ self.channels = channels
61
+ self.gru = ConvGRU(channels // 2)
62
+
63
+ def forward(self, x, r: Optional[Tensor]):
64
+ a, b = x.split(self.channels // 2, dim=-3)
65
+ b, r = self.gru(b, r)
66
+ x = torch.cat([a, b], dim=-3)
67
+ return x, r
68
+
69
+
70
+ class UpsamplingBlock(nn.Module):
71
+ def __init__(self, in_channels, skip_channels, src_channels, out_channels):
72
+ super().__init__()
73
+ self.out_channels = out_channels
74
+ self.upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False)
75
+ self.conv = nn.Sequential(
76
+ nn.Conv2d(in_channels + skip_channels + src_channels, out_channels, 3, 1, 1, bias=False),
77
+ nn.BatchNorm2d(out_channels),
78
+ nn.ReLU(True),
79
+ )
80
+ self.gru = ConvGRU(out_channels // 2)
81
+
82
+ def forward_single_frame(self, x, f, s, r: Optional[Tensor]):
83
+ x = self.upsample(x)
84
+ x = x[:, :, :s.size(2), :s.size(3)]
85
+ x = torch.cat([x, f, s], dim=1)
86
+ x = self.conv(x)
87
+ a, b = x.split(self.out_channels // 2, dim=1)
88
+ b, r = self.gru(b, r)
89
+ x = torch.cat([a, b], dim=1)
90
+ return x, r
91
+
92
+ def forward_time_series(self, x, f, s, r: Optional[Tensor]):
93
+ B, T, _, H, W = s.shape
94
+ x = x.flatten(0, 1)
95
+ f = f.flatten(0, 1)
96
+ s = s.flatten(0, 1)
97
+ x = self.upsample(x)
98
+ x = x[:, :, :H, :W]
99
+ x = torch.cat([x, f, s], dim=1)
100
+ x = self.conv(x)
101
+ x = x.unflatten(0, (B, T))
102
+ a, b = x.split(self.out_channels // 2, dim=2)
103
+ b, r = self.gru(b, r)
104
+ x = torch.cat([a, b], dim=2)
105
+ return x, r
106
+
107
+ def forward(self, x, f, s, r: Optional[Tensor]):
108
+ if x.ndim == 5:
109
+ return self.forward_time_series(x, f, s, r)
110
+ else:
111
+ return self.forward_single_frame(x, f, s, r)
112
+
113
+
114
+ class OutputBlock(nn.Module):
115
+ def __init__(self, in_channels, src_channels, out_channels):
116
+ super().__init__()
117
+ self.upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False)
118
+ self.conv = nn.Sequential(
119
+ nn.Conv2d(in_channels + src_channels, out_channels, 3, 1, 1, bias=False),
120
+ nn.BatchNorm2d(out_channels),
121
+ nn.ReLU(True),
122
+ nn.Conv2d(out_channels, out_channels, 3, 1, 1, bias=False),
123
+ nn.BatchNorm2d(out_channels),
124
+ nn.ReLU(True),
125
+ )
126
+
127
+ def forward_single_frame(self, x, s):
128
+ x = self.upsample(x)
129
+ x = x[:, :, :s.size(2), :s.size(3)]
130
+ x = torch.cat([x, s], dim=1)
131
+ x = self.conv(x)
132
+ return x
133
+
134
+ def forward_time_series(self, x, s):
135
+ B, T, _, H, W = s.shape
136
+ x = x.flatten(0, 1)
137
+ s = s.flatten(0, 1)
138
+ x = self.upsample(x)
139
+ x = x[:, :, :H, :W]
140
+ x = torch.cat([x, s], dim=1)
141
+ x = self.conv(x)
142
+ x = x.unflatten(0, (B, T))
143
+ return x
144
+
145
+ def forward(self, x, s):
146
+ if x.ndim == 5:
147
+ return self.forward_time_series(x, s)
148
+ else:
149
+ return self.forward_single_frame(x, s)
150
+
151
+
152
+ class ConvGRU(nn.Module):
153
+ def __init__(self,
154
+ channels: int,
155
+ kernel_size: int = 3,
156
+ padding: int = 1):
157
+ super().__init__()
158
+ self.channels = channels
159
+ self.ih = nn.Sequential(
160
+ nn.Conv2d(channels * 2, channels * 2, kernel_size, padding=padding),
161
+ nn.Sigmoid()
162
+ )
163
+ self.hh = nn.Sequential(
164
+ nn.Conv2d(channels * 2, channels, kernel_size, padding=padding),
165
+ nn.Tanh()
166
+ )
167
+
168
+ def forward_single_frame(self, x, h):
169
+ r, z = self.ih(torch.cat([x, h], dim=1)).split(self.channels, dim=1)
170
+ c = self.hh(torch.cat([x, r * h], dim=1))
171
+ h = (1 - z) * h + z * c
172
+ return h, h
173
+
174
+ def forward_time_series(self, x, h):
175
+ o = []
176
+ for xt in x.unbind(dim=1):
177
+ ot, h = self.forward_single_frame(xt, h)
178
+ o.append(ot)
179
+ o = torch.stack(o, dim=1)
180
+ return o, h
181
+
182
+ def forward(self, x, h: Optional[Tensor]):
183
+ if h is None:
184
+ h = torch.zeros((x.size(0), x.size(-3), x.size(-2), x.size(-1)),
185
+ device=x.device, dtype=x.dtype)
186
+
187
+ if x.ndim == 5:
188
+ return self.forward_time_series(x, h)
189
+ else:
190
+ return self.forward_single_frame(x, h)
191
+
192
+
193
+ class Projection(nn.Module):
194
+ def __init__(self, in_channels, out_channels):
195
+ super().__init__()
196
+ self.conv = nn.Conv2d(in_channels, out_channels, 1)
197
+
198
+ def forward_single_frame(self, x):
199
+ return self.conv(x)
200
+
201
+ def forward_time_series(self, x):
202
+ B, T = x.shape[:2]
203
+ return self.conv(x.flatten(0, 1)).unflatten(0, (B, T))
204
+
205
+ def forward(self, x):
206
+ if x.ndim == 5:
207
+ return self.forward_time_series(x)
208
+ else:
209
+ return self.forward_single_frame(x)
210
+
RobustVideoMatting/model/deep_guided_filter.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+ from torch.nn import functional as F
4
+
5
+ """
6
+ Adopted from <https://github.com/wuhuikai/DeepGuidedFilter/>
7
+ """
8
+
9
+ class DeepGuidedFilterRefiner(nn.Module):
10
+ def __init__(self, hid_channels=16):
11
+ super().__init__()
12
+ self.box_filter = nn.Conv2d(4, 4, kernel_size=3, padding=1, bias=False, groups=4)
13
+ self.box_filter.weight.data[...] = 1 / 9
14
+ self.conv = nn.Sequential(
15
+ nn.Conv2d(4 * 2 + hid_channels, hid_channels, kernel_size=1, bias=False),
16
+ nn.BatchNorm2d(hid_channels),
17
+ nn.ReLU(True),
18
+ nn.Conv2d(hid_channels, hid_channels, kernel_size=1, bias=False),
19
+ nn.BatchNorm2d(hid_channels),
20
+ nn.ReLU(True),
21
+ nn.Conv2d(hid_channels, 4, kernel_size=1, bias=True)
22
+ )
23
+
24
+ def forward_single_frame(self, fine_src, base_src, base_fgr, base_pha, base_hid):
25
+ fine_x = torch.cat([fine_src, fine_src.mean(1, keepdim=True)], dim=1)
26
+ base_x = torch.cat([base_src, base_src.mean(1, keepdim=True)], dim=1)
27
+ base_y = torch.cat([base_fgr, base_pha], dim=1)
28
+
29
+ mean_x = self.box_filter(base_x)
30
+ mean_y = self.box_filter(base_y)
31
+ cov_xy = self.box_filter(base_x * base_y) - mean_x * mean_y
32
+ var_x = self.box_filter(base_x * base_x) - mean_x * mean_x
33
+
34
+ A = self.conv(torch.cat([cov_xy, var_x, base_hid], dim=1))
35
+ b = mean_y - A * mean_x
36
+
37
+ H, W = fine_src.shape[2:]
38
+ A = F.interpolate(A, (H, W), mode='bilinear', align_corners=False)
39
+ b = F.interpolate(b, (H, W), mode='bilinear', align_corners=False)
40
+
41
+ out = A * fine_x + b
42
+ fgr, pha = out.split([3, 1], dim=1)
43
+ return fgr, pha
44
+
45
+ def forward_time_series(self, fine_src, base_src, base_fgr, base_pha, base_hid):
46
+ B, T = fine_src.shape[:2]
47
+ fgr, pha = self.forward_single_frame(
48
+ fine_src.flatten(0, 1),
49
+ base_src.flatten(0, 1),
50
+ base_fgr.flatten(0, 1),
51
+ base_pha.flatten(0, 1),
52
+ base_hid.flatten(0, 1))
53
+ fgr = fgr.unflatten(0, (B, T))
54
+ pha = pha.unflatten(0, (B, T))
55
+ return fgr, pha
56
+
57
+ def forward(self, fine_src, base_src, base_fgr, base_pha, base_hid):
58
+ if fine_src.ndim == 5:
59
+ return self.forward_time_series(fine_src, base_src, base_fgr, base_pha, base_hid)
60
+ else:
61
+ return self.forward_single_frame(fine_src, base_src, base_fgr, base_pha, base_hid)
RobustVideoMatting/model/fast_guided_filter.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+ from torch.nn import functional as F
4
+
5
+ """
6
+ Adopted from <https://github.com/wuhuikai/DeepGuidedFilter/>
7
+ """
8
+
9
+ class FastGuidedFilterRefiner(nn.Module):
10
+ def __init__(self, *args, **kwargs):
11
+ super().__init__()
12
+ self.guilded_filter = FastGuidedFilter(1)
13
+
14
+ def forward_single_frame(self, fine_src, base_src, base_fgr, base_pha):
15
+ fine_src_gray = fine_src.mean(1, keepdim=True)
16
+ base_src_gray = base_src.mean(1, keepdim=True)
17
+
18
+ fgr, pha = self.guilded_filter(
19
+ torch.cat([base_src, base_src_gray], dim=1),
20
+ torch.cat([base_fgr, base_pha], dim=1),
21
+ torch.cat([fine_src, fine_src_gray], dim=1)).split([3, 1], dim=1)
22
+
23
+ return fgr, pha
24
+
25
+ def forward_time_series(self, fine_src, base_src, base_fgr, base_pha):
26
+ B, T = fine_src.shape[:2]
27
+ fgr, pha = self.forward_single_frame(
28
+ fine_src.flatten(0, 1),
29
+ base_src.flatten(0, 1),
30
+ base_fgr.flatten(0, 1),
31
+ base_pha.flatten(0, 1))
32
+ fgr = fgr.unflatten(0, (B, T))
33
+ pha = pha.unflatten(0, (B, T))
34
+ return fgr, pha
35
+
36
+ def forward(self, fine_src, base_src, base_fgr, base_pha, base_hid):
37
+ if fine_src.ndim == 5:
38
+ return self.forward_time_series(fine_src, base_src, base_fgr, base_pha)
39
+ else:
40
+ return self.forward_single_frame(fine_src, base_src, base_fgr, base_pha)
41
+
42
+
43
+ class FastGuidedFilter(nn.Module):
44
+ def __init__(self, r: int, eps: float = 1e-5):
45
+ super().__init__()
46
+ self.r = r
47
+ self.eps = eps
48
+ self.boxfilter = BoxFilter(r)
49
+
50
+ def forward(self, lr_x, lr_y, hr_x):
51
+ mean_x = self.boxfilter(lr_x)
52
+ mean_y = self.boxfilter(lr_y)
53
+ cov_xy = self.boxfilter(lr_x * lr_y) - mean_x * mean_y
54
+ var_x = self.boxfilter(lr_x * lr_x) - mean_x * mean_x
55
+ A = cov_xy / (var_x + self.eps)
56
+ b = mean_y - A * mean_x
57
+ A = F.interpolate(A, hr_x.shape[2:], mode='bilinear', align_corners=False)
58
+ b = F.interpolate(b, hr_x.shape[2:], mode='bilinear', align_corners=False)
59
+ return A * hr_x + b
60
+
61
+
62
+ class BoxFilter(nn.Module):
63
+ def __init__(self, r):
64
+ super(BoxFilter, self).__init__()
65
+ self.r = r
66
+
67
+ def forward(self, x):
68
+ # Note: The original implementation at <https://github.com/wuhuikai/DeepGuidedFilter/>
69
+ # uses faster box blur. However, it may not be friendly for ONNX export.
70
+ # We are switching to use simple convolution for box blur.
71
+ kernel_size = 2 * self.r + 1
72
+ kernel_x = torch.full((x.data.shape[1], 1, 1, kernel_size), 1 / kernel_size, device=x.device, dtype=x.dtype)
73
+ kernel_y = torch.full((x.data.shape[1], 1, kernel_size, 1), 1 / kernel_size, device=x.device, dtype=x.dtype)
74
+ x = F.conv2d(x, kernel_x, padding=(0, self.r), groups=x.data.shape[1])
75
+ x = F.conv2d(x, kernel_y, padding=(self.r, 0), groups=x.data.shape[1])
76
+ return x
RobustVideoMatting/model/lraspp.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from torch import nn
2
+
3
+ class LRASPP(nn.Module):
4
+ def __init__(self, in_channels, out_channels):
5
+ super().__init__()
6
+ self.aspp1 = nn.Sequential(
7
+ nn.Conv2d(in_channels, out_channels, 1, bias=False),
8
+ nn.BatchNorm2d(out_channels),
9
+ nn.ReLU(True)
10
+ )
11
+ self.aspp2 = nn.Sequential(
12
+ nn.AdaptiveAvgPool2d(1),
13
+ nn.Conv2d(in_channels, out_channels, 1, bias=False),
14
+ nn.Sigmoid()
15
+ )
16
+
17
+ def forward_single_frame(self, x):
18
+ return self.aspp1(x) * self.aspp2(x)
19
+
20
+ def forward_time_series(self, x):
21
+ B, T = x.shape[:2]
22
+ x = self.forward_single_frame(x.flatten(0, 1)).unflatten(0, (B, T))
23
+ return x
24
+
25
+ def forward(self, x):
26
+ if x.ndim == 5:
27
+ return self.forward_time_series(x)
28
+ else:
29
+ return self.forward_single_frame(x)
RobustVideoMatting/model/mobilenetv3.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+ from torchvision.models.mobilenetv3 import MobileNetV3, InvertedResidualConfig
4
+ from torchvision.transforms.functional import normalize
5
+
6
+ class MobileNetV3LargeEncoder(MobileNetV3):
7
+ def __init__(self, pretrained: bool = False):
8
+ super().__init__(
9
+ inverted_residual_setting=[
10
+ InvertedResidualConfig( 16, 3, 16, 16, False, "RE", 1, 1, 1),
11
+ InvertedResidualConfig( 16, 3, 64, 24, False, "RE", 2, 1, 1), # C1
12
+ InvertedResidualConfig( 24, 3, 72, 24, False, "RE", 1, 1, 1),
13
+ InvertedResidualConfig( 24, 5, 72, 40, True, "RE", 2, 1, 1), # C2
14
+ InvertedResidualConfig( 40, 5, 120, 40, True, "RE", 1, 1, 1),
15
+ InvertedResidualConfig( 40, 5, 120, 40, True, "RE", 1, 1, 1),
16
+ InvertedResidualConfig( 40, 3, 240, 80, False, "HS", 2, 1, 1), # C3
17
+ InvertedResidualConfig( 80, 3, 200, 80, False, "HS", 1, 1, 1),
18
+ InvertedResidualConfig( 80, 3, 184, 80, False, "HS", 1, 1, 1),
19
+ InvertedResidualConfig( 80, 3, 184, 80, False, "HS", 1, 1, 1),
20
+ InvertedResidualConfig( 80, 3, 480, 112, True, "HS", 1, 1, 1),
21
+ InvertedResidualConfig(112, 3, 672, 112, True, "HS", 1, 1, 1),
22
+ InvertedResidualConfig(112, 5, 672, 160, True, "HS", 2, 2, 1), # C4
23
+ InvertedResidualConfig(160, 5, 960, 160, True, "HS", 1, 2, 1),
24
+ InvertedResidualConfig(160, 5, 960, 160, True, "HS", 1, 2, 1),
25
+ ],
26
+ last_channel=1280
27
+ )
28
+
29
+ if pretrained:
30
+ self.load_state_dict(torch.hub.load_state_dict_from_url(
31
+ 'https://download.pytorch.org/models/mobilenet_v3_large-8738ca79.pth'))
32
+
33
+ del self.avgpool
34
+ del self.classifier
35
+
36
+ def forward_single_frame(self, x):
37
+ x = normalize(x, [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
38
+
39
+ x = self.features[0](x)
40
+ x = self.features[1](x)
41
+ f1 = x
42
+ x = self.features[2](x)
43
+ x = self.features[3](x)
44
+ f2 = x
45
+ x = self.features[4](x)
46
+ x = self.features[5](x)
47
+ x = self.features[6](x)
48
+ f3 = x
49
+ x = self.features[7](x)
50
+ x = self.features[8](x)
51
+ x = self.features[9](x)
52
+ x = self.features[10](x)
53
+ x = self.features[11](x)
54
+ x = self.features[12](x)
55
+ x = self.features[13](x)
56
+ x = self.features[14](x)
57
+ x = self.features[15](x)
58
+ x = self.features[16](x)
59
+ f4 = x
60
+ return [f1, f2, f3, f4]
61
+
62
+ def forward_time_series(self, x):
63
+ B, T = x.shape[:2]
64
+ features = self.forward_single_frame(x.flatten(0, 1))
65
+ features = [f.unflatten(0, (B, T)) for f in features]
66
+ return features
67
+
68
+ def forward(self, x):
69
+ if x.ndim == 5:
70
+ return self.forward_time_series(x)
71
+ else:
72
+ return self.forward_single_frame(x)