From 7d14b88ce9f3126153088c6c61c7683e9d233707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=AD=E9=A6=96=E5=8D=9A?= Date: Wed, 28 Jun 2023 14:32:56 +0000 Subject: [PATCH 1/3] =?UTF-8?q?BasicVSR++=E9=A6=96=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 彭首博 --- PyTorch/contrib/cv/video/BasicVSR++/LICENSE | 236 ++++++++++++++ PyTorch/contrib/cv/video/BasicVSR++/README.md | 175 ++++++++++ .../_base_/datasets/basicvsr_test_config.py | 182 +++++++++++ .../configs/_base_/default_runtime.py | 39 +++ .../configs/basicvsr/basicvsr_2xb4_reds4.py | 119 +++++++ .../basicvsr-pp_c64n7_8xb1-600k_reds4.py | 46 +++ .../cv/video/BasicVSR++/requirements.txt | 3 + .../cv/video/BasicVSR++/requirements/docs.txt | 10 + .../BasicVSR++/requirements/mminstall.txt | 2 + .../BasicVSR++/requirements/optional.txt | 6 + .../BasicVSR++/requirements/readthedocs.txt | 14 + .../video/BasicVSR++/requirements/runtime.txt | 19 ++ .../video/BasicVSR++/requirements/tests.txt | 14 + .../reds/preprocess_reds_dataset.py | 305 ++++++++++++++++++ .../cv/video/BasicVSR++/tools/train.py | 111 +++++++ 15 files changed, 1281 insertions(+) create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/LICENSE create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/README.md create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/configs/_base_/datasets/basicvsr_test_config.py create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/configs/_base_/default_runtime.py create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/configs/basicvsr/basicvsr_2xb4_reds4.py create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/configs/basicvsr_pp/basicvsr-pp_c64n7_8xb1-600k_reds4.py create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/requirements.txt create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/requirements/docs.txt create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/requirements/mminstall.txt create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/requirements/optional.txt create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/requirements/readthedocs.txt create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/requirements/runtime.txt create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/requirements/tests.txt create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/tools/dataset_converters/reds/preprocess_reds_dataset.py create mode 100644 PyTorch/contrib/cv/video/BasicVSR++/tools/train.py diff --git a/PyTorch/contrib/cv/video/BasicVSR++/LICENSE b/PyTorch/contrib/cv/video/BasicVSR++/LICENSE new file mode 100644 index 0000000000..8719b270aa --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/LICENSE @@ -0,0 +1,236 @@ +'''# -*- coding: utf-8 -*- +# BSD 3-Clause License +# +# Copyright (c) 2017 +# All rights reserved. +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ========================================================================== +''' +Copyright (c) MMEditing Authors. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 MMEditing Authors. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/PyTorch/contrib/cv/video/BasicVSR++/README.md b/PyTorch/contrib/cv/video/BasicVSR++/README.md new file mode 100644 index 0000000000..e5a4f86ee2 --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/README.md @@ -0,0 +1,175 @@ +# BasicVSR for PyTorch + +- [概述](概述.md) +- [准备训练环境](准备训练环境.md) +- [开始训练](开始训练.md) +- [训练结果展示](训练结果展示.md) +- [版本说明](版本说明.md) + + + +# 概述 + +## 简述 + +循环结构是视频超分辨率任务的常用框架选择。最先进的方法BasicVSR采用双向传播和特征对齐,以有效地利用来自整个输入视频的信息。在这项研究中,我们通过提出二阶网格传播和流引导的可变形对齐来重新设计BasicVSR。我们表明,通过增强的传播和对齐来增强循环框架,可以更有效地利用未对齐视频帧的时空信息。新组件可在类似的计算约束下提高性能。 + +- 适配昇腾 AI 处理器的实现: + + ``` + url=https://gitee.com/ascend/ModelZoo-PyTorch.git + code_path=PyTorch/contrib/cv/detection + ``` + +- 通过Git获取代码方法如下: + + ``` + # Basic所属包为MMEditing包,该包目前以与MMGeneration包和并成为MMagic包。 + # 本项目使用代码为MMEditing-1.0.0rc6代码 + # 本项目依赖包mmcv-2.0.0rc4和MMengine0.6.0 + # 代码相关链接如下,可点击DownloadZIP进行下载,也可Git获取代码 + https://github.com/open-mmlab/mmagic/tree/v1.0.0rc6 #MMEditing + https://github.com/open-mmlab/mmcv/tree/v2.0.0rc4 #MMcv + ``` + +# 准备训练环境 + +## 准备环境 + +- 环境准备指导。 + + 请参考《[Pytorch框架训练环境准备](https://www.hiascend.com/document/detail/zh/ModelZoo/pytorchframework/ptes)》。 + + 安装依赖版本如下: + + **表 1** 版本支持表 + + | name| 依赖环境 | + |----------------------------------| ----------------------------------| + | PyTorch 1.8 | torchvision==0.6.0;pillow==8.4.0 | + | mmedit 1.0.0rc6 | mmcv==2.0.0rc4;mmengine==0.6.0 | + +- 安装依赖。 + + 在模型源码包根目录下执行命令,安装模型对应版本需要的依赖 + ``` + # 首先安装MMCV + # 解压压缩包 + unzip mmcv-2.x.zip + cd mmcv-2.x/ + # 安装到库中 + python3.7 setup.py install + + # 安装MMEditing + # 解压压缩包 + unzip mmediting-1.x.zip + cd mmediting-1.x/ + # 安装到库中 + python3.7 setup.py install + + # 安装MMengine + pip3.7 install mmengine==0.6.0 + + ``` + + +## 准备数据集 + +1. 获取数据集。 + ``` + # 下载REDS数据集 + https://seungjunnah.github.io/Datasets/reds.html + # 下载train_sharp val_sharp train_sharp_bicubic val_sharp_bicubic四个数据集 + https://drive.google.com/open?id=1YLksKtMhd2mWyVSkvhDaDLWSc1qYNCz- # train_sharp.zip + https://drive.google.com/open?id=1MGeObVQ1-Z29f-myDP7-8c3u0_xECKXq # val_sharp.zip + https://drive.google.com/open?id=1a4PrjqT-hShvY9IyJm3sPF0ZaXyrCozR # train_sharp_bicubic.zip + https://drive.google.com/open?id=1sChhtzN9Css10gX7Xsmc2JaC-2Pzco6a # val_sharp_bicubic.zip + + # 创建文件夹REDS,将四个压缩文件放入REDS文件夹即可 + ``` +2. 数据预处理。 + 需要使用MMEditing提供的方案预处理数据集。 + ``` + # 找到处理文件preprocess_reds_dataset.py,进入文件所在路径 + cd mmediting-1.x/tools/dataset_converters/reds/ + + # 执行文件,处理数据集 + python3.7 preprocess_reds_dataset.py --root-path={REDS文件夹所属绝对路径或相对路径}/REDS + + # 得到输出如下(简化版) + Unzip data/REDS\train_sharp.zip to data/REDS\train_sharp + Unzip data/REDS\train_sharp_bicubic.zip to data/REDS\train_sharp_bicubic + Unzip data/REDS\val_sharp_bicubic.zip to data/REDS\val_sharp_bicubic + Unzip data/REDS\val_sharp.zip to data/REDS\val_sharp + Move data/REDS\val_sharp to data/REDS\train_sharp... + Remove data/REDS\val_sharp + Move data/REDS\val_sharp_bicubic\X4 to data/REDS\train_sharp_bicubic\X4... + Remove data/REDS\val_sharp_bicubic + Generate annotation files meta_info_REDS_GT.txt... + + # 自动完成解压整理即生成标注文件 + ``` + +3. 配置文件处理。 + ``` + # 进入路径configs/basicvsr + cd mmediting-1.x/configs/basicvsr/ + # 编辑文件 basicvsr_2xb4_reds4.py + # 将 data_root 编辑为本设备所属路径,以本文设备为例 + data_root = '/home/test_user07/REDS' + # BasicVSR++的配置文件会复用BasicVSR配置文件中的信息 + ``` + +# 开始训练 + +## 训练模型 + +1. 进入解压后的源码包tools目录。 + + ``` + cd mmediting-1.x/tools/ + ``` + +2. 运行训练脚本。 + + 启动训练。 + + ``` + python3.7 train.py ../configs/basicvsr_pp/basicvsr-pp_c64n7_8xb1-600k_reds4.py --amp + ``` + + 训练完成后,会在tools目录下保存模型权重文件,并输出模型训练精度和性能信息。 + +# 训练结果展示 + +在训练5,000个epoch情况下的结果: +**表 2** 训练结果展示表 + +| Method | PSNR | SSIM | +|--------|---------|-------:| +| NPU | 29.5969 | 0.8541 | +| 竞品V | 29.5860 | 0.8544 | + + +# 版本说明 + +## 变更 + +2023.6.20:首次发布 + +## 已知问题 + +**_当前发行版本中存在的问题描述。_** + +无 + + + + + + + + + + + diff --git a/PyTorch/contrib/cv/video/BasicVSR++/configs/_base_/datasets/basicvsr_test_config.py b/PyTorch/contrib/cv/video/BasicVSR++/configs/_base_/datasets/basicvsr_test_config.py new file mode 100644 index 0000000000..14804c01eb --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/configs/_base_/datasets/basicvsr_test_config.py @@ -0,0 +1,182 @@ +# configs for REDS4 +reds_data_root = 'data/REDS' + +reds_pipeline = [ + dict(type='GenerateSegmentIndices', interval_list=[1]), + dict(type='LoadImageFromFile', key='img', channel_order='rgb'), + dict(type='LoadImageFromFile', key='gt', channel_order='rgb'), + dict(type='PackEditInputs') +] + +reds_dataloader = dict( + num_workers=1, + batch_size=1, + persistent_workers=False, + sampler=dict(type='DefaultSampler', shuffle=False), + dataset=dict( + type='BasicFramesDataset', + metainfo=dict(dataset_type='reds_reds4', task_name='vsr'), + data_root=reds_data_root, + data_prefix=dict(img='train_sharp_bicubic/X4', gt='train_sharp'), + ann_file='meta_info_reds4_val.txt', + depth=1, + num_input_frames=100, + fixed_seq_len=100, + pipeline=reds_pipeline)) + +reds_evaluator = [ + dict(type='PSNR', prefix='REDS4-BIx4-RGB'), + dict(type='SSIM', prefix='REDS4-BIx4-RGB') +] + +# configs for vimeo90k-bd and vimeo90k-bi +vimeo_90k_data_root = 'data/vimeo90k' +vimeo_90k_file_list = [ + 'im1.png', 'im2.png', 'im3.png', 'im4.png', 'im5.png', 'im6.png', 'im7.png' +] + +vimeo_90k_pipeline = [ + dict(type='LoadImageFromFile', key='img', channel_order='rgb'), + dict(type='LoadImageFromFile', key='gt', channel_order='rgb'), + dict(type='MirrorSequence', keys=['img']), + dict(type='PackEditInputs') +] + +vimeo_90k_bd_dataloader = dict( + num_workers=1, + batch_size=1, + persistent_workers=False, + sampler=dict(type='DefaultSampler', shuffle=False), + dataset=dict( + type='BasicFramesDataset', + metainfo=dict(dataset_type='vimeo90k_seq', task_name='vsr'), + data_root=vimeo_90k_data_root, + data_prefix=dict(img='BDx4', gt='GT'), + ann_file='meta_info_Vimeo90K_test_GT.txt', + depth=2, + num_input_frames=7, + fixed_seq_len=7, + load_frames_list=dict(img=vimeo_90k_file_list, gt=['im4.png']), + pipeline=vimeo_90k_pipeline)) + +vimeo_90k_bi_dataloader = dict( + num_workers=1, + batch_size=1, + persistent_workers=False, + sampler=dict(type='DefaultSampler', shuffle=False), + dataset=dict( + type='BasicFramesDataset', + metainfo=dict(dataset_type='vimeo90k_seq', task_name='vsr'), + data_root=vimeo_90k_data_root, + data_prefix=dict(img='BIx4', gt='GT'), + ann_file='meta_info_Vimeo90K_test_GT.txt', + depth=2, + num_input_frames=7, + fixed_seq_len=7, + load_frames_list=dict(img=vimeo_90k_file_list, gt=['im4.png']), + pipeline=vimeo_90k_pipeline)) + +vimeo_90k_bd_evaluator = [ + dict(type='PSNR', convert_to='Y', prefix='Vimeo-90K-T-BDx4-Y'), + dict(type='SSIM', convert_to='Y', prefix='Vimeo-90K-T-BDx4-Y'), +] + +vimeo_90k_bi_evaluator = [ + dict(type='PSNR', convert_to='Y', prefix='Vimeo-90K-T-BIx4-Y'), + dict(type='SSIM', convert_to='Y', prefix='Vimeo-90K-T-BIx4-Y'), +] + +# config for UDM10 (BDx4) +udm10_data_root = 'data/UDM10' + +udm10_pipeline = [ + dict( + type='GenerateSegmentIndices', + interval_list=[1], + filename_tmpl='{:04d}.png'), + dict(type='LoadImageFromFile', key='img', channel_order='rgb'), + dict(type='LoadImageFromFile', key='gt', channel_order='rgb'), + dict(type='PackEditInputs') +] + +udm10_dataloader = dict( + num_workers=1, + batch_size=1, + persistent_workers=False, + sampler=dict(type='DefaultSampler', shuffle=False), + dataset=dict( + type='BasicFramesDataset', + metainfo=dict(dataset_type='udm10', task_name='vsr'), + data_root=udm10_data_root, + data_prefix=dict(img='BDx4', gt='GT'), + pipeline=udm10_pipeline)) + +udm10_evaluator = [ + dict(type='PSNR', convert_to='Y', prefix='UDM10-BDx4-Y'), + dict(type='SSIM', convert_to='Y', prefix='UDM10-BDx4-Y') +] + +# config for vid4 +vid4_data_root = 'data/Vid4' + +vid4_pipeline = [ + dict(type='GenerateSegmentIndices', interval_list=[1]), + dict(type='LoadImageFromFile', key='img', channel_order='rgb'), + dict(type='LoadImageFromFile', key='gt', channel_order='rgb'), + dict(type='PackEditInputs') +] +vid4_bd_dataloader = dict( + num_workers=1, + batch_size=1, + persistent_workers=False, + sampler=dict(type='DefaultSampler', shuffle=False), + dataset=dict( + type='BasicFramesDataset', + metainfo=dict(dataset_type='vid4', task_name='vsr'), + data_root=vid4_data_root, + data_prefix=dict(img='BDx4', gt='GT'), + ann_file='meta_info_Vid4_GT.txt', + depth=1, + pipeline=vid4_pipeline)) + +vid4_bi_dataloader = dict( + num_workers=1, + batch_size=1, + persistent_workers=False, + sampler=dict(type='DefaultSampler', shuffle=False), + dataset=dict( + type='BasicFramesDataset', + metainfo=dict(dataset_type='vid4', task_name='vsr'), + data_root=vid4_data_root, + data_prefix=dict(img='BIx4', gt='GT'), + ann_file='meta_info_Vid4_GT.txt', + depth=1, + pipeline=vid4_pipeline)) + +vid4_bd_evaluator = [ + dict(type='PSNR', convert_to='Y', prefix='VID4-BDx4-Y'), + dict(type='SSIM', convert_to='Y', prefix='VID4-BDx4-Y'), +] +vid4_bi_evaluator = [ + dict(type='PSNR', convert_to='Y', prefix='VID4-BIx4-Y'), + dict(type='SSIM', convert_to='Y', prefix='VID4-BIx4-Y'), +] + +# config for test +test_cfg = dict(type='EditTestLoop') +test_dataloader = [ + reds_dataloader, + vimeo_90k_bd_dataloader, + vimeo_90k_bi_dataloader, + udm10_dataloader, + vid4_bd_dataloader, + vid4_bi_dataloader, +] +test_evaluator = [ + reds_evaluator, + vimeo_90k_bd_evaluator, + vimeo_90k_bi_evaluator, + udm10_evaluator, + vid4_bd_evaluator, + vid4_bi_evaluator, +] diff --git a/PyTorch/contrib/cv/video/BasicVSR++/configs/_base_/default_runtime.py b/PyTorch/contrib/cv/video/BasicVSR++/configs/_base_/default_runtime.py new file mode 100644 index 0000000000..195e1e6d2e --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/configs/_base_/default_runtime.py @@ -0,0 +1,39 @@ +default_scope = 'mmedit' +save_dir = './work_dirs' + +default_hooks = dict( + timer=dict(type='EditIterTimerHook'), + logger=dict(type='LoggerHook', interval=100), + param_scheduler=dict(type='ParamSchedulerHook'), + checkpoint=dict( + type='CheckpointHook', + interval=5000, + out_dir=save_dir, + by_epoch=False, + max_keep_ckpts=10, + save_best='PSNR', + rule='greater', + ), + sampler_seed=dict(type='DistSamplerSeedHook'), +) + +env_cfg = dict( + cudnn_benchmark=False, + mp_cfg=dict(mp_start_method='fork', opencv_num_threads=4), + dist_cfg=dict(backend='nccl'), +) + +log_level = 'INFO' +log_processor = dict(type='EditLogProcessor', window_size=100, by_epoch=False) + +load_from = None +resume = False + +vis_backends = [dict(type='LocalVisBackend')] +visualizer = dict( + type='ConcatImageVisualizer', + vis_backends=vis_backends, + fn_key='gt_path', + img_keys=['gt_img', 'input', 'pred_img'], + bgr2rgb=True) +custom_hooks = [dict(type='BasicVisualizationHook', interval=1)] diff --git a/PyTorch/contrib/cv/video/BasicVSR++/configs/basicvsr/basicvsr_2xb4_reds4.py b/PyTorch/contrib/cv/video/BasicVSR++/configs/basicvsr/basicvsr_2xb4_reds4.py new file mode 100644 index 0000000000..53e37f3ec2 --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/configs/basicvsr/basicvsr_2xb4_reds4.py @@ -0,0 +1,119 @@ +_base_ = [ + '../_base_/default_runtime.py', + '../_base_/datasets/basicvsr_test_config.py' +] + +experiment_name = 'basicvsr_2xb4_reds4' +work_dir = f'./work_dirs/{experiment_name}' +save_dir = './work_dirs' + +scale = 4 + +# model settings +model = dict( + type='BasicVSR', + generator=dict( + type='BasicVSRNet', + mid_channels=64, + num_blocks=30, + spynet_pretrained='https://download.openmmlab.com/mmediting/restorers/' + 'basicvsr/spynet_20210409-c6c1bd09.pth'), + pixel_loss=dict(type='CharbonnierLoss', loss_weight=1.0, reduction='mean'), + train_cfg=dict(fix_iter=5000), + data_preprocessor=dict( + type='EditDataPreprocessor', + mean=[0., 0., 0.], + std=[255., 255., 255.], + )) + +train_pipeline = [ + dict(type='GenerateSegmentIndices', interval_list=[1]), + dict(type='LoadImageFromFile', key='img', channel_order='rgb'), + dict(type='LoadImageFromFile', key='gt', channel_order='rgb'), + dict(type='SetValues', dictionary=dict(scale=scale)), + dict(type='PairedRandomCrop', gt_patch_size=256), + dict( + type='Flip', + keys=['img', 'gt'], + flip_ratio=0.5, + direction='horizontal'), + dict( + type='Flip', keys=['img', 'gt'], flip_ratio=0.5, direction='vertical'), + dict(type='RandomTransposeHW', keys=['img', 'gt'], transpose_ratio=0.5), + dict(type='PackEditInputs') +] + +val_pipeline = [ + dict(type='GenerateSegmentIndices', interval_list=[1]), + dict(type='LoadImageFromFile', key='img', channel_order='rgb'), + dict(type='LoadImageFromFile', key='gt', channel_order='rgb'), + dict(type='PackEditInputs') +] + +demo_pipeline = [ + dict(type='GenerateSegmentIndices', interval_list=[1]), + dict(type='LoadImageFromFile', key='img', channel_order='rgb'), + dict(type='PackEditInputs') +] + +data_root = 'data/REDS' + +train_dataloader = dict( + num_workers=6, + batch_size=4, + persistent_workers=False, + sampler=dict(type='InfiniteSampler', shuffle=True), + dataset=dict( + type='BasicFramesDataset', + metainfo=dict(dataset_type='reds_reds4', task_name='vsr'), + data_root=data_root, + data_prefix=dict(img='train_sharp_bicubic/X4', gt='train_sharp'), + ann_file='meta_info_reds4_train.txt', + depth=1, + num_input_frames=15, + pipeline=train_pipeline)) + +val_dataloader = dict( + num_workers=1, + batch_size=1, + persistent_workers=False, + sampler=dict(type='DefaultSampler', shuffle=False), + dataset=dict( + type='BasicFramesDataset', + metainfo=dict(dataset_type='reds_reds4', task_name='vsr'), + data_root=data_root, + data_prefix=dict(img='train_sharp_bicubic/X4', gt='train_sharp'), + ann_file='meta_info_reds4_val.txt', + depth=1, + num_input_frames=100, + fixed_seq_len=100, + pipeline=val_pipeline)) + +val_evaluator = dict( + type='EditEvaluator', metrics=[ + dict(type='PSNR'), + dict(type='SSIM'), + ]) + +train_cfg = dict( + type='IterBasedTrainLoop', max_iters=300_000, val_interval=5000) +val_cfg = dict(type='EditValLoop') + +# optimizer +optim_wrapper = dict( + constructor='DefaultOptimWrapperConstructor', + type='OptimWrapper', + optimizer=dict(type='Adam', lr=2e-4, betas=(0.9, 0.99)), + paramwise_cfg=dict(custom_keys={'spynet': dict(lr_mult=0.125)})) + +default_hooks = dict(checkpoint=dict(out_dir=save_dir)) + +# learning policy +param_scheduler = dict( + type='CosineRestartLR', + by_epoch=False, + periods=[300000], + restart_weights=[1], + eta_min=1e-7) + +find_unused_parameters = True diff --git a/PyTorch/contrib/cv/video/BasicVSR++/configs/basicvsr_pp/basicvsr-pp_c64n7_8xb1-600k_reds4.py b/PyTorch/contrib/cv/video/BasicVSR++/configs/basicvsr_pp/basicvsr-pp_c64n7_8xb1-600k_reds4.py new file mode 100644 index 0000000000..17e44da401 --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/configs/basicvsr_pp/basicvsr-pp_c64n7_8xb1-600k_reds4.py @@ -0,0 +1,46 @@ +_base_ = '../basicvsr/basicvsr_2xb4_reds4.py' + +experiment_name = 'basicvsr-pp_c64n7_8xb1-600k_reds4' +work_dir = f'./work_dirs/{experiment_name}' +save_dir = './work_dirs' + +# model settings +model = dict( + type='BasicVSR', + generator=dict( + type='BasicVSRPlusPlusNet', + mid_channels=64, + num_blocks=7, + is_low_res_input=True, + spynet_pretrained='https://download.openmmlab.com/mmediting/restorers/' + 'basicvsr/spynet_20210409-c6c1bd09.pth'), + pixel_loss=dict(type='CharbonnierLoss', loss_weight=1.0, reduction='mean'), + train_cfg=dict(fix_iter=5000), + data_preprocessor=dict( + type='EditDataPreprocessor', + mean=[0., 0., 0.], + std=[255., 255., 255.], + )) + +train_dataloader = dict( + num_workers=6, batch_size=1, dataset=dict(num_input_frames=30)) + +train_cfg = dict( + type='IterBasedTrainLoop', max_iters=600_000, val_interval=5000) + +# optimizer +optim_wrapper = dict( + constructor='DefaultOptimWrapperConstructor', + type='OptimWrapper', + optimizer=dict(type='Adam', lr=1e-4, betas=(0.9, 0.99)), + paramwise_cfg=dict(custom_keys={'spynet': dict(lr_mult=0.25)})) + +default_hooks = dict(checkpoint=dict(out_dir=save_dir)) + +# learning policy +param_scheduler = dict( + type='CosineRestartLR', + by_epoch=False, + periods=[600000], + restart_weights=[1], + eta_min=1e-7) diff --git a/PyTorch/contrib/cv/video/BasicVSR++/requirements.txt b/PyTorch/contrib/cv/video/BasicVSR++/requirements.txt new file mode 100644 index 0000000000..bc18a5033c --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/requirements.txt @@ -0,0 +1,3 @@ +-r requirements/runtime.txt +-r requirements/tests.txt +-r requirements/optional.txt diff --git a/PyTorch/contrib/cv/video/BasicVSR++/requirements/docs.txt b/PyTorch/contrib/cv/video/BasicVSR++/requirements/docs.txt new file mode 100644 index 0000000000..486729d482 --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/requirements/docs.txt @@ -0,0 +1,10 @@ +docutils==0.16.0 +modelindex +myst_parser +-e git+https://github.com/open-mmlab/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme +sphinx==4.5.0 +sphinx-autoapi +sphinx-copybutton +sphinx-notfound-page +sphinx-tabs +sphinx_markdown_tables diff --git a/PyTorch/contrib/cv/video/BasicVSR++/requirements/mminstall.txt b/PyTorch/contrib/cv/video/BasicVSR++/requirements/mminstall.txt new file mode 100644 index 0000000000..992f383c8b --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/requirements/mminstall.txt @@ -0,0 +1,2 @@ +mmcv>=2.0.0rc1 +mmengine>=0.4.0 diff --git a/PyTorch/contrib/cv/video/BasicVSR++/requirements/optional.txt b/PyTorch/contrib/cv/video/BasicVSR++/requirements/optional.txt new file mode 100644 index 0000000000..3f110c9191 --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/requirements/optional.txt @@ -0,0 +1,6 @@ +-e git+https://github.com/openai/CLIP.git@d50d76daa670286dd6cacf3bcd80b5e4823fc8e1#egg=clip +imageio-ffmpeg==0.4.4 +mmdet >= 3.0.0rc2 +open_clip_torch +PyQt5 +transformers diff --git a/PyTorch/contrib/cv/video/BasicVSR++/requirements/readthedocs.txt b/PyTorch/contrib/cv/video/BasicVSR++/requirements/readthedocs.txt new file mode 100644 index 0000000000..17e5a5c8ea --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/requirements/readthedocs.txt @@ -0,0 +1,14 @@ +lmdb +lpips +mmcv>=2.0.0rc1 +mmdet >= 3.0.0rc2 +mmengine +prettytable +Pygments +regex +scikit-image +tabulate +titlecase +torch +torchvision +tqdm diff --git a/PyTorch/contrib/cv/video/BasicVSR++/requirements/runtime.txt b/PyTorch/contrib/cv/video/BasicVSR++/requirements/runtime.txt new file mode 100644 index 0000000000..d98b318732 --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/requirements/runtime.txt @@ -0,0 +1,19 @@ +av +av==8.0.3; python_version < '3.7' +einops +face-alignment +facexlib +lmdb +lpips +mmcv>=2.0.0rc1 +mmengine +numpy +opencv-python!=4.5.5.62,!=4.5.5.64 +# MMCV depends opencv-python instead of headless, thus we install opencv-python +# Due to a bug from upstream, we skip this two version +# https://github.com/opencv/opencv-python/issues/602 +# https://github.com/opencv/opencv/issues/21366 +# It seems to be fixed in https://github.com/opencv/opencv/pull/21382 +Pillow +resize_right +tensorboard diff --git a/PyTorch/contrib/cv/video/BasicVSR++/requirements/tests.txt b/PyTorch/contrib/cv/video/BasicVSR++/requirements/tests.txt new file mode 100644 index 0000000000..d20b4c3785 --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/requirements/tests.txt @@ -0,0 +1,14 @@ +-e git+https://github.com/openai/CLIP.git@d50d76daa670286dd6cacf3bcd80b5e4823fc8e1#egg=clip +# codecov +# flake8 +# isort==5.10.1 +# onnxruntime +# pytest +# pytest-runner +# yapf +coverage < 7.0.0 +imageio-ffmpeg==0.4.4 +interrogate +mmdet >= 3.0.0rc2 +pytest +transformers diff --git a/PyTorch/contrib/cv/video/BasicVSR++/tools/dataset_converters/reds/preprocess_reds_dataset.py b/PyTorch/contrib/cv/video/BasicVSR++/tools/dataset_converters/reds/preprocess_reds_dataset.py new file mode 100644 index 0000000000..0f39483b36 --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/tools/dataset_converters/reds/preprocess_reds_dataset.py @@ -0,0 +1,305 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import argparse +import glob +import os +import os.path as osp +import re +import shutil +import sys + +import cv2 +import lmdb +import mmcv + + +def make_lmdb(mode, data_path, lmdb_path, batch=5000, compress_level=1): + """Create lmdb for the REDS dataset. + + Contents of lmdb. The file structure is: + example.lmdb + ├── data.mdb + ├── lock.mdb + ├── meta_info.txt + + The data.mdb and lock.mdb are standard lmdb files and you can refer to + https://lmdb.readthedocs.io/en/release/ for more details. + + The meta_info.txt is a specified txt file to record the meta information + of our datasets. It will be automatically created when preparing + datasets by our provided dataset tools. + Each line in the txt file records 1)image name (with extension), + 2)image shape, and 3)compression level, separated by a white space. + + For example, the meta information could be: + `000_00000000.png (720,1280,3) 1`, which means: + 1) image name (with extension): 000_00000000.png; + 2) image shape: (720,1280,3); + 3) compression level: 1 + + We use the image name without extension as the lmdb key. + + Args: + mode (str): REDS dataset mode. Choices: ['train_sharp', 'train_blur', + 'train_blur_comp', 'train_sharp_bicubic', 'train_blur_bicubic']. + They are used to identify different reds dataset for different + tasks. Specifically: + 'train_sharp': GT frames; + 'train_blur': Blur frames for deblur task. + 'train_blur_comp': Blur and compressed frames for deblur and + compression task. + 'train_sharp_bicubic': Bicubic downsampled sharp frames for SR + task. + 'train_blur_bicubic': Bicubic downsampled blur frames for SR task. + data_path (str): Data path for reading images. + lmdb_path (str): Lmdb save path. + batch (int): After processing batch images, lmdb commits. + Default: 5000. + compress_level (int): Compress level when encoding images. Default: 1. + """ + + print(f'Create lmdb for {data_path}, save to {lmdb_path}...') + if mode in ['train_sharp', 'train_blur', 'train_blur_comp']: + h_dst, w_dst = 720, 1280 + else: + h_dst, w_dst = 180, 320 + + if osp.exists(lmdb_path): + print(f'Folder {lmdb_path} already exists. Exit.') + sys.exit(1) + + print('Reading image path list ...') + img_path_list = sorted( + list(mmcv.scandir(data_path, suffix='png', recursive=True))) + keys = [] + for img_path in img_path_list: + parts = re.split(r'[\\/]', img_path) + folder = parts[-2] + img_name = parts[-1].split('.png')[0] + keys.append(folder + '_' + img_name) # example: 000_00000000 + + # create lmdb environment + # obtain data size for one image + img = mmcv.imread(osp.join(data_path, img_path_list[0]), flag='unchanged') + _, img_byte = cv2.imencode('.png', img, + [cv2.IMWRITE_PNG_COMPRESSION, compress_level]) + data_size_per_img = img_byte.nbytes + print('Data size per image is: ', data_size_per_img) + data_size = data_size_per_img * len(img_path_list) + env = lmdb.open(lmdb_path, map_size=data_size * 10) + + # write data to lmdb + pbar = mmcv.ProgressBar(len(img_path_list)) + txn = env.begin(write=True) + txt_file = open(osp.join(lmdb_path, 'meta_info.txt'), 'w') + for idx, (path, key) in enumerate(zip(img_path_list, keys)): + pbar.update() + key_byte = key.encode('ascii') + img = mmcv.imread(osp.join(data_path, path), flag='unchanged') + h, w, c = img.shape + _, img_byte = cv2.imencode( + '.png', img, [cv2.IMWRITE_PNG_COMPRESSION, compress_level]) + assert h == h_dst and w == w_dst and c == 3, ( + f'Wrong shape ({h, w}), should be ({h_dst, w_dst}).') + txn.put(key_byte, img_byte) + # write meta information + txt_file.write(f'{key}.png ({h},{w},{c}) {compress_level}\n') + if idx % batch == 0: + txn.commit() + txn = env.begin(write=True) + txn.commit() + env.close() + txt_file.close() + print('\nFinish writing lmdb.') + + +def merge_train_val(train_path, val_path): + """Merge the train and val datasets of REDS. + + Because the EDVR uses a different validation partition, so we merge train + and val datasets in REDS for easy switching between REDS4 partition (used + in EDVR) and the official validation partition. + + The original val dataset (clip names from 000 to 029) are modified to avoid + conflicts with training dataset (total 240 clips). Specifically, the clip + names are changed to 240, 241, ... 269. + + Args: + train_path (str): Training folder paths. + val_path (str): Validation folder paths. + """ + + print(f'Move {val_path} to {train_path}...') + val_folders = glob.glob(osp.join(val_path, '*')) + for folder in val_folders: + index = int(re.split(r'[\\/]', folder)[-1]) + new_folder_idx = f'{index + 240:03d}' + shutil.move(folder, osp.join(train_path, new_folder_idx)) + + +def generate_anno_file(root_path, file_name='meta_info_REDS_GT.txt'): + """Generate anno file for REDS datasets from the ground-truth folder. + + Args: + root_path (str): Root path for REDS datasets. + """ + + print(f'Generate annotation files {file_name}...') + txt_file = osp.join(root_path, file_name) + mmcv.utils.mkdir_or_exist(osp.dirname(txt_file)) + with open(txt_file, 'w') as f: + for i in range(270): + for j in range(100): + f.write(f'{i:03d}/{j:08d}.png (720, 1280, 3)\n') + + +def split_anno_file(root_path, val_partition='REDS4'): + """Split anno file for REDS datasets from the ground-truth folder. + + Args: + root_path (str): Root path for REDS datasets. + val_partition (str): The way of split. + Selected from `RED4` and `official`. Default: 'REDS4' + """ + + print(f'Split annotation files in {val_partition} mode...') + if val_partition == 'official': + val_partition = [f'{v:03d}' for v in range(240, 270)] + train_txt_file = osp.join(root_path, 'meta_info_official_train.txt') + val_txt_file = osp.join(root_path, 'meta_info_official_val.txt') + else: + val_partition = ['000', '011', '015', '020'] + train_txt_file = osp.join(root_path, 'meta_info_reds4_train.txt') + val_txt_file = osp.join(root_path, 'meta_info_reds4_val.txt') + + train_list = [] + val_list = [] + for i in range(270): + for j in range(100): + if f'{i:03d}' in val_partition: + val_list.append(f'{i:03d}/{j:08d}.png (720, 1280, 3)') + else: + train_list.append(f'{i:03d}/{j:08d}.png (720, 1280, 3)') + + mmcv.utils.mkdir_or_exist(osp.dirname(train_txt_file)) + with open(train_txt_file, 'w') as f: + f.write('\n'.join(train_list)) + + mmcv.utils.mkdir_or_exist(osp.dirname(val_txt_file)) + with open(val_txt_file, 'w') as f: + f.write('\n'.join(val_list)) + + +def unzip(zip_path): + """Unzip zip files. It will scan all zip files in zip_path and return unzip + folder names. + + Args: + zip_path (str): Path for zip files. + + Returns: + list: unzip folder names. + """ + zip_files = mmcv.scandir(zip_path, suffix='zip', recursive=False) + import shutil + import zipfile + unzip_folders = [] + for zip_file in zip_files: + zip_file = osp.join(zip_path, zip_file) + unzip_folder = zip_file.replace('.zip', '').split('_part')[0] + print(f'Unzip {zip_file} to {unzip_folder}') + with zipfile.ZipFile(zip_file, 'r') as zip_ref: + zip_ref.extractall(unzip_folder) + data_name = osp.basename(unzip_folder) + data_type = data_name.split('_')[0] + # if data path like `train_sharp/train/train_sharp/*` + # begin reorganizing to `train_sharp/*` + if osp.isdir(osp.join(unzip_folder, data_type, data_name)): + data_folder = osp.join(unzip_folder, data_type, data_name) + for i in os.listdir(data_folder): + shutil.move(osp.join(data_folder, i), unzip_folder) + shutil.rmtree(osp.join(unzip_folder, data_type)) + # end reorganizing + unzip_folders.append(unzip_folder) + return unzip_folders + + +def parse_args(): + parser = argparse.ArgumentParser( + description='Preprocess REDS datasets', + epilog='You can first download REDS datasets using the script from:' + 'https://gist.github.com/SeungjunNah/b10d369b92840cb8dd2118dd4f41d643') + parser.add_argument('--root-path', type=str, help='root path for REDS') + parser.add_argument( + '--make-lmdb', action='store_true', help='create lmdb files') + + args = parser.parse_args() + return args + + +if __name__ == '__main__': + """You can first download datasets using the scripts: + https://gist.github.com/SeungjunNah/b10d369b92840cb8dd2118dd4f41d643. + + The folder structure should be like: + REDS + ├── train_sharp_part1.zip + ├── train_sharp_part2.zip + ├── train_sharp_part3.zip + ├── train_sharp_bicubic.zip + ├── val_sharp.zip + ├── val_sharp_bicubic.zip + The following are optional: + REDS + ├── train_blur_bicubic.zip + ├── val_blur_bicubic.zip + ├── train_blur_part1.zip + ├── train_blur_part2.zip + ├── train_blur_part3.zip + ├── val_blur.zip + ├── train_blur_comp_part1.zip + ├── train_blur_comp_part2.zip + ├── train_blur_comp_part3.zip + ├── val_blur_comp.zip + """ + + args = parse_args() + root_path = args.root_path + + # unzip files and obtain available folder names + folder_paths = unzip(root_path) + folder_paths = set(folder_paths) + + train_folders = [ + osp.basename(v) for v in folder_paths if 'train' in osp.basename(v) + ] + + for train_folder in train_folders: + train_path = osp.join(root_path, train_folder) + val_path = osp.join(root_path, train_folder.replace('train_', 'val_')) + # folders with 'bicubic' will have subfolder X4 + if 'bicubic' in train_folder: + train_path = osp.join(train_path, 'X4') + val_path = osp.join(val_path, 'X4') + # merge train and val datasets + merge_train_val(train_path, val_path) + + # remove validation folders + if 'bicubic' in train_folder: + val_path = osp.dirname(val_path) + print(f'Remove {val_path}') + shutil.rmtree(val_path) + + # generate image list anno file + generate_anno_file(root_path) + split_anno_file(root_path, val_partition='RED4') + split_anno_file(root_path, val_partition='official') + + # create lmdb file + if args.make_lmdb: + for train_folder in train_folders: + lmdb_path = osp.join(root_path, train_folder + '.lmdb') + data_path = osp.join(root_path, train_folder) + if 'bicubic' in train_folder: + data_path = osp.join(data_path, 'X4') + make_lmdb( + mode=train_folder, data_path=data_path, lmdb_path=lmdb_path) diff --git a/PyTorch/contrib/cv/video/BasicVSR++/tools/train.py b/PyTorch/contrib/cv/video/BasicVSR++/tools/train.py new file mode 100644 index 0000000000..37d6214ea4 --- /dev/null +++ b/PyTorch/contrib/cv/video/BasicVSR++/tools/train.py @@ -0,0 +1,111 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import argparse +import logging +import os +import os.path as osp + +from mmengine.config import Config, DictAction +from mmengine.runner import Runner + +from mmedit.utils import print_colored_log + + +def parse_args(): + parser = argparse.ArgumentParser(description='Train a model') + parser.add_argument('config', help='train config file path') + parser.add_argument('--work-dir', help='the dir to save logs and models') + parser.add_argument( + '--resume', action='store_true', help='Whether to resume checkpoint.') + parser.add_argument( + '--amp', + action='store_true', + default=False, + help='enable automatic-mixed-precision training') + parser.add_argument( + '--auto-scale-lr', + action='store_true', + help='enable automatically scaling LR.') + parser.add_argument( + '--cfg-options', + nargs='+', + action=DictAction, + help='override some settings in the used config, the key-value pair ' + 'in xxx=yyy format will be merged into config file. If the value to ' + 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' + 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' + 'Note that the quotation marks are necessary and that no white space ' + 'is allowed.') + parser.add_argument( + '--launcher', + choices=['none', 'pytorch', 'slurm', 'mpi'], + default='none', + help='job launcher') + parser.add_argument('--local_rank', type=int, default=0) + args = parser.parse_args() + if 'LOCAL_RANK' not in os.environ: + os.environ['LOCAL_RANK'] = str(args.local_rank) + + return args + + +def main(): + args = parse_args() + + # load config + cfg = Config.fromfile(args.config) + cfg.launcher = args.launcher + if args.cfg_options is not None: + cfg.merge_from_dict(args.cfg_options) + + # work_dir is determined in this priority: CLI > segment in file > filename + if args.work_dir: # none or empty str + # update configs according to CLI args if args.work_dir is not None + cfg.work_dir = args.work_dir + elif cfg.get('work_dir', None) is None: + # use config filename as default work_dir if cfg.work_dir is None + cfg.work_dir = osp.join('./work_dirs', + osp.splitext(osp.basename(args.config))[0]) + + # enable automatic-mixed-precision training + if args.amp is True: + if ('constructor' not in cfg.optim_wrapper) or \ + cfg.optim_wrapper['constructor'] == 'DefaultOptimWrapperConstructor': # noqa + optim_wrapper = cfg.optim_wrapper.type + if optim_wrapper == 'AmpOptimWrapper': + print_colored_log( + 'AMP training is already enabled in your config.', + logger='current', + level=logging.WARNING) + else: + assert optim_wrapper == 'OptimWrapper', ( + '`--amp` is only supported when the optimizer wrapper ' + f'`type is OptimWrapper` but got {optim_wrapper}.') + cfg.optim_wrapper.type = 'AmpOptimWrapper' + cfg.optim_wrapper.loss_scale = 'dynamic' + else: + for key, val in cfg.optim_wrapper.items(): + if isinstance(val, dict) and 'type' in val: + assert val.type == 'OptimWrapper', ( + '`--amp` is only supported when the optimizer wrapper ' + f'`type is OptimWrapper` but got {val.type}.') + cfg.optim_wrapper[key].type = 'AmpOptimWrapper' + cfg.optim_wrapper[key].loss_scale = 'dynamic' + + if args.resume: + cfg.resume = True + + # build the runner from config + runner = Runner.from_cfg(cfg) + + print_colored_log(f'Working directory: {cfg.work_dir}') + print_colored_log(f'Log directiry: {runner._log_dir}') + + # start training + runner.train() + + print_colored_log(f'Log saved under {runner._log_dir}') + print_colored_log(f'Checkpoint saved under {cfg.work_dir}') + + +if __name__ == '__main__': + main() -- Gitee From 79fa70ea136249f0905b5501e038e2fecdaee654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=AD=E9=A6=96=E5=8D=9A?= Date: Wed, 28 Jun 2023 14:37:55 +0000 Subject: [PATCH 2/3] update PyTorch/contrib/cv/video/BasicVSR++/README.md. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 彭首博 --- PyTorch/contrib/cv/video/BasicVSR++/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PyTorch/contrib/cv/video/BasicVSR++/README.md b/PyTorch/contrib/cv/video/BasicVSR++/README.md index e5a4f86ee2..c5821313ec 100644 --- a/PyTorch/contrib/cv/video/BasicVSR++/README.md +++ b/PyTorch/contrib/cv/video/BasicVSR++/README.md @@ -1,4 +1,4 @@ -# BasicVSR for PyTorch +# BasicVSR++ for PyTorch - [概述](概述.md) - [准备训练环境](准备训练环境.md) -- Gitee From 1249c58a78725838b605c51ea296e36dd8e43a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=AD=E9=A6=96=E5=8D=9A?= Date: Thu, 29 Jun 2023 06:45:07 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20PyTo?= =?UTF-8?q?rch/contrib/cv/video/BasicVSR++/tools/dataset=5Fconverters/reds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reds/preprocess_reds_dataset.py | 305 ------------------ 1 file changed, 305 deletions(-) delete mode 100644 PyTorch/contrib/cv/video/BasicVSR++/tools/dataset_converters/reds/preprocess_reds_dataset.py diff --git a/PyTorch/contrib/cv/video/BasicVSR++/tools/dataset_converters/reds/preprocess_reds_dataset.py b/PyTorch/contrib/cv/video/BasicVSR++/tools/dataset_converters/reds/preprocess_reds_dataset.py deleted file mode 100644 index 0f39483b36..0000000000 --- a/PyTorch/contrib/cv/video/BasicVSR++/tools/dataset_converters/reds/preprocess_reds_dataset.py +++ /dev/null @@ -1,305 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import glob -import os -import os.path as osp -import re -import shutil -import sys - -import cv2 -import lmdb -import mmcv - - -def make_lmdb(mode, data_path, lmdb_path, batch=5000, compress_level=1): - """Create lmdb for the REDS dataset. - - Contents of lmdb. The file structure is: - example.lmdb - ├── data.mdb - ├── lock.mdb - ├── meta_info.txt - - The data.mdb and lock.mdb are standard lmdb files and you can refer to - https://lmdb.readthedocs.io/en/release/ for more details. - - The meta_info.txt is a specified txt file to record the meta information - of our datasets. It will be automatically created when preparing - datasets by our provided dataset tools. - Each line in the txt file records 1)image name (with extension), - 2)image shape, and 3)compression level, separated by a white space. - - For example, the meta information could be: - `000_00000000.png (720,1280,3) 1`, which means: - 1) image name (with extension): 000_00000000.png; - 2) image shape: (720,1280,3); - 3) compression level: 1 - - We use the image name without extension as the lmdb key. - - Args: - mode (str): REDS dataset mode. Choices: ['train_sharp', 'train_blur', - 'train_blur_comp', 'train_sharp_bicubic', 'train_blur_bicubic']. - They are used to identify different reds dataset for different - tasks. Specifically: - 'train_sharp': GT frames; - 'train_blur': Blur frames for deblur task. - 'train_blur_comp': Blur and compressed frames for deblur and - compression task. - 'train_sharp_bicubic': Bicubic downsampled sharp frames for SR - task. - 'train_blur_bicubic': Bicubic downsampled blur frames for SR task. - data_path (str): Data path for reading images. - lmdb_path (str): Lmdb save path. - batch (int): After processing batch images, lmdb commits. - Default: 5000. - compress_level (int): Compress level when encoding images. Default: 1. - """ - - print(f'Create lmdb for {data_path}, save to {lmdb_path}...') - if mode in ['train_sharp', 'train_blur', 'train_blur_comp']: - h_dst, w_dst = 720, 1280 - else: - h_dst, w_dst = 180, 320 - - if osp.exists(lmdb_path): - print(f'Folder {lmdb_path} already exists. Exit.') - sys.exit(1) - - print('Reading image path list ...') - img_path_list = sorted( - list(mmcv.scandir(data_path, suffix='png', recursive=True))) - keys = [] - for img_path in img_path_list: - parts = re.split(r'[\\/]', img_path) - folder = parts[-2] - img_name = parts[-1].split('.png')[0] - keys.append(folder + '_' + img_name) # example: 000_00000000 - - # create lmdb environment - # obtain data size for one image - img = mmcv.imread(osp.join(data_path, img_path_list[0]), flag='unchanged') - _, img_byte = cv2.imencode('.png', img, - [cv2.IMWRITE_PNG_COMPRESSION, compress_level]) - data_size_per_img = img_byte.nbytes - print('Data size per image is: ', data_size_per_img) - data_size = data_size_per_img * len(img_path_list) - env = lmdb.open(lmdb_path, map_size=data_size * 10) - - # write data to lmdb - pbar = mmcv.ProgressBar(len(img_path_list)) - txn = env.begin(write=True) - txt_file = open(osp.join(lmdb_path, 'meta_info.txt'), 'w') - for idx, (path, key) in enumerate(zip(img_path_list, keys)): - pbar.update() - key_byte = key.encode('ascii') - img = mmcv.imread(osp.join(data_path, path), flag='unchanged') - h, w, c = img.shape - _, img_byte = cv2.imencode( - '.png', img, [cv2.IMWRITE_PNG_COMPRESSION, compress_level]) - assert h == h_dst and w == w_dst and c == 3, ( - f'Wrong shape ({h, w}), should be ({h_dst, w_dst}).') - txn.put(key_byte, img_byte) - # write meta information - txt_file.write(f'{key}.png ({h},{w},{c}) {compress_level}\n') - if idx % batch == 0: - txn.commit() - txn = env.begin(write=True) - txn.commit() - env.close() - txt_file.close() - print('\nFinish writing lmdb.') - - -def merge_train_val(train_path, val_path): - """Merge the train and val datasets of REDS. - - Because the EDVR uses a different validation partition, so we merge train - and val datasets in REDS for easy switching between REDS4 partition (used - in EDVR) and the official validation partition. - - The original val dataset (clip names from 000 to 029) are modified to avoid - conflicts with training dataset (total 240 clips). Specifically, the clip - names are changed to 240, 241, ... 269. - - Args: - train_path (str): Training folder paths. - val_path (str): Validation folder paths. - """ - - print(f'Move {val_path} to {train_path}...') - val_folders = glob.glob(osp.join(val_path, '*')) - for folder in val_folders: - index = int(re.split(r'[\\/]', folder)[-1]) - new_folder_idx = f'{index + 240:03d}' - shutil.move(folder, osp.join(train_path, new_folder_idx)) - - -def generate_anno_file(root_path, file_name='meta_info_REDS_GT.txt'): - """Generate anno file for REDS datasets from the ground-truth folder. - - Args: - root_path (str): Root path for REDS datasets. - """ - - print(f'Generate annotation files {file_name}...') - txt_file = osp.join(root_path, file_name) - mmcv.utils.mkdir_or_exist(osp.dirname(txt_file)) - with open(txt_file, 'w') as f: - for i in range(270): - for j in range(100): - f.write(f'{i:03d}/{j:08d}.png (720, 1280, 3)\n') - - -def split_anno_file(root_path, val_partition='REDS4'): - """Split anno file for REDS datasets from the ground-truth folder. - - Args: - root_path (str): Root path for REDS datasets. - val_partition (str): The way of split. - Selected from `RED4` and `official`. Default: 'REDS4' - """ - - print(f'Split annotation files in {val_partition} mode...') - if val_partition == 'official': - val_partition = [f'{v:03d}' for v in range(240, 270)] - train_txt_file = osp.join(root_path, 'meta_info_official_train.txt') - val_txt_file = osp.join(root_path, 'meta_info_official_val.txt') - else: - val_partition = ['000', '011', '015', '020'] - train_txt_file = osp.join(root_path, 'meta_info_reds4_train.txt') - val_txt_file = osp.join(root_path, 'meta_info_reds4_val.txt') - - train_list = [] - val_list = [] - for i in range(270): - for j in range(100): - if f'{i:03d}' in val_partition: - val_list.append(f'{i:03d}/{j:08d}.png (720, 1280, 3)') - else: - train_list.append(f'{i:03d}/{j:08d}.png (720, 1280, 3)') - - mmcv.utils.mkdir_or_exist(osp.dirname(train_txt_file)) - with open(train_txt_file, 'w') as f: - f.write('\n'.join(train_list)) - - mmcv.utils.mkdir_or_exist(osp.dirname(val_txt_file)) - with open(val_txt_file, 'w') as f: - f.write('\n'.join(val_list)) - - -def unzip(zip_path): - """Unzip zip files. It will scan all zip files in zip_path and return unzip - folder names. - - Args: - zip_path (str): Path for zip files. - - Returns: - list: unzip folder names. - """ - zip_files = mmcv.scandir(zip_path, suffix='zip', recursive=False) - import shutil - import zipfile - unzip_folders = [] - for zip_file in zip_files: - zip_file = osp.join(zip_path, zip_file) - unzip_folder = zip_file.replace('.zip', '').split('_part')[0] - print(f'Unzip {zip_file} to {unzip_folder}') - with zipfile.ZipFile(zip_file, 'r') as zip_ref: - zip_ref.extractall(unzip_folder) - data_name = osp.basename(unzip_folder) - data_type = data_name.split('_')[0] - # if data path like `train_sharp/train/train_sharp/*` - # begin reorganizing to `train_sharp/*` - if osp.isdir(osp.join(unzip_folder, data_type, data_name)): - data_folder = osp.join(unzip_folder, data_type, data_name) - for i in os.listdir(data_folder): - shutil.move(osp.join(data_folder, i), unzip_folder) - shutil.rmtree(osp.join(unzip_folder, data_type)) - # end reorganizing - unzip_folders.append(unzip_folder) - return unzip_folders - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Preprocess REDS datasets', - epilog='You can first download REDS datasets using the script from:' - 'https://gist.github.com/SeungjunNah/b10d369b92840cb8dd2118dd4f41d643') - parser.add_argument('--root-path', type=str, help='root path for REDS') - parser.add_argument( - '--make-lmdb', action='store_true', help='create lmdb files') - - args = parser.parse_args() - return args - - -if __name__ == '__main__': - """You can first download datasets using the scripts: - https://gist.github.com/SeungjunNah/b10d369b92840cb8dd2118dd4f41d643. - - The folder structure should be like: - REDS - ├── train_sharp_part1.zip - ├── train_sharp_part2.zip - ├── train_sharp_part3.zip - ├── train_sharp_bicubic.zip - ├── val_sharp.zip - ├── val_sharp_bicubic.zip - The following are optional: - REDS - ├── train_blur_bicubic.zip - ├── val_blur_bicubic.zip - ├── train_blur_part1.zip - ├── train_blur_part2.zip - ├── train_blur_part3.zip - ├── val_blur.zip - ├── train_blur_comp_part1.zip - ├── train_blur_comp_part2.zip - ├── train_blur_comp_part3.zip - ├── val_blur_comp.zip - """ - - args = parse_args() - root_path = args.root_path - - # unzip files and obtain available folder names - folder_paths = unzip(root_path) - folder_paths = set(folder_paths) - - train_folders = [ - osp.basename(v) for v in folder_paths if 'train' in osp.basename(v) - ] - - for train_folder in train_folders: - train_path = osp.join(root_path, train_folder) - val_path = osp.join(root_path, train_folder.replace('train_', 'val_')) - # folders with 'bicubic' will have subfolder X4 - if 'bicubic' in train_folder: - train_path = osp.join(train_path, 'X4') - val_path = osp.join(val_path, 'X4') - # merge train and val datasets - merge_train_val(train_path, val_path) - - # remove validation folders - if 'bicubic' in train_folder: - val_path = osp.dirname(val_path) - print(f'Remove {val_path}') - shutil.rmtree(val_path) - - # generate image list anno file - generate_anno_file(root_path) - split_anno_file(root_path, val_partition='RED4') - split_anno_file(root_path, val_partition='official') - - # create lmdb file - if args.make_lmdb: - for train_folder in train_folders: - lmdb_path = osp.join(root_path, train_folder + '.lmdb') - data_path = osp.join(root_path, train_folder) - if 'bicubic' in train_folder: - data_path = osp.join(data_path, 'X4') - make_lmdb( - mode=train_folder, data_path=data_path, lmdb_path=lmdb_path) -- Gitee