[
  {
    "path": ".gitignore",
    "content": "**/__pycache__\n.vscode\ncheckpoints\nlogs\noutputs\ntemp"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 Yiming Zhao\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "## UDiffText: A Unified Framework for High-quality Text Synthesis in Arbitrary Images via Character-aware Diffusion Models\n\n<a href='https://arxiv.org/abs/2312.04884'><img src='https://img.shields.io/badge/Arxiv-2312.04884-DF826C'></a> \n<a href='https://udifftext.github.io/'><img src='https://img.shields.io/badge/Project-UDiffText-D0F288'></a> \n<a href='https://huggingface.co/spaces/ZYMPKU/UDiffText'><img src='https://img.shields.io/badge/%F0%9F%A4%97%20Demo-UDiffText-8ADAB2'></a> \n\n#### Our proposed UDiffText is capable of synthesizing accurate and harmonious text in either synthetic or real-word images, thus can be applied to tasks like scene text editing (a), arbitrary text generation (b) and accurate T2I generation (c)\n\n![UDiffText Teaser](demo/teaser.png)\n\n### 📬 News\n\n- **2023.7.16** Our paper is accepted by ECCV2024!🥳\n- **2023.12.11** Version 2.0 update (getting rid of trash codes🚮)\n- **2023.12.3** Build Hugging Face demo\n- **2023.12.1** Build Github project page\n- **2023.11.30** Version 1.0 upload\n\n### 🔨 Installation\n\n1. Clone this repo: \n```\ngit clone https://github.com/ZYM-PKU/UDiffText.git\ncd UDiffText\n```\n\n2. Install required Python packages\n\n```\nconda create -n udiff python=3.11\nconda activate udiff\npip install torch==2.1.1 torchvision==0.16.1 --index-url https://download.pytorch.org/whl/cu121\npip install -r requirements.txt\n```\n\n3. Make the checkpoint directory and build the tree structure\n\n```\nmkdir ./checkpoints\n\ncheckpoints\n├── AEs                    // AutoEncoder\n├── encoders             \n    ├── LabelEncoder       // Character-level encoder\n    └── ViTSTR             // STR encoder\n├── predictors             // STR model\n├── pretrained             // Pretrained SD\n└── ***.ckpt               // UDiffText checkpoint\n```\n\n### 💻 Training\n\n1. Prepare your data\n\n#### LAION-OCR\n- Create a data directory **{your data root}/LAION-OCR** in your disk and put your data in it. Then set the **data_root** field in **./configs/dataset/locr.yaml**.\n- For the downloading and preprocessing of Laion-OCR dataset, please refer to [TextDiffuser](https://github.com/microsoft/unilm/tree/master/textdiffuser) and our **./scripts/preprocess/laion_ocr_pre.ipynb**.\n\n#### ICDAR13\n- Create a data directory **{your data root}/ICDAR13** in your disk and put your data in it. Then set the **data_root** field in **./configs/dataset/icd13.yaml**.\n- Build the tree structure as below:\n```\nICDAR13\n├── train                  // training set\n    ├── annos              // annotations\n        ├── gt_x.txt\n        ├── ...\n    └── images             // images\n        ├── img_x.jpg\n        ├── ...\n└── val                    // validation set\n    ├── annos              // annotations\n        ├── gt_img_x.txt\n        ├── ...\n    └── images             // images\n        ├── img_x.jpg\n        ├── ...\n```\n\n#### TextSeg\n- Create a data directory **{your data root}/TextSeg** in your disk and put your data in it. Then set the **data_root** field in **./configs/dataset/tsg.yaml**.\n- Build the tree structure as below:\n```\nTextSeg\n├── train                  // training set\n    ├── annotation         // annotations\n        ├── x_anno.json    // annotation json file\n        ├── x_mask.png     // character-level mask\n        ├── ...\n    └── image              // images\n        ├── x.jpg.jpg\n        ├── ...\n└── val                    // validation set\n    ├── annotation         // annotations\n        ├── x_anno.json    // annotation json file\n        ├── x_mask.png     // character-level mask\n        ├── ...\n    └── image              // images\n        ├── x.jpg\n        ├── ...\n```\n\n#### SynthText\n- Create a data directory **{your data root}/SynthText** in your disk and put your data in it. Then set the **data_root** field in **./configs/dataset/st.yaml**.\n- Build the tree structure as below:\n```\nSynthText\n├── 1                      // part 1\n    ├── ant+hill_1_0.jpg   // image\n    ├── ant+hill_1_1.jpg\n    ├── ...\n├── 2                      // part 2\n├── ...\n└── gt.mat                 // annotation file\n```\n\n2. Train the character-level encoder\n\nSet the parameters in **./configs/pretrain.yaml** and run:\n\n```\npython pretrain.py\n```\n\n3. Train the UDiffText model\n\nDownload the [pretrained model](https://huggingface.co/stabilityai/stable-diffusion-2-inpainting/blob/main/512-inpainting-ema.ckpt) and put it in **./checkpoints/pretrained/**. You can ignore the \"Missing Key\" or \"Unexcepted Key\" warning when loading the checkpoint.\n\nSet the parameters in **./configs/train.yaml**, especially the paths:\n\n```\nload_ckpt_path: ./checkpoints/pretrained/512-inpainting-ema.ckpt // Checkpoint of the pretrained SD\nmodel_cfg_path: ./configs/train/textdesign_sd_2.yaml // UDiffText model config\ndataset_cfg_path: ./configs/dataset/locr.yaml // Use the Laion-OCR dataset\n```\n\nand run:\n\n```\npython train.py\n```\n\n### 📏 Evaluation\n\n1. Download our available [checkpoints](https://drive.google.com/drive/folders/1s8IWqqydaJBjukxViGKFj2N33lfoVkGf?usp=sharing) and put them in the corresponding directories in **./checkpoints**.\n\n2. Set the parameters in **./configs/test.yaml**, especially the paths:\n\n```\nload_ckpt_path: \"./checkpoints/***.ckpt\"  // UDiffText checkpoint\nmodel_cfg_path: \"./configs/test/textdesign_sd_2.yaml\"  // UDiffText model config\ndataset_cfg_path: \"./configs/dataset/locr.yaml\"  // LAION-OCR dataset config\n```\n\nand run:\n\n```\npython test.py\n```\n\n### 🖼️ Demo\n\nIn order to run an interactive demo on your own machine, execute the code:\n\n```\npython demo.py\n```\n\nor try our online demo at [hugging face](https://huggingface.co/spaces/ZYMPKU/UDiffText):\n\n![Demo](demo/demo.png)\n\n### 🎉 Acknowledgement\n\n- **Dataset**: We sincerely thank the open-source large image-text dataset LAION-OCR with character-level segmentations provided by [TextDiffuser](https://github.com/microsoft/unilm/tree/master/textdiffuser).\n\n- **Code & Model**: We build our project based on the code repo of [Stable Diffusion XL](https://github.com/Stability-AI/generative-models) and leverage the pretrained checkpoint of [Stable Diffusion 2.0](https://github.com/Stability-AI/stablediffusion).\n\n### 🪬 Citation\n\n```\n@misc{zhao2023udifftext,\n      title={UDiffText: A Unified Framework for High-quality Text Synthesis in Arbitrary Images via Character-aware Diffusion Models}, \n      author={Yiming Zhao and Zhouhui Lian},\n      year={2023},\n      eprint={2312.04884},\n      archivePrefix={arXiv},\n      primaryClass={cs.CV}\n}\n```\n"
  },
  {
    "path": "configs/dataset/icd13.yaml",
    "content": "target: ICDAR13Dataset\nparams:\n\n  data_root: '{your data root}'\n\n  H: 512\n  W: 512\n  word_len: [1, 8]\n  seq_len: 12\n  mask_min_ratio: 0.01\n  aug_text_enabled: True\n  aug_text_ratio: 1.0"
  },
  {
    "path": "configs/dataset/locr.yaml",
    "content": "target: LAIONOCRDataset\nparams:\n\n  data_root: '{your data root}'\n\n  H: 512\n  W: 512\n  word_len: [1, 12]\n  seq_len: 12\n  mask_min_ratio: 0.01\n  seg_min_ratio: 0.001\n  aug_text_enabled: True\n  aug_text_ratio: 1.0\n\n  use_cached: False\n  length: 100000"
  },
  {
    "path": "configs/dataset/st.yaml",
    "content": "target: SynthTextDataset\nparams:\n\n  data_root: '{your data root}'\n\n  H: 512\n  W: 512\n  word_len: [1, 12]\n  mask_min_ratio: 0.01\n  seg_min_ratio: 0.001\n\n  length: 100000\n  use_cached: False"
  },
  {
    "path": "configs/dataset/tsg.yaml",
    "content": "target: TextSegDataset\nparams:\n\n  data_root: '{your data root}'\n\n  H: 512\n  W: 512\n  word_len: [1, 12]\n  seq_len: 12\n  mask_min_ratio: 0.01\n  seg_min_ratio: 0.005\n  aug_text_enabled: True\n  aug_text_ratio: 1.0"
  },
  {
    "path": "configs/demo.yaml",
    "content": "type: \"demo\"\n\n# path\nload_ckpt_path: \"./checkpoints/{your checkpoint path}.ckpt\"\nmodel_cfg_path: \"./configs/test/textdesign_sd_2.yaml\"\n\n# param\nH: 512\nW: 512\nseq_len: 12\nbatch_size: 1\n\nchannel: 4 # AE latent channel\nfactor: 8 # AE downsample factor\nscale: [4.0, 0.0] # cfg scale, None\nnoise_iters: 10\nforce_uc_zero_embeddings: [\"ref\", \"label\"]\naae_enabled: False\ndetailed: False\n\n# runtime\nsteps: 50\ninit_step: 0\nnum_workers: 0\ngpu: 0\n\n"
  },
  {
    "path": "configs/pretrain.yaml",
    "content": "# path\nckpt_dir: './checkpoints/encoders/LabelEncoder'\n\ndataset:\n  target: dataset.dataloader.LabelDataset\n  params:\n    size: 224\n    length: 100000\n    font_path: './dataset/utils/arial.ttf'\n    min_len: 1\n    max_len: 12\n\nmodel:\n  target: sgm.modules.encoders.modules.LabelEncoder\n  params:\n    trainable: True\n    max_len: 12\n    emb_dim: 2048\n    n_heads: 8\n    n_trans_layers: 12\n    lr: 1e-5\n    lambda_cls: 0.1\n    lambda_pos: 0.1\n\n    visual_config:\n      target: sgm.modules.encoders.modules.ViTSTREncoder\n      params:\n        freeze: True\n        ckpt_path: \"./checkpoints/encoders/ViTSTR/vitstr_base_patch16_224.pth\"\n        size: 224\n        patch_size: 16\n        embed_dim: 768\n        depth: 12\n        num_heads: 12\n        mlp_ratio: 4\n        qkv_bias: True\n        in_chans: 1\n\n\nnum_workers: 0\nbatch_size: 256\ncheck_freq: 5\n\n\nlightning:\n  max_epochs: 1000\n  accelerator: \"cuda\"\n  devices: \n    - 0\n  default_root_dir: \"./logs/pre_logs\""
  },
  {
    "path": "configs/test/textdesign_sd_2.yaml",
    "content": "model:\n  target: sgm.models.diffusion.DiffusionEngine\n  params:\n    opt_keys:\n      - t_attn\n    input_key: image\n    scale_factor: 0.18215\n    disable_first_stage_autocast: True\n\n    denoiser_config:\n      target: sgm.modules.diffusionmodules.denoiser.DiscreteDenoiser\n      params:\n        num_idx: 1000\n\n        weighting_config:\n          target: sgm.modules.diffusionmodules.denoiser_weighting.EpsWeighting\n        scaling_config:\n          target: sgm.modules.diffusionmodules.denoiser_scaling.EpsScaling\n        discretization_config:\n          target: sgm.modules.diffusionmodules.discretizer.LegacyDDPMDiscretization\n\n    network_config:\n      target: sgm.modules.diffusionmodules.openaimodel.UnifiedUNetModel\n      params:\n        in_channels: 9\n        out_channels: 4\n        ctrl_channels: 0\n        model_channels: 320\n        attention_resolutions: [4, 2, 1]\n        save_attn_type: [t_attn]\n        save_attn_layers: [output_blocks.6.1]\n        num_res_blocks: 2\n        channel_mult: [1, 2, 4, 4]\n        num_head_channels: 64\n        use_linear_in_transformer: True\n        transformer_depth: 1\n        t_context_dim: 2048\n\n    conditioner_config:\n      target: sgm.modules.GeneralConditioner\n      params:\n        emb_models:\n          # textual crossattn cond\n          - is_trainable: False\n            emb_key: t_crossattn\n            ucg_rate: 0.1\n            input_key: label\n            target: sgm.modules.encoders.modules.LabelEncoder\n            params:\n              max_len: 12\n              emb_dim: 2048\n              n_heads: 8\n              n_trans_layers: 12\n              ckpt_path: ./checkpoints/encoders/LabelEncoder/epoch=19-step=7820.ckpt\n          # concat cond\n          - is_trainable: False\n            input_key: mask\n            target: sgm.modules.encoders.modules.SpatialRescaler\n            params:\n              in_channels: 1\n              multiplier: 0.125\n          - is_trainable: False\n            input_key: masked\n            target: sgm.modules.encoders.modules.LatentEncoder\n            params:\n              scale_factor: 0.18215\n              config:\n                target: sgm.models.autoencoder.AutoencoderKLInferenceWrapper\n                params:\n                  ckpt_path: ./checkpoints/AEs/AE_inpainting_2.safetensors\n                  embed_dim: 4\n                  monitor: val/rec_loss\n                  ddconfig:\n                    attn_type: vanilla-xformers\n                    double_z: true\n                    z_channels: 4\n                    resolution: 256\n                    in_channels: 3\n                    out_ch: 3\n                    ch: 128\n                    ch_mult: [1, 2, 4, 4]\n                    num_res_blocks: 2\n                    attn_resolutions: []\n                    dropout: 0.0\n                  lossconfig:\n                    target: torch.nn.Identity\n\n    first_stage_config:\n      target: sgm.models.autoencoder.AutoencoderKLInferenceWrapper\n      params:\n        ckpt_path: ./checkpoints/AEs/AE_inpainting_2.safetensors\n        embed_dim: 4\n        monitor: val/rec_loss\n        ddconfig:\n          attn_type: vanilla-xformers\n          double_z: true\n          z_channels: 4\n          resolution: 256\n          in_channels: 3\n          out_ch: 3\n          ch: 128\n          ch_mult: [1, 2, 4, 4]\n          num_res_blocks: 2\n          attn_resolutions: []\n          dropout: 0.0\n        lossconfig:\n          target: torch.nn.Identity\n\n    loss_fn_config:\n      target: sgm.modules.diffusionmodules.loss.FullLoss # StandardDiffusionLoss\n      params:\n        seq_len: 12\n        kernel_size: 3\n        gaussian_sigma: 1.0\n        min_attn_size: 16\n        lambda_local_loss: 0.01\n        lambda_ocr_loss: 0.001\n        ocr_enabled: False\n\n        predictor_config:\n          target: sgm.modules.predictors.model.ParseqPredictor\n          params:\n            ckpt_path: \"./checkpoints/predictors/parseq-bb5792a6.pt\"\n        \n        sigma_sampler_config:\n          target: sgm.modules.diffusionmodules.sigma_sampling.DiscreteSampling\n          params:\n            num_idx: 1000\n            \n            discretization_config:\n              target: sgm.modules.diffusionmodules.discretizer.LegacyDDPMDiscretization"
  },
  {
    "path": "configs/test.yaml",
    "content": "type: \"test\"\n\n# path\nload_ckpt_path: \"./checkpoints/{your checkpoint path}.ckpt\"\nmodel_cfg_path: \"./configs/test/textdesign_sd_2.yaml\"\ndataset_cfg_path: \"./configs/dataset/icd13.yaml\"\noutput_dir: \"./outputs\"\ntemp_dir: \"./temp\"\n\n# param\nchannel: 4 # AE latent channel\nfactor: 8 # AE downsample factor\nscale: [5.0, 0.0] # cfg scale, None\nnoise_iters: 10 # iterations for initial noise searching\nforce_uc_zero_embeddings: [\"label\"] # condition label\naae_enabled: False # attend and excite\ndetailed: False # save visualization results\n\n# runtime\nsteps: 50 # sampling steps\ninit_step: 0\nbatch_size: 1\nnum_workers: 0\ngpu: 0 # index of your gpu device\nmax_iter: 100\nshuffle: True\nquan_test: False # quantitative test\n\n# ocr\nocr_enabled: True\npredictor_config:\n  target: sgm.modules.predictors.model.ParseqPredictor\n  params:\n    ckpt_path: \"./checkpoints/predictors/parseq-bb5792a6.pt\""
  },
  {
    "path": "configs/train/textdesign_sd_2.yaml",
    "content": "model:\n  target: sgm.models.diffusion.DiffusionEngine\n  params:\n    opt_keys:\n      - t_attn\n      - t_norm\n    input_key: image\n    scale_factor: 0.18215\n    disable_first_stage_autocast: True\n\n    denoiser_config:\n      target: sgm.modules.diffusionmodules.denoiser.DiscreteDenoiser\n      params:\n        num_idx: 1000\n\n        weighting_config:\n          target: sgm.modules.diffusionmodules.denoiser_weighting.EpsWeighting\n        scaling_config:\n          target: sgm.modules.diffusionmodules.denoiser_scaling.EpsScaling\n        discretization_config:\n          target: sgm.modules.diffusionmodules.discretizer.LegacyDDPMDiscretization\n\n    network_config:\n      target: sgm.modules.diffusionmodules.openaimodel.UnifiedUNetModel\n      params:\n        in_channels: 9\n        out_channels: 4\n        ctrl_channels: 0\n        model_channels: 320\n        attention_resolutions: [4, 2, 1]\n        save_attn_type: [t_attn]\n        save_attn_layers: [output_blocks.6.1]\n        num_res_blocks: 2\n        channel_mult: [1, 2, 4, 4]\n        num_head_channels: 64\n        use_linear_in_transformer: True\n        transformer_depth: 1\n        t_context_dim: 2048\n\n    conditioner_config:\n      target: sgm.modules.GeneralConditioner\n      params:\n        emb_models:\n          # textual crossattn cond\n          - is_trainable: False\n            emb_key: t_crossattn\n            ucg_rate: 0.1\n            input_key: label\n            target: sgm.modules.encoders.modules.LabelEncoder\n            params:\n              max_len: 12\n              emb_dim: 2048\n              n_heads: 8\n              n_trans_layers: 12\n              ckpt_path: ./checkpoints/encoders/LabelEncoder/epoch=19-step=7820.ckpt\n          # concat cond\n          - is_trainable: False\n            input_key: mask\n            target: sgm.modules.encoders.modules.SpatialRescaler\n            params:\n              in_channels: 1\n              multiplier: 0.125\n          - is_trainable: False\n            input_key: masked\n            target: sgm.modules.encoders.modules.LatentEncoder\n            params:\n              scale_factor: 0.18215\n              config:\n                target: sgm.models.autoencoder.AutoencoderKLInferenceWrapper\n                params:\n                  ckpt_path: ./checkpoints/AEs/AE_inpainting_2.safetensors\n                  embed_dim: 4\n                  monitor: val/rec_loss\n                  ddconfig:\n                    attn_type: vanilla-xformers\n                    double_z: true\n                    z_channels: 4\n                    resolution: 256\n                    in_channels: 3\n                    out_ch: 3\n                    ch: 128\n                    ch_mult: [1, 2, 4, 4]\n                    num_res_blocks: 2\n                    attn_resolutions: []\n                    dropout: 0.0\n                  lossconfig:\n                    target: torch.nn.Identity\n\n    first_stage_config:\n      target: sgm.models.autoencoder.AutoencoderKLInferenceWrapper\n      params:\n        ckpt_path: ./checkpoints/AEs/AE_inpainting_2.safetensors\n        embed_dim: 4\n        monitor: val/rec_loss\n        ddconfig:\n          attn_type: vanilla-xformers\n          double_z: true\n          z_channels: 4\n          resolution: 256\n          in_channels: 3\n          out_ch: 3\n          ch: 128\n          ch_mult: [1, 2, 4, 4]\n          num_res_blocks: 2\n          attn_resolutions: []\n          dropout: 0.0\n        lossconfig:\n          target: torch.nn.Identity\n\n    loss_fn_config:\n      target: sgm.modules.diffusionmodules.loss.FullLoss # StandardDiffusionLoss\n      params:\n        seq_len: 12\n        kernel_size: 3\n        gaussian_sigma: 1.0\n        min_attn_size: 16\n        lambda_local_loss: 0.01\n        lambda_ocr_loss: 0.001\n        ocr_enabled: False\n\n        predictor_config:\n          target: sgm.modules.predictors.model.ParseqPredictor\n          params:\n            ckpt_path: \"./checkpoints/predictors/parseq-bb5792a6.pt\"\n        \n        sigma_sampler_config:\n          target: sgm.modules.diffusionmodules.sigma_sampling.DiscreteSampling\n          params:\n            num_idx: 1000\n            \n            discretization_config:\n              target: sgm.modules.diffusionmodules.discretizer.LegacyDDPMDiscretization\n\n    sampler_config:\n      target: sgm.modules.diffusionmodules.sampling.EulerEDMSampler\n      params:\n        num_steps: 50\n\n        discretization_config:\n          target: sgm.modules.diffusionmodules.discretizer.LegacyDDPMDiscretization\n\n        guider_config:\n          target: sgm.modules.diffusionmodules.guiders.VanillaCFG\n          params:\n            scale: 5.0"
  },
  {
    "path": "configs/train.yaml",
    "content": "type: \"train\"\n\n# path\nsave_ckpt_dir: ./checkpoints\nload_ckpt_path: ./checkpoints/pretrained/512-inpainting-ema.ckpt\nmodel_cfg_path: ./configs/train/textdesign_sd_2.yaml\ndataset_cfg_path: ./configs/dataset/locr.yaml\n\n# param\nsave_ckpt_freq: 1\nnum_workers: 0\nbatch_size: 16\nbase_learning_rate: 5.0e-5\nshuffle: False\n\n# runtime\nlightning:\n  max_epochs: 100\n  accelerator: gpu\n  strategy: ddp_find_unused_parameters_true\n  accumulate_grad_batches: 4 \n  devices: [1,2,3,4,5,6,7,8]\n  default_root_dir: ./logs/base_logs\n  profiler: simple "
  },
  {
    "path": "dataset/__init__.py",
    "content": ""
  },
  {
    "path": "dataset/dataloader.py",
    "content": "import os,glob\nimport torch\nimport cv2\nimport scipy\nimport string\nimport json\nimport torchvision.transforms as transforms\nimport torch.nn.functional as F\nimport torch.utils.data as data\nimport numpy as np\n\nfrom tqdm import tqdm\nfrom omegaconf import OmegaConf\nfrom PIL import Image, ImageDraw, ImageFont\nfrom os.path import join as ospj\nfrom torchvision.utils import save_image\nfrom random import choice, randint, sample, uniform, shuffle\nfrom util import *\n\n\ndef region_draw_text(H, W, r_bbox, text, font_path = \"./dataset/utils/arial.ttf\"):\n\n    m_top, m_bottom, m_left, m_right = r_bbox\n    m_h, m_w = m_bottom-m_top, m_right-m_left\n\n    font = ImageFont.truetype(font_path, 128)\n    std_l, std_t, std_r, std_b = font.getbbox(text)\n    std_h, std_w = std_b - std_t, std_r - std_l\n    image = Image.new('RGB', (std_w, std_h), color = (255, 255, 255))\n    draw = ImageDraw.Draw(image)\n    draw.text((0, 0), text, fill = (0, 0, 0), font=font, anchor=\"lt\")\n    \n    transform = transforms.Compose([\n        transforms.Resize((m_h, m_w), transforms.InterpolationMode.BICUBIC, antialias=True),\n        transforms.ToTensor()\n    ])\n    \n    image = transform(image)\n\n    result = torch.ones((3, H, W))\n    result[:, m_top:m_bottom, m_left:m_right] = image\n\n    return result\n\n\ndef initialize_word_dict():\n\n        with open('./dataset/utils/words.txt', 'r') as f:\n            word_list = f.readlines()\n        \n        words = []\n        for word_line in word_list:\n            words += word_line[:-1].split(\" \")\n\n        words.sort(key = lambda w: len(w))\n        word_dict = {l:[] for l in range(len(words[0]), len(words[-1])+1)}\n        for word in words:\n            word_dict[len(word)].append(word)\n\n        return word_dict\n        \n\nclass LabelDataset(data.Dataset):\n\n    def __init__(self, size, length, font_path, min_len, max_len) -> None:\n        super().__init__()\n\n        # constraint\n        self.length = length\n        self.size = size\n\n        # path\n        self.font_path = font_path\n\n        # word dict\n        self.character = string.printable[:-6]\n        self.min_len = min_len\n        self.max_len = max_len\n\n        self.grayscale = transforms.Grayscale()\n        self.resize = transforms.Resize((self.size, self.size), transforms.InterpolationMode.BICUBIC, antialias=True)\n\n    def __len__(self):\n        \n        return self.length\n    \n    def __getitem__(self, index):\n\n        while True:\n\n            text_len = randint(self.min_len, self.max_len)\n            text = \"\".join([choice(self.character) for i in range(text_len)])\n            font_path = self.font_path\n\n            try: \n                font = ImageFont.truetype(font_path, 128)\n                std_l, std_t, std_r, std_b = font.getbbox(text)\n                std_h, std_w = std_b - std_t, std_r - std_l\n                if std_h == 0 or std_w == 0:\n                    continue\n            except:\n                continue\n            \n            try:\n                image = Image.new('RGB', (std_w, std_h), color = (0,0,0))\n                draw = ImageDraw.Draw(image)\n                draw.text((0, 0), text, fill = (255,255,255), font=font, anchor=\"lt\")\n            except:\n                continue\n\n            image = transforms.ToTensor()(image)\n            image = self.grayscale(image)\n            image = self.resize(image)\n\n            batch = {\n                \"image\": image,\n                \"text\": text\n            }\n\n            return batch\n    \n\nclass ICDAR13Dataset(data.Dataset):\n\n    def __init__(self, cfgs, datype) -> None:\n        super().__init__()\n\n        # basic\n        self.type = datype\n        self.character = string.printable[:-6]\n\n        # path\n        self.data_root = ospj(cfgs.data_root, \"ICDAR13\", self.type)\n        self.image_root = ospj(self.data_root, \"images\")\n        self.anno_root = ospj(self.data_root, \"annos\")\n        self.anno_paths = sorted(glob.glob(ospj(self.anno_root, \"*.txt\")))\n\n        # constraint\n        self.H = cfgs.H\n        self.W = cfgs.W\n        self.word_len = cfgs.word_len\n        self.seq_len = cfgs.seq_len\n        self.mask_min_ratio = cfgs.mask_min_ratio\n        self.aug_text_enabled = cfgs.aug_text_enabled\n        self.aug_text_ratio = cfgs.aug_text_ratio\n\n        self.items = []\n        total_count = 0\n        for anno_path in self.anno_paths:\n            name = anno_path.split(os.sep)[-1].split(\".\")[0].replace(\"gt_\", \"\")\n            with open(anno_path, \"r\") as fp:\n                annos = fp.readlines()\n\n            for anno in annos:\n\n                total_count += 1\n                text = anno.split(\"\\\"\")[1]\n                left, top, right, bottom = [int(s) for s in anno.split(\", \")[:4]]\n                area = (bottom-top) * (right-left)\n                bbox = np.array((top, bottom, left, right))\n\n                if len(text) < self.word_len[0] or len(text) > self.word_len[1]: continue\n                if not all([c in self.character for c in text]): continue\n                if area / (self.H * self.W) < self.mask_min_ratio: continue\n\n                self.items.append({\n                    \"image_path\": ospj(self.image_root, f\"{name}.jpg\"),\n                    \"text\": text,\n                    \"bbox\": bbox\n                })\n\n        self.length = len(self.items)\n        print(f\"Total: {total_count}, filtered: {self.length}\")\n        self.count = -1\n        self.word_dict = initialize_word_dict()\n    \n    def __len__(self):\n        \n        return self.length\n    \n    def augment(self, image, bbox):\n\n        h, w, _ = image.shape\n        m_top, m_bottom, m_left, m_right = bbox\n\n        mask = np.ones((h, w), dtype=np.uint8)\n        mask[m_top:m_bottom, m_left:m_right] = 0\n\n        if h >= w:\n            delta = (h-w)//2\n            m_left += delta; m_right += delta\n            image = cv2.copyMakeBorder(image, 0,0,delta,delta, cv2.BORDER_REPLICATE)\n            mask = cv2.copyMakeBorder(mask, 0,0,delta,delta, cv2.BORDER_CONSTANT, value = (1,1,1))\n        else:\n            delta = (w-h)//2\n            m_top += delta; m_bottom += delta\n            image = cv2.copyMakeBorder(image, delta,delta,0,0, cv2.BORDER_REPLICATE)\n            mask = cv2.copyMakeBorder(mask, delta,delta,0,0, cv2.BORDER_CONSTANT, value = (1,1,1))\n\n        m_h, m_w = int(m_bottom-m_top), int(m_right-m_left)\n        c_h, c_w = m_top + m_h//2, m_left + m_w//2\n\n        h, w, _ = image.shape\n        area = (m_bottom-m_top) * (m_right-m_left)\n        aug_min_ratio = self.mask_min_ratio * 4\n        if area/(h*w) < aug_min_ratio:\n            d = int((area/aug_min_ratio)**0.5)\n            d = max(d, max(m_h, m_w))\n            if c_h <= h - c_h:\n                delta_top = min(c_h, d//2)\n                delta_bottom = d - delta_top\n            else:\n                delta_bottom = min(h - c_h, d//2)\n                delta_top = d - delta_bottom\n            if c_w <= w - c_w:\n                delta_left = min(c_w, d//2)\n                delta_right = d - delta_left\n            else:\n                delta_right = min(w - c_w, d//2)\n                delta_left = d - delta_right\n\n            n_top, n_bottom = c_h - delta_top, c_h + delta_bottom\n            n_left, n_right = c_w - delta_left, c_w + delta_right\n\n            image = image[n_top:n_bottom, n_left:n_right, :]\n            mask = mask[n_top:n_bottom, n_left:n_right]\n\n            m_top -= n_top; m_bottom -= n_top\n            m_left -= n_left; m_right -= n_left\n\n        h, w, _ = image.shape\n        m_top, m_bottom = int(m_top * (self.H/h)), int(m_bottom * (self.H/h))\n        m_left, m_right = int(m_left * (self.W/w)), int(m_right * (self.W/w))\n        \n        image = cv2.resize(image, (self.W, self.H))\n        mask = cv2.resize(mask, (self.W, self.H))\n\n        r_bbox = torch.tensor((m_top, m_bottom, m_left, m_right))\n        \n        return image, mask, r_bbox\n        \n    def __getitem__(self, index):\n\n        self.count += 1\n\n        item = self.items[index]\n        image_path = item[\"image_path\"]\n        text = item[\"text\"]\n        bbox = item[\"bbox\"]\n\n        aug_text = choice(self.word_dict[len(text)]) if uniform(0, 1) <= self.aug_text_ratio else text\n\n        image = Image.open(image_path).convert(\"RGB\")\n        w, h = image.size\n        image = np.asarray(image)\n        image, mask, r_bbox = self.augment(image, bbox)\n\n        image = torch.from_numpy(image.transpose(2,0,1)).to(dtype=torch.float32) / 127.5 - 1.0\n\n        mask = torch.from_numpy(mask[None]).to(dtype=torch.float32)\n        masked = image * mask\n        mask = 1 - mask\n\n        seg_mask = torch.cat((torch.ones(len(text)), torch.zeros(self.seq_len-len(text))))\n\n        rendered = region_draw_text(self.H, self.W, r_bbox, aug_text if self.aug_text_enabled else text)\n\n        # additional cond\n        txt = f\"\\\"{aug_text if self.aug_text_enabled else text}\\\"\"\n        original_size_as_tuple = torch.tensor((h, w))\n        crop_coords_top_left = torch.tensor((0, 0))\n        target_size_as_tuple = torch.tensor((self.H, self.W))\n\n        batch = {\n            \"image\": image,\n            \"mask\": mask,\n            \"masked\": masked,\n            \"seg_mask\": seg_mask,\n            \"r_bbox\": r_bbox,\n            \"rendered\": rendered,\n            \"label\": aug_text if self.aug_text_enabled else text,\n            \"txt\": txt,\n            \"original_size_as_tuple\": original_size_as_tuple,\n            \"crop_coords_top_left\": crop_coords_top_left,\n            \"target_size_as_tuple\": target_size_as_tuple,\n            \"name\": str(self.count)\n        }\n\n        return batch\n\n\nclass TextSegDataset(data.Dataset):\n\n    def __init__(self, cfgs, datype) -> None:\n        super().__init__()\n\n        # basic\n        self.type = datype\n        self.character = string.printable[:-6]\n\n        # path\n        self.data_root = ospj(cfgs.data_root, \"TextSeg\", self.type)\n        self.image_root = ospj(self.data_root, \"image\")\n        self.anno_root = ospj(self.data_root, \"annotation\")\n\n        # constraint\n        self.H = cfgs.H\n        self.W = cfgs.W\n        self.word_len = cfgs.word_len\n        self.seq_len = cfgs.seq_len\n        self.mask_min_ratio = cfgs.mask_min_ratio\n        self.seg_min_ratio = cfgs.seg_min_ratio\n        self.aug_text_enabled = cfgs.aug_text_enabled\n        self.aug_text_ratio = cfgs.aug_text_ratio\n\n        image_paths = sorted(glob.glob(ospj(self.image_root, \"*.jpg\")))\n        anno_paths = sorted(glob.glob(ospj(self.anno_root, \"*.json\")))\n        seg_paths = sorted([p for p in glob.glob(ospj(self.anno_root, \"*.png\")) if \"eff\" not in p])\n\n        self.items = []\n        total_count = 0\n        for image_path, anno_path, seg_path in zip(image_paths, anno_paths, seg_paths):\n            with open(anno_path, \"rb\") as fp:\n                annos = json.load(fp)\n            for anno in annos.values():\n                total_count += 1\n                text = anno[\"text\"]\n                chars = [anno[\"char\"][key][\"text\"] for key in anno[\"char\"]]\n                bbox = np.array(anno[\"bbox\"]).reshape((4,2))\n                seg_values = [c[\"mask_value\"] for c in anno[\"char\"].values()]\n                area = cv2.contourArea(bbox)\n\n                if \"\".join(chars) != text: continue\n                if \"#\" in text: continue\n                if len(text) < self.word_len[0] or len(text) > self.word_len[1]: continue\n                if not all([c in self.character for c in text]): continue\n                if area / (self.H * self.W) < self.mask_min_ratio: continue\n\n                self.items.append({\n                    \"image_path\": image_path,\n                    \"seg_path\": seg_path,\n                    \"text\": text,\n                    \"bbox\": bbox,\n                    \"seg_values\": seg_values\n                })\n\n        self.length = len(self.items)\n        print(f\"Total: {total_count}, filtered: {self.length}\")\n        self.count = -1\n        self.word_dict = initialize_word_dict()\n\n    def __len__(self):\n        \n        return self.length\n    \n    def augment(self, image, seg, text, bbox, seg_values):\n\n        h, w, _ = image.shape\n        m_top, m_bottom = int(np.min(bbox[:,1])), int(np.max(bbox[:,1]))\n        m_left, m_right = int(np.min(bbox[:,0])), int(np.max(bbox[:,0]))\n\n        mask = np.ones((h, w), dtype=np.uint8)\n        mask = cv2.fillConvexPoly(mask, bbox, 0)\n\n        if h >= w:\n            delta = (h-w)//2\n            m_left += delta; m_right += delta\n            image = cv2.copyMakeBorder(image, 0,0,delta,delta, cv2.BORDER_REPLICATE)\n            mask = cv2.copyMakeBorder(mask, 0,0,delta,delta, cv2.BORDER_CONSTANT, value = (1,1,1))\n            seg = cv2.copyMakeBorder(seg, 0,0,delta,delta, cv2.BORDER_CONSTANT, value = (0,0,0))\n        else:\n            delta = (w-h)//2\n            m_top += delta; m_bottom += delta\n            image = cv2.copyMakeBorder(image, delta,delta,0,0, cv2.BORDER_REPLICATE)\n            mask = cv2.copyMakeBorder(mask, delta,delta,0,0, cv2.BORDER_CONSTANT, value = (1,1,1))\n            seg = cv2.copyMakeBorder(seg, delta,delta,0,0, cv2.BORDER_CONSTANT, value = (0,0,0))\n\n        m_h, m_w = int(m_bottom-m_top), int(m_right-m_left)\n        c_h, c_w = m_top + m_h//2, m_left + m_w//2\n\n        h, w, _ = image.shape\n        area = cv2.contourArea(bbox)\n        aug_min_ratio = self.mask_min_ratio * 4\n        if area/(h*w) < aug_min_ratio:\n            d = int((area/aug_min_ratio)**0.5)\n            d = max(d, max(m_h, m_w))\n            if c_h <= h - c_h:\n                delta_top = min(c_h, d//2)\n                delta_bottom = d - delta_top\n            else:\n                delta_bottom = min(h - c_h, d//2)\n                delta_top = d - delta_bottom\n            if c_w <= w - c_w:\n                delta_left = min(c_w, d//2)\n                delta_right = d - delta_left\n            else:\n                delta_right = min(w - c_w, d//2)\n                delta_left = d - delta_right\n\n            n_top, n_bottom = c_h - delta_top, c_h + delta_bottom\n            n_left, n_right = c_w - delta_left, c_w + delta_right\n\n            image = image[n_top:n_bottom, n_left:n_right, :]\n            mask = mask[n_top:n_bottom, n_left:n_right]\n            seg = seg[n_top:n_bottom, n_left:n_right, :]\n\n            m_top -= n_top; m_bottom -= n_top\n            m_left -= n_left; m_right -= n_left\n\n        segs = []\n        text_indices = [[i for i, c in enumerate(text) if c == ch] for ch in text]\n        for i in range(len(text)):\n            indices = text_indices[i]\n            seg_i = np.sum([(seg == seg_values[ind]).astype(np.uint8).mean(axis=-1) for ind in indices], axis=0) # position un-aware\n            seg_i = np.clip(seg_i, 0, 1)\n            seg_i = cv2.morphologyEx(seg_i, cv2.MORPH_OPEN, np.ones((1,2),np.int8), iterations=2) # denoise\n            seg_i = cv2.morphologyEx(seg_i, cv2.MORPH_OPEN, np.ones((2,1),np.int8), iterations=2) # denoise\n            seg_i = cv2.morphologyEx(seg_i, cv2.MORPH_DILATE, np.ones((3,3),np.int8), iterations=7) # dilate\n            segs.append(seg_i[None])\n\n        segs = segs + [np.zeros_like(segs[0]) for i in range(self.seq_len-len(segs))]\n        seg = np.concatenate(segs, axis=0)\n\n        h, w, _ = image.shape\n        m_top, m_bottom = int(m_top * (self.H/h)), int(m_bottom * (self.H/h))\n        m_left, m_right = int(m_left * (self.W/w)), int(m_right * (self.W/w))\n        \n        image = cv2.resize(image, (self.W, self.H))\n        seg = cv2.resize(seg.transpose((1,2,0)), (self.W, self.H)).transpose((2,0,1))\n        mask = cv2.resize(mask, (self.W, self.H))\n\n        r_bbox = torch.tensor((m_top, m_bottom, m_left, m_right))\n        \n        return image, seg, mask, r_bbox\n        \n    def __getitem__(self, index):\n\n        self.count += 1\n\n        while True:\n\n            item = self.items[index]\n            image_path = item[\"image_path\"]\n            seg_path = item[\"seg_path\"]\n            text = item[\"text\"]\n            bbox = item[\"bbox\"]\n            seg_values = item[\"seg_values\"]\n            \n            aug_text = choice(self.word_dict[len(text)]) if uniform(0, 1) <= self.aug_text_ratio else text\n\n            image = Image.open(image_path).convert(\"RGB\")\n            seg = Image.open(seg_path).convert(\"RGB\")\n            w, h = image.size\n            image = np.asarray(image)\n            seg = np.asarray(seg)\n            image, seg, mask, r_bbox = self.augment(image, seg, text, bbox, seg_values)\n            \n            image = torch.from_numpy(image.transpose(2,0,1)).to(dtype=torch.float32) / 127.5 - 1.0\n\n            mask = torch.from_numpy(mask[None]).to(dtype=torch.float32)\n            masked = image * mask\n            mask = 1 - mask\n\n            seg = torch.from_numpy(seg)\n            seg_mask = torch.cat((torch.ones(len(text)), torch.zeros(self.seq_len-len(text))))\n\n            rendered = region_draw_text(self.H, self.W, r_bbox, aug_text if self.aug_text_enabled else text)\n\n            # additional cond\n            txt = f\"\\\"{aug_text if self.aug_text_enabled else text}\\\"\"\n            original_size_as_tuple = torch.tensor((h, w))\n            crop_coords_top_left = torch.tensor((0, 0))\n            target_size_as_tuple = torch.tensor((self.H, self.W))\n\n            batch = {\n                \"image\": image,\n                \"seg\": seg,\n                \"seg_mask\": seg_mask,\n                \"mask\": mask,\n                \"masked\": masked,\n                \"r_bbox\": r_bbox,\n                \"rendered\": rendered,\n                \"label\": aug_text if self.aug_text_enabled else text,\n                \"txt\": txt,\n                \"original_size_as_tuple\": original_size_as_tuple,\n                \"crop_coords_top_left\": crop_coords_top_left,\n                \"target_size_as_tuple\": target_size_as_tuple,\n                \"name\": str(self.count)\n            }\n\n            return batch\n\n\nclass SynthTextDataset(data.Dataset):\n\n    def __init__(self, cfgs, datype) -> None:\n        super().__init__()\n\n        # basic\n        self.type = datype\n        self.length = cfgs.length\n        self.character = string.printable[:-6]\n\n        # path\n        self.data_root = ospj(cfgs.data_root, \"SynthText\")\n        self.anno_path = ospj(self.data_root, \"gt.mat\")\n\n        # constraint\n        self.H = cfgs.H\n        self.W = cfgs.W\n        self.word_len = cfgs.word_len\n        self.mask_min_ratio = cfgs.mask_min_ratio\n        self.seg_min_ratio = cfgs.seg_min_ratio\n\n        anno = scipy.io.loadmat(self.anno_path)\n        image_names = anno[\"imnames\"][0]\n        word_bboxes = anno[\"wordBB\"][0]\n        char_bboxes = anno[\"charBB\"][0]\n        txts = anno[\"txt\"][0]\n\n        if cfgs.use_cached:\n            with open(ospj(self.data_root, \"items.json\"), \"r\") as fp:\n                self.items = json.load(fp)\n        else:\n            self.items = []\n            for image_name, word_bbox, char_bbox, txt in zip(image_names, word_bboxes, char_bboxes, txts):\n                image_name = image_name[0]\n                image_path = ospj(self.data_root, image_name)\n\n                txt_list = []\n                for frag in txt:\n                    frag = frag.replace(\"\\n\", \" \")\n                    frags = [s for s in frag.split(\" \") if s != \"\"]\n                    txt_list += frags\n                \n                if word_bbox.ndim < 3: word_bbox = word_bbox[...,None]\n                word_bbox = word_bbox.transpose((2,1,0)).astype(np.int32)\n                char_bbox = char_bbox.transpose((2,1,0)).astype(np.int32)\n\n                pointer = 0\n                for bbox, text in zip(word_bbox, txt_list):\n\n                    seg_bboxs = char_bbox[pointer: pointer+len(text)]\n                    pointer += len(text)\n                    area = cv2.contourArea(bbox)\n\n                    if len(text) < self.word_len[0] or len(text) > self.word_len[1]: continue\n                    if area / (self.H * self.W) < self.mask_min_ratio: continue\n\n                    self.items.append({\n                        \"image_path\": image_path,\n                        \"text\": text,\n                        \"bbox\": bbox.tolist(),\n                        \"seg_bboxs\" : seg_bboxs.tolist()\n                    })\n\n            with open(ospj(self.data_root, \"items.json\"), \"w\") as fp:\n                json.dump(self.items, fp)\n\n        self.count = -1\n    \n    def __len__(self):\n        \n        return self.length\n    \n    def augment(self, image, bbox, seg_bboxs):\n\n        h, w, _ = image.shape\n        m_top, m_bottom = max(0, int(np.min(bbox[:,1]))), min(h, int(np.max(bbox[:,1])))\n        m_left, m_right = max(0, int(np.min(bbox[:,0]))), min(w, int(np.max(bbox[:,0])))\n\n        mask = np.ones((h, w), dtype=np.uint8)\n        mask = cv2.fillConvexPoly(mask, bbox, 0)\n\n        segs = []\n        seg_sum = 0\n        for seg_bbox in seg_bboxs:\n            seg_i = np.zeros_like(mask)\n            seg_i = cv2.fillConvexPoly(seg_i, seg_bbox, 1)\n            segs.append(seg_i[None])\n            seg_sum += seg_i.sum()\n        \n        seg_ratio = float(seg_sum / len(segs)) / (h*w)\n        segs = segs + [np.zeros_like(segs[0]) for i in range(self.word_len[1]-len(segs))]\n        seg = np.concatenate(segs, axis=0)\n\n        if h >= w:\n            delta = (h-w)//2\n            m_left += delta; m_right += delta\n            image = cv2.copyMakeBorder(image, 0,0,delta,delta, cv2.BORDER_REPLICATE)\n            mask = cv2.copyMakeBorder(mask, 0,0,delta,delta, cv2.BORDER_CONSTANT, value = (1,1,1))\n            seg = cv2.copyMakeBorder(seg.transpose((1,2,0)), 0,0,delta,delta, cv2.BORDER_CONSTANT, value = (0,0,0)).transpose((2,0,1))\n\n        else:\n            delta = (w-h)//2\n            m_top += delta; m_bottom += delta\n            image = cv2.copyMakeBorder(image, delta,delta,0,0, cv2.BORDER_REPLICATE)\n            mask = cv2.copyMakeBorder(mask, delta,delta,0,0, cv2.BORDER_CONSTANT, value = (1,1,1))\n            seg = cv2.copyMakeBorder(seg.transpose((1,2,0)), delta,delta,0,0, cv2.BORDER_CONSTANT, value = (0,0,0)).transpose((2,0,1))\n\n        m_h, m_w = int(m_bottom-m_top), int(m_right-m_left)\n        c_h, c_w = m_top + m_h//2, m_left + m_w//2\n\n        h, w, _ = image.shape\n        area = cv2.contourArea(bbox)\n        aug_min_ratio = self.mask_min_ratio * 4\n        if area/(h*w) < aug_min_ratio:\n            d = int((area/aug_min_ratio)**0.5)\n            d = max(d, max(m_h, m_w))\n            if c_h <= h - c_h:\n                delta_top = min(c_h, d//2)\n                delta_bottom = d - delta_top\n            else:\n                delta_bottom = min(h - c_h, d//2)\n                delta_top = d - delta_bottom\n            if c_w <= w - c_w:\n                delta_left = min(c_w, d//2)\n                delta_right = d - delta_left\n            else:\n                delta_right = min(w - c_w, d//2)\n                delta_left = d - delta_right\n\n            n_top, n_bottom = c_h - delta_top, c_h + delta_bottom\n            n_left, n_right = c_w - delta_left, c_w + delta_right\n\n            image = image[n_top:n_bottom, n_left:n_right, :]\n            mask = mask[n_top:n_bottom, n_left:n_right]\n            seg = seg[:, n_top:n_bottom, n_left:n_right]\n\n            m_top -= n_top; m_bottom -= n_top\n            m_left -= n_left; m_right -= n_left\n\n        h, w, _ = image.shape\n        m_top, m_bottom = int(m_top * (self.H/h)), int(m_bottom * (self.H/h))\n        m_left, m_right = int(m_left * (self.W/w)), int(m_right * (self.W/w))\n        \n        image = cv2.resize(image, (self.W, self.H))\n        seg = cv2.resize(seg.transpose((1,2,0)), (self.W, self.H)).transpose((2,0,1))\n        mask = cv2.resize(mask, (self.W, self.H))\n\n        r_bbox = torch.tensor((m_top, m_bottom, m_left, m_right))\n        \n        return image, seg, mask, seg_ratio, r_bbox\n        \n    def __getitem__(self, index):\n\n        self.count += 1\n\n        while True:\n        \n            item = choice(self.items)\n            image_path = item[\"image_path\"]\n            text = item[\"text\"]\n            bbox = np.array(item[\"bbox\"])\n            seg_bboxs = np.array(item[\"seg_bboxs\"])\n\n            image = Image.open(image_path).convert(\"RGB\")\n            w, h = image.size\n            image = np.asarray(image)\n            image, seg, mask, seg_ratio, r_bbox = self.augment(image, bbox, seg_bboxs)\n\n            if seg_ratio < self.seg_min_ratio: continue\n            \n            image = torch.from_numpy(image.transpose(2,0,1)).to(dtype=torch.float32) / 127.5 - 1.0\n\n            mask = torch.from_numpy(mask[None]).to(dtype=torch.float32)\n            masked = image * mask\n            mask = 1 - mask\n\n            seg = torch.from_numpy(seg).to(dtype=torch.float32)\n            seg_mask = torch.cat((torch.ones(len(text)), torch.zeros(self.word_len[1]-len(text))))\n\n            # additional cond\n            txt = f\"\\\"{text}\\\"\"\n            original_size_as_tuple = torch.tensor((h, w))\n            crop_coords_top_left = torch.tensor((0, 0))\n            target_size_as_tuple = torch.tensor((self.H, self.W))\n\n            batch = {\n                \"image\": image,\n                \"seg\": seg,\n                \"seg_mask\": seg_mask,\n                \"mask\": mask,\n                \"masked\": masked,\n                \"r_bbox\": r_bbox,\n                \"label\": text,\n                \"txt\": txt,\n                \"original_size_as_tuple\": original_size_as_tuple,\n                \"crop_coords_top_left\": crop_coords_top_left,\n                \"target_size_as_tuple\": target_size_as_tuple,\n                \"name\": str(self.count)\n            }\n\n            return batch\n\n\nclass LAIONOCRDataset(data.Dataset):\n\n    def __init__(self, cfgs, datype) -> None:\n        super().__init__()\n\n        # basic\n        self.type = datype\n        self.character = string.printable[:-6]\n\n        # path\n        self.data_root = ospj(cfgs.data_root, \"LAION-OCR\", self.type)\n\n        # constraint\n        self.H = cfgs.H\n        self.W = cfgs.W\n        self.W_std = 512\n        self.H_std = 512\n        self.word_len = cfgs.word_len\n        self.seq_len = cfgs.seq_len\n        self.mask_min_ratio = cfgs.mask_min_ratio\n        self.seg_min_ratio = cfgs.seg_min_ratio\n        self.aug_text_enabled = cfgs.aug_text_enabled if self.type != \"train\" else False\n        self.aug_text_ratio = cfgs.aug_text_ratio\n\n        if cfgs.use_cached:\n            with open(ospj(cfgs.data_root, \"LAION-OCR\", f\"{self.type}_items.json\"), \"r\") as fp:\n                self.items = json.load(fp)\n        else:\n            self.items = []\n            data_dirs = sorted(glob.glob(ospj(self.data_root, \"*\")))\n            len_count = area_count = text_count = 0\n            for data_dir in data_dirs:\n                image_path = ospj(data_dir, \"image.jpg\")\n                ocr_path = ospj(data_dir, \"ocr.txt\")\n                seg_path = ospj(data_dir, \"charseg.npy\")\n                \n                with open(ocr_path, \"r\") as fp:\n                    ocrs = fp.readlines()\n                for ocr in ocrs:\n                    text, bbox_str, _ = ocr.strip(\"\\n\").split(\" \")\n                    bbox = np.array([int(v) for v in bbox_str.split(\",\")]).reshape((4,2))\n                    area = cv2.contourArea(bbox)\n\n                    if len(text) < self.word_len[0] or len(text) > self.word_len[1]:\n                        len_count += 1\n                        continue\n                    if not all([c in self.character for c in text]): \n                        text_count += 1\n                        continue\n                    if area / (self.W_std*self.H_std) < self.mask_min_ratio:\n                        area_count += 1\n                        continue\n\n                    self.items.append({\n                        \"image_path\": image_path,\n                        \"seg_path\": seg_path,\n                        \"text\": text,\n                        \"bbox_str\": bbox_str,\n                    })\n\n            with open(ospj(cfgs.data_root, \"LAION-OCR\", f\"{self.type}_items.json\"), \"w\") as fp:\n                json.dump(self.items, fp)\n            \n            print(f\"Total length: {len(self.items)}  filtered out {len_count} len_ill, {area_count} area_ill, {text_count} text_ill\")\n            \n        self.length = cfgs.length\n        self.count = -1\n        self.word_dict = initialize_word_dict()\n\n    def __len__(self):\n        \n        return self.length\n    \n    def augment(self, image, seg, text, bbox):\n\n        image = cv2.resize(image, (self.W_std, self.H_std))\n        seg = cv2.resize(seg.astype(np.uint8), (self.W_std, self.H_std))\n        mask = np.ones((self.H_std, self.W_std), dtype=np.uint8)\n        mask = cv2.fillConvexPoly(mask, bbox, 0)\n\n        h, w, _ = image.shape\n        m_top, m_bottom = max(0, int(np.min(bbox[:,1]))), min(self.H_std, int(np.max(bbox[:,1])))\n        m_left, m_right = max(0, int(np.min(bbox[:,0]))), min(self.W_std, int(np.max(bbox[:,0])))\n        m_h, m_w = int(m_bottom-m_top), int(m_right-m_left)\n        c_h, c_w = m_top + m_h//2, m_left + m_w//2\n\n        area = cv2.contourArea(bbox)\n        aug_min_ratio = self.mask_min_ratio * 4\n        if area/(h*w) < aug_min_ratio:\n            d = int((area/aug_min_ratio)**0.5)\n            d = max(d, max(m_h, m_w))\n            if c_h <= h - c_h:\n                delta_top = min(c_h, d//2)\n                delta_bottom = d - delta_top\n            else:\n                delta_bottom = min(h - c_h, d//2)\n                delta_top = d - delta_bottom\n            if c_w <= w - c_w:\n                delta_left = min(c_w, d//2)\n                delta_right = d - delta_left\n            else:\n                delta_right = min(w - c_w, d//2)\n                delta_left = d - delta_right\n\n            n_top, n_bottom = c_h - delta_top, c_h + delta_bottom\n            n_left, n_right = c_w - delta_left, c_w + delta_right\n\n            image = image[n_top:n_bottom, n_left:n_right, :]\n            mask = mask[n_top:n_bottom, n_left:n_right]\n            seg = seg[n_top:n_bottom, n_left:n_right]\n\n            m_top -= n_top; m_bottom -= n_top\n            m_left -= n_left; m_right -= n_left\n\n        seg = seg * (1 - mask)\n\n        segs = [None for i in range(len(text))]\n        ch_dict = {}\n        for i in range(len(text)):\n            if text[i] in ch_dict: ch_dict[text[i]].append(i)\n            else: ch_dict[text[i]] = [i]\n        \n        for ch in ch_dict:\n            ind = self.character.find(ch) + 1\n            ind_l = self.character.find(ch.lower()) + 1\n            seg_i = ((seg == ind).astype(np.uint8) + (seg == ind_l).astype(np.uint8))\n\n            seg_i = cv2.morphologyEx(seg_i, cv2.MORPH_OPEN, np.ones((1,2),np.int8), iterations=1) # denoise\n            seg_i = cv2.morphologyEx(seg_i, cv2.MORPH_OPEN, np.ones((2,1),np.int8), iterations=1) # denoise\n            seg_i = cv2.morphologyEx(seg_i, cv2.MORPH_DILATE, np.ones((3,3),np.int8), iterations=5) # dilate\n\n            retval, labels, stats, centroids = cv2.connectedComponentsWithStats(seg_i, connectivity=4)\n            if retval < len(ch_dict[ch]) + 1:\n                return None, None, None, None\n\n            stats = stats[1:].tolist()\n            if retval > len(ch_dict[ch]) + 1:\n                stats.sort(key = lambda st: st[-1])\n                stats.reverse()\n                stats = stats[:len(ch_dict[ch])]\n\n            stats.sort(key = lambda st: st[0])\n            for idx, stat in enumerate(stats):\n                x, y, w, h, s = stat\n                s_mask = np.zeros_like(seg_i)\n                s_mask[y:y+h, x:x+w] = 1\n                seg_i_mask = seg_i * s_mask\n                segs[ch_dict[ch][idx]] = seg_i_mask[None]\n\n        segs = segs + [np.zeros_like(segs[0]) for i in range(self.seq_len-len(segs))]\n        seg = np.concatenate(segs, axis=0)\n\n        h, w, _ = image.shape\n        m_top, m_bottom = int(m_top * (self.H/h)), int(m_bottom * (self.H/h))\n        m_left, m_right = int(m_left * (self.W/w)), int(m_right * (self.W/w))\n\n        image = cv2.resize(image, (self.W, self.H))\n        seg = cv2.resize(seg.transpose((1,2,0)), (self.W, self.H)).transpose((2,0,1))\n        mask = cv2.resize(mask, (self.W, self.H))\n\n        r_bbox = torch.tensor((m_top, m_bottom, m_left, m_right))\n\n        return image, seg, mask, r_bbox\n        \n    def __getitem__(self, index):\n        \n        self.count += 1\n\n        while True:\n            \n            item = choice(self.items)\n            image_path = item[\"image_path\"]\n            seg_path = item[\"seg_path\"]\n            text = item[\"text\"]\n            bbox_str = item[\"bbox_str\"]\n            bbox = np.array([int(v) for v in bbox_str.split(\",\")]).reshape((4,2))\n\n            aug_text = choice(self.word_dict[len(text)]) if uniform(0, 1) <= self.aug_text_ratio else text\n\n            image = Image.open(image_path).convert(\"RGB\")\n            seg = np.load(seg_path)\n            w, h = image.size\n            image = np.asarray(image)\n            image, seg, mask, r_bbox = self.augment(image, seg, text, bbox)\n\n            if image is None: continue\n            \n            image = torch.from_numpy(image.transpose(2,0,1)).to(dtype=torch.float32) / 127.5 - 1.0\n\n            mask = torch.from_numpy(mask[None]).to(dtype=torch.float32)\n            masked = image * mask\n            mask = 1 - mask\n\n            seg = torch.from_numpy(seg).to(dtype=torch.float32)\n            seg_mask = torch.cat((torch.ones(len(text)), torch.zeros(self.seq_len-len(text))))\n\n            m_top, m_bottom, m_left, m_right = r_bbox\n            ref = image[:, m_top:m_bottom, m_left:m_right]\n            ref = F.interpolate(ref[None], (128, 128))[0]\n\n            # rendered = region_draw_text(self.H, self.W, r_bbox, aug_text if self.aug_text_enabled else text)\n\n            # additional cond\n            txt = f\"\\\"{aug_text if self.aug_text_enabled else text}\\\"\"\n            original_size_as_tuple = torch.tensor((h, w))\n            crop_coords_top_left = torch.tensor((0, 0))\n            target_size_as_tuple = torch.tensor((self.H, self.W))\n\n            batch = {\n                \"image\": image,\n                \"seg\": seg,\n                \"seg_mask\": seg_mask,\n                \"mask\": mask,\n                \"masked\": masked,\n                \"r_bbox\": r_bbox,\n                \"ref\": ref,\n                # \"rendered\": rendered,\n                \"label\": aug_text if self.aug_text_enabled else text,\n                \"txt\": txt,\n                \"original_size_as_tuple\": original_size_as_tuple,\n                \"crop_coords_top_left\": crop_coords_top_left,\n                \"target_size_as_tuple\": target_size_as_tuple,\n                \"name\": str(self.count)\n            }\n\n            return batch\n\n\ndef get_dataloader(cfgs, datype=\"train\"):\n\n    dataset_cfgs = OmegaConf.load(cfgs.dataset_cfg_path)\n    print(f\"Extracting data from {dataset_cfgs.target}\")\n    Dataset = eval(dataset_cfgs.target)\n    dataset = Dataset(dataset_cfgs.params, datype = datype)\n\n    return data.DataLoader(dataset=dataset, batch_size=cfgs.batch_size, shuffle=cfgs.shuffle, num_workers=cfgs.num_workers, drop_last=True)\n\n"
  },
  {
    "path": "dataset/utils/words.txt",
    "content": "the of and to in a is was that for as with by on are from be or his were it an at not which have he had this has also their but one can its on the other been more they used first all two citation than into would only time who most may such some many when after between over these her about there use no them new him will out during made both then often so any being such as where number could main p. through system people known each while if called convert same later three because well work before the same under part very different became year did large example several city early until much government found own since she even form power do those around state including set high life against second century within world still end using small name what now usually American without however began like as well area make common the most water United States another way due must long less four death said film order due to back public does left based few become known as s given country major British place group considered among game point used to period support war music down million important systems control should took day family language last original result political line members case as well as see single just process along similar take following we although countries right either times areas published the other local include population never data home every various the time modern further development per how led possible military popular term though history generally you off rather men law developed German held human production body general the world light sometimes states late field based on having came above available book others York next created U.S. show himself out of wrote days died word play again great service age seen children level released works continued pp. the two five higher species energy required change means team January information theory New York produced making built design role addition included almost side position groups able de land total range July national space written social version Europe season force air allowed largest good type itself received women low throughout taken standard little least that is free cases size thus school especially old upon particular terms effect provide lower certain together present always short parts words third April described too up to established might played forces natural June once months rate European numbers six man rather than hand typically value England London October could be final the country September average France instead current December international program character increased a few surface across thought company followed best provided economic games significant named function building went return uses fact study below full source lost America person a number changes longer research individual languages strong structure party larger run open cause aircraft away far region need food forms increase outside started material cannot November half head market near record traditional special style all the sent story February player designed top at least themselves model returned band because of help types come points added events network limited services nature army former father close view allow won specific elements practice lines gave pressure introduced produce moved whether religious put official movement my Germany students trade method attack in order problems art eventually evidence referred results caused remained influence success meaning brought believed enough features give young white culture problem characters lead action according referred to conditions performance according to effects amount black business working better difficult temperature education real money smaller create located directly sound leading ground therefore get private formed particularly Chinese television subject south cities album class industry computer beginning community already complete go players associated related physical our town move complex interest rule speed commonly in order to rights living for example greater writing find growth killed court levels house against the office done shows Spanish needed saw central entire fire son books mostly access soon today earlier variety additional percent foreign ever recorded future widely title all of shown successful radio project construction changed king remains mass personal policy code includes involved Africa idea names separate base length rules key units likely makes release cost church films majority primary performed methods stated appear whole so that member allows decided Japanese reported sold capital science society rest stage event whose recent color legal highly career song frequently placed simple defined me mother appeared India schools turn simply relationship multiple nearly sense past relatively forced software attempt north William quickly companies programs products direct site sources previous technology battle front provides necessary served originally along with approach image seven experience Greek parts of text towards completed memory ability commercial UK health replaced worked yet start economy highest property behind knowledge test response largely sea reached president wide night operations supported act training reason Britain list issues occur loss the government reduced active functions remain lack to do clear gas island cells taking contains takes approximately exist basis distance independent intended elected product adopted actually wife ended born Christian keep potential course matter love issue objects things heavy oil center regular George the following divided eight date James summer direction laws numerous account income individuals concept plan announced Russian failed applied values northern proposed ships resulting estimated appears continue Canada red married object studies media engine passed Henry Australia machine quality founded hold say parties letter Japan expected treatment normal signal blood financial status video older carried compared know wanted accepted via read each other types of animals turned unit feature southern increasing met prevent songs applications require opened marriage contain equipment playing section remaining properties asked the case ten Charles activity whom ways buildings basic probably running materials stories edition location noted cell analysis Russia Rome removed becomes paper activities billion fall child cut operating degree ship occurs less than novel completely except inside Robert troops Jewish report call flow metal appointed offered told frequency initial campaign discovered definition historical models allowing operation police poor weight not have ancient here primarily agreed hit west heat security David claimed positive easily environment scientific going regions ISBN ball tradition reach requires St. extended charge female face serve equal Italy letters refused immediately believe supply effective internal versions cultural leave th mainly negative soldiers question spread suggested Paul\ndifference unique fully musical relations civil cover something board becoming famous hard plants situation workers Indian shot medical instead of risk device decision techniques weeks previously western phase responsible purpose race article gives reference giving authority critical sets Italian in the world ideas California growing element instance kept moving rock share nuclear causes reduce vote efforts table output car determined win era notes finally ordered application develop distribution represented leaving quite the population woman devices contrast offer tried presence the top mean scale us river collection content attempt to says powerful comes the best humans travel resulted proved African combined road felt page receive tax address teams connected pay annual despite disease birth closed kind agreement actual price claim goal management flight rise fuel brother maximum month presented write east male gold reaction powers resources latter plant middle motion observed rates your currently station appearance volume avoid consists deal families argued room scene user depending advantage weapons target prior records temperatures speech spent focus mission dead constant existence lived daughter signed relative blue occurred helped damage meant actions professional friends contact places solution Asia beyond meeting structures standards care exchange peace sequence carry county nor organization chemical showed factors federal transport capacity Washington e in that meet be used to claims upper pass perform attacks extremely effort the public opening tend initially literature alone attention paid sides useful centuries fixed follow winter Thomas crew origin serious alternative differences featured responsible for centre pieces strength measure examples track plans officers shape star Richard joined refer command existing cards leaders figure plays Louis piece houses classes conflict fields stars th century composed positions vary round gain tend to seeAlso impact slightly behavior depending on look as to nothing raised hands compared to reasons toward choice heart identified creating maintain refers condition constructed overall reading marked fourth division failure saying tour recently formal unable have to needs mind stations digital religion Spain think grew represent typical parents display competition acid and so fell week determine protection wave users build processes projects return to technique to keep fish cast drive starting unable to expanded affected entirely combination forward resistance entered factor equivalent attempts episode attempted away from providing sector external increases university ice ones achieved decades expressed Americans double demand regarded refer to architecture audience issued fight citizens match in all aid limit sexual goods seems costs distinct granted perhaps improved green file need to on to importance electric significantly DNA cycle normally components describes creation TV layer tree bring fighting stop and also core format increasingly thousands animal leader understanding break unknown classical extensive sign Israel the people host causing destroyed protect describe Peter arrived square voice industrial purposes magnetic containing finished follows nation rare cars continues that time planned radiation recording advanced engines principle student islands philosophy sought officials rapidly recognized secondary particles pattern staff covered runs traffic awarded lives deep the whole United Kingdom expansion identity context controlled images organizations derived Ireland administration bodies opposed friend chosen greatly and not designs nations surrounding urban zero otherwise error native setting gained why territory acts artists communication launched J. regional card freedom coast visible begin contract arms showing the past message generation involved in Mary nine leaves stone ran global popularity broadcast minor sites instruments improve wall contemporary soil Edward carbon conducted symbol as one at the end operate somewhat be seen close to news communities developing influenced domestic port machines figures views got declared note search contained employed input step lead to bridge respect worldwide caused by the idea captured want maintained route achieve publication consider magazine orders winning formation Australian ring reports onto subsequent details safety storage plane climate World War II accept connection the field dark managed practical shared be found measures visit capable movie spring calls independence obtained aspects dance learning opposition begins towns split brain interest in Jews at all technical directed introduction belief border owned prior to Chicago doing involves suffered transfer offers enemy correct possibly oxygen greatest exists measured closely policies dedicated press producing scholars steel heard accounts usage computers daily walls Canadian fiction rejected stable score engineering ensure indicate mentioned eastern forms of path skin proper trial oldest statement mixed most important officially portion reality the book dry heavily height authors fast hundred specifically author kind of block channel string Mexico Jesus institutions instrument residents combat selected vehicles coming manner rarely Irish goes hot pair royal neither organized effectively expensive really suggests processing opposite severe operated depends characteristics regarding yards enter interests glass college a high practices testing bands the story trees ends facilities place in faith grow slow armed churches mode background job writers defeated cable leadership transmission patterns thing Dutch density attached big bottom atmosphere networks degrees dropped electronic i might be strongly items roles translation pages apply assigned signals extent viewed Michael debate minimum periods programming visited ultimately frame hardware prices whereas as a result establish permanent holds strategy escape authorities acting join listed sex edge screen decade critics cold continuous generated solid violence liquid iron investment likely to sufficient composition broke experienced et exactly map ratio documents anything questions twice North America converted elections slaves threat time in studio contributed household wind train truth changing for each rural capable of sales secret bus football movements theories labor modified Christ cross expression stock atoms chain earth leads understand box impossible principles requirements weather considerable director earliest vehicle added to moral Johnson district literary respectively am set up prepared subsequently preferred trying sports mechanism concerned courts leading to task treated voltage translated hydrogen inspired victory advance appropriate faster nearby theme to go drawn standing arts parallel adding component relationships waves focused kill locations notable printed separated resolution tools officer subject to the amount essential medium opportunity solar etc. teaching argument don't Columbia GDP cultures painting tests bit amounts molecules roads launch crime fleet holding learned stay stored pure balance and all successfully picture politics circuit request Joseph environmental distributed produces affect prime revealed eye universe fans patients wood governments Berlin occasionally selection taught acquired fundamental linear Alexander choose episodes n floor angle incorporated try benefit description die studied Poland dates list of artist flat protein concluded hundreds speak extra subjects approved electrons chance easy implemented husband ideal planning concepts grown understood extreme intelligence quantum receiving Scotland online knew unless Jones out to rapid shall Texas academic reputation the future experiments explained park eyes sections audio in fact persons store budget corresponding sister charged broken capture script rich work on\nmoves opera Williams progress logic massive root assumed writer post be able domain foot link bank library document someone criticism everything huge linked performances represents seats thousand obtain bad mechanical Martin come to crisis and others clearly colors sample instructions the air circumstances electrical inner made of scenes copies e.g funds prominent straight personnel rank adult stand losses speaking ago m exact support for category fit housing necessarily attended limits mark couple roughly seasons widespread remove a certain street opinion review concern entry wear decisions exception identify categories compounds council efficient algorithm markets performing suitable the majority electron in turn males reaching serving slowly possibility stages horse skills concerns collected mental moment younger let manufacturing suggest recognition variable village add substantial styles detailed visual learn Angeles tells weak guitar defeat delivered ending feel in particular passing abandoned discovery poetry lowest mathematical apparent designated gun varies seem the church ethnic gradually newly p plot supporting and/or reducing Los Angeles confirmed hair supplies zone W. looking drug involving pain sell brief constitution benefits declined efficiency spiritual titles channels explain fear merely representation filled instruction perfect orbit replace guns tracks goals University Press articles lands matters conventional apart encouraged newspaper probability settlement silver succeeded symptoms a man in general in two joint populations transition vast legislation theorem votes immediate mountains adapted fifth fine genetic signs assistance consumption existed indicated symbols traditions settled prison spaces bass formula scientists the front transferred shift chose entitled kingdom save shortly candidates mountain one's desire mobile send lot sugar taxes trading interpretation else proof reform serves charges bits committed BBC secure steps the line the point Arab fewer infrastructure seek outer relation sort absolute drop files interface net implementation returning New York City bound decline morning variations vector absence attributed cancer membership responsibility spirit chief classified discussed Zealand discussion landing principal historians camera opposed to branch need for ordinary regularly e.g. survive agents proteins seconds universal interview accompanied arrested distinction reign specified repeated New Zealand accurate agricultural jobs unusual carrying mounted Oxford executed vessels work in yellow easier evolved peak permitted promote seat suit arranged tube unlike evolution reduction union writings dangerous wild dependent diameter pilot survived usual define equation noise particle rail together with circle installed interior none sounds streets variation wealth consistent finding in use novels passes weapon executive experiment exposure forest hunting integrated vertical hour protected restricted Egypt exposed partly boat fashion situations Virginia conference draw planet links strike tell outside of printing sentence depth Jackson at that criminal identical other than returns roots telephone agent argues Dr. communications engaged females convention frequent print i.e. uniform be an texts velocity transportation years old Hitler falls seemed Elizabeth administrative closer anyone debt references representing agriculture laid interested duties Wilson bear broad didn't physics finite server throne attached to award copy in addition meat fluid invasion affairs experiences median ceremony paintings vision IBM credit disk in front killing km losing demonstrated faced apparently composed of measurement regard wine writes stopped the present Sunday quarter tanks spoken Asian bar finds funding given to incident routes comparison procedure begun large number museum Hollywood birds experimental mathematics raise ruled biological consisting solutions theatre twelve drawing employees reference to spectrum banks damaged reactions stands blocks characteristic firm homes or not visitors clock agree favor ready safe prove Francisco accused flying an average desired painted varying the law Christians answer consisted evil traditionally brown interaction notably appeal extension recommended stability be in illegal bill forming industries it's lasted trained assembly candidate herself option association beliefs committee essentially percentage sons sum happened hope indicates minister von Germans layers Soviet Union purchased gender perceived promoted the season actors aspect deaths driven effect of stores Super Bowl clubs concentration worth arrangement as a result of keeping approaches surrounded Sweden chapter mouth plate revenue league phrase classic mixture researchers tool years ago calling errors lay reflect so-called statements hospital operational passage concrete plus thin arrival concert conduct heads offices conversion drugs for which messages treaty brothers deal with featuring the military attacked difficulty jazz strings try to la in front of pitch rotation elsewhere medicine miles comic fishing receiver column covers duty demands earned axis sleep want to web hence purchase Davis turns as of bought criticized generate Christmas hypothesis the data fired lose turning divisions in place at the same time businesses emphasis farmers label living in themes offering caught poem switch alcohol murder reflected youth senior boundaries handle historic indeed lists scheme superior arm connections dominated sun the greatest campus soul detail continuing master selling infantry missions the individual I'm random rose slave wrong destruction distinguished piano proposal sport partially producer rising suffering waters foundation artillery doctrine magnitude atomic boundary speakers accuracy department nd consisting of inhabitants meetings platform railway struggle thinking camp dominant getting in addition to ranges supports varied calendar educational papers enjoyed isolated to come boats sale door passengers tape Mr. diplomatic employment optical branches controls formerly milk x argue colour downtown influential solo Christianity editor empty owners rivers South Africa credited challenge classification controversy count worn characterized phenomenon responded retired fresh horses notion organic musicians snow involve vacuum attracted narrow technologies Philip boy interpreted invited made up depicted occupied surviving website destroy hearing organisms Muslim labour missile targets cited empire execution tail grounds lies index more than one marks partner restrictions creates controversial manager stress pairs remote restored festival carrier albums load specialized diverse kinds operator soft invented simultaneously configuration temporary differ focus on precise reforms bringing in part customers genre airport aware decrease denied live in the great codes complexity electricity exports stood frequencies purpose of quantity dating folk participate territories virtually buried peoples satellite equal to trip express properly quantities speeds perspective societies voted annually extend governor magic USA hole lifetime regime requiring wing Boston Netherlands account of equipped evening honor session steam surfaces DVD Lincoln admitted maintenance naturally make it separation thereby variables Lewis adults poverty retained armies wire ed. exclusively headquarters movies warfare roll United Nations aim explanation legs starts thermal Islam curve enemies lake objective volumes opponents rear civilian the market approval contribution fruit ownership prisoners gone ocean protocol regardless row tons facility reverse threatened wooden artistic fellow medieval meters stream Norway receives structural victims Mars Microsoft clothing emerged falling participants supposed attributed to fair fairly for two measurements tribes chamber consequence inflation operates owner stronger theoretical equally therapy Korea emperor offensive valid voting celebrated investigation origins spot supporters twenty Swedish recognize shorter tasks the ship tissue wearing revolution slavery San Francisco binary distinguish fought gods eggs injury a lot centers procedures\nrealized exercise hired buy disc long-term observation estate recovery Jerusalem mechanisms province Austria brand fly manufacturers trend bonds concerning dated functional observations substance independently logo replacement partial struck driving journey justice except for expand i.e intense one another publicly scheduled seeking warm aimed editions factory Vietnam difficulties plastic Arthur ranging Egyptian defend processor radar tank the East Frederick binding crystal motor rain spacecraft thick arguments attend hosted judge of age advice naval regardless of with this Indians everyone operators decay dimensions on which ties expedition racing sees formally sequences strict villages bomb contributions license underlying varieties Portuguese bacteria emergency gene spending breaking draft entering equations rely patent stayed colonies occasions tested tower covering divine reviews districts looked producers convinced infinite intellectual advertising maintaining maps teacher edited estimate indigenous races ranked coverage displayed dramatic explains cash concentrated courses guide referring samples sizes Brazil dog possession consist dispute distances improvements driver gravity hosts ions mining relevant winner Harry Stephen beam distinctive for instance passenger proportion regulations survey commission compound universities finish patient philosophical shooting strategic drum interactions radical relief ahead biggest contribute graphics logical scored poems this way consequences salt considerably bond complicated recordings definitions go to crowd explicitly wounded at which comedy delivery equilibrium alive applies competitive feed ill marry publishing actor agencies artificial compete Pennsylvania involvement manufactured metals neck NFL algorithms array keys readers requirement underground copper tourism conclusion consumer vice waste burned drew feeling insurance missing serve as tied as if by one eat jurisdiction nominated occasion retain talk voters addressed beautiful behaviour leg personality completion defensive gets violent supplied this point disorder intention rise to take place virtual wider capabilities custom describing diseases girls neutral illness imperial phone prey prayer provinces Israeli delay accident circular Philadelphia automatically negotiations reliable abroad adjacent calculated cavalry displays genes lyrics window database flag helps reduces register sharp stones dynamic farm missiles Pakistan attempting client influences referring to demanded entertainment flights hall potentially amongst opponent trains check facing familiar followers judges once again pointed rice chart cooperation ask newspapers obvious transmitted Greece alongside beings tends the throne women's st allies high school matrix sensitive stating beat casualties childhood composer much as terminal marketing coastal commissioned dollars entrance establishment penalty prevented shell compression faces Ohio quoted releases seeing Adams directions retirement a little kinds of Scott capita enabled spoke that one vowel mechanics molecular permission removal the common coalition conservative counties devoted titled wish baseball mainstream node registered engage notation singing atom funeral integral marine programme psychological rooms strip transformation shaped significance skill tournament Simon colonial constitutional detect engineers girl horizontal millions decreased hoped loop participation raw ruling god modes set in sky valuable coach columns drives fusion suicide remainder representative worship Francis compatible historian laser CPU c compact destination generations kings mail parliament recovered imposed in time minimal mix occurring ranks wings alliance coins consist of danger invention ongoing shares sphere acceptance assault believes environments relative to at that time handling liberal sure Apollo Cambridge collections in the past opportunities crimes deck deemed implies termed traveled Roosevelt bishops developments fitted minority altitude b corner decide exhibit foods keyboard momentum on one renamed collapse tall Kennedy bases depend divorce promised rocket wealthy Clark abstract corporate drink filter holes implement injured per capita walk rd Judaism excess hidden mention representatives spend synthesis Victoria abilities connect originated preserved rings confusion magnetic field resigned strictly unemployment Robinson orientation plasma portrayed similarly Islamic aged boys bright options partners planets Carolina Taylor addresses alleged bed institution rainfall well-known molecule pushed wars assist at one bears locally maintains winds export forth injuries vessel Florida historically awards bulk currency fallen laboratory manuscript capability colony interference minute physically Romans nucleus repeatedly vocal architectural eliminated initiated preserve trials all over certainly favour hear requested with it estimates wins alternate dependent on distant preparation sending the Union trust Albert ages banned creative psychology assigned to castle establishing firing heating by two drinking Vienna called for in response membrane no more parks singles drama extensively for it ministers next to retreat sixth to explain Kong conflicts phenomena powered sort of act as as it is Caribbean circuits headed identification longest permit punishment statistics variants as long as crucial dialogue saved scores teeth arrest commander fill improvement science fiction belt cutting des crown sole be called claiming immigrants literally restaurants restore taste accomplished participate in to sell Nazi agency bone campaigns chemistry happen personally pilots presidential recalled respond criteria enormous lie matches rescue variant wore corruption defines facts flowers gathered parameters quick solve tension audiences sight anniversary carries documentary encourage narrative reason for reportedly reserves teachers vital wheel arrive behalf diet seriously the road vowels yield infection perception poorly respective solely treat Saturday considering informed insisted interval teach van whatever Norwegian assets duration enable grant metric ports shock Jefferson Mexican assume attitude computing giant give the jury parent truly accessible burning crops gap profit shapes statistical consecutive determining drivers rational the distance adaptation briefly coal defence diagram doubt sfn acted appearances at home cooking dogs landscape prepare residence with respect to acids cabinet limitations oral sand come from consent counter discipline emotional encountered manage pictures severely suspended aware of smooth spin cargo correspondence pope publications valley warning bars entities altered challenges confidence eating jet lasting raising signature the many cattle deeply imported integration settings submitted tendency tubes deployed guilty nitrogen advantages intermediate open to framework only a few pop succession happens interesting predicted pursue detected in favor so much worst Illinois as such at the time of commercially disaster rely on survival Michigan dealing dimension filmed fled jump paint placing thereafter to death Catherine Howard manufacture temple Jim Napoleon as a whole cooling fan instances seed BCE Switzerland absorbed cool exclusive precipitation prize rival shifted watch willing Danish competing poet scope Hong asks departure depression dress presents reaches tries Olympic beer dust expanding firms guest lift regulation surgery wedding at first constantly legally reception walking analog confused discuss edges indicating muscle outcome schedule sessions settlers successor Bruce Roger exceptions hits masses meets delayed electromagnetic excellent geometry traveling Korean Muslims attacking libraries migration select tribute ultimate children's consciousness humanity priority feelings ignored intensity availability bones breaks carriers crop democracy fighters push apartment finance intervention make up shipping construct franchise looks situated Samuel catch craft dialects dramatically dual neighborhood opposing plates camps guard precisely verse North American borders dioxide gift nomination reserve reserved windows communicate consumed correctly engineer no one wet comprehensive unsuccessful verb Indiana adds diversity grand moderate prohibited v. Cuba appearing fail segment the City touch Moscow in the middle tables findings mutual nouns removing\ntended travels Gregory Scottish dynasty hotel specification unlikely Nobel democratic flows protest sufficiently bishop dishes escaped founding judicial packet scales arbitrary possess appointment ceased dismissed in favor of settlements theater Broadway contents cotton holiday several times account for as much as atmospheric crossing detection introduce orbital organs purely revised actively afterwards aired c. embedded happy waiting angles bread concerts for that grave praised precision pump visiting acceptable at night charts condita connecting coup deliver layout readily rebels suddenly intervals manual pronounced proven transported acknowledged beauty bet cult demand for economics legislature priests resource surprise urbe Ab urbe condita compromise crash elite expense forcing prefer automatic clergy plain replacing tropical vulnerable approached cloud commitment everyday frames integer putting rounds substances unclear Sydney documented farming mine mirror moon of interest politicians repair spelling strategies bombing harm tip wavelength Afghanistan lighter victim Joe exile linked to measuring updated Jupiter carefully coffee fraction freely provisions replied stem agreements asking der doors enforcement forests panel religions reveals roof sentences worse Allen Hebrew cm continuously cuts garden lacked studying the club IP doesn't exhibition muscles utility MHz al. conservation departments item only if overcome racial tactics the community Lawrence clinical corresponds customs employ graduate instrumental phases sword the Indian transform adoption canal demonstrate dense educated fighter graph priest rocks stops Margaret bow collect electoral enjoy made up of overseas participated profile secured transformed Persian affects ban clay fragments joining lens processors ritual City of fiber pool proportional reveal sacred Andrew Marshall avoided combine focuses inspiration on top qualities sit Colorado arose chip disappeared experts nodes pg. publish recover separately the few theology Welsh arise collective contest gases heritage inherited navigation payment residential weekly Douglas bridges concentrations consideration diagnosis latest navy organ Hamilton Jane amateur civilians closing consistently friendly grade helping sophisticated withdrew landed legacy resolved romantic rubber shut clean coin critic developers grain grammar internet radius Alfred anywhere attributes depend on directors disputes entity knows sharing shots Finland Kansas funded nobility orange thrown travelled Denmark circles expressions fee friendship governed Missouri bid in large internationally legend mild Iraq Muhammad Turkish Walter assumption eaten storm surrender telling the stage dying filed heated noun sitting smoke subset Otto bird breeding examination fate follow the jurisdictions occupation beach formats ion sciences the earth Massachusetts Steve commands fires reader Berkeley belong contracts deals debut elaborate mature on the other hand portions relatives revolt temporarily the truth trouble evolutionary partnership presentation sectors shells shoot shops this day three times unity Georgia authorized consumers newer substantially Daniel Hughes chips households journal magazines occasional pollution scoring so as suffer working on assistant daughters ink not known proposals sail the dead ammunition cameras so far attract large-scale loved profits railroad swimming thrust uncertain wages year in World War I disorders glucose mines pulled terrain tourist wants Jordan advances battles blade charter console dream exposed to fat feedback adopt autumn explosion lighting attractive chains cognitive package renewed touchdown fictional primitive revolutionary singer topic Gilbert Wright antenna contrary improving myth reflects shore Harvard buses essay fantasy gay in the field mainland opinions powder drawings eliminate most likely numbered pick recognised suitable for Tony calculations comparable portrait simpler steady tunnel captain clients clothes customer descent disputed flood heavier minerals scattered the normal topics absorption algebra birthday f imports teachings at this time default mere rough segments silent the true Hong Kong decreases grid in public most often observe seeds smallest striking the same way desert feared legitimate paths picked stack Anderson crystals flexible fund inserted slower statue the poor Austrian circulation extending facilitate genus hospitals lesser settle threats zones Caesar compensation found on in practice politically siege commentary consensus contacts dies doctor hostile monitor parliamentary suggesting sustained the usual affair aggressive conventions correspond cycles guidance handled licensed promotion rebellion sea level whilst Norman awareness citizenship condemned dancing differs encoding exist in feeding lakes palace professor seventh submarine talks the information visits Donald balls defining ease faculty preceding Mississippi Swiss accommodate comments festivals servers tea width absent accordance civilization commented compositions emissions golden relating restoration totally uniforms Russell abuse arithmetic bandwidth eleven in love planes pleasure prototype sheet skilled squares targeted wait collaboration continent differently doctors emission imprisoned inches organisation permanently serial static Taiwan enhanced enters equality filming habitat y Great Britain championship flew lunar mentions merchants nose productions promise symbolic to date virus Star Trek USS clouds enables neighboring secular Houston South America al apply to contribute to exploration hull implementations indirect nervous ruler speaker aimed at applying gains lights privately sweet technological translations blow cancelled cuisine determines loyal photographs suspected accurately aims dissolved rhythm traits Jr. boards brings hierarchy sacrifice stadium too much Carl arrangements chemicals combinations currents dealt doubled orchestra NASA d elevation encounter healthy resolve thoughts See also advised banking cinema cylinder derivative effectiveness on the right radioactive satellites set to switching withdrawal Arizona Moore cluster Morris colleges descendants isotopes rotating verbs Finnish Franklin dubbed essence gaining impressed manuscripts pole posts queen wasn't Athens Friday bronze expelled informal legislative loyalty regions of remarkable switched the letter Cooper Warner continental discussions memorial promoting ride systematic the soil throw uncommon Alan citizen discover divide observer pressures render risks Kelly can't controlling conversation founder immigration interviews merged names of Harold In July baby evaluation schemes Portugal RAF adequate considers descriptions reflection reversed slight syndrome talent the record tie treatments In April Juan beneath burn compared with favored in common monthly preventing reconstruction thirty competitions composers conjunction passive seal search for suited virtue Disney blind enhance immune impression leather linguistic mammals mineral pp prompted tobacco witness Kent Shakespeare acquire bearing celebration counted on the ground polar ready to realm wishes Brian after which do with evident gauge intelligent interpretations nerve out with philosophers progressive pull the lead Cleveland assassination excessive human rights ingredients monks nobles point of view sculpture spite Buddhist Karl challenged coordinate extends fails favorite tales Iran Puerto amino assembled battery closest erected explicit gravitational odd payments platforms provision reactor requests the planet Catholic Church Europeans Middle East Thompson en intent it may be alphabet delegates discrete farms judgment rendered shoulder sin successive wished Czech Dallas allow for associations gate harsh investors naming peaceful reasoning European Union Indonesia RNA acoustic coordinates full of graduated hill yields Orleans Sullivan assessment belong to dozen drag governing incorporate revived self sudden the press trace Ruth arc civil war combining practiced rulers Anthony Sierra blues coat colours deliberately discrimination reasonable unstable administered colored digits in length pregnancy qualified retail theatrical egg knowing meal meanings processed protests soldier vegetation withdraw biography engage in engagement loaded martial regulated strikes stroke tourists great deal noble paying propaganda raids resumed trends uranium Julian casting demonstration\nexpect fifty hero registers remember transit unified Ottoman insufficient isolation municipal refugees sentenced timing welfare st century autonomy boom decorated del fame on behalf of routine synthetic the border topology Christopher Second World Stalin accounting bombs hills spite of streams the help tongue toxic tribe united Benjamin Minnesota RFC all but ancestors combustion deposits electronics exceed explosive gathering illustrated in charge notice shop slot solved succeed tiny Carter Diego New England biology conceived if not neutron perfectly preparing relied valve vectors The American architect collapsed full-time mortality odds accounted harmonic reads sing cables constitute dollar handed loans moments pursued territorial to the right Greeks artifacts assisted barrier ceremonies coined deficit finger grows guests in exchange manufacturer of course phrases revenues Melbourne Syria bias credits emphasized fifteen kills symmetry Intel colleagues drove forbidden monetary strips unions corporations gross mayor questioned volunteers Alice aboard fees friction interact labeled lifestyle no other resist servants shallow work for Gordon and so on bytes loose populated touring FBI Palestinian Stanley consonant container emerging expert march on top of pregnant provincial tale Second World War armed forces di entries lengths openly threw wheels with that Louisiana differing ethical fabric independent of responses steadily viewing Marx New Jersey ahead of arrives closely related extinction hands of merchant modifications nevertheless prime minister productive rifle set out versus a great deal animation choosing defended explore hopes integers loan melting meter offspring outcomes reproduction rigid sisters switches Nicholas The National choices confined du genome persuaded possessed pounds seasonal sodium ticket Aristotle Murray New Orleans aside compiled footage honour observers papal spirits vs. acceleration apart from centres chest convert to publisher shopping borrowed breed careful dances murdered nights plague preference revival wound animated creatures crossed flavor flower harder innovation introducing investigate operas specify vegetables Belgium Parsons arriving attendance contributing elevated enzymes insulin peasants protocols angular athletes convicted eligible mixing monopoly pressed sends to the point wireless Anglo-Saxon Harris amendment at times constraints dish failing objectives organism rating secretary Hungary Reagan broadcasting cap celebrate designer drums exit factories harmony inherent present-day remembered so as to submarines technically the open Mr Oregon calculation carbon dioxide chapters completing deeper done in elementary neutrons prevents pulse shoes Pierre counting fed optimal projected rolling seized terminology wavelengths Campbell Constantine Manchester depicting geometric justified scholar wage Hall of Fame NBC afternoon cellular derive ear examined gates lacking small number voyage whenever Wilhelm broader desirable differential organised printer resignation revision Middle Ages controller excluded in the future initiative kingdoms most recent slide so many turbine uncertainty assumptions at about autobiography balanced belonging focusing inheritance pace presidency relating to relies reporting sovereignty throwing empirical give a photo soils timber Harrison airports armour at the top conducting devoted to editing functionality guaranteed in accordance oxidation oxide performers predict similarities transactions wheat wish to Marcus Ontario archaeological as is clusters councils emitted erosion holy occupy proceeded riding shipped span tours triangle vapor blacks emotions favorable hip identified as looking for nuclei offset periodic plural proceed sheets Turner advocated believing coupled genuine go on hybrid limiting long time not allowed outbreak protective spatial Batman Moses designers foreign policy fossil hop seeks singular tensions undergraduate work as Manhattan Thailand The New York Times consequently domains heir more or less optional organize passages printers professionals punk ranking reliability Churchill advent cloth correspond to decreasing exhibits gear in relation to lessons margin modules monitoring orbits stick Constantinople addressing boiling cheaper commanded compiler convenient copyright factions federal government generating modest necessity not used on board permits rays reject two weeks asserted flexibility furniture highway installation nickname of the best reinforced talking Darwin Nintendo allied basketball carry out destroying dose horror import liver nm skull Leo Victorian Wisconsin anxiety broadcasts consonants contexts earn grants labels missed monarch physician travelling up for Collins Nixon calcium continuity enterprise fingers onwards outstanding relates simplified summit syntax assuming blocked collecting debated implications lectures liked mid mood pipe pride resident responsibilities rod terrestrial the worst Amsterdam Latin America Prussia descended enacted freed in accordance with insects junior motivated overhead rituals twentieth valued Maryland Morocco achievement alter backed da decides draws fiscal livestock modification on the left patrol reflecting rows scholarship southeast survivors theological tissues towers Argentina abolished attitudes determination discharge explored referendum Atari Clinton D.C. I've Miami back of comment dreams dried northwest one or two proximity routing attending calculate cultivation exhaust ministry multiplication signing standardized strain University of California Zhou at once begin to brass communist emerge geographical hide hunt k oath philosopher satisfy Florence Milan Vietnamese chair curves emergence floating foundations inputs organisations touchdowns viewers Alaska Baltimore Roman Catholic Russians bell chess countryside declaration fatal filters grass keeps machinery outdoor panels predators traced trapped vocabulary wires Mrs. Parker arguing barrel competitors conquest hotels not use noticed restaurant sake southwest spots trigger undertaken CIA Eric Northern Ireland altogether ancestry conception derives dialect essays generic gifts myself omitted pay for salary syllable acquisition cooked costly decimal dominance donated identifying museums run by tactical the elements worlds Sri amplitude boost cheap enabling fever flesh graphic implied infant intact integrity nationwide photography prisoner protecting quarters seemingly suggestion tomb two-thirds vitamin Wagner bitter gardens innovative module praise prefix privileges sailing tonnes transmit Eisenhower Wallace chronic definite dominate drops extensions horn managing recreational saving scientist scripts shield the vote to this day valves Monday carved divorced execute fruits genres ratings representations respected undergo Luke Zeus choose to commanders declare gray in the middle of infrared literacy realistic safely thanks wisdom Alexandria afford editors inferior neighbouring participating pen pointing proceedings proceeds strange the gods the screen uncle voices Mitchell heaven horizon matching natural gas polarization practitioners tight Bernard Catholics Germanic armor axioms burial guarantee men's one day posted specimens stolen strongest treaties tuning achieving guards inhabited inverse neighborhoods profound projection urged withdrawn Antonio Stewart Torah allegedly badly conquered cream gather half-life imply more and more polynomial profession pursuit speeches trick victories Hungarian Mao brick by means of consistent with extraordinary kg lawyer managers militia not considered not exist prevalent providers quiet subtle testimony trail I will approximation conductor extract flash infected mistake moisture purple raid renowned stamp supposedly unconscious Ali Toronto adventure astronomical battlefield bore cat derivatives flux geographic intake invaded lifted minimize numerical opens oriented releasing simplest stamps subjected tenure venues abandon ballot champion compressed cultivated forty incorrect leaf offense sailed stressed toll Nazis Tennessee acute attraction case the defendant demanding diamond helicopter in charge of investigated memories monarchy nominal not know reluctant silicon Buddhism bay charity induced modify one time pass through peaks privacy stance tolerance venture adjusted backing be added belonged decisive dressed expectations harmful lateral mystery performs rivalry sunlight Baltic abundant freight inclusion look at owing predominantly premiere repeat struggled swing texture video game worker Anna Lloyd analogous barely collision compare conscious entropy eventual expenses finishing fission guided housed imprisonment prince\nsensors traded tribal whereby whites counterparts deceased develops dinner exhibited lying many times planted programmes revenge sick sung surrendered that's wives Abraham any time bills closure enterprises fibers flowing illustration playoffs suburbs tens the necessary torture viable walked commerce frozen guidelines in love with irregular metropolitan poles puts respond to rifles rises the works upgrade utilized Morgan associate centered coil corps economic growth exceeded inland investigations morality photograph preservation prone pronunciation similarity thesis truck adaptations byte faithful functioning kinetic marketed possibilities resemble with the exception of Isaac Thai algebraic as for attribute bombers burden companion counts couples defending discoveries elderly ensuing flour incomplete presumably refined reward sensitivity shifts threshold weakened Fred Oklahoma Sony a million affecting answers belongs cannon comics et al identifies musician nutrients on the surface publicity put on surplus torpedo transparent Eugene Prussian albeit allocated averages behaviors concludes damages emphasize ethics fun g kick locked paradox portable reply spreading starred telecommunications witnessed Hudson Saudi cement curriculum grammatical namely not take regulatory shadow socialist structured Malaysia absorb accessed answered boxes coding criticised devised encouraging failures fault grandfather imaging investments marking modulation mythology regulate scholarly studios the specific transaction voluntary warned Byzantine Ukraine Venice aerial disks efficiently fortune ideals incidents made for masters packets rendering speculation tiles tones up on youngest abundance arises certified clause conclusions lecture negotiate occurrence productivity react rebuilt remarked sharply underwent Holland Jonathan achievements ally as far as assumes cartridge copied cut off everywhere explaining farther fastest inability liberty obsolete point to prayers resistant weaker Hawaii arrows beta dam eternal fertility filling hardly helium indirectly receivers registration sealed sheep soundtrack staged sustain the crowd triple Einstein Linux Lucas Peru Pius actress chairman citing continually crowned enforce feels freezing globe journalist obligations overlap trademark venue warriors Afghan Native Americans Rhine Vatican barriers clocks cup exercises free of mouse out for preceded rated rejection spectral videos watching widow Boeing Columbus Denver Dick Matthew Saturn deployment designation earning elder email explanations loading lock marriages mile painter syllables Jan apparatus arrow aviation bullet contracted elect imagination in case kernel lengthy negotiated put in put into sat Bulgaria Pittsburgh about to accepting appear to be artwork breakdown buying considerations crews dropping flooding incoming interpret mate specifications stationary thickness topological unrelated Montgomery Native American Venus absolutely aesthetic as soon bigger burst conspiracy discontinued firearms in conjunction interfaces patents specially substitute Nigeria Warsaw aggression approaching astronomy avoiding enzyme fraud functions of invested log marched ordering predecessor relate salvation stems suppressed the latest versa Bulgarian South Carolina baptism beaches cache enforced hitting lamps people's propulsion prose secretly shaft sponsored stretch tune veterans Abu Hispanic Iowa batteries colonists correction couldn't customary debts formations formulas grades likewise profitable refusal rhetoric shut down supreme vice versa IEEE Nelson Romania Talmud Watson adapted to compensate consume cruise curved economies eighth grey handful high-speed highways pack rope time was universally urine Armstrong accidentally amplifier appealed arch convince declining in conjunction with individually naked problematic ratios twin Jacob adverse aided alike as soon as depiction ears indication kept in loses metallic scenario upgraded Austin Chris Hindu Lois Philippines bonus complained cousin damaging extant illustrations lacks predictions slope taxation ers Patrick admission ambiguous be at cleared composite deny desktop firmly governmental mothers packages resign sequel thumb adventures approximate at sea capitalism cave cricket crude encoded forever generates hub insight loads missionaries norm owing to payload poll seldom senses spread out whales Anglican Arabia Herbert advocate autonomous bin encounters exceptional family size gallery gradual hoping humidity kilometers mobility neurons northeast passion realize regards sailors subjective to the left Claudius accidents cavity championships commodity connection with contested in return peers picked up prints publishers r refuse rivals servant short-lived Roman Empire bankruptcy bottle cathode combines entitled to extinct helicopters honey republic validity winners witnesses Albania Kentucky Methodist Ross alien aligned analyzed ancestor announcement busy cartridges delays disciplines ecological ensuring envelope facto galaxy gaming grains innovations limbs magical merger norms relativity temples tense trips vendors Singapore activated after the war applicable biographer calculus climb cubic displaced economically famine in question interfere journals jumping ratified storms successes till vulnerable to Turing ambitious anger coming from computation computational contained in disabled employs grouped impose in power man's not possible paved refuses relay shortened simplicity sketch tuned underwater Americas Atlanta Cuban Detroit Friedrich Marie San Diego a bit cathedral diesel dynamics eldest happiness lawyers leagues licensing pension ray resonance satisfied sovereign thoroughly tidal watched Hz confirm conflict with denote diminished expertise extracted humor infections knights lung other side receptors Munich Plato alpha be determined challenging conclude cosmic dancers discs enrolled fears hiding journalists judged myths observing parade photons pink refuge retaining tenth the arts the string weekend Yale alternating angry breast by far compatibility displacement harvest impressive mandatory neighbors parameter statute terminals trucks verses Curtis Protestants Stuart USSR agrees births blamed denominations drought early years estates expressing justification microwave prominence stomach strengthened suspect the ring tournaments transitions weights beneficial blades committees comparing costume diving heroes in this way inexpensive invisible number one prescribed receptor simulation surprised violin wake Euclidean Nancy activists automated axiom bound to box office button canon compulsory crushed dice distributions drainage flame fur impacts in the air nationalist per cent summary transcription AFL Evans Venezuela all that appeal to climbing congregation connects dependence documentation employee examine homosexual inadequate listening lit lungs no. realism sampling signaling socially stake suits transmitter Liverpool North Africa electric field faction healing instability lamp lowered obtaining peaked prone to recommendations rockets rounded spinning the score unnecessary wartime Richmond Seattle assignment consistency in effect monastery onset robust scandal sensor triggered Edwards Panama Tokyo coefficient collectively de facto diagnosed exterior formulation grace he's inch interests of loops lost to municipalities practically sufficient to suspension warming weakness Barry Dave Rogers basically dealer debris generals ideology monument parking perpendicular rectangular the negative to retain violation Tim asserts buffer chord commit cure debates dozens earthquake exhausted fourteen in prison nervous system of language petroleum proclaimed readings rebel searching strengthen African-American Augustine built-in cheese elimination exported goddess governance internally liberation loud medal motivation placement recruited resort riders sauce sensory silence specialist traces tracking bonding bounded dissolution dividing exploit genera inscription likelihood obliged phones promising rotor sulfur Voyager accent bomber breathing complement frontier imagery meaningful not allow obligation persecution probe quantum mechanics solving the Academy theaters Giovanni Socrates USB aging auxiliary bind corporation decorative exercised mapping playoff railroads relocated rider seals specimen the eye the other side toured Dominican Serbia XML adjust call for chicken comfortable comprises flee hemisphere isotope pm postal preliminary protagonist somewhere suppress the religious treating Gary Syrian backwards bilateral but for coefficients convenience if and only if nuclear weapons princes progressed prosperity recall restriction slowed spare warrant Utah celebrations chaos critically diffusion drinks excluding\nfulfill galaxies in place of lawsuit mount sorts terrorist transfers verbal winters Alabama Joan at most bag brilliant converts creature decree deities depicts distortion ensemble feathers finest hockey induction latitude like to mutations peripheral plains pushing reactors thread volcanic wolf wool Hugh armoured digit honored jumps makers melody nationalism obscure predecessors programmers propagation provided for quarterback replication selective supernatural telescope traders tragedy Catholicism Colombia Indies Quebec admit celestial come in congregations correlation deposited desires doses favourite financing ft identities impulse injection instant knife not so numbering pocket resisted simultaneous spontaneous submit the Americas the particular triumph unrest unusually valleys Clement Julius Lebanon Sanskrit accelerated compilation defenders detector distance between enclosed extraction jail lasts loosely manipulation prosecution robot sexuality sixteen speaks stationed statues stopping subordinate superiority the numbers wounds Dewey Fourier HTML IMF Jerry Oakland Princeton Tracy accompanying basin downward generator guilt hanging incorporates linking matched photos physicians possessions pot prestige pumps tickets uprising upset voiced Edgar Larry Ltd. Nevada Roy UV Vikings appoint assert comprising executives exotic flown give up heading interactive parish pin reviewed short-term sympathetic tunnels Charlotte Palestine Rio chambers chapel comparatively contemporaries generators governors in full irrigation lattice lenses marching nephew plots presenting prolonged reprinted ski soap supplement surname yearly Belgian Calvin Celtic Gandhi Rico a thousand advancing biblical broadly concentrate dome formulated in support of inscriptions intentions monasteries poetic proportions rally resembles variance barrels brands careers chromosomes conferences creator designing destinations evenly instructed lanes nonetheless poets proprietary starring thirteen zinc Ferdinand Google New Testament Red Army Stanford Yugoslavia collaborated converting defects diabetes exchanged fort intersection key to manually natives one-third ought persistent potassium preparations quotes reconnaissance router salts smoking socialism sporting stretched two or three upward vol. volunteer way in welcomed with the help of Baptist Freud Ralph adapt agenda any one attorney battalions contributes cooler custody definitive locate migrated nationally premium pupils reproduce shelter the picture update upright wholly Adelaide Edmund Hopkins Romanian Salvador afraid be allowed beaten bets competed complexes marble minds resulting from sketches the administration Benedict Brooklyn Reynolds affiliated authentic browser certificate chances chocolate controllers coordination degradation dictionary gamma guitars higher education implementing in high justify marginal mutually mysterious posed protons quote surveillance temporal unchanged I/O Jedi Lithuania Luftwaffe Oliver Raymond Sox Stockholm architects automobile backward branches of collectors convey delivering diary ensured grip isn't lover maritime mutation persuade petition pose pronouns realised regain singers the good Holmes Iranian Motorola blocking buried in commenced disadvantage excited favoured hereditary oppose outward planetary proposition restrict rushing short story suffrage symmetric tennis to the end vocals Bryan Lanka Leone acclaim anticipated cartoon circa delegation demonstrations dive drafted energies geography intentionally juice ninth ozone plantations promises thanks to to let unaware Canterbury Charlie Dublin Geneva Portland aluminium cabin constitutes foreigners mountainous packed partition pixel probable rods rolled tricks very much wildlife Jimmy Malcolm Naples Roberts Ronald a base allegations allocation blend canceled constituted deciding ecclesiastical emotion lovers mask noting pulling purchasing shortage tallest Academy Award Africans alternatives at any time at the bottom bacterial ceremonial defenses directing discussing engaging expeditions force in genius imaginary interrupt introduces lab luxury photon portraits prestigious prevailing punished purity qualify repertoire retains shifting short of specifies stocks susceptible unexpected warmer wrapped South Korea aftermath bowl chooses cleaning discovers dispersed encryption header ineffective manages mankind medals once more pipeline retreated run in sensation tremendous whale Baldwin Beijing Cameron Columbia University Graham Norse Slavic accordingly ambassador as seen exploitation fossils interrupted keen noticeable poison risen souls tin ultraviolet unprecedented Barbara Final Fantasy Ian Jesus Christ North Carolina Southeast Asia a vast acclaimed constructing disbanded do it employers invitation nineteenth substitution accusations calm cord daytime deer feasible fix general public govern halls in a way integrate masculine orchestral right hand the wild three-dimensional tide Royal Navy Yankees accepts clan common law corners demolished dot immense instituted learns not enough penalties positively semiconductor struggles suite surgical swept take up the complete tram Wayne accession advocates arguably axes believe in canonical cease containers costumes darkness departed emperors financially golf habits locals massacre of service pathway possesses reactive respects scarce scattering tariff terrible threatening Croatia House of Representatives Ryan Vancouver adjectives amplifiers bicycle brightness contrary to corn declaring down on fertile forgotten hash holidays in line in. incidence kinetic energy obviously ordained peninsula remarks rigorous semantics sins sleeping spiral steep stripped the article trilogy Wikipedia World Bank X-Men capturing configurations dams floors horns ideological indoor innings maturity midnight pipes screening severity substrate trails walks USAF Vincent Wednesday abbreviated accidental ambiguity breach civic discarded ensures fierce hollow in advance in contact inevitable meditation mistakes provider sensitive to solids stays take on the dark underneath websites Bennett Jamaica Steelers accompany agree to airborne allegiance ammonia aristocracy comprised continents convergence crust diamonds licenses listen literal precedent proton raises take over tightly und unpopular wax Ann Burton a hard admired arena beef benefit from complaints concessions constellation depended devastating discusses enthusiasm gasoline harbour in the way intensive lost in overwhelming pound premiered proving reproductive routinely smell soup spherical staple tough vice president Prague Security Council South Wales Taliban Western Europe civil rights corrected crashed criterion disagreement dye inquiry keyboards patron reunited revealing telegraph the strong unification uniquely wines ASCII Conrad Eastern Europe Sami almost entirely asset caves continue to be distribute employer employing five-year for certain mandate paradigm positioned post-war regiment rent slopes speculated supervision tract Arabs Brazilian Game Boy Sri Lanka Susan airline anchor appeals burnt destroyers differed dwarf enlarged inequality licence of choice penetration propellant regiments small amount stuck take advantage of the opposition warships Blake Hoover Johnny additions altar cartoons checked complications courage depths discourse folded fragment hat infinity joke large amount octave ore resting retire run on savings summoned taking place the possible trap weighed Helen Macedonia Madrid Personal life Puerto Rico Redskins Sicily Thursday a couple of averaging behavioral censorship doubles eliminating feminist flags gambling halt helmet humanitarian in a series magnet not change pagan prevention repairs reporter sexually silk unhappy welcome I'll Iraqi Johann Republic of China Vegas activation amended assess be heard be known as beams beds can do coastline constituent epic for long fuels geological heights liability pottery suffix Amiga Carlos Kansas City Nile attain best-known brake bubble deity differ from exploring ferry free will honorary hypotheses innocent leap legendary nearest networking objected originating patch progression surveys terrorism undertook Costa ambient aperture beside darker drain earnings evaluate expresses famously generalized humorous impedance importantly in spite intrinsic kitchen manga obstacles peasant prohibition put it refusing seated short time sperm tactic theatres think of toxicity transformations waiting for Solomon abortion calculating captivity corrupt editorial equals every day happening limestone marker mid-s notions posthumously prediction pulses reconstructed sang set by warrior Bismarck Edinburgh Nero Sherman South African Unix altitudes analyze associates astronomers boot cats climates consoles dawn dietary divides embarked fatty fights gaps ghost hammer\nhurt in spite of infants inference instantly lifelong lightning mating neural performer physiological premises recurring rolls ruins runway rushed successors treason Athenian Isabella MTV North Korea assured blast butter ceiling come into comparative conflicting depict devil diagnostic differentiate disciples inherently mercury merge modeled oceans organizing outlined solvent suburban sums swim the special unofficial vertically viruses Chiang Ethiopia Gibson Luther Sudan Tamil USD at the start ballet conviction criminals destructive disappear experiencing for a time hate help to joints kidney military service motions quit real-time recipient referenced robots rotated senators submerged the details the general public trusted vibration violations wolves young man th-century Hepburn I'd aggregate alloys aluminum attracting blank contamination databases evolve hunters inform local government outlets owns periodically precious programmer seek to so long steal stepped strains summers t Gibraltar Latino Sarah backup be true benefited characterised colonization computed coupled with disliked feminine fortified fuselage hunted illusion illustrate mathematicians nest penetrate saints storyline survives transistors utilize well in yeast Bavaria Tucker accumulation after that anonymous beating by law conceptual denoted deposition exchanges folklore glands glory habit holder in the face of in the presence of indicator launching lineage pale programming language put to real estate recognizes sculptures separating trade with yard Buddha Kim Mann Theodore accumulated analogy anime annexation auction chromosome complementary coronation day in demonstrates drift familiar with floppy in service initiatives it will be lifting maternal metabolic numerals organizational presumed revelation spectacular suspicion Bros. José Qing Ted Terry Tolkien anatomy at the expense of circulated commodities counterpart crosses deaf distinguish between embraced explosives interim knight listing mud municipality polymer preferences screens touched vertices young people English language Las Vegas Sega athletic behave commentators disability download evaluated floods gradient hung infinitely junction palm presidents private sector semantic spectators subsidies susceptible to sustainable Carnegie Helena Kosovo Montreal Norfolk after this amounted be difficult bull by hand certification chloride chorus cocaine confrontation criticisms crowds defeating deviation energetic first place fitness guerrilla home run homosexuality in search of incompatible jointly miners opium passing through pathways phosphate platinum proofs railways rape the present day trait wanting Esperanto Irving Libya airlines amino acids be regarded compliance continuation dissolve exploited graphical inconsistent knot mirrors mistaken negatively oscillator pirates pretty treatise withstand Amazon Lutheran Manuel Old English Westminster attachment be on bison blame carriage contradiction cruisers dairy distinguishing drunk economists enlisted feast granting house in interfere with interpreter jerseys locks loves mice of use pixels quest reasonably reinforcements rejects seas slang the moon threads trunk unlimited wise Aaron Albany Milton Vladimir be expected be possible besides bold comprise depressed dining exiled ignore informally meals nicknamed panic ranged recession saint snake terror torque undergoing Arnold Caroline Connecticut Jacques Saxon Thomson battalion boarding brought about continuum distinctions drummer exceeding exhibitions halted humour le manned memoirs progressively rainy rescued styles of submission the limit Antony Hugo Jon NBA PRC Scots accounting for communal cylinders deliberate earthquakes feudal head of state histories leisure look for null popularized portrayal prepared for propose referee retrieve satire siblings staying strained surprising the crown updates wells workforce Chaplin Gaza House of Commons Wittgenstein acquiring afterward assassinated averaged canals capitalist civilizations compensate for consolidated dots embassy exceeds graduates hazardous helpful initiate lots motors offshore on land procession shoots specialty teaches the executive thinks tired Bengal Cromwell Emma Geoffrey Kevin New South Toledo Welles acres ancestral assessed cardinals cockpit comfort ever since habitats horizontally iconic inclined jewelry know the legends liquids methodology missionary outline pursuing repeating sanctions suppression tariffs theorists transistor treasure triangular vague vote for wherein Argentine Franz Leonard Macedonian Malta and the rest anyway bent communicate with cooperative coordinated deputy disposal endorsed in between markings mounting obliged to offence overview perceive pleased pressing proposes rotate run for screenplay shuttle spanning upwards usable veto Achilles Saudi Arabia Ukrainian all the way balloon coating compete with confronted deposit domination done with doubts fake for the purpose of hell heroic incorporating jaw limitation nominations painters paired precursor quantitative reconciliation removes resemblance revisions satisfaction squadrons step in supporter unwilling KMT Kant Kenya Normandy Powell beans captive except that gameplay healthcare homage knocked migrants millennium minorities ought to patronage pins prototypes proud randomly reservoir reside shortages tag tapes the living thicker torn transmitting yes alert cardinal carry the checks convex cooled deeds denounced endemic endings facilitated finale financed focal for life freshwater invade knee look like lord patented prizes respiratory result from shoulders throughout the world translate trio unreliable visibility Carthage Cicero MGM Odysseus Steven approve arrays ballistic be present be understood boss cone critique deficiency devastated gang highlighted in combat intervene metaphor needle not occur p.m. piston propositions rats riots second edition sediment soluble springs straightforward vicinity violated work at Godzilla Harvey Hausdorff LP Nicaragua Star Wars The First Titus Truman accommodation adjustment attained cancel eighteen equator exponential function as hierarchical in print induce manipulate neglected outright parity principally recreation securities verified Central Asia Jew Neil airplane cassette centralized charging coasts coral distress etymology evacuated exclusion from the first glasses hostility hydraulic hypothetical in color institutional issuing oak oneself originate pants plaque psychologists set theory spheres suggestions underway vote in Chen Democratic Party Egyptians Gabriel Kabul Mormon Routledge Thames bloody catalogue collector compose displaying fall into fermentation holdings icon in parallel in writing kit pad patches phonetic rites salmon sits splitting substituted surpassed tears terminated the interests wherever Almost all Congo Galileo Lockheed abbreviation aerodynamic attacker certainty chords colleague conform convincing denotes derivation desperate eager exclude field goal four-year generous globally have done immunity in contact with in detail injected invest joy malaria murders nationality occupies owed relying resurrection shed sink solitary sorted sparked stresses the courts trade in turbines useless working class Alex Hinduism IQ Turks a poor acknowledge all-time archaic bare batch bricks but then comply contraction diffraction ed emphasizes endangered escapes expectancy galleries improves joins lady laying migrate modeling monk moons nerves objections overthrow perform a pigment pioneer pitcher residual sunk synthesized trajectory trivial wouldn't Birmingham Craig Falun Fraser Monroe New Mexico New South Wales Petersburg aquatic arising boiled bottles breeds conditional constructions diagrams equivalence expenditure flies fluids going on guitarist hire hosting indefinitely miniature other's outputs pitched pray prophet ready for restoring rush scan selecting smart spy stripes subsidiary superseded tails the summit Babylonian Dakota Kenneth Ron affinity be regarded as beats clearing decoration demographic depleted drying enthusiastic experimentation finals heart attack hunger inception incorrectly inscribed interacting left hand lions mercy merit morale offerings olive painful psychiatric snakes sometime spell spotted viewpoint virtues Hume Milwaukee Whigs all in assertion asteroid blockade casino chiefly chiefs commissions debuted ecosystem fireworks fundamentally gentle immigrant in operation in the absence of limb oils physicists plantation republics routers shortest streak subspecies sympathy throat woods A major Ethernet Macintosh Maine Mickey Sacramento a deal apple ash assign cane coconut coupling elastic embryo escort facial for sale hatred honors inversion its way make use of on earth passed by programmed prolific rebuilding resolutions sentiment settling sinking specialists suppose synonymous talents worried Barnes Delhi First World Grammy\nHerodotus Ludwig Shaw accumulate alignment anthropology attested backbone classify coherent collisions composing compute demonstrating devotion fall in fragile fulfilled graduation guides inefficient influx live on luck more than half not believe official language onward persisted predominant prominently recommendation sails shocked skeleton slots stellar surroundings the duration tips v vertex African American Barcelona Tibetan Tuesday Viking adjective alliances armored at one time attracted to attractions be changed besieged campuses cancellation coloured companions compelled cow deteriorated directory disastrous ejected evaporation favourable finances float gel handles in society in the event of jokes lap like that no matter nobody pigments practicing scanning stimulus subject matter the authorities trillion voltages Gaulle Grand Prix Julia Oxford University Press Vermont alcoholic authenticity ceramic inaugurated inducted insertion institute matrices natural resources prevalence signatures torpedoes undergone Carson Jason NCAA antiquity arcade become one coming to comparisons conductors contingent depictions dispatched downstream enduring go into granite intercepted jersey legitimacy lesbian mill monuments occupying parallels pointer recursive the contrary weigh yielded ATP Boris Greenland Hitchcock Latin American Phillips Rachel Shanghai attended by cites conservatives contrasts corpse decomposition fluctuations fog forum imagine listeners modernization obey optimization pigs popularly relaxed repaired resembling scenarios tile topped troubled tunes unwanted Bosnia Burke DJ Hawaiian Mongolia RCA Sierra Leone States, the Tacitus all over the world antennas apartments breakthrough deadly dignity donations dragon ecology embrace ethanol executing expired follow-up glaciers gospel grams income tax larvae maximize metabolism preaching prefixes premier promptly propeller rails reporters reunion ridges squad sticks subdivided supplier suspicious talked to some extent upcoming volatile workshop AFC Algeria Babylon Beatles Cohen Gerald Hubbard Santiago The President VHS acidic comic book conscience cylindrical disagreed establishments fungi impractical inspection lasers microscopic mold monastic monitors ourselves potato potatoes recorder resume schools of seize telephones theorems welding Buffy Cornwall HD Hampshire Kirk Norton Pluto The International Vulcan and blue archives baptized burns cf fairy farmer figure in in production of old planting poured protects provoked rim ruined securing somehow spells sued targeting tertiary with regard to you're Allan Andy Dover Ernst Franco Gaelic Israelis Obama TARDIS West Indies Zimbabwe advertisements amendments bullets causal communion crystalline demise disasters encompasses fold for free garrison heirs hormone in reference to intellectuals intimate invalid jets linguistics morphology nutrient orthodox pioneered promotional radically relate to simulate tangent there's verify viewer Castro Eusebius abolition antibiotics breath brigades buttons checking commonly known constrained deprived duo enslaved epidemic eruption ethnicity expenditures expulsion homeland in reality installations lever lucrative pick up pledged pork prospect racism reinforce remnants slogan snowfall spaced stretching very well visitor washed won't BIOS Dennis Dorothy English-speaking HIV Hamburg Ltd O. Wilde anode aristocratic chase confirmation confusing cooperate defect diplomacy disappointed discretion drastically electrically entirety experimented explorers fathers fins flank frustrated humid in case of lined mathematician microprocessor misleading motive plagued prefer to presided radio station rap recognizable republican systematically take part in truce Bermuda Breton Clarke Indonesian Plutarch Taft answer to authoritative casual coincide commonplace delta descriptive dug emigrated fearing holders in excess it must be localized parachute pedal pit preserving privilege quartz radios realizes rid stimulated subscription transforms ACLU Albanian Brunswick Delaware Franks Haiti John Paul Taiwanese additive administrator adopting ambitions arrange cake coils congestion constituents contrasted daylight efficacy emit expectation first round font fortifications good and h in the background incomes inventory linguists lion mills narrator non observable packaging pendulum poisoning queue rebuild rotary see that skating stretches the Commonwealth theoretically therapeutic throughput transferring voter weighing without being First World War Leopold Mason Nasser UC West Africa abstraction academy accomplish alarm at this point celebrity configured conquer consulting contaminated cortex day-to-day disguise donation fence fleeing hearts highest point honest hook imminent in the hands of insect inventor lease mathematically melt not work oversee photographic politician polls portray rewarded routines sacked setup shade skiing spelled sponsor sworn under construction unfinished unsuccessfully veteran Adolf Athena Broncos Erie Estonia Gustav Horace Nietzsche PCI Paige Syracuse abnormal bark bodily commanding decks endurance escaping exceptionally fibre flooded grounded implying in private in the process of labelled landowners middle class monster multiplied nowadays overnight professors ratification theft triangles Alfonso PlayStation Quran Yemen alloy atop be due beads binds canvas catalyst cipher counsel declares fauna flavors fused hazard implicit infancy interviewed medications obstacle open the participant political party proponents recruit sacrifices subscribers sweep textiles visually void Basel Ernest Falun Gong Keith Mbit/s Mozart Palestinians Persia The King Tibet admits affection amino acid ancient times blessing derive from diagonal discouraged enrollment faded fried highlight install locomotives lowering memorable minus monarchs monitored nickel notorious papacy pleasant polymers promotes rabbis recovering reed romance safer sank so it is spacing spans thorough touching travelers uniformly Athenians Chapman Diana Ecuador Hanover Heracles Indianapolis Morse Scotia Wall Street adhere adherents aforementioned analyses angel antibodies archive bags bat classroom commemorate contacted contempt cook corrosion doubling elevations foul graphite greenhouse inventions kids landscapes modern-day overlapping pollen postponed puzzle responds rhythmic ridge shoe spinal stimulate strands struggling swords tags unto variously Assyrian Bobby Davies Hannibal Joel Khrushchev accelerate believers brakes bugs categorized come up constants convoy definitely differentiation disadvantages duel evolving grandmother harbor hostilities incapable journalism lexical lips live with metaphysical mystical negligible not found on the part of paragraph passed on password protested pseudonym qualification recognise recognizing rooted seating seem to be sexes taxi the Father transitional twins vegetable verdict z Cincinnati Hamas Hancock Luis Savoy Whig Xavier Yankee biased bleeding bride concealed cope cycling discovering elders exaggerated for a long time genetically in principle motives noon not included pads parody patrons plug prevailed radial reproduced rocky southeastern strengthening stuff textbook the weather unavailable utilizing veins voluntarily worthy years. Berber Bohemia British Army Great Lakes Gregorian Humphrey People's Republic The Roman administrators angels appreciation athlete backgrounds be thought of belts can not characterization choir conjecture deposed distinguishes first edition fitting fortress guy in with investigating lethal lightweight nomenclature of the dead personalities rented rumors strand sunshine the campus the unique tragic upstream very good Ambrose CPUs Copenhagen adulthood advancement affiliation algae anarchist appointments ashes back and forth boots care for celebrities confident confiscated connector dB denomination donor enthusiasts equity establishes first person harvested human being inside of liturgical mediation monsoon plea probabilities pulp recruits reel reformed replica rotational supervised supplemented textbooks trauma tumor turning point undertake upgrades Clara Heinrich House of Lords Huxley Indo-European Jerome Keynes Lenin Macmillan Omaha Somerset Versailles Weber a.m. air force angular momentum augmented be so bypass calibration discharged electrode fight against from the beginning get to grief heterosexual imagined impurities lifespan martial arts miss negotiation nurse provisional public domain quietly quotation secrets segregation to be seen truths two-year unpublished vastly velocities Cambodia Latvia Saxony Stevenson The European Whitney adaptive analytical anthem coated compass convened cows cyclic dedication delicate dictatorship dipole disturbed drop in encourages founders free from hybrids incentive infectious knots latitudes lineup methane mg narrower originates pearls populace postwar proverbs public opinion realization recipes reflective slip tender Bristol Church of England Goldman Hercules"
  },
  {
    "path": "demo.py",
    "content": "import cv2\nimport torch\nimport os, glob\nimport numpy as np\nimport gradio as gr\nfrom PIL import Image\nfrom omegaconf import OmegaConf\nfrom contextlib import nullcontext\nfrom pytorch_lightning import seed_everything\nfrom os.path import join as ospj\n\nfrom util import *\n\n\ndef predict(cfgs, model, sampler, batch):\n\n    context = nullcontext if cfgs.aae_enabled else torch.no_grad\n    \n    with context():\n        \n        batch, batch_uc_1 = prepare_batch(cfgs, batch)\n\n        c, uc_1 = model.conditioner.get_unconditional_conditioning(\n            batch,\n            batch_uc=batch_uc_1,\n            force_uc_zero_embeddings=cfgs.force_uc_zero_embeddings,\n        )\n        \n        x = sampler.get_init_noise(cfgs, model, cond=c, batch=batch, uc=uc_1)\n        samples_z = sampler(model, x, cond=c, batch=batch, uc=uc_1, init_step=0,\n                            aae_enabled = cfgs.aae_enabled, detailed = cfgs.detailed)\n\n        samples_x = model.decode_first_stage(samples_z)\n        samples = torch.clamp((samples_x + 1.0) / 2.0, min=0.0, max=1.0)\n\n        return samples, samples_z\n\n\ndef demo_predict(input_blk, text, num_samples, steps, scale, seed, show_detail):\n\n    global cfgs, global_index\n\n    global_index += 1\n\n    if num_samples > 1: cfgs.noise_iters = 0\n\n    cfgs.batch_size = num_samples\n    cfgs.steps = steps\n    cfgs.scale[0] = scale\n    cfgs.detailed = show_detail\n    seed_everything(seed)\n\n    sampler = init_sampling(cfgs)\n\n    image = input_blk[\"image\"]\n    mask = input_blk[\"mask\"]\n    image = cv2.resize(image, (cfgs.W, cfgs.H))\n    mask = cv2.resize(mask, (cfgs.W, cfgs.H))\n\n    mask = (mask == 0).astype(np.int32)\n\n    image = torch.from_numpy(image.transpose(2,0,1)).to(dtype=torch.float32) / 127.5 - 1.0\n    mask = torch.from_numpy(mask.transpose(2,0,1)).to(dtype=torch.float32).mean(dim=0, keepdim=True)\n    masked = image * mask\n    mask = 1 - mask\n\n    seg_mask = torch.cat((torch.ones(len(text)), torch.zeros(cfgs.seq_len-len(text))))\n\n    # additional cond\n    txt = f\"\\\"{text}\\\"\"\n    original_size_as_tuple = torch.tensor((cfgs.H, cfgs.W))\n    crop_coords_top_left = torch.tensor((0, 0))\n    target_size_as_tuple = torch.tensor((cfgs.H, cfgs.W))\n\n    image = torch.tile(image[None], (num_samples, 1, 1, 1))\n    mask = torch.tile(mask[None], (num_samples, 1, 1, 1))\n    masked = torch.tile(masked[None], (num_samples, 1, 1, 1))\n    seg_mask = torch.tile(seg_mask[None], (num_samples, 1))\n    original_size_as_tuple = torch.tile(original_size_as_tuple[None], (num_samples, 1))\n    crop_coords_top_left = torch.tile(crop_coords_top_left[None], (num_samples, 1))\n    target_size_as_tuple = torch.tile(target_size_as_tuple[None], (num_samples, 1))\n\n    text = [text for i in range(num_samples)]\n    txt = [txt for i in range(num_samples)]\n    name = [str(global_index) for i in range(num_samples)]\n\n    batch = {\n        \"image\": image,\n        \"mask\": mask,\n        \"masked\": masked,\n        \"seg_mask\": seg_mask,\n        \"label\": text,\n        \"txt\": txt,\n        \"original_size_as_tuple\": original_size_as_tuple,\n        \"crop_coords_top_left\": crop_coords_top_left,\n        \"target_size_as_tuple\": target_size_as_tuple,\n        \"name\": name\n    }\n\n    samples, samples_z = predict(cfgs, model, sampler, batch)\n    samples = samples.cpu().numpy().transpose(0, 2, 3, 1) * 255\n    results = [Image.fromarray(sample.astype(np.uint8)) for sample in samples]\n\n    if cfgs.detailed:\n        sections = []\n        attn_map = Image.open(f\"./temp/attn_map/attn_map_{global_index}.png\")\n        seg_maps = np.load(f\"./temp/seg_map/seg_{global_index}.npy\")\n        for i, seg_map in enumerate(seg_maps):\n            seg_map = cv2.resize(seg_map, (cfgs.W, cfgs.H))\n            sections.append((seg_map, text[0][i]))\n        seg = (results[0], sections)\n    else:\n        attn_map = None\n        seg = None\n\n    return results, attn_map, seg\n\n\nif __name__ == \"__main__\":\n\n    os.makedirs(\"./temp\", exist_ok=True)\n    os.makedirs(\"./temp/attn_map\", exist_ok=True)\n    os.makedirs(\"./temp/seg_map\", exist_ok=True)\n\n    cfgs = OmegaConf.load(\"./configs/demo.yaml\")\n\n    model = init_model(cfgs)\n    global_index = 0\n\n    block = gr.Blocks().queue()\n    with block:\n\n        with gr.Row():\n\n            gr.HTML(\n                \"\"\"\n                <div style=\"text-align: center; max-width: 1200px; margin: 20px auto;\">\n                <h1 style=\"font-weight: 600; font-size: 2rem; margin: 0.5rem;\">\n                    UDiffText: A Unified Framework for High-quality Text Synthesis in Arbitrary Images via Character-aware Diffusion Models\n                </h1>        \n                <ul style=\"text-align: center; margin: 0.5rem;\"> \n                    <li style=\"display: inline-block; margin:auto;\"><a href='https://arxiv.org/abs/2312.04884'><img src='https://img.shields.io/badge/Arxiv-2312.04884-DF826C'></a></li>\n                    <li style=\"display: inline-block; margin:auto;\"><a href='https://github.com/ZYM-PKU/UDiffText'><img src='https://img.shields.io/badge/Code-UDiffText-D0F288'></a></li>\n                    <li style=\"display: inline-block; margin:auto;\"><a href='https://udifftext.github.io'><img src='https://img.shields.io/badge/Project-UDiffText-8ADAB2'></a></li>\n                </ul> \n                <h2 style=\"text-align: left; font-weight: 450; font-size: 1rem; margin: 0.5rem;\">\n                    Our proposed UDiffText is capable of synthesizing accurate and harmonious text in either synthetic or real-word images, thus can be applied to tasks like scene text editing (a), arbitrary text generation (b) and accurate T2I generation (c)\n                </h2>\n                <div align=center><img src=\"file/demo/teaser.png\" alt=\"UDiffText\" width=\"80%\"></div> \n                </div>\n                \"\"\"\n            )\n\n        with gr.Row():\n\n            with gr.Column():\n\n                input_blk = gr.Image(source='upload', tool='sketch', type=\"numpy\", label=\"Input\", height=512)\n                text = gr.Textbox(label=\"Text to render:\", info=\"the text you want to render at the masked region\")\n                run_button = gr.Button(variant=\"primary\")\n\n                with gr.Accordion(\"Advanced options\", open=False):\n\n                    num_samples = gr.Slider(label=\"Images\", info=\"number of generated images, locked as 1\", minimum=1, maximum=1, value=1, step=1)\n                    steps = gr.Slider(label=\"Steps\", info =\"denoising sampling steps\", minimum=1, maximum=200, value=50, step=1)\n                    scale = gr.Slider(label=\"Guidance Scale\", info=\"the scale of classifier-free guidance (CFG)\", minimum=0.0, maximum=10.0, value=4.0, step=0.1)\n                    seed = gr.Slider(label=\"Seed\", info=\"random seed for noise initialization\", minimum=0, maximum=2147483647, step=1, randomize=True)\n                    show_detail = gr.Checkbox(label=\"Show Detail\", info=\"show the additional visualization results\", value=False)\n\n            with gr.Column():\n\n                gallery = gr.Gallery(label=\"Output\", height=512, preview=True)\n\n                with gr.Accordion(\"Visualization results\", open=True):\n\n                    with gr.Tab(label=\"Attention Maps\"):\n                        gr.Markdown(\"### Attention maps for each character (extracted from middle blocks at intermediate sampling step):\")\n                        attn_map = gr.Image(show_label=False, show_download_button=False)\n                    with gr.Tab(label=\"Segmentation Maps\"):\n                        gr.Markdown(\"### Character-level segmentation maps (using upscaled attention maps):\")\n                        seg_map = gr.AnnotatedImage(height=384, show_label=False)\n\n        # examples\n        examples = []\n        example_paths = sorted(glob.glob(ospj(\"./demo/examples\", \"*\")))\n        for example_path in example_paths:\n            label = example_path.split(os.sep)[-1].split(\".\")[0].split(\"_\")[0]\n            examples.append([example_path, label])\n\n        gr.Markdown(\"## Examples:\")\n        gr.Examples(\n            examples=examples,\n            inputs=[input_blk, text]\n        )\n\n        run_button.click(fn=demo_predict, inputs=[input_blk, text, num_samples, steps, scale, seed, show_detail], outputs=[gallery, attn_map, seg_map])\n\n    block.launch()"
  },
  {
    "path": "metrics.py",
    "content": "import lpips\nimport os,glob\nfrom os.path import join as ospj\n\ndef calc_fid(fake_dir, real_dir, batch_size=1, gpu='0'):\n\n    print(f\"evaluating FID score between '{fake_dir}' and '{real_dir}'\")\n\n    os.system(f\"python -m pytorch_fid {fake_dir} {real_dir} --batch-size {batch_size} --device cuda:{gpu}\")\n\n\ndef calc_lpips(fake_dir, real_dir):\n\n    print(f\"evaluating LPIPS score between '{fake_dir}' and '{real_dir}'\")\n\n    loss_fn = lpips.LPIPS(net='alex').cuda()\n\n    fake_paths = sorted(glob.glob(ospj(fake_dir, \"*\")))\n    real_paths = sorted(glob.glob(ospj(real_dir, \"*\")))\n\n    dists = []\n    for fake_path, real_path in zip(fake_paths, real_paths):\n\n        fake_img = lpips.im2tensor(lpips.load_image(fake_path)).cuda() # RGB image from [-1,1]\n        real_img = lpips.im2tensor(lpips.load_image(real_path)).cuda()\n    \n        dist = loss_fn.forward(fake_img, real_img)\n        dists.append(dist)\n    \n    print(f\"lpips score: {sum(dists)/len(dists)}\")\n\n"
  },
  {
    "path": "pretrain.py",
    "content": "import torch\nimport torch.utils.data as data\nimport pytorch_lightning as pl\nfrom omegaconf import OmegaConf\nfrom sgm.util import instantiate_from_config\nfrom pytorch_lightning.callbacks import ModelCheckpoint\n\n\ndef get_dataloader(cfgs):\n\n    dataset = instantiate_from_config(cfgs.dataset)\n    dataloader = data.DataLoader(dataset=dataset, batch_size=cfgs.batch_size, shuffle=False, num_workers=cfgs.num_workers)\n\n    return dataloader\n\ndef get_model(cfgs):\n\n    model = instantiate_from_config(cfgs.model)\n    if \"load_ckpt_path\" in cfgs:\n        model.load_state_dict(torch.load(cfgs.load_ckpt_path, map_location=\"cpu\")[\"state_dict\"], strict=False)\n\n    return model\n\ndef train(cfgs):\n\n    dataloader = get_dataloader(cfgs)\n    model = get_model(cfgs)\n\n    checkpoint_callback = ModelCheckpoint(dirpath = cfgs.ckpt_dir, every_n_epochs = cfgs.check_freq)\n\n    trainer = pl.Trainer(callbacks = [checkpoint_callback], **cfgs.lightning)\n    trainer.fit(model = model, train_dataloaders = dataloader)\n\n    \nif __name__ == \"__main__\":\n\n    config_path = 'configs/pretrain.yaml'\n    cfgs = OmegaConf.load(config_path)\n    train(cfgs)"
  },
  {
    "path": "requirements.txt",
    "content": "colorlover==0.3.0\neinops==0.6.1\ngradio==3.41.0\nimageio==2.31.2\nimg2dataset==1.42.0\nkornia==0.6.9\nlpips==0.1.4\nmatplotlib==3.7.2\nnltk==3.8.1\nnumpy==1.25.1\nomegaconf==2.3.0\nopen-clip-torch==2.20.0\nopencv-python==4.6.0.66\nPillow==9.5.0\npytorch-fid==0.3.0\npytorch-lightning==2.0.1\nsafetensors==0.3.1\nscikit-learn==1.3.0\nscipy==1.11.1\nseaborn==0.12.2\nsocksio==1.0.0\ntensorboard==2.14.0\ntimm==0.9.2\ntokenizers==0.13.3\ntqdm==4.65.0\ntransformers==4.30.2\nxformers==0.0.22.post7\n\n"
  },
  {
    "path": "scripts/preprocess/laion_ocr_pre.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"import os,glob\\n\",\n    \"import json\\n\",\n    \"from tqdm import tqdm\\n\",\n    \"from os.path import join as ospj\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"data_root = \\\"{your data root}/LAION-OCR\\\"\\n\",\n    \"image_root = ospj(data_root, \\\"image\\\")\\n\",\n    \"anno_root = ospj(data_root, \\\"annotation\\\")\\n\",\n    \"cache_root = ospj(data_root, \\\"cache\\\")\\n\",\n    \"os.makedirs(cache_root, exist_ok=True)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 3,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"8754781\"\n      ]\n     },\n     \"execution_count\": 3,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"url_txt = \\\"{your data root}/LAION-OCR/mario_laion_image_url/mario-laion-index-url.txt\\\"\\n\",\n    \"with open(url_txt, 'r') as fp:\\n\",\n    \"    res = fp.readlines()\\n\",\n    \"\\n\",\n    \"url_lst = []\\n\",\n    \"for r in res:\\n\",\n    \"    idx, url = r.split(\\\" \\\")\\n\",\n    \"    url = url[:-1]\\n\",\n    \"    ex_idx, in_idx = idx.split(\\\"_\\\")\\n\",\n    \"    if int(ex_idx) >= 50000: continue\\n\",\n    \"    url_lst.append({\\\"ex_idx\\\": ex_idx, \\\"in_idx\\\": in_idx, \\\"url\\\": url})\\n\",\n    \"len(url_lst)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 4,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"[{'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000012',\\n\",\n       \"  'url': 'https://www.rockfordsystems.com/wp-content/uploads/2015/04/kst194-p.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000061',\\n\",\n       \"  'url': 'https://s3.reutersmedia.net/resources/r/?m=02&d=20161208&t=2&i=1164715083&w=644&fh=&fw=&ll=&pl=&sq=&r=LYNXMPECB70YK'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000108',\\n\",\n       \"  'url': 'https://images.puma.net/images/907235/02/bv/fnd/EEA/w/288/h/288/'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000146',\\n\",\n       \"  'url': 'http://www.slicingupeyeballs.com/wp-content/uploads/2009/05/stoneroses452.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000199',\\n\",\n       \"  'url': 'https://amheath.com/wp-content/uploads/2016/12/stranger.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000214',\\n\",\n       \"  'url': 'https://www.musicalweb.nl/wp-content/uploads/2020/05/singalong_disney.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000247',\\n\",\n       \"  'url': 'http://rlv.zcache.ca/the_coolest_people_are_from_maine_cards-r3dc62ebdf5334ecb909eb464787226c8_xvuat_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000255',\\n\",\n       \"  'url': 'https://i.pinimg.com/736x/eb/2e/f4/eb2ef48889d7ccd2c51b914a8e4cb7d5--alphabet-design-alphabet-letters.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000278',\\n\",\n       \"  'url': 'http://rlv.zcache.com.au/keep_calm_and_listen_to_the_great_egrets_poster-r65983dd96acd4ca8a7c3fcfe9f761802_wvu_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000352',\\n\",\n       \"  'url': 'https://i1.wp.com/techget.net/wp-content/uploads/2013/04/Best-Apps-for-Movie-lovers.png?fit=560%2C315&'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000410',\\n\",\n       \"  'url': 'http://img.wfrcdn.com/lf/49/hash/18584/7768691/1/1/1.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000599',\\n\",\n       \"  'url': 'https://dynamic.brandcrowd.com/asset/logo/ca14c463-843d-4df5-9b7b-40dddadef740/logo?v=4'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000632',\\n\",\n       \"  'url': 'https://d3v4qu4rwgk1m7.cloudfront.net/wp-content/uploads/2019/06/24100253/Car-Wash-Kits.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000731',\\n\",\n       \"  'url': 'http://img1.imagesbn.com/p/2940011445422_p0_v2_s260x420.JPG'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000762',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/1sfyKP83vBY/0.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000791',\\n\",\n       \"  'url': 'https://img5274.weyesimg.com/uploads/y91cvhn0.allweyes.com/images/928d71a9b0a1bfc2f633671c010d28a1.jpg?imageView2/2/w/1920/q/75'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000868',\\n\",\n       \"  'url': 'http://rlv.zcache.ca/appreciate_iphone_case_iphone_4_case-r055f4e6687ea4ed2b4e3563faacab0d4_a4643_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000985',\\n\",\n       \"  'url': 'https://3.bp.blogspot.com/-6rPl3MUmfDw/ViD57JQ9UHI/AAAAAAAAAEc/8sQaECCwOvc/s1600/Atlas%2BAnatomy%2B3d%2Bedit.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000000994',\\n\",\n       \"  'url': 'https://us.123rf.com/450wm/teamplayfor/teamplayfor1701/teamplayfor170100022/69327415-stock-vector-vector-decorate-cakes-with-cream-from-pastry-bag-isolated-illustration-on-white-backgroung-kitchenwa.jpg?ver=6'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001019',\\n\",\n       \"  'url': 'http://cdn.rekkerd.org/img/201503/famousaudio_atmosphericpianothemes3.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001043',\\n\",\n       \"  'url': 'https://etsytelemall.com/uploads/169f08c23dc9da2c8df7cd920ee286e0.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001091',\\n\",\n       \"  'url': 'http://rlv.zcache.ca/trick_or_treat_spider_print-r3328ccd5fa2147c0a2537824815caecc_6azo_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001202',\\n\",\n       \"  'url': 'https://timtirelli.files.wordpress.com/2015/01/rock-hall-night-bluejpg.jpg?w=490&h=310'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001210',\\n\",\n       \"  'url': 'http://2.bp.blogspot.com/-N9nHT7V1J_A/UDqYxlV_q2I/AAAAAAAABXQ/HBNjQOS17L4/s1600/a%2Bblog%2Bby%2Bmischelle%2Bheader.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001231',\\n\",\n       \"  'url': 'https://capemarkets.co.za/wp-content/uploads/2019/06/Winter-Wonderland-Logo-300x300.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001252',\\n\",\n       \"  'url': 'http://images.tvfanatic.com/iu/s--HtSeWKj0--/t_teaser_wide/f_autofl_lossyq_75/v1414435450/attachment/the-walking-dead-rt-depreciated.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001277',\\n\",\n       \"  'url': 'http://clould.ohcosplay.com/images/product/acc7895.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001316',\\n\",\n       \"  'url': 'https://www.c4dt.org/wp-content/uploads/2019/10/News_CPI_slogan-1.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001396',\\n\",\n       \"  'url': 'http://i00.i.aliimg.com/wsphoto/v1/1345135562_1/AA-13-multiple-Asdrubal-Cabrera-jersey-Indians-new-white-gray-navy-ivory-authentic-jersey.jpg_350x350.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001432',\\n\",\n       \"  'url': 'https://www.anikama.co.il/content/images/thumbs/0005499_im-not-getting-any-younger-magnet_600.jpeg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001589',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.net/image-server/v1/compositions/T635A2PA1289PT17X26Y58D135375903S72/views/1width=300height=300appearanceId=2backgroundColor=E8E8E8/not-without-my-fatbike-maenner-bio-t-shirt.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001613',\\n\",\n       \"  'url': 'https://i2.cdn.hhv.de/catalog/475x475/00436/436572.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001675',\\n\",\n       \"  'url': 'https://elgurudelbasket.com/wp-content/uploads/2015/07/as17_new_orleans-e1511020319987.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001682',\\n\",\n       \"  'url': 'http://www.rapbasement.com/wp-content/uploads/2015/01/spenzo-dripping-in-gold-feat-lep-bogus-boys-300x300.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001703',\\n\",\n       \"  'url': 'https://ctl.s6img.com/society6/img/hFwzz_bCCX1PNjeEBKa1071X3vs/h_264w_264/prints/~artwork/s6-original-art-uploads/society6/uploads/misc/b6309341286b42b7afe25334ecd88968/~~/you-did-not-wake-up-today-to-be-mediocre1301106-prints.jpg?wait=0&attempt=0'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001774',\\n\",\n       \"  'url': 'http://files1.comics.org//img/gcd/covers_by_id/203/w400/203539.jpg?-4555063407778287876'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001816',\\n\",\n       \"  'url': 'https://www.davenportmachine.com/wp-content/uploads/multi-spindle-automatic-lathe.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001850',\\n\",\n       \"  'url': 'https://www.monsterbacklinks.com/pics/000/153/279/2bfeec8853935db7688c479e7d9cfd21.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001860',\\n\",\n       \"  'url': 'http://republicjewelry.com/images/upperdeck_logo2.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001886',\\n\",\n       \"  'url': 'https://guide.alibaba.com/image/i1/excellent-shellfish-love-baby-wipes-baby-wipes-ass-dedicated-small-packet-of-skin-cleaning-wipes-8-bags-free-shipping-wholesale-25-pcs/TB19CkiLVXXXXaiaXXXXXXXXXXX_!!0-item_pic.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001889',\\n\",\n       \"  'url': 'https://www.acupunctureworld.com/out/pictures/generated/product/thumb/375_375_90/points-series@2x.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001908',\\n\",\n       \"  'url': 'https://healthyhomecleaning.websitehabitat.com/wp-content/uploads/sites/49/2015/05/which-norwex-dryer-balls-are-best.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001920',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0505/0407/3414/products/100-pioneering-women-book-art-architecture-books-house-home-product-type-select-pricing-0-3000-ikkadukka-store-ikka-dukka-the-eclectic-online_947_740x.jpg?v=1602935507'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001923',\\n\",\n       \"  'url': 'https://www.slantmagazine.com/assets/house/5941/books_seenowthen__article-prose-260x.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001931',\\n\",\n       \"  'url': 'https://bcassetcdn.com/asset/logo/f016a010-62c1-4105-b0af-f59735620074/logo?v=4&text=Logo+Text+Here'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000001994',\\n\",\n       \"  'url': 'http://www.minimeandluxury.co.uk/wp-content/uploads/2019/04/Birdland-400x400.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002015',\\n\",\n       \"  'url': 'https://ssl.c.photoshelter.com/img-get2/I0000gnsMogy4NUM/fit=1000x750/20110623-0192.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002055',\\n\",\n       \"  'url': 'https://i2.wp.com/thepinterestedparent.com/wp-content/uploads/2017/07/ftgj-550x1024.jpg?resize=257%2C478'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002061',\\n\",\n       \"  'url': 'http://wwwcache.wral.com/asset/lifestyles/goaskmom/2012/11/21/11802796/craftymomturkeybaster-347x300.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002196',\\n\",\n       \"  'url': 'https://static1.bigstockphoto.com/thumbs/2/3/1/large2/132530996.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002307',\\n\",\n       \"  'url': 'https://1.bp.blogspot.com/-IzADoO3QH50/X9FHiyLmd5I/AAAAAAAAAG4/8uYW8olwIYcW78LkjqBEgi7JuaaGIY_agCLcBGAsYHQ/w680/o%2Bpapel%2Bdo%2Bmarketing%2Bdigital%2Bpara%2Badvogados.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002316',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0076/9258/2997/files/ABP_-_About_Us_51fbde1d-d702-4604-b637-a22a539ec493_grande.jpg?v=1559323856'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002386',\\n\",\n       \"  'url': 'https://resources.tidal.com/images/65e8862c/e460/41f0/981e/b25fb974d537/640x640.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002410',\\n\",\n       \"  'url': 'https://direct.rhapsody.com/imageserver/images/alb.204079853/500x500.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002426',\\n\",\n       \"  'url': 'https://www.selon-l.fr/wp-content/uploads/2019/04/banniere-wp-bx-events.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002433',\\n\",\n       \"  'url': 'http://upperdeckblog.com/wp-content/uploads/2013/04/Doc-Jacobs-Event-Upper-Deck-Operation-Gratitude-Tommy-Lasorda-Talking-to-Doc.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002466',\\n\",\n       \"  'url': 'https://i0.wp.com/www.silkejager.com/wp-content/uploads/2018/06/BronzeAmbassadorJourney.jpg?fit=300%2C300&ssl=1'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002530',\\n\",\n       \"  'url': 'http://az721511.vo.msecnd.net/images/544/2724104.JPG?636166319456700257'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002593',\\n\",\n       \"  'url': 'https://3.bp.blogspot.com/-h9WhTMJYFZ8/UMgIU8ZJoOI/AAAAAAAAACA/kOX1Ol6n1rs/s320/AIRTEL_F_V_RGB_3D.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002624',\\n\",\n       \"  'url': 'https://us.123rf.com/450wm/lmv/lmv1302/lmv130200006/17989339-restaurant-menu-design.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002667',\\n\",\n       \"  'url': 'https://www.iapa.org/wp-content/uploads/2019/02/Webp.net-resizeimage-2-e1552245032578-370x370.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002740',\\n\",\n       \"  'url': 'https://thumb7.shutterstock.com/image-photo/stock-vector-beauty-hair-salon-logo-450w-401606989.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002919',\\n\",\n       \"  'url': 'https://images.edealer.ca/18/71297032.jpeg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002935',\\n\",\n       \"  'url': 'https://i1.wp.com/blognife.com/wp-content/uploads/2017/05/fan-1.png?fit=1024%2C512'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002946',\\n\",\n       \"  'url': 'https://syamsulrijal.com/wp-content/uploads/2020/04/header-logo1.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002963',\\n\",\n       \"  'url': 'http://rlv.zcache.ca/holiday_recipe_binder_2_size-r62214748d53c48e7a08f16f245cd4baa_xz8lg_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002981',\\n\",\n       \"  'url': 'https://thetalononline.com/wp-content/uploads/2019/10/theatrecollective-900x600.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002983',\\n\",\n       \"  'url': 'https://lh4.ggpht.com/vSQLNnu5TCx-YLLW-udrS2Q2bYGa1MlEq3PprqWQS3x4hYK1cX7yPls-i619l6aF_DxZ=h900'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000002989',\\n\",\n       \"  'url': 'https://itswritenow.com/wp-content/uploads/2018/02/template_358_Alexandra_John.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003025',\\n\",\n       \"  'url': 'http://d202m5krfqbpi5.cloudfront.net/books/1348717970l/1342556.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003058',\\n\",\n       \"  'url': 'http://thephotobrigade.com/wp-content/uploads/2013/02/Photographers_Outlook_on_2013_2.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003087',\\n\",\n       \"  'url': 'https://top10cinema.com/dataimages/29548-a.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003119',\\n\",\n       \"  'url': 'https://cdn.waterstones.com/bookjackets/large/9780/5712/9780571284184.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003172',\\n\",\n       \"  'url': 'http://a.mktgcdn.com/p/o0iwgIPT4C22tIJHrYXOhtZCqklYALxoZSX50aDxiro/280x280.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003264',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/U-9zhay1_hM/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003314',\\n\",\n       \"  'url': 'https://media.gettyimages.com/vectors/music-elements-vector-id469890499?s=612x612'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003333',\\n\",\n       \"  'url': 'https://rlv.zcache.com/first_coffee_then_tambura_music_lover_button-r092976bd1ff04826b2a6cfd00908cd11_k94rk_500.jpg?rlvnet=1'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003344',\\n\",\n       \"  'url': 'https://64.media.tumblr.com/tumblr_m6krabL2Hv1qz5q5oo1_500.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003508',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/2835/0496/products/Screen_Shot_2019-09-26_at_3.52.05_PM_1024x1024@2x.png?v=1571827904'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003557',\\n\",\n       \"  'url': 'https://shafferoffshoresolutions.com/wp-content/uploads/logo-blue-working-copy.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003604',\\n\",\n       \"  'url': 'https://www.makingmusicfun.net/images/thumbs/scott-joplin-word-search-worksheet.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003621',\\n\",\n       \"  'url': 'https://i2.wp.com/invisioncommunity.co.uk/wp-content/uploads/2021/05/SEGA-Announce-Lost-Judgment.jpg?resize=640%2C450&ssl=1'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003686',\\n\",\n       \"  'url': 'https://s3.amazonaws.com/lollipuff/media/blog/383/authentic-prada-hardware-logo-authentication.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003740',\\n\",\n       \"  'url': 'https://ibizafilmoffice.com/wp-content/uploads/2020/01/the-story-of-plastic-1024x576.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003771',\\n\",\n       \"  'url': 'https://printablehappybirthdaycards.com/wp-content/uploads/2019/03/For-Wife-Printable-Happy-Birthday-Cards-825x510.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003778',\\n\",\n       \"  'url': 'https://media.moddb.com/cache/images/downloads/1/62/61287/thumb_620x2000/tttt.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003813',\\n\",\n       \"  'url': 'https://i0.wp.com/finderskeeperscrafting.com/wp-content/uploads/2016/12/pr_ts_ma.jpg?fit=300%2C300'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003843',\\n\",\n       \"  'url': 'http://barsuk.com.mx/wp-content/uploads/2016/01/samba-maya-Featured.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003890',\\n\",\n       \"  'url': 'https://i1.wp.com/flaviogarcia.es/vlog/wp-content/uploads/2016/01/tendencias-seo-para-2016.jpg?resize=800%2C445&amp;ssl=1'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000003967',\\n\",\n       \"  'url': 'https://static.wixstatic.com/media/264a63_9b686aa038fd8e13286a3845a02a5783.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004069',\\n\",\n       \"  'url': 'https://homesteadsurvivalsite.com/wp-content/uploads/15-ways-to-improve-your-garden-soil-pin-1.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004070',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/YMAzj5T0tUs/0.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004112',\\n\",\n       \"  'url': 'https://www.dft-valves.com/img/Adobe-Spark-2-500x281.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004139',\\n\",\n       \"  'url': 'http://cdn.pastemagazine.com/www/articles/2014/03/04/PasteSXSW_InteractiveLead.jpg?635300843171398639'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004158',\\n\",\n       \"  'url': 'https://i.ibb.co/QDKjzjy/Property-Management-1-65.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004343',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/18ffCe-0fIo/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004372',\\n\",\n       \"  'url': 'https://eclecticmomsense.com/wp-content/uploads/2015/10/frankenstein-cupcakes-678x1024.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004375',\\n\",\n       \"  'url': 'https://images.sftcdn.net/images/t_optimizedf_auto/p/97bc217b-e1f1-4f32-a877-6229c529c7ea/291508945/rise-of-the-tomb-raider-20-year-celebration-ps-vr-ps4-logo.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004399',\\n\",\n       \"  'url': 'https://i.ebayimg.com/images/g/Te8AAOSwa1ZcHpMP/s-l500.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004468',\\n\",\n       \"  'url': 'http://images.comiccollectorlive.com/covers/c49/c4997944-611b-4c53-9c47-0db0a2897874.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004476',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/51635CFGPSL.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004488',\\n\",\n       \"  'url': 'https://www.hanaflorists.com/images/zoom_julias-designer-choices-17072792216.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004519',\\n\",\n       \"  'url': 'https://i1.wp.com/thetrademarkninja.com/wp-content/uploads/2017/07/T-Mobile-IOT-Trademark-Applications.png?resize=380%2C380'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004636',\\n\",\n       \"  'url': 'https://miriamrune.co.uk/content/images/2015/05/novel-ideas-blog-logo.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004644',\\n\",\n       \"  'url': 'http://www.game-fort.com/_nw/32/92027781.jpeg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004729',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/56089dfce4b0fb7874bd4c50/1516294695428-PLHOZGEPA8T6DZLD5K31/FINLA_Revision_IQ_LOGO_DESIGN-2.jpg?content-type=image%2Fjpeg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004792',\\n\",\n       \"  'url': 'https://4.bp.blogspot.com/-QMboo-_XrI0/V28PKYgX4uI/AAAAAAAAUhw/Nkr57HEEDxwfEsIlNI9UQ9xCWYdFhs9CQCLcB/s400/Slide1.PNG'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004803',\\n\",\n       \"  'url': 'https://images.lookhuman.com/render/standard/SoRDxb8l07DddiNxBNerv8VtK4rK45KA/6710-heathered_black-z1-t-yoga-with-the-omies.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004868',\\n\",\n       \"  'url': 'https://www.peche-leurre-evolution.com/gfx_folders/1301839227267.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000004903',\\n\",\n       \"  'url': 'http://im.rediff.com/money/2013/apr/18biggest-companies4.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005089',\\n\",\n       \"  'url': 'https://i0.wp.com/wayofdharma.com/wp-content/uploads/2018/08/caste-and-hinduism.jpg?resize=665%2C365&'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005105',\\n\",\n       \"  'url': 'https://www.mauvais-genres.com/17147-large_default/day-after-french-movie-poster-47x63-81-day-after.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005127',\\n\",\n       \"  'url': 'http://1.bp.blogspot.com/-RdNlgePPMsk/UQO56ttd27I/AAAAAAAAGyU/5VeHmid3SvA/s320/pokemon-platine.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005148',\\n\",\n       \"  'url': 'http://drh2.img.digitalriver.com/DRHM/Storefront/Company/ubi/images/hero/Rabbids_BlackTshirt_Hero_FR.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005206',\\n\",\n       \"  'url': 'https://bokklubben.no/servlet/VisBildeServlet?produktId=6996035&amp;width=95'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005381',\\n\",\n       \"  'url': 'http://indiecurrent.com/wp-content/uploads/2012/12/Favourite-Canadian-Music-Videos-of-2012-Ride-The-Tempo.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005394',\\n\",\n       \"  'url': 'http://cascadiasfault.com/wp-content/uploads/2020/06/free-alphabet-coloring-pictures-disney-pages-printable-for.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005398',\\n\",\n       \"  'url': 'https://static.onleihe.de/images/bookwire_inter/20210127/9783863911546/im9783863911546s.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005426',\\n\",\n       \"  'url': 'http://3o7tpx32lt6v2lcovs4a53lb.wpengine.netdna-cdn.com/wp-content/uploads/2013/08/Geronimo-Shot-Bar-smaller--500x303.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005489',\\n\",\n       \"  'url': 'http://a1.phobos.apple.com/us/r1000/020/Purple/53/f6/b4/mzl.jxdsthto.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005490',\\n\",\n       \"  'url': 'https://i.pinimg.com/736x/f2/6a/57/f26a57a7472692e5d3b47f47f6cb852e--the-breakfast-club-breakfast-club-quotes.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005521',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0204/7208/t/26/assets/logo_accessible360.png?62012'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005536',\\n\",\n       \"  'url': 'http://cdn.pastemagazine.com/www/articles/2011/03/21/The-Hobbit-book-cover-square.jpg?635336299397505126'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005676',\\n\",\n       \"  'url': 'https://www.miletasigns.co.uk/media/catalog/product/cache/1/thumbnail/300x400/9df78eab33525d08d6e5fb8d27136e95/c/p/cp049_1.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005705',\\n\",\n       \"  'url': 'http://1.bp.blogspot.com/-Ivmfcqtq1Kc/UbCpQ_7uU7I/AAAAAAAACIg/SCtecu39WDQ/s1600/96089427449264466617.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005816',\\n\",\n       \"  'url': 'https://s3.amazonaws.com/gigsalad_media/t/tumbao_chicago/5a7b3321ea1b3_300_sq'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005845',\\n\",\n       \"  'url': 'https://www.inspireflyer.com/img/build-with-skyscanner-badge-footer.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005883',\\n\",\n       \"  'url': 'https://www.com-sub.biz/uploads/images/full/025930bc60c28c918fc9c5fe5645a3b5.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000005941',\\n\",\n       \"  'url': 'http://www.quickmeme.com/img/75/751c3ffd668537a299b5f92789aa13ac2f127516425e2426b72c53d72648c698.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006194',\\n\",\n       \"  'url': 'https://static.uk.groupon-content.net/app/00/00/default0000.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006312',\\n\",\n       \"  'url': 'https://lh3.googleusercontent.com/-YNPcanrd01s/WYCX2nI091I/AAAAAAAAFIc/in9jbS4NS7A6uRFRIqn5PvSbz30-wW1ZgCHMYCw/sinnertitle_thumb%255B2%255D?imgmax=800'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006317',\\n\",\n       \"  'url': 'https://cdn.prestosports.com/action/cdn/logos/id/1oz6458b0o827r98.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006322',\\n\",\n       \"  'url': 'https://images.justlanded.com/classified_images/Ecuador_Pichincha_Quito/Servicios_Ordenadores-Internet/Agencias-De-Inbound-Marketing-Ecuador/photo/big_scaled_1736569_2650086.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006381',\\n\",\n       \"  'url': 'http://rlv.zcache.com.au/worlds_greatest_boss_mousepads-rde3e974765ec45b284e201d0fb7ebb33_x74vi_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006383',\\n\",\n       \"  'url': 'http://tse3.mm.bing.net/th?id=OIP.V8QL0RFK5cnF68JAuwnocQHaHa'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006396',\\n\",\n       \"  'url': 'https://d3fa68hw0m2vcc.cloudfront.net/9f9/178414816.jpeg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006399',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/dKLJaTMFjY4/0.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006444',\\n\",\n       \"  'url': 'http://a.espncdn.com/combiner/i?img=/espn360/images/showassets/ERJJ.jpg&w=640&h=360&20160201093651'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006559',\\n\",\n       \"  'url': 'https://p19cdn4static.sharpschool.com/UserFiles/Servers/Server_2434311/Image/26%20Jan%20TMSA%20Board%20Meeting%20Small.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006564',\\n\",\n       \"  'url': 'https://images.saymedia-content.com/.image/t_share/MTc0NjM5OTU4NDY3MDk0NTE4/repetition-and-the-living-dead-an-analysis-of-james-joyces-the-dead.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006671',\\n\",\n       \"  'url': 'https://seoheronews.com/adwords-clicks-620x0.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006697',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.net/image-server/v1/compositions/T631A1PA1280PT17X8Y0D130215843S32/views/1width=300height=300appearanceId=1backgroundColor=E8E8E8/leaf-me-women-s-t-shirt.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006754',\\n\",\n       \"  'url': 'https://www.netoingenieria.com/wp-content/uploads/2018/01/congreso-multisectiorial-carreteas.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006838',\\n\",\n       \"  'url': 'https://i.pinimg.com/736x/4a/10/1d/4a101d10f6c776ac0a90f07ab4d3e7bd.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006852',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/51G+-ANR8LL._SL300_.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006899',\\n\",\n       \"  'url': 'https://www.schlossstrasse-koblenz.de/thumbnail.php?thumb=img/shops/logos/105.jpg&width=300&height=300'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006904',\\n\",\n       \"  'url': 'https://andreasnotebook.com/wp-content/uploads/2011/10/Viking-helmet-tutorial.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006917',\\n\",\n       \"  'url': 'http://az721511.vo.msecnd.net/images/130/651640.JPG?636237457983790927'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006941',\\n\",\n       \"  'url': 'http://canalauthenticgames.com.br/content/uploads/Prime-Authentic-Games-2017-Facebook-166-265x265.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000006981',\\n\",\n       \"  'url': 'https://static.wixstatic.com/media/45af5e_bdb9587dab814438a8d04dadcf2f17a7~mv2.png/v1/fit/w_500h_500q_90/45af5e_bdb9587dab814438a8d04dadcf2f17a7~mv2.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007039',\\n\",\n       \"  'url': 'https://www.platformhg.com/media/images/versions/img94joktmu71652.jpg?bev=1362'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007050',\\n\",\n       \"  'url': 'https://images.8tracks.com/cover/i/000/772/489/tumblr_mu65k05sDR1rjp4l1o1_500-8945.jpg?rect=00480480&q=98&fm=jpg&fit=max&w=320&h=320'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007076',\\n\",\n       \"  'url': 'https://s3.amazonaws.com/media.locally.net/logo-270x270/14590507_1361209293919802_4617975925624903492_n_2017-05-01-10-22-01.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007137',\\n\",\n       \"  'url': 'https://www.divyajanani.org/wp-content/uploads/2019/01/happy-mothers-day-coloring-pages-beautiful-happy-mothers-day-coloring-pages-heart-coloring-pages-of-happy-mothers-day-coloring-pages.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007171',\\n\",\n       \"  'url': 'http://4.bp.blogspot.com/-015aW2JD4SI/U5tAQXMp6fI/AAAAAAAAPgU/1GsiWnzAJ2k/s1600/capa.jpg%22%22%22%22%22%22%22%22%22%22%22%2222%22%22%22%22%22%22%22%22%22'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007255',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/1567/1365/products/thumb_fc7cca25-33ce-4d4a-8b68-bf6cd2f13e6e_480x480.jpg?v=1535551974'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007258',\\n\",\n       \"  'url': 'https://www.eden.co.uk/images/300/9781631463228.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007260',\\n\",\n       \"  'url': 'https://bestlakecountylawyer.com/wp-content/uploads/2019/05/Criminal-Defense-2019-620x380.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007264',\\n\",\n       \"  'url': 'https://www.techmelife.com/wp-content/uploads/2021/03/10-Ways-Social-Media-Helps-Your-SEO-Strategies-e1616572147271.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007321',\\n\",\n       \"  'url': 'https://cdn4.singleinterface.com/files/offer-images/80/home_page-3929_1521540985_SIBanner434x434compressor.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007404',\\n\",\n       \"  'url': 'http://cdn01.ru/files/users/images/ac/c8/acc89416fe58bf7e2d9eb8b1ba58cc42.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007471',\\n\",\n       \"  'url': 'http://i00.i.aliimg.com/wsphoto/v0/1316216120_1/Fast-Shipping-Multifunction-Robot-Vacuum-Cleaner-Big-Mop-Low-noise-Home-Aplicances.jpg_350x350.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007637',\\n\",\n       \"  'url': 'https://m.media-amazon.com/images/I/61PpqZ8ETqL._SL500_.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007656',\\n\",\n       \"  'url': 'http://static.kodajo.com/images/user/fileUp/6c5a0da6437719cfa5d028c7fd21535d.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007713',\\n\",\n       \"  'url': 'https://kcmix.com/wp-content/uploads/2019/11/Youth-Crime-What-Can-I-Do-380x285.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007854',\\n\",\n       \"  'url': 'http://d202m5krfqbpi5.cloudfront.net/books/1332982516l/13564669.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007859',\\n\",\n       \"  'url': 'https://d12swbtw719y4s.cloudfront.net/images/k5r8coRk/7RMlT0x3kJc80NGb9ERG/5BxK2cS4zj.jpeg?w=600'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007913',\\n\",\n       \"  'url': 'https://thisdelicioushouse.com/wp-content/uploads/2019/07/Untitled-185-683x1024.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007918',\\n\",\n       \"  'url': 'http://direct.rhapsody.com/imageserver/images/Alb.17950649/500x500.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007920',\\n\",\n       \"  'url': 'https://consumerqueen.com/wp-content/uploads/2020/03/FREE-ONLINE-FITNESS-STUDIOS.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000007967',\\n\",\n       \"  'url': 'https://i1.wp.com/sunuptosundown.net/wp-content/uploads/2018/10/ChocolateIce-CreamDay.png?resize=800%2C374&ssl=1'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008141',\\n\",\n       \"  'url': 'https://www.jasoncouponking.com/wp-content/uploads/2013/12/target.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008155',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0656/6139/products/OS_0013_Men_Olive_1024x1024.jpg?v=1488698479'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008182',\\n\",\n       \"  'url': 'http://images.slideplayer.com/5/1557963/slides/slide_1.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008196',\\n\",\n       \"  'url': 'https://static.wixstatic.com/media/nsplsh_6f775a4137377252416963~mv2_d_4909_3264_s_4_2.jpg/v1/fill/w_454h_333fp_0.50_0.50q_90/nsplsh_6f775a4137377252416963~mv2_d_4909_3264_s_4_2.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008232',\\n\",\n       \"  'url': 'https://i1.wp.com/conference.virtualreality.to/wp-content/uploads/2019/05/Cream_1x1_logo-500.png?fit=500%2C500&ssl=1'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008284',\\n\",\n       \"  'url': 'https://truthernews.files.wordpress.com/2014/02/nba-ncaa-bio-terror-warning.jpg?w=385&'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008295',\\n\",\n       \"  'url': 'http://speed90calgary.com/content/images/thumbs/0001286_samsung_300.jpeg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008322',\\n\",\n       \"  'url': 'https://fernyhillretreat.blog/wp-content/uploads/2019/02/FHR-blog-logo-no-blog.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008345',\\n\",\n       \"  'url': 'https://s3.us-west-2.amazonaws.com/ycbm.production.upload.files/ycbm/NWk5WMYLoHitQCCzphdr/images/logotestforbookingcalendar.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008378',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/q9sfOGXWcAA/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008398',\\n\",\n       \"  'url': 'https://fonolive.com/US/ca/vannuys/17857513/05041519f02b3dd8b6fa1037c854b4c9.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008412',\\n\",\n       \"  'url': 'https://bookstore.firststepspublishing.com/wp-content/uploads/2017/07/SurvivingHitler_th.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008455',\\n\",\n       \"  'url': 'https://i2.wp.com/tkcoleman.wpengine.com/wp-content/uploads/2014/05/follow-me-and-i-ll-follow-back-2.png?resize=257%2C300'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008511',\\n\",\n       \"  'url': 'http://rlv.zcache.com/peace_love_juggle_round_stickers-r68cd2e9a0bb247f99b9a988d6d8e0d21_v9waf_8byvr_512.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008560',\\n\",\n       \"  'url': 'https://photos.bandsintown.com/thumb/6262168.jpeg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008565',\\n\",\n       \"  'url': 'https://www.fashionsvp.com/wp-content/uploads/2018/10/svp-logo-social-media.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008594',\\n\",\n       \"  'url': 'http://i2.ytimg.com/vi/UX-TX0_P9f4/0.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008717',\\n\",\n       \"  'url': 'https://i2.wp.com/asempanews.com/wp-content/uploads/2019/02/Emmanuel-Boateng-has-completed-his-move-to-Dalian-Yifang.jpg?resize=682%2C402&amp;ssl=1'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008740',\\n\",\n       \"  'url': 'https://dshgames.ru/wp-content/uploads/2019/11/45178826.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008763',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/he_loves_me_more_than_videogames_round_sticker-r695b34ab74b040f2b989cb2987bdf6b0_v9waf_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008870',\\n\",\n       \"  'url': 'https://www.idplr.com/components/com_remository_files/file_image_12046/img_12046_01.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008883',\\n\",\n       \"  'url': 'https://matsuri.sc/sys/wp-content/uploads/2019/06/ma2k-600x491.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000008985',\\n\",\n       \"  'url': 'http://a4.mzstatic.com/us/r1000/071/Purple/d0/5f/6e/mzm.caxvphqx.png'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009070',\\n\",\n       \"  'url': 'https://knickoftime.net/wp-content/uploads/2016/08/autumn-stencil-give-thanks.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009122',\\n\",\n       \"  'url': 'https://images.financialexpress.com/2019/04/upsc-2.jpg?w=660&h=440&imflag=true'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009162',\\n\",\n       \"  'url': 'https://images.amain.com/images/large/asc/asc89278.jpg?width=200'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009228',\\n\",\n       \"  'url': 'https://static1.squarespace.com/static/559abcdae4b078c942e5c735/t/5d5d4f0c7d731300013b9232/1568920539487/?format=1500w'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009229',\\n\",\n       \"  'url': 'http://direct.rhapsody.com/imageserver/images/Alb.169582/500x500.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009306',\\n\",\n       \"  'url': 'http://3.bp.blogspot.com/-baNJ0IO7XYM/T-T98u8iTVI/AAAAAAAAGJ0/c3elxsuWRhQ/s400/559b.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009379',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/51sSN7pB9bL.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009507',\\n\",\n       \"  'url': 'https://us.123rf.com/450wm/pinnacleanimates/pinnacleanimates1211/pinnacleanimates121100044/16131281-vector-happy-diwali-greeting-illustration.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009627',\\n\",\n       \"  'url': 'https://images.lookhuman.com/render/standard/0842006469058676/iphone7sn-whi-z1-t-i-drink-haterade-all-day.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009708',\\n\",\n       \"  'url': 'http://clubjerseys.net/pic/Nike-Dolphins--2325-Xavien-Howard-Orange-Women-27s-Stitched-NFL-Limited-Rush-Jersey-4399-17297.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009727',\\n\",\n       \"  'url': 'https://secureservercdn.net/198.71.233.109/zz0.685.myftpupload.com/wp-content/uploads/2019/11/CVS-CAT-DIGITAL.jpg?time=1594677716'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009750',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/61Glg3X8kXL.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009813',\\n\",\n       \"  'url': 'https://static3.bigstockphoto.com/thumbs/1/4/1/large2/141030668.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009860',\\n\",\n       \"  'url': 'https://www.challengecoins.ca/wp-content/uploads/2019/05/Manitoba-Correctional-Services-Established-1871-316x316.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009915',\\n\",\n       \"  'url': 'https://s3.eu-central-1.amazonaws.com/kidsread-books/bossy-and-the-blue-elephant/thumbs/1.jpg'},\\n\",\n       \" {'ex_idx': '00000',\\n\",\n       \"  'in_idx': '000009990',\\n\",\n       \"  'url': 'http://pic.rutube.ru/video/c8/02/c8026a8d3720e3c779ad2c2f5cdd5b7a.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010044',\\n\",\n       \"  'url': 'https://www.tamilkavithaihal.com/uploads/kadhal-Kavithaigal/kadhal-kavithaigal-photos-idhayam-thudippathu-unakaga-mattum-meera-tamil-kavithai-photos-download.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010091',\\n\",\n       \"  'url': 'https://d7olld39l2hok.cloudfront.net/logo/4214275.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010187',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0243/7761/products/ScreenShot2020-06-03at4.50.27PM_380x@2x.png?v=1591226983'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010232',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/563cb327e4b03fd23ffc5941/1511024795146-TVJKAJM1ME4G58MYGOXE/Pleasure%2BSnowboard%2BMagazin.jpeg?content-type=image%2Fjpeg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010234',\\n\",\n       \"  'url': 'https://storage.googleapis.com/hipstamp/p/ea887acd95042517885d324fcac9254c-300.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010238',\\n\",\n       \"  'url': 'http://brokenequipment.com/viewer.php?p6Y2ma6u2spDa6P2V8ze8Vz88egg8x88bJYD'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010277',\\n\",\n       \"  'url': 'https://i0.wp.com/raksbooks.com/wp-content/uploads/2020/04/false-value-audiobook-by-ben-aaronovitch.jpg?fit=300%2C300&ssl=1'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010296',\\n\",\n       \"  'url': 'http://images.slideplayer.com/2/761664/slides/slide_7.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010301',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/61lt37IzjiL._SL300_.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010303',\\n\",\n       \"  'url': 'https://1.bp.blogspot.com/-F6lQA6MJvlg/WaR_xzhpzuI/AAAAAAAAHpE/nMkQJQTNHCY49S2FQ3q0J2gSc_FXf35fgCLcBGAs/s320/blog%2Bbutton.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010330',\\n\",\n       \"  'url': 'http://edtech.wwcsd.net/wp-content/uploads/2017/06/technology-equipment.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010387',\\n\",\n       \"  'url': 'https://1.bp.blogspot.com/-Vei8-b26r_o/XtjQdMndLyI/AAAAAAAAs-E/X0js5p954gYZ4g1glJXfOMKwS4zrbSevQCLcBGAsYHQ/s1600/Verse-of-the-Day.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010420',\\n\",\n       \"  'url': 'https://www.investbypro.com/wp-content/uploads/2020/03/National-Savings-Certificates-min.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010438',\\n\",\n       \"  'url': 'https://s1.dmcdn.net/v/H0QPw1Nl0vN9hGfMZ/x720'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010464',\\n\",\n       \"  'url': 'http://rlv.zcache.ca/i_was_born_to_play_the_drums_mousepads-r0ea790343aa6467085c35e645eedd7f4_x74vi_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010482',\\n\",\n       \"  'url': 'https://i.pinimg.com/originals/1b/7b/e5/1b7be564de6e47f0e39731cdec34b166.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010514',\\n\",\n       \"  'url': 'https://dynamic.brandcrowd.com/asset/logo/69918387-908c-4f04-be1f-61f5d2bab958/logo?v=4&text=Logo+Text+Here'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010550',\\n\",\n       \"  'url': 'https://ss.shayanashop.com/oi/slider/Kaleidoskope/Kaleidoskope_580x319pix_EN.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010634',\\n\",\n       \"  'url': 'https://images.bwbcovers.com/006/The-Art-of-Loving-Fromm-Erich-9780060958282.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010677',\\n\",\n       \"  'url': 'https://images-na.ssl-images-amazon.com/images/S/cmx-images-prod/Item/735067/735067._SX312_QL80_TTD_.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010718',\\n\",\n       \"  'url': 'http://gumps.scene7.com/is/image/Gumps/168992_is?$PIP_Main$'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010748',\\n\",\n       \"  'url': 'https://pbcdn1.podbean.com/imglogo/image-logo/2577245/Bad_Reception_Large.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010799',\\n\",\n       \"  'url': 'https://ducttapeanddenim.com/wp-content/uploads/2018/04/Before-and-after-spring-green-jewelry-cabinet-DuctTapeAndDenim.com_-1024x1024-600x600.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010857',\\n\",\n       \"  'url': 'http://www.playingrockguitar.com/wp-content/uploads/2009/12/Dis_Pedal-300x266.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010929',\\n\",\n       \"  'url': 'http://4.bp.blogspot.com/-x51sJBXY_Ys/U7LkGTKuRrI/AAAAAAAAFPw/qgGCgMLx4e4/s300/summerfest.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010931',\\n\",\n       \"  'url': 'https://img.youtube.com/vi/Y4DoPk0JLvc/0.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010957',\\n\",\n       \"  'url': 'https://purvesinsurance.com/wp-content/uploads/2015/08/Fotolia_87325583_Subscription_Monthly_M-1024x682.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010962',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/Sh2A48Unh4s/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000010976',\\n\",\n       \"  'url': 'https://i0.wp.com/www.theartofcoachingvolleyball.com/wp-content/uploads/2016/06/Attacking-Comprehensive-Course.png?fit=264%2C264&amp;ssl=1'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011036',\\n\",\n       \"  'url': 'http://obscure-abhorrence.de/cover/21508trifixionthefirstandthelastcommandment120151208.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011053',\\n\",\n       \"  'url': 'http://img1.imagesbn.com/p/9780970696298_p0_v1_s260x420.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011155',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/JvFxJmGJaBY/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011171',\\n\",\n       \"  'url': 'https://artwork-cdn.7static.com/static/img/sleeveart/00/029/720/0002972006_350.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011209',\\n\",\n       \"  'url': 'http://www.acousticcentre.co.uk/user/products/thumbnails/NS%20WAV5%20Double%20Bass%20-%20Amber.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011324',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/1786/8373/products/small-lilac-hurts-so-good-womens-workout-tank-top-19157926728_195x195@2x.jpg?v=1549990604'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011348',\\n\",\n       \"  'url': 'https://s3.amazonaws.com/static.noisetrade.com/w/64249e7a-038a-4f68-bf9a-0896c7f703f7/good_investigations.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011411',\\n\",\n       \"  'url': 'https://i24.servimg.com/u/f24/15/34/85/80/anh6121.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011422',\\n\",\n       \"  'url': 'https://cdn11.bigcommerce.com/s-df4cz/images/stencil/500x659/products/6970/5095/SHAKA654.2-2__24741.1398390775.jpg?c=2'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011511',\\n\",\n       \"  'url': 'http://pick.cyberpe.org/forum/films/tv_show/The.Colony.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011544',\\n\",\n       \"  'url': 'http://ih0.redbubble.net/image.80441319.3160/pp370x410-pad420x460f8f8f8.u3.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011607',\\n\",\n       \"  'url': 'https://cdnfa.com/difwear/aee9/files/normal/3262067.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011650',\\n\",\n       \"  'url': 'https://i2.wp.com/www.musicarenagh.com/wp-content/uploads/2018/03/OMI-JASON-Victory-Over-SinProdby-Mr-Benchie-mp3-image.jpg?fit=1200%2C1200&'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011662',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/mshHomZlMOY/0.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011826',\\n\",\n       \"  'url': 'https://deliverlogic-cravedel.s3.amazonaws.com/logos/front/8483.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011870',\\n\",\n       \"  'url': 'http://4.bp.blogspot.com/-Ap4fzk4UVQo/Vd7nIIR_W1I/AAAAAAAADss/ZB1-L-sjWy0/s400/Download%2BFree%2BGame%2BTales%2Bfrom%2Bthe%2BBorderlands%2B%2528All%2BVersions%2529%2BUnlock%2BMulti-pack%2B%255BEpisodes%2B2-5%255D%2B100%2525%2BWorking%2Band%2BTested%2Bfor%2BIOS%2Band%2BAndroid.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000011998',\\n\",\n       \"  'url': 'https://www.hashtagbylily.com/wp-content/uploads/2018/04/What-to-do-in-Hong-Kong-part-2-330x330.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012023',\\n\",\n       \"  'url': 'http://novavideoz.com/wp-content/uploads/2018/04/Harmonize-ft-Diamond-Platnumz-%E2%80%93-Kwangwaru-500x400.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012082',\\n\",\n       \"  'url': 'https://mk0gamesnetentcxytko.kinstacdn.com/wp-content/uploads/2019/02/flowers-christmas.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012091',\\n\",\n       \"  'url': 'http://i.vimeocdn.com/portrait/2124563_300x300.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012141',\\n\",\n       \"  'url': 'https://s3.amazonaws.com/cms.ipressroom.com/256/files/20200/56967ac05e8eef6fa444ee12_Engineering+logo/Engineering+logo_s.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012237',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/54a43fb9e4b0d1cd06e51447/1422156942904-CJ5T32H1HZIEO1XJZ96A/habitation+conference+logo_ivey+media+group.jpg?format=1000w&content-type=image%2Fjpeg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012301',\\n\",\n       \"  'url': 'https://img0.etsystatic.com/203/0/11077597/il_340x270.1417321564_bles.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012348',\\n\",\n       \"  'url': 'http://messhelper.com/wp-content/uploads/12333/tmp-b2e676b2-a196-45b9-bdaf-6c09ff1222c6.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012399',\\n\",\n       \"  'url': 'https://static.es.groupon-content.net/app/00/00/default0000.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012461',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0044/3328/1135/products/image_3c54afbe-0199-436f-af5b-e792df2a836a_250x250@2x.jpg?v=1596859943'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012533',\\n\",\n       \"  'url': 'https://clipground.com/images/reptilium-clipart-9.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012724',\\n\",\n       \"  'url': 'http://kitelife.com/wp-content/uploads/2016/12/KiteKites_comad.png?pas=15877746031702240410'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012745',\\n\",\n       \"  'url': 'http://rlv.zcache.com/happy_birthday_teddy_bear_2nd_birthday_sticker-r64214bedd6ea45a7ad472d423858a4a3_v9waf_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012758',\\n\",\n       \"  'url': 'https://images.socialwelfare.library.vcu.edu/files/thumbnails/1f5f551dd68e86f9cfaa53c397cffe3a.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012790',\\n\",\n       \"  'url': 'http://conniechapman.com/wp-content/uploads/2016/07/slow-down-tune-in.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012839',\\n\",\n       \"  'url': 'https://irbarcelona.it/wp-content/uploads/2016/10/purchase-citypassbcn.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012925',\\n\",\n       \"  'url': 'https://di2ponv0v5otw.cloudfront.net/posts/2019/06/06/5cf958dcfe19c7f83506ee97/s_5cf9592cc953d822221f62c2.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012952',\\n\",\n       \"  'url': 'https://st.depositphotos.com/1013213/2954/v/380/depositphotos_29549137-stock-illustration-summer-travel-design.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000012974',\\n\",\n       \"  'url': 'https://milnersblog.files.wordpress.com/2018/05/solo-a-star-wars-story-dolby-amc-exclusive-film-poster-banner.jpg?w=672&amp;h=372&amp;crop=1'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013048',\\n\",\n       \"  'url': 'https://m.media-amazon.com/images/I/51tIQJNKc0L._SL500_.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013086',\\n\",\n       \"  'url': 'http://anchorstone.com/wp-content/uploads/2014/08/discovered.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013163',\\n\",\n       \"  'url': 'https://i.ebayimg.com/00/s/NTUxWDU2Mg==/z/4iUAAMXQs6FRQvBE/$T2eC16Z!yEE9s5jGJZWBRQvBEH+Tg~~6060_35.JPG?set_id=8800005007'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013168',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/Pxuju4laJHw/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013173',\\n\",\n       \"  'url': 'https://images-eu.ssl-images-amazon.com/images/I/511aRAabyPL.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013219',\\n\",\n       \"  'url': 'https://static1.bigstockphoto.com/thumbs/0/5/1/large2/150481730.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013226',\\n\",\n       \"  'url': 'https://media.karousell.com/media/photos/products/2017/08/14/100042_121789023_thumbnailW'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013237',\\n\",\n       \"  'url': 'http://betterbuilthomes.com.au/wp-content/uploads/2016/11/xmas-lightbox-669x272.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013248',\\n\",\n       \"  'url': 'https://media.karousell.com/media/photos/products/2016/07/14/102716_60199909_thumbnaily'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013397',\\n\",\n       \"  'url': 'https://i1.wp.com/bmariephoto.com/wp-content/uploads/2014/03/tuesday-top-3-1.jpg?resize=640%2C640'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013405',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.com/image-server/v1/compositions/111004097/views/1width=300height=300version=1473664654/enjay-t-shirt-green-men-s-premium-t-shirt.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013502',\\n\",\n       \"  'url': 'http://b.vimeocdn.com/ps/622/075/6220755_300.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013510',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/qgL-rvBRrpw/0.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013523',\\n\",\n       \"  'url': 'https://m.media-amazon.com/images/I/51sRrOp3TuL._SL320_.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013619',\\n\",\n       \"  'url': 'https://lyricstamizha.com/wp-content/uploads/2018/01/Yaar-Ivan-Song-Lyrics.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013671',\\n\",\n       \"  'url': 'https://direct.rhapsody.com/imageserver/images/Alb.259691142/500x500.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013761',\\n\",\n       \"  'url': 'https://thefullnester.com/wp-content/uploads/2018/09/CopingwithHomesicknessin-College-1-683x1024.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013775',\\n\",\n       \"  'url': 'http://freedesignresources.net/wp-content/uploads/2016/07/10-Free-Realistic-Landscape-Background-prev02.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013876',\\n\",\n       \"  'url': 'https://cdn.doyou.com/wp/2015/08/6-Alignment-Tips-for-Revolved-Triangle-Pose.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013887',\\n\",\n       \"  'url': 'https://static1.bigstockphoto.com/2/5/1/large2/152304320.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013889',\\n\",\n       \"  'url': 'http://savedbylovecreations.com/wp-content/uploads/2012/04/TinCans.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013939',\\n\",\n       \"  'url': 'https://www.sbdcnj.com/wp-content/uploads/SS_Logo_Clean_Blue.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013952',\\n\",\n       \"  'url': 'https://cfl-createforless.netdna-ssl.com/p-images/3/2015/0925/289687-3-1.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000013994',\\n\",\n       \"  'url': 'http://tse1.mm.bing.net/th?id=OIP.kaH_IuK8_d4Ej_-_iO8HmQHaGW'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014045',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/547e23cee4b078699e15e789/1441912932977-4084X18EP745WT6U1U5R/logo.jpg?format=1500w&content-type=image%2Fjpeg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014092',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/i_brake_for_horse_trainers_bumper_sticker-r3ac6fd3ceed54253bb08b492fb7aeaa9_v9wht_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014106',\\n\",\n       \"  'url': 'https://s-lite.qwant.com/thumbr/480x320/6/2/69a98eee41e36abcdf9d33c7c1253e58f8b2525699e694c13041cefedeeef1/s-l300.jpg?u=https%3A%2F%2Fi.ebayimg.com%2Fimages%2Fg%2FTg0AAOSw3Ppe5C9N%2Fs-l300.jpg&q=0&b=1&p=0&a=0'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014148',\\n\",\n       \"  'url': 'https://i1.ytimg.com/vi/Y9AHZwoOCY8/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014165',\\n\",\n       \"  'url': 'http://lh3.googleusercontent.com/PrDlHY9uJxbV6avABA968De_On7OrxzNde_NIpz81eIAPwWZaF6UsCQ-vq0zquIiYA=w300'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014197',\\n\",\n       \"  'url': 'https://central-prod.s3-us-west-2.amazonaws.com/files/events/logos/2214/banner/Logo_Corrida.jpg?1526998128'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014231',\\n\",\n       \"  'url': 'https://www.planetdance.com/Graphics/Product_Thumbnails/Medi_WB09.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014234',\\n\",\n       \"  'url': 'https://gamexguide.com/wp-content/uploads/2019/11/uC430zc6irJxqplknVELig-760x567.jpeg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014299',\\n\",\n       \"  'url': 'http://3.bp.blogspot.com/-uYlwGl-n790/UDrZtCgyaAI/AAAAAAAAABc/xEgCWXltdck/s1600/header.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014305',\\n\",\n       \"  'url': 'https://cdn2.hubspot.net/hub/147789/file-405510524-jpg/SSF_Color_Logo.jpg?t=1405012110500'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014423',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/1717/6009/products/beta-tools-mastercargotm-5-drawer-workbench-c57sd-workbench-beta-tools-5_large.jpg?v=1538022284'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014451',\\n\",\n       \"  'url': 'http://ep.yimg.com/ay/collegefanfare/tennessee-27-adidas-replica-football-jersey-orange-9.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014509',\\n\",\n       \"  'url': 'https://images.g2crowd.com/uploads/product/image/large_detail/large_detail_1515183774/schoolauction-net.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014543',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/B5lzt42Tb20/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014634',\\n\",\n       \"  'url': 'https://us.123rf.com/450wm/onirb/onirb1307/onirb130701947/21142535-3d-graphic-with-vintage-wifi-label-on-vintage-background.jpg?ver=6'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014682',\\n\",\n       \"  'url': 'https://www.pjfiala.com/wp-content/uploads/2018/01/Boxed-set-front-cover-505x800.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014733',\\n\",\n       \"  'url': 'https://apollo-singapore.akamaized.net:443/v1/files/gra8oewsopyl1-IN/image;s=272x0'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014744',\\n\",\n       \"  'url': 'https://thumbs.dreamstime.com/b/text-sign-showing-my-goals-conceptual-photo-goal-aim-strategy-determination-career-plan-objective-target-vision-written-notebo-114007591.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014764',\\n\",\n       \"  'url': 'https://animalgiftclub-static.myshopblocks.com/images/2019/03/contain/512x512/5f3e387ea533cd46681d4f74d5ed9834.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014826',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/2138/0103/products/RWYAGreyTee_450x450.jpg?v=1599673652'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014864',\\n\",\n       \"  'url': 'https://www.wooconn.sk/wp-content/uploads/2020/07/ikros-connector-362x362.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014869',\\n\",\n       \"  'url': 'https://audiobyray.com/wp-content/uploads/2018/10/Animas-Music-Record-Label-AudiobyRay-Listing.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014932',\\n\",\n       \"  'url': 'http://freevector.co/wp-content/uploads/2012/12/winthrop-eagles-9.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000014984',\\n\",\n       \"  'url': 'https://i.servimg.com/u/f19/20/13/02/96/frt10.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015004',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/59c90fb68dd041d078f5c847/1542315256617-GBLTQBBE4CI1487V51I4/raspberry-fact3.png?content-type=image%2Fpng'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015011',\\n\",\n       \"  'url': 'https://image.isu.pub/131218144804-b0191da45469b90009ae59b010761cff/jpg/page_1_thumb_large.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015110',\\n\",\n       \"  'url': 'https://dm0qx8t0i9gc9.cloudfront.net/thumbnails/image/rDtN98Qoishumwih/2013-schedule-calendar-shows-future-business-targets_zk6agVP__thumb.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015140',\\n\",\n       \"  'url': 'https://cdn3.volusion.com/mqpef.fzrft/v/vspfiles/photos/LS-KOO-1.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015206',\\n\",\n       \"  'url': 'https://www.dhresource.com/260x260s/f2-albu-g7-M01-D1-60-rBVaSlvrzDWARayaAAGqmGjmXy8633.jpg/ipl-shr-hair-removal-machine-most-popular.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015286',\\n\",\n       \"  'url': 'https://mir-s3-cdn-cf.behance.net/projects/404/e8e0df27146041.55810d5d31e40.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015436',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/BgjUPo8Iw5s/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015457',\\n\",\n       \"  'url': 'https://www.onlinecasinoreports.co.nz/images/crazywinners770x436.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015506',\\n\",\n       \"  'url': 'http://ecimages.kobobooks.com/Image.ashx?imageID=I7IGb83X-0mAS-0LDfVX5Q&amp;Type=Full'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015533',\\n\",\n       \"  'url': 'https://i3.cpcache.com/product/75375638/Camp_Hair_Dont_Care_Mug_300x300.jpg?height=300&amp;width=300&amp;qv=90&amp;side=back&amp;Filters=[%7B&quot;name&quot;:&quot;background&quot;&quot;value&quot;:&quot;ddddde&quot;&quot;sequence&quot;:2%7D]'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015555',\\n\",\n       \"  'url': 'https://www.cmirad.net/wp-content/uploads/awards-BOES-2020-upd.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015632',\\n\",\n       \"  'url': 'http://tse1.mm.bing.net/th?id=OIP.oyCsBr2oouFNT1mJcIIF-AHaHa'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015651',\\n\",\n       \"  'url': 'https://oi-punk.com/out/pictures/generated/product/1/380_340_70/plan.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015666',\\n\",\n       \"  'url': 'https://cloud.firebrandtech.com/api/v2/img/111/9781781314630/L'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015723',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/DHmFB5lSMR0/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015928',\\n\",\n       \"  'url': 'https://cache.mansion.com/shared/lobby/web/games/251x147/bjp.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000015941',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0049/1182/4965/products/9781938328923_250x250@2x.jpg?v=1550157343'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016024',\\n\",\n       \"  'url': 'http://direct.rhapsody.com/imageserver/images/Alb.53223107/500x500.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016050',\\n\",\n       \"  'url': 'https://londonjuniorknights.com/public/images/teams/1550/sponsors/THE_TRAP_DOC_Inc.JPG'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016169',\\n\",\n       \"  'url': 'http://cdn.pastemagazine.com/www/articles/2014/03/04/PasteSXSW_InteractiveLead.jpg?635301253355093818'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016181',\\n\",\n       \"  'url': 'http://www.wytheraceway.com/wp-content/uploads/2018/04/Feature-Winner.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016267',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0045/6608/9774/products/6E6A1189_GPSS_1500X_9e63f73f-8c3d-4bd0-a8e7-d3514beadd28_530x@2x.jpg?v=1606243775'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016307',\\n\",\n       \"  'url': 'http://img2.imagesbn.com/p/9781433601521_p0_v2_s260x420.JPG'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016330',\\n\",\n       \"  'url': 'http://exomotive.com/wp-content/uploads/2012/02/Rocket_Article-640x416.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016363',\\n\",\n       \"  'url': 'http://img.gawkerassets.com/img/17m8au650w2isjpg/original.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016393',\\n\",\n       \"  'url': 'https://st2.depositphotos.com/3740491/8097/v/380/depositphotos_80973478-stock-illustration-bakery-sticker-collection-with-hand.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016436',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0018/9340/0623/products/BlackForestCherryOnTop_512x.jpg?v=1608188301'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016444',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0006/2124/8569/articles/unnamed_242849a4-ebed-4d15-b777-d9036068bf85_2000x.jpg?v=1611589943'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016459',\\n\",\n       \"  'url': 'https://static3.bigstockphoto.com/8/4/1/large2/148921085.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016466',\\n\",\n       \"  'url': 'https://images.all-free-download.com/images/graphicthumb/summer_sale_banner_red_flame_decoration_6833658.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016472',\\n\",\n       \"  'url': 'https://phantanews.de/wp/wp-content/uploads/2015/06/e3-trailer-zu-mass-effect-androm.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016489',\\n\",\n       \"  'url': 'https://i0.wp.com/www.justcoloring.info/wp-content/uploads/2018/05/letter-c-is-for-cat-coloring-page-free-printable-letter-c-coloring-pages.png?ssl=1'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016494',\\n\",\n       \"  'url': 'http://i.ytimg.com/vi/p6ZxI5_A69M/0.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016507',\\n\",\n       \"  'url': 'http://www.quickmeme.com/img/57/57bdf230d100cb528d9d6bd3a76350a9ac1a0602791dd3471761d911b6262d10.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016516',\\n\",\n       \"  'url': 'https://d3wo5wojvuv7l.cloudfront.net/t_square_limited_320/images.spreaker.com/original/855219ba9e6089f767ddddebcbaea2f1.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016556',\\n\",\n       \"  'url': 'https://i.dlpng.com/static/png/256405_thumb.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016564',\\n\",\n       \"  'url': 'https://04b8419750745bc449e5-6ed6708ab01c785479edc27d79224746.ssl.cf1.rackcdn.com/thumbnails/WDCTG4GB9GJ209372/21d61f81e0123c8566d6980912ee1d9e.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016597',\\n\",\n       \"  'url': 'http://balloony.de/media/image/thumbnail/1005502_470x470.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016613',\\n\",\n       \"  'url': 'https://mk0muwucepum99ape3ia.kinstacdn.com/wp-content/uploads/2018/09/UniversityApplication-300x300.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016627',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/cXDULZBOb5U/0.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016708',\\n\",\n       \"  'url': 'https://i0.wp.com/apkonehack.com/wp-content/uploads/2019/03/Stardew-Valley-FULL-APK-ANDROID-DOWNLOAD.png?resize=720%2C349&ssl=1'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016723',\\n\",\n       \"  'url': 'https://s3.amazonaws.com/rapgenius/1360663053_cole-truly-yours.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016752',\\n\",\n       \"  'url': 'https://static2.bigstockphoto.com/thumbs/3/3/1/large2/133496717.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016814',\\n\",\n       \"  'url': 'https://media.karousell.com/media/photos/products/2016/09/11/083521_67299830_thumbnailH'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016820',\\n\",\n       \"  'url': 'https://mjobriendesign.com/wp-content/uploads/2015/01/logo-YEP-logo.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016868',\\n\",\n       \"  'url': 'https://www.hollywoodlanews.com/wp-content/uploads/2016/01/if-then-idina-menzel-pantages-theater-la-394x330.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016951',\\n\",\n       \"  'url': 'http://i1.wp.com/www.findingthewardrobe.com/wp-content/uploads/2016/06/Book-Review-The-Nest.png?resize=735%2C420'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000016991',\\n\",\n       \"  'url': 'http://rlv.zcache.com/funny_horse_junkie_gifts_iphone_case-rc5b5b6e612444b25832466f8eec3af5a_vx34d_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017032',\\n\",\n       \"  'url': 'https://c5.staticflickr.com/1/289/31847994852_3cc8b1ca0e.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017041',\\n\",\n       \"  'url': 'https://i0.wp.com/www.casinoplayersreport.com/wp-content/uploads/2016/08/gan_chicksaw.jpg?resize=300%2C275&ssl=1'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017084',\\n\",\n       \"  'url': 'https://i.harperapps.com/hcuk/covers/9780007347056/x350.JPG'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017214',\\n\",\n       \"  'url': 'http://weekendnotessydney.hubgarden.com/images/make_a_wish_sydney_comedy_night_2.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017261',\\n\",\n       \"  'url': 'https://img-1.fyyd.de/pd/layout/57701959833d11c42e0ef896fffe806a21053.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017271',\\n\",\n       \"  'url': 'https://kpbs.media.clients.ellingtoncms.com/img/features/2016/01/25/CaliforniaCounts_t640.png?a6ea3ebd4438a44b86d2e9c39ecf7613005fe067'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017303',\\n\",\n       \"  'url': 'http://s019.radikal.ru/i627/1603/a2/d227236fba8c.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017308',\\n\",\n       \"  'url': 'https://us.123rf.com/450wm/alexwhite/alexwhite1406/alexwhite140600377/29103811-new-year-2015-red-computer-icon-on-white-background.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017342',\\n\",\n       \"  'url': 'https://mcdn.elefant.ro/images/84/494784/a-short-history-of-the-jews-paperback_1_fullsize.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017392',\\n\",\n       \"  'url': 'https://is1-ssl.mzstatic.com/image/pf/us/r30/Purple3/v4/7c/2c/02/7c2c028e-d01a-cc3d-1dcf-31b70175050b/mzl.imgpgofn.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017414',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0268/9249/t/2/assets/slideshow_2.jpg?0'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017512',\\n\",\n       \"  'url': 'http://newyearsevenight.com/wp-content/uploads/2018/12/2019_3D_logo-2.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017548',\\n\",\n       \"  'url': 'http://img2.imagesbn.com/p/2940033207091_p0_v2_s260x420.JPG'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017591',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/1436/4514/t/4/assets/logo.png?1058400102851290921'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017597',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.net/image-server/v1/compositions/114665055/views/1width=300height=300appearanceId=2backgroundColor=E8E8E8version=1450277104/believe-in-yourself-caps-hats-winter-hat.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017697',\\n\",\n       \"  'url': 'https://www.steamyconcepts.com/wp-content/uploads/Steamy-Concepts-Carpet-Cleaning-Tab.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017704',\\n\",\n       \"  'url': 'http://snapwishes.com/uploads/Online-Love-Photo-Card-Maker-with-Name-300x420.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017717',\\n\",\n       \"  'url': 'https://bestwishes4birthday.com/wp-content/uploads/D/happy%20birthday%20cupcake%20sign%20;%2080067-Happy-Birthday-Cupcakes.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017806',\\n\",\n       \"  'url': 'https://machinehub.com/images/machine-hub-logo.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017824',\\n\",\n       \"  'url': 'http://media3.fcbarcelona.com/media/asset_publics/resources/000/089/901/size_640x360/2013-10-26_BARCELONA-MADRID_18.v1395220946.JPG'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017834',\\n\",\n       \"  'url': 'https://cdn11.bigcommerce.com/s-4pg4qzd524/images/stencil/original/products/23265/19204/stcl1585_with_my_whole_heart_for_my_whole_life_pi_red_barn_2_1__28733.1486194762.jpg?c=2'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017835',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/region_west_leverkusen_buttons-r2d66cc58690646b4900855f4b4458015_x7j18_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017911',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/Kx_g_QCtOZI/0.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017953',\\n\",\n       \"  'url': 'https://i0.wp.com/www.stuffwelike.com/wp-content/uploads/2009/07/troncomp_ref.jpg?fit=1200%2C642&ssl=1'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000017997',\\n\",\n       \"  'url': 'https://avatars.sched.co/1/ce/8875235/avatar.jpg?635'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018040',\\n\",\n       \"  'url': 'https://tse2.mm.bing.net/th?id=OIP.y2jIGh1-kSQNBom5Zo3IHgAAAA&amp;w=131&amp;h=131'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018058',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/genuine_biographer_tee_shirt-r6780d11d87b54678a4a8958af4dc6f31_vjfef_324.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018108',\\n\",\n       \"  'url': 'https://www.heartki.com/wp-content/uploads/2014/05/theuniverse_1.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018161',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/dont_feed_the_animals_round_stickers-r896cf30504234a2096d453821d98b3cb_v9wth_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018192',\\n\",\n       \"  'url': 'https://therewillbe.games/media/reviews/photos/thumbnail/640x640s/cf/94/b7/skulk-hollow-board-game-review-91-1574900473.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018228',\\n\",\n       \"  'url': 'https://davidlhudsonjr.com/wp-content/uploads/2018/08/Andrew-Goodman-FDN.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018369',\\n\",\n       \"  'url': 'https://pefnj.org/wp-content/uploads/2017/08/Screen-Shot-2017-02-06-at-11.07.29-AM-768x495-510x382.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018410',\\n\",\n       \"  'url': 'https://images-na.ssl-images-amazon.com/images/I/81kmu3Go09L._SX425_.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018616',\\n\",\n       \"  'url': 'https://d1gij4u04nulni.cloudfront.net/pics/property/339205702/1/IDX_1/v2//crop/540358'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018638',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/i_love_two_handed_tennis_pc_speakers-r5d4e74a526ff4c18b92700aa635341ff_vs8xj_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018699',\\n\",\n       \"  'url': 'https://api.historyit.com/iiif/2/5ae8f0730fe610.99848740/5bec37695453b2.58493308.jpg/full/!400400/0/default.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018717',\\n\",\n       \"  'url': 'http://womanofmanyroles.com/wp-content/uploads/2013/03/easterdrinksdon-1024x1024.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018757',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/585314452994cae130a1afbb/1482340264347-UHW5ZV298EGAZD3HM6AV/BCC+Logo+-+white.png?format=1500w&content-type=image%2Fpng'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018762',\\n\",\n       \"  'url': 'http://www.5thround.com/wp-content/uploads/2015/05/UFCPosterFightNight.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018767',\\n\",\n       \"  'url': 'https://www.digitalindiagov.in/wp-content/uploads/2019/01/unnamed-517x300.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018806',\\n\",\n       \"  'url': 'https://memegenerator.net/img/instances/43590803/morena.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018875',\\n\",\n       \"  'url': 'https://apshop.eu/880965-home_default/strongflex-rear-upper-link-inner-bush-sport-221552a.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018912',\\n\",\n       \"  'url': 'https://rlv.zcache.co.uk/speak_fluent_sarcasm_t_shirt-r6f7e547c4b474f4eb8b5cb2174d5b58e_65ye0_540.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018942',\\n\",\n       \"  'url': 'http://cybergrass.com/Images/BCGDivide.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000018945',\\n\",\n       \"  'url': 'http://i.qkme.me/3q3s9o.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019003',\\n\",\n       \"  'url': 'https://ktperformance.net/images/T140584741.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019016',\\n\",\n       \"  'url': 'https://realtor.remarketer.ca/Members/Images/brokerage/ash_ashestates.cal.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019054',\\n\",\n       \"  'url': 'https://joegilpin.hipcast.com/albumart/1000_1610946106.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019082',\\n\",\n       \"  'url': 'https://online-casino-codes.com/wp-content/uploads/2018/04/285-match-bonus-at-Adler-Online-Casino.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019090',\\n\",\n       \"  'url': 'https://image.tmdb.org/t/p/w342/B0WkSmfxuyK4jbPkfmTsKX5I4b.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019094',\\n\",\n       \"  'url': 'http://krui.fm/wordpress/wp-content/uploads/2015/01/Iowa-Basketball.jpeg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019161',\\n\",\n       \"  'url': 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Open_Access_Week_stencil_and_card_made_from_stencil_%28square%29.jpg/300px-Open_Access_Week_stencil_and_card_made_from_stencil_%28square%29.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019188',\\n\",\n       \"  'url': 'https://images.happycow.net/venues/1024/98/97/hcmp98976_375991.jpeg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019243',\\n\",\n       \"  'url': 'https://www.ligue-bretagne-surf.bzh/wp-content/uploads/2019/03/Logo-ESB-Surf-Club-e1553374195720.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019308',\\n\",\n       \"  'url': 'https://gadgetsmatrix.com/wp-content/uploads/thumbs_dir/ccdf-la5hrtu22zlid7p4fl3f2mbe9rfvy6dhxny67amf9k.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019317',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0955/6214/products/cocktails_and_dreams_neon_large.jpg?v=1522417184'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019504',\\n\",\n       \"  'url': 'http://img.bleacherreport.net/img/images/photos/002/196/975/liberty_vs_gardner_webb_crop_north.jpg?w=630&h=420&q=75'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019549',\\n\",\n       \"  'url': 'http://koothoomi-records.com/images/5074.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019560',\\n\",\n       \"  'url': 'https://media1.5amily.com/prev_cache/4e8ebcd42fe808208a1a1fa8ddf5939d055b465e.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019587',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/JBVpQzvrJ4w/maxresdefault.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019634',\\n\",\n       \"  'url': 'https://i2.wp.com/simpleathome.com/wp-content/uploads/2018/09/build-the-best-garden-soil.jpg?resize=700%2C1049'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019741',\\n\",\n       \"  'url': 'https://i.pinimg.com/736x/4b/a3/35/4ba33590e09a435343eec10614d4b785.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019743',\\n\",\n       \"  'url': 'https://hackster.imgix.net/uploads/attachments/304767/thumbnail_2pNvkPYiOk.jpg?auto=compress%2Cformat&amp;w=400&amp;h=300&amp;fit=min'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019762',\\n\",\n       \"  'url': 'https://img.youtube.com/vi/GMj0TheMEb8/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019802',\\n\",\n       \"  'url': 'https://www.leaksmith.com/wp-content/uploads/2019/04/gaf-certified.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019806',\\n\",\n       \"  'url': 'https://jp.slotsup.com/wp-content/uploads/logo-star-trek-red-alert-wms.png'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019824',\\n\",\n       \"  'url': 'https://secrethampsted.files.wordpress.com/2016/04/the_clash_visite_rock_londres.jpg?w=768'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019851',\\n\",\n       \"  'url': 'http://cdn.pastemagazine.com/www/articles/tomb%20raider%20square.jpg?635334990394962546'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019913',\\n\",\n       \"  'url': 'https://media.istockphoto.com/vectors/word-world-smile-day-vector-in-flat-style-vector-id1166140477?b=1&k=6&m=1166140477&s=170667a&h=MfQ4Z1SkDBjSCA6OPa5AhOFPoMaqm_YxjqaO8IqJQkQ='},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019940',\\n\",\n       \"  'url': 'https://us.123rf.com/450wm/reginast777/reginast7771802/reginast777180200059/96433124-verzameling-van-cute-cartoon-vlinders-ge%C3%83%C2%AFsoleerd-op-een-witte-achtergrond-.jpg?ver=6'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019955',\\n\",\n       \"  'url': 'https://solrio.org/wp-content/uploads/2017/09/Poseidon-Huntington-Beach-desalination-plant-770x300.jpg'},\\n\",\n       \" {'ex_idx': '00001',\\n\",\n       \"  'in_idx': '000019984',\\n\",\n       \"  'url': 'http://www.jvgs.net/dmw2/wp-content/uploads/2016/10/lm.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020005',\\n\",\n       \"  'url': 'https://i.pinimg.com/736x/91/a4/f6/91a4f6ef5f1149364f1299ff46d78a8a--seattle-washington-washington-state.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020047',\\n\",\n       \"  'url': 'http://d202m5krfqbpi5.cloudfront.net/books/1360616440l/15705572.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020053',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/made_in_1987_button-r16a5ff1ec8124ad8a764d37d2321c8b9_x7j3i_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020174',\\n\",\n       \"  'url': 'https://direct.rhapsody.com/imageserver/images/alb.56897913/500x500.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020181',\\n\",\n       \"  'url': 'https://tse1.mm.bing.net/th?id=OIP.dRWjySvbhkcObga2aQfbDgHaEK'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020209',\\n\",\n       \"  'url': 'http://lr-assets.storage.googleapis.com/_nielsen/400/9781780553955.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020290',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0225/2973/products/image_4ec88890-e6dc-44de-ac79-70242d014490_large.png?v=1579123646'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020357',\\n\",\n       \"  'url': 'http://www.thedesignsheppard.com/wp-content/uploads/2017/03/National-Tile-Week-2017-635x318.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020419',\\n\",\n       \"  'url': 'https://i.pinimg.com/736x/e1/d8/c8/e1d8c8adb56236cdf6c4c3b06b8f3e8a.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020423',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0820/7847/files/psn.jpg?v=1491706456'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020425',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0920/0236/t/1/assets/logo.png?10530566925433882303'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020463',\\n\",\n       \"  'url': 'https://www.thepalmbeaches.com/sites/default/master/files/styles/listing_thumb/public/mmg_lfef_images/wicked-delray-ghost-tours-42162-8bd91ec2352d0e062c9192d5a48baf2e.jpg?itok=4nbfrX8_'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020573',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0278/7289/files/Free-retro-vintage-fonts_Big-John-Slim-Joe_1024x1024.jpg?v=1495367650'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020581',\\n\",\n       \"  'url': 'https://content.sportslogos.net/news/2020/05/Screen-Shot-2020-05-23-at-1.43.51-AM.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020605',\\n\",\n       \"  'url': 'https://images1.americanlisted.com/nlarge/2015-kia-optima-ex-americanlisted_113934401.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020610',\\n\",\n       \"  'url': 'https://dynamic.activeactivities.com.au/resources.php?image=listings/0/8/7/163087/images/22062.jpg&amp;width=70&amp;height=70'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020653',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/i_love_passion_fruit_round_sticker-raf59190d45674a4fbd980db2cc84a27f_v9waf_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020654',\\n\",\n       \"  'url': 'http://southreggae.com/imagens/livros/bookmarleyjapanbarrylaz.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020673',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0168/3980/products/s-l1600_1308345b-a6e6-42fb-a3ce-6ffa00ae149e_large.jpg?v=1469192722'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020687',\\n\",\n       \"  'url': 'https://d2si46jc38oa3k.cloudfront.net/system/images/links/l900/81/81bcdf1811172ae7.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020791',\\n\",\n       \"  'url': 'http://cdn-s3-1.wanelo.com/product/image/1701690/full_size.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020872',\\n\",\n       \"  'url': 'https://images.megaphone.fm/PDGfrFNqU_oLaMG0y-2zeGPctSZdOl66TRibwKDb4lo/plain/s3://megaphone-prod/podcasts/32534192-614b-11e9-b18c-03a0ee6b52a1/image/HOF-Episode-27-JackJohnson-pt02-TheFight.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020884',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/2062/5273/products/9781612363042_b3c49114-6e7b-44e9-a564-360d8204de39_1024x1024.jpg?v=1550585009'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020898',\\n\",\n       \"  'url': 'https://i0.wp.com/emmareed.net/wp-content/uploads/2017/03/International-Womens-Day-1-e1488974203349.jpg?fit=737%2C363&ssl=1'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020930',\\n\",\n       \"  'url': 'https://perfectaquatics.co.uk/media/catalog/product/cache/1/small_image/295x295/9df78eab33525d08d6e5fb8d27136e95/m/i/mini_underwater_filter_200_lph.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000020994',\\n\",\n       \"  'url': 'http://i.bosscdn.com/product/1b/8e/b4/0b2bb6bab36f48cb5e7694a442.jpg@4e_360w_360h.src'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021004',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/2131/6377/products/GARM-GR29009-TS-MAN-WHT_400x.jpg?v=1578612760'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021032',\\n\",\n       \"  'url': \\\"http://images.publicjerseyz.ru/images/Billy/2017/2017-Super-Bowl/Youth's/Nike-Patriots-87-Rob-Gronkowski-Red-2017-Super-Bowl-LI-Champions-Youth-Game-Jersey.jpg\\\"},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021055',\\n\",\n       \"  'url': 'http://img2.imagesbn.com/p/9788420548463_p0_v1_s260x420.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021088',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/create_your_own_football_jersey_black_silver_table_card-red37722737c841ddb7848730b77ffd0c_i40g8_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021160',\\n\",\n       \"  'url': 'http://4.bp.blogspot.com/-LHYK_0HLnl4/TjoGrYZKAKI/AAAAAAAAABA/duKabJLUA68/s1600/Elec-City-banner-sized.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021163',\\n\",\n       \"  'url': 'https://img.youtube.com/vi/luRhYG9wKWE/0.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021173',\\n\",\n       \"  'url': 'http://i1.cpcache.com/product/186037057.jpg?height=150&width=150'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021338',\\n\",\n       \"  'url': 'https://i.scdn.co/image/ab67616d00001e02a1a8927c6ecc651e2682ed7e'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021340',\\n\",\n       \"  'url': 'http://cdn.resize.sparkplatform.com/key/1024x768/true/20170725202802606519000000-o.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021384',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0040/6316/6531/products/FREQUENCYBREAK-RECHARGE-QUADIBLEINTEGRITY-KARMICATTUNEMENT_300x300.png?v=1590794582'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021388',\\n\",\n       \"  'url': 'https://dudemom.com/wp-content/uploads/2013/11/holiday-gift-guide-for-boys.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021463',\\n\",\n       \"  'url': 'https://www.psdmarket.net/wp-content/uploads/2018/02/latin_party_flyer_psd_psdmarket_1-300x300.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021511',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/2164/1263/products/The_Gentle_Whisper_of_Living_Things_195x195@2x.jpg?v=1500925576'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021545',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0270/5759/products/newcastle-ale_large.jpg?v=1422039992'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021709',\\n\",\n       \"  'url': 'https://img.freepik.com/free-vector/welcome-summer-bright-poster-design-pink-flamingo_74855-484.jpg?size=626&ext=jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021711',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.com/image-server/v1/compositions/105907793/views/3width=300height=300appearanceId=1backgroundColor=E8E8E8version=1367139512/not-just-any-eejit-and-irish-one-st-patricks-day-bottles-mugs-travel-mug.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021720',\\n\",\n       \"  'url': 'http://images.football.co.uk/630x472/126b982a1b0b7dd39c9a28011cb75f3a.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021788',\\n\",\n       \"  'url': 'https://www.jasplastik.com/img/dodavatelia/_squares/Quality-manual.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021830',\\n\",\n       \"  'url': 'http://read.images.worldlibrary.org/img/Members.3/Audio_eBooks/Chapters/magnificent_ambersons_r/magnificent_ambersons_r.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021836',\\n\",\n       \"  'url': 'https://1.bp.blogspot.com/-lcDAdGH-50c/WQ5hoW9vzNI/AAAAAAAAiqs/KOnGKBRtf3sra6wDvzZ-Z-FBEJYtjyplQCK4B/s1600/The%2BOld%2BFair%2BLOGO%2B512X512.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021843',\\n\",\n       \"  'url': 'https://lcimages.s3.amazonaws.com/data/feat_img/4518/10329/1589227464.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000021872',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/59b7b1e0f9a61e3c5131a0c8/1523428157057-EKRR5P4QYPBWLACT2RY2/3c606d640e77a530e517a367318a5512_original.jpg?format=1000w&content-type=image%2Fjpeg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022005',\\n\",\n       \"  'url': 'http://lh4.googleusercontent.com/-uYEPltupeBw/AAAAAAAAAAI/AAAAAAAABFM/SsFVlx2TCVc/photo.jpg?sz=257'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022032',\\n\",\n       \"  'url': 'https://cheapdigitaldownload.com/wp-content/uploads/buy-nickelodeon-kart-racer-cd-key-compare-prices-2.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022053',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/i_love_advance_missouri_mousepad-r28259db956304dd08021299c2bbc91d9_x74vi_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022072',\\n\",\n       \"  'url': 'https://media.apnarm.net.au/media/images/2020/07/11/v3imagesbin94a8164c32cbef14101124c65891aa9c-4go8qjgh9w6svplonu2_t1880.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022172',\\n\",\n       \"  'url': 'https://blog.polleverywhere.com/wp-content/uploads/2019/08/womenstrivia.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022202',\\n\",\n       \"  'url': 'https://4.bp.blogspot.com/-UH66hGpfNmo/Ubh3jJ9OJ2I/AAAAAAAAYvs/L4BNJtNm-DU/s1600/Rapture%27s+Edge.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022256',\\n\",\n       \"  'url': 'http://aksdm.aldianews.com/sites/default/files/styles/article_image/public/articles/warreniowa.jpg?itok=44dJAzNI'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022306',\\n\",\n       \"  'url': 'https://i0.wp.com/stayfitmom.com/wp-content/uploads/2015/05/fullbodyworkout.jpg?resize=600%2C646'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022315',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/1d_q05HlGO0/maxresdefault.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022477',\\n\",\n       \"  'url': 'http://img.comc.com/i/Baseball/2013/Topps-Tribute---Certified-Autograph-Issue---Orange-Autographed/TA-MO2/Mike-Olt.jpg?id=9e0b3a15-bd0a-44b7-a188-f779187c468b&size=original'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022552',\\n\",\n       \"  'url': 'https://yearendparty.vn/wp-content/uploads/2015/07/year-end-party.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022561',\\n\",\n       \"  'url': 'https://s3.amazonaws.com/cdn.innovativelanguage.com/sns/em/2016/june/Learn+in+car.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022633',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/53bb42f4e4b0eb51cfda5752/1423714592510-AIB73FCO65DEHTJXNRUN/MOBLogo?content-type=image%2Fjpeg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022635',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.com/image-server/v1/compositions/1013554421/views/1width=300height=300version=1478482171/i-don-t-give-a-damn-funny-graphic-t-shirt-t-shirts-men-s-premium-t-shirt.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022651',\\n\",\n       \"  'url': 'https://christiancamppro.com/wp-content/uploads/2018/02/Best-Camps-Retreats-in-Alabama.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022666',\\n\",\n       \"  'url': 'https://static.wixstatic.com/media/4e9949_a76b7d6aedd440668c4da5527c040acd~mv2.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022859',\\n\",\n       \"  'url': 'https://trendytechie.files.wordpress.com/2016/05/pike_place_market_seattle_trendy_techie_2.jpg?w=720'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022923',\\n\",\n       \"  'url': 'http://d2dzjyo4yc2sta.cloudfront.net/?url=images.pitchero.com%2Fui%2F126397%2Fimage_59259361814fc.jpg&amp;w=800&amp;h=800&amp;t=square'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022943',\\n\",\n       \"  'url': 'https://image.shutterstock.com/image-photo/stock-vector-baby-car-seat-vector-icons-toddle-car-seat-safe-child-traveling-icons-vector-icons-set-baby-450w-722346157.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000022948',\\n\",\n       \"  'url': 'http://jouonsplus.com/img/p/568-703-large.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023014',\\n\",\n       \"  'url': 'https://static3.bigstockphoto.com/5/8/1/large2/185465425.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023027',\\n\",\n       \"  'url': 'http://st.depositphotos.com/1001439/2873/v/450/depositphotos_28737429-Colorful-easter-floral-background.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023040',\\n\",\n       \"  'url': 'https://i.pinimg.com/originals/19/3c/f6/193cf6467b7b38eaf7f5e8bd9e179c12.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023093',\\n\",\n       \"  'url': 'https://lh3.googleusercontent.com/E7n1TWkgqvj7hrSgPo2RFvea9X0t7_m5_vIAjbQABmdXe1KObY9WbQhXKmLjH87Xicj_=h355'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023102',\\n\",\n       \"  'url': 'https://images.vexels.com/media/users/3/71949/list/69412a008cbf8fcc670d5288e92a685a-20-businessman-icons.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023136',\\n\",\n       \"  'url': 'http://img.picturequotes.com/2/3/2986/live-like-someone-left-the-gate-open-quote-2.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023213',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.net/image-server/v1/compositions/124117021/views/1width=300height=300appearanceId=2version=1439812226.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023302',\\n\",\n       \"  'url': 'https://birthright.com.au/wp-content/uploads/2018/03/quotes-Mentoring-for-Doulas-300x300.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023326',\\n\",\n       \"  'url': 'https://ppntestblog.files.wordpress.com/2012/08/mainimages_conference.jpg?w=780'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023386',\\n\",\n       \"  'url': 'https://www.partnachlodge.de/wp-content/uploads/2020/08/cropped-logo-partnachlodge-header.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023480',\\n\",\n       \"  'url': 'https://thumb7.shutterstock.com/image-photo/stock-vector-creative-vector-abstract-for-mother-s-day-with-nice-and-creative-illustration-in-background-450w-406105375.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023491',\\n\",\n       \"  'url': 'http://www.netnewsledger.com/wp-content/uploads/2014/09/Frost-on-the-Windshield-Sept-18-2014.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023553',\\n\",\n       \"  'url': 'http://covers.booktopia.com.au/big/9780977535644/heroic-forceful-and-fearless.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023562',\\n\",\n       \"  'url': 'https://us.123rf.com/450wm/mrswilkins/mrswilkins1804/mrswilkins180400769/99227082-north-pole-post-office-rubber-stamp.jpg?ver=6'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023602',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.com/image-server/v1/compositions/1010284665/views/1width=300height=300appearanceId=359version=1456137105/you-make-me-whole-tote-bag.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023657',\\n\",\n       \"  'url': 'https://expressautologistics.com/wp-content/uploads/authorize.net_.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023667',\\n\",\n       \"  'url': 'https://mmedia.ozone.ru/multimedia/1010816996.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023679',\\n\",\n       \"  'url': 'https://cdn.schoolloop.com/uimgcdn/aHR0cHM6Ly9wbXMtc2N1c2QtY2Euc2Nob29sbG9vcC5jb20vdWltZy9maWxlLzE1MjAwNjU0NDc5ODQvMTQwNzk4OTcwMzEzMS82Mzc0MTQ3NTI5MDk0MDc1MDEzLmpwZw=='},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023737',\\n\",\n       \"  'url': 'https://i1.sndcdn.com/avatars-000210261349-jci4gw-t500x500.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023741',\\n\",\n       \"  'url': 'https://m.media-amazon.com/images/I/41XWFjjf7NL.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023779',\\n\",\n       \"  'url': 'http://img2.imagesbn.com/p/97361227245_p0_v1_s260x420.JPG'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023784',\\n\",\n       \"  'url': 'https://i.icanvas.com/LFS79?d=3&sh=v&p=1&s=m&bg=g&t=1569551609'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023862',\\n\",\n       \"  'url': 'https://static1.bigstockphoto.com/thumbs/1/3/1/large2/131294690.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023879',\\n\",\n       \"  'url': 'http://oldies.scdn5.secure.raxcdn.com/i/boxart/w340/a-z/e/esjz4201369.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023889',\\n\",\n       \"  'url': 'https://i2.wp.com/www.aw2y.es/wp-content/uploads/2017/07/logo12.png?fit=772%2C478&'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023955',\\n\",\n       \"  'url': 'http://welovegraphics.com/Tagarooz/Saturday/sat2_9865fkkfjja.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000023980',\\n\",\n       \"  'url': 'https://i.imgur.com/8qV0LZs.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024031',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/C8sn0VY6zEo/0.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024038',\\n\",\n       \"  'url': 'http://blog-imgs-43.fc2.com/d/o/n/donkichirou/WinUtilities12.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024053',\\n\",\n       \"  'url': 'https://www.jeffhendricksondesign.com/wp-content/uploads/2016/04/hanley-retro-font-12.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024095',\\n\",\n       \"  'url': 'https://media.karousell.com/media/photos/products/2018/02/26/162652_156521227_thumbnail.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024119',\\n\",\n       \"  'url': 'http://o.aolcdn.com/dims-shared/dims3/GLOB/crop/1499x999+0+0/resize/590x393!/format/jpg/quality/85/http://o.aolcdn.com/hss/storage/midas/30a7c153cbd10e5de0bc56700199833a/204146627/MASP3313.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024120',\\n\",\n       \"  'url': 'https://base-ec2if.akamaized.net/w=300a=0q=90u=1/images/item/origin/96e00bcee336876e16c12010c5252fa1.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024142',\\n\",\n       \"  'url': 'https://olxlbimages-a.akamaihd.net/43f8b3582dda3424f27f9b3d80f866a3/olxlb_2256154_1_644x461.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024181',\\n\",\n       \"  'url': 'https://www.thedigitalhash.com/wp-content/uploads/2019/07/xmukul2.jpg.pagespeed.ic.Nh_D9ZiO57.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024203',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/1506/5418/products/ELECTRICAL_POWER_SHUNT_TRIP_large.JPG?v=1524148794'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024209',\\n\",\n       \"  'url': 'https://d1mi3s36zg393u.cloudfront.net/event/183046/5f6700fc83fc400882b1342359bf0209.image!jpeg.377782.jpg.thelovefestivalmx650X650.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024211',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0920/5942/products/HM_2014_Archive_large.jpg?v=1438716132'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024426',\\n\",\n       \"  'url': 'https://lh3.googleusercontent.com/proxy/WYKepEFhldG2ZA5w46bkui825WJYxaIUDQlkjO55ZXu5PYZNKPfl1PFcDC15Cg6X4g2gbX23dkSSJSEfSLBwd864w4Cg69HGZEwRMq2ryPgxhddfnE9WstLMllJq2qXFIuihIbfB7pBORAwIZYoo7zYDDgcCCKszD4ms0A=w530-h298-p'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024441',\\n\",\n       \"  'url': 'https://www.egrabber.com/blog/wp-content/uploads/2015/04/b2b_blog.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024455',\\n\",\n       \"  'url': 'https://www.gamesforcats.com/wp-content/uploads/2019/10/Word-Villas-Answers-and-cheats.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024500',\\n\",\n       \"  'url': 'https://wgna.com/files/2012/10/night-at-north-pole-logo-1.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024571',\\n\",\n       \"  'url': 'http://blog.contentools.com/wp-content/uploads/2018/09/3-types-600x300.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024608',\\n\",\n       \"  'url': 'https://farm3.staticflickr.com/2192/2407869633_fb2b491ddc.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024639',\\n\",\n       \"  'url': 'https://mlpnk72yciwc.i.optimole.com/cqhiHLc.WqA8~2eefa/w:350/h:350/q:75/rt:fill/g:ce/https://bleedingcool.com/wp-content/uploads/2017/05/Perception6.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024678',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/1631/5609/articles/Namm2020andSpeakerCompressionFeaturedImage_345x.png?v=1580256974'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024822',\\n\",\n       \"  'url': 'https://s3.amazonaws.com/saumcbrmedia/wp/wp-content/uploads/2018/03/10111147/easter-egg-hunt-18.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024946',\\n\",\n       \"  'url': 'https://content.cdntwrk.com/mediaproxy?url=https%3A%2F%2Fi.ytimg.com%2Fvi%2FyoTvjWxXlow%2Fhqdefault.jpg&size=1&version=1541786369&sig=b0ea12890c1f4eb644f3d4205c361510&default=hubs%2Ftilebg-videos.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024948',\\n\",\n       \"  'url': 'https://petgroomershenandoahvalley.com/wp-content/themes/apm-2018/images/logo.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024994',\\n\",\n       \"  'url': 'https://i.scdn.co/image/112994ca261bb7dfb627b9f76cc7ddbafc016a36'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000024999',\\n\",\n       \"  'url': 'https://dx72k0ec4onep.cloudfront.net/product/3512/1085889221/GNA5EA-1500730304-390x390-double_trouble_12.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025018',\\n\",\n       \"  'url': 'http://4.bp.blogspot.com/-gWZ-YdGG_zk/VwMNaYl515I/AAAAAAAACEI/gR0NPMDWy9szjhswOMeVTj519gnpfGZug/s270/Earth%2BDay%2BActivities%2B-%2BThumbnail.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025040',\\n\",\n       \"  'url': 'https://www.angelsachse.de/images/product_images/info_images/4603_0.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025059',\\n\",\n       \"  'url': 'http://www.geeksofdoom.com/GoD/img/2013/03/2013-03-05-game_of_thrones_dragon.jpeg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025164',\\n\",\n       \"  'url': 'https://pictures.abebooks.com/isbn/9781557420589-es-300.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025265',\\n\",\n       \"  'url': 'http://cdn.shopify.com/s/files/1/0207/0894/products/bball-labels1_grande.png?v=1425494100'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025309',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0091/7579/3744/t/2/assets/logo.png?2667'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025485',\\n\",\n       \"  'url': 'https://d3t3ozftmdmh3i.cloudfront.net/production/podcast_uploaded/1478497/1478497-1550942884084-6314ed1f9c1e2.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025507',\\n\",\n       \"  'url': 'http://covestreetcapital.com/wp-content/uploads/2017/02/Give_ChildrensHospitalLA.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025598',\\n\",\n       \"  'url': 'https://www.hrstransportinc.com/content/uploads/2016/04/cropped-logo.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025611',\\n\",\n       \"  'url': 'http://image.tmdb.org/t/p/w300/ef4gmMkP4At2TZbHJ2lViwFG53S.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025613',\\n\",\n       \"  'url': 'https://www.lifewithbabykicks.com/wp-content/uploads/2015/01/clean-eating-peanut-butter-flapjacks-1.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025649',\\n\",\n       \"  'url': 'http://rooms101.com/images/og/image.php?c=3137'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025669',\\n\",\n       \"  'url': \\\"https://cdn.shopify.com/s/files/1/1211/5954/products/watermelon_760x.jpg?v=1546411434'\\\"},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025675',\\n\",\n       \"  'url': 'https://www.literaryroadhouse.com/wp-content/uploads/2015/11/Literary-Roadhouse-Header-PC-300x300.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025704',\\n\",\n       \"  'url': 'https://warrenisweird.files.wordpress.com/2015/11/top05winter.jpg?w=547&h=391'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025710',\\n\",\n       \"  'url': 'http://i1.ytimg.com/vi/51fk0Mj5oLE/0.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025780',\\n\",\n       \"  'url': 'https://i0.wp.com/frame-poythress.org/wp-content/uploads/2015/02/poythresssociology_shrink.png?fit=298%2C475&ssl=1'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025819',\\n\",\n       \"  'url': 'https://fleetimages.bobitstudios.com/upload/government-fleet/content/news/logos/gfx-logo-hr-01-__-600x300-a.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025829',\\n\",\n       \"  'url': 'http://static1.squarespace.com/static/54980ccbe4b08da3f829c707/t/54a269b2e4b0bcb26c2e5655/1508233935044/?format=1500w'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025892',\\n\",\n       \"  'url': 'https://img.reelgood.com/content/movie/cbd7b705-0d13-4d92-8253-916259cbe09f/poster-342.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025907',\\n\",\n       \"  'url': 'https://www.onplatinum.com.au/wp-content/uploads/2019/05/Phishing-Scam-Emails.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025917',\\n\",\n       \"  'url': 'https://d1e4pidl3fu268.cloudfront.net/880d3898-2a29-4d76-835e-6bcd0019fc3a/schoolICTpolicydevelopment.crop_651x488_740.preview.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025931',\\n\",\n       \"  'url': 'https://us.123rf.com/450wm/uniyok/uniyok1509/uniyok150900018/45649395-stock-vector-template-design-of-logo-stamp-silhouette-hello-autumn-watercolor-orange-texture-vector.jpg?ver=6'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000025948',\\n\",\n       \"  'url': 'https://memegenerator.net/img/instances/10576464/live-mediocre-life-die-at-age-72.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026049',\\n\",\n       \"  'url': 'http://tse2.mm.bing.net/th?id=OIP.q8GWyJqNZxQWan3TLOfcQgHaI2'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026070',\\n\",\n       \"  'url': 'https://fromabcstoacts.com/wp-content/uploads/2018/06/A-Camping-Spree-with-Mr-Magee.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026126',\\n\",\n       \"  'url': 'https://brandslogo.net/wp-content/uploads/2015/07/auto-meter-logo-vector-download.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026181',\\n\",\n       \"  'url': 'http://soundings.com/wp-content/uploads/2010/10/cd_350px_healing-waters.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026184',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/v1/542451b5e4b0b6739e3ba5ec/1505874996302-GCDBAMRJECCA60AX9O1Q/ke17ZwdGBToddI8pDm48kP06O0_IHyRXSOOiqwgWaApZw-zPPgdn4jUwVcJE1ZvWEtT5uBSRWt4vQZAgTJucoTqqXjS3CfNDSuuf31e0tVEHLRkg2cosQUGLeQ33UzXdgIxPDaVwE3LlEpL74qP4JVW4jCyXLPvvdR287iymYt8/wild-pitch-podcast.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026211',\\n\",\n       \"  'url': 'https://images.internetstores.de/products//675540/02/30534a/Jack_Wolfskin_Milton_Gloves_grey_heather[280x280].jpg?forceSize=true&amp;forceAspectRatio=true&amp;useTrim=true'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026235',\\n\",\n       \"  'url': 'http://riverside.n-yorks.sch.uk/images/images/website-graphics/logos/sainsburys-school-games-gold-16-17-flat.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026239',\\n\",\n       \"  'url': 'https://www.graphicsfactory.com/clip-art/image_files/image/8/1556448-Professor-Or-Scientist-Cartoon-Character-Holding-A-Pointer-With-Speech-Bubble-Vector-Illustration-Flat-Design-With-Background.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026371',\\n\",\n       \"  'url': 'https://nscurl.com/wp-content/uploads/2019/09/coaches_week_en.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026384',\\n\",\n       \"  'url': 'https://www.happydecal.ca/image/cache/data//Q116%20%E6%95%88%E6%9E%9C%E5%9B%BE1-350x280_0.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026408',\\n\",\n       \"  'url': 'https://a57.foxnews.com/images.foxnews.com/content/fox-business/markets/2017/05/09/allergan-reports-1q-loss/_jcr_content/par/featured-media/media-0.img.jpg/932/470/1494330210073.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026435',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.com/image-server/v1/compositions/P1010395418T1188A70PC1017383866PA2539PT10X0Y19S24/views/1width=300height=300appearanceId=70backgroundColor=E8E8E8version=1456746674/penguin-with-a-hokey-stick-buttons-iphone-7-rubber-case.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026451',\\n\",\n       \"  'url': 'https://www.fairtradeproduktetest.com/wp-content/uploads/2019/03/Fairtrade-Produkte-Test-Logo_Stand-20190320.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026574',\\n\",\n       \"  'url': 'https://seguinfoundation.org/wp-content/uploads/2019/06/LogoSquished.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026649',\\n\",\n       \"  'url': 'https://images.sftcdn.net/images/t_optimizedf_auto/p/a10e795c-9b6a-11e6-9c6d-00163ec9f5fa/397203148/gta-liberty-city-stories-logo.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026686',\\n\",\n       \"  'url': 'http://mcdn.zulily.com/images/cache/product/350x1000/57024/collins_cs6344_creamandred_1374712683.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026729',\\n\",\n       \"  'url': 'https://img.sanctuary.fr/fiche/300/5215.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026776',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/1326/8593/products/made_in_1987Sweatshirts_Layer_1_grande.jpg?v=1468948927'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026789',\\n\",\n       \"  'url': 'https://www.reversepaisa.com/wp-content/uploads/2020/05/images-1.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026791',\\n\",\n       \"  'url': 'http://www.calspasfremont.com/img/logos/made-in-us-lg-bg.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026840',\\n\",\n       \"  'url': 'https://rjs-industrie-resine-france.com/wp-content/uploads/2020/02/cropped-lofo-rjs-1.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000026931',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/5a77ba07aeb6254c5dc87624/1619738538983-4C5Z2A2V7SL4LZV0GW1K/unusual_light_source.jpg?content-type=image%2Fjpeg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027057',\\n\",\n       \"  'url': 'https://static1.bigstockphoto.com/2/1/8/large2/81299213.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027070',\\n\",\n       \"  'url': 'https://img0.etsystatic.com/101/1/8703712/il_340x270.894142720_pzk0.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027145',\\n\",\n       \"  'url': 'https://img1.liveinternet.ru/images/attach/c/10/109/9/109009223_Mini_Motif_crochet_pattern_000.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027150',\\n\",\n       \"  'url': 'https://cdn.abclocal.go.com/images/otrc/2010/photos/free-agents_nbc.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027242',\\n\",\n       \"  'url': 'http://rlv.zcache.com/save_the_date_wedding_personalized_invite-r07b55a4e7bc74ff3b2d985f93a3741a2_8dnmv_8byvr_325.jpg?bg=0xffffff'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027270',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/fHkLZC2A9Wo/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027285',\\n\",\n       \"  'url': 'https://guitargeargiveaway.co.uk/wp-content/uploads/2020/11/500-Christmas-Cash-600x600.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027359',\\n\",\n       \"  'url': 'https://www.techmen.net/wp-content/uploads/2018/03/1-21.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027556',\\n\",\n       \"  'url': 'https://dasg7xwmldix6.cloudfront.net/hostpics/d861f0b7-092b-4296-a3fd-3500ce729eab_podcast_art__talk_star_wars_rob_wade.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027594',\\n\",\n       \"  'url': 'https://eventticketboss.com/sites/default/files/styles/focal_cropped_thumbs/public/Cher_510x475-0c24c1d49b.png?itok=9vBJLU-d'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027623',\\n\",\n       \"  'url': 'https://images-na.ssl-images-amazon.com/images/I/61HDiD8wJqL._SL300_.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027637',\\n\",\n       \"  'url': 'http://dvdyatii.com//media/prod_20110617025507.pjpeg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027647',\\n\",\n       \"  'url': 'https://d.gr-assets.com/books/1403198824l/20757532.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027788',\\n\",\n       \"  'url': 'https://media2.wnyc.org/i/800/0/c/80/photologue/photos/no-more-corruption.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027797',\\n\",\n       \"  'url': 'http://i.ytimg.com/vi/yBbmxkoi3QY/0.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000027935',\\n\",\n       \"  'url': 'http://cdn.images.express.co.uk/img/dynamic/1/590x/daily-milk-chocolate-429500.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000028026',\\n\",\n       \"  'url': 'https://baobook.pl/2009-home_default/tickly-christmas-wibbly-pig.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000028065',\\n\",\n       \"  'url': 'https://keep-calm.net/images/keep-calm-and-drink-tea-600-800-white-green.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000028127',\\n\",\n       \"  'url': 'https://us.123rf.com/450wm/popcar/popcar1812/popcar181200003/115903656-farmers-market-metal-sign-with-retro-pickup-.jpg?ver=6'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000028395',\\n\",\n       \"  'url': 'https://jellytoastblog.com/wp-content/uploads/2014/12/OPA_HolidayScramble_graphic_3.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000028406',\\n\",\n       \"  'url': 'http://thepost.s3.amazonaws.com/wp-content/uploads/2013/03/national-weather-service-logo.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000028493',\\n\",\n       \"  'url': 'https://base-ec2if.akamaized.net/w=500a=0q=90u=1/images/item/origin/0e8f2282a2bb6a84a8334dc455fd3010.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000028563',\\n\",\n       \"  'url': 'http://tweitesfamilyfarm.com/wp-content/uploads/2018/12/Celebrating-31-Years.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000028683',\\n\",\n       \"  'url': 'https://thecommoncentsclub.com/wp-content/uploads/2018/11/Making-Sense-of-Affiliate-Marketing.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000028708',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.com/image-server/v1/compositions/T1186A1PA2537PT17X12Y2D1010534728S25/views/1width=300height=300appearanceId=1/curly-hair-don-t-care-adjustable-apron.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000028894',\\n\",\n       \"  'url': 'https://d3rbxgeqn1ye9j.cloudfront.net/fileadmin/_processed_/f/c/csm_uvex-apache-folder-eyeprotection-for-special-forces_28211ac607.jpg?1519809586'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000028955',\\n\",\n       \"  'url': 'https://i.etsystatic.com/13863278/d/il/db4673/1671561977/il_340x270.1671561977_dk79.jpg?version=0'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029093',\\n\",\n       \"  'url': 'https://www.puromanga.net/wp-content/uploads/2018/11/1-MARVEL--300x300.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029151',\\n\",\n       \"  'url': 'https://media.gettyimages.com/photos/indian-cricketers-pose-for-photographers-after-their-victory-in-the-picture-id843406370?s=612x612'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029221',\\n\",\n       \"  'url': 'http://spatialplanningtudelft.org/wp-content/uploads/2016/03/Pages-from-Fresh-Eyes-on-the-Refugee-Crisis_titel-290x290.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029259',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/PveEqxH2sfM/maxresdefault.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029300',\\n\",\n       \"  'url': 'https://kbimages1-a.akamaihd.net/a93bc055-2ee0-4e26-b006-2d673177a18a/353/569/90/False/the-meaning-of-relativity-illustrated.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029319',\\n\",\n       \"  'url': 'http://i3.cpcache.com/product/367233782/the_man_behind_the_belly_greeting_card.jpg?width=550&amp;height=550&amp;Filters=%5b%7b%22name%22%3a%22background%22%2c%22value%22%3a%22F2F2F2%22%2c%22sequence%22%3a2%7d%5d'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029325',\\n\",\n       \"  'url': 'http://4.bp.blogspot.com/-_7GA4rftsP0/Wf_IuXZZ5hI/AAAAAAAAFTg/nF8V7Ra5-kUgVUJsEBdq7fpXX06_58inACK4BGAYYCw/s1600/Remakes%2BBlogfest.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029432',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0264/1553/0030/t/7/assets/logo.png?v=10844027794414651129'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029446',\\n\",\n       \"  'url': 'https://img1.fold3.com/img/thumbnail/162005711/300/400/0_0_1179_1755.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029503',\\n\",\n       \"  'url': 'https://www.cdfinancial.co.uk/wp-content/uploads/2021/04/helptobuy-img.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029510',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/51fML9Jn00L._SL300_.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029528',\\n\",\n       \"  'url': 'https://cdn.mytheatreland.com/images/show/27360_show_portrait_large.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029576',\\n\",\n       \"  'url': 'http://www.tabletalkmedia.co.uk/wp-content/uploads/2015/06/penguin-random-house-600x403.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029620',\\n\",\n       \"  'url': 'https://i2.wp.com/www.speechtherapyfun.com/wp-content/uploads/2016/07/Slide2.png?resize=576%2C394'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029693',\\n\",\n       \"  'url': 'https://chirosportsandwellness.com/wp-content/uploads/2018/01/chiro-logo.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029721',\\n\",\n       \"  'url': 'http://b.vimeocdn.com/ps/665/723/6657231_300.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029728',\\n\",\n       \"  'url': 'https://static5.groundgame.com/eng_il_Collection-Athletic-2-0-Kids-393.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029740',\\n\",\n       \"  'url': 'https://www.shenzhenjewelleryfair.com/Portals/35/Jewellery_and_Gem_Shenzhen_logo_RGB_L.png?ver=2020-02-06-184806-083'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029772',\\n\",\n       \"  'url': 'https://rosenthal-c00.kxcdn.com/thumbnails/images/showcase/f5/6f/fc/de/25container_498x750_food_presenter_20-w341-center.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029797',\\n\",\n       \"  'url': 'https://www.thecometonline.com/wp-content/uploads/2021/02/A-guide-to-a-healthier-quarantine.png'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029803',\\n\",\n       \"  'url': 'http://vafloc02.s3.amazonaws.com/isyn/images/f828/img-2139828-m.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029808',\\n\",\n       \"  'url': 'https://us.123rf.com/450wm/bartusp/bartusp1902/bartusp190200390/116736627-different-letters-flag-of-france-and-question-do-you-speak-french.jpg?ver=6'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029864',\\n\",\n       \"  'url': 'https://worldtruthvideos.website/upload/photos/2021/04/NNS43eVslWQvyM6E3fua_27_1fb2acbd3637ffe9bef8383b5451e864_image.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029910',\\n\",\n       \"  'url': 'https://ecdn.teacherspayteachers.com/thumbitem/-1-Deal-Number-Cards-0-100-3308792-1501698550/original-3308792-1.jpg'},\\n\",\n       \" {'ex_idx': '00002',\\n\",\n       \"  'in_idx': '000029920',\\n\",\n       \"  'url': 'https://img1.etsystatic.com/201/0/17118105/il_340x270.1477136441_dal3.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000030183',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/keep_calm_and_drink_wine_grapes_alcohol_social_dri_case-r0f0ac9275ce24ad5b9a308f443d58d51_80cs8_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000030294',\\n\",\n       \"  'url': 'https://ia802703.us.archive.org/view_archive.php?archive=/21/items/olcovers199/olcovers199-L.zip&file=1994634-L.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000030371',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/1917/6859/products/DSC_6743_1024x1024.jpg?v=1573500607'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000030472',\\n\",\n       \"  'url': 'https://thumbs4.ebaystatic.com/d/l300/pict/162348978159_1.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000030546',\\n\",\n       \"  'url': 'https://img-1.fyyd.de/pd/layout/35224bede40395450f977de3d010f6613d86.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000030588',\\n\",\n       \"  'url': 'https://www.holdson.com/images/thumbs/0006534_4m-science-motorised-robot-hand_360.jpeg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000030594',\\n\",\n       \"  'url': 'http://i.ytimg.com/vi/TU_hEUl9cMo/hqdefault.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000030704',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/51752444e4b0f7b91d66f159/1513215184927-03YZELSF26HA9V3LSYOI/?format=1000w&content-type=image%2Fjpeg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000030725',\\n\",\n       \"  'url': 'https://static3.bigstockphoto.com/thumbs/9/1/5/large2/51984985.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000030888',\\n\",\n       \"  'url': 'https://www.uphe.com/sites/default/files/styles/scale__344w_/public/2015/04/025192208942_DVD_2D-X.png?itok=wQRb_UYH'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000030920',\\n\",\n       \"  'url': 'https://www0.alibris-static.com/the-art-of-the-lord-of-the-rings/isbn/9780618510986_l.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031036',\\n\",\n       \"  'url': 'https://iwantnitroicecream.com/wp-content/uploads/fun-mathheets-multiplication-flower-for-kids-printable-pack-the-color-by-montessoriheet-waldorf-nature-stunning-672x1008.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031067',\\n\",\n       \"  'url': 'https://i1.wp.com/8subjects.com/wp-content/uploads/2017/08/plugins.jpg?fit=1280%2C720'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031085',\\n\",\n       \"  'url': 'https://freevector.co/wp-content/uploads/2011/09/fx-by-ram-golf.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031100',\\n\",\n       \"  'url': 'https://s3.amazonaws.com/tkpro-assets/bow_2020/bow_2020_badges(120x120).png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031128',\\n\",\n       \"  'url': 'http://www.plant-magic.co.uk/userfiles/blog/t_56.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031158',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.com/image-server/v1/compositions/1016496174/views/1width=300height=300appearanceId=351backgroundColor=E8E8E8version=1489401380/it-s-never-too-late-mature-couple-wedding-men-s-t-shirt.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031187',\\n\",\n       \"  'url': 'https://mfcdn.de/product/300x500/louis-vuitton-clochette-mit-schloss-388c6c.jpeg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031194',\\n\",\n       \"  'url': 'https://images10.newegg.com/ProductImage/A12K_129887030631380063OvAsGfwBJD.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031315',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/ZBtdTiHsQqI/0.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031359',\\n\",\n       \"  'url': 'https://images.justlanded.com/directory_images/Ireland_Dublin/78/TOPCHEFS-Careers-Recruitment/photo/big_scaled_86701_11211_logo.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031422',\\n\",\n       \"  'url': 'https://is1-ssl.mzstatic.com/image/thumb/Purple114/v4/4e/7e/79/4e7e7907-db65-f9c8-a8de-b0ca66e5684e/source/512x512bb.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031449',\\n\",\n       \"  'url': 'https://www.bestmessage.org/wp-content/uploads/2018/04/friday-the-13th-good-luck-messages.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031464',\\n\",\n       \"  'url': 'http://fooddaycelebration.org/wp-content/uploads/2013/04/HLA_Final_Logo-large-webonly.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031492',\\n\",\n       \"  'url': 'https://prodimage.images-bn.com/pimages/9781935978732_p0_v5_s550x406.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031543',\\n\",\n       \"  'url': 'http://images.footballfanatics.com/FFImage/thumb.aspx?i=/productImages/_648000/ff_648157_xl.jpg&amp;w=180'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031607',\\n\",\n       \"  'url': 'https://1.bp.blogspot.com/-olvHmEGC6M4/XdBNsNINZWI/AAAAAAAAKnE/SCJiq-pbUs8BqQ1CDatqBTuhXp9bvrSiwCLcBGAsYHQ/s1600/parveen%2Bshakir%2Bpoetry%2Bin%2Burdu%2B%25282%2529.webp'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031713',\\n\",\n       \"  'url': 'https://s.s-bol.com/imgbase0/imagebase3/large/FC/5/8/7/2/9200000035872785.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031743',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/1787/2513/products/komedie-4x-png_480x480.png?v=1539689562'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031755',\\n\",\n       \"  'url': 'https://cdn.knoji.com/images/logo/slimleatherjacketscom.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031794',\\n\",\n       \"  'url': 'https://lh6.googleusercontent.com/proxy/djg5z7yNFWFkBKCo_18449AAyjho8qr0ofCU91rpB9g_z-Ngu9-V1qGoACmMGWZSSNwslwou00F0dqsvtYyRhspR4a-RSY5V2dlkDfIeAcGqtWjsVc0vxZ5slPPN2oeQdxaMvaGDFSU=s0-d'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031806',\\n\",\n       \"  'url': 'http://e2.365dm.com/14/01/16-9/20/li-na_3060486.jpg?20140103114207'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031838',\\n\",\n       \"  'url': 'http://oldies.scdn5.secure.raxcdn.com/i/boxart/w340/17/36/883316173602.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031839',\\n\",\n       \"  'url': 'https://res-4.cloudinary.com/simpleview/image/fetch/c_fillf_autoh_300q_75w_300/https://res.cloudinary.com/simpleview/image/upload/v1427052866/clients/roanoke15/Roanoke_Awards_58a27038-6644-4064-a4ba-b14f3edc8852.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031847',\\n\",\n       \"  'url': 'http://www.texascraftykitchen.com/wp-content/uploads/2016/04/A-Z-Kitchen-Organizing-Pin.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031892',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0057/6159/7510/products/41wzje-SuML_bc03befa-698a-4e0c-bce1-0bf219aef904_195x195@2x.jpg?v=1575474619'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031909',\\n\",\n       \"  'url': 'https://img.youtube.com/vi/U027HlrtwzA/0.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031948',\\n\",\n       \"  'url': 'https://www.surfertoday.com/images/stories/surfsummerdeals.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031983',\\n\",\n       \"  'url': 'https://static.edealer.ca/V3_1/assets/images/new_vehicles_images_coming.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000031988',\\n\",\n       \"  'url': 'https://cdn11.bigcommerce.com/s-eoqdgo/images/stencil/500x659/products/5633/79090/997e4524f8d216fc4ca78391c9bbd6be696e1ce6__46306.1580691796.jpg?c=2'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032068',\\n\",\n       \"  'url': 'https://noiseandheatreduction.co.za/wp-content/uploads/2018/10/cropped-Logo.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032079',\\n\",\n       \"  'url': 'http://i4.ytimg.com/vi/pOmtzdSLVIQ/0.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032132',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/1JxL7qaDk9w/0.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032157',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/61-19os-saL.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032160',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.net/image-server/v1/compositions/P21743842T281A2PC31660930PA447PT17X38Y49S52/views/2width=300height=300appearanceId=2backgroundColor=E8E8E8version=1464324274/los-angeles-california-barneskjorter-poloskjorte-slim-for-menn.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032162',\\n\",\n       \"  'url': 'https://cdn.quotesgram.com/small/88/50/1056512902-Quotes_to_Start_the_New_School_Year_from_Clever_Classroom.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032176',\\n\",\n       \"  'url': 'http://cdn2.bigcommerce.com/n-biq04i/6c9tf/products/86/images/284/preserves_blackberry__16548.1383772329.386.513.jpg?c=2'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032190',\\n\",\n       \"  'url': 'https://d1wli5mq9yq9mw.cloudfront.net/files/cards/full/JPSTATIONERY104.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032217',\\n\",\n       \"  'url': 'http://tse4.mm.bing.net/th?id=OIP.EGvJhvpFhfHYlXptmfpCIAHaH_'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032285',\\n\",\n       \"  'url': 'http://www.meteocat.org/wp-content/uploads/2020/09/coloring-pages-ideas-fantasticesus-loves-me-printables-photo-inspirations-page.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032323',\\n\",\n       \"  'url': 'https://static3.bigstockphoto.com/thumbs/2/5/7/large2/75245572.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032330',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/1730/4297/products/CL-BR-FLM_1ba4d98f-a371-4ad6-a031-d4624534a029_195x195@2x.jpg?v=1535396230'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032331',\\n\",\n       \"  'url': 'http://blog.continentalclub.co.uk/wp-content/uploads/2016/11/Continental-Club-Air-Fare-Alert-BA-BF-630x490.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032393',\\n\",\n       \"  'url': 'https://thelovelylifestyle.files.wordpress.com/2013/11/today-is-a-good-day-tea-towel.jpg?w=500'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032396',\\n\",\n       \"  'url': 'http://ourpeacefulplanet.com/wp/wp-content/uploads/2015/03/Clothespin-Dragon-Fly-Note-Holder-Tutorial.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032450',\\n\",\n       \"  'url': 'https://i0.wp.com/www.rushinformation.com/wp-content/uploads/2016/04/Farming-Simulator-compressed.jpg?fit=613%2C331'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032642',\\n\",\n       \"  'url': 'http://freevector.co/wp-content/uploads/2009/04/nnmotors.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032705',\\n\",\n       \"  'url': 'http://img2.imagesbn.com/p/9780124016781_p0_v1_s260x420.JPG'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032804',\\n\",\n       \"  'url': 'https://gentlemint-media.s3.amazonaws.com/images/2016/10/15/829d37ff.jpg.757x975_q85.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032805',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0958/5794/products/Green-Bay-Packers-50-60-Singular-Printed-Throw-Blanket_58378d5a-73bd-4cb3-a656-98c5f0dc419a_large.jpg?v=1551304842'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032859',\\n\",\n       \"  'url': 'https://motionarray-portfolio.imgix.net/preview-86972-f4c9eccd90c94854b5c769851f7a4e11-low.png?w=660&q=60&fit=max&auto=format'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032886',\\n\",\n       \"  'url': 'https://i.imgur.com/4yBk2ar.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032899',\\n\",\n       \"  'url': 'http://static9.depositphotos.com/1006708/1121/i/450/depositphotos_11218814-Last-Will-and-Testament-and-glass-of-whiskey.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032904',\\n\",\n       \"  'url': 'https://content.cdntwrk.com/mediaproxy?url=https%3A%2F%2Fwww.concordiatechnology.org%2Fhubfs%2F_blogs%2Ftechnology-and-your-ministry%2F2018%2F09%2Fcontinued-education-free-ways-to-continue-your-education-seminary-youtube-blog-post.png%3Ft%3D1543598284641%23keepProtocol&size=1&version=1543607592&sig=0e62bbc1a99b77d2f05bff15bbf5528e&default=hubs%2Ftilebg-blogs.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032939',\\n\",\n       \"  'url': 'https://www.yesteryearbooks.co.uk/assets/images/product/052297.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032949',\\n\",\n       \"  'url': 'https://d24m66tiq5iban.cloudfront.net/pics/property/397739282/3/IDX_3/v3//crop/656400/'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032975',\\n\",\n       \"  'url': 'https://d2pafxp37ue0ak.cloudfront.net/system/zen_products/images/4988/shop/2014_0205_ka081.jpg?1553544215'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000032984',\\n\",\n       \"  'url': 'http://beautygeekuk.com/wp-content/uploads/2015/05/Mavala-Summer-and-Garden-Party-Collection1-960x1024.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033028',\\n\",\n       \"  'url': 'https://nordicbyname.dk/wp-content/uploads/2020/08/Glitter_sort.boss_11promax-300x300.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033036',\\n\",\n       \"  'url': 'https://platform.nashvilleparent.com/media/BambiniVillage-LOGO-web.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033077',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/2099/9145/products/tacoma-fuji-records-tacoma-fuji-records-inc-t-shirt-navy-supplies-and-co-2_600x.jpg?v=1599138331'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033221',\\n\",\n       \"  'url': 'https://s1.yimg.com/bt/api/res/1.2/otDIwb0r06dzauz43WN2Mg--/YXBwaWQ9eW5ld3M7Zmk9ZmlsbDtoPTI2MTtweW9mZj0wO3E9NzU7dz0zNTA-/http://media.zenfs.com/en_US/News/TheWrap/directv_main.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033226',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/kiev_ukraine_english_ukrainian_language_key_ring-rb5cabfe968214cb989fb14f2d1b6018c_x76wx_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033244',\\n\",\n       \"  'url': 'https://img1.etsystatic.com/016/0/6652828/il_340x270.454627855_68lv.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033294',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0781/1065/products/2DTHRFLAME10000_300x300.png?v=1577570006'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033361',\\n\",\n       \"  'url': 'https://www.nissan-cdn.net/content/dam/Nissan/gb/vehicles/Navara-NP300/d23/1_carryover/overview/Navara_Video_HackNo4_BcFsAlqg2jE_Thumbnail.png.ximg.l_full_m.smart.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033364',\\n\",\n       \"  'url': 'http://i.vimeocdn.com/portrait/3348655_300x300.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033428',\\n\",\n       \"  'url': 'https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.com%2Fimages%2F91987371%2F403569244279%2F1%2Foriginal.20200211-180055?w=512&auto=format%2Ccompress&q=75&sharp=10&rect=1%2C312%2C1274%2C637&s=18b2ae1659447d0fcf839237c4d89860'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033433',\\n\",\n       \"  'url': 'https://gravelroadseries.it/wp-content/uploads/2019/12/specoalized-logo-1024x394.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033492',\\n\",\n       \"  'url': 'https://bioprox.es/1179-home_default/vitobest-fat-burner-triple-accion-90-caps.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033522',\\n\",\n       \"  'url': 'https://upload.wikimedia.org/wikipedia/en/8/82/Joanna_-_Kool_%26_The_Gang.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033548',\\n\",\n       \"  'url': 'http://i.vimeocdn.com/portrait/853297_300x300.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033586',\\n\",\n       \"  'url': 'https://pcgimg.azureedge.net/Upload/Product/37771-i-just-wanna-dance-Photo1-20200103114654.jpg?maxwidth=700'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033608',\\n\",\n       \"  'url': 'https://cdn-ec.niceshops.com/upload/image/product/medium/default/sylveco-firming-natural-soap-120-ml-1219283-it.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033629',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/jqmTALgobsU/0.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033700',\\n\",\n       \"  'url': 'https://m.media-amazon.com/images/I/51AWBdzXORL._SL320_.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033710',\\n\",\n       \"  'url': 'https://webassets.inman.com/wp-content/uploads/2014/12/linkedin-lead-generation-1400x621.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033730',\\n\",\n       \"  'url': 'https://skoolopedia.com/app/upload/2017/02/c5276aec0a2a481e9e3295e99efac9d0.jpeg.square-sm.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033751',\\n\",\n       \"  'url': 'https://i.pinimg.com/736x/43/0f/f5/430ff5977045e7a4c8d9a61598681d8c--chalk-fonts-chalk-lettering.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033762',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/1308/1459/products/my-gardening-mug_per302-001_grande.jpg?v=1474976923'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033807',\\n\",\n       \"  'url': 'https://i.imgflip.com/1ujcg2.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000033985',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/51s6FAS-fXL.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034033',\\n\",\n       \"  'url': 'https://zezeewithbooks.files.wordpress.com/2015/09/crown-of-midnight.jpg?w=645'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034067',\\n\",\n       \"  'url': 'http://i01.i.aliimg.com/wsphoto/v0/436016105/Free-Shipping-New-Swimmer-ipx8-Sport-Waterproof-MP3-Player-2GB-Swimming-Running-Surfing-Blue-color-.jpg_350x350.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034250',\\n\",\n       \"  'url': 'http://s3.amazonaws.com/libapps/accounts/50774/images/idea_lab_logo.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034264',\\n\",\n       \"  'url': 'https://mobimg.b-cdn.net/v2/fetch/81/81343e5a428bfd8d14dbb5878cf42081.jpeg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034325',\\n\",\n       \"  'url': 'https://s3.amazonaws.com/webassets.ticketmob.com/TS/images/comedians/LaGrangeatributetoZZTop.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034331',\\n\",\n       \"  'url': 'https://www.clumsycrafter.com/wp-content/uploads/2013/11/7-Elf-on-the-Shelf-Ideas.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034440',\\n\",\n       \"  'url': 'https://ecelonline.disaldigital.com.br/content/images/thumbs/0069508_macmillan-english-grammar-in-context-intermediate-without-key-with-cd_550.jpeg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034457',\\n\",\n       \"  'url': 'https://npirtube.com/wp-content/uploads/2017/04/plugin_yeelight_desk_lamp.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034480',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/v1/538d1920e4b0369e0b93e74b/1415043461328-Q4T9H9ATMQYM3JOLR09L/ke17ZwdGBToddI8pDm48kPx25wW2-RVvoRgxIT6HShBZw-zPPgdn4jUwVcJE1ZvWQUxwkmyExglNqGp0IvTJZUJFbgE-7XRK3dMEBRBhUpwGbtSA7WutlFA3XjmDXUDFwmxX_uEhqHOBUlPnU0mYmf1Qvd6diXKmxQIX-f1CXeo/Child-of-the-Chozo-Will-Brueggemann-super-marcato-bros'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034567',\\n\",\n       \"  'url': 'https://menslifeadvice.com/wp-content/uploads/2015/03/fi_17-2.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034609',\\n\",\n       \"  'url': 'https://www.signs.com/blog/wp-content/uploads/2019/04/Ferrari-Acquisition-02.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034676',\\n\",\n       \"  'url': 'https://images.bwbcovers.com/054/9780547006956.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034714',\\n\",\n       \"  'url': 'http://kingstonpound.org/wp-content/uploads/K%C2%A3-trail-1-3-1.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034738',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0354/9655/8651/products/3bb52418-3baf-5537-9ecf-05ec0794ff4a_71e9cef4-0c33-4979-8d66-39d011890d02_1024x1024.jpg?v=1596057924'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034749',\\n\",\n       \"  'url': 'http://www.londoncoins.co.uk/img.php?a=157&amp;l=2742&amp;f=r&amp;s=t'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034789',\\n\",\n       \"  'url': 'https://flipgive.imgix.net/images/campaigns/photos/000/051/929/width_480/1479752732AHSwimTeam_App.png?ch=Width%2CDPR%2CSave-Data&amp;auto=format%2Ccompress&amp;dpr=2&amp;format=jpg&amp;w=263'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034817',\\n\",\n       \"  'url': 'https://www.fabulous-femme.com/wp-content/uploads/2015/05/lifeistilive-quote.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000034876',\\n\",\n       \"  'url': 'https://base-ec2if.akamaized.net/w=2048a=0q=90u=0/images/user/logo/8311a3198df4251f9eee72b06cf3463d.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035021',\\n\",\n       \"  'url': 'https://cdn.fitimg.in/studio_logo_34AE8B5F1E4DC5.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035050',\\n\",\n       \"  'url': 'https://cooklikejames.typepad.com/.a/6a010536eec1a6970c0133eca04c3e970b-800wi'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035114',\\n\",\n       \"  'url': 'https://i2.wp.com/owsaprint.com/wp-content/uploads/2019/03/Screenshot_46.jpg?resize=300%2C300&amp;ssl=1'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035143',\\n\",\n       \"  'url': 'https://www.catchopcd.net/4723-large_default/gfriend-2nd-mini-album-flower-bud-reissue.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035346',\\n\",\n       \"  'url': 'http://coloradopols.com/wp-content/uploads/2013/06/I-am-created-equal-logo-300x300.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035351',\\n\",\n       \"  'url': 'http://www.talkncoffee.com/images/Chemical%20Solvent%20Free%201.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035359',\\n\",\n       \"  'url': 'https://sandro-keil.de/slides/img/docker/sticker-swarm.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035529',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/51fVGnt3RTL._SL300_.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035608',\\n\",\n       \"  'url': 'https://img0-placeit-net.s3-accelerate.amazonaws.com/uploads/stage/uploaded_thumb_image/2831/large_thumb_IMG_8686_thumb.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035735',\\n\",\n       \"  'url': 'https://cdn.proline-rus.ru/b1750/7dafb/b1a33/5b9c0/c36cd/cad1e/25773/8360.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035797',\\n\",\n       \"  'url': 'http://www.HipHopSince1987.com/wp-content/uploads/2012/05/chill-moody-so-in-love-ft-aaron-camper-prod-by-dilemma-2012-HHS1987.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035844',\\n\",\n       \"  'url': 'https://snugglesquilts.com/wp-content/uploads/2017/08/blooming-patchwork-book-300x300.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035856',\\n\",\n       \"  'url': 'https://s3-ap-southeast-2.amazonaws.com/bookhunter/media/catalog/product/h/400/9/7/9780759102798.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035954',\\n\",\n       \"  'url': 'http://cdn.pastemagazine.com/www/blogs/lists/2010/12/01/best_of_2010.jpg?635298359223700934'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000035980',\\n\",\n       \"  'url': 'http://rprnrwxhpnpq5p.leadongcdn.com/cloud/jkBooKnnSRmpplirj/55555555585591415252.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036054',\\n\",\n       \"  'url': 'http://cdn33.printdirect.ru/cache/product/c1/41/4032859/tov/all/400z400_front_722_0_0_0_11a7159796bd8a8e55b918a7bf7e.jpg?rnd=1338059716'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036248',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/5616c092e4b06489ba877e15/1508812844111-R27JCLC1Q7L6YV9MOQNT/Dirtt-Logo-Brown_bluebigger.png?content-type=image%2Fpng'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036367',\\n\",\n       \"  'url': 'http://previewcf.turbosquid.com/Preview/2011/08/12__15_29_09/3.jpg2e4506c9-9103-4a79-bb65-c6e293f192f5Large.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036377',\\n\",\n       \"  'url': 'http://ecimages.kobobooks.com/Image.ashx?imageID=FGBNf5N-fUue3W0znxYRjA&amp;Type=Full'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036413',\\n\",\n       \"  'url': 'https://static1.squarespace.com/static/55fc7592e4b0f81e4e5760a4/t/55ff11f7e4b08aa6c7f6a7c1/1529071091411/?format=750w'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036441',\\n\",\n       \"  'url': 'http://www.unisealshop.com/Uniseals/photos/3-inch-uniseal-insitu_2.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036529',\\n\",\n       \"  'url': 'https://is3-ssl.mzstatic.com/image/thumb/Podcasts123/v4/8f/74/39/8f74397c-1715-58dd-330b-cb7346e94a01/mza_4523520513334220402.jpg/600x600bb.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036586',\\n\",\n       \"  'url': 'https://www.interactive.org/images/games_developers/nbc_sm.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036706',\\n\",\n       \"  'url': 'https://mk0blogpublicgorygjw.kinstacdn.com/wp-content/uploads/2020/02/PublicGoods_ContentImagery_202002_CoconutShorelineWaikiki_Thumbnail_3200x3200-1.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036746',\\n\",\n       \"  'url': 'https://rlv.zcache.com/funny_accent_yankee_wicked_smart_smaht_bostonian_trucker_hat-rd12044e06a164d53bde2523641d4bb85_eahwi_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036755',\\n\",\n       \"  'url': 'https://i2.wp.com/adventuresinnewengland.com/wp-content/uploads/2020/12/Mystic-Pin.png?fit=603%2C930&ssl=1'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036762',\\n\",\n       \"  'url': 'https://us.123rf.com/450wm/diagon/diagon1311/diagon131100015/23755218-furniture-icons.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036805',\\n\",\n       \"  'url': 'http://wzgamerslab.net/img_games/2018_tri1/treasure_adventure_world_principal.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036864',\\n\",\n       \"  'url': 'https://www.drfergusonaz.com/wp-content/uploads/2016/02/On-The-Road-To-Good-Dental-Health-Logo.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036872',\\n\",\n       \"  'url': 'http://images.randomhouse.com/cover/9780307388629'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036875',\\n\",\n       \"  'url': 'https://img1.od-cdn.com/ImageType-400/6611-1/776/599/81/%7B77659981-5BBD-4A42-BF90-38784BDBCF04%7DImg400.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036886',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/celebrate_diversity_custom_announcement-r552fa0726bf44280aa04831f24b37a8a_imtq3_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000036938',\\n\",\n       \"  'url': 'http://static1.1.sqspcdn.com/static/f/389779/6233054/1602516308790/LDM-new-new2.png?token=1WxhCdrajbk%2BS%2FOBB2%2BMO5pCh7s%3D'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037048',\\n\",\n       \"  'url': 'http://1.bp.blogspot.com/_-0OPosueKrE/TOLJUFxlt6I/AAAAAAAAA5E/IBOKW9dJWG8/s400/acousticep.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037095',\\n\",\n       \"  'url': 'https://prnewswire2-a.akamaihd.net/p/1893751/sp/189375100/thumbnail/entry_id/0_h60be8fb/def_height/400/def_width/400/version/100012/type/1'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037333',\\n\",\n       \"  'url': 'http://i00.i.aliimg.com/wsphoto/v6/1490710460_1/New-2014-Fashion-Women-Blouses-Hot-Selling-Loose-font-b-Animal-b-font-Flower-Printed-Chiffon.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037361',\\n\",\n       \"  'url': 'https://static.ok.co.uk/media/images/300x400_ct/1150563_as_5758eccf1fa6d109e673a6a166dcf269.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037438',\\n\",\n       \"  'url': 'https://2.bp.blogspot.com/-HKJRyGBTp2Y/V5odddpIeII/AAAAAAACYbw/D7Ni6EMh8_YsqTOsRNxepYar0eOqWlMuACLcB/s1600/buttermilk%2Bpound%2Bcake%2Bcollage%2B3.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037459',\\n\",\n       \"  'url': 'http://i92.photobucket.com/albums/l37/theBULLDOGfan/oregon_lottery_300px.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037462',\\n\",\n       \"  'url': 'http://rlv.zcache.com.au/stop_global_whining_mugs-r644896d0bb574861a045d3705c0be07a_x7jgr_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037564',\\n\",\n       \"  'url': 'http://3.bp.blogspot.com/-_pWLIJNlPpE/UwTS_p6NxvI/AAAAAAAAA_g/ulJrVNSc5ko/s1600/discovery-history-black-salvatier.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037571',\\n\",\n       \"  'url': 'https://sr20.driftworks.com/media/catalog/product/cache/1/small_image/280x/9df78eab33525d08d6e5fb8d27136e95/i/m/image_CVR22090P5L4566BT_18101_1_1.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037579',\\n\",\n       \"  'url': 'http://onceuponanalpha.bookblog.io/wp-content/uploads/sites/126/2016/05/Conviction-hi-res-683x1024.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037674',\\n\",\n       \"  'url': 'https://static1.bigstockphoto.com/thumbs/9/2/1/large2/129018050.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037692',\\n\",\n       \"  'url': 'https://3.bp.blogspot.com/-ZOJFCvPg4fc/VW6qJLWELOI/AAAAAAAAnS0/pojE2B10SVw/s1600/Android%2BErrore%2Bspazio%2Bdi%2Barchiviaizone%2Binsufficiente.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037726',\\n\",\n       \"  'url': 'https://www.geraldedwardwilliamshepherd.com/medias/mini/g/e/geraldshepherd/artwork/11789543_movements-in-sky-and-sand.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037878',\\n\",\n       \"  'url': 'https://www.prlog.org/12334260-disaster-recovery-answering-services.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037887',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.net/image-server/v1/compositions/137735145/views/1width=300height=300version=1456916136/nerd-geek-freak-top-top-da-donna-ecologico.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037892',\\n\",\n       \"  'url': 'https://us.123rf.com/450wm/fosin/fosin1508/fosin150800092/44414257-vector-set-of-different-glasses-on-white-background-retro-wayfarer-aviator-geek-hipster-frames-man-a.jpg?ver=6'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000037978',\\n\",\n       \"  'url': 'https://www.newjerseyclub.ru/upfile/pladd/Lakers--32-Magic-Johnson-Purple-Basketball-Swingman-Statement-Edition-Jersey.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038014',\\n\",\n       \"  'url': 'https://i.ytimg.com/vi/BsHztnIjVQc/hqdefault.jpg?sqp=-oaymwEjCPYBEIoBSFryq4qpAxUIARUAAAAAGAElAADIQj0AgKJDeAE=&amp;rs=AOn4CLCYJ2LEpjEcpzWkJiEnEDlx-oRyRQ'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038063',\\n\",\n       \"  'url': 'https://images-na.ssl-images-amazon.com/images/I/51F9qzVPkYL.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038077',\\n\",\n       \"  'url': 'https://i.imgur.com/M0fAvZ1.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038108',\\n\",\n       \"  'url': 'https://images-na.ssl-images-amazon.com/images/I/41hazvbAxNL._SX352_BO1204203200_.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038130',\\n\",\n       \"  'url': 'https://secureservercdn.net/198.71.233.254/25d.494.myftpupload.com/wp-content/uploads/2017/05/cropped-logo-pic-from-phone-2_LI-6.jpg?time=1581808610'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038156',\\n\",\n       \"  'url': 'http://rlv.zcache.co.nz/gerbils_are_awesome_greeting_cards-r257f1a131fd14fc39e93fae639ab11f1_xvuak_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038179',\\n\",\n       \"  'url': 'https://srisriuniversity.edu.in/wp-content/uploads/2018/11/5th-Convocation-Pic-1-409x258.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038250',\\n\",\n       \"  'url': 'https://ntseniorscard.org.au/wp-content/uploads/Optical-Superstore-550x550.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038256',\\n\",\n       \"  'url': 'http://assets.cat5.com/images/catalog/products/3/5/1/9/8/0-325-mechanix-wear-the-original-grip-black.jpg?v=15009'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038329',\\n\",\n       \"  'url': 'https://careerbeacon-canada.s3.ca-central-1.amazonaws.com/company/228926/5aff0fd6190d3.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038330',\\n\",\n       \"  'url': 'https://1.bp.blogspot.com/-xUzc0v_XHmE/Wz7pQKeJLYI/AAAAAAACcaw/IEiMPbdBnfYb44xMW3dMfjaliYrPYUXcwCLcBGAs/s1600/d.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038339',\\n\",\n       \"  'url': 'https://s01.sgp1.cdn.digitaloceanspaces.com/book/115200-wcwlakdtfy-1556807365.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038373',\\n\",\n       \"  'url': 'http://musicjap.com/images/101/various-eurobeat-best-hits.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038382',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/55785642e4b0e3cc13013b23/1456514420456-ZNZV3K6RTZTIL6GFNT7O/a-brialliant-smile-ebook-cover.png?content-type=image%2Fpng'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038478',\\n\",\n       \"  'url': 'https://asweetlife.org/wp-content/uploads/2012/03/JDRF-type-1-diabetes-research-summit-Riva-Greenberg.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038494',\\n\",\n       \"  'url': 'https://www.nikkilynndesign.com/wp-content/uploads/2017/06/Striped-Weasel-Thirteen-Lined-Ground-Squirrel-Digging-Holes-in-Garden-and-Yard.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038513',\\n\",\n       \"  'url': 'https://harrowgrace.org/wp-content/uploads/2016/03/YouTube-icon.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038586',\\n\",\n       \"  'url': 'https://i.servimg.com/u/f85/19/81/18/31/red-ho12.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038642',\\n\",\n       \"  'url': 'https://www.zdidit.com/wp-content/uploads/2016/02/add-sitelinks-searchbox-with-yoast-seo-320x320.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038696',\\n\",\n       \"  'url': 'http://www.tennisnow.com/images/2019/September/Kenin-9-20-19.aspx'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038715',\\n\",\n       \"  'url': 'https://ih0.redbubble.net/image.309167950.5709/raunisex_tshirtx2000101010:01c5ca27c6front-c490436420460-pad420x460f8f8f8.u1.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038719',\\n\",\n       \"  'url': 'http://freevector.co/wp-content/uploads/2012/04/clock-watching.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038726',\\n\",\n       \"  'url': 'https://www.storemypic.com/images/2016/11/04/good-evening-friends-cake-74042.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038759',\\n\",\n       \"  'url': 'https://files.speakerdeck.com/presentations/450cab6d72d6454e9e51a03c028bbc93/slide_30.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038792',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.net/image-server/v1/mp/compositions/T814A366PA1675PT17X41Y10D16422977S106CxFFFFFF/views/1width=400height=400appearanceId=366backgroundColor=C20329noPt=trueversion=1494487205/dear-santa-i-can-explain-koszulka-dziecieca-premium.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038830',\\n\",\n       \"  'url': 'http://blogue.dessinsdrummond.com/wp-content/uploads/2015/04/Drummond-House-Plans-Garage-plan-2989-32-1.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038895',\\n\",\n       \"  'url': 'https://s3-eu-west-1.amazonaws.com/storage.quickbutik.com/stores/1637U/products/5bfba3aa9d77e.jpeg?w=250&h=270&fit=crop&auto=format'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038910',\\n\",\n       \"  'url': 'http://i1.cpcache.com/product/183440425.jpg?height=150&width=150'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038946',\\n\",\n       \"  'url': 'http://smilegreat.com/wp-content/uploads/2016/12/Pic28-cropped.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038949',\\n\",\n       \"  'url': 'http://img.picturequotes.com/2/48/47743/talk-is-cheap-because-supply-exceeds-demand-quote-1.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000038952',\\n\",\n       \"  'url': 'https://i2.wp.com/justabouttv.fr/wp-content/uploads/336522-9.jpg?fit=680%2C1000'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039008',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0445/8423/9272/collections/advanced-nutrients-logo-retina_330x330@2x.png?v=1602159335'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039107',\\n\",\n       \"  'url': 'https://images-na.ssl-images-amazon.com/images/S/cmx-images-prod/Series/64553/c21780779d1f946881aab4edae2aa9f7._SX312_QL80_TTD_.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039150',\\n\",\n       \"  'url': 'https://cache-graphicslib.viator.com/graphicslib/media/06/at-moulin-rouge-en-el-molino-rojo-photo_5997062-770tall.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039187',\\n\",\n       \"  'url': 'http://cdn3.volusion.com/qvzw6.ewf2b/v/vspfiles/photos/CPYOYOAXLBLUE-2T.jpg?1366296987'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039205',\\n\",\n       \"  'url': 'http://www.reigatedrivingschools.co.uk/wp-content/uploads/2015/04/pass-you-theory-test-banner-1-495x400.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039245',\\n\",\n       \"  'url': 'https://1.bp.blogspot.com/-JPTVPAsnFfQ/VZnyUzOUJcI/AAAAAAAAF6Q/XkPghIHTFW4/s1600/AA%2BF-414%2BEnhanced.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039325',\\n\",\n       \"  'url': 'http://cdn.pastemagazine.com/www/articles/2014/03/04/PasteSXSW_InteractiveLead.jpg?635299665269807641'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039421',\\n\",\n       \"  'url': 'https://m-cdn.phonearena.com/images/articles/79373-500/Sony-Ericcson-Windows-Phone-Jolie.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039433',\\n\",\n       \"  'url': 'https://www.indiexl.nl/wp-content/uploads/2015/06/1424854448.gangoffour-300x300.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039572',\\n\",\n       \"  'url': 'https://thechirpingmoms.com/wp-content/uploads/2017/04/9-Loo-Roll-Crafts-for-Kids-Square-2.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039588',\\n\",\n       \"  'url': 'https://www.producerspot.com/wp-content/uploads/2019/06/Black-Octopus-Sound-Professional-Deep-House-Essentials-300x300.jpeg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039661',\\n\",\n       \"  'url': 'https://enamewishes.com/myimages/Best-2020-New-Year-Wish-Name-Cake-320x320.png'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039712',\\n\",\n       \"  'url': 'https://content.cdntwrk.com/mediaproxy?url=http%3A%2F%2Fshare.opsy.st%2F5ed7dcbee5d3f-Embedded-Executive-Labrosse.jpg&size=1&version=1591205125&sig=ac736b9f02b68a424e95e4ed2ce40a7d&default=hubs%2Ftilebg-blogs.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039844',\\n\",\n       \"  'url': 'https://images.vexels.com/media/users/3/154998/list/1d6a18669b809ac8120c602876af6cc2-be-creative-school-camiseta-de-diseno.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039872',\\n\",\n       \"  'url': 'https://images.saymedia-content.com/.image/ar_8:10%2Cc_fill%2Ccs_srgb%2Cfl_progressive%2Cg_faces:center%2Cq_auto:good%2Cw_620/MTc0NTE4MDMxNTkyOTkwNjY1/the-chesapeake-bay-retriever-a-guide-for-owners.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039890',\\n\",\n       \"  'url': 'https://direct.rhapsody.com/imageserver/images/alb.24505732/500x500.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039940',\\n\",\n       \"  'url': 'http://images.allocine.fr/r_640_600/b_1_d6d6d6/medias/nmedia/18/78/35/82/20303823.jpg'},\\n\",\n       \" {'ex_idx': '00003',\\n\",\n       \"  'in_idx': '000039972',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0720/6291/t/2/assets/logo.png?6551784379037359213'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040065',\\n\",\n       \"  'url': 'https://i.pinimg.com/736x/5c/36/68/5c366820d9a009a6902ee5e1c6697063--space-cupcakes-kid-cupcakes.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040107',\\n\",\n       \"  'url': 'http://bonanzleimages.s3.amazonaws.com/afu/images/0616/9087/4a0d5f66a7029a3fae087116d9cb110a_1_.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040180',\\n\",\n       \"  'url': 'http://images.gr-assets.com/books/1448318289l/25184383.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040301',\\n\",\n       \"  'url': 'https://secureservercdn.net/198.71.233.199/rbl.451.myftpupload.com/wp-content/uploads/2015/01/The-Alchemist-Logo.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040340',\\n\",\n       \"  'url': 'http://blog-imgs-42.fc2.com/m/i/n/minefield/201208261613256b8.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040401',\\n\",\n       \"  'url': 'https://rlv.zcache.com.au/christening_banner_baptism_invitation_blue-r57711267c68845089a6388dfa8184ed1_zk9rh_324.jpg?rlvnet=1'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040471',\\n\",\n       \"  'url': 'https://images.lookhuman.com/render/standard/2044052650002600/6040-heathered_gray_nl-md-t-valentines-day-aint-nobody-got-time-for-that.png'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040566',\\n\",\n       \"  'url': 'https://images.booksense.com/images/824/712/9781942712824.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040571',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/monogrammed_wedding_stickers-rebb6bfe0a2e545d7bd43c39e78e8e15c_v9waf_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040704',\\n\",\n       \"  'url': 'http://rlv.zcache.com/keep_calm_and_fight_villains_post_cards-rc8bc0ad4abee4276b26801ef129447f6_vgbaq_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040749',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/41N78grAHCL.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040753',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/2498/0760/products/GManera_WGM_OnlineShop_032_540x720.jpg?v=1547545823'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040793',\\n\",\n       \"  'url': 'https://gchmanhattan.pl/wp-content/uploads/2018/03/manhattanTV_17_kwadrat.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040812',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/31JD6ortxVL.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040838',\\n\",\n       \"  'url': 'http://rlv.zcache.co.nz/well_behaved_women_rarely_make_history_card-r6b9f0dc1e9264aa09e456d0bda2dd134_xvuat_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040886',\\n\",\n       \"  'url': 'https://www.gghschool.com/wp-content/uploads/2020/05/ei-600x350.png'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000040931',\\n\",\n       \"  'url': 'https://daregreatlycoaching.com/wp-content/uploads/2017/12/20171214-Trap-of-your-own-making-400x270.png'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041040',\\n\",\n       \"  'url': 'https://www.soundaffects.com/images/products/thumbnails/1507203969-48938200.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041044',\\n\",\n       \"  'url': 'https://secureservercdn.net/198.71.233.195/7fe.723.myftpupload.com/wp-content/uploads/2016/02/20160131_150003-1-1024x392.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041061',\\n\",\n       \"  'url': 'http://tse3.mm.bing.net/th?id=OIP.-KTF4tl6w_xzzx7tPAuUQQHaEj'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041074',\\n\",\n       \"  'url': 'https://d3nuqriibqh3vw.cloudfront.net/styles/aotw_card_ir/s3/a_tale_of_one_city.jpg?itok=XSq1_JMF'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041111',\\n\",\n       \"  'url': 'https://images.justlanded.com/directory_images/Switzerland_Lucerne/16120/IMI-University-Centre-Switzerland/photo/big_scaled_86987_11909_logo.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041240',\\n\",\n       \"  'url': 'https://www.notovna.cz/images//zbozi/ML2984.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041264',\\n\",\n       \"  'url': 'http://i01.i.aliimg.com/wsphoto/v0/571075635/72inch-LCD-font-b-FPV-b-font-video-font-b-goggles-b-font-16-9-wide.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041383',\\n\",\n       \"  'url': 'https://aentcdn.azureedge.net/graphics/items/sdimages/a/500/1/8/5/7/1947581.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041417',\\n\",\n       \"  'url': 'http://i.qkme.me/3pyx9q.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041431',\\n\",\n       \"  'url': 'https://pics.showlettwestbooks.com/subdirectory13/29252.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041490',\\n\",\n       \"  'url': 'http://rlv.zcache.com/languid_lavender_and_white_quatrefoil_pattern_planner-rad9f27945d224873aa0197fd2f2148f3_2izru_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041539',\\n\",\n       \"  'url': 'http://rlv.zcache.com.au/earth_high_recycling_team_mouse_pad-ra2cc59afaab04cdba566f436c8838144_x74vi_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041565',\\n\",\n       \"  'url': 'http://yabibliophile.bookblog.io/wp-content/uploads/sites/19/2018/03/34728667.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041571',\\n\",\n       \"  'url': 'https://d2fkddr0p2jbv6.cloudfront.net/render/standard/iEq4S1R42NqAUaAL7qZIvpIIWEN0Ih0jWAJddg6eg5GTawEeEeaMXEKaaYG8DpOU/iphonex-blue-z1-t-did-someone-say-merica.png'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041777',\\n\",\n       \"  'url': 'https://i0.wp.com/www.talkofthestreets.com/wp-content/uploads/2015/08/Plies_Aint_No_Mixtape_Bih-front-large.jpg?resize=300%2C300'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041794',\\n\",\n       \"  'url': 'http://movietvtechgeeks.com/wp-content/uploads/2015/11/draftkings-weekly-update-new-york-ag-2015-nfl-images-600x315.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041885',\\n\",\n       \"  'url': 'http://img.picturequotes.com/2/20/19503/i-refuse-to-be-anything-less-than-successful-quote-1.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041919',\\n\",\n       \"  'url': 'https://blog.billiongraves.com//wp-content/uploads/2019/07/BillionGraves-Summer-Surfing-Contest.png'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041931',\\n\",\n       \"  'url': 'http://d202m5krfqbpi5.cloudfront.net/books/1387744391l/395587.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000041980',\\n\",\n       \"  'url': 'https://ruttendesign.nl/wp-content/uploads/2021/02/Rutten_Design-Logo_WIT-uai-258x285.png'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042019',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/1623/6609/products/image_2174e7d7-1a32-4b28-b769-845e7829c426_480x.jpg?v=1598213129'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042054',\\n\",\n       \"  'url': 'https://stuffhappens.us/wp-content/uploads/2015/02/temperament-quiz.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042128',\\n\",\n       \"  'url': 'https://mir-s3-cdn-cf.behance.net/projects/404/fd703e73333987.Y3JvcCwxMzgwLDEwODAsMjcwLDA.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042173',\\n\",\n       \"  'url': 'https://lh3.googleusercontent.com/BlC-ZWpBlbQvp7L6g0De0IdRn2aIifd53ijzPzULCtGwNKnGBb_uNxWkBwJww3mwCcw'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042195',\\n\",\n       \"  'url': 'https://d39l2hkdp2esp1.cloudfront.net/img/eps/E4153/c/E4153_ff.jpg?20190418194111'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042243',\\n\",\n       \"  'url': 'https://s3.images-iherb.com/bkm/bkm00660/u/1.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042246',\\n\",\n       \"  'url': 'https://a57.foxnews.com/static.foxbusiness.com/foxbusiness.com/content/uploads/2020/08/0/0/AP20211690502320.jpg?ve=1&tl=1'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042297',\\n\",\n       \"  'url': 'http://d.gr-assets.com/books/1348244912l/8343235.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042378',\\n\",\n       \"  'url': 'https://assets.audiomack.com/dj-warface/get-that-money-ft-chris-brown-french-montana-275-275.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042438',\\n\",\n       \"  'url': 'https://reverbraccoon.com/wp-content/uploads/2019/03/Farrow.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042454',\\n\",\n       \"  'url': 'https://deow9bq0xqvbj.cloudfront.net/image-logo/1297595/IMG_3223.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042457',\\n\",\n       \"  'url': 'https://edwardsfss.com/wordpress/wp-content/uploads/Torch-Club-logo.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042459',\\n\",\n       \"  'url': 'https://static.twentyoverten.com/5bb243e4d619867e5feafb99/B1MzoTEhm/image001.png'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042504',\\n\",\n       \"  'url': 'https://images.financialexpress.com/2018/07/binani-3-1-620x413.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042544',\\n\",\n       \"  'url': 'http://cdn.pastemagazine.com/www/articles/flanaganlead.jpeg?635494693631627879'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042559',\\n\",\n       \"  'url': 'https://3.bp.blogspot.com/-ja1x17EpeLw/WEWPBiEKkVI/AAAAAAAAiHI/pMgQReH4N60iZ_Pu_qntGQu6WfrvbQFqACLcB/s400/Slide55.JPG'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042563',\\n\",\n       \"  'url': 'https://images-na.ssl-images-amazon.com/images/S/cmx-images-prod/Series/81693/81693._SX312_QL80_TTD_.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042676',\\n\",\n       \"  'url': 'https://d2g43ubxtnccwi.cloudfront.net/52051_1_medium.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042680',\\n\",\n       \"  'url': 'https://professionalbuildersmerchant.co.uk/wp-content/uploads/2019/08/CPA-Construction-Products-Association.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042691',\\n\",\n       \"  'url': 'https://d33wubrfki0l68.cloudfront.net/fd733c3f2992dd850f3bca5ec5065e19b3de595d/78737/wp-content/uploads/2017/10/how-to-save-all-my-iphone-contacts-into-icloud.png'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042697',\\n\",\n       \"  'url': 'https://images-na.ssl-images-amazon.com/images/I/61XYQjlVq3L.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042736',\\n\",\n       \"  'url': 'https://i2.wp.com/goodsparkgarage.com/wp-content/uploads/2017/04/dead-ace-co-moto-supply-4.jpg?resize=1024%2C683'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042753',\\n\",\n       \"  'url': 'https://websitesandcoffee.com/wp-content/uploads/2012/06/wordpress-com-vs-org-300x281.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042768',\\n\",\n       \"  'url': 'http://images.moviepostershop.com/the-hangover-2-movie-poster-2011-1010698671.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042871',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0001/8032/2355/products/back_3114717b-9c6f-42fa-b785-cb6c8eb70e60_large.png?v=1576418474'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042963',\\n\",\n       \"  'url': 'https://i.chzbgr.com/full/5143192832/hD696F3A9/'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000042999',\\n\",\n       \"  'url': 'http://melbourneletteringclub.com/wp-content/uploads/2016/11/rob_clarke_work_9.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043000',\\n\",\n       \"  'url': 'https://i1.wp.com/motherhoodinmay.com/wp-content/uploads/2017/12/GIFT-GUIDE-2.png?resize=400%2C500&amp;ssl=1'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043009',\\n\",\n       \"  'url': 'https://pokermaniashop.com/1235-home_default/carte-bicycle-magic-double-face-scatola-blu-o-rossa.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043104',\\n\",\n       \"  'url': 'https://cdn-origin.bibliocommons.com/images/CO-PUEBLO/logo.png?1397900483289'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043162',\\n\",\n       \"  'url': 'http://www.tnfarmersbuyersguide.com/images/adv/611/200008611l.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043206',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/51B3gsMN5WL._SL300_.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043221',\\n\",\n       \"  'url': 'https://i0.wp.com/www.amour-des-saveurs.com/wp-content/uploads/2019/03/candy-melts-12ozbright-white.jpg?fit=300%2C300&ssl=1'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043240',\\n\",\n       \"  'url': 'https://f4.bcbits.com/img/a2412680191_16.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043350',\\n\",\n       \"  'url': 'https://i.imgur.com/oirSj.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043368',\\n\",\n       \"  'url': 'http://rlv.zcache.com/dream_mer_mermaid_posters-r5f32bf37eeb947a3a045fb550deaf861_fq24_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043378',\\n\",\n       \"  'url': 'http://g.christianbook.com/dg/product/cbd/f400/20745.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043440',\\n\",\n       \"  'url': 'http://www.quarterrockpress.com/media/k2/items/cache/b91e0a97ce980c6b93c011a7d228a301_Generic.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043702',\\n\",\n       \"  'url': 'https://www.littlebibliophile.com/wp-content/uploads/2017/11/Happy-Childrens-Day-2017-750x350.png'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043706',\\n\",\n       \"  'url': 'http://www.justiceworksltd.org/wp-content/uploads/2010/10/Just-run-round-No-date1-300x300.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043710',\\n\",\n       \"  'url': 'https://stephcalvertart.com/wp-content/uploads/2016/11/shop-no-cavities-club-dentist-12x12canvas-steph-calvert-art-mockup.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043734',\\n\",\n       \"  'url': 'http://cached.imagescaler.hbpl.co.uk/resize/scaleWidth/445/offlinehbpl.hbpl.co.uk/news/OWM/838FC6A7-A116-8248-AB33CF00756FFC6C.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043740',\\n\",\n       \"  'url': 'https://ih1.redbubble.net/image.120238612.6669/raf750x1000075theather_grey.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043791',\\n\",\n       \"  'url': 'https://theimaginationtree.com/wp-content/uploads/2013/01/Red+and+White+Clay+Hearts.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043800',\\n\",\n       \"  'url': 'https://altadenabaptist.hipcast.com/albumart/1003_1618837671.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043805',\\n\",\n       \"  'url': 'https://images-na.ssl-images-amazon.com/images/I/61gMzcNJQPL._SL300_.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043811',\\n\",\n       \"  'url': 'https://wampuscatstudentnews.com/wp-content/uploads/2019/10/Screen-Shot-2019-10-28-at-1.36.16-PM.png'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043936',\\n\",\n       \"  'url': 'https://d3wo5wojvuv7l.cloudfront.net/t_square_limited_320/images.spreaker.com/original/c383719c3af216baf7e072d61dbaed30.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000043980',\\n\",\n       \"  'url': 'https://nerdgeistdotcom.files.wordpress.com/2016/03/10-cloverfield-lane-jj-abrams.jpg?w=639&'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044052',\\n\",\n       \"  'url': 'http://t.qkme.me/3uigwj.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044153',\\n\",\n       \"  'url': 'http://ecx.images-amazon.com/images/I/51Z2RBHVCYL._SL500_AA300_.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044332',\\n\",\n       \"  'url': 'https://cdn.bleacherreport.net/images_root/slides/photos/000/414/385/95781002_original.jpg?1285605227'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044347',\\n\",\n       \"  'url': 'http://kelleykeller.com/wp-content/uploads/contracts-must-be-in-writing-800x300.png'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044370',\\n\",\n       \"  'url': 'https://cdn11.bigcommerce.com/s-omo2hp/images/stencil/1280x1280/products/2042/8954/bearbook6__94997.1479446314.jpg?c=2'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044374',\\n\",\n       \"  'url': 'http://i.ytimg.com/vi/zdOSOQWPtEQ/0.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044416',\\n\",\n       \"  'url': 'https://assets1.ignimgs.com/thumbs/2016/06/08/624fce02be8b446b0b58bb5a6d356b30-1465426762/frame_0000.jpg?fit=bounds&amp;dpr=1&amp;quality=75&amp;crop=16%3A9&amp;width=300&amp;format=pjpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044443',\\n\",\n       \"  'url': 'https://sr20.driftworks.com/media/catalog/product/cache/1/thumbnail/180x/9df78eab33525d08d6e5fb8d27136e95/i/m/image_JR181895XX2067HB_11287_1_4.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044448',\\n\",\n       \"  'url': 'https://i.pinimg.com/736x/f5/80/49/f5804962db4f0edf4828f029c032f570.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044480',\\n\",\n       \"  'url': 'https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.com%2Fimages%2F55815573%2F267423464591%2F1%2Foriginal.20190128-100832?w=512&auto=compress&rect=0%2C134%2C804%2C402&s=bf6d28ff17d65fcee3ddb0355d608cf3'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044623',\\n\",\n       \"  'url': 'https://xark.typepad.com/.a/6a00d8341c5d3453ef0167616ce05e970b-500wi'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044648',\\n\",\n       \"  'url': 'https://img1.od-cdn.com/ImageType-400/9161-1/D34/1EF/E2/%7BD341EFE2-F037-4CD7-886D-2B11C0C56B64%7DImg400.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044680',\\n\",\n       \"  'url': 'http://androidinfo.hu/wp-content/uploads/2017/05/android-apple2.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044809',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.com/image-server/v1/compositions/111242357/views/1width=300height=300appearanceId=1version=1460621206.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044879',\\n\",\n       \"  'url': 'http://labrador.se/wp-content/uploads/2013/07/shop_maryonettes_evilcoast.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044895',\\n\",\n       \"  'url': 'http://vafloc02.s3.amazonaws.com/isyn/images/f117/img-1708117-m.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000044906',\\n\",\n       \"  'url': 'https://images.squarespace-cdn.com/content/57ffc1d9414fb54338533658/1482170872136-OSVDHYNPO8KD5ZFII54Y/Oberg+Logo+-+From+Main+St.+Graphics.png?content-type=image%2Fpng'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045014',\\n\",\n       \"  'url': 'http://www.indigoinkprint.com/wp-content/uploads/2013/10/team-page.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045106',\\n\",\n       \"  'url': 'http://images.kitbag.com/mufc-130315.jpg?width=170&amp;height=170&amp;quality=95'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045124',\\n\",\n       \"  'url': 'http://rlv.zcache.co.uk/athlete_photo_insert_graduation_party_a_invitation-r886af006e6d94ea3b7cdf36cc3ff69e8_imtqg_8byvr_324.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045211',\\n\",\n       \"  'url': 'https://lacuillereenbois.fr/wp-content/uploads/2018/11/20181126_fruits_grolet-2-1140x758.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045215',\\n\",\n       \"  'url': 'https://images.tol-expo.it/cloud/logos/o/8VNL1F0U56HU_iwbank-private-investments.png?d=0&v=3970'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045323',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/0549/1389/2517/files/Pretty_Girl_Jewels_Logo_Metalic.png?v=1614445832'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045327',\\n\",\n       \"  'url': 'https://www.product-reviews.net/wp-content/uploads/clash-of-clans-update-today.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045362',\\n\",\n       \"  'url': 'http://www.emptykingdom.com/wp-content/uploads/2012/01/EKI_Heidi-Taillefer-600x395.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045379',\\n\",\n       \"  'url': 'http://i.ytimg.com/vi/-kjWJjtMwuQ/0.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045419',\\n\",\n       \"  'url': 'https://res.9appsinstall.com/group1/M00/8B/11/p4YBAFdA1wKAdU2VAAA1K2_Xpy8985.png'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045432',\\n\",\n       \"  'url': 'https://smlycdn.akamaized.net/products/270x270-fill/166e2d3c7b/3e535e9a4ddd692c4d56000baba61919e8a13d54.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045435',\\n\",\n       \"  'url': 'https://i1.wp.com/thebirthdaybest.com/wp-content/uploads/2020/11/25th-bday-quotes.jpg?resize=640%2C402&ssl=1'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045479',\\n\",\n       \"  'url': 'https://assets.burberry.com/is/image/Burberryltd/266c256165384d3b3596d9cfcdf088e1d6108c14.jpg?$BBY_V2_SL_4X3$&wid=760&hei=570'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045480',\\n\",\n       \"  'url': 'https://cdn.shopify.com/s/files/1/2770/5310/products/CART67995_87eb210c-ce3c-4509-8008-2afd03fa751e_200x200@2x.jpg?v=1571732568'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045482',\\n\",\n       \"  'url': 'https://blog.entheosweb.com/wp-content/uploads/2012/03/socialicons4.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045536',\\n\",\n       \"  'url': 'https://i.imgflip.com/3rtwts.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045741',\\n\",\n       \"  'url': 'http://a.mktgcdn.com/p/L3LHun9mgq8tnWEek4b2A-p0jExDnvcjt3rMkMI9mtE/500x500.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045774',\\n\",\n       \"  'url': 'http://i0.wp.com/hypebeast.com/image/2012/08/carhartt-x-vans-fall-2012-old-skool-camo-3.jpg?w=930'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045809',\\n\",\n       \"  'url': 'https://cdn.iset.io/assets/55084/produtos/175/brc074_thumb_atlhetica_best_whey_900g_original.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045923',\\n\",\n       \"  'url': 'https://diyprojects.com/wp-content/uploads/2014/01/where-to-get-pallets-600x384.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045953',\\n\",\n       \"  'url': 'https://cdn62.zvooq.com/pic?type=release&id=1033344&size=300x300&ext=jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045957',\\n\",\n       \"  'url': 'http://www.modorent.it/actionphp/thumb.output.php?src=Prodotti%2FBEA_18_PARTNER.jpg&wmax=300&hmax=300&quality=80&bgcol=FFFFFF&type=2&sid=cb10c60c8841161d6b3b1f00efc9723b'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045978',\\n\",\n       \"  'url': 'https://34hhymr52r9delys55ykaema-wpengine.netdna-ssl.com/wp-content/uploads/unnamed-24-300x300.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000045998',\\n\",\n       \"  'url': 'https://www.marcheleos.com/media/catalog/product/cache/1/small_image/295x295/9df78eab33525d08d6e5fb8d27136e95/3/5/355644.jpeg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000046076',\\n\",\n       \"  'url': 'https://artwork-cdn.7static.com/static/img/sleeveart/00/058/137/0005813722_350.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000046079',\\n\",\n       \"  'url': 'https://image.spreadshirtmedia.net/image-server/v1/compositions/141961824/views/1width=300height=300appearanceId=231backgroundColor=E8E8E8version=1472100673/offline-is-the-new-luxury-t-shirts-womens-premium-t-shirt.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000046081',\\n\",\n       \"  'url': 'https://knowth.com/images-kn/knowth-k78-700.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000046103',\\n\",\n       \"  'url': 'https://d14wch1fpzoq5q.cloudfront.net/2017/05/16155333/C_VHRc3XkAIe8Le-945x600.jpg'},\\n\",\n       \" {'ex_idx': '00004',\\n\",\n       \"  'in_idx': '000046229',\\n\",\n       \"  'url': 'https://proassets.monopile.cloud/43845/1ed51390512f6426baece897500f518a_m.jpg'},\\n\",\n       \" ...]\"\n      ]\n     },\n     \"execution_count\": 4,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"url_lst.sort(key = lambda x: int(x[\\\"in_idx\\\"]))\\n\",\n    \"url_lst\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"url_txt_path = ospj(data_root, \\\"urls.txt\\\")\\n\",\n    \"os.system(f\\\"rm {url_txt_path}\\\")\\n\",\n    \"urls = []\\n\",\n    \"for item in tqdm(url_lst):\\n\",\n    \"    ex_idx = item[\\\"ex_idx\\\"]\\n\",\n    \"    in_idx = item[\\\"in_idx\\\"]\\n\",\n    \"    url = item[\\\"url\\\"]\\n\",\n    \"    urls.append(url+\\\"\\\\n\\\")\\n\",\n    \"\\n\",\n    \"with open(url_txt_path, \\\"w\\\") as fp:\\n\",\n    \"    fp.writelines(urls)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"print(f\\\"img2dataset --url_list={url_txt_path} --output_folder={cache_root} --thread_count=64  --resize_mode=no\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"pointer = 0\\n\",\n    \"total = 0\\n\",\n    \"ex_dirs = sorted(glob.glob(ospj(cache_root, \\\"?????\\\")))\\n\",\n    \"for ex_dir in tqdm(ex_dirs):\\n\",\n    \"    img_paths = sorted(glob.glob(ospj(ex_dir, \\\"*.jpg\\\")))\\n\",\n    \"    info_paths = sorted(glob.glob(ospj(ex_dir, \\\"*.json\\\")))\\n\",\n    \"    total += len(info_paths)\\n\",\n    \"    for img_path in img_paths:\\n\",\n    \"        name = img_path.split(os.sep)[-1].split(\\\".\\\")[0]\\n\",\n    \"        with open(ospj(ex_dir, f\\\"{name}.json\\\"), \\\"rb\\\") as fp:\\n\",\n    \"            info = json.load(fp)\\n\",\n    \"\\n\",\n    \"        while info[\\\"url\\\"] != url_lst[pointer][\\\"url\\\"]:\\n\",\n    \"            pointer += 1\\n\",\n    \"        if pointer >= len(url_lst):\\n\",\n    \"            print(\\\"pointer error\\\")\\n\",\n    \"        ex_idx = url_lst[pointer][\\\"ex_idx\\\"]\\n\",\n    \"        in_idx = url_lst[pointer][\\\"in_idx\\\"]\\n\",\n    \"        os.makedirs(ospj(image_root, ex_idx), exist_ok=True)\\n\",\n    \"        os.rename(img_path, ospj(image_root, ex_idx, f\\\"{in_idx}.jpg\\\"))\\n\",\n    \"\\n\",\n    \"print(f\\\"Total num: {total}\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"img_dirs = sorted(glob.glob(ospj(image_root, \\\"?????\\\")))\\n\",\n    \"div = int(len(img_dirs) * 0.95)\\n\",\n    \"train_total = 0\\n\",\n    \"val_total = 0\\n\",\n    \"for i, img_dir in enumerate(tqdm(img_dirs)):\\n\",\n    \"    stype = \\\"train\\\" if i < div else \\\"val\\\" \\n\",\n    \"    img_paths = sorted(glob.glob(ospj(img_dir, \\\"*.jpg\\\")))\\n\",\n    \"    for img_path in img_paths:\\n\",\n    \"        if stype == \\\"train\\\":\\n\",\n    \"            train_total += 1\\n\",\n    \"        else:\\n\",\n    \"            val_total += 1\\n\",\n    \"        ex_idx = img_dir.split(os.sep)[-1]\\n\",\n    \"        in_idx = img_path.split(os.sep)[-1].split(\\\".\\\")[0]\\n\",\n    \"        anno_dir = ospj(anno_root, ex_idx, in_idx)\\n\",\n    \"        target_dir = ospj(data_root, stype, in_idx)\\n\",\n    \"        target_img_path = ospj(target_dir, 'image.jpg')\\n\",\n    \"        if os.path.exists(target_img_path):\\n\",\n    \"            continue\\n\",\n    \"        os.system(f\\\"cp -r {anno_dir} {target_dir}\\\")\\n\",\n    \"        os.system(f\\\"cp {img_path} {target_img_path}\\\")\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"print(f\\\"div {train_total} train samples and {val_total} val samples\\\")\\n\",\n    \"        \"\n   ]\n  }\n ],\n \"metadata\": {\n  \"kernelspec\": {\n   \"display_name\": \"sdxl\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.11.4\"\n  },\n  \"orig_nbformat\": 4\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "sgm/__init__.py",
    "content": "from .models import AutoencodingEngine, DiffusionEngine\nfrom .util import instantiate_from_config\n"
  },
  {
    "path": "sgm/lr_scheduler.py",
    "content": "import numpy as np\n\n\nclass LambdaWarmUpCosineScheduler:\n    \"\"\"\n    note: use with a base_lr of 1.0\n    \"\"\"\n\n    def __init__(\n        self,\n        warm_up_steps,\n        lr_min,\n        lr_max,\n        lr_start,\n        max_decay_steps,\n        verbosity_interval=0,\n    ):\n        self.lr_warm_up_steps = warm_up_steps\n        self.lr_start = lr_start\n        self.lr_min = lr_min\n        self.lr_max = lr_max\n        self.lr_max_decay_steps = max_decay_steps\n        self.last_lr = 0.0\n        self.verbosity_interval = verbosity_interval\n\n    def schedule(self, n, **kwargs):\n        if self.verbosity_interval > 0:\n            if n % self.verbosity_interval == 0:\n                print(f\"current step: {n}, recent lr-multiplier: {self.last_lr}\")\n        if n < self.lr_warm_up_steps:\n            lr = (\n                self.lr_max - self.lr_start\n            ) / self.lr_warm_up_steps * n + self.lr_start\n            self.last_lr = lr\n            return lr\n        else:\n            t = (n - self.lr_warm_up_steps) / (\n                self.lr_max_decay_steps - self.lr_warm_up_steps\n            )\n            t = min(t, 1.0)\n            lr = self.lr_min + 0.5 * (self.lr_max - self.lr_min) * (\n                1 + np.cos(t * np.pi)\n            )\n            self.last_lr = lr\n            return lr\n\n    def __call__(self, n, **kwargs):\n        return self.schedule(n, **kwargs)\n\n\nclass LambdaWarmUpCosineScheduler2:\n    \"\"\"\n    supports repeated iterations, configurable via lists\n    note: use with a base_lr of 1.0.\n    \"\"\"\n\n    def __init__(\n        self, warm_up_steps, f_min, f_max, f_start, cycle_lengths, verbosity_interval=0\n    ):\n        assert (\n            len(warm_up_steps)\n            == len(f_min)\n            == len(f_max)\n            == len(f_start)\n            == len(cycle_lengths)\n        )\n        self.lr_warm_up_steps = warm_up_steps\n        self.f_start = f_start\n        self.f_min = f_min\n        self.f_max = f_max\n        self.cycle_lengths = cycle_lengths\n        self.cum_cycles = np.cumsum([0] + list(self.cycle_lengths))\n        self.last_f = 0.0\n        self.verbosity_interval = verbosity_interval\n\n    def find_in_interval(self, n):\n        interval = 0\n        for cl in self.cum_cycles[1:]:\n            if n <= cl:\n                return interval\n            interval += 1\n\n    def schedule(self, n, **kwargs):\n        cycle = self.find_in_interval(n)\n        n = n - self.cum_cycles[cycle]\n        if self.verbosity_interval > 0:\n            if n % self.verbosity_interval == 0:\n                print(\n                    f\"current step: {n}, recent lr-multiplier: {self.last_f}, \"\n                    f\"current cycle {cycle}\"\n                )\n        if n < self.lr_warm_up_steps[cycle]:\n            f = (self.f_max[cycle] - self.f_start[cycle]) / self.lr_warm_up_steps[\n                cycle\n            ] * n + self.f_start[cycle]\n            self.last_f = f\n            return f\n        else:\n            t = (n - self.lr_warm_up_steps[cycle]) / (\n                self.cycle_lengths[cycle] - self.lr_warm_up_steps[cycle]\n            )\n            t = min(t, 1.0)\n            f = self.f_min[cycle] + 0.5 * (self.f_max[cycle] - self.f_min[cycle]) * (\n                1 + np.cos(t * np.pi)\n            )\n            self.last_f = f\n            return f\n\n    def __call__(self, n, **kwargs):\n        return self.schedule(n, **kwargs)\n\n\nclass LambdaLinearScheduler(LambdaWarmUpCosineScheduler2):\n    def schedule(self, n, **kwargs):\n        cycle = self.find_in_interval(n)\n        n = n - self.cum_cycles[cycle]\n        if self.verbosity_interval > 0:\n            if n % self.verbosity_interval == 0:\n                print(\n                    f\"current step: {n}, recent lr-multiplier: {self.last_f}, \"\n                    f\"current cycle {cycle}\"\n                )\n\n        if n < self.lr_warm_up_steps[cycle]:\n            f = (self.f_max[cycle] - self.f_start[cycle]) / self.lr_warm_up_steps[\n                cycle\n            ] * n + self.f_start[cycle]\n            self.last_f = f\n            return f\n        else:\n            f = self.f_min[cycle] + (self.f_max[cycle] - self.f_min[cycle]) * (\n                self.cycle_lengths[cycle] - n\n            ) / (self.cycle_lengths[cycle])\n            self.last_f = f\n            return f\n"
  },
  {
    "path": "sgm/models/__init__.py",
    "content": "from .autoencoder import AutoencodingEngine\nfrom .diffusion import DiffusionEngine\n"
  },
  {
    "path": "sgm/models/autoencoder.py",
    "content": "import re\nfrom abc import abstractmethod\nfrom contextlib import contextmanager\nfrom typing import Any, Dict, Tuple, Union\n\nimport pytorch_lightning as pl\nimport torch\nfrom omegaconf import ListConfig\nfrom packaging import version\nfrom safetensors.torch import load_file as load_safetensors\n\nfrom ..modules.diffusionmodules.model import Decoder, Encoder\nfrom ..modules.distributions.distributions import DiagonalGaussianDistribution\nfrom ..modules.ema import LitEma\nfrom ..util import default, get_obj_from_str, instantiate_from_config\n\n\nclass AbstractAutoencoder(pl.LightningModule):\n    \"\"\"\n    This is the base class for all autoencoders, including image autoencoders, image autoencoders with discriminators,\n    unCLIP models, etc. Hence, it is fairly general, and specific features\n    (e.g. discriminator training, encoding, decoding) must be implemented in subclasses.\n    \"\"\"\n\n    def __init__(\n        self,\n        ema_decay: Union[None, float] = None,\n        monitor: Union[None, str] = None,\n        input_key: str = \"jpg\",\n        ckpt_path: Union[None, str] = None,\n        ignore_keys: Union[Tuple, list, ListConfig] = (),\n    ):\n        super().__init__()\n        self.input_key = input_key\n        self.use_ema = ema_decay is not None\n        if monitor is not None:\n            self.monitor = monitor\n\n        if self.use_ema:\n            self.model_ema = LitEma(self, decay=ema_decay)\n            print(f\"Keeping EMAs of {len(list(self.model_ema.buffers()))}.\")\n\n        if ckpt_path is not None:\n            self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys)\n\n        if version.parse(torch.__version__) >= version.parse(\"2.0.0\"):\n            self.automatic_optimization = False\n\n    def init_from_ckpt(\n        self, path: str, ignore_keys: Union[Tuple, list, ListConfig] = tuple()\n    ) -> None:\n        if path.endswith(\"ckpt\"):\n            sd = torch.load(path, map_location=\"cpu\")[\"state_dict\"]\n        elif path.endswith(\"safetensors\"):\n            sd = load_safetensors(path)\n        else:\n            raise NotImplementedError\n\n        keys = list(sd.keys())\n        for k in keys:\n            for ik in ignore_keys:\n                if re.match(ik, k):\n                    print(\"Deleting key {} from state_dict.\".format(k))\n                    del sd[k]\n        missing, unexpected = self.load_state_dict(sd, strict=False)\n        print(\n            f\"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys\"\n        )\n        if len(missing) > 0:\n            print(f\"Missing Keys: {missing}\")\n        if len(unexpected) > 0:\n            print(f\"Unexpected Keys: {unexpected}\")\n\n    @abstractmethod\n    def get_input(self, batch) -> Any:\n        raise NotImplementedError()\n\n    def on_train_batch_end(self, *args, **kwargs):\n        # for EMA computation\n        if self.use_ema:\n            self.model_ema(self)\n\n    @contextmanager\n    def ema_scope(self, context=None):\n        if self.use_ema:\n            self.model_ema.store(self.parameters())\n            self.model_ema.copy_to(self)\n            if context is not None:\n                print(f\"{context}: Switched to EMA weights\")\n        try:\n            yield None\n        finally:\n            if self.use_ema:\n                self.model_ema.restore(self.parameters())\n                if context is not None:\n                    print(f\"{context}: Restored training weights\")\n\n    @abstractmethod\n    def encode(self, *args, **kwargs) -> torch.Tensor:\n        raise NotImplementedError(\"encode()-method of abstract base class called\")\n\n    @abstractmethod\n    def decode(self, *args, **kwargs) -> torch.Tensor:\n        raise NotImplementedError(\"decode()-method of abstract base class called\")\n\n    def instantiate_optimizer_from_config(self, params, lr, cfg):\n        print(f\"loading >>> {cfg['target']} <<< optimizer from config\")\n        return get_obj_from_str(cfg[\"target\"])(\n            params, lr=lr, **cfg.get(\"params\", dict())\n        )\n\n    def configure_optimizers(self) -> Any:\n        raise NotImplementedError()\n\n\nclass AutoencodingEngine(AbstractAutoencoder):\n    \"\"\"\n    Base class for all image autoencoders that we train, like VQGAN or AutoencoderKL\n    (we also restore them explicitly as special cases for legacy reasons).\n    Regularizations such as KL or VQ are moved to the regularizer class.\n    \"\"\"\n\n    def __init__(\n        self,\n        *args,\n        encoder_config: Dict,\n        decoder_config: Dict,\n        loss_config: Dict,\n        regularizer_config: Dict,\n        optimizer_config: Union[Dict, None] = None,\n        lr_g_factor: float = 1.0,\n        **kwargs,\n    ):\n        super().__init__(*args, **kwargs)\n        # todo: add options to freeze encoder/decoder\n        self.encoder = instantiate_from_config(encoder_config)\n        self.decoder = instantiate_from_config(decoder_config)\n        self.loss = instantiate_from_config(loss_config)\n        self.regularization = instantiate_from_config(regularizer_config)\n        self.optimizer_config = default(\n            optimizer_config, {\"target\": \"torch.optim.Adam\"}\n        )\n        self.lr_g_factor = lr_g_factor\n\n    def get_input(self, batch: Dict) -> torch.Tensor:\n        # assuming unified data format, dataloader returns a dict.\n        # image tensors should be scaled to -1 ... 1 and in channels-first format (e.g., bchw instead if bhwc)\n        return batch[self.input_key]\n\n    def get_autoencoder_params(self) -> list:\n        params = (\n            list(self.encoder.parameters())\n            + list(self.decoder.parameters())\n            + list(self.regularization.get_trainable_parameters())\n            + list(self.loss.get_trainable_autoencoder_parameters())\n        )\n        return params\n\n    def get_discriminator_params(self) -> list:\n        params = list(self.loss.get_trainable_parameters())  # e.g., discriminator\n        return params\n\n    def get_last_layer(self):\n        return self.decoder.get_last_layer()\n\n    def encode(self, x: Any, return_reg_log: bool = False) -> Any:\n        z = self.encoder(x)\n        z, reg_log = self.regularization(z)\n        if return_reg_log:\n            return z, reg_log\n        return z\n\n    def decode(self, z: Any) -> torch.Tensor:\n        x = self.decoder(z)\n        return x\n\n    def forward(self, x: Any) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:\n        z, reg_log = self.encode(x, return_reg_log=True)\n        dec = self.decode(z)\n        return z, dec, reg_log\n\n    def training_step(self, batch, batch_idx, optimizer_idx) -> Any:\n        x = self.get_input(batch)\n        z, xrec, regularization_log = self(x)\n\n        if optimizer_idx == 0:\n            # autoencode\n            aeloss, log_dict_ae = self.loss(\n                regularization_log,\n                x,\n                xrec,\n                optimizer_idx,\n                self.global_step,\n                last_layer=self.get_last_layer(),\n                split=\"train\",\n            )\n\n            self.log_dict(\n                log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=True\n            )\n            return aeloss\n\n        if optimizer_idx == 1:\n            # discriminator\n            discloss, log_dict_disc = self.loss(\n                regularization_log,\n                x,\n                xrec,\n                optimizer_idx,\n                self.global_step,\n                last_layer=self.get_last_layer(),\n                split=\"train\",\n            )\n            self.log_dict(\n                log_dict_disc, prog_bar=False, logger=True, on_step=True, on_epoch=True\n            )\n            return discloss\n\n    def validation_step(self, batch, batch_idx) -> Dict:\n        log_dict = self._validation_step(batch, batch_idx)\n        with self.ema_scope():\n            log_dict_ema = self._validation_step(batch, batch_idx, postfix=\"_ema\")\n            log_dict.update(log_dict_ema)\n        return log_dict\n\n    def _validation_step(self, batch, batch_idx, postfix=\"\") -> Dict:\n        x = self.get_input(batch)\n\n        z, xrec, regularization_log = self(x)\n        aeloss, log_dict_ae = self.loss(\n            regularization_log,\n            x,\n            xrec,\n            0,\n            self.global_step,\n            last_layer=self.get_last_layer(),\n            split=\"val\" + postfix,\n        )\n\n        discloss, log_dict_disc = self.loss(\n            regularization_log,\n            x,\n            xrec,\n            1,\n            self.global_step,\n            last_layer=self.get_last_layer(),\n            split=\"val\" + postfix,\n        )\n        self.log(f\"val{postfix}/rec_loss\", log_dict_ae[f\"val{postfix}/rec_loss\"])\n        log_dict_ae.update(log_dict_disc)\n        self.log_dict(log_dict_ae)\n        return log_dict_ae\n\n    def configure_optimizers(self) -> Any:\n        ae_params = self.get_autoencoder_params()\n        disc_params = self.get_discriminator_params()\n\n        opt_ae = self.instantiate_optimizer_from_config(\n            ae_params,\n            default(self.lr_g_factor, 1.0) * self.learning_rate,\n            self.optimizer_config,\n        )\n        opt_disc = self.instantiate_optimizer_from_config(\n            disc_params, self.learning_rate, self.optimizer_config\n        )\n\n        return [opt_ae, opt_disc], []\n\n    @torch.no_grad()\n    def log_images(self, batch: Dict, **kwargs) -> Dict:\n        log = dict()\n        x = self.get_input(batch)\n        _, xrec, _ = self(x)\n        log[\"inputs\"] = x\n        log[\"reconstructions\"] = xrec\n        with self.ema_scope():\n            _, xrec_ema, _ = self(x)\n            log[\"reconstructions_ema\"] = xrec_ema\n        return log\n\n\nclass AutoencoderKL(AutoencodingEngine):\n    def __init__(self, embed_dim: int, **kwargs):\n        ddconfig = kwargs.pop(\"ddconfig\")\n        ckpt_path = kwargs.pop(\"ckpt_path\", None)\n        ignore_keys = kwargs.pop(\"ignore_keys\", ())\n        super().__init__(\n            encoder_config={\"target\": \"torch.nn.Identity\"},\n            decoder_config={\"target\": \"torch.nn.Identity\"},\n            regularizer_config={\"target\": \"torch.nn.Identity\"},\n            loss_config=kwargs.pop(\"lossconfig\"),\n            **kwargs,\n        )\n        assert ddconfig[\"double_z\"]\n        self.encoder = Encoder(**ddconfig)\n        self.decoder = Decoder(**ddconfig)\n        self.quant_conv = torch.nn.Conv2d(2 * ddconfig[\"z_channels\"], 2 * embed_dim, 1)\n        self.post_quant_conv = torch.nn.Conv2d(embed_dim, ddconfig[\"z_channels\"], 1)\n        self.embed_dim = embed_dim\n\n        if ckpt_path is not None:\n            self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys)\n\n    def encode(self, x):\n        assert (\n            not self.training\n        ), f\"{self.__class__.__name__} only supports inference currently\"\n        h = self.encoder(x)\n        moments = self.quant_conv(h) \n        posterior = DiagonalGaussianDistribution(moments)\n        return posterior\n\n    def decode(self, z, **decoder_kwargs):\n        z = self.post_quant_conv(z)\n        dec = self.decoder(z, **decoder_kwargs)\n        return dec\n\n\nclass AutoencoderKLInferenceWrapper(AutoencoderKL):\n    def encode(self, x):\n        return super().encode(x).sample()\n\n\nclass IdentityFirstStage(AbstractAutoencoder):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n    def get_input(self, x: Any) -> Any:\n        return x\n\n    def encode(self, x: Any, *args, **kwargs) -> Any:\n        return x\n\n    def decode(self, x: Any, *args, **kwargs) -> Any:\n        return x\n"
  },
  {
    "path": "sgm/models/diffusion.py",
    "content": "from contextlib import contextmanager\nfrom typing import Any, Dict, List, Tuple, Union\n\nimport pytorch_lightning as pl\nimport torch\nfrom omegaconf import ListConfig, OmegaConf\nfrom safetensors.torch import load_file as load_safetensors\nfrom torch.optim.lr_scheduler import LambdaLR\n\nfrom ..modules import UNCONDITIONAL_CONFIG\nfrom ..modules.diffusionmodules.wrappers import OPENAIUNETWRAPPER\nfrom ..modules.ema import LitEma\nfrom ..util import (\n    default,\n    disabled_train,\n    get_obj_from_str,\n    instantiate_from_config,\n    log_txt_as_img,\n)\n\n\nclass DiffusionEngine(pl.LightningModule):\n    def __init__(\n        self,\n        network_config,\n        denoiser_config,\n        first_stage_config,\n        conditioner_config: Union[None, Dict, ListConfig, OmegaConf] = None,\n        sampler_config: Union[None, Dict, ListConfig, OmegaConf] = None,\n        optimizer_config: Union[None, Dict, ListConfig, OmegaConf] = None,\n        scheduler_config: Union[None, Dict, ListConfig, OmegaConf] = None,\n        loss_fn_config: Union[None, Dict, ListConfig, OmegaConf] = None,\n        network_wrapper: Union[None, str] = None,\n        ckpt_path: Union[None, str] = None,\n        use_ema: bool = False,\n        ema_decay_rate: float = 0.9999,\n        scale_factor: float = 1.0,\n        disable_first_stage_autocast=False,\n        input_key: str = \"jpg\",\n        log_keys: Union[List, None] = None,\n        no_cond_log: bool = False,\n        compile_model: bool = False,\n        opt_keys: Union[List, None] = None\n    ):\n        super().__init__()\n        self.opt_keys = opt_keys\n        self.log_keys = log_keys\n        self.input_key = input_key\n        self.optimizer_config = default(\n            optimizer_config, {\"target\": \"torch.optim.AdamW\"}\n        )\n        model = instantiate_from_config(network_config)\n        self.model = get_obj_from_str(default(network_wrapper, OPENAIUNETWRAPPER))(\n            model, compile_model=compile_model\n        )\n\n        self.denoiser = instantiate_from_config(denoiser_config)\n        self.sampler = (\n            instantiate_from_config(sampler_config)\n            if sampler_config is not None\n            else None\n        )\n        self.conditioner = instantiate_from_config(\n            default(conditioner_config, UNCONDITIONAL_CONFIG)\n        )\n        self.scheduler_config = scheduler_config\n        self._init_first_stage(first_stage_config)\n\n        self.loss_fn = (\n            instantiate_from_config(loss_fn_config)\n            if loss_fn_config is not None\n            else None\n        )\n\n        self.use_ema = use_ema\n        if self.use_ema:\n            self.model_ema = LitEma(self.model, decay=ema_decay_rate)\n            print(f\"Keeping EMAs of {len(list(self.model_ema.buffers()))}.\")\n\n        self.scale_factor = scale_factor\n        self.disable_first_stage_autocast = disable_first_stage_autocast\n        self.no_cond_log = no_cond_log\n\n        if ckpt_path is not None:\n            self.init_from_ckpt(ckpt_path)\n\n    def init_from_ckpt(\n        self,\n        path: str,\n    ) -> None:\n        if path.endswith(\"ckpt\"):\n            sd = torch.load(path, map_location=\"cpu\")[\"state_dict\"]\n        elif path.endswith(\"safetensors\"):\n            sd = load_safetensors(path)\n        else:\n            raise NotImplementedError\n\n        missing, unexpected = self.load_state_dict(sd, strict=False)\n        print(\n            f\"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys\"\n        )\n        if len(missing) > 0:\n            print(f\"Missing Keys: {missing}\")\n        if len(unexpected) > 0:\n            print(f\"Unexpected Keys: {unexpected}\")\n\n    def freeze(self):\n\n        for param in self.parameters():\n            param.requires_grad_(False)\n            \n    def _init_first_stage(self, config):\n        model = instantiate_from_config(config).eval()\n        model.train = disabled_train\n        for param in model.parameters():\n            param.requires_grad = False\n        self.first_stage_model = model\n\n    def get_input(self, batch):\n        # assuming unified data format, dataloader returns a dict.\n        # image tensors should be scaled to -1 ... 1 and in bchw format\n        return batch[self.input_key]\n\n    @torch.no_grad()\n    def decode_first_stage(self, z):\n        z = 1.0 / self.scale_factor * z\n        with torch.autocast(\"cuda\", enabled=not self.disable_first_stage_autocast):\n            out = self.first_stage_model.decode(z)\n        return out\n\n    @torch.no_grad()\n    def encode_first_stage(self, x):\n        with torch.autocast(\"cuda\", enabled=not self.disable_first_stage_autocast):\n            z = self.first_stage_model.encode(x)\n        z = self.scale_factor * z\n        return z\n\n    def forward(self, x, batch):\n\n        loss, loss_dict = self.loss_fn(self.model, self.denoiser, self.conditioner, x, batch, self.first_stage_model, self.scale_factor)\n\n        return loss, loss_dict\n\n    def shared_step(self, batch: Dict) -> Any:\n        x = self.get_input(batch)\n        x = self.encode_first_stage(x)\n        batch[\"global_step\"] = self.global_step\n        loss, loss_dict = self(x, batch)\n        return loss, loss_dict\n\n    def training_step(self, batch, batch_idx):\n        loss, loss_dict = self.shared_step(batch)\n\n        self.log_dict(\n            loss_dict, prog_bar=True, logger=True, on_step=True, on_epoch=False\n        )\n\n        self.log(\n            \"global_step\",\n            float(self.global_step),\n            prog_bar=True,\n            logger=True,\n            on_step=True,\n            on_epoch=False,\n        )\n\n        lr = self.optimizers().param_groups[0][\"lr\"]\n        self.log(\n            \"lr_abs\", lr, prog_bar=True, logger=True, on_step=True, on_epoch=False\n        )\n\n        return loss\n\n    def on_train_start(self, *args, **kwargs):\n        if self.sampler is None or self.loss_fn is None:\n            raise ValueError(\"Sampler and loss function need to be set for training.\")\n\n    def on_train_batch_end(self, *args, **kwargs):\n        if self.use_ema:\n            self.model_ema(self.model)\n\n    @contextmanager\n    def ema_scope(self, context=None):\n        if self.use_ema:\n            self.model_ema.store(self.model.parameters())\n            self.model_ema.copy_to(self.model)\n            if context is not None:\n                print(f\"{context}: Switched to EMA weights\")\n        try:\n            yield None\n        finally:\n            if self.use_ema:\n                self.model_ema.restore(self.model.parameters())\n                if context is not None:\n                    print(f\"{context}: Restored training weights\")\n\n    def instantiate_optimizer_from_config(self, params, lr, cfg):\n        return get_obj_from_str(cfg[\"target\"])(\n            params, lr=lr, **cfg.get(\"params\", dict())\n        )\n\n    def configure_optimizers(self):\n        lr = self.learning_rate\n        params = []\n        print(\"Trainable parameter list: \")\n        print(\"-\"*20)\n        for name, param in self.model.named_parameters():\n            if any([key in name for key in self.opt_keys]):\n                params.append(param)\n                print(name)\n            else:\n                param.requires_grad_(False)\n        for embedder in self.conditioner.embedders:\n            if embedder.is_trainable:\n                for name, param in embedder.named_parameters():\n                    params.append(param)\n                    print(name)\n        print(\"-\"*20)\n        opt = self.instantiate_optimizer_from_config(params, lr, self.optimizer_config)\n        scheduler = torch.optim.lr_scheduler.LambdaLR(opt, lr_lambda=lambda epoch: 0.95**epoch)\n\n        return [opt], scheduler\n\n    @torch.no_grad()\n    def sample(\n        self,\n        cond: Dict,\n        uc: Union[Dict, None] = None,\n        batch_size: int = 16,\n        shape: Union[None, Tuple, List] = None,\n        **kwargs,\n    ):\n        randn = torch.randn(batch_size, *shape).to(self.device)\n\n        denoiser = lambda input, sigma, c: self.denoiser(\n            self.model, input, sigma, c, **kwargs\n        )\n        samples = self.sampler(denoiser, randn, cond, uc=uc)\n        return samples\n\n    @torch.no_grad()\n    def log_conditionings(self, batch: Dict, n: int) -> Dict:\n        \"\"\"\n        Defines heuristics to log different conditionings.\n        These can be lists of strings (text-to-image), tensors, ints, ...\n        \"\"\"\n        image_h, image_w = batch[self.input_key].shape[2:]\n        log = dict()\n\n        for embedder in self.conditioner.embedders:\n            if (\n                (self.log_keys is None) or (embedder.input_key in self.log_keys)\n            ) and not self.no_cond_log:\n                x = batch[embedder.input_key][:n]\n                if isinstance(x, torch.Tensor):\n                    if x.dim() == 1:\n                        # class-conditional, convert integer to string\n                        x = [str(x[i].item()) for i in range(x.shape[0])]\n                        xc = log_txt_as_img((image_h, image_w), x, size=image_h // 4)\n                    elif x.dim() == 2:\n                        # size and crop cond and the like\n                        x = [\n                            \"x\".join([str(xx) for xx in x[i].tolist()])\n                            for i in range(x.shape[0])\n                        ]\n                        xc = log_txt_as_img((image_h, image_w), x, size=image_h // 20)\n                    else:\n                        raise NotImplementedError()\n                elif isinstance(x, (List, ListConfig)):\n                    if isinstance(x[0], str):\n                        # strings\n                        xc = log_txt_as_img((image_h, image_w), x, size=image_h // 20)\n                    else:\n                        raise NotImplementedError()\n                else:\n                    raise NotImplementedError()\n                log[embedder.input_key] = xc\n        return log\n\n    @torch.no_grad()\n    def log_images(\n        self,\n        batch: Dict,\n        N: int = 8,\n        sample: bool = True,\n        ucg_keys: List[str] = None,\n        **kwargs,\n    ) -> Dict:\n        conditioner_input_keys = [e.input_key for e in self.conditioner.embedders]\n        if ucg_keys:\n            assert all(map(lambda x: x in conditioner_input_keys, ucg_keys)), (\n                \"Each defined ucg key for sampling must be in the provided conditioner input keys,\"\n                f\"but we have {ucg_keys} vs. {conditioner_input_keys}\"\n            )\n        else:\n            ucg_keys = conditioner_input_keys\n        log = dict()\n\n        x = self.get_input(batch)\n\n        c, uc = self.conditioner.get_unconditional_conditioning(\n            batch,\n            force_uc_zero_embeddings=ucg_keys\n            if len(self.conditioner.embedders) > 0\n            else [],\n        )\n\n        sampling_kwargs = {}\n\n        N = min(x.shape[0], N)\n        x = x.to(self.device)[:N]\n        log[\"inputs\"] = x\n        z = self.encode_first_stage(x)\n        log[\"reconstructions\"] = self.decode_first_stage(z)\n        log.update(self.log_conditionings(batch, N))\n\n        for k in c:\n            if isinstance(c[k], torch.Tensor):\n                c[k], uc[k] = map(lambda y: y[k][:N].to(self.device), (c, uc))\n\n        if sample:\n            with self.ema_scope(\"Plotting\"):\n                samples = self.sample(\n                    c, shape=z.shape[1:], uc=uc, batch_size=N, **sampling_kwargs\n                )\n            samples = self.decode_first_stage(samples)\n            log[\"samples\"] = samples\n        return log\n"
  },
  {
    "path": "sgm/modules/__init__.py",
    "content": "from .encoders.modules import GeneralConditioner\n\nUNCONDITIONAL_CONFIG = {\n    \"target\": \"sgm.modules.GeneralConditioner\",\n    \"params\": {\"emb_models\": []},\n}\n"
  },
  {
    "path": "sgm/modules/attention.py",
    "content": "import math\nfrom inspect import isfunction\nfrom typing import Any, Optional\n\nimport torch\nimport torch.nn.functional as F\nfrom einops import rearrange, repeat\nfrom torch import nn, einsum\n\ntry:\n    import xformers\n    import xformers.ops\n    XFORMERS_IS_AVAILABLE = True\nexcept:\n    XFORMERS_IS_AVAILABLE = False\n    print(\"No module 'xformers'.\")\n\n\ndef exists(val):\n    return val is not None\n\n\ndef uniq(arr):\n    return {el: True for el in arr}.keys()\n\n\ndef default(val, d):\n    if exists(val):\n        return val\n    return d() if isfunction(d) else d\n\n\ndef max_neg_value(t):\n    return -torch.finfo(t.dtype).max\n\n\ndef init_(tensor):\n    dim = tensor.shape[-1]\n    std = 1 / math.sqrt(dim)\n    tensor.uniform_(-std, std)\n    return tensor\n\n# feedforward\nclass GEGLU(nn.Module):\n    def __init__(self, dim_in, dim_out):\n        super().__init__()\n        self.proj = nn.Linear(dim_in, dim_out * 2)\n\n    def forward(self, x):\n        x, gate = self.proj(x).chunk(2, dim=-1)\n        return x * F.gelu(gate)\n\n\nclass FeedForward(nn.Module):\n    def __init__(self, dim, dim_out=None, mult=4, glu=False, dropout=0.0):\n        super().__init__()\n        inner_dim = int(dim * mult)\n        dim_out = default(dim_out, dim)\n        project_in = (\n            nn.Sequential(nn.Linear(dim, inner_dim), nn.GELU())\n            if not glu\n            else GEGLU(dim, inner_dim)\n        )\n\n        self.net = nn.Sequential(\n            project_in, nn.Dropout(dropout), nn.Linear(inner_dim, dim_out)\n        )\n\n    def forward(self, x):\n        return self.net(x)\n\n\ndef zero_module(module):\n    \"\"\"\n    Zero out the parameters of a module and return it.\n    \"\"\"\n    for p in module.parameters():\n        p.detach().zero_()\n    return module\n\n\ndef Normalize(in_channels):\n    return torch.nn.GroupNorm(\n        num_groups=32, num_channels=in_channels, eps=1e-6, affine=True\n    )\n\n\nclass LinearAttention(nn.Module):\n    def __init__(self, dim, heads=4, dim_head=32):\n        super().__init__()\n        self.heads = heads\n        hidden_dim = dim_head * heads\n        self.to_qkv = nn.Conv2d(dim, hidden_dim * 3, 1, bias=False)\n        self.to_out = nn.Conv2d(hidden_dim, dim, 1)\n\n    def forward(self, x):\n        b, c, h, w = x.shape\n        qkv = self.to_qkv(x)\n        q, k, v = rearrange(\n            qkv, \"b (qkv heads c) h w -> qkv b heads c (h w)\", heads=self.heads, qkv=3\n        )\n        k = k.softmax(dim=-1)\n        context = torch.einsum(\"bhdn,bhen->bhde\", k, v)\n        out = torch.einsum(\"bhde,bhdn->bhen\", context, q)\n        out = rearrange(\n            out, \"b heads c (h w) -> b (heads c) h w\", heads=self.heads, h=h, w=w\n        )\n        return self.to_out(out)\n\n\nclass CrossAttention(nn.Module):\n    def __init__(\n        self,\n        query_dim,\n        context_dim=None,\n        heads=8,\n        dim_head=64,\n        dropout=0.0\n    ):\n        super().__init__()\n        inner_dim = dim_head * heads\n        context_dim = default(context_dim, query_dim)\n\n        self.scale = dim_head**-0.5\n        self.heads = heads\n\n        self.to_q = nn.Linear(query_dim, inner_dim, bias=False)\n        self.to_k = nn.Linear(context_dim, inner_dim, bias=False)\n        self.to_v = nn.Linear(context_dim, inner_dim, bias=False)\n\n        self.to_out = zero_module(\n            nn.Sequential(\n                nn.Linear(inner_dim, query_dim),\n                nn.Dropout(dropout)\n            )\n        )\n\n        self.attn_map_cache = None\n\n    def forward(\n        self,\n        x,\n        context=None\n    ):\n        h = self.heads\n\n        q = self.to_q(x)\n        context = default(context, x)\n        k = self.to_k(context)\n        v = self.to_v(context)\n\n        q, k, v = map(lambda t: rearrange(t, \"b n (h d) -> (b h) n d\", h=h), (q, k, v))\n\n        ## old\n        sim = einsum('b i d, b j d -> b i j', q, k) * self.scale\n        del q, k\n\n        # attention, what we cannot get enough of\n        if sim.shape[-1] > 1:\n            sim = sim.softmax(dim=-1) # softmax on token dim\n        else:\n            sim = sim.sigmoid() # sigmoid on pixel dim\n\n        # save attn_map\n        if self.attn_map_cache is not None:\n            bh, n, l = sim.shape\n            size = int(n**0.5)\n            self.attn_map_cache[\"size\"] = size\n            self.attn_map_cache[\"attn_map\"] = sim\n\n        out = einsum('b i j, b j d -> b i d', sim, v)\n        out = rearrange(out, \"(b h) n d -> b n (h d)\", h=h)\n        \n        return self.to_out(out)\n\n\nclass MemoryEfficientCrossAttention(nn.Module):\n    # https://github.com/MatthieuTPHR/diffusers/blob/d80b531ff8060ec1ea982b65a1b8df70f73aa67c/src/diffusers/models/attention.py#L223\n    def __init__(\n        self, query_dim, context_dim=None, heads=8, dim_head=64, dropout=0.0, **kwargs\n    ):\n        super().__init__()\n        # print(\n        #     f\"Setting up {self.__class__.__name__}. Query dim is {query_dim}, context_dim is {context_dim} and using \"\n        #     f\"{heads} heads with a dimension of {dim_head}.\"\n        # )\n        inner_dim = dim_head * heads\n        context_dim = default(context_dim, query_dim)\n\n        self.heads = heads\n        self.dim_head = dim_head\n\n        self.to_q = nn.Linear(query_dim, inner_dim, bias=False)\n        self.to_k = nn.Linear(context_dim, inner_dim, bias=False)\n        self.to_v = nn.Linear(context_dim, inner_dim, bias=False)\n\n        self.to_out = nn.Sequential(\n            nn.Linear(inner_dim, query_dim), nn.Dropout(dropout)\n        )\n        self.attention_op: Optional[Any] = None\n\n    def forward(\n        self,\n        x,\n        context=None,\n        mask=None,\n        additional_tokens=None,\n        n_times_crossframe_attn_in_self=0,\n    ):\n        if additional_tokens is not None:\n            # get the number of masked tokens at the beginning of the output sequence\n            n_tokens_to_mask = additional_tokens.shape[1]\n            # add additional token\n            x = torch.cat([additional_tokens, x], dim=1)\n        q = self.to_q(x)\n        context = default(context, x)\n        k = self.to_k(context)\n        v = self.to_v(context)\n\n        if n_times_crossframe_attn_in_self:\n            # reprogramming cross-frame attention as in https://arxiv.org/abs/2303.13439\n            assert x.shape[0] % n_times_crossframe_attn_in_self == 0\n            # n_cp = x.shape[0]//n_times_crossframe_attn_in_self\n            k = repeat(\n                k[::n_times_crossframe_attn_in_self],\n                \"b ... -> (b n) ...\",\n                n=n_times_crossframe_attn_in_self,\n            )\n            v = repeat(\n                v[::n_times_crossframe_attn_in_self],\n                \"b ... -> (b n) ...\",\n                n=n_times_crossframe_attn_in_self,\n            )\n\n        b, _, _ = q.shape\n        q, k, v = map(\n            lambda t: t.unsqueeze(3)\n            .reshape(b, t.shape[1], self.heads, self.dim_head)\n            .permute(0, 2, 1, 3)\n            .reshape(b * self.heads, t.shape[1], self.dim_head)\n            .contiguous(),\n            (q, k, v),\n        )\n\n        # actually compute the attention, what we cannot get enough of\n        out = xformers.ops.memory_efficient_attention(\n            q, k, v, attn_bias=None, op=self.attention_op\n        )\n\n        # TODO: Use this directly in the attention operation, as a bias\n        if exists(mask):\n            raise NotImplementedError\n        out = (\n            out.unsqueeze(0)\n            .reshape(b, self.heads, out.shape[1], self.dim_head)\n            .permute(0, 2, 1, 3)\n            .reshape(b, out.shape[1], self.heads * self.dim_head)\n        )\n        if additional_tokens is not None:\n            # remove additional token\n            out = out[:, n_tokens_to_mask:]\n        return self.to_out(out)\n\n\nclass BasicTransformerBlock(nn.Module):\n\n    def __init__(\n        self,\n        dim,\n        n_heads,\n        d_head,\n        dropout=0.0,\n        t_context_dim=None,\n        v_context_dim=None,\n        gated_ff=True\n    ):\n        super().__init__()\n\n        # self-attention\n        self.attn1 = MemoryEfficientCrossAttention(\n            query_dim=dim,\n            heads=n_heads,\n            dim_head=d_head,\n            dropout=dropout,\n            context_dim=None\n        )\n\n        # textual cross-attention\n        if t_context_dim is not None and t_context_dim > 0:\n            self.t_attn = CrossAttention(\n                query_dim=dim,\n                context_dim=t_context_dim,\n                heads=n_heads,\n                dim_head=d_head,\n                dropout=dropout\n            )\n            self.t_norm = nn.LayerNorm(dim)\n        \n        # visual cross-attention\n        if v_context_dim is not None and v_context_dim > 0:\n            self.v_attn = CrossAttention(\n                query_dim=dim,\n                context_dim=v_context_dim,\n                heads=n_heads,\n                dim_head=d_head,\n                dropout=dropout\n            )\n            self.v_norm = nn.LayerNorm(dim)\n\n        self.norm1 = nn.LayerNorm(dim)\n        self.norm3 = nn.LayerNorm(dim)\n        self.ff = FeedForward(dim, dropout=dropout, glu=gated_ff)\n\n    def forward(self, x, t_context=None, v_context=None):\n        x = (\n            self.attn1(\n                self.norm1(x),\n                context=None\n            )\n            + x\n        )\n        if hasattr(self, \"t_attn\"):\n            x = (\n                self.t_attn(\n                    self.t_norm(x),\n                    context=t_context\n                )\n                + x\n            )\n        if hasattr(self, \"v_attn\"):\n            x = (\n                self.v_attn(\n                    self.v_norm(x),\n                    context=v_context\n                )\n                + x\n            )\n\n        x = self.ff(self.norm3(x)) + x\n\n        return x\n\n\nclass SpatialTransformer(nn.Module):\n    \"\"\"\n    Transformer block for image-like data.\n    First, project the input (aka embedding)\n    and reshape to b, t, d.\n    Then apply standard transformer action.\n    Finally, reshape to image\n    NEW: use_linear for more efficiency instead of the 1x1 convs\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels,\n        n_heads,\n        d_head,\n        depth=1,\n        dropout=0.0,\n        t_context_dim=None,\n        v_context_dim=None,\n        use_linear=False\n    ):\n        super().__init__()\n \n        self.in_channels = in_channels\n        inner_dim = n_heads * d_head\n        self.norm = Normalize(in_channels)\n        if not use_linear:\n            self.proj_in = nn.Conv2d(\n                in_channels, inner_dim, kernel_size=1, stride=1, padding=0\n            )\n        else:\n            self.proj_in = nn.Linear(in_channels, inner_dim)\n\n        self.transformer_blocks = nn.ModuleList(\n            [\n                BasicTransformerBlock(\n                    inner_dim,\n                    n_heads,\n                    d_head,\n                    dropout=dropout,\n                    t_context_dim=t_context_dim,\n                    v_context_dim=v_context_dim\n                )\n                for d in range(depth)\n            ]\n        )\n        if not use_linear:\n            self.proj_out = zero_module(\n                nn.Conv2d(inner_dim, in_channels, kernel_size=1, stride=1, padding=0)\n            )\n        else:\n            self.proj_out = zero_module(nn.Linear(inner_dim, in_channels))\n        self.use_linear = use_linear\n\n    def forward(self, x, t_context=None, v_context=None):\n\n        b, c, h, w = x.shape\n        x_in = x\n        x = self.norm(x)\n        if not self.use_linear:\n            x = self.proj_in(x)\n        x = rearrange(x, \"b c h w -> b (h w) c\").contiguous()\n        if self.use_linear:\n            x = self.proj_in(x)\n        for i, block in enumerate(self.transformer_blocks):\n            x = block(x, t_context=t_context, v_context=v_context)\n        if self.use_linear:\n            x = self.proj_out(x)\n        x = rearrange(x, \"b (h w) c -> b c h w\", h=h, w=w).contiguous()\n        if not self.use_linear:\n            x = self.proj_out(x)\n\n        return x + x_in"
  },
  {
    "path": "sgm/modules/autoencoding/__init__.py",
    "content": ""
  },
  {
    "path": "sgm/modules/autoencoding/losses/__init__.py",
    "content": "from typing import Any, Union\n\nimport torch\nimport torch.nn as nn\nfrom einops import rearrange\nfrom taming.modules.discriminator.model import NLayerDiscriminator, weights_init\nfrom taming.modules.losses.lpips import LPIPS\nfrom taming.modules.losses.vqperceptual import hinge_d_loss, vanilla_d_loss\n\nfrom ....util import default, instantiate_from_config\n\n\ndef adopt_weight(weight, global_step, threshold=0, value=0.0):\n    if global_step < threshold:\n        weight = value\n    return weight\n\n\nclass LatentLPIPS(nn.Module):\n    def __init__(\n        self,\n        decoder_config,\n        perceptual_weight=1.0,\n        latent_weight=1.0,\n        scale_input_to_tgt_size=False,\n        scale_tgt_to_input_size=False,\n        perceptual_weight_on_inputs=0.0,\n    ):\n        super().__init__()\n        self.scale_input_to_tgt_size = scale_input_to_tgt_size\n        self.scale_tgt_to_input_size = scale_tgt_to_input_size\n        self.init_decoder(decoder_config)\n        self.perceptual_loss = LPIPS().eval()\n        self.perceptual_weight = perceptual_weight\n        self.latent_weight = latent_weight\n        self.perceptual_weight_on_inputs = perceptual_weight_on_inputs\n\n    def init_decoder(self, config):\n        self.decoder = instantiate_from_config(config)\n        if hasattr(self.decoder, \"encoder\"):\n            del self.decoder.encoder\n\n    def forward(self, latent_inputs, latent_predictions, image_inputs, split=\"train\"):\n        log = dict()\n        loss = (latent_inputs - latent_predictions) ** 2\n        log[f\"{split}/latent_l2_loss\"] = loss.mean().detach()\n        image_reconstructions = None\n        if self.perceptual_weight > 0.0:\n            image_reconstructions = self.decoder.decode(latent_predictions)\n            image_targets = self.decoder.decode(latent_inputs)\n            perceptual_loss = self.perceptual_loss(\n                image_targets.contiguous(), image_reconstructions.contiguous()\n            )\n            loss = (\n                self.latent_weight * loss.mean()\n                + self.perceptual_weight * perceptual_loss.mean()\n            )\n            log[f\"{split}/perceptual_loss\"] = perceptual_loss.mean().detach()\n\n        if self.perceptual_weight_on_inputs > 0.0:\n            image_reconstructions = default(\n                image_reconstructions, self.decoder.decode(latent_predictions)\n            )\n            if self.scale_input_to_tgt_size:\n                image_inputs = torch.nn.functional.interpolate(\n                    image_inputs,\n                    image_reconstructions.shape[2:],\n                    mode=\"bicubic\",\n                    antialias=True,\n                )\n            elif self.scale_tgt_to_input_size:\n                image_reconstructions = torch.nn.functional.interpolate(\n                    image_reconstructions,\n                    image_inputs.shape[2:],\n                    mode=\"bicubic\",\n                    antialias=True,\n                )\n\n            perceptual_loss2 = self.perceptual_loss(\n                image_inputs.contiguous(), image_reconstructions.contiguous()\n            )\n            loss = loss + self.perceptual_weight_on_inputs * perceptual_loss2.mean()\n            log[f\"{split}/perceptual_loss_on_inputs\"] = perceptual_loss2.mean().detach()\n        return loss, log\n\n\nclass GeneralLPIPSWithDiscriminator(nn.Module):\n    def __init__(\n        self,\n        disc_start: int,\n        logvar_init: float = 0.0,\n        pixelloss_weight=1.0,\n        disc_num_layers: int = 3,\n        disc_in_channels: int = 3,\n        disc_factor: float = 1.0,\n        disc_weight: float = 1.0,\n        perceptual_weight: float = 1.0,\n        disc_loss: str = \"hinge\",\n        scale_input_to_tgt_size: bool = False,\n        dims: int = 2,\n        learn_logvar: bool = False,\n        regularization_weights: Union[None, dict] = None,\n    ):\n        super().__init__()\n        self.dims = dims\n        if self.dims > 2:\n            print(\n                f\"running with dims={dims}. This means that for perceptual loss calculation, \"\n                f\"the LPIPS loss will be applied to each frame independently. \"\n            )\n        self.scale_input_to_tgt_size = scale_input_to_tgt_size\n        assert disc_loss in [\"hinge\", \"vanilla\"]\n        self.pixel_weight = pixelloss_weight\n        self.perceptual_loss = LPIPS().eval()\n        self.perceptual_weight = perceptual_weight\n        # output log variance\n        self.logvar = nn.Parameter(torch.ones(size=()) * logvar_init)\n        self.learn_logvar = learn_logvar\n\n        self.discriminator = NLayerDiscriminator(\n            input_nc=disc_in_channels, n_layers=disc_num_layers, use_actnorm=False\n        ).apply(weights_init)\n        self.discriminator_iter_start = disc_start\n        self.disc_loss = hinge_d_loss if disc_loss == \"hinge\" else vanilla_d_loss\n        self.disc_factor = disc_factor\n        self.discriminator_weight = disc_weight\n        self.regularization_weights = default(regularization_weights, {})\n\n    def get_trainable_parameters(self) -> Any:\n        return self.discriminator.parameters()\n\n    def get_trainable_autoencoder_parameters(self) -> Any:\n        if self.learn_logvar:\n            yield self.logvar\n        yield from ()\n\n    def calculate_adaptive_weight(self, nll_loss, g_loss, last_layer=None):\n        if last_layer is not None:\n            nll_grads = torch.autograd.grad(nll_loss, last_layer, retain_graph=True)[0]\n            g_grads = torch.autograd.grad(g_loss, last_layer, retain_graph=True)[0]\n        else:\n            nll_grads = torch.autograd.grad(\n                nll_loss, self.last_layer[0], retain_graph=True\n            )[0]\n            g_grads = torch.autograd.grad(\n                g_loss, self.last_layer[0], retain_graph=True\n            )[0]\n\n        d_weight = torch.norm(nll_grads) / (torch.norm(g_grads) + 1e-4)\n        d_weight = torch.clamp(d_weight, 0.0, 1e4).detach()\n        d_weight = d_weight * self.discriminator_weight\n        return d_weight\n\n    def forward(\n        self,\n        regularization_log,\n        inputs,\n        reconstructions,\n        optimizer_idx,\n        global_step,\n        last_layer=None,\n        split=\"train\",\n        weights=None,\n    ):\n        if self.scale_input_to_tgt_size:\n            inputs = torch.nn.functional.interpolate(\n                inputs, reconstructions.shape[2:], mode=\"bicubic\", antialias=True\n            )\n\n        if self.dims > 2:\n            inputs, reconstructions = map(\n                lambda x: rearrange(x, \"b c t h w -> (b t) c h w\"),\n                (inputs, reconstructions),\n            )\n\n        rec_loss = torch.abs(inputs.contiguous() - reconstructions.contiguous())\n        if self.perceptual_weight > 0:\n            p_loss = self.perceptual_loss(\n                inputs.contiguous(), reconstructions.contiguous()\n            )\n            rec_loss = rec_loss + self.perceptual_weight * p_loss\n\n        nll_loss = rec_loss / torch.exp(self.logvar) + self.logvar\n        weighted_nll_loss = nll_loss\n        if weights is not None:\n            weighted_nll_loss = weights * nll_loss\n        weighted_nll_loss = torch.sum(weighted_nll_loss) / weighted_nll_loss.shape[0]\n        nll_loss = torch.sum(nll_loss) / nll_loss.shape[0]\n\n        # now the GAN part\n        if optimizer_idx == 0:\n            # generator update\n            logits_fake = self.discriminator(reconstructions.contiguous())\n            g_loss = -torch.mean(logits_fake)\n\n            if self.disc_factor > 0.0:\n                try:\n                    d_weight = self.calculate_adaptive_weight(\n                        nll_loss, g_loss, last_layer=last_layer\n                    )\n                except RuntimeError:\n                    assert not self.training\n                    d_weight = torch.tensor(0.0)\n            else:\n                d_weight = torch.tensor(0.0)\n\n            disc_factor = adopt_weight(\n                self.disc_factor, global_step, threshold=self.discriminator_iter_start\n            )\n            loss = weighted_nll_loss + d_weight * disc_factor * g_loss\n            log = dict()\n            for k in regularization_log:\n                if k in self.regularization_weights:\n                    loss = loss + self.regularization_weights[k] * regularization_log[k]\n                log[f\"{split}/{k}\"] = regularization_log[k].detach().mean()\n\n            log.update(\n                {\n                    \"{}/total_loss\".format(split): loss.clone().detach().mean(),\n                    \"{}/logvar\".format(split): self.logvar.detach(),\n                    \"{}/nll_loss\".format(split): nll_loss.detach().mean(),\n                    \"{}/rec_loss\".format(split): rec_loss.detach().mean(),\n                    \"{}/d_weight\".format(split): d_weight.detach(),\n                    \"{}/disc_factor\".format(split): torch.tensor(disc_factor),\n                    \"{}/g_loss\".format(split): g_loss.detach().mean(),\n                }\n            )\n\n            return loss, log\n\n        if optimizer_idx == 1:\n            # second pass for discriminator update\n            logits_real = self.discriminator(inputs.contiguous().detach())\n            logits_fake = self.discriminator(reconstructions.contiguous().detach())\n\n            disc_factor = adopt_weight(\n                self.disc_factor, global_step, threshold=self.discriminator_iter_start\n            )\n            d_loss = disc_factor * self.disc_loss(logits_real, logits_fake)\n\n            log = {\n                \"{}/disc_loss\".format(split): d_loss.clone().detach().mean(),\n                \"{}/logits_real\".format(split): logits_real.detach().mean(),\n                \"{}/logits_fake\".format(split): logits_fake.detach().mean(),\n            }\n            return d_loss, log\n"
  },
  {
    "path": "sgm/modules/autoencoding/regularizers/__init__.py",
    "content": "from abc import abstractmethod\nfrom typing import Any, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom ....modules.distributions.distributions import DiagonalGaussianDistribution\n\n\nclass AbstractRegularizer(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n    def forward(self, z: torch.Tensor) -> Tuple[torch.Tensor, dict]:\n        raise NotImplementedError()\n\n    @abstractmethod\n    def get_trainable_parameters(self) -> Any:\n        raise NotImplementedError()\n\n\nclass DiagonalGaussianRegularizer(AbstractRegularizer):\n    def __init__(self, sample: bool = True):\n        super().__init__()\n        self.sample = sample\n\n    def get_trainable_parameters(self) -> Any:\n        yield from ()\n\n    def forward(self, z: torch.Tensor) -> Tuple[torch.Tensor, dict]:\n        log = dict()\n        posterior = DiagonalGaussianDistribution(z)\n        if self.sample:\n            z = posterior.sample()\n        else:\n            z = posterior.mode()\n        kl_loss = posterior.kl()\n        kl_loss = torch.sum(kl_loss) / kl_loss.shape[0]\n        log[\"kl_loss\"] = kl_loss\n        return z, log\n\n\ndef measure_perplexity(predicted_indices, num_centroids):\n    # src: https://github.com/karpathy/deep-vector-quantization/blob/main/model.py\n    # eval cluster perplexity. when perplexity == num_embeddings then all clusters are used exactly equally\n    encodings = (\n        F.one_hot(predicted_indices, num_centroids).float().reshape(-1, num_centroids)\n    )\n    avg_probs = encodings.mean(0)\n    perplexity = (-(avg_probs * torch.log(avg_probs + 1e-10)).sum()).exp()\n    cluster_use = torch.sum(avg_probs > 0)\n    return perplexity, cluster_use\n"
  },
  {
    "path": "sgm/modules/diffusionmodules/__init__.py",
    "content": "from .denoiser import Denoiser\nfrom .discretizer import Discretization\nfrom .loss import StandardDiffusionLoss\nfrom .model import Model, Encoder, Decoder\nfrom .openaimodel import UnifiedUNetModel\nfrom .sampling import BaseDiffusionSampler\nfrom .wrappers import OpenAIWrapper\n"
  },
  {
    "path": "sgm/modules/diffusionmodules/denoiser.py",
    "content": "import torch.nn as nn\n\nfrom ...util import append_dims, instantiate_from_config\n\n\nclass Denoiser(nn.Module):\n    def __init__(self, weighting_config, scaling_config):\n        super().__init__()\n\n        self.weighting = instantiate_from_config(weighting_config)\n        self.scaling = instantiate_from_config(scaling_config)\n\n    def possibly_quantize_sigma(self, sigma):\n        return sigma\n\n    def possibly_quantize_c_noise(self, c_noise):\n        return c_noise\n\n    def w(self, sigma):\n        return self.weighting(sigma)\n\n    def __call__(self, network, input, sigma, cond):\n        sigma = self.possibly_quantize_sigma(sigma)\n        sigma_shape = sigma.shape\n        sigma = append_dims(sigma, input.ndim)\n        c_skip, c_out, c_in, c_noise = self.scaling(sigma)\n        c_noise = self.possibly_quantize_c_noise(c_noise.reshape(sigma_shape))\n        return network(input * c_in, c_noise, cond) * c_out + input * c_skip\n\n\nclass DiscreteDenoiser(Denoiser):\n    def __init__(\n        self,\n        weighting_config,\n        scaling_config,\n        num_idx,\n        discretization_config,\n        do_append_zero=False,\n        quantize_c_noise=True,\n        flip=True,\n    ):\n        super().__init__(weighting_config, scaling_config)\n        sigmas = instantiate_from_config(discretization_config)(\n            num_idx, do_append_zero=do_append_zero, flip=flip\n        )\n        self.register_buffer(\"sigmas\", sigmas)\n        self.quantize_c_noise = quantize_c_noise\n\n    def sigma_to_idx(self, sigma):\n        dists = sigma - self.sigmas[:, None]\n        return dists.abs().argmin(dim=0).view(sigma.shape)\n\n    def idx_to_sigma(self, idx):\n        return self.sigmas[idx]\n\n    def possibly_quantize_sigma(self, sigma):\n        return self.idx_to_sigma(self.sigma_to_idx(sigma))\n\n    def possibly_quantize_c_noise(self, c_noise):\n        if self.quantize_c_noise:\n            return self.sigma_to_idx(c_noise)\n        else:\n            return c_noise\n"
  },
  {
    "path": "sgm/modules/diffusionmodules/denoiser_scaling.py",
    "content": "import torch\n\n\nclass EDMScaling:\n    def __init__(self, sigma_data=0.5):\n        self.sigma_data = sigma_data\n\n    def __call__(self, sigma):\n        c_skip = self.sigma_data**2 / (sigma**2 + self.sigma_data**2)\n        c_out = sigma * self.sigma_data / (sigma**2 + self.sigma_data**2) ** 0.5\n        c_in = 1 / (sigma**2 + self.sigma_data**2) ** 0.5\n        c_noise = 0.25 * sigma.log()\n        return c_skip, c_out, c_in, c_noise\n\n\nclass EpsScaling:\n    def __call__(self, sigma):\n        c_skip = torch.ones_like(sigma, device=sigma.device)\n        c_out = -sigma\n        c_in = 1 / (sigma**2 + 1.0) ** 0.5\n        c_noise = sigma.clone()\n        return c_skip, c_out, c_in, c_noise\n\n\nclass VScaling:\n    def __call__(self, sigma):\n        c_skip = 1.0 / (sigma**2 + 1.0)\n        c_out = -sigma / (sigma**2 + 1.0) ** 0.5\n        c_in = 1.0 / (sigma**2 + 1.0) ** 0.5\n        c_noise = sigma.clone()\n        return c_skip, c_out, c_in, c_noise\n"
  },
  {
    "path": "sgm/modules/diffusionmodules/denoiser_weighting.py",
    "content": "import torch\n\n\nclass UnitWeighting:\n    def __call__(self, sigma):\n        return torch.ones_like(sigma, device=sigma.device)\n\n\nclass EDMWeighting:\n    def __init__(self, sigma_data=0.5):\n        self.sigma_data = sigma_data\n\n    def __call__(self, sigma):\n        return (sigma**2 + self.sigma_data**2) / (sigma * self.sigma_data) ** 2\n\n\nclass VWeighting(EDMWeighting):\n    def __init__(self):\n        super().__init__(sigma_data=1.0)\n\n\nclass EpsWeighting:\n    def __call__(self, sigma):\n        return sigma**-2.0\n"
  },
  {
    "path": "sgm/modules/diffusionmodules/discretizer.py",
    "content": "import torch\nimport numpy as np\nfrom functools import partial\nfrom abc import abstractmethod\n\nfrom ...util import append_zero\nfrom ...modules.diffusionmodules.util import make_beta_schedule\n\n\ndef generate_roughly_equally_spaced_steps(\n    num_substeps: int, max_step: int\n) -> np.ndarray:\n    return np.linspace(max_step - 1, 0, num_substeps, endpoint=False).astype(int)[::-1]\n\n\nclass Discretization:\n    def __call__(self, n, do_append_zero=True, device=\"cpu\", flip=False):\n        sigmas = self.get_sigmas(n, device=device)\n        sigmas = append_zero(sigmas) if do_append_zero else sigmas\n        return sigmas if not flip else torch.flip(sigmas, (0,))\n\n    @abstractmethod\n    def get_sigmas(self, n, device):\n        pass\n\n\nclass EDMDiscretization(Discretization):\n    def __init__(self, sigma_min=0.02, sigma_max=80.0, rho=7.0):\n        self.sigma_min = sigma_min\n        self.sigma_max = sigma_max\n        self.rho = rho\n\n    def get_sigmas(self, n, device=\"cpu\"):\n        ramp = torch.linspace(0, 1, n, device=device)\n        min_inv_rho = self.sigma_min ** (1 / self.rho)\n        max_inv_rho = self.sigma_max ** (1 / self.rho)\n        sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho)) ** self.rho\n        return sigmas\n\n\nclass LegacyDDPMDiscretization(Discretization):\n    def __init__(\n        self,\n        linear_start=0.00085,\n        linear_end=0.0120,\n        num_timesteps=1000,\n    ):\n        super().__init__()\n        self.num_timesteps = num_timesteps\n        betas = make_beta_schedule(\n            \"linear\", num_timesteps, linear_start=linear_start, linear_end=linear_end\n        )\n        alphas = 1.0 - betas\n        self.alphas_cumprod = np.cumprod(alphas, axis=0)\n        self.to_torch = partial(torch.tensor, dtype=torch.float32)\n\n    def get_sigmas(self, n, device=\"cpu\"):\n        if n < self.num_timesteps:\n            timesteps = generate_roughly_equally_spaced_steps(n, self.num_timesteps)\n            alphas_cumprod = self.alphas_cumprod[timesteps]\n        elif n == self.num_timesteps:\n            alphas_cumprod = self.alphas_cumprod\n        else:\n            raise ValueError\n\n        to_torch = partial(torch.tensor, dtype=torch.float32, device=device)\n        sigmas = to_torch((1 - alphas_cumprod) / alphas_cumprod) ** 0.5\n        return torch.flip(sigmas, (0,))\n"
  },
  {
    "path": "sgm/modules/diffusionmodules/guiders.py",
    "content": "from functools import partial\n\nimport torch\n\nfrom ...util import default, instantiate_from_config\n\n\nclass VanillaCFG:\n    \"\"\"\n    implements parallelized CFG\n    \"\"\"\n\n    def __init__(self, scale, dyn_thresh_config=None):\n        scale_schedule = lambda scale, sigma: scale  # independent of step\n        self.scale_schedule = partial(scale_schedule, scale)\n        self.dyn_thresh = instantiate_from_config(\n            default(\n                dyn_thresh_config,\n                {\n                    \"target\": \"sgm.modules.diffusionmodules.sampling_utils.NoDynamicThresholding\"\n                },\n            )\n        )\n\n    def __call__(self, x, sigma):\n        x_u, x_c = x.chunk(2)\n        scale_value = self.scale_schedule(sigma)\n        x_pred = self.dyn_thresh(x_u, x_c, scale_value)\n        return x_pred\n\n    def prepare_inputs(self, x, s, c, uc):\n        c_out = dict()\n\n        for k in c:\n            if k in [\"vector\", \"t_crossattn\", \"v_crossattn\", \"concat\"]:\n                c_out[k] = torch.cat((uc[k], c[k]), 0)\n            else:\n                assert c[k] == uc[k]\n                c_out[k] = c[k]\n        return torch.cat([x] * 2), torch.cat([s] * 2), c_out\n    \n\nclass IdentityGuider:\n    def __call__(self, x, sigma):\n        return x\n\n    def prepare_inputs(self, x, s, c, uc):\n        c_out = dict()\n\n        for k in c:\n            c_out[k] = c[k]\n\n        return x, s, c_out\n"
  },
  {
    "path": "sgm/modules/diffusionmodules/loss.py",
    "content": "from typing import List, Optional, Union\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom omegaconf import ListConfig\nfrom torchvision.utils import save_image\nfrom ...util import append_dims, instantiate_from_config\n\n\nclass StandardDiffusionLoss(nn.Module):\n    def __init__(\n        self,\n        sigma_sampler_config,\n        type=\"l2\",\n        offset_noise_level=0.0,\n        batch2model_keys: Optional[Union[str, List[str], ListConfig]] = None,\n    ):\n        super().__init__()\n\n        assert type in [\"l2\", \"l1\"]\n\n        self.sigma_sampler = instantiate_from_config(sigma_sampler_config)\n\n        self.type = type\n        self.offset_noise_level = offset_noise_level\n\n        if not batch2model_keys:\n            batch2model_keys = []\n\n        if isinstance(batch2model_keys, str):\n            batch2model_keys = [batch2model_keys]\n\n        self.batch2model_keys = set(batch2model_keys)\n\n    def __call__(self, network, denoiser, conditioner, input, batch, *args, **kwarg):\n        cond = conditioner(batch)\n        additional_model_inputs = {\n            key: batch[key] for key in self.batch2model_keys.intersection(batch)\n        }\n\n        sigmas = self.sigma_sampler(input.shape[0]).to(input.device)\n        noise = torch.randn_like(input)\n        if self.offset_noise_level > 0.0:\n            noise = noise + self.offset_noise_level * append_dims(\n                torch.randn(input.shape[0], device=input.device), input.ndim\n            )\n        noised_input = input + noise * append_dims(sigmas, input.ndim)\n        model_output = denoiser(\n            network, noised_input, sigmas, cond, **additional_model_inputs\n        )\n        w = append_dims(denoiser.w(sigmas), input.ndim)\n\n        loss = self.get_diff_loss(model_output, input, w)\n        loss = loss.mean()\n        loss_dict = {\"loss\": loss}\n\n        return loss, loss_dict\n\n    def get_diff_loss(self, model_output, target, w):\n        if self.type == \"l2\":\n            return torch.mean(\n                (w * (model_output - target) ** 2).reshape(target.shape[0], -1), 1\n            )\n        elif self.type == \"l1\":\n            return torch.mean(\n                (w * (model_output - target).abs()).reshape(target.shape[0], -1), 1\n            )\n\n\nclass FullLoss(StandardDiffusionLoss):\n\n    def __init__(\n        self,\n        seq_len=12,\n        kernel_size=3,\n        gaussian_sigma=0.5,\n        min_attn_size=16,\n        lambda_local_loss=0.0,\n        lambda_ocr_loss=0.0,\n        lambda_style_loss=0.0,\n        ocr_enabled = False,\n        style_enabled = False,\n        predictor_config = None,\n        *args, **kwarg\n    ):\n        super().__init__(*args, **kwarg)\n\n        self.gaussian_kernel_size = kernel_size\n        gaussian_kernel = self.get_gaussian_kernel(kernel_size=self.gaussian_kernel_size, sigma=gaussian_sigma, out_channels=seq_len)\n        self.register_buffer(\"g_kernel\", gaussian_kernel.requires_grad_(False))\n\n        self.min_attn_size = min_attn_size\n        self.lambda_local_loss = lambda_local_loss\n        self.lambda_ocr_loss = lambda_ocr_loss\n        self.lambda_style_loss = lambda_style_loss\n\n        self.style_enabled = style_enabled\n        self.ocr_enabled = ocr_enabled\n        if ocr_enabled:\n            self.predictor = instantiate_from_config(predictor_config)\n    \n    def get_gaussian_kernel(self, kernel_size=3, sigma=1, out_channels=3):\n        # Create a x, y coordinate grid of shape (kernel_size, kernel_size, 2)\n        x_coord = torch.arange(kernel_size)\n        x_grid = x_coord.repeat(kernel_size).view(kernel_size, kernel_size)\n        y_grid = x_grid.t()\n        xy_grid = torch.stack([x_grid, y_grid], dim=-1).float()\n\n        mean = (kernel_size - 1)/2.\n        variance = sigma**2.\n\n        # Calculate the 2-dimensional gaussian kernel which is\n        # the product of two gaussian distributions for two different\n        # variables (in this case called x and y)\n        gaussian_kernel = (1./(2.*torch.pi*variance)) *\\\n                        torch.exp(\n                            -torch.sum((xy_grid - mean)**2., dim=-1) /\\\n                            (2*variance)\n                        )\n\n        # Make sure sum of values in gaussian kernel equals 1.\n        gaussian_kernel = gaussian_kernel / torch.sum(gaussian_kernel)\n\n        # Reshape to 2d depthwise convolutional weight\n        gaussian_kernel = gaussian_kernel.view(1, 1, kernel_size, kernel_size)\n        gaussian_kernel = gaussian_kernel.tile(out_channels, 1, 1, 1)\n        \n        return gaussian_kernel\n\n    def __call__(self, network, denoiser, conditioner, input, batch, first_stage_model, scaler):\n\n        cond = conditioner(batch)\n\n        sigmas = self.sigma_sampler(input.shape[0]).to(input.device)\n        noise = torch.randn_like(input)\n        if self.offset_noise_level > 0.0:\n            noise = noise + self.offset_noise_level * append_dims(\n                torch.randn(input.shape[0], device=input.device), input.ndim\n            )\n\n        noised_input = input + noise * append_dims(sigmas, input.ndim)\n        model_output = denoiser(network, noised_input, sigmas, cond)\n        w = append_dims(denoiser.w(sigmas), input.ndim)\n\n        diff_loss = self.get_diff_loss(model_output, input, w)\n        local_loss = self.get_local_loss(network.diffusion_model.attn_map_cache, batch[\"seg\"], batch[\"seg_mask\"])\n        diff_loss = diff_loss.mean()\n        local_loss = local_loss.mean()\n\n        if self.ocr_enabled:\n            ocr_loss = self.get_ocr_loss(model_output, batch[\"r_bbox\"], batch[\"label\"], first_stage_model, scaler)\n            ocr_loss = ocr_loss.mean()\n\n        if self.style_enabled:\n            style_loss = self.get_style_local_loss(network.diffusion_model.attn_map_cache, batch[\"mask\"])\n            style_loss = style_loss.mean()\n\n        loss = diff_loss + self.lambda_local_loss * local_loss\n        if self.ocr_enabled:\n            loss += self.lambda_ocr_loss * ocr_loss\n        if self.style_enabled:\n            loss += self.lambda_style_loss * style_loss\n\n        loss_dict = {\n            \"loss/diff_loss\": diff_loss,\n            \"loss/local_loss\": local_loss,\n            \"loss/full_loss\": loss\n        }\n\n        if self.ocr_enabled:\n            loss_dict[\"loss/ocr_loss\"] = ocr_loss\n        if self.style_enabled:\n            loss_dict[\"loss/style_loss\"] = style_loss\n\n        return loss, loss_dict\n    \n    def get_ocr_loss(self, model_output, r_bbox, label, first_stage_model, scaler):\n\n        model_output = 1 / scaler * model_output\n        model_output_decoded = first_stage_model.decode(model_output)\n        model_output_crops = []\n        \n        for i, bbox in enumerate(r_bbox):\n            m_top, m_bottom, m_left, m_right = bbox\n            model_output_crops.append(model_output_decoded[i, :, m_top:m_bottom, m_left:m_right])\n\n        loss = self.predictor.calc_loss(model_output_crops, label)\n\n        return loss\n\n    def get_min_local_loss(self, attn_map_cache, mask, seg_mask):\n\n        loss = 0\n        count = 0\n\n        for item in attn_map_cache:\n\n            name = item[\"name\"]\n            if not name.endswith(\"t_attn\"): continue\n\n            heads = item[\"heads\"]\n            size = item[\"size\"]\n            attn_map = item[\"attn_map\"]\n\n            if size < self.min_attn_size: continue\n\n            seg_l = seg_mask.shape[1]\n\n            bh, n, l = attn_map.shape # bh: batch size * heads / n : pixel length(h*w) / l: token length\n            attn_map = attn_map.reshape((-1, heads, n, l)) # b, h, n, l\n            \n            assert seg_l <= l\n            attn_map = attn_map[..., :seg_l]\n            attn_map = attn_map.permute(0, 1, 3, 2) # b, h, l, n\n            attn_map = attn_map.mean(dim = 1) # b, l, n\n\n            attn_map = attn_map.reshape((-1, seg_l, size, size)) # b, l, s, s\n            attn_map = F.conv2d(attn_map, self.g_kernel, padding = self.gaussian_kernel_size//2, groups=seg_l) # gaussian blur on each channel\n            attn_map = attn_map.reshape((-1, seg_l, n)) # b, l, n\n            \n            mask_map = F.interpolate(mask, (size, size))\n            mask_map = mask_map.tile((1, seg_l, 1, 1))\n            mask_map = mask_map.reshape((-1, seg_l, n)) # b, l, n\n\n            p_loss = (mask_map * attn_map).max(dim = -1)[0] # b, l\n            p_loss = p_loss + (1 - seg_mask) # b, l\n            p_loss = p_loss.min(dim = -1)[0] # b,\n\n            loss += -p_loss\n            count += 1\n\n        loss = loss / count\n\n        return loss\n\n    def get_local_loss(self, attn_map_cache, seg, seg_mask):\n\n        loss = 0\n        count = 0\n\n        for item in attn_map_cache:\n\n            name = item[\"name\"]\n            if not name.endswith(\"t_attn\"): continue\n\n            heads = item[\"heads\"]\n            size = item[\"size\"]\n            attn_map = item[\"attn_map\"]\n\n            if size < self.min_attn_size: continue\n\n            seg_l = seg_mask.shape[1]\n\n            bh, n, l = attn_map.shape # bh: batch size * heads / n: pixel length(h*w) / l: token length\n            attn_map = attn_map.reshape((-1, heads, n, l)) # b, h, n, l\n            \n            assert seg_l <= l\n            attn_map = attn_map[..., :seg_l]\n            attn_map = attn_map.permute(0, 1, 3, 2) # b, h, l, n\n            attn_map = attn_map.mean(dim = 1) # b, l, n\n\n            attn_map = attn_map.reshape((-1, seg_l, size, size)) # b, l, s, s\n            attn_map = F.conv2d(attn_map, self.g_kernel, padding = self.gaussian_kernel_size//2, groups=seg_l) # gaussian blur on each channel\n            attn_map = attn_map.reshape((-1, seg_l, n)) # b, l, n\n\n            seg_map = F.interpolate(seg, (size, size))\n            seg_map = seg_map.reshape((-1, seg_l, n)) # b, l, n\n            n_seg_map = 1 - seg_map\n\n            p_loss = (seg_map * attn_map).max(dim = -1)[0] # b, l\n            n_loss = (n_seg_map * attn_map).max(dim = -1)[0] # b, l\n\n            p_loss = p_loss * seg_mask # b, l\n            n_loss = n_loss * seg_mask # b, l\n\n            p_loss = p_loss.sum(dim = -1) / seg_mask.sum(dim = -1) # b,\n            n_loss = n_loss.sum(dim = -1) / seg_mask.sum(dim = -1) # b,\n\n            f_loss = n_loss - p_loss # b,\n            loss += f_loss\n            count += 1\n\n        loss = loss / count\n\n        return loss\n    "
  },
  {
    "path": "sgm/modules/diffusionmodules/model.py",
    "content": "# pytorch_diffusion + derived encoder decoder\nimport math\nfrom typing import Any, Callable, Optional\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom einops import rearrange\nfrom packaging import version\n\ntry:\n    import xformers\n    import xformers.ops\n\n    XFORMERS_IS_AVAILABLE = True\nexcept:\n    XFORMERS_IS_AVAILABLE = False\n    print(\"no module 'xformers'. Processing without...\")\n\nfrom ...modules.attention import LinearAttention, MemoryEfficientCrossAttention\n\n\ndef get_timestep_embedding(timesteps, embedding_dim):\n    \"\"\"\n    This matches the implementation in Denoising Diffusion Probabilistic Models:\n    From Fairseq.\n    Build sinusoidal embeddings.\n    This matches the implementation in tensor2tensor, but differs slightly\n    from the description in Section 3.5 of \"Attention Is All You Need\".\n    \"\"\"\n    assert len(timesteps.shape) == 1\n\n    half_dim = embedding_dim // 2\n    emb = math.log(10000) / (half_dim - 1)\n    emb = torch.exp(torch.arange(half_dim, dtype=torch.float32) * -emb)\n    emb = emb.to(device=timesteps.device)\n    emb = timesteps.float()[:, None] * emb[None, :]\n    emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=1)\n    if embedding_dim % 2 == 1:  # zero pad\n        emb = torch.nn.functional.pad(emb, (0, 1, 0, 0))\n    return emb\n\n\ndef nonlinearity(x):\n    # swish\n    return x * torch.sigmoid(x)\n\n\ndef Normalize(in_channels, num_groups=32):\n    return torch.nn.GroupNorm(\n        num_groups=num_groups, num_channels=in_channels, eps=1e-6, affine=True\n    )\n\n\nclass Upsample(nn.Module):\n    def __init__(self, in_channels, with_conv):\n        super().__init__()\n        self.with_conv = with_conv\n        if self.with_conv:\n            self.conv = torch.nn.Conv2d(\n                in_channels, in_channels, kernel_size=3, stride=1, padding=1\n            )\n\n    def forward(self, x):\n        x = torch.nn.functional.interpolate(x, scale_factor=2.0, mode=\"nearest\")\n        if self.with_conv:\n            x = self.conv(x)\n        return x\n\n\nclass Downsample(nn.Module):\n    def __init__(self, in_channels, with_conv):\n        super().__init__()\n        self.with_conv = with_conv\n        if self.with_conv:\n            # no asymmetric padding in torch conv, must do it ourselves\n            self.conv = torch.nn.Conv2d(\n                in_channels, in_channels, kernel_size=3, stride=2, padding=0\n            )\n\n    def forward(self, x):\n        if self.with_conv:\n            pad = (0, 1, 0, 1)\n            x = torch.nn.functional.pad(x, pad, mode=\"constant\", value=0)\n            x = self.conv(x)\n        else:\n            x = torch.nn.functional.avg_pool2d(x, kernel_size=2, stride=2)\n        return x\n\n\nclass ResnetBlock(nn.Module):\n    def __init__(\n        self,\n        *,\n        in_channels,\n        out_channels=None,\n        conv_shortcut=False,\n        dropout,\n        temb_channels=512,\n    ):\n        super().__init__()\n        self.in_channels = in_channels\n        out_channels = in_channels if out_channels is None else out_channels\n        self.out_channels = out_channels\n        self.use_conv_shortcut = conv_shortcut\n\n        self.norm1 = Normalize(in_channels)\n        self.conv1 = torch.nn.Conv2d(\n            in_channels, out_channels, kernel_size=3, stride=1, padding=1\n        )\n        if temb_channels > 0:\n            self.temb_proj = torch.nn.Linear(temb_channels, out_channels)\n        self.norm2 = Normalize(out_channels)\n        self.dropout = torch.nn.Dropout(dropout)\n        self.conv2 = torch.nn.Conv2d(\n            out_channels, out_channels, kernel_size=3, stride=1, padding=1\n        )\n        if self.in_channels != self.out_channels:\n            if self.use_conv_shortcut:\n                self.conv_shortcut = torch.nn.Conv2d(\n                    in_channels, out_channels, kernel_size=3, stride=1, padding=1\n                )\n            else:\n                self.nin_shortcut = torch.nn.Conv2d(\n                    in_channels, out_channels, kernel_size=1, stride=1, padding=0\n                )\n\n    def forward(self, x, temb):\n        h = x\n        h = self.norm1(h)\n        h = nonlinearity(h)\n        h = self.conv1(h)\n\n        if temb is not None:\n            h = h + self.temb_proj(nonlinearity(temb))[:, :, None, None]\n\n        h = self.norm2(h)\n        h = nonlinearity(h)\n        h = self.dropout(h)\n        h = self.conv2(h)\n\n        if self.in_channels != self.out_channels:\n            if self.use_conv_shortcut:\n                x = self.conv_shortcut(x)\n            else:\n                x = self.nin_shortcut(x)\n\n        return x + h\n\n\nclass LinAttnBlock(LinearAttention):\n    \"\"\"to match AttnBlock usage\"\"\"\n\n    def __init__(self, in_channels):\n        super().__init__(dim=in_channels, heads=1, dim_head=in_channels)\n\n\nclass AttnBlock(nn.Module):\n    def __init__(self, in_channels):\n        super().__init__()\n        self.in_channels = in_channels\n\n        self.norm = Normalize(in_channels)\n        self.q = torch.nn.Conv2d(\n            in_channels, in_channels, kernel_size=1, stride=1, padding=0\n        )\n        self.k = torch.nn.Conv2d(\n            in_channels, in_channels, kernel_size=1, stride=1, padding=0\n        )\n        self.v = torch.nn.Conv2d(\n            in_channels, in_channels, kernel_size=1, stride=1, padding=0\n        )\n        self.proj_out = torch.nn.Conv2d(\n            in_channels, in_channels, kernel_size=1, stride=1, padding=0\n        )\n\n    def attention(self, h_: torch.Tensor) -> torch.Tensor:\n        h_ = self.norm(h_)\n        q = self.q(h_)\n        k = self.k(h_)\n        v = self.v(h_)\n\n        b, c, h, w = q.shape\n        q, k, v = map(\n            lambda x: rearrange(x, \"b c h w -> b 1 (h w) c\").contiguous(), (q, k, v)\n        )\n        h_ = torch.nn.functional.scaled_dot_product_attention(\n            q, k, v\n        )  # scale is dim ** -0.5 per default\n        # compute attention\n\n        return rearrange(h_, \"b 1 (h w) c -> b c h w\", h=h, w=w, c=c, b=b)\n\n    def forward(self, x, **kwargs):\n        h_ = x\n        h_ = self.attention(h_)\n        h_ = self.proj_out(h_)\n        return x + h_\n\n\nclass MemoryEfficientAttnBlock(nn.Module):\n    \"\"\"\n    Uses xformers efficient implementation,\n    see https://github.com/MatthieuTPHR/diffusers/blob/d80b531ff8060ec1ea982b65a1b8df70f73aa67c/src/diffusers/models/attention.py#L223\n    Note: this is a single-head self-attention operation\n    \"\"\"\n\n    #\n    def __init__(self, in_channels):\n        super().__init__()\n        self.in_channels = in_channels\n\n        self.norm = Normalize(in_channels)\n        self.q = torch.nn.Conv2d(\n            in_channels, in_channels, kernel_size=1, stride=1, padding=0\n        )\n        self.k = torch.nn.Conv2d(\n            in_channels, in_channels, kernel_size=1, stride=1, padding=0\n        )\n        self.v = torch.nn.Conv2d(\n            in_channels, in_channels, kernel_size=1, stride=1, padding=0\n        )\n        self.proj_out = torch.nn.Conv2d(\n            in_channels, in_channels, kernel_size=1, stride=1, padding=0\n        )\n        self.attention_op: Optional[Any] = None\n\n    def attention(self, h_: torch.Tensor) -> torch.Tensor:\n        h_ = self.norm(h_)\n        q = self.q(h_)\n        k = self.k(h_)\n        v = self.v(h_)\n\n        # compute attention\n        B, C, H, W = q.shape\n        q, k, v = map(lambda x: rearrange(x, \"b c h w -> b (h w) c\"), (q, k, v))\n\n        q, k, v = map(\n            lambda t: t.unsqueeze(3)\n            .reshape(B, t.shape[1], 1, C)\n            .permute(0, 2, 1, 3)\n            .reshape(B * 1, t.shape[1], C)\n            .contiguous(),\n            (q, k, v),\n        )\n        out = xformers.ops.memory_efficient_attention(\n            q, k, v, attn_bias=None, op=self.attention_op\n        )\n\n        out = (\n            out.unsqueeze(0)\n            .reshape(B, 1, out.shape[1], C)\n            .permute(0, 2, 1, 3)\n            .reshape(B, out.shape[1], C)\n        )\n        return rearrange(out, \"b (h w) c -> b c h w\", b=B, h=H, w=W, c=C)\n\n    def forward(self, x, **kwargs):\n        h_ = x\n        h_ = self.attention(h_)\n        h_ = self.proj_out(h_)\n        return x + h_\n\n\nclass MemoryEfficientCrossAttentionWrapper(MemoryEfficientCrossAttention):\n    def forward(self, x, context=None, mask=None, **unused_kwargs):\n        b, c, h, w = x.shape\n        x = rearrange(x, \"b c h w -> b (h w) c\")\n        out = super().forward(x, context=context, mask=mask)\n        out = rearrange(out, \"b (h w) c -> b c h w\", h=h, w=w, c=c)\n        return x + out\n\n\ndef make_attn(in_channels, attn_type=\"vanilla\", attn_kwargs=None):\n    assert attn_type in [\n        \"vanilla\",\n        \"vanilla-xformers\",\n        \"memory-efficient-cross-attn\",\n        \"linear\",\n        \"none\",\n    ], f\"attn_type {attn_type} unknown\"\n    if (\n        version.parse(torch.__version__) < version.parse(\"2.0.0\")\n        and attn_type != \"none\"\n    ):\n        assert XFORMERS_IS_AVAILABLE, (\n            f\"We do not support vanilla attention in {torch.__version__} anymore, \"\n            f\"as it is too expensive. Please install xformers via e.g. 'pip install xformers==0.0.16'\"\n        )\n        attn_type = \"vanilla-xformers\"\n    print(f\"making attention of type '{attn_type}' with {in_channels} in_channels\")\n    if attn_type == \"vanilla\":\n        assert attn_kwargs is None\n        return AttnBlock(in_channels)\n    elif attn_type == \"vanilla-xformers\":\n        print(f\"building MemoryEfficientAttnBlock with {in_channels} in_channels...\")\n        return MemoryEfficientAttnBlock(in_channels)\n    elif type == \"memory-efficient-cross-attn\":\n        attn_kwargs[\"query_dim\"] = in_channels\n        return MemoryEfficientCrossAttentionWrapper(**attn_kwargs)\n    elif attn_type == \"none\":\n        return nn.Identity(in_channels)\n    else:\n        return LinAttnBlock(in_channels)\n\n\nclass Model(nn.Module):\n    def __init__(\n        self,\n        *,\n        ch,\n        out_ch,\n        ch_mult=(1, 2, 4, 8),\n        num_res_blocks,\n        attn_resolutions,\n        dropout=0.0,\n        resamp_with_conv=True,\n        in_channels,\n        resolution,\n        use_timestep=True,\n        use_linear_attn=False,\n        attn_type=\"vanilla\",\n    ):\n        super().__init__()\n        if use_linear_attn:\n            attn_type = \"linear\"\n        self.ch = ch\n        self.temb_ch = self.ch * 4\n        self.num_resolutions = len(ch_mult)\n        self.num_res_blocks = num_res_blocks\n        self.resolution = resolution\n        self.in_channels = in_channels\n\n        self.use_timestep = use_timestep\n        if self.use_timestep:\n            # timestep embedding\n            self.temb = nn.Module()\n            self.temb.dense = nn.ModuleList(\n                [\n                    torch.nn.Linear(self.ch, self.temb_ch),\n                    torch.nn.Linear(self.temb_ch, self.temb_ch),\n                ]\n            )\n\n        # downsampling\n        self.conv_in = torch.nn.Conv2d(\n            in_channels, self.ch, kernel_size=3, stride=1, padding=1\n        )\n\n        curr_res = resolution\n        in_ch_mult = (1,) + tuple(ch_mult)\n        self.down = nn.ModuleList()\n        for i_level in range(self.num_resolutions):\n            block = nn.ModuleList()\n            attn = nn.ModuleList()\n            block_in = ch * in_ch_mult[i_level]\n            block_out = ch * ch_mult[i_level]\n            for i_block in range(self.num_res_blocks):\n                block.append(\n                    ResnetBlock(\n                        in_channels=block_in,\n                        out_channels=block_out,\n                        temb_channels=self.temb_ch,\n                        dropout=dropout,\n                    )\n                )\n                block_in = block_out\n                if curr_res in attn_resolutions:\n                    attn.append(make_attn(block_in, attn_type=attn_type))\n            down = nn.Module()\n            down.block = block\n            down.attn = attn\n            if i_level != self.num_resolutions - 1:\n                down.downsample = Downsample(block_in, resamp_with_conv)\n                curr_res = curr_res // 2\n            self.down.append(down)\n\n        # middle\n        self.mid = nn.Module()\n        self.mid.block_1 = ResnetBlock(\n            in_channels=block_in,\n            out_channels=block_in,\n            temb_channels=self.temb_ch,\n            dropout=dropout,\n        )\n        self.mid.attn_1 = make_attn(block_in, attn_type=attn_type)\n        self.mid.block_2 = ResnetBlock(\n            in_channels=block_in,\n            out_channels=block_in,\n            temb_channels=self.temb_ch,\n            dropout=dropout,\n        )\n\n        # upsampling\n        self.up = nn.ModuleList()\n        for i_level in reversed(range(self.num_resolutions)):\n            block = nn.ModuleList()\n            attn = nn.ModuleList()\n            block_out = ch * ch_mult[i_level]\n            skip_in = ch * ch_mult[i_level]\n            for i_block in range(self.num_res_blocks + 1):\n                if i_block == self.num_res_blocks:\n                    skip_in = ch * in_ch_mult[i_level]\n                block.append(\n                    ResnetBlock(\n                        in_channels=block_in + skip_in,\n                        out_channels=block_out,\n                        temb_channels=self.temb_ch,\n                        dropout=dropout,\n                    )\n                )\n                block_in = block_out\n                if curr_res in attn_resolutions:\n                    attn.append(make_attn(block_in, attn_type=attn_type))\n            up = nn.Module()\n            up.block = block\n            up.attn = attn\n            if i_level != 0:\n                up.upsample = Upsample(block_in, resamp_with_conv)\n                curr_res = curr_res * 2\n            self.up.insert(0, up)  # prepend to get consistent order\n\n        # end\n        self.norm_out = Normalize(block_in)\n        self.conv_out = torch.nn.Conv2d(\n            block_in, out_ch, kernel_size=3, stride=1, padding=1\n        )\n\n    def forward(self, x, t=None, context=None):\n        # assert x.shape[2] == x.shape[3] == self.resolution\n        if context is not None:\n            # assume aligned context, cat along channel axis\n            x = torch.cat((x, context), dim=1)\n        if self.use_timestep:\n            # timestep embedding\n            assert t is not None\n            temb = get_timestep_embedding(t, self.ch)\n            temb = self.temb.dense[0](temb)\n            temb = nonlinearity(temb)\n            temb = self.temb.dense[1](temb)\n        else:\n            temb = None\n\n        # downsampling\n        hs = [self.conv_in(x)]\n        for i_level in range(self.num_resolutions):\n            for i_block in range(self.num_res_blocks):\n                h = self.down[i_level].block[i_block](hs[-1], temb)\n                if len(self.down[i_level].attn) > 0:\n                    h = self.down[i_level].attn[i_block](h)\n                hs.append(h)\n            if i_level != self.num_resolutions - 1:\n                hs.append(self.down[i_level].downsample(hs[-1]))\n\n        # middle\n        h = hs[-1]\n        h = self.mid.block_1(h, temb)\n        h = self.mid.attn_1(h)\n        h = self.mid.block_2(h, temb)\n\n        # upsampling\n        for i_level in reversed(range(self.num_resolutions)):\n            for i_block in range(self.num_res_blocks + 1):\n                h = self.up[i_level].block[i_block](\n                    torch.cat([h, hs.pop()], dim=1), temb\n                )\n                if len(self.up[i_level].attn) > 0:\n                    h = self.up[i_level].attn[i_block](h)\n            if i_level != 0:\n                h = self.up[i_level].upsample(h)\n\n        # end\n        h = self.norm_out(h)\n        h = nonlinearity(h)\n        h = self.conv_out(h)\n        return h\n\n    def get_last_layer(self):\n        return self.conv_out.weight\n\n\nclass Encoder(nn.Module):\n    def __init__(\n        self,\n        *,\n        ch,\n        out_ch,\n        ch_mult=(1, 2, 4, 8),\n        num_res_blocks,\n        attn_resolutions,\n        dropout=0.0,\n        resamp_with_conv=True,\n        in_channels,\n        resolution,\n        z_channels,\n        double_z=True,\n        use_linear_attn=False,\n        attn_type=\"vanilla\",\n        **ignore_kwargs,\n    ):\n        super().__init__()\n        if use_linear_attn:\n            attn_type = \"linear\"\n        self.ch = ch\n        self.temb_ch = 0\n        self.num_resolutions = len(ch_mult)\n        self.num_res_blocks = num_res_blocks\n        self.resolution = resolution\n        self.in_channels = in_channels\n\n        # downsampling\n        self.conv_in = torch.nn.Conv2d(\n            in_channels, self.ch, kernel_size=3, stride=1, padding=1\n        )\n\n        curr_res = resolution\n        in_ch_mult = (1,) + tuple(ch_mult)\n        self.in_ch_mult = in_ch_mult\n        self.down = nn.ModuleList()\n        for i_level in range(self.num_resolutions):\n            block = nn.ModuleList()\n            attn = nn.ModuleList()\n            block_in = ch * in_ch_mult[i_level]\n            block_out = ch * ch_mult[i_level]\n            for i_block in range(self.num_res_blocks):\n                block.append(\n                    ResnetBlock(\n                        in_channels=block_in,\n                        out_channels=block_out,\n                        temb_channels=self.temb_ch,\n                        dropout=dropout,\n                    )\n                )\n                block_in = block_out\n                if curr_res in attn_resolutions:\n                    attn.append(make_attn(block_in, attn_type=attn_type))\n            down = nn.Module()\n            down.block = block\n            down.attn = attn\n            if i_level != self.num_resolutions - 1:\n                down.downsample = Downsample(block_in, resamp_with_conv)\n                curr_res = curr_res // 2\n            self.down.append(down)\n\n        # middle\n        self.mid = nn.Module()\n        self.mid.block_1 = ResnetBlock(\n            in_channels=block_in,\n            out_channels=block_in,\n            temb_channels=self.temb_ch,\n            dropout=dropout,\n        )\n        self.mid.attn_1 = make_attn(block_in, attn_type=attn_type)\n        self.mid.block_2 = ResnetBlock(\n            in_channels=block_in,\n            out_channels=block_in,\n            temb_channels=self.temb_ch,\n            dropout=dropout,\n        )\n\n        # end\n        self.norm_out = Normalize(block_in)\n        self.conv_out = torch.nn.Conv2d(\n            block_in,\n            2 * z_channels if double_z else z_channels,\n            kernel_size=3,\n            stride=1,\n            padding=1,\n        )\n\n    def forward(self, x):\n        # timestep embedding\n        temb = None\n\n        # downsampling\n        hs = [self.conv_in(x)]\n        for i_level in range(self.num_resolutions):\n            for i_block in range(self.num_res_blocks):\n                h = self.down[i_level].block[i_block](hs[-1], temb)\n                if len(self.down[i_level].attn) > 0:\n                    h = self.down[i_level].attn[i_block](h)\n                hs.append(h)\n            if i_level != self.num_resolutions - 1:\n                hs.append(self.down[i_level].downsample(hs[-1]))\n\n        # middle\n        h = hs[-1]\n        h = self.mid.block_1(h, temb)\n        h = self.mid.attn_1(h)\n        h = self.mid.block_2(h, temb)\n\n        # end\n        h = self.norm_out(h)\n        h = nonlinearity(h)\n        h = self.conv_out(h)\n        return h\n\n\nclass Decoder(nn.Module):\n    def __init__(\n        self,\n        *,\n        ch,\n        out_ch,\n        ch_mult=(1, 2, 4, 8),\n        num_res_blocks,\n        attn_resolutions,\n        dropout=0.0,\n        resamp_with_conv=True,\n        in_channels,\n        resolution,\n        z_channels,\n        give_pre_end=False,\n        tanh_out=False,\n        use_linear_attn=False,\n        attn_type=\"vanilla\",\n        **ignorekwargs,\n    ):\n        super().__init__()\n        if use_linear_attn:\n            attn_type = \"linear\"\n        self.ch = ch\n        self.temb_ch = 0\n        self.num_resolutions = len(ch_mult)\n        self.num_res_blocks = num_res_blocks\n        self.resolution = resolution\n        self.in_channels = in_channels\n        self.give_pre_end = give_pre_end\n        self.tanh_out = tanh_out\n\n        # compute in_ch_mult, block_in and curr_res at lowest res\n        in_ch_mult = (1,) + tuple(ch_mult)\n        block_in = ch * ch_mult[self.num_resolutions - 1]\n        curr_res = resolution // 2 ** (self.num_resolutions - 1)\n        self.z_shape = (1, z_channels, curr_res, curr_res)\n        print(\n            \"Working with z of shape {} = {} dimensions.\".format(\n                self.z_shape, np.prod(self.z_shape)\n            )\n        )\n\n        make_attn_cls = self._make_attn()\n        make_resblock_cls = self._make_resblock()\n        make_conv_cls = self._make_conv()\n        # z to block_in\n        self.conv_in = torch.nn.Conv2d(\n            z_channels, block_in, kernel_size=3, stride=1, padding=1\n        )\n\n        # middle\n        self.mid = nn.Module()\n        self.mid.block_1 = make_resblock_cls(\n            in_channels=block_in,\n            out_channels=block_in,\n            temb_channels=self.temb_ch,\n            dropout=dropout,\n        )\n        self.mid.attn_1 = make_attn_cls(block_in, attn_type=attn_type)\n        self.mid.block_2 = make_resblock_cls(\n            in_channels=block_in,\n            out_channels=block_in,\n            temb_channels=self.temb_ch,\n            dropout=dropout,\n        )\n\n        # upsampling\n        self.up = nn.ModuleList()\n        for i_level in reversed(range(self.num_resolutions)):\n            block = nn.ModuleList()\n            attn = nn.ModuleList()\n            block_out = ch * ch_mult[i_level]\n            for i_block in range(self.num_res_blocks + 1):\n                block.append(\n                    make_resblock_cls(\n                        in_channels=block_in,\n                        out_channels=block_out,\n                        temb_channels=self.temb_ch,\n                        dropout=dropout,\n                    )\n                )\n                block_in = block_out\n                if curr_res in attn_resolutions:\n                    attn.append(make_attn_cls(block_in, attn_type=attn_type))\n            up = nn.Module()\n            up.block = block\n            up.attn = attn\n            if i_level != 0:\n                up.upsample = Upsample(block_in, resamp_with_conv)\n                curr_res = curr_res * 2\n            self.up.insert(0, up)  # prepend to get consistent order\n\n        # end\n        self.norm_out = Normalize(block_in)\n        self.conv_out = make_conv_cls(\n            block_in, out_ch, kernel_size=3, stride=1, padding=1\n        )\n\n    def _make_attn(self) -> Callable:\n        return make_attn\n\n    def _make_resblock(self) -> Callable:\n        return ResnetBlock\n\n    def _make_conv(self) -> Callable:\n        return torch.nn.Conv2d\n\n    def get_last_layer(self, **kwargs):\n        return self.conv_out.weight\n\n    def forward(self, z, **kwargs):\n        # assert z.shape[1:] == self.z_shape[1:]\n        self.last_z_shape = z.shape\n\n        # timestep embedding\n        temb = None\n\n        # z to block_in\n        h = self.conv_in(z)\n\n        # middle\n        h = self.mid.block_1(h, temb, **kwargs)\n        h = self.mid.attn_1(h, **kwargs)\n        h = self.mid.block_2(h, temb, **kwargs)\n\n        # upsampling\n        for i_level in reversed(range(self.num_resolutions)):\n            for i_block in range(self.num_res_blocks + 1):\n                h = self.up[i_level].block[i_block](h, temb, **kwargs)\n                if len(self.up[i_level].attn) > 0:\n                    h = self.up[i_level].attn[i_block](h, **kwargs)\n            if i_level != 0:\n                h = self.up[i_level].upsample(h)\n\n        # end\n        if self.give_pre_end:\n            return h\n\n        h = self.norm_out(h)\n        h = nonlinearity(h)\n        h = self.conv_out(h, **kwargs)\n        if self.tanh_out:\n            h = torch.tanh(h)\n        return h\n"
  },
  {
    "path": "sgm/modules/diffusionmodules/openaimodel.py",
    "content": "from abc import abstractmethod\nfrom typing import Iterable\n\nimport numpy as np\nimport torch as th\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom einops import rearrange\n\nfrom ...modules.attention import SpatialTransformer\nfrom ...modules.diffusionmodules.util import (\n    avg_pool_nd,\n    conv_nd,\n    linear,\n    normalization,\n    timestep_embedding,\n    zero_module,\n)\nfrom ...util import default, exists\n\n\nclass Timestep(nn.Module):\n    def __init__(self, dim):\n        super().__init__()\n        self.dim = dim\n\n    def forward(self, t):\n        return timestep_embedding(t, self.dim)\n    \n\nclass TimestepBlock(nn.Module):\n    \"\"\"\n    Any module where forward() takes timestep embeddings as a second argument.\n    \"\"\"\n\n    @abstractmethod\n    def forward(self, x, emb):\n        \"\"\"\n        Apply the module to `x` given `emb` timestep embeddings.\n        \"\"\"\n\n\nclass TimestepEmbedSequential(nn.Sequential, TimestepBlock):\n    \"\"\"\n    A sequential module that passes timestep embeddings to the children that\n    support it as an extra input.\n    \"\"\"\n\n    def forward(\n        self,\n        x,\n        emb,\n        t_context=None,\n        v_context=None\n    ):\n        for layer in self:\n            if isinstance(layer, TimestepBlock):\n                x = layer(x, emb)\n            elif isinstance(layer, SpatialTransformer):\n                x = layer(x, t_context, v_context)\n            else:\n                x = layer(x)\n        return x\n\n\nclass Upsample(nn.Module):\n    \"\"\"\n    An upsampling layer with an optional convolution.\n    :param channels: channels in the inputs and outputs.\n    :param use_conv: a bool determining if a convolution is applied.\n    :param dims: determines if the signal is 1D, 2D, or 3D. If 3D, then\n                 upsampling occurs in the inner-two dimensions.\n    \"\"\"\n\n    def __init__(\n        self, channels, use_conv, dims=2, out_channels=None, padding=1, third_up=False\n    ):\n        super().__init__()\n        self.channels = channels\n        self.out_channels = out_channels or channels\n        self.use_conv = use_conv\n        self.dims = dims\n        self.third_up = third_up\n        if use_conv:\n            self.conv = conv_nd(\n                dims, self.channels, self.out_channels, 3, padding=padding\n            )\n\n    def forward(self, x):\n        assert x.shape[1] == self.channels\n        if self.dims == 3:\n            t_factor = 1 if not self.third_up else 2\n            x = F.interpolate(\n                x,\n                (t_factor * x.shape[2], x.shape[3] * 2, x.shape[4] * 2),\n                mode=\"nearest\",\n            )\n        else:\n            x = F.interpolate(x, scale_factor=2, mode=\"nearest\")\n        if self.use_conv:\n            x = self.conv(x)\n        return x\n\n\nclass Downsample(nn.Module):\n    \"\"\"\n    A downsampling layer with an optional convolution.\n    :param channels: channels in the inputs and outputs.\n    :param use_conv: a bool determining if a convolution is applied.\n    :param dims: determines if the signal is 1D, 2D, or 3D. If 3D, then\n                 downsampling occurs in the inner-two dimensions.\n    \"\"\"\n\n    def __init__(\n        self, channels, use_conv, dims=2, out_channels=None, padding=1, third_down=False\n    ):\n        super().__init__()\n        self.channels = channels\n        self.out_channels = out_channels or channels\n        self.use_conv = use_conv\n        self.dims = dims\n        stride = 2 if dims != 3 else ((1, 2, 2) if not third_down else (2, 2, 2))\n        if use_conv:\n            # print(f\"Building a Downsample layer with {dims} dims.\")\n            # print(\n            #     f\"  --> settings are: \\n in-chn: {self.channels}, out-chn: {self.out_channels}, \"\n            #     f\"kernel-size: 3, stride: {stride}, padding: {padding}\"\n            # )\n            if dims == 3:\n                pass\n                # print(f\"  --> Downsampling third axis (time): {third_down}\")\n            self.op = conv_nd(\n                dims,\n                self.channels,\n                self.out_channels,\n                3,\n                stride=stride,\n                padding=padding,\n            )\n        else:\n            assert self.channels == self.out_channels\n            self.op = avg_pool_nd(dims, kernel_size=stride, stride=stride)\n\n    def forward(self, x):\n        assert x.shape[1] == self.channels\n        return self.op(x)\n\n\nclass ResBlock(TimestepBlock):\n    \"\"\"\n    A residual block that can optionally change the number of channels.\n    \"\"\"\n\n    def __init__(\n        self,\n        channels,\n        emb_channels,\n        dropout,\n        out_channels=None,\n        use_conv=False,\n        use_scale_shift_norm=False,\n        dims=2,\n        up=False,\n        down=False,\n        kernel_size=3,\n        exchange_temb_dims=False,\n        skip_t_emb=False\n    ):\n        super().__init__()\n        self.channels = channels\n        self.emb_channels = emb_channels\n        self.dropout = dropout\n        self.out_channels = out_channels or channels\n        self.use_conv = use_conv\n        self.use_scale_shift_norm = use_scale_shift_norm\n        self.exchange_temb_dims = exchange_temb_dims\n\n        if isinstance(kernel_size, Iterable):\n            padding = [k // 2 for k in kernel_size]\n        else:\n            padding = kernel_size // 2\n\n        self.in_layers = nn.Sequential(\n            normalization(channels),\n            nn.SiLU(),\n            conv_nd(dims, channels, self.out_channels, kernel_size, padding=padding),\n        )\n\n        self.updown = up or down\n\n        if up:\n            self.h_upd = Upsample(channels, False, dims)\n            self.x_upd = Upsample(channels, False, dims)\n        elif down:\n            self.h_upd = Downsample(channels, False, dims)\n            self.x_upd = Downsample(channels, False, dims)\n        else:\n            self.h_upd = self.x_upd = nn.Identity()\n\n        self.skip_t_emb = skip_t_emb\n        self.emb_out_channels = (\n            2 * self.out_channels if use_scale_shift_norm else self.out_channels\n        )\n        if self.skip_t_emb:\n            print(f\"Skipping timestep embedding in {self.__class__.__name__}\")\n            assert not self.use_scale_shift_norm\n            self.emb_layers = None\n            self.exchange_temb_dims = False\n        else:\n            self.emb_layers = nn.Sequential(\n                nn.SiLU(),\n                linear(\n                    emb_channels,\n                    self.emb_out_channels,\n                ),\n            )\n\n        self.out_layers = nn.Sequential(\n            normalization(self.out_channels),\n            nn.SiLU(),\n            nn.Dropout(p=dropout),\n            zero_module(\n                conv_nd(\n                    dims,\n                    self.out_channels,\n                    self.out_channels,\n                    kernel_size,\n                    padding=padding,\n                )\n            ),\n        )\n\n        if self.out_channels == channels:\n            self.skip_connection = nn.Identity()\n        elif use_conv:\n            self.skip_connection = conv_nd(\n                dims, channels, self.out_channels, kernel_size, padding=padding\n            )\n        else:\n            self.skip_connection = conv_nd(dims, channels, self.out_channels, 1)\n\n    def forward(self, x, emb):\n        if self.updown:\n            in_rest, in_conv = self.in_layers[:-1], self.in_layers[-1]\n            h = in_rest(x)\n            h = self.h_upd(h)\n            x = self.x_upd(x)\n            h = in_conv(h)\n        else:\n            h = self.in_layers(x)\n\n        if self.skip_t_emb:\n            emb_out = th.zeros_like(h)\n        else:\n            emb_out = self.emb_layers(emb).type(h.dtype)\n        while len(emb_out.shape) < len(h.shape):\n            emb_out = emb_out[..., None]\n        if self.use_scale_shift_norm:\n            out_norm, out_rest = self.out_layers[0], self.out_layers[1:]\n            scale, shift = th.chunk(emb_out, 2, dim=1)\n            h = out_norm(h) * (1 + scale) + shift\n            h = out_rest(h)\n        else:\n            if self.exchange_temb_dims:\n                emb_out = rearrange(emb_out, \"b t c ... -> b c t ...\")\n            h = h + emb_out\n            h = self.out_layers(h)\n        return self.skip_connection(x) + h\n\n        \nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n\nclass UnifiedUNetModel(nn.Module):\n\n    def __init__(\n        self,\n        in_channels,\n        ctrl_channels,\n        model_channels,\n        out_channels,\n        num_res_blocks,\n        attention_resolutions,\n        dropout=0,\n        channel_mult=(1, 2, 4, 8),\n        save_attn_type=None,\n        save_attn_layers=[],\n        conv_resample=True,\n        dims=2,\n        use_label=None,\n        num_heads=-1,\n        num_head_channels=-1,\n        num_heads_upsample=-1,\n        use_scale_shift_norm=False,\n        resblock_updown=False,\n        transformer_depth=1,\n        t_context_dim=None, \n        v_context_dim=None,\n        num_attention_blocks=None,\n        use_linear_in_transformer=False,\n        adm_in_channels=None,\n        transformer_depth_middle=None\n    ):\n        super().__init__()\n\n        if num_heads_upsample == -1:\n            num_heads_upsample = num_heads\n\n        if num_heads == -1:\n            assert (\n                num_head_channels != -1\n            ), \"Either num_heads or num_head_channels has to be set\"\n\n        if num_head_channels == -1:\n            assert (\n                num_heads != -1\n            ), \"Either num_heads or num_head_channels has to be set\"\n\n        self.in_channels = in_channels\n        self.ctrl_channels = ctrl_channels\n        self.model_channels = model_channels\n        self.out_channels = out_channels\n\n        transformer_depth = len(channel_mult) * [transformer_depth]\n        transformer_depth_middle = default(transformer_depth_middle, transformer_depth[-1])\n\n        self.num_res_blocks = len(channel_mult) * [num_res_blocks]\n\n        self.attention_resolutions = attention_resolutions\n        self.dropout = dropout\n        self.channel_mult = channel_mult\n        self.conv_resample = conv_resample\n        self.use_label = use_label\n        self.num_heads = num_heads\n        self.num_head_channels = num_head_channels\n        self.num_heads_upsample = num_heads_upsample\n\n        time_embed_dim = model_channels * 4\n        self.time_embed = nn.Sequential(\n            linear(model_channels, time_embed_dim),\n            nn.SiLU(),\n            linear(time_embed_dim, time_embed_dim),\n        )\n        \n        if self.use_label is not None:\n            self.label_emb = nn.Sequential(\n                nn.Sequential(\n                    linear(adm_in_channels, time_embed_dim),\n                    nn.SiLU(),\n                    linear(time_embed_dim, time_embed_dim),\n                )\n            )\n\n        self.input_blocks = nn.ModuleList(\n            [\n                TimestepEmbedSequential(\n                    conv_nd(dims, in_channels, model_channels, 3, padding=1)\n                )\n            ]\n        )\n\n        if self.ctrl_channels > 0:\n            self.ctrl_block = TimestepEmbedSequential(\n                conv_nd(dims, ctrl_channels, 16, 3, padding=1),\n                nn.SiLU(),\n                conv_nd(dims, 16, 16, 3, padding=1),\n                nn.SiLU(),\n                conv_nd(dims, 16, 32, 3, padding=1),\n                nn.SiLU(),\n                conv_nd(dims, 32, 32, 3, padding=1),\n                nn.SiLU(),\n                conv_nd(dims, 32, 96, 3, padding=1),\n                nn.SiLU(),\n                conv_nd(dims, 96, 96, 3, padding=1),\n                nn.SiLU(),\n                conv_nd(dims, 96, 256, 3, padding=1),\n                nn.SiLU(),\n                zero_module(conv_nd(dims, 256, model_channels, 3, padding=1))\n            )\n        \n        self._feature_size = model_channels\n        input_block_chans = [model_channels]\n        ch = model_channels\n        ds = 1\n        for level, mult in enumerate(channel_mult):\n            for nr in range(self.num_res_blocks[level]):\n                layers = [\n                    ResBlock(\n                        ch,\n                        time_embed_dim,\n                        dropout,\n                        out_channels=mult * model_channels,\n                        dims=dims,\n                        use_scale_shift_norm=use_scale_shift_norm\n                    )\n                ]\n                ch = mult * model_channels\n                if ds in attention_resolutions:\n                    if num_head_channels == -1:\n                        dim_head = ch // num_heads\n                    else:\n                        num_heads = ch // num_head_channels\n                        dim_head = num_head_channels\n                    if (\n                        not exists(num_attention_blocks)\n                        or nr < num_attention_blocks[level]\n                    ):\n                        layers.append(\n                            SpatialTransformer(\n                                ch,\n                                num_heads,\n                                dim_head,\n                                depth=transformer_depth[level],\n                                t_context_dim=t_context_dim,\n                                v_context_dim=v_context_dim,\n                                use_linear=use_linear_in_transformer\n                            )\n                        )\n                self.input_blocks.append(TimestepEmbedSequential(*layers))\n                self._feature_size += ch\n                input_block_chans.append(ch)\n            if level != len(channel_mult) - 1:\n                out_ch = ch\n                self.input_blocks.append(\n                    TimestepEmbedSequential(\n                        ResBlock(\n                            ch,\n                            time_embed_dim,\n                            dropout,\n                            out_channels=out_ch,\n                            dims=dims,\n                            use_scale_shift_norm=use_scale_shift_norm,\n                            down=True\n                        )\n                        if resblock_updown\n                        else Downsample(\n                            ch, conv_resample, dims=dims, out_channels=out_ch\n                        )\n                    )\n                )\n                ch = out_ch\n                input_block_chans.append(ch)\n                ds *= 2\n                self._feature_size += ch\n\n        if num_head_channels == -1:\n            dim_head = ch // num_heads\n        else:\n            num_heads = ch // num_head_channels\n            dim_head = num_head_channels\n\n        self.middle_block = TimestepEmbedSequential(\n            ResBlock(\n                ch,\n                time_embed_dim,\n                dropout,\n                dims=dims,\n                use_scale_shift_norm=use_scale_shift_norm\n            ),\n            SpatialTransformer(  # always uses a self-attn\n                ch,\n                num_heads,\n                dim_head,\n                depth=transformer_depth_middle,\n                t_context_dim=t_context_dim,\n                v_context_dim=v_context_dim,\n                use_linear=use_linear_in_transformer\n            ),\n            ResBlock(\n                ch,\n                time_embed_dim,\n                dropout,\n                dims=dims,\n                use_scale_shift_norm=use_scale_shift_norm\n            )\n        )\n\n        self._feature_size += ch\n\n        self.output_blocks = nn.ModuleList([])\n        for level, mult in list(enumerate(channel_mult))[::-1]:\n            for i in range(self.num_res_blocks[level] + 1):\n                ich = input_block_chans.pop()\n                layers = [\n                    ResBlock(\n                        ch + ich,\n                        time_embed_dim,\n                        dropout,\n                        out_channels=model_channels * mult,\n                        dims=dims,\n                        use_scale_shift_norm=use_scale_shift_norm\n                    )\n                ]\n                ch = model_channels * mult\n                if ds in attention_resolutions:\n                    if num_head_channels == -1:\n                        dim_head = ch // num_heads\n                    else:\n                        num_heads = ch // num_head_channels\n                        dim_head = num_head_channels\n                    if (\n                        not exists(num_attention_blocks)\n                        or i < num_attention_blocks[level]\n                    ):\n                        layers.append(\n                            SpatialTransformer(\n                                ch,\n                                num_heads,\n                                dim_head,\n                                depth=transformer_depth[level],\n                                t_context_dim=t_context_dim,\n                                v_context_dim=v_context_dim,\n                                use_linear=use_linear_in_transformer\n                            )\n                        )\n                if level and i == self.num_res_blocks[level]:\n                    out_ch = ch\n                    layers.append(\n                        ResBlock(\n                            ch,\n                            time_embed_dim,\n                            dropout,\n                            out_channels=out_ch,\n                            dims=dims,\n                            use_scale_shift_norm=use_scale_shift_norm,\n                            up=True\n                        )\n                        if resblock_updown\n                        else Upsample(ch, conv_resample, dims=dims, out_channels=out_ch)\n                    )\n                    ds //= 2\n                self.output_blocks.append(TimestepEmbedSequential(*layers))\n                self._feature_size += ch\n\n        self.out = nn.Sequential(\n            normalization(ch),\n            nn.SiLU(),\n            zero_module(conv_nd(dims, model_channels, out_channels, 3, padding=1))\n        )\n        \n        # cache attn map\n        self.attn_type = save_attn_type\n        self.attn_layers = save_attn_layers\n        self.attn_map_cache = []\n        for name, module in self.named_modules():\n            if any([name.endswith(attn_type) for attn_type in self.attn_type]):\n                item = {\"name\": name, \"heads\": module.heads, \"size\": None, \"attn_map\": None}\n                self.attn_map_cache.append(item)\n                module.attn_map_cache = item\n\n    def clear_attn_map(self):\n\n        for item in self.attn_map_cache:\n            if item[\"attn_map\"] is not None:\n                del item[\"attn_map\"]\n                item[\"attn_map\"] = None\n\n    def save_attn_map(self, attn_type=\"t_attn\", save_name=\"temp\", tokens=\"\"):\n\n        attn_maps = []\n        for item in self.attn_map_cache:\n            name = item[\"name\"]\n            if any([name.startswith(block) for block in self.attn_layers]) and name.endswith(attn_type):\n                heads = item[\"heads\"]\n                attn_maps.append(item[\"attn_map\"].detach().cpu())\n\n        attn_map = th.stack(attn_maps, dim=0)\n        attn_map = th.mean(attn_map, dim=0)\n\n        # attn_map: bh * n * l\n        bh, n, l = attn_map.shape # bh: batch size * heads / n : pixel length(h*w) / l: token length\n        attn_map = attn_map.reshape((-1,heads,n,l)).mean(dim=1)\n        b = attn_map.shape[0]\n\n        h = w = int(n**0.5)\n        attn_map = attn_map.permute(0,2,1).reshape((b,l,h,w)).numpy()\n        attn_map_i = attn_map[-1]\n\n        l = attn_map_i.shape[0]\n        fig = plt.figure(figsize=(12, 8), dpi=300)\n        for j in range(12):\n            if j >= l: break\n            ax = fig.add_subplot(3, 4, j+1)\n            sns.heatmap(attn_map_i[j], square=True, xticklabels=False, yticklabels=False)\n            if j < len(tokens):\n                ax.set_title(tokens[j])\n        fig.savefig(f\"temp/attn_map/attn_map_{save_name}.png\")\n        plt.close()\n\n        return attn_map_i\n    \n    def forward(self, x, timesteps=None, t_context=None, v_context=None, y=None, **kwargs):\n\n        assert (y is not None) == (\n            self.use_label is not None\n        ), \"must specify y if and only if the model is class-conditional\"\n\n        self.clear_attn_map()\n\n        hs = []\n        t_emb = timestep_embedding(timesteps, self.model_channels, repeat_only=False)\n        emb = self.time_embed(t_emb)\n\n        if self.use_label is not None:\n            assert y.shape[0] == x.shape[0]\n            emb = emb + self.label_emb(y)\n\n        h = x\n        if self.ctrl_channels > 0:\n            in_h, add_h = th.split(h, [self.in_channels, self.ctrl_channels], dim=1)\n        for i, module in enumerate(self.input_blocks):\n            if self.ctrl_channels > 0 and i == 0:\n                h = module(in_h, emb, t_context, v_context) + self.ctrl_block(add_h, emb, t_context, v_context)\n            else:\n                h = module(h, emb, t_context, v_context)\n            hs.append(h)\n        h = self.middle_block(h, emb, t_context, v_context)\n        for i, module in enumerate(self.output_blocks):\n            h = th.cat([h, hs.pop()], dim=1)\n            h = module(h, emb, t_context, v_context)\n        h = h.type(x.dtype)\n\n        return self.out(h)"
  },
  {
    "path": "sgm/modules/diffusionmodules/sampling.py",
    "content": "\"\"\"\n    Partially ported from https://github.com/crowsonkb/k-diffusion/blob/master/k_diffusion/sampling.py\n\"\"\"\n\n\nfrom typing import Dict, Union\n\nimport imageio\nimport torch\nimport numpy as np\nimport torch.nn.functional as F\nfrom omegaconf import ListConfig, OmegaConf\nfrom tqdm import tqdm\n\nfrom ...modules.diffusionmodules.sampling_utils import (\n    get_ancestral_step,\n    linear_multistep_coeff,\n    to_d,\n    to_neg_log_sigma,\n    to_sigma,\n)\nfrom ...util import append_dims, default, instantiate_from_config\nfrom torchvision.utils import save_image\n\nDEFAULT_GUIDER = {\"target\": \"sgm.modules.diffusionmodules.guiders.IdentityGuider\"}\n\n\nclass BaseDiffusionSampler:\n    def __init__(\n        self,\n        discretization_config: Union[Dict, ListConfig, OmegaConf],\n        num_steps: Union[int, None] = None,\n        guider_config: Union[Dict, ListConfig, OmegaConf, None] = None,\n        verbose: bool = False,\n        device: str = \"cuda\",\n    ):\n        self.num_steps = num_steps\n        self.discretization = instantiate_from_config(discretization_config)\n        self.guider = instantiate_from_config(\n            default(\n                guider_config,\n                DEFAULT_GUIDER,\n            )\n        )\n        self.verbose = verbose\n        self.device = device\n\n    def prepare_sampling_loop(self, x, cond, uc=None, num_steps=None):\n        sigmas = self.discretization(\n            self.num_steps if num_steps is None else num_steps, device=self.device\n        )\n        uc = default(uc, cond)\n\n        x *= torch.sqrt(1.0 + sigmas[0] ** 2.0)\n        num_sigmas = len(sigmas)\n\n        s_in = x.new_ones([x.shape[0]])\n\n        return x, s_in, sigmas, num_sigmas, cond, uc\n\n    def denoise(self, x, model, sigma, cond, uc):\n        denoised = model.denoiser(model.model, *self.guider.prepare_inputs(x, sigma, cond, uc))\n        denoised = self.guider(denoised, sigma)\n        return denoised\n\n    def get_sigma_gen(self, num_sigmas, init_step=0):\n        sigma_generator = range(init_step, num_sigmas - 1)\n        if self.verbose:\n            print(\"#\" * 30, \" Sampling setting \", \"#\" * 30)\n            print(f\"Sampler: {self.__class__.__name__}\")\n            print(f\"Discretization: {self.discretization.__class__.__name__}\")\n            print(f\"Guider: {self.guider.__class__.__name__}\")\n            sigma_generator = tqdm(\n                sigma_generator,\n                total=num_sigmas-1-init_step,\n                desc=f\"Sampling with {self.__class__.__name__} for {num_sigmas-1-init_step} steps\",\n            )\n        return sigma_generator\n\n\nclass SingleStepDiffusionSampler(BaseDiffusionSampler):\n    def sampler_step(self, sigma, next_sigma, denoiser, x, cond, uc, *args, **kwargs):\n        raise NotImplementedError\n\n    def euler_step(self, x, d, dt):\n        return x + dt * d\n\n\nclass EDMSampler(SingleStepDiffusionSampler):\n    def __init__(\n        self, s_churn=0.0, s_tmin=0.0, s_tmax=float(\"inf\"), s_noise=1.0, *args, **kwargs\n    ):\n        super().__init__(*args, **kwargs)\n\n        self.s_churn = s_churn\n        self.s_tmin = s_tmin\n        self.s_tmax = s_tmax\n        self.s_noise = s_noise\n\n    def sampler_step(self, sigma, next_sigma, denoiser, x, cond, uc=None, gamma=0.0):\n        sigma_hat = sigma * (gamma + 1.0)\n        if gamma > 0:\n            eps = torch.randn_like(x) * self.s_noise\n            x = x + eps * append_dims(sigma_hat**2 - sigma**2, x.ndim) ** 0.5\n\n        denoised = self.denoise(x, denoiser, sigma_hat, cond, uc)\n        d = to_d(x, sigma_hat, denoised)\n        dt = append_dims(next_sigma - sigma_hat, x.ndim)\n\n        euler_step = self.euler_step(x, d, dt)\n        x = self.possible_correction_step(\n            euler_step, x, d, dt, next_sigma, denoiser, cond, uc\n        )\n        return x\n\n    def __call__(self, denoiser, x, cond, uc=None, num_steps=None):\n        x, s_in, sigmas, num_sigmas, cond, uc = self.prepare_sampling_loop(\n            x, cond, uc, num_steps\n        )\n\n        for i in self.get_sigma_gen(num_sigmas):\n            gamma = (\n                min(self.s_churn / (num_sigmas - 1), 2**0.5 - 1)\n                if self.s_tmin <= sigmas[i] <= self.s_tmax\n                else 0.0\n            )\n            x = self.sampler_step(\n                s_in * sigmas[i],\n                s_in * sigmas[i + 1],\n                denoiser,\n                x,\n                cond,\n                uc,\n                gamma,\n            )\n\n        return x\n\n\nclass AncestralSampler(SingleStepDiffusionSampler):\n    def __init__(self, eta=1.0, s_noise=1.0, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        self.eta = eta\n        self.s_noise = s_noise\n        self.noise_sampler = lambda x: torch.randn_like(x)\n\n    def ancestral_euler_step(self, x, denoised, sigma, sigma_down):\n        d = to_d(x, sigma, denoised)\n        dt = append_dims(sigma_down - sigma, x.ndim)\n\n        return self.euler_step(x, d, dt)\n\n    def ancestral_step(self, x, sigma, next_sigma, sigma_up):\n        x = torch.where(\n            append_dims(next_sigma, x.ndim) > 0.0,\n            x + self.noise_sampler(x) * self.s_noise * append_dims(sigma_up, x.ndim),\n            x,\n        )\n        return x\n\n    def __call__(self, denoiser, x, cond, uc=None, num_steps=None):\n        x, s_in, sigmas, num_sigmas, cond, uc = self.prepare_sampling_loop(\n            x, cond, uc, num_steps\n        )\n\n        for i in self.get_sigma_gen(num_sigmas):\n            x = self.sampler_step(\n                s_in * sigmas[i],\n                s_in * sigmas[i + 1],\n                denoiser,\n                x,\n                cond,\n                uc,\n            )\n\n        return x\n\n\nclass LinearMultistepSampler(BaseDiffusionSampler):\n    def __init__(\n        self,\n        order=4,\n        *args,\n        **kwargs,\n    ):\n        super().__init__(*args, **kwargs)\n\n        self.order = order\n\n    def __call__(self, denoiser, x, cond, uc=None, num_steps=None, **kwargs):\n        x, s_in, sigmas, num_sigmas, cond, uc = self.prepare_sampling_loop(\n            x, cond, uc, num_steps\n        )\n\n        ds = []\n        sigmas_cpu = sigmas.detach().cpu().numpy()\n        for i in self.get_sigma_gen(num_sigmas):\n            sigma = s_in * sigmas[i]\n            denoised = denoiser(\n                *self.guider.prepare_inputs(x, sigma, cond, uc), **kwargs\n            )\n            denoised = self.guider(denoised, sigma)\n            d = to_d(x, sigma, denoised)\n            ds.append(d)\n            if len(ds) > self.order:\n                ds.pop(0)\n            cur_order = min(i + 1, self.order)\n            coeffs = [\n                linear_multistep_coeff(cur_order, sigmas_cpu, i, j)\n                for j in range(cur_order)\n            ]\n            x = x + sum(coeff * d for coeff, d in zip(coeffs, reversed(ds)))\n\n        return x\n\n\nclass EulerEDMSampler(EDMSampler):\n\n    def possible_correction_step(\n        self, euler_step, x, d, dt, next_sigma, denoiser, cond, uc\n    ):\n        return euler_step\n\n    def get_c_noise(self, x, model, sigma):\n        sigma = model.denoiser.possibly_quantize_sigma(sigma)\n        sigma_shape = sigma.shape\n        sigma = append_dims(sigma, x.ndim)\n        c_skip, c_out, c_in, c_noise = model.denoiser.scaling(sigma)\n        c_noise = model.denoiser.possibly_quantize_c_noise(c_noise.reshape(sigma_shape))\n        return c_noise\n    \n    def attend_and_excite(self, x, model, sigma, cond, batch, alpha, iter_enabled, thres, max_iter=20):\n\n        # calc timestep\n        c_noise = self.get_c_noise(x, model, sigma)\n        \n        x = x.clone().detach().requires_grad_(True)  # https://github.com/yuval-alaluf/Attend-and-Excite/blob/main/pipeline_attend_and_excite.py#L288\n\n        iters = 0\n        while True:\n\n            model_output = model.model(x, c_noise, cond)\n            local_loss = model.loss_fn.get_min_local_loss(model.model.diffusion_model.attn_map_cache, batch[\"mask\"], batch[\"seg_mask\"])\n            grad = torch.autograd.grad(local_loss.requires_grad_(True), [x], retain_graph=True)[0]\n            x = x - alpha * grad\n            iters += 1\n\n            if not iter_enabled or local_loss <= thres or iters > max_iter:\n                break\n\n        return x\n\n    def save_segment_map(self, attn_maps, tokens=None, save_name=None):\n\n        sections = []\n        for i in range(len(tokens)): \n            attn_map = attn_maps[i]\n            sections.append(attn_map)\n        \n        section = np.stack(sections)\n        np.save(f\"./temp/seg_map/seg_{save_name}.npy\", section)\n\n    def get_init_noise(self, cfgs, model, cond, batch, uc=None):\n\n        H, W = batch[\"target_size_as_tuple\"][0]\n        shape = (cfgs.batch_size, cfgs.channel, int(H) // cfgs.factor, int(W) // cfgs.factor)\n\n        randn = torch.randn(shape).to(torch.device(\"cuda\", index=cfgs.gpu))\n        x = randn.clone()\n\n        xs = []\n        self.verbose = False\n        for _ in range(cfgs.noise_iters):\n            \n            x, s_in, sigmas, num_sigmas, cond, uc = self.prepare_sampling_loop(\n                x, cond, uc, num_steps=2\n            )\n\n            superv = {\n                \"mask\": batch[\"mask\"] if \"mask\" in batch else None,\n                \"seg_mask\": batch[\"seg_mask\"] if \"seg_mask\" in batch else None\n            }\n\n            local_losses = []\n\n            for i in self.get_sigma_gen(num_sigmas):\n\n                gamma = (\n                    min(self.s_churn / (num_sigmas - 1), 2**0.5 - 1)\n                    if self.s_tmin <= sigmas[i] <= self.s_tmax\n                    else 0.0\n                )\n\n                x, inter, local_loss = self.sampler_step(\n                    s_in * sigmas[i],\n                    s_in * sigmas[i + 1],\n                    model,\n                    x,\n                    cond,\n                    superv,\n                    uc,\n                    gamma,\n                    save_loss=True\n                )\n\n                local_losses.append(local_loss.item())\n            \n            xs.append((randn, local_losses[-1]))\n\n            randn = torch.randn(shape).to(torch.device(\"cuda\", index=cfgs.gpu))\n            x = randn.clone()\n\n        self.verbose = True\n        \n        xs.sort(key = lambda x: x[-1])\n\n        if len(xs) > 0:\n            print(f\"Init local loss: Best {xs[0][1]} Worst {xs[-1][1]}\")\n            x = xs[0][0]\n\n        return x\n\n    def sampler_step(self, sigma, next_sigma, model, x, cond, batch=None, uc=None, \n                     gamma=0.0, alpha=0, iter_enabled=False, thres=None, update=False,\n                     name=None, save_loss=False, save_attn=False, save_inter=False):\n        \n        sigma_hat = sigma * (gamma + 1.0)\n        if gamma > 0:\n            eps = torch.randn_like(x) * self.s_noise\n            x = x + eps * append_dims(sigma_hat**2 - sigma**2, x.ndim) ** 0.5\n        \n        if update:\n            x = self.attend_and_excite(x, model, sigma_hat, cond, batch, alpha, iter_enabled, thres)\n\n        denoised = self.denoise(x, model, sigma_hat, cond, uc)\n        denoised_decode = model.decode_first_stage(denoised) if save_inter else None\n        \n        if save_loss:\n            local_loss = model.loss_fn.get_min_local_loss(model.model.diffusion_model.attn_map_cache, batch[\"mask\"], batch[\"seg_mask\"])\n            local_loss = local_loss[local_loss.shape[0]//2:]\n        else:\n            local_loss = torch.zeros(1)\n        if save_attn:\n            attn_map = model.model.diffusion_model.save_attn_map(save_name=name, tokens=batch[\"label\"][0])\n            self.save_segment_map(attn_map, tokens=batch[\"label\"][0], save_name=name)\n\n        d = to_d(x, sigma_hat, denoised)\n        dt = append_dims(next_sigma - sigma_hat, x.ndim)\n\n        euler_step = self.euler_step(x, d, dt)\n\n        return euler_step, denoised_decode, local_loss\n    \n    def __call__(self, model, x, cond, batch=None, uc=None, num_steps=None, init_step=0, \n                 name=None, aae_enabled=False, detailed=False):\n\n        x, s_in, sigmas, num_sigmas, cond, uc = self.prepare_sampling_loop(\n            x, cond, uc, num_steps\n        )\n\n        name = batch[\"name\"][0]\n        inters = []\n        local_losses = []\n        scales = np.linspace(start=1.0, stop=0, num=num_sigmas)\n        iter_lst = np.linspace(start=5, stop=25, num=6, dtype=np.int32)\n        thres_lst = np.linspace(start=-0.5, stop=-0.8, num=6)\n\n        for i in self.get_sigma_gen(num_sigmas, init_step=init_step):\n\n            gamma = (\n                min(self.s_churn / (num_sigmas - 1), 2**0.5 - 1)\n                if self.s_tmin <= sigmas[i] <= self.s_tmax\n                else 0.0\n            )\n\n            alpha = 20 * np.sqrt(scales[i])\n            update = aae_enabled\n            save_loss = aae_enabled\n            save_attn = detailed and (i == (num_sigmas-1)//2)\n            save_inter = aae_enabled\n\n            if i in iter_lst:\n                iter_enabled = True\n                thres = thres_lst[list(iter_lst).index(i)]\n            else:\n                iter_enabled = False\n                thres = 0.0\n\n            x, inter, local_loss = self.sampler_step(\n                s_in * sigmas[i],\n                s_in * sigmas[i + 1],\n                model,\n                x,\n                cond,\n                batch,\n                uc,\n                gamma,\n                alpha=alpha,\n                iter_enabled=iter_enabled,\n                thres=thres,\n                update=update,\n                name=name,\n                save_loss=save_loss,\n                save_attn=save_attn,\n                save_inter=save_inter\n            )\n\n            local_losses.append(local_loss.item())\n            if inter is not None:\n                inter = torch.clamp((inter + 1.0) / 2.0, min=0.0, max=1.0)[0]\n                inter = inter.cpu().numpy().transpose(1, 2, 0) * 255\n                inters.append(inter.astype(np.uint8))\n\n        print(f\"Local losses: {local_losses}\")\n\n        if len(inters) > 0:\n            imageio.mimsave(f\"./temp/inters/{name}.gif\", inters, 'GIF', duration=0.02)\n\n        return x\n\n\nclass HeunEDMSampler(EDMSampler):\n    def possible_correction_step(\n        self, euler_step, x, d, dt, next_sigma, denoiser, cond, uc\n    ):\n        if torch.sum(next_sigma) < 1e-14:\n            # Save a network evaluation if all noise levels are 0\n            return euler_step\n        else:\n            denoised = self.denoise(euler_step, denoiser, next_sigma, cond, uc)\n            d_new = to_d(euler_step, next_sigma, denoised)\n            d_prime = (d + d_new) / 2.0\n\n            # apply correction if noise level is not 0\n            x = torch.where(\n                append_dims(next_sigma, x.ndim) > 0.0, x + d_prime * dt, euler_step\n            )\n            return x\n\n\nclass EulerAncestralSampler(AncestralSampler):\n    def sampler_step(self, sigma, next_sigma, denoiser, x, cond, uc):\n        sigma_down, sigma_up = get_ancestral_step(sigma, next_sigma, eta=self.eta)\n        denoised = self.denoise(x, denoiser, sigma, cond, uc)\n        x = self.ancestral_euler_step(x, denoised, sigma, sigma_down)\n        x = self.ancestral_step(x, sigma, next_sigma, sigma_up)\n\n        return x\n\n\nclass DPMPP2SAncestralSampler(AncestralSampler):\n    def get_variables(self, sigma, sigma_down):\n        t, t_next = [to_neg_log_sigma(s) for s in (sigma, sigma_down)]\n        h = t_next - t\n        s = t + 0.5 * h\n        return h, s, t, t_next\n\n    def get_mult(self, h, s, t, t_next):\n        mult1 = to_sigma(s) / to_sigma(t)\n        mult2 = (-0.5 * h).expm1()\n        mult3 = to_sigma(t_next) / to_sigma(t)\n        mult4 = (-h).expm1()\n\n        return mult1, mult2, mult3, mult4\n\n    def sampler_step(self, sigma, next_sigma, denoiser, x, cond, uc=None, **kwargs):\n        sigma_down, sigma_up = get_ancestral_step(sigma, next_sigma, eta=self.eta)\n        denoised = self.denoise(x, denoiser, sigma, cond, uc)\n        x_euler = self.ancestral_euler_step(x, denoised, sigma, sigma_down)\n\n        if torch.sum(sigma_down) < 1e-14:\n            # Save a network evaluation if all noise levels are 0\n            x = x_euler\n        else:\n            h, s, t, t_next = self.get_variables(sigma, sigma_down)\n            mult = [\n                append_dims(mult, x.ndim) for mult in self.get_mult(h, s, t, t_next)\n            ]\n\n            x2 = mult[0] * x - mult[1] * denoised\n            denoised2 = self.denoise(x2, denoiser, to_sigma(s), cond, uc)\n            x_dpmpp2s = mult[2] * x - mult[3] * denoised2\n\n            # apply correction if noise level is not 0\n            x = torch.where(append_dims(sigma_down, x.ndim) > 0.0, x_dpmpp2s, x_euler)\n\n        x = self.ancestral_step(x, sigma, next_sigma, sigma_up)\n        return x\n\n\nclass DPMPP2MSampler(BaseDiffusionSampler):\n    def get_variables(self, sigma, next_sigma, previous_sigma=None):\n        t, t_next = [to_neg_log_sigma(s) for s in (sigma, next_sigma)]\n        h = t_next - t\n\n        if previous_sigma is not None:\n            h_last = t - to_neg_log_sigma(previous_sigma)\n            r = h_last / h\n            return h, r, t, t_next\n        else:\n            return h, None, t, t_next\n\n    def get_mult(self, h, r, t, t_next, previous_sigma):\n        mult1 = to_sigma(t_next) / to_sigma(t)\n        mult2 = (-h).expm1()\n\n        if previous_sigma is not None:\n            mult3 = 1 + 1 / (2 * r)\n            mult4 = 1 / (2 * r)\n            return mult1, mult2, mult3, mult4\n        else:\n            return mult1, mult2\n\n    def sampler_step(\n        self,\n        old_denoised,\n        previous_sigma,\n        sigma,\n        next_sigma,\n        denoiser,\n        x,\n        cond,\n        uc=None,\n    ):\n        denoised = self.denoise(x, denoiser, sigma, cond, uc)\n\n        h, r, t, t_next = self.get_variables(sigma, next_sigma, previous_sigma)\n        mult = [\n            append_dims(mult, x.ndim)\n            for mult in self.get_mult(h, r, t, t_next, previous_sigma)\n        ]\n\n        x_standard = mult[0] * x - mult[1] * denoised\n        if old_denoised is None or torch.sum(next_sigma) < 1e-14:\n            # Save a network evaluation if all noise levels are 0 or on the first step\n            return x_standard, denoised\n        else:\n            denoised_d = mult[2] * denoised - mult[3] * old_denoised\n            x_advanced = mult[0] * x - mult[1] * denoised_d\n\n            # apply correction if noise level is not 0 and not first step\n            x = torch.where(\n                append_dims(next_sigma, x.ndim) > 0.0, x_advanced, x_standard\n            )\n\n        return x, denoised\n\n    def __call__(self, denoiser, x, cond, uc=None, num_steps=None, init_step=0, **kwargs):\n        x, s_in, sigmas, num_sigmas, cond, uc = self.prepare_sampling_loop(\n            x, cond, uc, num_steps\n        )\n\n        old_denoised = None\n        for i in self.get_sigma_gen(num_sigmas, init_step=init_step):\n            x, old_denoised = self.sampler_step(\n                old_denoised,\n                None if i == 0 else s_in * sigmas[i - 1],\n                s_in * sigmas[i],\n                s_in * sigmas[i + 1],\n                denoiser,\n                x,\n                cond,\n                uc=uc,\n            )\n\n        return x\n"
  },
  {
    "path": "sgm/modules/diffusionmodules/sampling_utils.py",
    "content": "import torch\nfrom scipy import integrate\n\nfrom ...util import append_dims\n\n\nclass NoDynamicThresholding:\n    def __call__(self, uncond, cond, scale):\n        return uncond + scale * (cond - uncond)\n    \n\ndef linear_multistep_coeff(order, t, i, j, epsrel=1e-4):\n    if order - 1 > i:\n        raise ValueError(f\"Order {order} too high for step {i}\")\n\n    def fn(tau):\n        prod = 1.0\n        for k in range(order):\n            if j == k:\n                continue\n            prod *= (tau - t[i - k]) / (t[i - j] - t[i - k])\n        return prod\n\n    return integrate.quad(fn, t[i], t[i + 1], epsrel=epsrel)[0]\n\n\ndef get_ancestral_step(sigma_from, sigma_to, eta=1.0):\n    if not eta:\n        return sigma_to, 0.0\n    sigma_up = torch.minimum(\n        sigma_to,\n        eta\n        * (sigma_to**2 * (sigma_from**2 - sigma_to**2) / sigma_from**2) ** 0.5,\n    )\n    sigma_down = (sigma_to**2 - sigma_up**2) ** 0.5\n    return sigma_down, sigma_up\n\n\ndef to_d(x, sigma, denoised):\n    return (x - denoised) / append_dims(sigma, x.ndim)\n\n\ndef to_neg_log_sigma(sigma):\n    return sigma.log().neg()\n\n\ndef to_sigma(neg_log_sigma):\n    return neg_log_sigma.neg().exp()\n"
  },
  {
    "path": "sgm/modules/diffusionmodules/sigma_sampling.py",
    "content": "import torch\n\nfrom ...util import default, instantiate_from_config\n\n\nclass EDMSampling:\n    def __init__(self, p_mean=-1.2, p_std=1.2):\n        self.p_mean = p_mean\n        self.p_std = p_std\n\n    def __call__(self, n_samples, rand=None):\n        log_sigma = self.p_mean + self.p_std * default(rand, torch.randn((n_samples,)))\n        return log_sigma.exp()\n\n\nclass DiscreteSampling:\n    def __init__(self, discretization_config, num_idx, do_append_zero=False, flip=True):\n        self.num_idx = num_idx\n        self.sigmas = instantiate_from_config(discretization_config)(\n            num_idx, do_append_zero=do_append_zero, flip=flip\n        )\n\n    def idx_to_sigma(self, idx):\n        return self.sigmas[idx]\n\n    def __call__(self, n_samples, rand=None):\n        idx = default(\n            rand,\n            torch.randint(0, self.num_idx, (n_samples,)),\n        )\n        return self.idx_to_sigma(idx)\n"
  },
  {
    "path": "sgm/modules/diffusionmodules/util.py",
    "content": "\"\"\"\nadopted from\nhttps://github.com/openai/improved-diffusion/blob/main/improved_diffusion/gaussian_diffusion.py\nand\nhttps://github.com/lucidrains/denoising-diffusion-pytorch/blob/7706bdfc6f527f58d33f84b7b522e61e6e3164b3/denoising_diffusion_pytorch/denoising_diffusion_pytorch.py\nand\nhttps://github.com/openai/guided-diffusion/blob/0ba878e517b276c45d1195eb29f6f5f72659a05b/guided_diffusion/nn.py\n\nthanks!\n\"\"\"\n\nimport math\n\nimport torch\nimport torch.nn as nn\nfrom einops import repeat\n\n\ndef make_beta_schedule(\n    schedule,\n    n_timestep,\n    linear_start=1e-4,\n    linear_end=2e-2,\n):\n    if schedule == \"linear\":\n        betas = (\n            torch.linspace(\n                linear_start**0.5, linear_end**0.5, n_timestep, dtype=torch.float64\n            )\n            ** 2\n        )\n    return betas.numpy()\n\n\ndef extract_into_tensor(a, t, x_shape):\n    b, *_ = t.shape\n    out = a.gather(-1, t)\n    return out.reshape(b, *((1,) * (len(x_shape) - 1)))\n\n\ndef mixed_checkpoint(func, inputs: dict, params, flag):\n    \"\"\"\n    Evaluate a function without caching intermediate activations, allowing for\n    reduced memory at the expense of extra compute in the backward pass. This differs from the original checkpoint function\n    borrowed from https://github.com/openai/guided-diffusion/blob/0ba878e517b276c45d1195eb29f6f5f72659a05b/guided_diffusion/nn.py in that\n    it also works with non-tensor inputs\n    :param func: the function to evaluate.\n    :param inputs: the argument dictionary to pass to `func`.\n    :param params: a sequence of parameters `func` depends on but does not\n                   explicitly take as arguments.\n    :param flag: if False, disable gradient checkpointing.\n    \"\"\"\n    if flag:\n        tensor_keys = [key for key in inputs if isinstance(inputs[key], torch.Tensor)]\n        tensor_inputs = [\n            inputs[key] for key in inputs if isinstance(inputs[key], torch.Tensor)\n        ]\n        non_tensor_keys = [\n            key for key in inputs if not isinstance(inputs[key], torch.Tensor)\n        ]\n        non_tensor_inputs = [\n            inputs[key] for key in inputs if not isinstance(inputs[key], torch.Tensor)\n        ]\n        args = tuple(tensor_inputs) + tuple(non_tensor_inputs) + tuple(params)\n        return MixedCheckpointFunction.apply(\n            func,\n            len(tensor_inputs),\n            len(non_tensor_inputs),\n            tensor_keys,\n            non_tensor_keys,\n            *args,\n        )\n    else:\n        return func(**inputs)\n\n\nclass MixedCheckpointFunction(torch.autograd.Function):\n    @staticmethod\n    def forward(\n        ctx,\n        run_function,\n        length_tensors,\n        length_non_tensors,\n        tensor_keys,\n        non_tensor_keys,\n        *args,\n    ):\n        ctx.end_tensors = length_tensors\n        ctx.end_non_tensors = length_tensors + length_non_tensors\n        ctx.gpu_autocast_kwargs = {\n            \"enabled\": torch.is_autocast_enabled(),\n            \"dtype\": torch.get_autocast_gpu_dtype(),\n            \"cache_enabled\": torch.is_autocast_cache_enabled(),\n        }\n        assert (\n            len(tensor_keys) == length_tensors\n            and len(non_tensor_keys) == length_non_tensors\n        )\n\n        ctx.input_tensors = {\n            key: val for (key, val) in zip(tensor_keys, list(args[: ctx.end_tensors]))\n        }\n        ctx.input_non_tensors = {\n            key: val\n            for (key, val) in zip(\n                non_tensor_keys, list(args[ctx.end_tensors : ctx.end_non_tensors])\n            )\n        }\n        ctx.run_function = run_function\n        ctx.input_params = list(args[ctx.end_non_tensors :])\n\n        with torch.no_grad():\n            output_tensors = ctx.run_function(\n                **ctx.input_tensors, **ctx.input_non_tensors\n            )\n        return output_tensors\n\n    @staticmethod\n    def backward(ctx, *output_grads):\n        # additional_args = {key: ctx.input_tensors[key] for key in ctx.input_tensors if not isinstance(ctx.input_tensors[key],torch.Tensor)}\n        ctx.input_tensors = {\n            key: ctx.input_tensors[key].detach().requires_grad_(True)\n            for key in ctx.input_tensors\n        }\n\n        with torch.enable_grad(), torch.cuda.amp.autocast(**ctx.gpu_autocast_kwargs):\n            # Fixes a bug where the first op in run_function modifies the\n            # Tensor storage in place, which is not allowed for detach()'d\n            # Tensors.\n            shallow_copies = {\n                key: ctx.input_tensors[key].view_as(ctx.input_tensors[key])\n                for key in ctx.input_tensors\n            }\n            # shallow_copies.update(additional_args)\n            output_tensors = ctx.run_function(**shallow_copies, **ctx.input_non_tensors)\n        input_grads = torch.autograd.grad(\n            output_tensors,\n            list(ctx.input_tensors.values()) + ctx.input_params,\n            output_grads,\n            allow_unused=True,\n        )\n        del ctx.input_tensors\n        del ctx.input_params\n        del output_tensors\n        return (\n            (None, None, None, None, None)\n            + input_grads[: ctx.end_tensors]\n            + (None,) * (ctx.end_non_tensors - ctx.end_tensors)\n            + input_grads[ctx.end_tensors :]\n        )\n\n\ndef checkpoint(func, inputs, params, flag):\n    \"\"\"\n    Evaluate a function without caching intermediate activations, allowing for\n    reduced memory at the expense of extra compute in the backward pass.\n    :param func: the function to evaluate.\n    :param inputs: the argument sequence to pass to `func`.\n    :param params: a sequence of parameters `func` depends on but does not\n                   explicitly take as arguments.\n    :param flag: if False, disable gradient checkpointing.\n    \"\"\"\n    if flag:\n        args = tuple(inputs) + tuple(params)\n        return CheckpointFunction.apply(func, len(inputs), *args)\n    else:\n        return func(*inputs)\n\n\nclass CheckpointFunction(torch.autograd.Function):\n    @staticmethod\n    def forward(ctx, run_function, length, *args):\n        ctx.run_function = run_function\n        ctx.input_tensors = list(args[:length])\n        ctx.input_params = list(args[length:])\n        ctx.gpu_autocast_kwargs = {\n            \"enabled\": torch.is_autocast_enabled(),\n            \"dtype\": torch.get_autocast_gpu_dtype(),\n            \"cache_enabled\": torch.is_autocast_cache_enabled(),\n        }\n        with torch.no_grad():\n            output_tensors = ctx.run_function(*ctx.input_tensors)\n        return output_tensors\n\n    @staticmethod\n    def backward(ctx, *output_grads):\n        ctx.input_tensors = [x.detach().requires_grad_(True) for x in ctx.input_tensors]\n        with torch.enable_grad(), torch.cuda.amp.autocast(**ctx.gpu_autocast_kwargs):\n            # Fixes a bug where the first op in run_function modifies the\n            # Tensor storage in place, which is not allowed for detach()'d\n            # Tensors.\n            shallow_copies = [x.view_as(x) for x in ctx.input_tensors]\n            output_tensors = ctx.run_function(*shallow_copies)\n        input_grads = torch.autograd.grad(\n            output_tensors,\n            ctx.input_tensors + ctx.input_params,\n            output_grads,\n            allow_unused=True,\n        )\n        del ctx.input_tensors\n        del ctx.input_params\n        del output_tensors\n        return (None, None) + input_grads\n\n\ndef timestep_embedding(timesteps, dim, max_period=10000, repeat_only=False):\n    \"\"\"\n    Create sinusoidal timestep embeddings.\n    :param timesteps: a 1-D Tensor of N indices, one per batch element.\n                      These may be fractional.\n    :param dim: the dimension of the output.\n    :param max_period: controls the minimum frequency of the embeddings.\n    :return: an [N x dim] Tensor of positional embeddings.\n    \"\"\"\n    if not repeat_only:\n        half = dim // 2\n        freqs = torch.exp(\n            -math.log(max_period)\n            * torch.arange(start=0, end=half, dtype=torch.float32)\n            / half\n        ).to(device=timesteps.device)\n        args = timesteps[:, None].float() * freqs[None]\n        embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)\n        if dim % 2:\n            embedding = torch.cat(\n                [embedding, torch.zeros_like(embedding[:, :1])], dim=-1\n            )\n    else:\n        embedding = repeat(timesteps, \"b -> b d\", d=dim)\n    return embedding\n\n\ndef zero_module(module):\n    \"\"\"\n    Zero out the parameters of a module and return it.\n    \"\"\"\n    for p in module.parameters():\n        p.detach().zero_()\n    return module\n\n\ndef scale_module(module, scale):\n    \"\"\"\n    Scale the parameters of a module and return it.\n    \"\"\"\n    for p in module.parameters():\n        p.detach().mul_(scale)\n    return module\n\n\ndef mean_flat(tensor):\n    \"\"\"\n    Take the mean over all non-batch dimensions.\n    \"\"\"\n    return tensor.mean(dim=list(range(1, len(tensor.shape))))\n\n\ndef normalization(channels):\n    \"\"\"\n    Make a standard normalization layer.\n    :param channels: number of input channels.\n    :return: an nn.Module for normalization.\n    \"\"\"\n    return GroupNorm32(32, channels)\n\n\n# PyTorch 1.7 has SiLU, but we support PyTorch 1.5.\nclass SiLU(nn.Module):\n    def forward(self, x):\n        return x * torch.sigmoid(x)\n\n\nclass GroupNorm32(nn.GroupNorm):\n    def forward(self, x):\n        return super().forward(x.float()).type(x.dtype)\n\n\ndef conv_nd(dims, *args, **kwargs):\n    \"\"\"\n    Create a 1D, 2D, or 3D convolution module.\n    \"\"\"\n    if dims == 1:\n        return nn.Conv1d(*args, **kwargs)\n    elif dims == 2:\n        return nn.Conv2d(*args, **kwargs)\n    elif dims == 3:\n        return nn.Conv3d(*args, **kwargs)\n    raise ValueError(f\"unsupported dimensions: {dims}\")\n\n\ndef linear(*args, **kwargs):\n    \"\"\"\n    Create a linear module.\n    \"\"\"\n    return nn.Linear(*args, **kwargs)\n\n\ndef avg_pool_nd(dims, *args, **kwargs):\n    \"\"\"\n    Create a 1D, 2D, or 3D average pooling module.\n    \"\"\"\n    if dims == 1:\n        return nn.AvgPool1d(*args, **kwargs)\n    elif dims == 2:\n        return nn.AvgPool2d(*args, **kwargs)\n    elif dims == 3:\n        return nn.AvgPool3d(*args, **kwargs)\n    raise ValueError(f\"unsupported dimensions: {dims}\")\n"
  },
  {
    "path": "sgm/modules/diffusionmodules/wrappers.py",
    "content": "import torch\nimport torch.nn as nn\nfrom packaging import version\n\nOPENAIUNETWRAPPER = \"sgm.modules.diffusionmodules.wrappers.OpenAIWrapper\"\n\n\nclass IdentityWrapper(nn.Module):\n    def __init__(self, diffusion_model, compile_model: bool = False):\n        super().__init__()\n        compile = (\n            torch.compile\n            if (version.parse(torch.__version__) >= version.parse(\"2.0.0\"))\n            and compile_model\n            else lambda x: x\n        )\n        self.diffusion_model = compile(diffusion_model)\n\n    def forward(self, *args, **kwargs):\n        return self.diffusion_model(*args, **kwargs)\n\n\nclass OpenAIWrapper(IdentityWrapper):\n    def forward(\n        self, x: torch.Tensor, t: torch.Tensor, c: dict, **kwargs\n    ) -> torch.Tensor:\n        x = torch.cat((x, c.get(\"concat\", torch.Tensor([]).type_as(x))), dim=1)\n        return self.diffusion_model(\n            x,\n            timesteps=t,\n            t_context=c.get(\"t_crossattn\", None),\n            v_context=c.get(\"v_crossattn\", None),\n            y=c.get(\"vector\", None),\n            **kwargs\n        )\n"
  },
  {
    "path": "sgm/modules/distributions/__init__.py",
    "content": ""
  },
  {
    "path": "sgm/modules/distributions/distributions.py",
    "content": "import torch\nimport numpy as np\n\n\nclass AbstractDistribution:\n    def sample(self):\n        raise NotImplementedError()\n\n    def mode(self):\n        raise NotImplementedError()\n\n\nclass DiracDistribution(AbstractDistribution):\n    def __init__(self, value):\n        self.value = value\n\n    def sample(self):\n        return self.value\n\n    def mode(self):\n        return self.value\n\n\nclass DiagonalGaussianDistribution(object):\n    def __init__(self, parameters, deterministic=False):\n        self.parameters = parameters\n        self.mean, self.logvar = torch.chunk(parameters, 2, dim=1)\n        self.logvar = torch.clamp(self.logvar, -30.0, 20.0)\n        self.deterministic = deterministic\n        self.std = torch.exp(0.5 * self.logvar)\n        self.var = torch.exp(self.logvar)\n        if self.deterministic:\n            self.var = self.std = torch.zeros_like(self.mean).to(\n                device=self.parameters.device\n            )\n\n    def sample(self):\n        x = self.mean + self.std * torch.randn(self.mean.shape).to(\n            device=self.parameters.device\n        )\n        return x\n\n    def kl(self, other=None):\n        if self.deterministic:\n            return torch.Tensor([0.0])\n        else:\n            if other is None:\n                return 0.5 * torch.sum(\n                    torch.pow(self.mean, 2) + self.var - 1.0 - self.logvar,\n                    dim=[1, 2, 3],\n                )\n            else:\n                return 0.5 * torch.sum(\n                    torch.pow(self.mean - other.mean, 2) / other.var\n                    + self.var / other.var\n                    - 1.0\n                    - self.logvar\n                    + other.logvar,\n                    dim=[1, 2, 3],\n                )\n\n    def nll(self, sample, dims=[1, 2, 3]):\n        if self.deterministic:\n            return torch.Tensor([0.0])\n        logtwopi = np.log(2.0 * np.pi)\n        return 0.5 * torch.sum(\n            logtwopi + self.logvar + torch.pow(sample - self.mean, 2) / self.var,\n            dim=dims,\n        )\n\n    def mode(self):\n        return self.mean\n\n\ndef normal_kl(mean1, logvar1, mean2, logvar2):\n    \"\"\"\n    source: https://github.com/openai/guided-diffusion/blob/27c20a8fab9cb472df5d6bdd6c8d11c8f430b924/guided_diffusion/losses.py#L12\n    Compute the KL divergence between two gaussians.\n    Shapes are automatically broadcasted, so batches can be compared to\n    scalars, among other use cases.\n    \"\"\"\n    tensor = None\n    for obj in (mean1, logvar1, mean2, logvar2):\n        if isinstance(obj, torch.Tensor):\n            tensor = obj\n            break\n    assert tensor is not None, \"at least one argument must be a Tensor\"\n\n    # Force variances to be Tensors. Broadcasting helps convert scalars to\n    # Tensors, but it does not work for torch.exp().\n    logvar1, logvar2 = [\n        x if isinstance(x, torch.Tensor) else torch.tensor(x).to(tensor)\n        for x in (logvar1, logvar2)\n    ]\n\n    return 0.5 * (\n        -1.0\n        + logvar2\n        - logvar1\n        + torch.exp(logvar1 - logvar2)\n        + ((mean1 - mean2) ** 2) * torch.exp(-logvar2)\n    )\n"
  },
  {
    "path": "sgm/modules/ema.py",
    "content": "import torch\nfrom torch import nn\n\n\nclass LitEma(nn.Module):\n    def __init__(self, model, decay=0.9999, use_num_upates=True):\n        super().__init__()\n        if decay < 0.0 or decay > 1.0:\n            raise ValueError(\"Decay must be between 0 and 1\")\n\n        self.m_name2s_name = {}\n        self.register_buffer(\"decay\", torch.tensor(decay, dtype=torch.float32))\n        self.register_buffer(\n            \"num_updates\",\n            torch.tensor(0, dtype=torch.int)\n            if use_num_upates\n            else torch.tensor(-1, dtype=torch.int),\n        )\n\n        for name, p in model.named_parameters():\n            if p.requires_grad:\n                # remove as '.'-character is not allowed in buffers\n                s_name = name.replace(\".\", \"\")\n                self.m_name2s_name.update({name: s_name})\n                self.register_buffer(s_name, p.clone().detach().data)\n\n        self.collected_params = []\n\n    def reset_num_updates(self):\n        del self.num_updates\n        self.register_buffer(\"num_updates\", torch.tensor(0, dtype=torch.int))\n\n    def forward(self, model):\n        decay = self.decay\n\n        if self.num_updates >= 0:\n            self.num_updates += 1\n            decay = min(self.decay, (1 + self.num_updates) / (10 + self.num_updates))\n\n        one_minus_decay = 1.0 - decay\n\n        with torch.no_grad():\n            m_param = dict(model.named_parameters())\n            shadow_params = dict(self.named_buffers())\n\n            for key in m_param:\n                if m_param[key].requires_grad:\n                    sname = self.m_name2s_name[key]\n                    shadow_params[sname] = shadow_params[sname].type_as(m_param[key])\n                    shadow_params[sname].sub_(\n                        one_minus_decay * (shadow_params[sname] - m_param[key])\n                    )\n                else:\n                    assert not key in self.m_name2s_name\n\n    def copy_to(self, model):\n        m_param = dict(model.named_parameters())\n        shadow_params = dict(self.named_buffers())\n        for key in m_param:\n            if m_param[key].requires_grad:\n                m_param[key].data.copy_(shadow_params[self.m_name2s_name[key]].data)\n            else:\n                assert not key in self.m_name2s_name\n\n    def store(self, parameters):\n        \"\"\"\n        Save the current parameters for restoring later.\n        Args:\n          parameters: Iterable of `torch.nn.Parameter`; the parameters to be\n            temporarily stored.\n        \"\"\"\n        self.collected_params = [param.clone() for param in parameters]\n\n    def restore(self, parameters):\n        \"\"\"\n        Restore the parameters stored with the `store` method.\n        Useful to validate the model with EMA parameters without affecting the\n        original optimization process. Store the parameters before the\n        `copy_to` method. After validation (or model saving), use this to\n        restore the former parameters.\n        Args:\n          parameters: Iterable of `torch.nn.Parameter`; the parameters to be\n            updated with the stored parameters.\n        \"\"\"\n        for c_param, param in zip(self.collected_params, parameters):\n            param.data.copy_(c_param.data)\n"
  },
  {
    "path": "sgm/modules/encoders/__init__.py",
    "content": ""
  },
  {
    "path": "sgm/modules/encoders/modules.py",
    "content": "from contextlib import nullcontext\nfrom functools import partial\nfrom typing import Dict, List, Optional, Tuple, Union\n\nimport kornia\nimport numpy as np\nimport open_clip\nimport torch\nimport torch.nn as nn\nfrom einops import rearrange, repeat\nfrom omegaconf import ListConfig\nfrom torch.utils.checkpoint import checkpoint\nfrom transformers import (\n    ByT5Tokenizer,\n    CLIPTextModel,\n    CLIPTokenizer,\n    CLIPVisionModel,\n    T5EncoderModel,\n    T5Tokenizer,\n)\n\nfrom ...modules.autoencoding.regularizers import DiagonalGaussianRegularizer\nfrom ...modules.diffusionmodules.model import Encoder\nfrom ...modules.diffusionmodules.openaimodel import Timestep\nfrom ...modules.diffusionmodules.util import extract_into_tensor, make_beta_schedule\nfrom ...modules.distributions.distributions import DiagonalGaussianDistribution\nfrom ...util import (\n    autocast,\n    count_params,\n    default,\n    disabled_train,\n    expand_dims_like,\n    instantiate_from_config,\n)\n\nimport math\nimport string\nimport pytorch_lightning as pl\nfrom torchvision import transforms\nfrom timm.models.vision_transformer import VisionTransformer\nfrom safetensors.torch import load_file as load_safetensors\nfrom torchvision.utils import save_image\n\n# disable warning\nfrom transformers import logging\nlogging.set_verbosity_error()\n\nclass AbstractEmbModel(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self._is_trainable = None\n        self._ucg_rate = None\n        self._input_key = None\n        self._emb_key = None\n\n    @property\n    def is_trainable(self) -> bool:\n        return self._is_trainable\n\n    @property\n    def ucg_rate(self) -> Union[float, torch.Tensor]:\n        return self._ucg_rate\n\n    @property\n    def input_key(self) -> str:\n        return self._input_key\n\n    @property\n    def emb_key(self) -> str:\n        return self._emb_key\n\n    @is_trainable.setter\n    def is_trainable(self, value: bool):\n        self._is_trainable = value\n\n    @ucg_rate.setter\n    def ucg_rate(self, value: Union[float, torch.Tensor]):\n        self._ucg_rate = value\n\n    @input_key.setter\n    def input_key(self, value: str):\n        self._input_key = value\n\n    @emb_key.setter\n    def emb_key(self, value: str):\n        self._emb_key = value\n\n    @is_trainable.deleter\n    def is_trainable(self):\n        del self._is_trainable\n\n    @ucg_rate.deleter\n    def ucg_rate(self):\n        del self._ucg_rate\n\n    @input_key.deleter\n    def input_key(self):\n        del self._input_key\n\n    @emb_key.deleter\n    def emb_key(self):\n        del self._emb_key\n\n\nclass GeneralConditioner(nn.Module):\n    \n    OUTPUT_DIM2KEYS = {2: \"vector\", 3: \"crossattn\", 4: \"concat\", 5: \"concat\"}\n    KEY2CATDIM = {\"vector\": 1, \"crossattn\": 2, \"concat\": 1}\n\n    def __init__(self, emb_models: Union[List, ListConfig]):\n        super().__init__()\n        embedders = []\n        for n, embconfig in enumerate(emb_models):\n            embedder = instantiate_from_config(embconfig)\n            assert isinstance(\n                embedder, AbstractEmbModel\n            ), f\"embedder model {embedder.__class__.__name__} has to inherit from AbstractEmbModel\"\n            embedder.is_trainable = embconfig.get(\"is_trainable\", False)\n            embedder.ucg_rate = embconfig.get(\"ucg_rate\", 0.0)\n            if not embedder.is_trainable:\n                embedder.train = disabled_train\n                embedder.freeze()\n            print(\n                f\"Initialized embedder #{n}: {embedder.__class__.__name__} \"\n                f\"with {count_params(embedder, False)} params. Trainable: {embedder.is_trainable}\"\n            )\n            if \"emb_key\" in embconfig:\n                embedder.emb_key = embconfig[\"emb_key\"]\n            if \"input_key\" in embconfig:\n                embedder.input_key = embconfig[\"input_key\"]\n            elif \"input_keys\" in embconfig:\n                embedder.input_keys = embconfig[\"input_keys\"]\n            else:\n                raise KeyError(\n                    f\"need either 'input_key' or 'input_keys' for embedder {embedder.__class__.__name__}\"\n                )\n\n            embedder.legacy_ucg_val = embconfig.get(\"legacy_ucg_value\", None)\n            if embedder.legacy_ucg_val is not None:\n                embedder.ucg_prng = np.random.RandomState()\n\n            embedders.append(embedder)\n        self.embedders = nn.ModuleList(embedders)\n\n    def possibly_get_ucg_val(self, embedder: AbstractEmbModel, batch: Dict) -> Dict:\n        assert embedder.legacy_ucg_val is not None\n        p = embedder.ucg_rate\n        val = embedder.legacy_ucg_val\n        for i in range(len(batch[embedder.input_key])):\n            if embedder.ucg_prng.choice(2, p=[1 - p, p]):\n                batch[embedder.input_key][i] = val\n        return batch\n\n    def forward(\n        self, batch: Dict, force_zero_embeddings: Optional[List] = None\n    ) -> Dict:\n        output = dict()\n        if force_zero_embeddings is None:\n            force_zero_embeddings = []\n        for embedder in self.embedders:\n            embedding_context = nullcontext if embedder.is_trainable else torch.no_grad\n            with embedding_context():\n                if hasattr(embedder, \"input_key\") and (embedder.input_key is not None):\n                    if embedder.legacy_ucg_val is not None:\n                        batch = self.possibly_get_ucg_val(embedder, batch)\n                    emb_out = embedder(batch[embedder.input_key])\n                elif hasattr(embedder, \"input_keys\"):\n                    emb_out = embedder(*[batch[k] for k in embedder.input_keys])\n            assert isinstance(\n                emb_out, (torch.Tensor, list, tuple)\n            ), f\"encoder outputs must be tensors or a sequence, but got {type(emb_out)}\"\n            if not isinstance(emb_out, (list, tuple)):\n                emb_out = [emb_out]\n            for emb in emb_out:\n                if embedder.emb_key is not None:\n                    out_key = embedder.emb_key\n                else:\n                    out_key = self.OUTPUT_DIM2KEYS[emb.dim()]\n                if embedder.ucg_rate > 0.0 and embedder.legacy_ucg_val is None:\n                    emb = (\n                        expand_dims_like(\n                            torch.bernoulli(\n                                (1.0 - embedder.ucg_rate)\n                                * torch.ones(emb.shape[0], device=emb.device)\n                            ),\n                            emb,\n                        )\n                        * emb\n                    )\n                if (\n                    hasattr(embedder, \"input_key\")\n                    and embedder.input_key in force_zero_embeddings\n                ):\n                    emb = torch.zeros_like(emb)\n                if out_key in output:\n                    output[out_key] = torch.cat(\n                        (output[out_key], emb), self.KEY2CATDIM[out_key]\n                    )\n                else:\n                    output[out_key] = emb\n        return output\n\n    def get_unconditional_conditioning(\n        self, batch_c, batch_uc=None, force_uc_zero_embeddings=None\n    ):\n        if force_uc_zero_embeddings is None:\n            force_uc_zero_embeddings = []\n        ucg_rates = list()\n        for embedder in self.embedders:\n            ucg_rates.append(embedder.ucg_rate)\n            embedder.ucg_rate = 0.0\n        c = self(batch_c)\n        uc = self(batch_c if batch_uc is None else batch_uc, force_uc_zero_embeddings)\n\n        for embedder, rate in zip(self.embedders, ucg_rates):\n            embedder.ucg_rate = rate\n        return c, uc\n    \n\nclass InceptionV3(nn.Module):\n    \"\"\"Wrapper around the https://github.com/mseitzer/pytorch-fid inception\n    port with an additional squeeze at the end\"\"\"\n\n    def __init__(self, normalize_input=False, **kwargs):\n        super().__init__()\n        from pytorch_fid import inception\n\n        kwargs[\"resize_input\"] = True\n        self.model = inception.InceptionV3(normalize_input=normalize_input, **kwargs)\n\n    def forward(self, inp):\n        # inp = kornia.geometry.resize(inp, (299, 299),\n        #                              interpolation='bicubic',\n        #                              align_corners=False,\n        #                              antialias=True)\n        # inp = inp.clamp(min=-1, max=1)\n\n        outp = self.model(inp)\n\n        if len(outp) == 1:\n            return outp[0].squeeze()\n\n        return outp\n\n\nclass IdentityEncoder(AbstractEmbModel):\n    def encode(self, x):\n        return x\n    def freeze(self):\n        return\n    def forward(self, x):\n        return x\n\n\nclass ClassEmbedder(AbstractEmbModel):\n    def __init__(self, embed_dim, n_classes=1000, add_sequence_dim=False):\n        super().__init__()\n        self.embedding = nn.Embedding(n_classes, embed_dim)\n        self.n_classes = n_classes\n        self.add_sequence_dim = add_sequence_dim\n\n    def forward(self, c):\n        c = self.embedding(c)\n        if self.add_sequence_dim:\n            c = c[:, None, :]\n        return c\n\n    def get_unconditional_conditioning(self, bs, device=\"cuda\"):\n        uc_class = (\n            self.n_classes - 1\n        )  # 1000 classes --> 0 ... 999, one extra class for ucg (class 1000)\n        uc = torch.ones((bs,), device=device) * uc_class\n        uc = {self.key: uc.long()}\n        return uc\n\n\nclass ClassEmbedderForMultiCond(ClassEmbedder):\n    def forward(self, batch, key=None, disable_dropout=False):\n        out = batch\n        key = default(key, self.key)\n        islist = isinstance(batch[key], list)\n        if islist:\n            batch[key] = batch[key][0]\n        c_out = super().forward(batch, key, disable_dropout)\n        out[key] = [c_out] if islist else c_out\n        return out\n\n\nclass FrozenT5Embedder(AbstractEmbModel):\n    \"\"\"Uses the T5 transformer encoder for text\"\"\"\n\n    def __init__(\n        self, version=\"google/t5-v1_1-xxl\", device=\"cuda\", max_length=77, freeze=True\n    ):  # others are google/t5-v1_1-xl and google/t5-v1_1-xxl\n        super().__init__()\n        self.tokenizer = T5Tokenizer.from_pretrained(version)\n        self.transformer = T5EncoderModel.from_pretrained(version)\n        self.device = device\n        self.max_length = max_length\n        if freeze:\n            self.freeze()\n\n    def freeze(self):\n        self.transformer = self.transformer.eval()\n\n        for param in self.parameters():\n            param.requires_grad = False\n\n    # @autocast\n    def forward(self, text):\n        batch_encoding = self.tokenizer(\n            text,\n            truncation=True,\n            max_length=self.max_length,\n            return_length=True,\n            return_overflowing_tokens=False,\n            padding=\"max_length\",\n            return_tensors=\"pt\",\n        )\n        tokens = batch_encoding[\"input_ids\"].to(self.device)\n        with torch.autocast(\"cuda\", enabled=False):\n            outputs = self.transformer(input_ids=tokens)\n        z = outputs.last_hidden_state\n        return z\n\n    def encode(self, text):\n        return self(text)\n\n\nclass FrozenByT5Embedder(AbstractEmbModel):\n    \"\"\"\n    Uses the ByT5 transformer encoder for text. Is character-aware.\n    \"\"\"\n\n    def __init__(\n        self, version=\"google/byt5-base\", device=\"cuda\", max_length=77, freeze=True, *args, **kwargs\n    ):  # others are google/t5-v1_1-xl and google/t5-v1_1-xxl\n        super().__init__(*args, **kwargs)\n        self.tokenizer = ByT5Tokenizer.from_pretrained(version)\n        self.transformer = T5EncoderModel.from_pretrained(version)\n        self.device = device\n        self.max_length = max_length\n        if freeze:\n            self.freeze()\n\n    def freeze(self):\n        self.transformer = self.transformer.eval()\n        for param in self.parameters():\n            param.requires_grad = False\n\n    def forward(self, text):\n        batch_encoding = self.tokenizer(\n            text,\n            truncation=True,\n            max_length=self.max_length,\n            return_length=True,\n            return_overflowing_tokens=False,\n            padding=\"max_length\",\n            return_tensors=\"pt\",\n        )\n        tokens = batch_encoding[\"input_ids\"].to(next(self.parameters()).device)\n        with torch.autocast(\"cuda\", enabled=False):\n            outputs = self.transformer(input_ids=tokens)\n        z = outputs.last_hidden_state # l, 1536\n        return z\n\n    def encode(self, text):\n        return self(text)\n\n\nclass FrozenCLIPEmbedder(AbstractEmbModel):\n    \"\"\"Uses the CLIP transformer encoder for text (from huggingface)\"\"\"\n\n    LAYERS = [\"last\", \"pooled\", \"hidden\"]\n\n    def __init__(\n        self,\n        version=\"openai/clip-vit-large-patch14\",\n        device=\"cuda\",\n        max_length=77,\n        freeze=True,\n        layer=\"last\",\n        layer_idx=None,\n        always_return_pooled=False,\n    ):  # clip-vit-base-patch32\n        super().__init__()\n        assert layer in self.LAYERS\n        self.tokenizer = CLIPTokenizer.from_pretrained(version)\n        self.transformer = CLIPTextModel.from_pretrained(version)\n        self.device = device\n        self.max_length = max_length\n        if freeze:\n            self.freeze()\n        self.layer = layer\n        self.layer_idx = layer_idx\n        self.return_pooled = always_return_pooled\n        if layer == \"hidden\":\n            assert layer_idx is not None\n            assert 0 <= abs(layer_idx) <= 12\n\n    def freeze(self):\n        self.transformer = self.transformer.eval()\n        for param in self.parameters():\n            param.requires_grad = False\n\n    @autocast\n    def forward(self, text):\n        batch_encoding = self.tokenizer(\n            text,\n            truncation=True,\n            max_length=self.max_length,\n            return_length=True,\n            return_overflowing_tokens=False,\n            padding=\"max_length\",\n            return_tensors=\"pt\",\n        )\n        device = next(self.transformer.parameters()).device\n        tokens = batch_encoding[\"input_ids\"].to(device)\n        outputs = self.transformer(\n            input_ids=tokens, output_hidden_states=self.layer == \"hidden\"\n        )\n        if self.layer == \"last\":\n            z = outputs.last_hidden_state\n        elif self.layer == \"pooled\":\n            z = outputs.pooler_output[:, None, :]\n        else:\n            z = outputs.hidden_states[self.layer_idx]\n        if self.return_pooled:\n            return z, outputs.pooler_output\n        return z\n\n    def encode(self, text):\n        return self(text)\n\n\nclass FrozenOpenCLIPEmbedder2(AbstractEmbModel):\n    \"\"\"\n    Uses the OpenCLIP transformer encoder for text\n    \"\"\"\n\n    LAYERS = [\"pooled\", \"last\", \"penultimate\"]\n\n    def __init__(\n        self,\n        arch=\"ViT-H-14\",\n        version=\"laion2b_s32b_b79k\",\n        device=\"cuda\",\n        max_length=77,\n        freeze=True,\n        layer=\"last\",\n        always_return_pooled=False,\n        legacy=True,\n    ):\n        super().__init__()\n        assert layer in self.LAYERS\n        model, _, _ = open_clip.create_model_and_transforms(\n            arch,\n            device=torch.device(\"cpu\"),\n            pretrained=version,\n        )\n        del model.visual\n        self.model = model\n\n        self.device = device\n        self.max_length = max_length\n        self.return_pooled = always_return_pooled\n        if freeze:\n            self.freeze()\n        self.layer = layer\n        if self.layer == \"last\":\n            self.layer_idx = 0\n        elif self.layer == \"penultimate\":\n            self.layer_idx = 1\n        else:\n            raise NotImplementedError()\n        self.legacy = legacy\n\n    def freeze(self):\n        self.model = self.model.eval()\n        for param in self.parameters():\n            param.requires_grad = False\n\n    @autocast\n    def forward(self, text):\n        device = next(self.model.parameters()).device\n        tokens = open_clip.tokenize(text)\n        z = self.encode_with_transformer(tokens.to(device))\n        if not self.return_pooled and self.legacy:\n            return z\n        if self.return_pooled:\n            assert not self.legacy\n            return z[self.layer], z[\"pooled\"]\n        return z[self.layer]\n\n    def encode_with_transformer(self, text):\n        x = self.model.token_embedding(text)  # [batch_size, n_ctx, d_model]\n        x = x + self.model.positional_embedding\n        x = x.permute(1, 0, 2)  # NLD -> LND\n        x = self.text_transformer_forward(x, attn_mask=self.model.attn_mask)\n        if self.legacy:\n            x = x[self.layer]\n            x = self.model.ln_final(x)\n            return x\n        else:\n            # x is a dict and will stay a dict\n            o = x[\"last\"]\n            o = self.model.ln_final(o)\n            pooled = self.pool(o, text)\n            x[\"pooled\"] = pooled\n            return x\n\n    def pool(self, x, text):\n        # take features from the eot embedding (eot_token is the highest number in each sequence)\n        x = (\n            x[torch.arange(x.shape[0]), text.argmax(dim=-1)]\n            @ self.model.text_projection\n        )\n        return x\n\n    def text_transformer_forward(self, x: torch.Tensor, attn_mask=None):\n        outputs = {}\n        for i, r in enumerate(self.model.transformer.resblocks):\n            if i == len(self.model.transformer.resblocks) - 1:\n                outputs[\"penultimate\"] = x.permute(1, 0, 2)  # LND -> NLD\n            if (\n                self.model.transformer.grad_checkpointing\n                and not torch.jit.is_scripting()\n            ):\n                x = checkpoint(r, x, attn_mask)\n            else:\n                x = r(x, attn_mask=attn_mask)\n        outputs[\"last\"] = x.permute(1, 0, 2)  # LND -> NLD\n        return outputs\n\n    def encode(self, text):\n        return self(text)\n\n\nclass FrozenOpenCLIPEmbedder(AbstractEmbModel):\n    LAYERS = [\n        # \"pooled\",\n        \"last\",\n        \"penultimate\",\n    ]\n\n    def __init__(\n        self,\n        arch=\"ViT-H-14\",\n        version=\"laion2b_s32b_b79k\",\n        device=\"cuda\",\n        max_length=77,\n        freeze=True,\n        layer=\"last\",\n    ):\n        super().__init__()\n        assert layer in self.LAYERS\n        model, _, _ = open_clip.create_model_and_transforms(\n            arch, device=torch.device(\"cpu\"), pretrained=version\n        )\n        del model.visual\n        self.model = model\n\n        self.device = device\n        self.max_length = max_length\n        if freeze:\n            self.freeze()\n        self.layer = layer\n        if self.layer == \"last\":\n            self.layer_idx = 0\n        elif self.layer == \"penultimate\":\n            self.layer_idx = 1\n        else:\n            raise NotImplementedError()\n\n    def freeze(self):\n        self.model = self.model.eval()\n        for param in self.parameters():\n            param.requires_grad = False\n\n    def forward(self, text):\n        device = next(self.model.parameters()).device\n        tokens = open_clip.tokenize(text)\n        z = self.encode_with_transformer(tokens.to(device))\n        return z\n\n    def encode_with_transformer(self, text):\n        x = self.model.token_embedding(text)  # [batch_size, n_ctx, d_model]\n        x = x + self.model.positional_embedding\n        x = x.permute(1, 0, 2)  # NLD -> LND\n        x = self.text_transformer_forward(x, attn_mask=self.model.attn_mask)\n        x = x.permute(1, 0, 2)  # LND -> NLD\n        x = self.model.ln_final(x)\n        return x\n\n    def text_transformer_forward(self, x: torch.Tensor, attn_mask=None):\n        for i, r in enumerate(self.model.transformer.resblocks):\n            if i == len(self.model.transformer.resblocks) - self.layer_idx:\n                break\n            if (\n                self.model.transformer.grad_checkpointing\n                and not torch.jit.is_scripting()\n            ):\n                x = checkpoint(r, x, attn_mask)\n            else:\n                x = r(x, attn_mask=attn_mask)\n        return x\n\n    def encode(self, text):\n        return self(text)\n\n\nclass FrozenOpenCLIPImageEmbedder(AbstractEmbModel):\n    \"\"\"\n    Uses the OpenCLIP vision transformer encoder for images\n    \"\"\"\n\n    def __init__(\n        self,\n        arch=\"ViT-H-14\",\n        version=\"laion2b_s32b_b79k\",\n        device=\"cuda\",\n        max_length=77,\n        freeze=True,\n        antialias=True,\n        ucg_rate=0.0,\n        unsqueeze_dim=False,\n        repeat_to_max_len=False,\n        num_image_crops=0,\n        output_tokens=False,\n    ):\n        super().__init__()\n        model, _, _ = open_clip.create_model_and_transforms(\n            arch,\n            device=torch.device(\"cpu\"),\n            pretrained=version,\n        )\n        del model.transformer\n        self.model = model\n        self.max_crops = num_image_crops\n        self.pad_to_max_len = self.max_crops > 0\n        self.repeat_to_max_len = repeat_to_max_len and (not self.pad_to_max_len)\n        self.device = device\n        self.max_length = max_length\n        if freeze:\n            self.freeze()\n\n        self.antialias = antialias\n\n        self.register_buffer(\n            \"mean\", torch.Tensor([0.48145466, 0.4578275, 0.40821073]), persistent=False\n        )\n        self.register_buffer(\n            \"std\", torch.Tensor([0.26862954, 0.26130258, 0.27577711]), persistent=False\n        )\n        self.ucg_rate = ucg_rate\n        self.unsqueeze_dim = unsqueeze_dim\n        self.stored_batch = None\n        self.model.visual.output_tokens = output_tokens\n        self.output_tokens = output_tokens\n\n    def preprocess(self, x):\n        # normalize to [0,1]\n        x = kornia.geometry.resize(\n            x,\n            (224, 224),\n            interpolation=\"bicubic\",\n            align_corners=True,\n            antialias=self.antialias,\n        )\n        x = (x + 1.0) / 2.0\n        # renormalize according to clip\n        x = kornia.enhance.normalize(x, self.mean, self.std)\n        return x\n\n    def freeze(self):\n        self.model = self.model.eval()\n        for param in self.parameters():\n            param.requires_grad = False\n\n    @autocast\n    def forward(self, image, no_dropout=False):\n        z = self.encode_with_vision_transformer(image)\n        tokens = None\n        if self.output_tokens:\n            z, tokens = z[0], z[1]\n        z = z.to(image.dtype)\n        # if self.ucg_rate > 0.0 and not no_dropout and not (self.max_crops > 0):\n        #     z = (\n        #         torch.bernoulli(\n        #             (1.0 - self.ucg_rate) * torch.ones(z.shape[0], device=z.device)\n        #         )[:, None]\n        #         * z\n        #     )\n        #     if tokens is not None:\n        #         tokens = (\n        #             expand_dims_like(\n        #                 torch.bernoulli(\n        #                     (1.0 - self.ucg_rate)\n        #                     * torch.ones(tokens.shape[0], device=tokens.device)\n        #                 ),\n        #                 tokens,\n        #             )\n        #             * tokens\n        #         )\n        if self.unsqueeze_dim:\n            z = z[:, None, :]\n        if self.output_tokens:\n            assert not self.repeat_to_max_len\n            assert not self.pad_to_max_len\n            return tokens, z\n        if self.repeat_to_max_len:\n            if z.dim() == 2:\n                z_ = z[:, None, :]\n            else:\n                z_ = z\n            return repeat(z_, \"b 1 d -> b n d\", n=self.max_length), z\n        elif self.pad_to_max_len:\n            assert z.dim() == 3\n            z_pad = torch.cat(\n                (\n                    z,\n                    torch.zeros(\n                        z.shape[0],\n                        self.max_length - z.shape[1],\n                        z.shape[2],\n                        device=z.device,\n                    ),\n                ),\n                1,\n            )\n            return z_pad, z_pad[:, 0, ...]\n        return z\n\n    def encode_with_vision_transformer(self, img):\n        # if self.max_crops > 0:\n        #    img = self.preprocess_by_cropping(img)\n        if img.dim() == 5:\n            assert self.max_crops == img.shape[1]\n            img = rearrange(img, \"b n c h w -> (b n) c h w\")\n        img = self.preprocess(img)\n        if not self.output_tokens:\n            assert not self.model.visual.output_tokens\n            x = self.model.visual(img)\n            tokens = None\n        else:\n            assert self.model.visual.output_tokens\n            x, tokens = self.model.visual(img)\n        if self.max_crops > 0:\n            x = rearrange(x, \"(b n) d -> b n d\", n=self.max_crops)\n            # drop out between 0 and all along the sequence axis\n            x = (\n                torch.bernoulli(\n                    (1.0 - self.ucg_rate)\n                    * torch.ones(x.shape[0], x.shape[1], 1, device=x.device)\n                )\n                * x\n            )\n            if tokens is not None:\n                tokens = rearrange(tokens, \"(b n) t d -> b t (n d)\", n=self.max_crops)\n                print(\n                    f\"You are running very experimental token-concat in {self.__class__.__name__}. \"\n                    f\"Check what you are doing, and then remove this message.\"\n                )\n        if self.output_tokens:\n            return x, tokens\n        return x\n\n    def encode(self, text):\n        return self(text)\n\n\nclass FrozenCLIPT5Encoder(AbstractEmbModel):\n    def __init__(\n        self,\n        clip_version=\"openai/clip-vit-large-patch14\",\n        t5_version=\"google/t5-v1_1-xl\",\n        device=\"cuda\",\n        clip_max_length=77,\n        t5_max_length=77,\n    ):\n        super().__init__()\n        self.clip_encoder = FrozenCLIPEmbedder(\n            clip_version, device, max_length=clip_max_length\n        )\n        self.t5_encoder = FrozenT5Embedder(t5_version, device, max_length=t5_max_length)\n        print(\n            f\"{self.clip_encoder.__class__.__name__} has {count_params(self.clip_encoder) * 1.e-6:.2f} M parameters, \"\n            f\"{self.t5_encoder.__class__.__name__} comes with {count_params(self.t5_encoder) * 1.e-6:.2f} M params.\"\n        )\n\n    def encode(self, text):\n        return self(text)\n\n    def forward(self, text):\n        clip_z = self.clip_encoder.encode(text)\n        t5_z = self.t5_encoder.encode(text)\n        return [clip_z, t5_z]\n\n\nclass SpatialRescaler(AbstractEmbModel):\n    def __init__(\n        self,\n        n_stages=1,\n        method=\"bilinear\",\n        multiplier=0.5,\n        in_channels=3,\n        out_channels=None,\n        bias=False,\n        wrap_video=False,\n        kernel_size=1,\n        remap_output=False,\n    ):\n        super().__init__()\n        self.n_stages = n_stages\n        assert self.n_stages >= 0\n        assert method in [\n            \"nearest\",\n            \"linear\",\n            \"bilinear\",\n            \"trilinear\",\n            \"bicubic\",\n            \"area\",\n        ]\n        self.multiplier = multiplier\n        self.interpolator = partial(torch.nn.functional.interpolate, mode=method)\n        self.remap_output = out_channels is not None or remap_output\n        if self.remap_output:\n            print(\n                f\"Spatial Rescaler mapping from {in_channels} to {out_channels} channels after resizing.\"\n            )\n            self.channel_mapper = nn.Conv2d(\n                in_channels,\n                out_channels,\n                kernel_size=kernel_size,\n                bias=bias,\n                padding=kernel_size // 2,\n            )\n        self.wrap_video = wrap_video\n    \n    def freeze(self):\n        pass\n\n    def forward(self, x):\n        if self.wrap_video and x.ndim == 5:\n            B, C, T, H, W = x.shape\n            x = rearrange(x, \"b c t h w -> b t c h w\")\n            x = rearrange(x, \"b t c h w -> (b t) c h w\")\n\n        for stage in range(self.n_stages):\n            x = self.interpolator(x, scale_factor=self.multiplier)\n\n        if self.wrap_video:\n            x = rearrange(x, \"(b t) c h w -> b t c h w\", b=B, t=T, c=C)\n            x = rearrange(x, \"b t c h w -> b c t h w\")\n        if self.remap_output:\n            x = self.channel_mapper(x)\n        return x\n\n    def encode(self, x):\n        return self(x)\n\n\nclass LowScaleEncoder(nn.Module):\n    def __init__(\n        self,\n        model_config,\n        linear_start,\n        linear_end,\n        timesteps=1000,\n        max_noise_level=250,\n        output_size=64,\n        scale_factor=1.0,\n    ):\n        super().__init__()\n        self.max_noise_level = max_noise_level\n        self.model = instantiate_from_config(model_config)\n        self.augmentation_schedule = self.register_schedule(\n            timesteps=timesteps, linear_start=linear_start, linear_end=linear_end\n        )\n        self.out_size = output_size\n        self.scale_factor = scale_factor\n\n    def register_schedule(\n        self,\n        beta_schedule=\"linear\",\n        timesteps=1000,\n        linear_start=1e-4,\n        linear_end=2e-2,\n        cosine_s=8e-3,\n    ):\n        betas = make_beta_schedule(\n            beta_schedule,\n            timesteps,\n            linear_start=linear_start,\n            linear_end=linear_end,\n            cosine_s=cosine_s,\n        )\n        alphas = 1.0 - betas\n        alphas_cumprod = np.cumprod(alphas, axis=0)\n        alphas_cumprod_prev = np.append(1.0, alphas_cumprod[:-1])\n\n        (timesteps,) = betas.shape\n        self.num_timesteps = int(timesteps)\n        self.linear_start = linear_start\n        self.linear_end = linear_end\n        assert (\n            alphas_cumprod.shape[0] == self.num_timesteps\n        ), \"alphas have to be defined for each timestep\"\n\n        to_torch = partial(torch.tensor, dtype=torch.float32)\n\n        self.register_buffer(\"betas\", to_torch(betas))\n        self.register_buffer(\"alphas_cumprod\", to_torch(alphas_cumprod))\n        self.register_buffer(\"alphas_cumprod_prev\", to_torch(alphas_cumprod_prev))\n\n        # calculations for diffusion q(x_t | x_{t-1}) and others\n        self.register_buffer(\"sqrt_alphas_cumprod\", to_torch(np.sqrt(alphas_cumprod)))\n        self.register_buffer(\n            \"sqrt_one_minus_alphas_cumprod\", to_torch(np.sqrt(1.0 - alphas_cumprod))\n        )\n        self.register_buffer(\n            \"log_one_minus_alphas_cumprod\", to_torch(np.log(1.0 - alphas_cumprod))\n        )\n        self.register_buffer(\n            \"sqrt_recip_alphas_cumprod\", to_torch(np.sqrt(1.0 / alphas_cumprod))\n        )\n        self.register_buffer(\n            \"sqrt_recipm1_alphas_cumprod\", to_torch(np.sqrt(1.0 / alphas_cumprod - 1))\n        )\n\n    def q_sample(self, x_start, t, noise=None):\n        noise = default(noise, lambda: torch.randn_like(x_start))\n        return (\n            extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start\n            + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape)\n            * noise\n        )\n\n    def forward(self, x):\n        z = self.model.encode(x)\n        if isinstance(z, DiagonalGaussianDistribution):\n            z = z.sample()\n        z = z * self.scale_factor\n        noise_level = torch.randint(\n            0, self.max_noise_level, (x.shape[0],), device=x.device\n        ).long()\n        z = self.q_sample(z, noise_level)\n        if self.out_size is not None:\n            z = torch.nn.functional.interpolate(z, size=self.out_size, mode=\"nearest\")\n        # z = z.repeat_interleave(2, -2).repeat_interleave(2, -1)\n        return z, noise_level\n\n    def decode(self, z):\n        z = z / self.scale_factor\n        return self.model.decode(z)\n\n\nclass ConcatTimestepEmbedderND(AbstractEmbModel):\n    \"\"\"embeds each dimension independently and concatenates them\"\"\"\n\n    def __init__(self, outdim):\n        super().__init__()\n        self.timestep = Timestep(outdim)\n        self.outdim = outdim\n    \n    def freeze(self):\n        self.eval()\n\n    def forward(self, x):\n        if x.ndim == 1:\n            x = x[:, None]\n        assert len(x.shape) == 2\n        b, dims = x.shape[0], x.shape[1]\n        x = rearrange(x, \"b d -> (b d)\")\n        emb = self.timestep(x)\n        emb = rearrange(emb, \"(b d) d2 -> b (d d2)\", b=b, d=dims, d2=self.outdim)\n        return emb\n\n\nclass GaussianEncoder(Encoder, AbstractEmbModel):\n    def __init__(\n        self, weight: float = 1.0, flatten_output: bool = True, *args, **kwargs\n    ):\n        super().__init__(*args, **kwargs)\n        self.posterior = DiagonalGaussianRegularizer()\n        self.weight = weight\n        self.flatten_output = flatten_output\n\n    def forward(self, x) -> Tuple[Dict, torch.Tensor]:\n        z = super().forward(x)\n        z, log = self.posterior(z)\n        log[\"loss\"] = log[\"kl_loss\"]\n        log[\"weight\"] = self.weight\n        if self.flatten_output:\n            z = rearrange(z, \"b c h w -> b (h w ) c\")\n        return log, z\n\n\nclass LatentEncoder(AbstractEmbModel):\n\n    def __init__(self, scale_factor, config, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.scale_factor = scale_factor\n        self.model = instantiate_from_config(config).eval()\n        self.model.train = disabled_train\n    \n    def freeze(self):\n        for param in self.model.parameters():\n            param.requires_grad = False\n\n    def forward(self, x):\n        z = self.model.encode(x)\n        z = self.scale_factor * z\n        return z\n\n\nclass ViTSTREncoder(VisionTransformer):\n    '''\n    ViTSTREncoder is basically a ViT that uses ViTSTR weights\n    '''\n    def __init__(self, size=224, ckpt_path=None, freeze=True, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        self.grayscale = transforms.Grayscale()\n        self.resize = transforms.Resize((size, size), transforms.InterpolationMode.BICUBIC, antialias=True)\n\n        self.character = string.printable[:-6]\n        self.reset_classifier(num_classes=len(self.character)+2)\n\n        if ckpt_path is not None:\n            self.load_state_dict(torch.load(ckpt_path, map_location=\"cpu\"), strict=False)\n        \n        if freeze:\n            self.freeze()\n\n    def reset_classifier(self, num_classes):\n        self.num_classes = num_classes\n        self.head = nn.Linear(self.embed_dim, num_classes) if num_classes > 0 else nn.Identity()\n\n    def freeze(self):\n        for param in self.parameters():\n            param.requires_grad_(False)    \n\n    def forward_features(self, x):\n        B = x.shape[0]\n        x = self.patch_embed(x)\n\n        cls_tokens = self.cls_token.expand(B, -1, -1)  # stole cls_tokens impl from Phil Wang, thanks\n        x = torch.cat((cls_tokens, x), dim=1)\n        x = x + self.pos_embed\n        x = self.pos_drop(x)\n\n        for blk in self.blocks:\n            x = blk(x)\n\n        x = self.norm(x)\n        return x\n\n    def forward(self, x):\n        \n        x = self.forward_features(x)\n\n        return x\n    \n    def encode(self, x):\n        return self(x)\n\n\nclass PositionalEncoding(nn.Module):\n\n    def __init__(self, d_model, dropout=0.1, max_len=5000):\n        super(PositionalEncoding, self).__init__()\n\n        self.dropout = nn.Dropout(p=dropout)\n\n        pe = torch.zeros(max_len, d_model)\n        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)\n        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))\n        pe[:, 0::2] = torch.sin(position * div_term)\n        pe[:, 1::2] = torch.cos(position * div_term)\n        self.register_buffer('pe', pe)\n\n    def forward(self, x):\n        x = x + torch.tile(self.pe[None, ...].to(x.device), (x.shape[0], 1, 1))\n        return self.dropout(x)\n\n\nclass LabelEncoder(AbstractEmbModel, pl.LightningModule):\n\n    def __init__(self, max_len, emb_dim, n_heads=8, n_trans_layers=12, ckpt_path=None, trainable=False, \n                 lr=1e-4, lambda_cls=0.1, lambda_pos=0.1, clip_dim=1024, visual_len=197, visual_dim=768, visual_config=None, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        self.max_len = max_len\n        self.emd_dim = emb_dim\n        self.n_heads = n_heads\n        self.n_trans_layers = n_trans_layers\n        self.character = string.printable[:-6]\n        self.num_cls = len(self.character) + 1\n\n        self.label_embedding = nn.Embedding(self.num_cls, self.emd_dim)\n        self.pos_embedding = PositionalEncoding(d_model=self.emd_dim, max_len=self.max_len)\n        transformer_block = nn.TransformerEncoderLayer(d_model=self.emd_dim, nhead=self.n_heads, batch_first=True)\n        self.encoder = nn.TransformerEncoder(transformer_block, num_layers=self.n_trans_layers)\n\n        if ckpt_path is not None:\n            self.load_state_dict(torch.load(ckpt_path, map_location=\"cpu\")[\"state_dict\"], strict=False)\n\n        if trainable:\n            \n            self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1 / 0.07))\n            self.visual_encoder = instantiate_from_config(visual_config)\n\n            self.learning_rate = lr\n            self.clip_dim = clip_dim\n            self.visual_len = visual_len\n            self.visual_dim = visual_dim\n            self.lambda_cls = lambda_cls\n            self.lambda_pos = lambda_pos\n\n            self.cls_head = nn.Sequential(*[\n                nn.InstanceNorm1d(self.max_len),\n                nn.Linear(self.emd_dim, self.emd_dim),\n                nn.GELU(),\n                nn.Linear(self.emd_dim, self.num_cls)\n            ])\n\n            self.pos_head = nn.Sequential(*[\n                nn.InstanceNorm1d(self.max_len),\n                nn.Linear(self.emd_dim, self.max_len, bias=False)\n            ])\n\n            self.text_head = nn.Sequential(*[\n                nn.InstanceNorm1d(self.max_len),\n                nn.Linear(self.emd_dim, self.clip_dim, bias=False),\n                nn.Conv1d(in_channels=self.max_len, out_channels=1, kernel_size=1)\n            ])\n\n            self.visual_head = nn.Sequential(*[\n                nn.InstanceNorm1d(self.visual_len),\n                nn.Linear(self.visual_dim, self.clip_dim, bias=False),\n                nn.Conv1d(in_channels=self.visual_len, out_channels=1, kernel_size=1)\n            ])\n\n    def freeze(self):\n        for param in self.parameters():\n            param.requires_grad = False\n\n    def get_index(self, labels):\n\n        indexes = []\n        for label in labels:\n            assert len(label) <= self.max_len\n            index = [self.character.find(c)+1 for c in label]\n            index = index + [0] * (self.max_len - len(index))\n            indexes.append(index)\n        \n        return torch.tensor(indexes, device=next(self.parameters()).device)\n    \n    def get_embeddings(self, x):\n        \n        emb = self.label_embedding(x)\n        emb = self.pos_embedding(emb)\n        out = self.encoder(emb)\n\n        return out\n\n    def forward(self, labels):\n        \n        idx = self.get_index(labels)\n        out = self.get_embeddings(idx)\n\n        return out\n    \n    def get_loss(self, text_out, visual_out, clip_target, cls_out, pos_out, cls_target, pos_target):\n\n        text_out = text_out / text_out.norm(dim=1, keepdim=True) # b, 1024\n        visual_out = visual_out / visual_out.norm(dim=1, keepdim=True) # b, 1024\n\n        logit_scale = self.logit_scale.exp()\n        logits_per_image = logit_scale * visual_out @ text_out.T # b, b\n        logits_per_text = logits_per_image.T # b, b\n\n        clip_loss_image = nn.functional.cross_entropy(logits_per_image, clip_target)\n        clip_loss_text = nn.functional.cross_entropy(logits_per_text, clip_target)\n        clip_loss = (clip_loss_image + clip_loss_text) / 2 \n        \n        cls_loss = nn.functional.cross_entropy(cls_out.permute(0,2,1), cls_target)\n        pos_loss = nn.functional.cross_entropy(pos_out.permute(0,2,1), pos_target)\n\n        return clip_loss, cls_loss, pos_loss, logits_per_text\n    \n    def training_step(self, batch, batch_idx):\n\n        text = batch[\"text\"]\n        image = batch[\"image\"]\n\n        idx = self.get_index(text)\n        text_emb = self.get_embeddings(idx) # b, l, d\n        visual_emb = self.visual_encoder(image) # b, n, d\n\n        cls_out = self.cls_head(text_emb) # b, l, c\n        pos_out = self.pos_head(text_emb) # b, l, p\n        text_out = self.text_head(text_emb).squeeze(1) # b, 1024\n        visual_out = self.visual_head(visual_emb).squeeze(1) # b, 1024\n        \n        cls_target = idx # b, c \n        pos_target = torch.arange(start=0, end=self.max_len, step=1)\n        pos_target = pos_target[None].tile((idx.shape[0], 1)).to(cls_target) # b, c\n        clip_target = torch.arange(0, idx.shape[0], 1).to(cls_target) # b,\n\n        clip_loss, cls_loss, pos_loss, logits_per_text = self.get_loss(text_out, visual_out, clip_target, cls_out, pos_out, cls_target, pos_target)\n        loss = clip_loss + self.lambda_cls * cls_loss + self.lambda_pos * pos_loss\n\n        loss_dict = {}\n        loss_dict[\"loss/clip_loss\"] = clip_loss\n        loss_dict[\"loss/cls_loss\"] = cls_loss\n        loss_dict[\"loss/pos_loss\"] = pos_loss\n        loss_dict[\"loss/full_loss\"] = loss\n\n        clip_idx = torch.max(logits_per_text, dim=-1).indices # b,\n        clip_acc = (clip_idx == clip_target).to(dtype=torch.float32).mean()\n\n        cls_idx = torch.max(cls_out, dim=-1).indices # b, l\n        cls_acc = (cls_idx == cls_target).to(dtype=torch.float32).mean()\n\n        pos_idx = torch.max(pos_out, dim=-1).indices # b, l\n        pos_acc = (pos_idx == pos_target).to(dtype=torch.float32).mean()\n\n        loss_dict[\"acc/clip_acc\"] = clip_acc\n        loss_dict[\"acc/cls_acc\"] = cls_acc\n        loss_dict[\"acc/pos_acc\"] = pos_acc\n\n        self.log_dict(loss_dict, prog_bar=True, batch_size=len(text),\n                    logger=True, on_step=True, on_epoch=True, sync_dist=True)\n\n        return loss\n\n    def configure_optimizers(self):\n\n        lr = self.learning_rate\n        opt = torch.optim.AdamW(filter(lambda p: p.requires_grad, self.parameters()), lr=lr)\n\n        return opt\n\n\n"
  },
  {
    "path": "sgm/modules/predictors/model.py",
    "content": "import torch\nimport torch.nn as nn\nfrom torchvision import transforms\nfrom torchvision.utils import save_image\n\n\nclass ParseqPredictor(nn.Module):\n\n    def __init__(self, ckpt_path=None, freeze=True, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        self.parseq = torch.hub.load('./src/parseq', 'parseq', source='local').eval()\n        self.parseq.load_state_dict(torch.load(ckpt_path, map_location=\"cpu\"))\n        self.parseq_transform = transforms.Compose([\n            transforms.Resize(self.parseq.hparams.img_size, transforms.InterpolationMode.BICUBIC, antialias=True),\n            transforms.Normalize(0.5, 0.5)\n        ])\n\n        if freeze:\n            self.freeze()\n\n    def freeze(self):\n        for param in self.parseq.parameters():\n            param.requires_grad_(False) \n\n    def forward(self, x):\n        \n        x = torch.cat([self.parseq_transform(t[None]) for t in x])\n        logits = self.parseq(x.to(next(self.parameters()).device))\n\n        return logits\n\n    def img2txt(self, x):\n\n        pred = self(x)\n        label, confidence = self.parseq.tokenizer.decode(pred)\n        return label\n\n    \n    def calc_loss(self, x, label):\n\n        preds = self(x)  # (B, l, C) l=26, C=95\n        gt_ids = self.parseq.tokenizer.encode(label).to(preds.device) # (B, l_trun)\n\n        losses = []\n        for pred, gt_id in zip(preds, gt_ids):\n\n            eos_id = (gt_id == 0).nonzero().item()\n            gt_id = gt_id[1: eos_id]\n            pred = pred[:eos_id-1, :]\n\n            ce_loss = nn.functional.cross_entropy(pred.permute(1, 0)[None], gt_id[None])\n            ce_loss = torch.clamp(ce_loss, max = 1.0)\n            losses.append(ce_loss[None])\n\n        loss = torch.cat(losses)\n\n        return loss"
  },
  {
    "path": "sgm/util.py",
    "content": "import functools\nimport importlib\nimport os\nfrom functools import partial\nfrom inspect import isfunction\n\nimport fsspec\nimport numpy as np\nimport torch\nfrom PIL import Image, ImageDraw, ImageFont\nfrom safetensors.torch import load_file as load_safetensors\n\n\ndef disabled_train(self, mode=True):\n    \"\"\"Overwrite model.train with this function to make sure train/eval mode\n    does not change anymore.\"\"\"\n    return self\n\n\ndef get_string_from_tuple(s):\n    try:\n        # Check if the string starts and ends with parentheses\n        if s[0] == \"(\" and s[-1] == \")\":\n            # Convert the string to a tuple\n            t = eval(s)\n            # Check if the type of t is tuple\n            if type(t) == tuple:\n                return t[0]\n            else:\n                pass\n    except:\n        pass\n    return s\n\n\ndef is_power_of_two(n):\n    \"\"\"\n    chat.openai.com/chat\n    Return True if n is a power of 2, otherwise return False.\n\n    The function is_power_of_two takes an integer n as input and returns True if n is a power of 2, otherwise it returns False.\n    The function works by first checking if n is less than or equal to 0. If n is less than or equal to 0, it can't be a power of 2, so the function returns False.\n    If n is greater than 0, the function checks whether n is a power of 2 by using a bitwise AND operation between n and n-1. If n is a power of 2, then it will have only one bit set to 1 in its binary representation. When we subtract 1 from a power of 2, all the bits to the right of that bit become 1, and the bit itself becomes 0. So, when we perform a bitwise AND between n and n-1, we get 0 if n is a power of 2, and a non-zero value otherwise.\n    Thus, if the result of the bitwise AND operation is 0, then n is a power of 2 and the function returns True. Otherwise, the function returns False.\n\n    \"\"\"\n    if n <= 0:\n        return False\n    return (n & (n - 1)) == 0\n\n\ndef autocast(f, enabled=True):\n    def do_autocast(*args, **kwargs):\n        with torch.cuda.amp.autocast(\n            enabled=enabled,\n            dtype=torch.get_autocast_gpu_dtype(),\n            cache_enabled=torch.is_autocast_cache_enabled(),\n        ):\n            return f(*args, **kwargs)\n\n    return do_autocast\n\n\ndef load_partial_from_config(config):\n    return partial(get_obj_from_str(config[\"target\"]), **config.get(\"params\", dict()))\n\n\ndef log_txt_as_img(wh, xc, size=10):\n    # wh a tuple of (width, height)\n    # xc a list of captions to plot\n    b = len(xc)\n    txts = list()\n    for bi in range(b):\n        txt = Image.new(\"RGB\", wh, color=\"white\")\n        draw = ImageDraw.Draw(txt)\n        font = ImageFont.truetype(\"data/DejaVuSans.ttf\", size=size)\n        nc = int(40 * (wh[0] / 256))\n        if isinstance(xc[bi], list):\n            text_seq = xc[bi][0]\n        else:\n            text_seq = xc[bi]\n        lines = \"\\n\".join(\n            text_seq[start : start + nc] for start in range(0, len(text_seq), nc)\n        )\n\n        try:\n            draw.text((0, 0), lines, fill=\"black\", font=font)\n        except UnicodeEncodeError:\n            print(\"Cant encode string for logging. Skipping.\")\n\n        txt = np.array(txt).transpose(2, 0, 1) / 127.5 - 1.0\n        txts.append(txt)\n    txts = np.stack(txts)\n    txts = torch.tensor(txts)\n    return txts\n\n\ndef partialclass(cls, *args, **kwargs):\n    class NewCls(cls):\n        __init__ = functools.partialmethod(cls.__init__, *args, **kwargs)\n\n    return NewCls\n\n\ndef make_path_absolute(path):\n    fs, p = fsspec.core.url_to_fs(path)\n    if fs.protocol == \"file\":\n        return os.path.abspath(p)\n    return path\n\n\ndef ismap(x):\n    if not isinstance(x, torch.Tensor):\n        return False\n    return (len(x.shape) == 4) and (x.shape[1] > 3)\n\n\ndef isimage(x):\n    if not isinstance(x, torch.Tensor):\n        return False\n    return (len(x.shape) == 4) and (x.shape[1] == 3 or x.shape[1] == 1)\n\n\ndef isheatmap(x):\n    if not isinstance(x, torch.Tensor):\n        return False\n\n    return x.ndim == 2\n\n\ndef isneighbors(x):\n    if not isinstance(x, torch.Tensor):\n        return False\n    return x.ndim == 5 and (x.shape[2] == 3 or x.shape[2] == 1)\n\n\ndef exists(x):\n    return x is not None\n\n\ndef expand_dims_like(x, y):\n    while x.dim() != y.dim():\n        x = x.unsqueeze(-1)\n    return x\n\n\ndef default(val, d):\n    if exists(val):\n        return val\n    return d() if isfunction(d) else d\n\n\ndef mean_flat(tensor):\n    \"\"\"\n    https://github.com/openai/guided-diffusion/blob/27c20a8fab9cb472df5d6bdd6c8d11c8f430b924/guided_diffusion/nn.py#L86\n    Take the mean over all non-batch dimensions.\n    \"\"\"\n    return tensor.mean(dim=list(range(1, len(tensor.shape))))\n\n\ndef count_params(model, verbose=False):\n    total_params = sum(p.numel() for p in model.parameters())\n    if verbose:\n        print(f\"{model.__class__.__name__} has {total_params * 1.e-6:.2f} M params.\")\n    return total_params\n\n\ndef instantiate_from_config(config):\n    if not \"target\" in config:\n        if config == \"__is_first_stage__\":\n            return None\n        elif config == \"__is_unconditional__\":\n            return None\n        raise KeyError(\"Expected key `target` to instantiate.\")\n    return get_obj_from_str(config[\"target\"])(**config.get(\"params\", dict()))\n\n\ndef get_obj_from_str(string, reload=False, invalidate_cache=True):\n    module, cls = string.rsplit(\".\", 1)\n    if invalidate_cache:\n        importlib.invalidate_caches()\n    if reload:\n        module_imp = importlib.import_module(module)\n        importlib.reload(module_imp)\n    return getattr(importlib.import_module(module, package=None), cls)\n\n\ndef append_zero(x):\n    return torch.cat([x, x.new_zeros([1])])\n\n\ndef append_dims(x, target_dims):\n    \"\"\"Appends dimensions to the end of a tensor until it has target_dims dimensions.\"\"\"\n    dims_to_append = target_dims - x.ndim\n    if dims_to_append < 0:\n        raise ValueError(\n            f\"input has {x.ndim} dims but target_dims is {target_dims}, which is less\"\n        )\n    return x[(...,) + (None,) * dims_to_append]\n\n\ndef load_model_from_config(config, ckpt, verbose=True, freeze=True):\n    print(f\"Loading model from {ckpt}\")\n    if ckpt.endswith(\"ckpt\"):\n        pl_sd = torch.load(ckpt, map_location=\"cpu\")\n        if \"global_step\" in pl_sd:\n            print(f\"Global Step: {pl_sd['global_step']}\")\n        sd = pl_sd[\"state_dict\"]\n    elif ckpt.endswith(\"safetensors\"):\n        sd = load_safetensors(ckpt)\n    else:\n        raise NotImplementedError\n\n    model = instantiate_from_config(config.model)\n    sd = pl_sd[\"state_dict\"]\n\n    m, u = model.load_state_dict(sd, strict=False)\n\n    if len(m) > 0 and verbose:\n        print(\"missing keys:\")\n        print(m)\n    if len(u) > 0 and verbose:\n        print(\"unexpected keys:\")\n        print(u)\n\n    if freeze:\n        for param in model.parameters():\n            param.requires_grad = False\n\n    model.eval()\n    return model\n"
  },
  {
    "path": "src/parseq/.gitignore",
    "content": "# Output directories\noutputs/\nmultirun/\nray_results/\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n.python-version\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n# IDE\n.idea/\n"
  },
  {
    "path": "src/parseq/Datasets.md",
    "content": "We use various synthetic and real datasets. More info is in Appendix F of the supplementary material. Some preprocessing scripts are included in [`tools/`](tools).\n\n| Dataset | Type  | Remarks |\n|:-------:|:-----:|:--------|\n| [MJSynth](https://www.robots.ox.ac.uk/~vgg/data/text/) | synthetic | Case-sensitive annotations were extracted from the image filenames |\n| [SynthText](https://www.robots.ox.ac.uk/~vgg/data/scenetext/) | synthetic | Processed with [`crop_by_word_bb_syn90k.py`](https://github.com/FangShancheng/ABINet/blob/main/tools/crop_by_word_bb_syn90k.py) |\n| [IC13](https://rrc.cvc.uab.es/?ch=2) | real | Three archives: 857, 1015, 1095 (full) |\n| [IC15](https://rrc.cvc.uab.es/?ch=4) | real | Two archives: 1811, 2077 (full) |\n| [CUTE80](http://cs-chan.com/downloads_cute80_dataset.html) | real | \\[1\\] |\n| [IIIT5k](https://cvit.iiit.ac.in/research/projects/cvit-projects/the-iiit-5k-word-dataset) | real | \\[1\\] |\n| [SVT](http://vision.ucsd.edu/~kai/svt/) | real | \\[1\\] |\n| [SVTP](https://openaccess.thecvf.com/content_iccv_2013/html/Phan_Recognizing_Text_with_2013_ICCV_paper.html) | real | \\[1\\] |\n| [ArT](https://rrc.cvc.uab.es/?ch=14) | real | \\[2\\] |\n| [LSVT](https://rrc.cvc.uab.es/?ch=16) | real | \\[2\\] |\n| [MLT19](https://rrc.cvc.uab.es/?ch=15) | real | \\[2\\] |\n| [RCTW17](https://rctw.vlrlab.net/dataset.html) | real | \\[2\\] |\n| [ReCTS](https://rrc.cvc.uab.es/?ch=12) | real | \\[2\\] |\n| [Uber-Text](https://s3-us-west-2.amazonaws.com/uber-common-public/ubertext/index.html) | real | \\[2\\] |\n| [COCO-Text v1.4](https://rrc.cvc.uab.es/?ch=5) | real | Processed with [`coco_text_converter.py`](tools/coco_text_converter.py) |\n| [COCO-Text v2.0](https://bgshih.github.io/cocotext/) | real | Processed with [`coco_2_converter.py`](tools/coco_2_converter.py) |\n| [OpenVINO](https://proceedings.mlr.press/v157/krylov21a.html) | real | [Annotations](https://storage.openvinotoolkit.org/repositories/openvino_training_extensions/datasets/open_images_v5_text/) for a subset of [Open Images](https://github.com/cvdfoundation/open-images-dataset). Processed with [`openvino_converter.py`](tools/openvino_converter.py). |\n| [TextOCR](https://textvqa.org/textocr/) | real | Annotations for a subset of Open Images. Processed with [`textocr_converter.py`](tools/textocr_converter.py). A _horizontal_ version can be generated by passing `--rectify_pose`. |\n\n\\[1\\] Case-sensitive annotations from [Long and Yao](https://github.com/Jyouhou/Case-Sensitive-Scene-Text-Recognition-Datasets) + [our corrections](https://github.com/baudm/Case-Sensitive-Scene-Text-Recognition-Datasets). Processed with [case_sensitive_str_datasets_converter.py](tools/case_sensitive_str_datasets_converter.py)<br/>\n\\[2\\] Archives used as-is from [Baek et al.](https://github.com/ku21fan/STR-Fewer-Labels/blob/main/data.md) They are included in the dataset release for convenience. Please refer to their work for more info about the datasets.\n\nThe preprocessed archives are available here: [val + test + most of train](https://drive.google.com/drive/folders/1NYuoi7dfJVgo-zUJogh8UQZgIMpLviOE), [TextOCR + OpenVINO](https://drive.google.com/drive/folders/1D9z_YJVa6f-O0juni-yG5jcwnhvYw-qC)\n\nThe expected filesystem structure is as follows:\n```\ndata\n├── test\n│   ├── ArT\n│   ├── COCOv1.4\n│   ├── CUTE80\n│   ├── IC13_1015\n│   ├── IC13_1095  # Full IC13 test set. Typically not used for benchmarking but provided here for convenience.\n│   ├── IC13_857\n│   ├── IC15_1811\n│   ├── IC15_2077\n│   ├── IIIT5k\n│   ├── SVT\n│   ├── SVTP\n│   └── Uber\n├── train\n│   ├── real\n│   │   ├── ArT\n│   │   │   ├── train\n│   │   │   └── val\n│   │   ├── COCOv2.0\n│   │   │   ├── train\n│   │   │   └── val\n│   │   ├── LSVT\n│   │   │   ├── test\n│   │   │   ├── train\n│   │   │   └── val\n│   │   ├── MLT19\n│   │   │   ├── test\n│   │   │   ├── train\n│   │   │   └── val\n│   │   ├── OpenVINO\n│   │   │   ├── train_1\n│   │   │   ├── train_2\n│   │   │   ├── train_5\n│   │   │   ├── train_f\n│   │   │   └── validation\n│   │   ├── RCTW17\n│   │   │   ├── test\n│   │   │   ├── train\n│   │   │   └── val\n│   │   ├── ReCTS\n│   │   │   ├── test\n│   │   │   ├── train\n│   │   │   └── val\n│   │   ├── TextOCR\n│   │   │   ├── train\n│   │   │   └── val\n│   │   └── Uber\n│   │       ├── train\n│   │       └── val\n│   └── synth\n│       ├── MJ\n│       │   ├── test\n│       │   ├── train\n│       │   └── val\n│       └── ST\n└── val\n   ├── IC13\n   ├── IC15\n   ├── IIIT5k\n   └── SVT\n```\n"
  },
  {
    "path": "src/parseq/LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "src/parseq/NOTICE",
    "content": "Scene Text Recognition Model Hub\nCopyright 2022 Darwin Bautista\n\nThe Initial Developer of strhub/models/abinet (sans system.py) is\nFang et al. (https://github.com/FangShancheng/ABINet).\nCopyright 2021-2022 USTC\n\nThe Initial Developer of strhub/models/crnn (sans system.py) is\nJieru Mei (https://github.com/meijieru/crnn.pytorch).\nCopyright 2017-2022 Jieru Mei\n\nThe Initial Developer of strhub/models/trba (sans system.py) is\nJeonghun Baek (https://github.com/clovaai/deep-text-recognition-benchmark).\nCopyright 2019-2022 NAVER Corp.\n\nThe Initial Developer of strhub/models/vitstr (sans system.py) is\nRowel Atienza (https://github.com/roatienza/deep-text-recognition-benchmark).\nCopyright 2021-2022 Rowel Atienza\n"
  },
  {
    "path": "src/parseq/README.md",
    "content": "<div align=\"center\">\n\n# Scene Text Recognition with<br/>Permuted Autoregressive Sequence Models\n[![Apache License 2.0](https://img.shields.io/github/license/baudm/parseq)](https://github.com/baudm/parseq/blob/main/LICENSE)\n[![arXiv preprint](http://img.shields.io/badge/arXiv-2207.06966-b31b1b)](https://arxiv.org/abs/2207.06966)\n[![In Proc. ECCV 2022](http://img.shields.io/badge/ECCV-2022-6790ac)](https://www.ecva.net/papers/eccv_2022/papers_ECCV/html/556_ECCV_2022_paper.php)\n[![Gradio demo](https://img.shields.io/badge/%F0%9F%A4%97%20demo-Gradio-ff7c00)](https://huggingface.co/spaces/baudm/PARSeq-OCR)\n\n[**Darwin Bautista**](https://github.com/baudm) and [**Rowel Atienza**](https://github.com/roatienza)\n\nElectrical and Electronics Engineering Institute<br/>\nUniversity of the Philippines, Diliman\n\n[Method](#method-tldr) | [Sample Results](#sample-results) | [Getting Started](#getting-started) | [FAQ](#frequently-asked-questions) | [Training](#training) | [Evaluation](#evaluation) | [Citation](#citation)\n\n</div>\n\nScene Text Recognition (STR) models use language context to be more robust against noisy or corrupted images. Recent approaches like ABINet use a standalone or external Language Model (LM) for prediction refinement. In this work, we show that the external LM&mdash;which requires upfront allocation of dedicated compute capacity&mdash;is inefficient for STR due to its poor performance vs cost characteristics. We propose a more efficient approach using **p**ermuted **a**uto**r**egressive **seq**uence (PARSeq) models. View our ECCV [poster](https://drive.google.com/file/d/19luOT_RMqmafLMhKQQHBnHNXV7fOCRfw/view) and [presentation](https://drive.google.com/file/d/11VoZW4QC5tbMwVIjKB44447uTiuCJAAD/view) for a brief overview.\n\n![PARSeq](.github/gh-teaser.png)\n\n**NOTE:** _P-S and P-Ti are shorthands for PARSeq-S and PARSeq-Ti, respectively._\n\n### Method tl;dr\n\nOur main insight is that with an ensemble of autoregressive (AR) models, we could unify the current STR decoding methods (context-aware AR and context-free non-AR) and the bidirectional (cloze) refinement model:\n<div align=\"center\"><img src=\".github/contexts-example.png\" alt=\"Unified STR model\" width=\"75%\"/></div>\n\nA single Transformer can realize different models by merely varying its attention masks. This characteristic coupled with Permutation Language Modeling allows for a _unified_ STR model capable of context-free and context-aware inference, as well as iterative prediction refinement using bidirectional context **without** requiring a standalone language model. PARSeq can be considered an ensemble of AR models with shared architecture and weights:\n\n![System](.github/system.png)\n\n\n### Sample Results\n<div align=\"center\">\n\n| Input Image                                                                | PARSeq-S<sub>A</sub> | ABINet            | TRBA              | ViTSTR-S          | CRNN              |\n|:--------------------------------------------------------------------------:|:--------------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|\n| <img src=\"demo_images/art-01107.jpg\" alt=\"CHEWBACCA\" width=\"128\"/>         | CHEWBACCA            | CHEWBA**GG**A     | CHEWBACCA         | CHEWBACCA         | CHEW**U**ACCA     |\n| <img src=\"demo_images/coco-1166773.jpg\" alt=\"Chevron\" width=\"128\"/>        | Chevro**l**          | Chevro\\_          | Chevro\\_          | Chevr\\_\\_         | Chevr\\_\\_         |\n| <img src=\"demo_images/cute-184.jpg\" alt=\"SALMON\" height=\"128\"/>            | SALMON               | SALMON            | SALMON            | SALMON            | SA\\_MON           |\n| <img src=\"demo_images/ic13_word_256.png\" alt=\"Verbandstoffe\" width=\"128\"/> | Verbandst**e**ffe    | Verbandst**e**ffe | Verbandst**ell**e | Verbandst**e**ffe | Verbands**le**ffe |\n| <img src=\"demo_images/ic15_word_26.png\" alt=\"Kappa\" width=\"128\"/>          | Kappa                | Kappa             | Ka**s**pa         | Kappa             | Ka**ad**a         |\n| <img src=\"demo_images/uber-27491.jpg\" alt=\"3rdAve\" height=\"128\"/>          | 3rdAve               | 3=-Ave            | 3rdAve            | 3rdAve            | **Coke**          |\n\n**NOTE:** _Bold letters and underscores indicate wrong and missing character predictions, respectively._\n</div>\n\n## Getting Started\nThis repository contains the reference implementation for PARSeq and reproduced models (collectively referred to as _Scene Text Recognition Model Hub_). See `NOTICE` for copyright information.\nMajority of the code is licensed under the Apache License v2.0 (see `LICENSE`) while ABINet and CRNN sources are\nreleased under the BSD and MIT licenses, respectively (see corresponding `LICENSE` files for details).\n\n### Demo\nAn [interactive Gradio demo](https://huggingface.co/spaces/baudm/PARSeq-OCR) hosted at Hugging Face is available. The pretrained weights released here are used for the demo.\n\n### Installation\nRequires Python 3.7 and PyTorch 1.10 or newer. Tested on Python 3.9 and PyTorch 1.10.\n```bash\n$ pip install -r requirements.txt\n$ pip install -e .\n ```\n### Datasets\nDownload the [datasets](Datasets.md) from the following links:\n1. [LMDB archives](https://drive.google.com/drive/folders/1NYuoi7dfJVgo-zUJogh8UQZgIMpLviOE) for MJSynth, SynthText, IIIT5k, SVT, SVTP, IC13, IC15, CUTE80, ArT, RCTW17, ReCTS, LSVT, MLT19, COCO-Text, and Uber-Text.\n2. [LMDB archives](https://drive.google.com/drive/folders/1D9z_YJVa6f-O0juni-yG5jcwnhvYw-qC) for TextOCR and OpenVINO.\n\n### Pretrained Models via Torch Hub\nAvailable models are: `abinet`, `crnn`, `trba`, `vitstr`, `parseq_tiny`, and `parseq`.\n```python\nimport torch\nfrom PIL import Image\nfrom strhub.data.module import SceneTextDataModule\n\n# Load model and image transforms\nparseq = torch.hub.load('baudm/parseq', 'parseq', pretrained=True).eval()\nimg_transform = SceneTextDataModule.get_transform(parseq.hparams.img_size)\n\nimg = Image.open('/path/to/image.png').convert('RGB')\n# Preprocess. Model expects a batch of images with shape: (B, C, H, W)\nimg = img_transform(img).unsqueeze(0)\n\nlogits = parseq(img)\nlogits.shape  # torch.Size([1, 26, 95]), 94 characters + [EOS] symbol\n\n# Greedy decoding\npred = logits.softmax(-1)\nlabel, confidence = parseq.tokenizer.decode(pred)\nprint('Decoded label = {}'.format(label[0]))\n```\n\n## Frequently Asked Questions\n- How do I train on a new language? See Issues [#5](https://github.com/baudm/parseq/issues/5) and [#9](https://github.com/baudm/parseq/issues/9).\n- Can you export to TorchScript or ONNX? Yes, see Issue [#12](https://github.com/baudm/parseq/issues/12#issuecomment-1267842315).\n- How do I test on my own dataset? See Issue [#27](https://github.com/baudm/parseq/issues/27).\n- How do I finetune and/or create a custom dataset? See Issue [#7](https://github.com/baudm/parseq/issues/7).\n- What is `val_NED`? See Issue [#10](https://github.com/baudm/parseq/issues/10).\n\n## Training\nThe training script can train any supported model. You can override any configuration using the command line. Please refer to [Hydra](https://hydra.cc) docs for more info about the syntax. Use `./train.py --help` to see the default configuration.\n\n<details><summary>Sample commands for different training configurations</summary><p>\n\n### Finetune using pretrained weights\n```bash\n./train.py pretrained=parseq-tiny  # Not all experiments have pretrained weights\n```\n\n### Train a model variant/preconfigured experiment\nThe base model configurations are in `configs/model/`, while variations are stored in `configs/experiment/`.\n```bash\n./train.py +experiment=parseq-tiny  # Some examples: abinet-sv, trbc\n```\n\n### Specify the character set for training\n```bash\n./train.py charset=94_full  # Other options: 36_lowercase or 62_mixed-case. See configs/charset/\n```\n\n### Specify the training dataset\n```bash\n./train.py dataset=real  # Other option: synth. See configs/dataset/\n```\n\n### Change general model training parameters\n```bash\n./train.py model.img_size=[32, 128] model.max_label_length=25 model.batch_size=384\n```\n\n### Change data-related training parameters\n```bash\n./train.py data.root_dir=data data.num_workers=2 data.augment=true\n```\n\n### Change `pytorch_lightning.Trainer` parameters\n```bash\n./train.py trainer.max_epochs=20 trainer.gpus=2 +trainer.accelerator=gpu\n```\nNote that you can pass any [Trainer parameter](https://pytorch-lightning.readthedocs.io/en/stable/common/trainer.html),\nyou just need to prefix it with `+` if it is not originally specified in `configs/main.yaml`.\n\n### Resume training from checkpoint (experimental)\n```bash\n./train.py +experiment=<model_exp> ckpt_path=outputs/<model>/<timestamp>/checkpoints/<checkpoint>.ckpt\n```\n\n</p></details>\n\n## Evaluation\nThe test script, ```test.py```, can be used to evaluate any model trained with this project. For more info, see ```./test.py --help```.\n\nPARSeq runtime parameters can be passed using the format `param:type=value`. For example, PARSeq NAR decoding can be invoked via `./test.py parseq.ckpt refine_iters:int=2 decode_ar:bool=false`.\n\n<details><summary>Sample commands for reproducing results</summary><p>\n\n### Lowercase alphanumeric comparison on benchmark datasets (Table 6)\n```bash\n./test.py outputs/<model>/<timestamp>/checkpoints/last.ckpt  # or use the released weights: ./test.py pretrained=parseq\n```\n**Sample output:**\n| Dataset   | # samples | Accuracy | 1 - NED | Confidence | Label Length |\n|:---------:|----------:|---------:|--------:|-----------:|-------------:|\n| IIIT5k    |      3000 |    99.00 |   99.79 |      97.09 |         5.09 |\n| SVT       |       647 |    97.84 |   99.54 |      95.87 |         5.86 |\n| IC13_1015 |      1015 |    98.13 |   99.43 |      97.19 |         5.31 |\n| IC15_2077 |      2077 |    89.22 |   96.43 |      91.91 |         5.33 |\n| SVTP      |       645 |    96.90 |   99.36 |      94.37 |         5.86 |\n| CUTE80    |       288 |    98.61 |   99.80 |      96.43 |         5.53 |\n| **Combined** | **7672** | **95.95** | **98.78** | **95.34** | **5.33** |\n--------------------------------------------------------------------------\n\n### Benchmark using different evaluation character sets (Table 4)\n```bash\n./test.py outputs/<model>/<timestamp>/checkpoints/last.ckpt  # lowercase alphanumeric (36-character set)\n./test.py outputs/<model>/<timestamp>/checkpoints/last.ckpt --cased  # mixed-case alphanumeric (62-character set)\n./test.py outputs/<model>/<timestamp>/checkpoints/last.ckpt --cased --punctuation  # mixed-case alphanumeric + punctuation (94-character set)\n```\n\n### Lowercase alphanumeric comparison on more challenging datasets (Table 5)\n```bash\n./test.py outputs/<model>/<timestamp>/checkpoints/last.ckpt --new\n```\n\n### Benchmark Model Compute Requirements (Figure 5)\n```bash\n./bench.py model=parseq model.decode_ar=false model.refine_iters=3\n<torch.utils.benchmark.utils.common.Measurement object at 0x7f8fcae67ee0>\nmodel(x)\n  Median: 14.87 ms\n  IQR:    0.33 ms (14.78 to 15.12)\n  7 measurements, 10 runs per measurement, 1 thread\n| module                | #parameters   | #flops   | #activations   |\n|:----------------------|:--------------|:---------|:---------------|\n| model                 | 23.833M       | 3.255G   | 8.214M         |\n|  encoder              |  21.381M      |  2.88G   |  7.127M        |\n|  decoder              |  2.368M       |  0.371G  |  1.078M        |\n|  head                 |  36.575K      |  3.794M  |  9.88K         |\n|  text_embed.embedding |  37.248K      |  0       |  0             |\n```\n\n### Latency Measurements vs Output Label Length (Appendix I)\n```bash\n./bench.py model=parseq model.decode_ar=false model.refine_iters=3 +range=true\n```\n\n### Orientation robustness benchmark (Appendix J)\n```bash\n./test.py outputs/<model>/<timestamp>/checkpoints/last.ckpt --cased --punctuation  # no rotation\n./test.py outputs/<model>/<timestamp>/checkpoints/last.ckpt --cased --punctuation --rotation 90\n./test.py outputs/<model>/<timestamp>/checkpoints/last.ckpt --cased --punctuation --rotation 180\n./test.py outputs/<model>/<timestamp>/checkpoints/last.ckpt --cased --punctuation --rotation 270\n```\n\n### Using trained models to read text from images (Appendix L)\n```bash\n./read.py outputs/<model>/<timestamp>/checkpoints/last.ckpt --images demo_images/*  # Or use ./read.py pretrained=parseq\nAdditional keyword arguments: {}\ndemo_images/art-01107.jpg: CHEWBACCA\ndemo_images/coco-1166773.jpg: Chevrol\ndemo_images/cute-184.jpg: SALMON\ndemo_images/ic13_word_256.png: Verbandsteffe\ndemo_images/ic15_word_26.png: Kaopa\ndemo_images/uber-27491.jpg: 3rdAve\n\n# use NAR decoding + 2 refinement iterations for PARSeq\n./read.py pretrained=parseq refine_iters:int=2 decode_ar:bool=false --images demo_images/*\n```\n</p></details>\n\n## Tuning\n\nWe use [Ray Tune](https://www.ray.io/ray-tune) for automated parameter tuning of the learning rate. See `./tune.py --help`. Extend `tune.py` to support tuning of other hyperparameters.\n```bash\n./tune.py tune.num_samples=20  # find optimum LR for PARSeq's default config using 20 trials\n./tune.py +experiment=tune_abinet-lm  # find the optimum learning rate for ABINet's language model\n```\n\n## Citation\n```bibtex\n@InProceedings{bautista2022parseq,\n  title={Scene Text Recognition with Permuted Autoregressive Sequence Models},\n  author={Bautista, Darwin and Atienza, Rowel},\n  booktitle={European Conference on Computer Vision},\n  pages={178--196},\n  month={10},\n  year={2022},\n  publisher={Springer Nature Switzerland},\n  address={Cham},\n  doi={10.1007/978-3-031-19815-1_11},\n  url={https://doi.org/10.1007/978-3-031-19815-1_11}\n}\n```\n"
  },
  {
    "path": "src/parseq/bench.py",
    "content": "#!/usr/bin/env python3\n# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\n\nimport torch\nfrom torch.utils import benchmark\n\nfrom fvcore.nn import FlopCountAnalysis, ActivationCountAnalysis, flop_count_table\n\nimport hydra\nfrom omegaconf import DictConfig\n\n\n@torch.inference_mode()\n@hydra.main(config_path='configs', config_name='bench', version_base='1.2')\ndef main(config: DictConfig):\n    # For consistent behavior\n    os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8'\n    torch.backends.cudnn.benchmark = False\n    torch.use_deterministic_algorithms(True)\n\n    device = config.get('device', 'cuda')\n\n    h, w = config.data.img_size\n    x = torch.rand(1, 3, h, w, device=device)\n    model = hydra.utils.instantiate(config.model).eval().to(device)\n\n    if config.get('range', False):\n        for i in range(1, 26, 4):\n            timer = benchmark.Timer(\n                stmt='model(x, len)',\n                globals={'model': model, 'x': x, 'len': i})\n            print(timer.blocked_autorange(min_run_time=1))\n    else:\n        timer = benchmark.Timer(\n            stmt='model(x)',\n            globals={'model': model, 'x': x})\n        flops = FlopCountAnalysis(model, x)\n        acts = ActivationCountAnalysis(model, x)\n        print(timer.blocked_autorange(min_run_time=1))\n        print(flop_count_table(flops, 1, acts, False))\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "src/parseq/configs/bench.yaml",
    "content": "# Disable any logging or output\ndefaults:\n  - main\n  - _self_\n  - override hydra/job_logging: disabled\n\nhydra:\n  output_subdir: null\n  run:\n    dir: .\n"
  },
  {
    "path": "src/parseq/configs/charset/36_lowercase.yaml",
    "content": "# @package _global_\nmodel:\n  charset_train: \"0123456789abcdefghijklmnopqrstuvwxyz\"\n"
  },
  {
    "path": "src/parseq/configs/charset/62_mixed-case.yaml",
    "content": "# @package _global_\nmodel:\n  charset_train: \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n"
  },
  {
    "path": "src/parseq/configs/charset/94_full.yaml",
    "content": "# @package _global_\nmodel:\n  charset_train: \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~\"\n"
  },
  {
    "path": "src/parseq/configs/dataset/real.yaml",
    "content": "# @package _global_\ndata:\n  train_dir: real\n"
  },
  {
    "path": "src/parseq/configs/dataset/synth.yaml",
    "content": "# @package _global_\ndata:\n  train_dir: synth\n  num_workers: 3\n\ntrainer:\n  limit_train_batches: 0.20496  # to match the steps per epoch of `real`\n"
  },
  {
    "path": "src/parseq/configs/experiment/abinet-sv.yaml",
    "content": "# @package _global_\ndefaults:\n  - override /model: abinet\n\nmodel:\n  name: abinet-sv\n  v_num_layers: 2\n  v_attention: attention\n"
  },
  {
    "path": "src/parseq/configs/experiment/abinet.yaml",
    "content": "# @package _global_\ndefaults:\n  - override /model: abinet\n"
  },
  {
    "path": "src/parseq/configs/experiment/crnn.yaml",
    "content": "# @package _global_\ndefaults:\n  - override /model: crnn\n\ndata:\n  num_workers: 5\n"
  },
  {
    "path": "src/parseq/configs/experiment/parseq-patch16-224.yaml",
    "content": "# @package _global_\ndefaults:\n  - override /model: parseq\n\nmodel:\n  img_size: [ 224, 224 ]  # [ height, width ]\n  patch_size: [ 16, 16 ]  # [ height, width ]\n"
  },
  {
    "path": "src/parseq/configs/experiment/parseq-tiny.yaml",
    "content": "# @package _global_\ndefaults:\n  - override /model: parseq\n\nmodel:\n  name: parseq-tiny\n  embed_dim: 192\n  enc_num_heads: 3\n  dec_num_heads: 6\n"
  },
  {
    "path": "src/parseq/configs/experiment/parseq.yaml",
    "content": "# @package _global_\ndefaults:\n  - override /model: parseq\n"
  },
  {
    "path": "src/parseq/configs/experiment/trba.yaml",
    "content": "# @package _global_\ndefaults:\n  - override /model: trba\n\ndata:\n  num_workers: 3\n"
  },
  {
    "path": "src/parseq/configs/experiment/trbc.yaml",
    "content": "# @package _global_\ndefaults:\n  - override /model: trba\n\nmodel:\n  name: trbc\n  _target_: strhub.models.trba.system.TRBC\n  lr: 1e-4\n\ndata:\n  num_workers: 3\n"
  },
  {
    "path": "src/parseq/configs/experiment/tune_abinet-lm.yaml",
    "content": "# @package _global_\ndefaults:\n  - override /model: abinet\n\nmodel:\n  name: abinet-lm\n  lm_only: true\n\ndata:\n  augment: false\n  num_workers: 3\n\ntune:\n  gpus_per_trial: 0.5\n  lr:\n    min: 1e-5\n    max: 1e-3\n"
  },
  {
    "path": "src/parseq/configs/experiment/vitstr.yaml",
    "content": "# @package _global_\ndefaults:\n  - override /model: vitstr\n\nmodel:\n  img_size: [ 32, 128 ]  # [ height, width ]\n  patch_size: [ 4, 8 ]  # [ height, width ]\n"
  },
  {
    "path": "src/parseq/configs/main.yaml",
    "content": "defaults:\n  - _self_\n  - model: parseq\n  - charset: 94_full\n  - dataset: real\n\nmodel:\n  _convert_: all\n  img_size: [ 32, 128 ]  # [ height, width ]\n  max_label_length: 25\n  # The ordering in charset_train matters. It determines the token IDs assigned to each character.\n  charset_train: ???\n  # For charset_test, ordering doesn't matter.\n  charset_test: \"0123456789abcdefghijklmnopqrstuvwxyz\"\n  batch_size: 384\n  weight_decay: 0.0\n  warmup_pct: 0.075  # equivalent to 1.5 epochs of warm up\n\ndata:\n  _target_: strhub.data.module.SceneTextDataModule\n  root_dir: data\n  train_dir: ???\n  batch_size: ${model.batch_size}\n  img_size: ${model.img_size}\n  charset_train: ${model.charset_train}\n  charset_test: ${model.charset_test}\n  max_label_length: ${model.max_label_length}\n  remove_whitespace: true\n  normalize_unicode: true\n  augment: true\n  num_workers: 2\n\ntrainer:\n  _target_: pytorch_lightning.Trainer\n  _convert_: all\n  val_check_interval: 1000\n  #max_steps: 169680  # 20 epochs x 8484 steps (for batch size = 384, real data)\n  max_epochs: 20\n  gradient_clip_val: 20\n  gpus: 2\n\nckpt_path: null\npretrained: null\n\nhydra:\n  output_subdir: config\n  run:\n    dir: outputs/${model.name}/${now:%Y-%m-%d}_${now:%H-%M-%S}\n  sweep:\n    dir: multirun/${model.name}/${now:%Y-%m-%d}_${now:%H-%M-%S}\n    subdir: ${hydra.job.override_dirname}\n"
  },
  {
    "path": "src/parseq/configs/model/abinet.yaml",
    "content": "name: abinet\n_target_: strhub.models.abinet.system.ABINet\n\n# Shared Transformer configuration\nd_model: 512\nnhead: 8\nd_inner: 2048\nactivation: relu\ndropout: 0.1\n\n# Architecture\nv_backbone: transformer\nv_num_layers: 3\nv_attention: position\nv_attention_mode: nearest\nl_num_layers: 4\nl_use_self_attn: false\n\n# Training\nlr: 3.4e-4\nl_lr: 3e-4\niter_size: 3\na_loss_weight: 1.\nv_loss_weight: 1.\nl_loss_weight: 1.\nl_detach: true\n"
  },
  {
    "path": "src/parseq/configs/model/crnn.yaml",
    "content": "name: crnn\n_target_: strhub.models.crnn.system.CRNN\n\n# Architecture\nhidden_size: 256\nleaky_relu: false\n\n# Training\nlr: 5.1e-4\n"
  },
  {
    "path": "src/parseq/configs/model/parseq.yaml",
    "content": "name: parseq\n_target_: strhub.models.parseq.system.PARSeq\n\n# Data\npatch_size: [ 4, 8 ]  # [ height, width ]\n\n# Architecture\nembed_dim: 384\nenc_num_heads: 6\nenc_mlp_ratio: 4\nenc_depth: 12\ndec_num_heads: 12\ndec_mlp_ratio: 4\ndec_depth: 1\n\n# Training\nlr: 7e-4\nperm_num: 6\nperm_forward: true\nperm_mirrored: true\ndropout: 0.1\n\n# Decoding mode (test)\ndecode_ar: true\nrefine_iters: 1\n"
  },
  {
    "path": "src/parseq/configs/model/trba.yaml",
    "content": "name: trba\n_target_: strhub.models.trba.system.TRBA\n\n# Architecture\nnum_fiducial: 20\noutput_channel: 512\nhidden_size: 256\n\n# Training\nlr: 6.9e-4\n"
  },
  {
    "path": "src/parseq/configs/model/vitstr.yaml",
    "content": "name: vitstr\n_target_: strhub.models.vitstr.system.ViTSTR\n\n# Data\nimg_size: [ 224, 224 ]  # [ height, width ]\npatch_size: [ 16, 16 ]  # [ height, width ]\n\n# Architecture\nembed_dim: 384\nnum_heads: 6\n\n# Training\nlr: 8.9e-4\n"
  },
  {
    "path": "src/parseq/configs/tune.yaml",
    "content": "defaults:\n  - main\n  - _self_\n\ntrainer:\n  gpus: 1  # tuning with DDP is not yet supported.\n\ntune:\n  num_samples: 10\n  gpus_per_trial: 1\n  lr:\n    min: 1e-4\n    max: 2e-3\n  resume_dir: null\n\nhydra:\n  run:\n    dir: ray_results/${model.name}/${now:%Y-%m-%d}_${now:%H-%M-%S}\n"
  },
  {
    "path": "src/parseq/hubconf.py",
    "content": "from strhub.models.utils import create_model\n\n\ndependencies = ['torch', 'pytorch_lightning', 'timm']\n\n\ndef parseq_tiny(pretrained: bool = False, decode_ar: bool = True, refine_iters: int = 1, **kwargs):\n    \"\"\"\n    PARSeq tiny model (img_size=128x32, patch_size=8x4, d_model=192)\n    @param pretrained: (bool) Use pretrained weights\n    @param decode_ar: (bool) use AR decoding\n    @param refine_iters: (int) number of refinement iterations to use\n    \"\"\"\n    return create_model('parseq-tiny', pretrained, decode_ar=decode_ar, refine_iters=refine_iters, **kwargs)\n\n\ndef parseq(pretrained: bool = False, decode_ar: bool = True, refine_iters: int = 1, **kwargs):\n    \"\"\"\n    PARSeq base model (img_size=128x32, patch_size=8x4, d_model=384)\n    @param pretrained: (bool) Use pretrained weights\n    @param decode_ar: (bool) use AR decoding\n    @param refine_iters: (int) number of refinement iterations to use\n    \"\"\"\n    return create_model('parseq', pretrained, decode_ar=decode_ar, refine_iters=refine_iters, **kwargs)\n\n\ndef abinet(pretrained: bool = False, iter_size: int = 3, **kwargs):\n    \"\"\"\n    ABINet model (img_size=128x32)\n    @param pretrained: (bool) Use pretrained weights\n    @param iter_size: (int) number of refinement iterations to use\n    \"\"\"\n    return create_model('abinet', pretrained, iter_size=iter_size, **kwargs)\n\n\ndef trba(pretrained: bool = False, **kwargs):\n    \"\"\"\n    TRBA model (img_size=128x32)\n    @param pretrained: (bool) Use pretrained weights\n    \"\"\"\n    return create_model('trba', pretrained, **kwargs)\n\n\ndef vitstr(pretrained: bool = False, **kwargs):\n    \"\"\"\n    ViTSTR small model (img_size=128x32, patch_size=8x4, d_model=384)\n    @param pretrained: (bool) Use pretrained weights\n    \"\"\"\n    return create_model('vitstr', pretrained, **kwargs)\n\n\ndef crnn(pretrained: bool = False, **kwargs):\n    \"\"\"\n    CRNN model (img_size=128x32)\n    @param pretrained: (bool) Use pretrained weights\n    \"\"\"\n    return create_model('crnn', pretrained, **kwargs)\n"
  },
  {
    "path": "src/parseq/read.py",
    "content": "#!/usr/bin/env python3\n# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\n\nimport torch\n\nfrom PIL import Image\n\nfrom strhub.data.module import SceneTextDataModule\nfrom strhub.models.utils import load_from_checkpoint, parse_model_args\n\n\n@torch.inference_mode()\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('checkpoint', help=\"Model checkpoint (or 'pretrained=<model_id>')\")\n    parser.add_argument('--images', nargs='+', help='Images to read')\n    parser.add_argument('--device', default='cuda')\n    args, unknown = parser.parse_known_args()\n    kwargs = parse_model_args(unknown)\n    print(f'Additional keyword arguments: {kwargs}')\n\n    model = load_from_checkpoint(args.checkpoint, **kwargs).eval().to(args.device)\n    img_transform = SceneTextDataModule.get_transform(model.hparams.img_size)\n\n    for fname in args.images:\n        # Load image and prepare for input\n        image = Image.open(fname).convert('RGB')\n        image = img_transform(image).unsqueeze(0).to(args.device)\n\n        p = model(image).softmax(-1)\n        pred, p = model.tokenizer.decode(p)\n        print(f'{fname}: {pred[0]}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "src/parseq/requirements.txt",
    "content": "torch>=1.10.2\ntorchvision>=0.11.3\npytorch-lightning~=1.6.5\ntimm~=0.6.5\nnltk~=3.7.0\nlmdb~=1.3.0\nPillow~=9.2.0\nimgaug~=0.4.0\nhydra-core~=1.2.0\nfvcore~=0.1.5.post20220512\nray[tune]~=1.13.0\nax-platform~=0.2.5.1\nPyYAML~=6.0.0\ntqdm~=4.64.0\n"
  },
  {
    "path": "src/parseq/setup.cfg",
    "content": "[tool:pytest]\nnorecursedirs =\n    .git\n    dist\n    build\naddopts =\n    --strict\n    --doctest-modules\n    --durations=0\n\n[coverage:report]\nexclude_lines =\n    pragma: no-cover\n    pass\n\n[flake8]\nmax-line-length = 120\nexclude = .tox,*.egg,build,temp\nselect = E,W,F\ndoctests = True\nverbose = 2\n# https://pep8.readthedocs.io/en/latest/intro.html#error-codes\nformat = pylint\n# see: https://www.flake8rules.com/\nignore =\n    E731  # Do not assign a lambda expression, use a def\n    W504  # Line break occurred after a binary operator\n    F401  # Module imported but unused\n    F841  # Local variable name is assigned to but never used\n    W605  # Invalid escape sequence 'x'\n\n# setup.cfg or tox.ini\n[check-manifest]\nignore =\n    *.yml\n    .github\n    .github/*\n\n[metadata]\nlicense_file = LICENSE\ndescription-file = README.md\n# long_description = file:README.md\n# long_description_content_type = text/markdown\n"
  },
  {
    "path": "src/parseq/setup.py",
    "content": "#!/usr/bin/env python\n\nfrom setuptools import setup, find_packages\n\nsetup(\n    name='strhub',\n    version='1.1.0',\n    description='Scene Text Recognition Model Hub: A collection of deep learning models for Scene Text Recognition',\n    author='Darwin Bautista',\n    author_email='baudm@users.noreply.github.com',\n    url='https://github.com/baudm/parseq',\n    install_requires=['torch~=1.12.1', 'pytorch-lightning~=1.6.5', 'timm~=0.6.5'],\n    packages=find_packages(),\n)\n"
  },
  {
    "path": "src/parseq/strhub/__init__.py",
    "content": ""
  },
  {
    "path": "src/parseq/strhub/data/__init__.py",
    "content": ""
  },
  {
    "path": "src/parseq/strhub/data/aa_overrides.py",
    "content": "# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Extends default ops to accept optional parameters.\"\"\"\nfrom functools import partial\n\nfrom timm.data.auto_augment import _LEVEL_DENOM, _randomly_negate, LEVEL_TO_ARG, NAME_TO_OP, rotate\n\n\ndef rotate_expand(img, degrees, **kwargs):\n    \"\"\"Rotate operation with expand=True to avoid cutting off the characters\"\"\"\n    kwargs['expand'] = True\n    return rotate(img, degrees, **kwargs)\n\n\ndef _level_to_arg(level, hparams, key, default):\n    magnitude = hparams.get(key, default)\n    level = (level / _LEVEL_DENOM) * magnitude\n    level = _randomly_negate(level)\n    return level,\n\n\ndef apply():\n    # Overrides\n    NAME_TO_OP.update({\n        'Rotate': rotate_expand\n    })\n    LEVEL_TO_ARG.update({\n        'Rotate': partial(_level_to_arg, key='rotate_deg', default=30.),\n        'ShearX': partial(_level_to_arg, key='shear_x_pct', default=0.3),\n        'ShearY': partial(_level_to_arg, key='shear_y_pct', default=0.3),\n        'TranslateXRel': partial(_level_to_arg, key='translate_x_pct', default=0.45),\n        'TranslateYRel': partial(_level_to_arg, key='translate_y_pct', default=0.45),\n    })\n"
  },
  {
    "path": "src/parseq/strhub/data/augment.py",
    "content": "# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom functools import partial\n\nimport imgaug.augmenters as iaa\nimport numpy as np\nfrom PIL import ImageFilter, Image\nfrom timm.data import auto_augment\n\nfrom strhub.data import aa_overrides\n\naa_overrides.apply()\n\n_OP_CACHE = {}\n\n\ndef _get_op(key, factory):\n    try:\n        op = _OP_CACHE[key]\n    except KeyError:\n        op = factory()\n        _OP_CACHE[key] = op\n    return op\n\n\ndef _get_param(level, img, max_dim_factor, min_level=1):\n    max_level = max(min_level, max_dim_factor * max(img.size))\n    return round(min(level, max_level))\n\n\ndef gaussian_blur(img, radius, **__):\n    radius = _get_param(radius, img, 0.02)\n    key = 'gaussian_blur_' + str(radius)\n    op = _get_op(key, lambda: ImageFilter.GaussianBlur(radius))\n    return img.filter(op)\n\n\ndef motion_blur(img, k, **__):\n    k = _get_param(k, img, 0.08, 3) | 1  # bin to odd values\n    key = 'motion_blur_' + str(k)\n    op = _get_op(key, lambda: iaa.MotionBlur(k))\n    return Image.fromarray(op(image=np.asarray(img)))\n\n\ndef gaussian_noise(img, scale, **_):\n    scale = _get_param(scale, img, 0.25) | 1  # bin to odd values\n    key = 'gaussian_noise_' + str(scale)\n    op = _get_op(key, lambda: iaa.AdditiveGaussianNoise(scale=scale))\n    return Image.fromarray(op(image=np.asarray(img)))\n\n\ndef poisson_noise(img, lam, **_):\n    lam = _get_param(lam, img, 0.2) | 1  # bin to odd values\n    key = 'poisson_noise_' + str(lam)\n    op = _get_op(key, lambda: iaa.AdditivePoissonNoise(lam))\n    return Image.fromarray(op(image=np.asarray(img)))\n\n\ndef _level_to_arg(level, _hparams, max):\n    level = max * level / auto_augment._LEVEL_DENOM\n    return level,\n\n\n_RAND_TRANSFORMS = auto_augment._RAND_INCREASING_TRANSFORMS.copy()\n_RAND_TRANSFORMS.remove('SharpnessIncreasing')  # remove, interferes with *blur ops\n_RAND_TRANSFORMS.extend([\n    'GaussianBlur',\n    # 'MotionBlur',\n    # 'GaussianNoise',\n    'PoissonNoise'\n])\nauto_augment.LEVEL_TO_ARG.update({\n    'GaussianBlur': partial(_level_to_arg, max=4),\n    'MotionBlur': partial(_level_to_arg, max=20),\n    'GaussianNoise': partial(_level_to_arg, max=0.1 * 255),\n    'PoissonNoise': partial(_level_to_arg, max=40)\n})\nauto_augment.NAME_TO_OP.update({\n    'GaussianBlur': gaussian_blur,\n    'MotionBlur': motion_blur,\n    'GaussianNoise': gaussian_noise,\n    'PoissonNoise': poisson_noise\n})\n\n\ndef rand_augment_transform(magnitude=5, num_layers=3):\n    # These are tuned for magnitude=5, which means that effective magnitudes are half of these values.\n    hparams = {\n        'rotate_deg': 30,\n        'shear_x_pct': 0.9,\n        'shear_y_pct': 0.2,\n        'translate_x_pct': 0.10,\n        'translate_y_pct': 0.30\n    }\n    ra_ops = auto_augment.rand_augment_ops(magnitude, hparams, transforms=_RAND_TRANSFORMS)\n    # Supply weights to disable replacement in random selection (i.e. avoid applying the same op twice)\n    choice_weights = [1. / len(ra_ops) for _ in range(len(ra_ops))]\n    return auto_augment.RandAugment(ra_ops, num_layers, choice_weights)\n"
  },
  {
    "path": "src/parseq/strhub/data/dataset.py",
    "content": "# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport glob\nimport io\nimport logging\nimport unicodedata\nfrom pathlib import Path, PurePath\nfrom typing import Callable, Optional, Union\n\nimport lmdb\nfrom PIL import Image\nfrom torch.utils.data import Dataset, ConcatDataset\n\nfrom strhub.data.utils import CharsetAdapter\n\nlog = logging.getLogger(__name__)\n\n\ndef build_tree_dataset(root: Union[PurePath, str], *args, **kwargs):\n    try:\n        kwargs.pop('root')  # prevent 'root' from being passed via kwargs\n    except KeyError:\n        pass\n    root = Path(root).absolute()\n    log.info(f'dataset root:\\t{root}')\n    datasets = []\n    for mdb in glob.glob(str(root / '**/data.mdb'), recursive=True):\n        mdb = Path(mdb)\n        ds_name = str(mdb.parent.relative_to(root))\n        ds_root = str(mdb.parent.absolute())\n        dataset = LmdbDataset(ds_root, *args, **kwargs)\n        log.info(f'\\tlmdb:\\t{ds_name}\\tnum samples: {len(dataset)}')\n        datasets.append(dataset)\n    return ConcatDataset(datasets)\n\n\nclass LmdbDataset(Dataset):\n    \"\"\"Dataset interface to an LMDB database.\n\n    It supports both labelled and unlabelled datasets. For unlabelled datasets, the image index itself is returned\n    as the label. Unicode characters are normalized by default. Case-sensitivity is inferred from the charset.\n    Labels are transformed according to the charset.\n    \"\"\"\n\n    def __init__(self, root: str, charset: str, max_label_len: int, min_image_dim: int = 0,\n                 remove_whitespace: bool = True, normalize_unicode: bool = True,\n                 unlabelled: bool = False, transform: Optional[Callable] = None):\n        self._env = None\n        self.root = root\n        self.unlabelled = unlabelled\n        self.transform = transform\n        self.labels = []\n        self.filtered_index_list = []\n        self.num_samples = self._preprocess_labels(charset, remove_whitespace, normalize_unicode,\n                                                   max_label_len, min_image_dim)\n\n    def __del__(self):\n        if self._env is not None:\n            self._env.close()\n            self._env = None\n\n    def _create_env(self):\n        return lmdb.open(self.root, max_readers=1, readonly=True, create=False,\n                         readahead=False, meminit=False, lock=False)\n\n    @property\n    def env(self):\n        if self._env is None:\n            self._env = self._create_env()\n        return self._env\n\n    def _preprocess_labels(self, charset, remove_whitespace, normalize_unicode, max_label_len, min_image_dim):\n        charset_adapter = CharsetAdapter(charset)\n        with self._create_env() as env, env.begin() as txn:\n            num_samples = int(txn.get('num-samples'.encode()))\n            if self.unlabelled:\n                return num_samples\n            for index in range(num_samples):\n                index += 1  # lmdb starts with 1\n                label_key = f'label-{index:09d}'.encode()\n                label = txn.get(label_key).decode()\n                # Normally, whitespace is removed from the labels.\n                if remove_whitespace:\n                    label = ''.join(label.split())\n                # Normalize unicode composites (if any) and convert to compatible ASCII characters\n                if normalize_unicode:\n                    label = unicodedata.normalize('NFKD', label).encode('ascii', 'ignore').decode()\n                # Filter by length before removing unsupported characters. The original label might be too long.\n                if len(label) > max_label_len:\n                    continue\n                label = charset_adapter(label)\n                # We filter out samples which don't contain any supported characters\n                if not label:\n                    continue\n                # Filter images that are too small.\n                if min_image_dim > 0:\n                    img_key = f'image-{index:09d}'.encode()\n                    buf = io.BytesIO(txn.get(img_key))\n                    w, h = Image.open(buf).size\n                    if w < self.min_image_dim or h < self.min_image_dim:\n                        continue\n                self.labels.append(label)\n                self.filtered_index_list.append(index)\n        return len(self.labels)\n\n    def __len__(self):\n        return self.num_samples\n\n    def __getitem__(self, index):\n        if self.unlabelled:\n            label = index\n        else:\n            label = self.labels[index]\n            index = self.filtered_index_list[index]\n\n        img_key = f'image-{index:09d}'.encode()\n        with self.env.begin() as txn:\n            imgbuf = txn.get(img_key)\n        buf = io.BytesIO(imgbuf)\n        img = Image.open(buf).convert('RGB')\n\n        if self.transform is not None:\n            img = self.transform(img)\n\n        return img, label\n"
  },
  {
    "path": "src/parseq/strhub/data/module.py",
    "content": "# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pathlib import PurePath\nfrom typing import Optional, Callable, Sequence, Tuple\n\nimport pytorch_lightning as pl\nfrom torch.utils.data import DataLoader\nfrom torchvision import transforms as T\n\nfrom .dataset import build_tree_dataset, LmdbDataset\n\n\nclass SceneTextDataModule(pl.LightningDataModule):\n    TEST_BENCHMARK_SUB = ('IIIT5k', 'SVT', 'IC13_857', 'IC15_1811', 'SVTP', 'CUTE80')\n    TEST_BENCHMARK = ('IIIT5k', 'SVT', 'IC13_1015', 'IC15_2077', 'SVTP', 'CUTE80')\n    TEST_NEW = ('ArT', 'COCOv1.4', 'Uber')\n    TEST_ALL = tuple(set(TEST_BENCHMARK_SUB + TEST_BENCHMARK + TEST_NEW))\n\n    def __init__(self, root_dir: str, train_dir: str, img_size: Sequence[int], max_label_length: int,\n                 charset_train: str, charset_test: str, batch_size: int, num_workers: int, augment: bool,\n                 remove_whitespace: bool = True, normalize_unicode: bool = True,\n                 min_image_dim: int = 0, rotation: int = 0, collate_fn: Optional[Callable] = None):\n        super().__init__()\n        self.root_dir = root_dir\n        self.train_dir = train_dir\n        self.img_size = tuple(img_size)\n        self.max_label_length = max_label_length\n        self.charset_train = charset_train\n        self.charset_test = charset_test\n        self.batch_size = batch_size\n        self.num_workers = num_workers\n        self.augment = augment\n        self.remove_whitespace = remove_whitespace\n        self.normalize_unicode = normalize_unicode\n        self.min_image_dim = min_image_dim\n        self.rotation = rotation\n        self.collate_fn = collate_fn\n        self._train_dataset = None\n        self._val_dataset = None\n\n    @staticmethod\n    def get_transform(img_size: Tuple[int], augment: bool = False, rotation: int = 0):\n        transforms = []\n        if augment:\n            from .augment import rand_augment_transform\n            transforms.append(rand_augment_transform())\n        if rotation:\n            transforms.append(lambda img: img.rotate(rotation, expand=True))\n        transforms.extend([\n            T.Resize(img_size, T.InterpolationMode.BICUBIC),\n            T.ToTensor(),\n            T.Normalize(0.5, 0.5)\n        ])\n        return T.Compose(transforms)\n\n    @property\n    def train_dataset(self):\n        if self._train_dataset is None:\n            transform = self.get_transform(self.img_size, self.augment)\n            root = PurePath(self.root_dir, 'train', self.train_dir)\n            self._train_dataset = build_tree_dataset(root, self.charset_train, self.max_label_length,\n                                                     self.min_image_dim, self.remove_whitespace, self.normalize_unicode,\n                                                     transform=transform)\n        return self._train_dataset\n\n    @property\n    def val_dataset(self):\n        if self._val_dataset is None:\n            transform = self.get_transform(self.img_size)\n            root = PurePath(self.root_dir, 'val')\n            self._val_dataset = build_tree_dataset(root, self.charset_test, self.max_label_length,\n                                                   self.min_image_dim, self.remove_whitespace, self.normalize_unicode,\n                                                   transform=transform)\n        return self._val_dataset\n\n    def train_dataloader(self):\n        return DataLoader(self.train_dataset, batch_size=self.batch_size, shuffle=True,\n                          num_workers=self.num_workers, persistent_workers=self.num_workers > 0,\n                          pin_memory=True, collate_fn=self.collate_fn)\n\n    def val_dataloader(self):\n        return DataLoader(self.val_dataset, batch_size=self.batch_size,\n                          num_workers=self.num_workers, persistent_workers=self.num_workers > 0,\n                          pin_memory=True, collate_fn=self.collate_fn)\n\n    def test_dataloaders(self, subset):\n        transform = self.get_transform(self.img_size, rotation=self.rotation)\n        root = PurePath(self.root_dir, 'test')\n        datasets = {s: LmdbDataset(str(root / s), self.charset_test, self.max_label_length,\n                                   self.min_image_dim, self.remove_whitespace, self.normalize_unicode,\n                                   transform=transform) for s in subset}\n        return {k: DataLoader(v, batch_size=self.batch_size, num_workers=self.num_workers,\n                              pin_memory=True, collate_fn=self.collate_fn)\n                for k, v in datasets.items()}\n"
  },
  {
    "path": "src/parseq/strhub/data/utils.py",
    "content": "# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\nfrom abc import ABC, abstractmethod\nfrom itertools import groupby\nfrom typing import List, Optional, Tuple\n\nimport torch\nfrom torch import Tensor\nfrom torch.nn.utils.rnn import pad_sequence\n\n\nclass CharsetAdapter:\n    \"\"\"Transforms labels according to the target charset.\"\"\"\n\n    def __init__(self, target_charset) -> None:\n        super().__init__()\n        self.lowercase_only = target_charset == target_charset.lower()\n        self.uppercase_only = target_charset == target_charset.upper()\n        self.unsupported = f'[^{re.escape(target_charset)}]'\n\n    def __call__(self, label):\n        if self.lowercase_only:\n            label = label.lower()\n        elif self.uppercase_only:\n            label = label.upper()\n        # Remove unsupported characters\n        label = re.sub(self.unsupported, '', label)\n        return label\n\n\nclass BaseTokenizer(ABC):\n\n    def __init__(self, charset: str, specials_first: tuple = (), specials_last: tuple = ()) -> None:\n        self._itos = specials_first + tuple(charset) + specials_last\n        self._stoi = {s: i for i, s in enumerate(self._itos)}\n\n    def __len__(self):\n        return len(self._itos)\n\n    def _tok2ids(self, tokens: str) -> List[int]:\n        return [self._stoi[s] for s in tokens]\n\n    def _ids2tok(self, token_ids: List[int], join: bool = True) -> str:\n        tokens = [self._itos[i] for i in token_ids]\n        return ''.join(tokens) if join else tokens\n\n    @abstractmethod\n    def encode(self, labels: List[str], device: Optional[torch.device] = None) -> Tensor:\n        \"\"\"Encode a batch of labels to a representation suitable for the model.\n\n        Args:\n            labels: List of labels. Each can be of arbitrary length.\n            device: Create tensor on this device.\n\n        Returns:\n            Batched tensor representation padded to the max label length. Shape: N, L\n        \"\"\"\n        raise NotImplementedError\n\n    @abstractmethod\n    def _filter(self, probs: Tensor, ids: Tensor) -> Tuple[Tensor, List[int]]:\n        \"\"\"Internal method which performs the necessary filtering prior to decoding.\"\"\"\n        raise NotImplementedError\n\n    def decode(self, token_dists: Tensor, raw: bool = False) -> Tuple[List[str], List[Tensor]]:\n        \"\"\"Decode a batch of token distributions.\n\n        Args:\n            token_dists: softmax probabilities over the token distribution. Shape: N, L, C\n            raw: return unprocessed labels (will return list of list of strings)\n\n        Returns:\n            list of string labels (arbitrary length) and\n            their corresponding sequence probabilities as a list of Tensors\n        \"\"\"\n        batch_tokens = []\n        batch_probs = []\n        for dist in token_dists:\n            probs, ids = dist.max(-1)  # greedy selection\n            if not raw:\n                probs, ids = self._filter(probs, ids)\n            tokens = self._ids2tok(ids, not raw)\n            batch_tokens.append(tokens)\n            batch_probs.append(probs)\n        return batch_tokens, batch_probs\n\n\nclass Tokenizer(BaseTokenizer):\n    BOS = '[B]'\n    EOS = '[E]'\n    PAD = '[P]'\n\n    def __init__(self, charset: str) -> None:\n        specials_first = (self.EOS,)\n        specials_last = (self.BOS, self.PAD)\n        super().__init__(charset, specials_first, specials_last)\n        self.eos_id, self.bos_id, self.pad_id = [self._stoi[s] for s in specials_first + specials_last]\n\n    def encode(self, labels: List[str], device: Optional[torch.device] = None) -> Tensor:\n        batch = [torch.as_tensor([self.bos_id] + self._tok2ids(y) + [self.eos_id], dtype=torch.long, device=device)\n                 for y in labels]\n        \n        return pad_sequence(batch, batch_first=True, padding_value=self.pad_id)\n\n    def _filter(self, probs: Tensor, ids: Tensor) -> Tuple[Tensor, List[int]]:\n        ids = ids.tolist()\n        try:\n            eos_idx = ids.index(self.eos_id)\n        except ValueError:\n            eos_idx = len(ids)  # Nothing to truncate.\n        # Truncate after EOS\n        ids = ids[:eos_idx]\n        probs = probs[:eos_idx + 1]  # but include prob. for EOS (if it exists)\n        return probs, ids\n\n\nclass CTCTokenizer(BaseTokenizer):\n    BLANK = '[B]'\n\n    def __init__(self, charset: str) -> None:\n        # BLANK uses index == 0 by default\n        super().__init__(charset, specials_first=(self.BLANK,))\n        self.blank_id = self._stoi[self.BLANK]\n\n    def encode(self, labels: List[str], device: Optional[torch.device] = None) -> Tensor:\n        # We use a padded representation since we don't want to use CUDNN's CTC implementation\n        batch = [torch.as_tensor(self._tok2ids(y), dtype=torch.long, device=device) for y in labels]\n        return pad_sequence(batch, batch_first=True, padding_value=self.blank_id)\n\n    def _filter(self, probs: Tensor, ids: Tensor) -> Tuple[Tensor, List[int]]:\n        # Best path decoding:\n        ids = list(zip(*groupby(ids.tolist())))[0]  # Remove duplicate tokens\n        ids = [x for x in ids if x != self.blank_id]  # Remove BLANKs\n        # `probs` is just pass-through since all positions are considered part of the path\n        return probs, ids\n"
  },
  {
    "path": "src/parseq/strhub/models/__init__.py",
    "content": ""
  },
  {
    "path": "src/parseq/strhub/models/abinet/LICENSE",
    "content": "ABINet for non-commercial purposes\n\nCopyright (c) 2021, USTC\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "src/parseq/strhub/models/abinet/__init__.py",
    "content": "r\"\"\"\nFang, Shancheng, Hongtao, Xie, Yuxin, Wang, Zhendong, Mao, and Yongdong, Zhang.\n\"Read Like Humans: Autonomous, Bidirectional and Iterative Language Modeling for Scene Text Recognition.\" .\nIn Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR) (pp. 7098-7107).2021.\n\nhttps://arxiv.org/abs/2103.06495\n\nAll source files, except `system.py`, are based on the implementation listed below,\nand hence are released under the license of the original.\n\nSource: https://github.com/FangShancheng/ABINet\nLicense: 2-clause BSD License (see included LICENSE file)\n\"\"\"\n"
  },
  {
    "path": "src/parseq/strhub/models/abinet/attention.py",
    "content": "import torch\nimport torch.nn as nn\n\nfrom .transformer import PositionalEncoding\n\n\nclass Attention(nn.Module):\n    def __init__(self, in_channels=512, max_length=25, n_feature=256):\n        super().__init__()\n        self.max_length = max_length\n\n        self.f0_embedding = nn.Embedding(max_length, in_channels)\n        self.w0 = nn.Linear(max_length, n_feature)\n        self.wv = nn.Linear(in_channels, in_channels)\n        self.we = nn.Linear(in_channels, max_length)\n\n        self.active = nn.Tanh()\n        self.softmax = nn.Softmax(dim=2)\n\n    def forward(self, enc_output):\n        enc_output = enc_output.permute(0, 2, 3, 1).flatten(1, 2)\n        reading_order = torch.arange(self.max_length, dtype=torch.long, device=enc_output.device)\n        reading_order = reading_order.unsqueeze(0).expand(enc_output.size(0), -1)  # (S,) -> (B, S)\n        reading_order_embed = self.f0_embedding(reading_order)  # b,25,512\n\n        t = self.w0(reading_order_embed.permute(0, 2, 1))  # b,512,256\n        t = self.active(t.permute(0, 2, 1) + self.wv(enc_output))  # b,256,512\n\n        attn = self.we(t)  # b,256,25\n        attn = self.softmax(attn.permute(0, 2, 1))  # b,25,256\n        g_output = torch.bmm(attn, enc_output)  # b,25,512\n        return g_output, attn.view(*attn.shape[:2], 8, 32)\n\n\ndef encoder_layer(in_c, out_c, k=3, s=2, p=1):\n    return nn.Sequential(nn.Conv2d(in_c, out_c, k, s, p),\n                         nn.BatchNorm2d(out_c),\n                         nn.ReLU(True))\n\n\ndef decoder_layer(in_c, out_c, k=3, s=1, p=1, mode='nearest', scale_factor=None, size=None):\n    align_corners = None if mode == 'nearest' else True\n    return nn.Sequential(nn.Upsample(size=size, scale_factor=scale_factor,\n                                     mode=mode, align_corners=align_corners),\n                         nn.Conv2d(in_c, out_c, k, s, p),\n                         nn.BatchNorm2d(out_c),\n                         nn.ReLU(True))\n\n\nclass PositionAttention(nn.Module):\n    def __init__(self, max_length, in_channels=512, num_channels=64,\n                 h=8, w=32, mode='nearest', **kwargs):\n        super().__init__()\n        self.max_length = max_length\n        self.k_encoder = nn.Sequential(\n            encoder_layer(in_channels, num_channels, s=(1, 2)),\n            encoder_layer(num_channels, num_channels, s=(2, 2)),\n            encoder_layer(num_channels, num_channels, s=(2, 2)),\n            encoder_layer(num_channels, num_channels, s=(2, 2))\n        )\n        self.k_decoder = nn.Sequential(\n            decoder_layer(num_channels, num_channels, scale_factor=2, mode=mode),\n            decoder_layer(num_channels, num_channels, scale_factor=2, mode=mode),\n            decoder_layer(num_channels, num_channels, scale_factor=2, mode=mode),\n            decoder_layer(num_channels, in_channels, size=(h, w), mode=mode)\n        )\n\n        self.pos_encoder = PositionalEncoding(in_channels, dropout=0., max_len=max_length)\n        self.project = nn.Linear(in_channels, in_channels)\n\n    def forward(self, x):\n        N, E, H, W = x.size()\n        k, v = x, x  # (N, E, H, W)\n\n        # calculate key vector\n        features = []\n        for i in range(0, len(self.k_encoder)):\n            k = self.k_encoder[i](k)\n            features.append(k)\n        for i in range(0, len(self.k_decoder) - 1):\n            k = self.k_decoder[i](k)\n            k = k + features[len(self.k_decoder) - 2 - i]\n        k = self.k_decoder[-1](k)\n\n        # calculate query vector\n        # TODO q=f(q,k)\n        zeros = x.new_zeros((self.max_length, N, E))  # (T, N, E)\n        q = self.pos_encoder(zeros)  # (T, N, E)\n        q = q.permute(1, 0, 2)  # (N, T, E)\n        q = self.project(q)  # (N, T, E)\n\n        # calculate attention\n        attn_scores = torch.bmm(q, k.flatten(2, 3))  # (N, T, (H*W))\n        attn_scores = attn_scores / (E ** 0.5)\n        attn_scores = torch.softmax(attn_scores, dim=-1)\n\n        v = v.permute(0, 2, 3, 1).view(N, -1, E)  # (N, (H*W), E)\n        attn_vecs = torch.bmm(attn_scores, v)  # (N, T, E)\n\n        return attn_vecs, attn_scores.view(N, -1, H, W)\n"
  },
  {
    "path": "src/parseq/strhub/models/abinet/backbone.py",
    "content": "import torch.nn as nn\nfrom torch.nn import TransformerEncoderLayer, TransformerEncoder\n\nfrom .resnet import resnet45\nfrom .transformer import PositionalEncoding\n\n\nclass ResTranformer(nn.Module):\n    def __init__(self, d_model=512, nhead=8, d_inner=2048, dropout=0.1, activation='relu', backbone_ln=2):\n        super().__init__()\n        self.resnet = resnet45()\n        self.pos_encoder = PositionalEncoding(d_model, max_len=8 * 32)\n        encoder_layer = TransformerEncoderLayer(d_model=d_model, nhead=nhead,\n                                                dim_feedforward=d_inner, dropout=dropout, activation=activation)\n        self.transformer = TransformerEncoder(encoder_layer, backbone_ln)\n\n    def forward(self, images):\n        feature = self.resnet(images)\n        n, c, h, w = feature.shape\n        feature = feature.view(n, c, -1).permute(2, 0, 1)\n        feature = self.pos_encoder(feature)\n        feature = self.transformer(feature)\n        feature = feature.permute(1, 2, 0).view(n, c, h, w)\n        return feature\n"
  },
  {
    "path": "src/parseq/strhub/models/abinet/model.py",
    "content": "import torch\nimport torch.nn as nn\n\n\nclass Model(nn.Module):\n\n    def __init__(self, dataset_max_length: int, null_label: int):\n        super().__init__()\n        self.max_length = dataset_max_length + 1  # additional stop token\n        self.null_label = null_label\n\n    def _get_length(self, logit, dim=-1):\n        \"\"\" Greed decoder to obtain length from logit\"\"\"\n        out = (logit.argmax(dim=-1) == self.null_label)\n        abn = out.any(dim)\n        out = ((out.cumsum(dim) == 1) & out).max(dim)[1]\n        out = out + 1  # additional end token\n        out = torch.where(abn, out, out.new_tensor(logit.shape[1], device=out.device))\n        return out\n\n    @staticmethod\n    def _get_padding_mask(length, max_length):\n        length = length.unsqueeze(-1)\n        grid = torch.arange(0, max_length, device=length.device).unsqueeze(0)\n        return grid >= length\n\n    @staticmethod\n    def _get_location_mask(sz, device=None):\n        mask = torch.eye(sz, device=device)\n        mask = mask.float().masked_fill(mask == 1, float('-inf'))\n        return mask\n"
  },
  {
    "path": "src/parseq/strhub/models/abinet/model_abinet_iter.py",
    "content": "import torch\nfrom torch import nn\n\nfrom .model_alignment import BaseAlignment\nfrom .model_language import BCNLanguage\nfrom .model_vision import BaseVision\n\n\nclass ABINetIterModel(nn.Module):\n    def __init__(self, dataset_max_length, null_label, num_classes, iter_size=1,\n                 d_model=512, nhead=8, d_inner=2048, dropout=0.1, activation='relu',\n                 v_loss_weight=1., v_attention='position', v_attention_mode='nearest',\n                 v_backbone='transformer', v_num_layers=2,\n                 l_loss_weight=1., l_num_layers=4, l_detach=True, l_use_self_attn=False,\n                 a_loss_weight=1.):\n        super().__init__()\n        self.iter_size = iter_size\n        self.vision = BaseVision(dataset_max_length, null_label, num_classes, v_attention, v_attention_mode,\n                                 v_loss_weight, d_model, nhead, d_inner, dropout, activation, v_backbone, v_num_layers)\n        self.language = BCNLanguage(dataset_max_length, null_label, num_classes, d_model, nhead, d_inner, dropout,\n                                    activation, l_num_layers, l_detach, l_use_self_attn, l_loss_weight)\n        self.alignment = BaseAlignment(dataset_max_length, null_label, num_classes, d_model, a_loss_weight)\n\n    def forward(self, images):\n        v_res = self.vision(images)\n        a_res = v_res\n        all_l_res, all_a_res = [], []\n        for _ in range(self.iter_size):\n            tokens = torch.softmax(a_res['logits'], dim=-1)\n            lengths = a_res['pt_lengths']\n            lengths.clamp_(2, self.language.max_length)  # TODO:move to langauge model\n            l_res = self.language(tokens, lengths)\n            all_l_res.append(l_res)\n            a_res = self.alignment(l_res['feature'], v_res['feature'])\n            all_a_res.append(a_res)\n        if self.training:\n            return all_a_res, all_l_res, v_res\n        else:\n            return a_res, all_l_res[-1], v_res\n"
  },
  {
    "path": "src/parseq/strhub/models/abinet/model_alignment.py",
    "content": "import torch\nimport torch.nn as nn\n\nfrom .model import Model\n\n\nclass BaseAlignment(Model):\n    def __init__(self, dataset_max_length, null_label, num_classes, d_model=512, loss_weight=1.0):\n        super().__init__(dataset_max_length, null_label)\n        self.loss_weight = loss_weight\n        self.w_att = nn.Linear(2 * d_model, d_model)\n        self.cls = nn.Linear(d_model, num_classes)\n\n    def forward(self, l_feature, v_feature):\n        \"\"\"\n        Args:\n            l_feature: (N, T, E) where T is length, N is batch size and d is dim of model\n            v_feature: (N, T, E) shape the same as l_feature \n        \"\"\"\n        f = torch.cat((l_feature, v_feature), dim=2)\n        f_att = torch.sigmoid(self.w_att(f))\n        output = f_att * v_feature + (1 - f_att) * l_feature\n\n        logits = self.cls(output)  # (N, T, C)\n        pt_lengths = self._get_length(logits)\n\n        return {'logits': logits, 'pt_lengths': pt_lengths, 'loss_weight': self.loss_weight,\n                'name': 'alignment'}\n"
  },
  {
    "path": "src/parseq/strhub/models/abinet/model_language.py",
    "content": "import torch.nn as nn\nfrom torch.nn import TransformerDecoder\n\nfrom .model import Model\nfrom .transformer import PositionalEncoding, TransformerDecoderLayer\n\n\nclass BCNLanguage(Model):\n    def __init__(self, dataset_max_length, null_label, num_classes, d_model=512, nhead=8, d_inner=2048, dropout=0.1,\n                 activation='relu', num_layers=4, detach=True, use_self_attn=False, loss_weight=1.0,\n                 global_debug=False):\n        super().__init__(dataset_max_length, null_label)\n        self.detach = detach\n        self.loss_weight = loss_weight\n        self.proj = nn.Linear(num_classes, d_model, False)\n        self.token_encoder = PositionalEncoding(d_model, max_len=self.max_length)\n        self.pos_encoder = PositionalEncoding(d_model, dropout=0, max_len=self.max_length)\n        decoder_layer = TransformerDecoderLayer(d_model, nhead, d_inner, dropout,\n                                                activation, self_attn=use_self_attn, debug=global_debug)\n        self.model = TransformerDecoder(decoder_layer, num_layers)\n        self.cls = nn.Linear(d_model, num_classes)\n\n    def forward(self, tokens, lengths):\n        \"\"\"\n        Args:\n            tokens: (N, T, C) where T is length, N is batch size and C is classes number\n            lengths: (N,)\n        \"\"\"\n        if self.detach:\n            tokens = tokens.detach()\n        embed = self.proj(tokens)  # (N, T, E)\n        embed = embed.permute(1, 0, 2)  # (T, N, E)\n        embed = self.token_encoder(embed)  # (T, N, E)\n        padding_mask = self._get_padding_mask(lengths, self.max_length)\n\n        zeros = embed.new_zeros(*embed.shape)\n        qeury = self.pos_encoder(zeros)\n        location_mask = self._get_location_mask(self.max_length, tokens.device)\n        output = self.model(qeury, embed,\n                            tgt_key_padding_mask=padding_mask,\n                            memory_mask=location_mask,\n                            memory_key_padding_mask=padding_mask)  # (T, N, E)\n        output = output.permute(1, 0, 2)  # (N, T, E)\n\n        logits = self.cls(output)  # (N, T, C)\n        pt_lengths = self._get_length(logits)\n\n        res = {'feature': output, 'logits': logits, 'pt_lengths': pt_lengths,\n               'loss_weight': self.loss_weight, 'name': 'language'}\n        return res\n"
  },
  {
    "path": "src/parseq/strhub/models/abinet/model_vision.py",
    "content": "from torch import nn\n\nfrom .attention import PositionAttention, Attention\nfrom .backbone import ResTranformer\nfrom .model import Model\nfrom .resnet import resnet45\n\n\nclass BaseVision(Model):\n    def __init__(self, dataset_max_length, null_label, num_classes,\n                 attention='position', attention_mode='nearest', loss_weight=1.0,\n                 d_model=512, nhead=8, d_inner=2048, dropout=0.1, activation='relu',\n                 backbone='transformer', backbone_ln=2):\n        super().__init__(dataset_max_length, null_label)\n        self.loss_weight = loss_weight\n        self.out_channels = d_model\n\n        if backbone == 'transformer':\n            self.backbone = ResTranformer(d_model, nhead, d_inner, dropout, activation, backbone_ln)\n        else:\n            self.backbone = resnet45()\n\n        if attention == 'position':\n            self.attention = PositionAttention(\n                max_length=self.max_length,\n                mode=attention_mode\n            )\n        elif attention == 'attention':\n            self.attention = Attention(\n                max_length=self.max_length,\n                n_feature=8 * 32,\n            )\n        else:\n            raise ValueError(f'invalid attention: {attention}')\n\n        self.cls = nn.Linear(self.out_channels, num_classes)\n\n    def forward(self, images):\n        features = self.backbone(images)  # (N, E, H, W)\n        attn_vecs, attn_scores = self.attention(features)  # (N, T, E), (N, T, H, W)\n        logits = self.cls(attn_vecs)  # (N, T, C)\n        pt_lengths = self._get_length(logits)\n\n        return {'feature': attn_vecs, 'logits': logits, 'pt_lengths': pt_lengths,\n                'attn_scores': attn_scores, 'loss_weight': self.loss_weight, 'name': 'vision'}\n"
  },
  {
    "path": "src/parseq/strhub/models/abinet/resnet.py",
    "content": "import math\nfrom typing import Optional, Callable\n\nimport torch.nn as nn\nfrom torchvision.models import resnet\n\n\nclass BasicBlock(resnet.BasicBlock):\n\n    def __init__(self, inplanes: int, planes: int, stride: int = 1, downsample: Optional[nn.Module] = None,\n                 groups: int = 1, base_width: int = 64, dilation: int = 1,\n                 norm_layer: Optional[Callable[..., nn.Module]] = None) -> None:\n        super().__init__(inplanes, planes, stride, downsample, groups, base_width, dilation, norm_layer)\n        self.conv1 = resnet.conv1x1(inplanes, planes)\n        self.conv2 = resnet.conv3x3(planes, planes, stride)\n\n\nclass ResNet(nn.Module):\n\n    def __init__(self, block, layers):\n        super().__init__()\n        self.inplanes = 32\n        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1,\n                               bias=False)\n        self.bn1 = nn.BatchNorm2d(32)\n        self.relu = nn.ReLU(inplace=True)\n\n        self.layer1 = self._make_layer(block, 32, layers[0], stride=2)\n        self.layer2 = self._make_layer(block, 64, layers[1], stride=1)\n        self.layer3 = self._make_layer(block, 128, layers[2], stride=2)\n        self.layer4 = self._make_layer(block, 256, layers[3], stride=1)\n        self.layer5 = self._make_layer(block, 512, layers[4], stride=1)\n\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels\n                m.weight.data.normal_(0, math.sqrt(2. / n))\n            elif isinstance(m, nn.BatchNorm2d):\n                m.weight.data.fill_(1)\n                m.bias.data.zero_()\n\n    def _make_layer(self, block, planes, blocks, stride=1):\n        downsample = None\n        if stride != 1 or self.inplanes != planes * block.expansion:\n            downsample = nn.Sequential(\n                nn.Conv2d(self.inplanes, planes * block.expansion,\n                          kernel_size=1, stride=stride, bias=False),\n                nn.BatchNorm2d(planes * block.expansion),\n            )\n\n        layers = []\n        layers.append(block(self.inplanes, planes, stride, downsample))\n        self.inplanes = planes * block.expansion\n        for i in range(1, blocks):\n            layers.append(block(self.inplanes, planes))\n\n        return nn.Sequential(*layers)\n\n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.relu(x)\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.layer4(x)\n        x = self.layer5(x)\n        return x\n\n\ndef resnet45():\n    return ResNet(BasicBlock, [3, 4, 6, 6, 3])\n"
  },
  {
    "path": "src/parseq/strhub/models/abinet/system.py",
    "content": "# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport logging\nimport math\nfrom typing import Any, Tuple, List, Optional\n\nimport torch\nimport torch.nn.functional as F\nfrom torch import Tensor, nn\nfrom torch.optim import AdamW\nfrom torch.optim.lr_scheduler import OneCycleLR\n\nfrom pytorch_lightning.utilities.types import STEP_OUTPUT\nfrom timm.optim.optim_factory import param_groups_weight_decay\n\nfrom strhub.models.base import CrossEntropySystem\nfrom strhub.models.utils import init_weights\nfrom .model_abinet_iter import ABINetIterModel as Model\n\nlog = logging.getLogger(__name__)\n\n\nclass ABINet(CrossEntropySystem):\n\n    def __init__(self, charset_train: str, charset_test: str, max_label_length: int,\n                 batch_size: int, lr: float, warmup_pct: float, weight_decay: float,\n                 iter_size: int, d_model: int, nhead: int, d_inner: int, dropout: float, activation: str,\n                 v_loss_weight: float, v_attention: str, v_attention_mode: str, v_backbone: str, v_num_layers: int,\n                 l_loss_weight: float, l_num_layers: int, l_detach: bool, l_use_self_attn: bool,\n                 l_lr: float, a_loss_weight: float, lm_only: bool = False, **kwargs) -> None:\n        super().__init__(charset_train, charset_test, batch_size, lr, warmup_pct, weight_decay)\n        self.scheduler = None\n        self.save_hyperparameters()\n        self.max_label_length = max_label_length\n        self.num_classes = len(self.tokenizer) - 2  # We don't predict <bos> nor <pad>\n        self.model = Model(max_label_length, self.eos_id, self.num_classes, iter_size, d_model, nhead, d_inner,\n                           dropout, activation, v_loss_weight, v_attention, v_attention_mode, v_backbone, v_num_layers,\n                           l_loss_weight, l_num_layers, l_detach, l_use_self_attn, a_loss_weight)\n        self.model.apply(init_weights)\n        # FIXME: doesn't support resumption from checkpoint yet\n        self._reset_alignment = True\n        self._reset_optimizers = True\n        self.l_lr = l_lr\n        self.lm_only = lm_only\n        # Train LM only. Freeze other submodels.\n        if lm_only:\n            self.l_lr = lr  # for tuning\n            self.model.vision.requires_grad_(False)\n            self.model.alignment.requires_grad_(False)\n\n    @property\n    def _pretraining(self):\n        # In the original work, VM was pretrained for 8 epochs while full model was trained for an additional 10 epochs.\n        total_steps = self.trainer.estimated_stepping_batches * self.trainer.accumulate_grad_batches\n        return self.global_step < (8 / (8 + 10)) * total_steps\n\n    @torch.jit.ignore\n    def no_weight_decay(self):\n        return {'model.language.proj.weight'}\n\n    def _add_weight_decay(self, model: nn.Module, skip_list=()):\n        if self.weight_decay:\n            return param_groups_weight_decay(model, self.weight_decay, skip_list)\n        else:\n            return [{'params': model.parameters()}]\n\n    def configure_optimizers(self):\n        agb = self.trainer.accumulate_grad_batches\n        # Linear scaling so that the effective learning rate is constant regardless of the number of GPUs used with DDP.\n        lr_scale = agb * math.sqrt(self.trainer.num_devices) * self.batch_size / 256.\n        lr = lr_scale * self.lr\n        l_lr = lr_scale * self.l_lr\n        params = []\n        params.extend(self._add_weight_decay(self.model.vision))\n        params.extend(self._add_weight_decay(self.model.alignment))\n        # We use a different learning rate for the LM.\n        for p in self._add_weight_decay(self.model.language, ('proj.weight',)):\n            p['lr'] = l_lr\n            params.append(p)\n        max_lr = [p.get('lr', lr) for p in params]\n        optim = AdamW(params, lr)\n        self.scheduler = OneCycleLR(optim, max_lr, self.trainer.estimated_stepping_batches,\n                                    pct_start=self.warmup_pct, cycle_momentum=False)\n        return {'optimizer': optim, 'lr_scheduler': {'scheduler': self.scheduler, 'interval': 'step'}}\n\n    def forward(self, images: Tensor, max_length: Optional[int] = None) -> Tensor:\n        max_length = self.max_label_length if max_length is None else min(max_length, self.max_label_length)\n        logits = self.model.forward(images)[0]['logits']\n        return logits[:, :max_length + 1]  # truncate\n\n    def calc_loss(self, targets, *res_lists) -> Tensor:\n        total_loss = 0\n        for res_list in res_lists:\n            loss = 0\n            if isinstance(res_list, dict):\n                res_list = [res_list]\n            for res in res_list:\n                logits = res['logits'].flatten(end_dim=1)\n                loss += F.cross_entropy(logits, targets.flatten(), ignore_index=self.pad_id)\n            loss /= len(res_list)\n            self.log('loss_' + res_list[0]['name'], loss)\n            total_loss += res_list[0]['loss_weight'] * loss\n        return total_loss\n\n    def on_train_batch_start(self, batch: Any, batch_idx: int) -> None:\n        if not self._pretraining and self._reset_optimizers:\n            log.info('Pretraining ends. Updating base LRs.')\n            self._reset_optimizers = False\n            # Make base_lr the same for all groups\n            base_lr = self.scheduler.base_lrs[0]  # base_lr of group 0 - VM\n            self.scheduler.base_lrs = [base_lr] * len(self.scheduler.base_lrs)\n\n    def _prepare_inputs_and_targets(self, labels):\n        # Use dummy label to ensure sequence length is constant.\n        dummy = ['0' * self.max_label_length]\n        targets = self.tokenizer.encode(dummy + list(labels), self.device)[1:]\n        targets = targets[:, 1:]  # remove <bos>. Unused here.\n        # Inputs are padded with eos_id\n        inputs = torch.where(targets == self.pad_id, self.eos_id, targets)\n        inputs = F.one_hot(inputs, self.num_classes).float()\n        lengths = torch.as_tensor(list(map(len, labels)), device=self.device) + 1  # +1 for eos\n        return inputs, lengths, targets\n\n    def training_step(self, batch, batch_idx) -> STEP_OUTPUT:\n        images, labels = batch\n        inputs, lengths, targets = self._prepare_inputs_and_targets(labels)\n        if self.lm_only:\n            l_res = self.model.language(inputs, lengths)\n            loss = self.calc_loss(targets, l_res)\n        # Pretrain submodels independently first\n        elif self._pretraining:\n            # Vision\n            v_res = self.model.vision(images)\n            # Language\n            l_res = self.model.language(inputs, lengths)\n            # We also train the alignment model to 'satisfy' DDP requirements (all parameters should be used).\n            # We'll reset its parameters prior to joint training.\n            a_res = self.model.alignment(l_res['feature'].detach(), v_res['feature'].detach())\n            loss = self.calc_loss(targets, v_res, l_res, a_res)\n        else:\n            # Reset alignment model's parameters once prior to full model training.\n            if self._reset_alignment:\n                log.info('Pretraining ends. Resetting alignment model.')\n                self._reset_alignment = False\n                self.model.alignment.apply(init_weights)\n            all_a_res, all_l_res, v_res = self.model.forward(images)\n            loss = self.calc_loss(targets, v_res, all_l_res, all_a_res)\n        self.log('loss', loss)\n        return loss\n\n    def forward_logits_loss(self, images: Tensor, labels: List[str]) -> Tuple[Tensor, Tensor, int]:\n        if self.lm_only:\n            inputs, lengths, targets = self._prepare_inputs_and_targets(labels)\n            l_res = self.model.language(inputs, lengths)\n            loss = self.calc_loss(targets, l_res)\n            loss_numel = (targets != self.pad_id).sum()\n            return l_res['logits'], loss, loss_numel\n        else:\n            return super().forward_logits_loss(images, labels)\n"
  },
  {
    "path": "src/parseq/strhub/models/abinet/transformer.py",
    "content": "import math\n\nimport torch\nimport torch.nn.functional as F\nfrom torch import nn\nfrom torch.nn.modules.transformer import _get_activation_fn\n\n\nclass TransformerDecoderLayer(nn.Module):\n    r\"\"\"TransformerDecoderLayer is made up of self-attn, multi-head-attn and feedforward network.\n    This standard decoder layer is based on the paper \"Attention Is All You Need\".\n    Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N Gomez,\n    Lukasz Kaiser, and Illia Polosukhin. 2017. Attention is all you need. In Advances in\n    Neural Information Processing Systems, pages 6000-6010. Users may modify or implement\n    in a different way during application.\n\n    Args:\n        d_model: the number of expected features in the input (required).\n        nhead: the number of heads in the multiheadattention models (required).\n        dim_feedforward: the dimension of the feedforward network model (default=2048).\n        dropout: the dropout value (default=0.1).\n        activation: the activation function of intermediate layer, relu or gelu (default=relu).\n\n    Examples::\n        >>> decoder_layer = nn.TransformerDecoderLayer(d_model=512, nhead=8)\n        >>> memory = torch.rand(10, 32, 512)\n        >>> tgt = torch.rand(20, 32, 512)\n        >>> out = decoder_layer(tgt, memory)\n    \"\"\"\n\n    def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1,\n                 activation=\"relu\", self_attn=True, siamese=False, debug=False):\n        super().__init__()\n        self.has_self_attn, self.siamese = self_attn, siamese\n        self.debug = debug\n        if self.has_self_attn:\n            self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)\n            self.norm1 = nn.LayerNorm(d_model)\n            self.dropout1 = nn.Dropout(dropout)\n        self.multihead_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)\n        # Implementation of Feedforward model\n        self.linear1 = nn.Linear(d_model, dim_feedforward)\n        self.dropout = nn.Dropout(dropout)\n        self.linear2 = nn.Linear(dim_feedforward, d_model)\n\n        self.norm2 = nn.LayerNorm(d_model)\n        self.norm3 = nn.LayerNorm(d_model)\n        self.dropout2 = nn.Dropout(dropout)\n        self.dropout3 = nn.Dropout(dropout)\n        if self.siamese:\n            self.multihead_attn2 = nn.MultiheadAttention(d_model, nhead, dropout=dropout)\n\n        self.activation = _get_activation_fn(activation)\n\n    def __setstate__(self, state):\n        if 'activation' not in state:\n            state['activation'] = F.relu\n        super().__setstate__(state)\n\n    def forward(self, tgt, memory, tgt_mask=None, memory_mask=None,\n                tgt_key_padding_mask=None, memory_key_padding_mask=None,\n                memory2=None, memory_mask2=None, memory_key_padding_mask2=None):\n        # type: (Tensor, Tensor, Optional[Tensor], Optional[Tensor], Optional[Tensor], Optional[Tensor]) -> Tensor\n        r\"\"\"Pass the inputs (and mask) through the decoder layer.\n\n        Args:\n            tgt: the sequence to the decoder layer (required).\n            memory: the sequence from the last layer of the encoder (required).\n            tgt_mask: the mask for the tgt sequence (optional).\n            memory_mask: the mask for the memory sequence (optional).\n            tgt_key_padding_mask: the mask for the tgt keys per batch (optional).\n            memory_key_padding_mask: the mask for the memory keys per batch (optional).\n\n        Shape:\n            see the docs in Transformer class.\n        \"\"\"\n        if self.has_self_attn:\n            tgt2, attn = self.self_attn(tgt, tgt, tgt, attn_mask=tgt_mask,\n                                        key_padding_mask=tgt_key_padding_mask)\n            tgt = tgt + self.dropout1(tgt2)\n            tgt = self.norm1(tgt)\n            if self.debug: self.attn = attn\n        tgt2, attn2 = self.multihead_attn(tgt, memory, memory, attn_mask=memory_mask,\n                                          key_padding_mask=memory_key_padding_mask)\n        if self.debug: self.attn2 = attn2\n\n        if self.siamese:\n            tgt3, attn3 = self.multihead_attn2(tgt, memory2, memory2, attn_mask=memory_mask2,\n                                               key_padding_mask=memory_key_padding_mask2)\n            tgt = tgt + self.dropout2(tgt3)\n            if self.debug: self.attn3 = attn3\n\n        tgt = tgt + self.dropout2(tgt2)\n        tgt = self.norm2(tgt)\n        tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt))))\n        tgt = tgt + self.dropout3(tgt2)\n        tgt = self.norm3(tgt)\n\n        return tgt\n\n\nclass PositionalEncoding(nn.Module):\n    r\"\"\"Inject some information about the relative or absolute position of the tokens\n        in the sequence. The positional encodings have the same dimension as\n        the embeddings, so that the two can be summed. Here, we use sine and cosine\n        functions of different frequencies.\n    .. math::\n        \\text{PosEncoder}(pos, 2i) = sin(pos/10000^(2i/d_model))\n        \\text{PosEncoder}(pos, 2i+1) = cos(pos/10000^(2i/d_model))\n        \\text{where pos is the word position and i is the embed idx)\n    Args:\n        d_model: the embed dim (required).\n        dropout: the dropout value (default=0.1).\n        max_len: the max. length of the incoming sequence (default=5000).\n    Examples:\n        >>> pos_encoder = PositionalEncoding(d_model)\n    \"\"\"\n\n    def __init__(self, d_model, dropout=0.1, max_len=5000):\n        super().__init__()\n        self.dropout = nn.Dropout(p=dropout)\n\n        pe = torch.zeros(max_len, d_model)\n        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)\n        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))\n        pe[:, 0::2] = torch.sin(position * div_term)\n        pe[:, 1::2] = torch.cos(position * div_term)\n        pe = pe.unsqueeze(0).transpose(0, 1)\n        self.register_buffer('pe', pe)\n\n    def forward(self, x):\n        r\"\"\"Inputs of forward function\n        Args:\n            x: the sequence fed to the positional encoder model (required).\n        Shape:\n            x: [sequence length, batch size, embed dim]\n            output: [sequence length, batch size, embed dim]\n        Examples:\n            >>> output = pos_encoder(x)\n        \"\"\"\n\n        x = x + self.pe[:x.size(0), :]\n        return self.dropout(x)\n"
  },
  {
    "path": "src/parseq/strhub/models/base.py",
    "content": "# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport math\nfrom abc import ABC, abstractmethod\nfrom dataclasses import dataclass\nfrom typing import Optional, Tuple, List\n\nimport pytorch_lightning as pl\nimport torch\nimport torch.nn.functional as F\nfrom pytorch_lightning.utilities.types import STEP_OUTPUT\nfrom timm.optim import create_optimizer_v2\nfrom torch import Tensor\nfrom torch.optim import Optimizer\nfrom torch.optim.lr_scheduler import OneCycleLR\n\nfrom strhub.data.utils import CharsetAdapter, CTCTokenizer, Tokenizer, BaseTokenizer\n\n\n@dataclass\nclass BatchResult:\n    num_samples: int\n    correct: int\n    ned: float\n    confidence: float\n    label_length: int\n    loss: Tensor\n    loss_numel: int\n\n\nclass BaseSystem(pl.LightningModule, ABC):\n\n    def __init__(self, tokenizer: BaseTokenizer, charset_test: str,\n                 batch_size: int, lr: float, warmup_pct: float, weight_decay: float) -> None:\n        super().__init__()\n        self.tokenizer = tokenizer\n        self.charset_adapter = CharsetAdapter(charset_test)\n        self.batch_size = batch_size\n        self.lr = lr\n        self.warmup_pct = warmup_pct\n        self.weight_decay = weight_decay\n\n    @abstractmethod\n    def forward(self, images: Tensor, max_length: Optional[int] = None) -> Tensor:\n        \"\"\"Inference\n\n        Args:\n            images: Batch of images. Shape: N, Ch, H, W\n            max_length: Max sequence length of the output. If None, will use default.\n\n        Returns:\n            logits: N, L, C (L = sequence length, C = number of classes, typically len(charset_train) + num specials)\n        \"\"\"\n        raise NotImplementedError\n\n    @abstractmethod\n    def forward_logits_loss(self, images: Tensor, labels: List[str]) -> Tuple[Tensor, Tensor, int]:\n        \"\"\"Like forward(), but also computes the loss (calls forward() internally).\n\n        Args:\n            images: Batch of images. Shape: N, Ch, H, W\n            labels: Text labels of the images\n\n        Returns:\n            logits: N, L, C (L = sequence length, C = number of classes, typically len(charset_train) + num specials)\n            loss: mean loss for the batch\n            loss_numel: number of elements the loss was calculated from\n        \"\"\"\n        raise NotImplementedError\n\n    def configure_optimizers(self):\n        agb = self.trainer.accumulate_grad_batches\n        # Linear scaling so that the effective learning rate is constant regardless of the number of GPUs used with DDP.\n        lr_scale = agb * math.sqrt(self.trainer.num_devices) * self.batch_size / 256.\n        lr = lr_scale * self.lr\n        optim = create_optimizer_v2(self, 'adamw', lr, self.weight_decay)\n        sched = OneCycleLR(optim, lr, self.trainer.estimated_stepping_batches, pct_start=self.warmup_pct,\n                           cycle_momentum=False)\n        return {'optimizer': optim, 'lr_scheduler': {'scheduler': sched, 'interval': 'step'}}\n\n    def optimizer_zero_grad(self, epoch: int, batch_idx: int, optimizer: Optimizer, optimizer_idx: int):\n        optimizer.zero_grad(set_to_none=True)\n\n    def _eval_step(self, batch, validation: bool) -> Optional[STEP_OUTPUT]:\n        images, labels = batch\n\n        correct = 0\n        total = 0\n        ned = 0\n        confidence = 0\n        label_length = 0\n        if validation:\n            logits, loss, loss_numel = self.forward_logits_loss(images, labels)\n        else:\n            # At test-time, we shouldn't specify a max_label_length because the test-time charset used\n            # might be different from the train-time charset. max_label_length in eval_logits_loss() is computed\n            # based on the transformed label, which could be wrong if the actual gt label contains characters existing\n            # in the train-time charset but not in the test-time charset. For example, \"aishahaleyes.blogspot.com\"\n            # is exactly 25 characters, but if processed by CharsetAdapter for the 36-char set, it becomes 23 characters\n            # long only, which sets max_label_length = 23. This will cause the model prediction to be truncated.\n            logits = self.forward(images)\n            loss = loss_numel = None  # Only used for validation; not needed at test-time.\n\n        probs = logits.softmax(-1)\n        preds, probs = self.tokenizer.decode(probs)\n        for pred, prob, gt in zip(preds, probs, labels):\n            confidence += prob.prod().item()\n            pred = self.charset_adapter(pred)\n            # Follow ICDAR 2019 definition of N.E.D.\n            ned += edit_distance(pred, gt) / max(len(pred), len(gt))\n            if pred == gt:\n                correct += 1\n            total += 1\n            label_length += len(pred)\n        return dict(output=BatchResult(total, correct, ned, confidence, label_length, loss, loss_numel))\n\n    @staticmethod\n    def _aggregate_results(outputs) -> Tuple[float, float, float]:\n        if not outputs:\n            return 0., 0., 0.\n        total_loss = 0\n        total_loss_numel = 0\n        total_n_correct = 0\n        total_norm_ED = 0\n        total_size = 0\n        for result in outputs:\n            result = result['output']\n            total_loss += result.loss_numel * result.loss\n            total_loss_numel += result.loss_numel\n            total_n_correct += result.correct\n            total_norm_ED += result.ned\n            total_size += result.num_samples\n        acc = total_n_correct / total_size\n        ned = (1 - total_norm_ED / total_size)\n        loss = total_loss / total_loss_numel\n        return acc, ned, loss\n\n    def validation_step(self, batch, batch_idx) -> Optional[STEP_OUTPUT]:\n        return self._eval_step(batch, True)\n\n    def validation_epoch_end(self, outputs) -> None:\n        acc, ned, loss = self._aggregate_results(outputs)\n        self.log('val_accuracy', 100 * acc, sync_dist=True)\n        self.log('val_NED', 100 * ned, sync_dist=True)\n        self.log('val_loss', loss, sync_dist=True)\n        self.log('hp_metric', acc, sync_dist=True)\n\n    def test_step(self, batch, batch_idx) -> Optional[STEP_OUTPUT]:\n        return self._eval_step(batch, False)\n\n\nclass CrossEntropySystem(BaseSystem):\n\n    def __init__(self, charset_train: str, charset_test: str,\n                 batch_size: int, lr: float, warmup_pct: float, weight_decay: float) -> None:\n        tokenizer = Tokenizer(charset_train)\n        super().__init__(tokenizer, charset_test, batch_size, lr, warmup_pct, weight_decay)\n        self.bos_id = tokenizer.bos_id\n        self.eos_id = tokenizer.eos_id\n        self.pad_id = tokenizer.pad_id\n\n    def forward_logits_loss(self, images: Tensor, labels: List[str]) -> Tuple[Tensor, Tensor, int]:\n        targets = self.tokenizer.encode(labels, self.device)\n        targets = targets[:, 1:]  # Discard <bos>\n        max_len = targets.shape[1] - 1  # exclude <eos> from count\n        logits = self.forward(images, max_len)\n        loss = F.cross_entropy(logits.flatten(end_dim=1), targets.flatten(), ignore_index=self.pad_id)\n        loss_numel = (targets != self.pad_id).sum()\n        return logits, loss, loss_numel\n\n\nclass CTCSystem(BaseSystem):\n\n    def __init__(self, charset_train: str, charset_test: str,\n                 batch_size: int, lr: float, warmup_pct: float, weight_decay: float) -> None:\n        tokenizer = CTCTokenizer(charset_train)\n        super().__init__(tokenizer, charset_test, batch_size, lr, warmup_pct, weight_decay)\n        self.blank_id = tokenizer.blank_id\n\n    def forward_logits_loss(self, images: Tensor, labels: List[str]) -> Tuple[Tensor, Tensor, int]:\n        targets = self.tokenizer.encode(labels, self.device)\n        logits = self.forward(images)\n        log_probs = logits.log_softmax(-1).transpose(0, 1)  # swap batch and seq. dims\n        T, N, _ = log_probs.shape\n        input_lengths = torch.full(size=(N,), fill_value=T, dtype=torch.long, device=self.device)\n        target_lengths = torch.as_tensor(list(map(len, labels)), dtype=torch.long, device=self.device)\n        loss = F.ctc_loss(log_probs, targets, input_lengths, target_lengths, blank=self.blank_id, zero_infinity=True)\n        return logits, loss, N\n"
  },
  {
    "path": "src/parseq/strhub/models/crnn/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2017 Jieru Mei <meijieru@gmail.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "src/parseq/strhub/models/crnn/__init__.py",
    "content": "r\"\"\"\nShi, Baoguang, Xiang Bai, and Cong Yao.\n\"An end-to-end trainable neural network for image-based sequence recognition and its application to scene text recognition.\"\nIEEE transactions on pattern analysis and machine intelligence 39, no. 11 (2016): 2298-2304.\n\nhttps://arxiv.org/abs/1507.05717\n\nAll source files, except `system.py`, are based on the implementation listed below,\nand hence are released under the license of the original.\n\nSource: https://github.com/meijieru/crnn.pytorch\nLicense: MIT License (see included LICENSE file)\n\"\"\"\n"
  },
  {
    "path": "src/parseq/strhub/models/crnn/model.py",
    "content": "import torch.nn as nn\n\nfrom strhub.models.modules import BidirectionalLSTM\n\n\nclass CRNN(nn.Module):\n\n    def __init__(self, img_h, nc, nclass, nh, leaky_relu=False):\n        super().__init__()\n        assert img_h % 16 == 0, 'img_h has to be a multiple of 16'\n\n        ks = [3, 3, 3, 3, 3, 3, 2]\n        ps = [1, 1, 1, 1, 1, 1, 0]\n        ss = [1, 1, 1, 1, 1, 1, 1]\n        nm = [64, 128, 256, 256, 512, 512, 512]\n\n        cnn = nn.Sequential()\n\n        def convRelu(i, batchNormalization=False):\n            nIn = nc if i == 0 else nm[i - 1]\n            nOut = nm[i]\n            cnn.add_module('conv{0}'.format(i),\n                           nn.Conv2d(nIn, nOut, ks[i], ss[i], ps[i], bias=not batchNormalization))\n            if batchNormalization:\n                cnn.add_module('batchnorm{0}'.format(i), nn.BatchNorm2d(nOut))\n            if leaky_relu:\n                cnn.add_module('relu{0}'.format(i),\n                               nn.LeakyReLU(0.2, inplace=True))\n            else:\n                cnn.add_module('relu{0}'.format(i), nn.ReLU(True))\n\n        convRelu(0)\n        cnn.add_module('pooling{0}'.format(0), nn.MaxPool2d(2, 2))  # 64x16x64\n        convRelu(1)\n        cnn.add_module('pooling{0}'.format(1), nn.MaxPool2d(2, 2))  # 128x8x32\n        convRelu(2, True)\n        convRelu(3)\n        cnn.add_module('pooling{0}'.format(2),\n                       nn.MaxPool2d((2, 2), (2, 1), (0, 1)))  # 256x4x16\n        convRelu(4, True)\n        convRelu(5)\n        cnn.add_module('pooling{0}'.format(3),\n                       nn.MaxPool2d((2, 2), (2, 1), (0, 1)))  # 512x2x16\n        convRelu(6, True)  # 512x1x16\n\n        self.cnn = cnn\n        self.rnn = nn.Sequential(\n            BidirectionalLSTM(512, nh, nh),\n            BidirectionalLSTM(nh, nh, nclass))\n\n    def forward(self, input):\n        # conv features\n        conv = self.cnn(input)\n        b, c, h, w = conv.size()\n        assert h == 1, 'the height of conv must be 1'\n        conv = conv.squeeze(2)\n        conv = conv.transpose(1, 2)  # [b, w, c]\n\n        # rnn features\n        output = self.rnn(conv)\n\n        return output\n"
  },
  {
    "path": "src/parseq/strhub/models/crnn/system.py",
    "content": "# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Sequence, Optional\n\nfrom pytorch_lightning.utilities.types import STEP_OUTPUT\nfrom torch import Tensor\n\nfrom strhub.models.base import CTCSystem\nfrom strhub.models.utils import init_weights\nfrom .model import CRNN as Model\n\n\nclass CRNN(CTCSystem):\n\n    def __init__(self, charset_train: str, charset_test: str, max_label_length: int,\n                 batch_size: int, lr: float, warmup_pct: float, weight_decay: float,\n                 img_size: Sequence[int], hidden_size: int, leaky_relu: bool, **kwargs) -> None:\n        super().__init__(charset_train, charset_test, batch_size, lr, warmup_pct, weight_decay)\n        self.save_hyperparameters()\n        self.model = Model(img_size[0], 3, len(self.tokenizer), hidden_size, leaky_relu)\n        self.model.apply(init_weights)\n\n    def forward(self, images: Tensor, max_length: Optional[int] = None) -> Tensor:\n        return self.model.forward(images)\n\n    def training_step(self, batch, batch_idx) -> STEP_OUTPUT:\n        images, labels = batch\n        loss = self.forward_logits_loss(images, labels)[1]\n        self.log('loss', loss)\n        return loss\n"
  },
  {
    "path": "src/parseq/strhub/models/modules.py",
    "content": "r\"\"\"Shared modules used by CRNN and TRBA\"\"\"\nfrom torch import nn\n\n\nclass BidirectionalLSTM(nn.Module):\n    \"\"\"Ref: https://github.com/clovaai/deep-text-recognition-benchmark/blob/master/modules/sequence_modeling.py\"\"\"\n\n    def __init__(self, input_size, hidden_size, output_size):\n        super().__init__()\n        self.rnn = nn.LSTM(input_size, hidden_size, bidirectional=True, batch_first=True)\n        self.linear = nn.Linear(hidden_size * 2, output_size)\n\n    def forward(self, input):\n        \"\"\"\n        input : visual feature [batch_size x T x input_size], T = num_steps.\n        output : contextual feature [batch_size x T x output_size]\n        \"\"\"\n        recurrent, _ = self.rnn(input)  # batch_size x T x input_size -> batch_size x T x (2*hidden_size)\n        output = self.linear(recurrent)  # batch_size x T x output_size\n        return output\n"
  },
  {
    "path": "src/parseq/strhub/models/parseq/__init__.py",
    "content": ""
  },
  {
    "path": "src/parseq/strhub/models/parseq/modules.py",
    "content": "# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport math\nfrom typing import Optional\n\nimport torch\nfrom torch import nn as nn, Tensor\nfrom torch.nn import functional as F\nfrom torch.nn.modules import transformer\n\nfrom timm.models.vision_transformer import VisionTransformer, PatchEmbed\n\n\nclass DecoderLayer(nn.Module):\n    \"\"\"A Transformer decoder layer supporting two-stream attention (XLNet)\n       This implements a pre-LN decoder, as opposed to the post-LN default in PyTorch.\"\"\"\n\n    def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1, activation='gelu',\n                 layer_norm_eps=1e-5):\n        super().__init__()\n        self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout, batch_first=True)\n        self.cross_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout, batch_first=True)\n        # Implementation of Feedforward model\n        self.linear1 = nn.Linear(d_model, dim_feedforward)\n        self.dropout = nn.Dropout(dropout)\n        self.linear2 = nn.Linear(dim_feedforward, d_model)\n\n        self.norm1 = nn.LayerNorm(d_model, eps=layer_norm_eps)\n        self.norm2 = nn.LayerNorm(d_model, eps=layer_norm_eps)\n        self.norm_q = nn.LayerNorm(d_model, eps=layer_norm_eps)\n        self.norm_c = nn.LayerNorm(d_model, eps=layer_norm_eps)\n        self.dropout1 = nn.Dropout(dropout)\n        self.dropout2 = nn.Dropout(dropout)\n        self.dropout3 = nn.Dropout(dropout)\n\n        self.activation = transformer._get_activation_fn(activation)\n\n    def __setstate__(self, state):\n        if 'activation' not in state:\n            state['activation'] = F.gelu\n        super().__setstate__(state)\n\n    def forward_stream(self, tgt: Tensor, tgt_norm: Tensor, tgt_kv: Tensor, memory: Tensor, tgt_mask: Optional[Tensor],\n                       tgt_key_padding_mask: Optional[Tensor]):\n        \"\"\"Forward pass for a single stream (i.e. content or query)\n        tgt_norm is just a LayerNorm'd tgt. Added as a separate parameter for efficiency.\n        Both tgt_kv and memory are expected to be LayerNorm'd too.\n        memory is LayerNorm'd by ViT.\n        \"\"\"\n        tgt2, sa_weights = self.self_attn(tgt_norm, tgt_kv, tgt_kv, attn_mask=tgt_mask,\n                                          key_padding_mask=tgt_key_padding_mask)\n        tgt = tgt + self.dropout1(tgt2)\n\n        tgt2, ca_weights = self.cross_attn(self.norm1(tgt), memory, memory)\n        tgt = tgt + self.dropout2(tgt2)\n\n        tgt2 = self.linear2(self.dropout(self.activation(self.linear1(self.norm2(tgt)))))\n        tgt = tgt + self.dropout3(tgt2)\n        return tgt, sa_weights, ca_weights\n\n    def forward(self, query, content, memory, query_mask: Optional[Tensor] = None, content_mask: Optional[Tensor] = None,\n                content_key_padding_mask: Optional[Tensor] = None, update_content: bool = True):\n        query_norm = self.norm_q(query)\n        content_norm = self.norm_c(content)\n        query = self.forward_stream(query, query_norm, content_norm, memory, query_mask, content_key_padding_mask)[0]\n        if update_content:\n            content = self.forward_stream(content, content_norm, content_norm, memory, content_mask,\n                                          content_key_padding_mask)[0]\n        return query, content\n\n\nclass Decoder(nn.Module):\n    __constants__ = ['norm']\n\n    def __init__(self, decoder_layer, num_layers, norm):\n        super().__init__()\n        self.layers = transformer._get_clones(decoder_layer, num_layers)\n        self.num_layers = num_layers\n        self.norm = norm\n\n    def forward(self, query, content, memory, query_mask: Optional[Tensor] = None, content_mask: Optional[Tensor] = None,\n                content_key_padding_mask: Optional[Tensor] = None):\n        for i, mod in enumerate(self.layers):\n            last = i == len(self.layers) - 1\n            query, content = mod(query, content, memory, query_mask, content_mask, content_key_padding_mask,\n                                 update_content=not last)\n        query = self.norm(query)\n        return query\n\n\nclass Encoder(VisionTransformer):\n\n    def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768, depth=12, num_heads=12, mlp_ratio=4.,\n                 qkv_bias=True, drop_rate=0., attn_drop_rate=0., drop_path_rate=0., embed_layer=PatchEmbed):\n        super().__init__(img_size, patch_size, in_chans, embed_dim=embed_dim, depth=depth, num_heads=num_heads,\n                         mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop_rate=drop_rate, attn_drop_rate=attn_drop_rate,\n                         drop_path_rate=drop_path_rate, embed_layer=embed_layer,\n                         num_classes=0, global_pool='', class_token=False)  # these disable the classifier head\n\n    def forward(self, x):\n        # Return all tokens\n        return self.forward_features(x)\n\n\nclass TokenEmbedding(nn.Module):\n\n    def __init__(self, charset_size: int, embed_dim: int):\n        super().__init__()\n        self.embedding = nn.Embedding(charset_size, embed_dim)\n        self.embed_dim = embed_dim\n\n    def forward(self, tokens: torch.Tensor):\n        return math.sqrt(self.embed_dim) * self.embedding(tokens)\n"
  },
  {
    "path": "src/parseq/strhub/models/parseq/system.py",
    "content": "# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport math\nfrom functools import partial\nfrom itertools import permutations\nfrom typing import Sequence, Any, Optional\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch import Tensor\n\nfrom pytorch_lightning.utilities.types import STEP_OUTPUT\nfrom timm.models.helpers import named_apply\n\nfrom strhub.models.base import CrossEntropySystem\nfrom strhub.models.utils import init_weights\nfrom .modules import DecoderLayer, Decoder, Encoder, TokenEmbedding\n\n\nclass PARSeq(CrossEntropySystem):\n\n    def __init__(self, charset_train: str, charset_test: str, max_label_length: int,\n                 batch_size: int, lr: float, warmup_pct: float, weight_decay: float,\n                 img_size: Sequence[int], patch_size: Sequence[int], embed_dim: int,\n                 enc_num_heads: int, enc_mlp_ratio: int, enc_depth: int,\n                 dec_num_heads: int, dec_mlp_ratio: int, dec_depth: int,\n                 perm_num: int, perm_forward: bool, perm_mirrored: bool,\n                 decode_ar: bool, refine_iters: int, dropout: float, **kwargs: Any) -> None:\n        super().__init__(charset_train, charset_test, batch_size, lr, warmup_pct, weight_decay)\n        self.save_hyperparameters()\n\n        self.max_label_length = max_label_length\n        self.decode_ar = decode_ar\n        self.refine_iters = refine_iters\n\n        self.encoder = Encoder(img_size, patch_size, embed_dim=embed_dim, depth=enc_depth, num_heads=enc_num_heads,\n                               mlp_ratio=enc_mlp_ratio)\n        decoder_layer = DecoderLayer(embed_dim, dec_num_heads, embed_dim * dec_mlp_ratio, dropout)\n        self.decoder = Decoder(decoder_layer, num_layers=dec_depth, norm=nn.LayerNorm(embed_dim))\n\n        # Perm/attn mask stuff\n        self.rng = np.random.default_rng()\n        self.max_gen_perms = perm_num // 2 if perm_mirrored else perm_num\n        self.perm_forward = perm_forward\n        self.perm_mirrored = perm_mirrored\n\n        # We don't predict <bos> nor <pad>\n        self.head = nn.Linear(embed_dim, len(self.tokenizer) - 2)\n        self.text_embed = TokenEmbedding(len(self.tokenizer), embed_dim)\n\n        # +1 for <eos>\n        self.pos_queries = nn.Parameter(torch.Tensor(1, max_label_length + 1, embed_dim))\n        self.dropout = nn.Dropout(p=dropout)\n        # Encoder has its own init.\n        named_apply(partial(init_weights, exclude=['encoder']), self)\n        nn.init.trunc_normal_(self.pos_queries, std=.02)\n\n    @torch.jit.ignore\n    def no_weight_decay(self):\n        param_names = {'text_embed.embedding.weight', 'pos_queries'}\n        enc_param_names = {'encoder.' + n for n in self.encoder.no_weight_decay()}\n        return param_names.union(enc_param_names)\n\n    def encode(self, img: torch.Tensor):\n        return self.encoder(img)\n\n    def decode(self, tgt: torch.Tensor, memory: torch.Tensor, tgt_mask: Optional[Tensor] = None,\n               tgt_padding_mask: Optional[Tensor] = None, tgt_query: Optional[Tensor] = None,\n               tgt_query_mask: Optional[Tensor] = None):\n        N, L = tgt.shape\n        # <bos> stands for the null context. We only supply position information for characters after <bos>.\n        null_ctx = self.text_embed(tgt[:, :1])\n        tgt_emb = self.pos_queries[:, :L - 1] + self.text_embed(tgt[:, 1:])\n        tgt_emb = self.dropout(torch.cat([null_ctx, tgt_emb], dim=1))\n        if tgt_query is None:\n            tgt_query = self.pos_queries[:, :L].expand(N, -1, -1)\n        tgt_query = self.dropout(tgt_query)\n        return self.decoder(tgt_query, tgt_emb, memory, tgt_query_mask, tgt_mask, tgt_padding_mask)\n\n    def forward(self, images: Tensor, max_length: Optional[int] = None) -> Tensor:\n        testing = max_length is None\n        max_length = self.max_label_length if max_length is None else min(max_length, self.max_label_length)\n        bs = images.shape[0]\n        # +1 for <eos> at end of sequence.\n        num_steps = max_length + 1\n        memory = self.encode(images)\n\n        # Query positions up to `num_steps`\n        pos_queries = self.pos_queries[:, :num_steps].expand(bs, -1, -1)\n\n        # Special case for the forward permutation. Faster than using `generate_attn_masks()`\n        tgt_mask = query_mask = torch.triu(torch.full((num_steps, num_steps), float('-inf'), device=self._device), 1)\n\n        if self.decode_ar:\n            tgt_in = torch.full((bs, num_steps), self.pad_id, dtype=torch.long, device=self._device)\n            tgt_in[:, 0] = self.bos_id\n\n            logits = []\n            for i in range(num_steps):\n                j = i + 1  # next token index\n                # Efficient decoding:\n                # Input the context up to the ith token. We use only one query (at position = i) at a time.\n                # This works because of the lookahead masking effect of the canonical (forward) AR context.\n                # Past tokens have no access to future tokens, hence are fixed once computed.\n                tgt_out = self.decode(tgt_in[:, :j], memory, tgt_mask[:j, :j], tgt_query=pos_queries[:, i:j],\n                                      tgt_query_mask=query_mask[i:j, :j])\n                # the next token probability is in the output's ith token position\n                p_i = self.head(tgt_out)\n                logits.append(p_i)\n                if j < num_steps:\n                    # greedy decode. add the next token index to the target input\n                    tgt_in[:, j] = p_i.squeeze().argmax(-1)\n                    # Efficient batch decoding: If all output words have at least one EOS token, end decoding.\n                    if testing and (tgt_in == self.eos_id).any(dim=-1).all():\n                        break\n\n            logits = torch.cat(logits, dim=1)\n        else:\n            # No prior context, so input is just <bos>. We query all positions.\n            tgt_in = torch.full((bs, 1), self.bos_id, dtype=torch.long, device=self._device)\n            tgt_out = self.decode(tgt_in, memory, tgt_query=pos_queries)\n            logits = self.head(tgt_out)\n\n        if self.refine_iters:\n            # For iterative refinement, we always use a 'cloze' mask.\n            # We can derive it from the AR forward mask by unmasking the token context to the right.\n            query_mask[torch.triu(torch.ones(num_steps, num_steps, dtype=torch.bool, device=self._device), 2)] = 0\n            bos = torch.full((bs, 1), self.bos_id, dtype=torch.long, device=self._device)\n            for i in range(self.refine_iters):\n                # Prior context is the previous output.\n                tgt_in = torch.cat([bos, logits[:, :-1].argmax(-1)], dim=1)\n                tgt_padding_mask = ((tgt_in == self.eos_id).int().cumsum(-1) > 0)  # mask tokens beyond the first EOS token.\n                tgt_out = self.decode(tgt_in, memory, tgt_mask, tgt_padding_mask,\n                                      tgt_query=pos_queries, tgt_query_mask=query_mask[:, :tgt_in.shape[1]])\n                logits = self.head(tgt_out)\n\n        return logits\n\n    def gen_tgt_perms(self, tgt):\n        \"\"\"Generate shared permutations for the whole batch.\n           This works because the same attention mask can be used for the shorter sequences\n           because of the padding mask.\n        \"\"\"\n        # We don't permute the position of BOS, we permute EOS separately\n        max_num_chars = tgt.shape[1] - 2\n        # Special handling for 1-character sequences\n        if max_num_chars == 1:\n            return torch.arange(3, device=self._device).unsqueeze(0)\n        perms = [torch.arange(max_num_chars, device=self._device)] if self.perm_forward else []\n        # Additional permutations if needed\n        max_perms = math.factorial(max_num_chars)\n        if self.perm_mirrored:\n            max_perms //= 2\n        num_gen_perms = min(self.max_gen_perms, max_perms)\n        # For 4-char sequences and shorter, we generate all permutations and sample from the pool to avoid collisions\n        # Note that this code path might NEVER get executed since the labels in a mini-batch typically exceed 4 chars.\n        if max_num_chars < 5:\n            # Pool of permutations to sample from. We only need the first half (if complementary option is selected)\n            # Special handling for max_num_chars == 4 which correctly divides the pool into the flipped halves\n            if max_num_chars == 4 and self.perm_mirrored:\n                selector = [0, 3, 4, 6, 9, 10, 12, 16, 17, 18, 19, 21]\n            else:\n                selector = list(range(max_perms))\n            perm_pool = torch.as_tensor(list(permutations(range(max_num_chars), max_num_chars)), device=self._device)[selector]\n            # If the forward permutation is always selected, no need to add it to the pool for sampling\n            if self.perm_forward:\n                perm_pool = perm_pool[1:]\n            perms = torch.stack(perms)\n            if len(perm_pool):\n                i = self.rng.choice(len(perm_pool), size=num_gen_perms - len(perms), replace=False)\n                perms = torch.cat([perms, perm_pool[i]])\n        else:\n            perms.extend([torch.randperm(max_num_chars, device=self._device) for _ in range(num_gen_perms - len(perms))])\n            perms = torch.stack(perms)\n        if self.perm_mirrored:\n            # Add complementary pairs\n            comp = perms.flip(-1)\n            # Stack in such a way that the pairs are next to each other.\n            perms = torch.stack([perms, comp]).transpose(0, 1).reshape(-1, max_num_chars)\n        # NOTE:\n        # The only meaningful way of permuting the EOS position is by moving it one character position at a time.\n        # However, since the number of permutations = T! and number of EOS positions = T + 1, the number of possible EOS\n        # positions will always be much less than the number of permutations (unless a low perm_num is set).\n        # Thus, it would be simpler to just train EOS using the full and null contexts rather than trying to evenly\n        # distribute it across the chosen number of permutations.\n        # Add position indices of BOS and EOS\n        bos_idx = perms.new_zeros((len(perms), 1))\n        eos_idx = perms.new_full((len(perms), 1), max_num_chars + 1)\n        perms = torch.cat([bos_idx, perms + 1, eos_idx], dim=1)\n        # Special handling for the reverse direction. This does two things:\n        # 1. Reverse context for the characters\n        # 2. Null context for [EOS] (required for learning to predict [EOS] in NAR mode)\n        if len(perms) > 1:\n            perms[1, 1:] = max_num_chars + 1 - torch.arange(max_num_chars + 1, device=self._device)\n        return perms\n\n    def generate_attn_masks(self, perm):\n        \"\"\"Generate attention masks given a sequence permutation (includes pos. for bos and eos tokens)\n        :param perm: the permutation sequence. i = 0 is always the BOS\n        :return: lookahead attention masks\n        \"\"\"\n        sz = perm.shape[0]\n        mask = torch.zeros((sz, sz), device=self._device)\n        for i in range(sz):\n            query_idx = perm[i]\n            masked_keys = perm[i + 1:]\n            mask[query_idx, masked_keys] = float('-inf')\n        content_mask = mask[:-1, :-1].clone()\n        mask[torch.eye(sz, dtype=torch.bool, device=self._device)] = float('-inf')  # mask \"self\"\n        query_mask = mask[1:, :-1]\n        return content_mask, query_mask\n\n    def training_step(self, batch, batch_idx) -> STEP_OUTPUT:\n        images, labels = batch\n        tgt = self.tokenizer.encode(labels, self._device)\n\n        # Encode the source sequence (i.e. the image codes)\n        memory = self.encode(images)\n\n        # Prepare the target sequences (input and output)\n        tgt_perms = self.gen_tgt_perms(tgt)\n        tgt_in = tgt[:, :-1]\n        tgt_out = tgt[:, 1:]\n        # The [EOS] token is not depended upon by any other token in any permutation ordering\n        tgt_padding_mask = (tgt_in == self.pad_id) | (tgt_in == self.eos_id)\n\n        loss = 0\n        loss_numel = 0\n        n = (tgt_out != self.pad_id).sum().item()\n        for i, perm in enumerate(tgt_perms):\n            tgt_mask, query_mask = self.generate_attn_masks(perm)\n            out = self.decode(tgt_in, memory, tgt_mask, tgt_padding_mask, tgt_query_mask=query_mask)\n            logits = self.head(out).flatten(end_dim=1)\n            loss += n * F.cross_entropy(logits, tgt_out.flatten(), ignore_index=self.pad_id)\n            loss_numel += n\n            # After the second iteration (i.e. done with canonical and reverse orderings),\n            # remove the [EOS] tokens for the succeeding perms\n            if i == 1:\n                tgt_out = torch.where(tgt_out == self.eos_id, self.pad_id, tgt_out)\n                n = (tgt_out != self.pad_id).sum().item()\n        loss /= loss_numel\n\n        self.log('loss', loss)\n        return loss\n"
  },
  {
    "path": "src/parseq/strhub/models/trba/__init__.py",
    "content": "r\"\"\"\nBaek, Jeonghun, Geewook Kim, Junyeop Lee, Sungrae Park, Dongyoon Han, Sangdoo Yun, Seong Joon Oh, and Hwalsuk Lee.\n\"What is wrong with scene text recognition model comparisons? dataset and model analysis.\"\nIn Proceedings of the IEEE/CVF International Conference on Computer Vision, pp. 4715-4723. 2019.\n\nhttps://arxiv.org/abs/1904.01906\n\nAll source files, except `system.py`, are based on the implementation listed below,\nand hence are released under the license of the original.\n\nSource: https://github.com/clovaai/deep-text-recognition-benchmark\nLicense: Apache License 2.0 (see LICENSE file in project root)\n\"\"\"\n"
  },
  {
    "path": "src/parseq/strhub/models/trba/feature_extraction.py",
    "content": "import torch.nn as nn\n\nfrom torchvision.models.resnet import BasicBlock\n\n\nclass ResNet_FeatureExtractor(nn.Module):\n    \"\"\" FeatureExtractor of FAN (http://openaccess.thecvf.com/content_ICCV_2017/papers/Cheng_Focusing_Attention_Towards_ICCV_2017_paper.pdf) \"\"\"\n\n    def __init__(self, input_channel, output_channel=512):\n        super().__init__()\n        self.ConvNet = ResNet(input_channel, output_channel, BasicBlock, [1, 2, 5, 3])\n\n    def forward(self, input):\n        return self.ConvNet(input)\n\n\nclass ResNet(nn.Module):\n\n    def __init__(self, input_channel, output_channel, block, layers):\n        super().__init__()\n\n        self.output_channel_block = [int(output_channel / 4), int(output_channel / 2), output_channel, output_channel]\n\n        self.inplanes = int(output_channel / 8)\n        self.conv0_1 = nn.Conv2d(input_channel, int(output_channel / 16),\n                                 kernel_size=3, stride=1, padding=1, bias=False)\n        self.bn0_1 = nn.BatchNorm2d(int(output_channel / 16))\n        self.conv0_2 = nn.Conv2d(int(output_channel / 16), self.inplanes,\n                                 kernel_size=3, stride=1, padding=1, bias=False)\n        self.bn0_2 = nn.BatchNorm2d(self.inplanes)\n        self.relu = nn.ReLU(inplace=True)\n\n        self.maxpool1 = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)\n        self.layer1 = self._make_layer(block, self.output_channel_block[0], layers[0])\n        self.conv1 = nn.Conv2d(self.output_channel_block[0], self.output_channel_block[\n            0], kernel_size=3, stride=1, padding=1, bias=False)\n        self.bn1 = nn.BatchNorm2d(self.output_channel_block[0])\n\n        self.maxpool2 = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)\n        self.layer2 = self._make_layer(block, self.output_channel_block[1], layers[1], stride=1)\n        self.conv2 = nn.Conv2d(self.output_channel_block[1], self.output_channel_block[\n            1], kernel_size=3, stride=1, padding=1, bias=False)\n        self.bn2 = nn.BatchNorm2d(self.output_channel_block[1])\n\n        self.maxpool3 = nn.MaxPool2d(kernel_size=2, stride=(2, 1), padding=(0, 1))\n        self.layer3 = self._make_layer(block, self.output_channel_block[2], layers[2], stride=1)\n        self.conv3 = nn.Conv2d(self.output_channel_block[2], self.output_channel_block[\n            2], kernel_size=3, stride=1, padding=1, bias=False)\n        self.bn3 = nn.BatchNorm2d(self.output_channel_block[2])\n\n        self.layer4 = self._make_layer(block, self.output_channel_block[3], layers[3], stride=1)\n        self.conv4_1 = nn.Conv2d(self.output_channel_block[3], self.output_channel_block[\n            3], kernel_size=2, stride=(2, 1), padding=(0, 1), bias=False)\n        self.bn4_1 = nn.BatchNorm2d(self.output_channel_block[3])\n        self.conv4_2 = nn.Conv2d(self.output_channel_block[3], self.output_channel_block[\n            3], kernel_size=2, stride=1, padding=0, bias=False)\n        self.bn4_2 = nn.BatchNorm2d(self.output_channel_block[3])\n\n    def _make_layer(self, block, planes, blocks, stride=1):\n        downsample = None\n        if stride != 1 or self.inplanes != planes * block.expansion:\n            downsample = nn.Sequential(\n                nn.Conv2d(self.inplanes, planes * block.expansion,\n                          kernel_size=1, stride=stride, bias=False),\n                nn.BatchNorm2d(planes * block.expansion),\n            )\n\n        layers = []\n        layers.append(block(self.inplanes, planes, stride, downsample))\n        self.inplanes = planes * block.expansion\n        for i in range(1, blocks):\n            layers.append(block(self.inplanes, planes))\n\n        return nn.Sequential(*layers)\n\n    def forward(self, x):\n        x = self.conv0_1(x)\n        x = self.bn0_1(x)\n        x = self.relu(x)\n        x = self.conv0_2(x)\n        x = self.bn0_2(x)\n        x = self.relu(x)\n\n        x = self.maxpool1(x)\n        x = self.layer1(x)\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.relu(x)\n\n        x = self.maxpool2(x)\n        x = self.layer2(x)\n        x = self.conv2(x)\n        x = self.bn2(x)\n        x = self.relu(x)\n\n        x = self.maxpool3(x)\n        x = self.layer3(x)\n        x = self.conv3(x)\n        x = self.bn3(x)\n        x = self.relu(x)\n\n        x = self.layer4(x)\n        x = self.conv4_1(x)\n        x = self.bn4_1(x)\n        x = self.relu(x)\n        x = self.conv4_2(x)\n        x = self.bn4_2(x)\n        x = self.relu(x)\n\n        return x\n"
  },
  {
    "path": "src/parseq/strhub/models/trba/model.py",
    "content": "import torch.nn as nn\n\nfrom strhub.models.modules import BidirectionalLSTM\nfrom .feature_extraction import ResNet_FeatureExtractor\nfrom .prediction import Attention\nfrom .transformation import TPS_SpatialTransformerNetwork\n\n\nclass TRBA(nn.Module):\n\n    def __init__(self, img_h, img_w, num_class, num_fiducial=20, input_channel=3, output_channel=512, hidden_size=256,\n                 use_ctc=False):\n        super().__init__()\n        \"\"\" Transformation \"\"\"\n        self.Transformation = TPS_SpatialTransformerNetwork(\n            F=num_fiducial, I_size=(img_h, img_w), I_r_size=(img_h, img_w),\n            I_channel_num=input_channel)\n\n        \"\"\" FeatureExtraction \"\"\"\n        self.FeatureExtraction = ResNet_FeatureExtractor(input_channel, output_channel)\n        self.FeatureExtraction_output = output_channel\n        self.AdaptiveAvgPool = nn.AdaptiveAvgPool2d((None, 1))  # Transform final (imgH/16-1) -> 1\n\n        \"\"\" Sequence modeling\"\"\"\n        self.SequenceModeling = nn.Sequential(\n            BidirectionalLSTM(self.FeatureExtraction_output, hidden_size, hidden_size),\n            BidirectionalLSTM(hidden_size, hidden_size, hidden_size))\n        self.SequenceModeling_output = hidden_size\n\n        \"\"\" Prediction \"\"\"\n        if use_ctc:\n            self.Prediction = nn.Linear(self.SequenceModeling_output, num_class)\n        else:\n            self.Prediction = Attention(self.SequenceModeling_output, hidden_size, num_class)\n\n    def forward(self, image, max_label_length, text=None):\n        \"\"\" Transformation stage \"\"\"\n        image = self.Transformation(image)\n\n        \"\"\" Feature extraction stage \"\"\"\n        visual_feature = self.FeatureExtraction(image)\n        visual_feature = visual_feature.permute(0, 3, 1, 2)  # [b, c, h, w] -> [b, w, c, h]\n        visual_feature = self.AdaptiveAvgPool(visual_feature)  # [b, w, c, h] -> [b, w, c, 1]\n        visual_feature = visual_feature.squeeze(3)  # [b, w, c, 1] -> [b, w, c]\n\n        \"\"\" Sequence modeling stage \"\"\"\n        contextual_feature = self.SequenceModeling(visual_feature)  # [b, num_steps, hidden_size]\n\n        \"\"\" Prediction stage \"\"\"\n        if isinstance(self.Prediction, Attention):\n            prediction = self.Prediction(contextual_feature.contiguous(), text, max_label_length)\n        else:\n            prediction = self.Prediction(contextual_feature.contiguous())  # CTC\n\n        return prediction  # [b, num_steps, num_class]\n"
  },
  {
    "path": "src/parseq/strhub/models/trba/prediction.py",
    "content": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\n\nclass Attention(nn.Module):\n\n    def __init__(self, input_size, hidden_size, num_class, num_char_embeddings=256):\n        super().__init__()\n        self.attention_cell = AttentionCell(input_size, hidden_size, num_char_embeddings)\n        self.hidden_size = hidden_size\n        self.num_class = num_class\n        self.generator = nn.Linear(hidden_size, num_class)\n        self.char_embeddings = nn.Embedding(num_class, num_char_embeddings)\n\n    def forward(self, batch_H, text, max_label_length=25):\n        \"\"\"\n        input:\n            batch_H : contextual_feature H = hidden state of encoder. [batch_size x num_steps x num_class]\n            text : the text-index of each image. [batch_size x (max_length+1)]. +1 for [SOS] token. text[:, 0] = [SOS].\n        output: probability distribution at each step [batch_size x num_steps x num_class]\n        \"\"\"\n        batch_size = batch_H.size(0)\n        num_steps = max_label_length + 1  # +1 for [EOS] at end of sentence.\n\n        output_hiddens = batch_H.new_zeros((batch_size, num_steps, self.hidden_size), dtype=torch.float)\n        hidden = (batch_H.new_zeros((batch_size, self.hidden_size), dtype=torch.float),\n                  batch_H.new_zeros((batch_size, self.hidden_size), dtype=torch.float))\n\n        if self.training:\n            for i in range(num_steps):\n                char_embeddings = self.char_embeddings(text[:, i])\n                # hidden : decoder's hidden s_{t-1}, batch_H : encoder's hidden H, char_embeddings : f(y_{t-1})\n                hidden, alpha = self.attention_cell(hidden, batch_H, char_embeddings)\n                output_hiddens[:, i, :] = hidden[0]  # LSTM hidden index (0: hidden, 1: Cell)\n            probs = self.generator(output_hiddens)\n\n        else:\n            targets = text[0].expand(batch_size)  # should be fill with [SOS] token\n            probs = batch_H.new_zeros((batch_size, num_steps, self.num_class), dtype=torch.float)\n\n            for i in range(num_steps):\n                char_embeddings = self.char_embeddings(targets)\n                hidden, alpha = self.attention_cell(hidden, batch_H, char_embeddings)\n                probs_step = self.generator(hidden[0])\n                probs[:, i, :] = probs_step\n                _, next_input = probs_step.max(1)\n                targets = next_input\n\n        return probs  # batch_size x num_steps x num_class\n\n\nclass AttentionCell(nn.Module):\n\n    def __init__(self, input_size, hidden_size, num_embeddings):\n        super().__init__()\n        self.i2h = nn.Linear(input_size, hidden_size, bias=False)\n        self.h2h = nn.Linear(hidden_size, hidden_size)  # either i2i or h2h should have bias\n        self.score = nn.Linear(hidden_size, 1, bias=False)\n        self.rnn = nn.LSTMCell(input_size + num_embeddings, hidden_size)\n        self.hidden_size = hidden_size\n\n    def forward(self, prev_hidden, batch_H, char_embeddings):\n        # [batch_size x num_encoder_step x num_channel] -> [batch_size x num_encoder_step x hidden_size]\n        batch_H_proj = self.i2h(batch_H)\n        prev_hidden_proj = self.h2h(prev_hidden[0]).unsqueeze(1)\n        e = self.score(torch.tanh(batch_H_proj + prev_hidden_proj))  # batch_size x num_encoder_step * 1\n\n        alpha = F.softmax(e, dim=1)\n        context = torch.bmm(alpha.permute(0, 2, 1), batch_H).squeeze(1)  # batch_size x num_channel\n        concat_context = torch.cat([context, char_embeddings], 1)  # batch_size x (num_channel + num_embedding)\n        cur_hidden = self.rnn(concat_context, prev_hidden)\n        return cur_hidden, alpha\n"
  },
  {
    "path": "src/parseq/strhub/models/trba/system.py",
    "content": "# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom functools import partial\nfrom typing import Sequence, Any, Optional\n\nimport torch\nimport torch.nn.functional as F\nfrom pytorch_lightning.utilities.types import STEP_OUTPUT\nfrom timm.models.helpers import named_apply\nfrom torch import Tensor\n\nfrom strhub.models.base import CrossEntropySystem, CTCSystem\nfrom strhub.models.utils import init_weights\nfrom .model import TRBA as Model\n\n\nclass TRBA(CrossEntropySystem):\n\n    def __init__(self, charset_train: str, charset_test: str, max_label_length: int,\n                 batch_size: int, lr: float, warmup_pct: float, weight_decay: float,\n                 img_size: Sequence[int], num_fiducial: int, output_channel: int, hidden_size: int,\n                 **kwargs: Any) -> None:\n        super().__init__(charset_train, charset_test, batch_size, lr, warmup_pct, weight_decay)\n        self.save_hyperparameters()\n        self.max_label_length = max_label_length\n        img_h, img_w = img_size\n        self.model = Model(img_h, img_w, len(self.tokenizer), num_fiducial,\n                           output_channel=output_channel, hidden_size=hidden_size, use_ctc=False)\n        named_apply(partial(init_weights, exclude=['Transformation.LocalizationNetwork.localization_fc2']), self.model)\n\n    @torch.jit.ignore\n    def no_weight_decay(self):\n        return {'model.Prediction.char_embeddings.weight'}\n\n    def forward(self, images: Tensor, max_length: Optional[int] = None) -> Tensor:\n        max_length = self.max_label_length if max_length is None else min(max_length, self.max_label_length)\n        text = images.new_full([1], self.bos_id, dtype=torch.long)\n        return self.model.forward(images, max_length, text)\n\n    def training_step(self, batch, batch_idx) -> STEP_OUTPUT:\n        images, labels = batch\n        encoded = self.tokenizer.encode(labels, self.device)\n        inputs = encoded[:, :-1]  # remove <eos>\n        targets = encoded[:, 1:]  # remove <bos>\n        max_length = encoded.shape[1] - 2  # exclude <bos> and <eos> from count\n        logits = self.model.forward(images, max_length, inputs)\n        loss = F.cross_entropy(logits.flatten(end_dim=1), targets.flatten(), ignore_index=self.pad_id)\n        self.log('loss', loss)\n        return loss\n\n\nclass TRBC(CTCSystem):\n\n    def __init__(self, charset_train: str, charset_test: str, max_label_length: int,\n                 batch_size: int, lr: float, warmup_pct: float, weight_decay: float,\n                 img_size: Sequence[int], num_fiducial: int, output_channel: int, hidden_size: int,\n                 **kwargs: Any) -> None:\n        super().__init__(charset_train, charset_test, batch_size, lr, warmup_pct, weight_decay)\n        self.save_hyperparameters()\n        self.max_label_length = max_label_length\n        img_h, img_w = img_size\n        self.model = Model(img_h, img_w, len(self.tokenizer), num_fiducial,\n                           output_channel=output_channel, hidden_size=hidden_size, use_ctc=True)\n        named_apply(partial(init_weights, exclude=['Transformation.LocalizationNetwork.localization_fc2']), self.model)\n\n    def forward(self, images: Tensor, max_length: Optional[int] = None) -> Tensor:\n        # max_label_length is unused in CTC prediction\n        return self.model.forward(images, None)\n\n    def training_step(self, batch, batch_idx) -> STEP_OUTPUT:\n        images, labels = batch\n        loss = self.forward_logits_loss(images, labels)[1]\n        self.log('loss', loss)\n        return loss\n"
  },
  {
    "path": "src/parseq/strhub/models/trba/transformation.py",
    "content": "import numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\n\nclass TPS_SpatialTransformerNetwork(nn.Module):\n    \"\"\" Rectification Network of RARE, namely TPS based STN \"\"\"\n\n    def __init__(self, F, I_size, I_r_size, I_channel_num=1):\n        \"\"\" Based on RARE TPS\n        input:\n            batch_I: Batch Input Image [batch_size x I_channel_num x I_height x I_width]\n            I_size : (height, width) of the input image I\n            I_r_size : (height, width) of the rectified image I_r\n            I_channel_num : the number of channels of the input image I\n        output:\n            batch_I_r: rectified image [batch_size x I_channel_num x I_r_height x I_r_width]\n        \"\"\"\n        super().__init__()\n        self.F = F\n        self.I_size = I_size\n        self.I_r_size = I_r_size  # = (I_r_height, I_r_width)\n        self.I_channel_num = I_channel_num\n        self.LocalizationNetwork = LocalizationNetwork(self.F, self.I_channel_num)\n        self.GridGenerator = GridGenerator(self.F, self.I_r_size)\n\n    def forward(self, batch_I):\n        batch_C_prime = self.LocalizationNetwork(batch_I)  # batch_size x K x 2\n        # batch_size x n (= I_r_width x I_r_height) x 2\n        build_P_prime = self.GridGenerator.build_P_prime(batch_C_prime)\n        build_P_prime_reshape = build_P_prime.reshape([build_P_prime.size(0), self.I_r_size[0], self.I_r_size[1], 2])\n\n        if torch.__version__ > \"1.2.0\":\n            batch_I_r = F.grid_sample(batch_I, build_P_prime_reshape, padding_mode='border', align_corners=True)\n        else:\n            batch_I_r = F.grid_sample(batch_I, build_P_prime_reshape, padding_mode='border')\n\n        return batch_I_r\n\n\nclass LocalizationNetwork(nn.Module):\n    \"\"\" Localization Network of RARE, which predicts C' (K x 2) from I (I_width x I_height) \"\"\"\n\n    def __init__(self, F, I_channel_num):\n        super().__init__()\n        self.F = F\n        self.I_channel_num = I_channel_num\n        self.conv = nn.Sequential(\n            nn.Conv2d(in_channels=self.I_channel_num, out_channels=64, kernel_size=3, stride=1, padding=1,\n                      bias=False), nn.BatchNorm2d(64), nn.ReLU(True),\n            nn.MaxPool2d(2, 2),  # batch_size x 64 x I_height/2 x I_width/2\n            nn.Conv2d(64, 128, 3, 1, 1, bias=False), nn.BatchNorm2d(128), nn.ReLU(True),\n            nn.MaxPool2d(2, 2),  # batch_size x 128 x I_height/4 x I_width/4\n            nn.Conv2d(128, 256, 3, 1, 1, bias=False), nn.BatchNorm2d(256), nn.ReLU(True),\n            nn.MaxPool2d(2, 2),  # batch_size x 256 x I_height/8 x I_width/8\n            nn.Conv2d(256, 512, 3, 1, 1, bias=False), nn.BatchNorm2d(512), nn.ReLU(True),\n            nn.AdaptiveAvgPool2d(1)  # batch_size x 512\n        )\n\n        self.localization_fc1 = nn.Sequential(nn.Linear(512, 256), nn.ReLU(True))\n        self.localization_fc2 = nn.Linear(256, self.F * 2)\n\n        # Init fc2 in LocalizationNetwork\n        self.localization_fc2.weight.data.fill_(0)\n        \"\"\" see RARE paper Fig. 6 (a) \"\"\"\n        ctrl_pts_x = np.linspace(-1.0, 1.0, int(F / 2))\n        ctrl_pts_y_top = np.linspace(0.0, -1.0, num=int(F / 2))\n        ctrl_pts_y_bottom = np.linspace(1.0, 0.0, num=int(F / 2))\n        ctrl_pts_top = np.stack([ctrl_pts_x, ctrl_pts_y_top], axis=1)\n        ctrl_pts_bottom = np.stack([ctrl_pts_x, ctrl_pts_y_bottom], axis=1)\n        initial_bias = np.concatenate([ctrl_pts_top, ctrl_pts_bottom], axis=0)\n        self.localization_fc2.bias.data = torch.from_numpy(initial_bias).float().view(-1)\n\n    def forward(self, batch_I):\n        \"\"\"\n        input:     batch_I : Batch Input Image [batch_size x I_channel_num x I_height x I_width]\n        output:    batch_C_prime : Predicted coordinates of fiducial points for input batch [batch_size x F x 2]\n        \"\"\"\n        batch_size = batch_I.size(0)\n        features = self.conv(batch_I).view(batch_size, -1)\n        batch_C_prime = self.localization_fc2(self.localization_fc1(features)).view(batch_size, self.F, 2)\n        return batch_C_prime\n\n\nclass GridGenerator(nn.Module):\n    \"\"\" Grid Generator of RARE, which produces P_prime by multipling T with P \"\"\"\n\n    def __init__(self, F, I_r_size):\n        \"\"\" Generate P_hat and inv_delta_C for later \"\"\"\n        super().__init__()\n        self.eps = 1e-6\n        self.I_r_height, self.I_r_width = I_r_size\n        self.F = F\n        self.C = self._build_C(self.F)  # F x 2\n        self.P = self._build_P(self.I_r_width, self.I_r_height)\n\n        # num_gpu = torch.cuda.device_count()\n        # if num_gpu > 1:\n        # for multi-gpu, you may need register buffer\n        self.register_buffer(\"inv_delta_C\", torch.tensor(\n            self._build_inv_delta_C(self.F, self.C)).float())  # F+3 x F+3\n        self.register_buffer(\"P_hat\", torch.tensor(self._build_P_hat(self.F, self.C, self.P)).float())  # n x F+3\n        # else:\n        #     # for fine-tuning with different image width, you may use below instead of self.register_buffer\n        #     self.inv_delta_C = torch.tensor(self._build_inv_delta_C(self.F, self.C)).float()  # F+3 x F+3\n        #     self.P_hat = torch.tensor(self._build_P_hat(self.F, self.C, self.P)).float()  # n x F+3\n\n    def _build_C(self, F):\n        \"\"\" Return coordinates of fiducial points in I_r; C \"\"\"\n        ctrl_pts_x = np.linspace(-1.0, 1.0, int(F / 2))\n        ctrl_pts_y_top = -1 * np.ones(int(F / 2))\n        ctrl_pts_y_bottom = np.ones(int(F / 2))\n        ctrl_pts_top = np.stack([ctrl_pts_x, ctrl_pts_y_top], axis=1)\n        ctrl_pts_bottom = np.stack([ctrl_pts_x, ctrl_pts_y_bottom], axis=1)\n        C = np.concatenate([ctrl_pts_top, ctrl_pts_bottom], axis=0)\n        return C  # F x 2\n\n    def _build_inv_delta_C(self, F, C):\n        \"\"\" Return inv_delta_C which is needed to calculate T \"\"\"\n        hat_C = np.zeros((F, F), dtype=float)  # F x F\n        for i in range(0, F):\n            for j in range(i, F):\n                r = np.linalg.norm(C[i] - C[j])\n                hat_C[i, j] = r\n                hat_C[j, i] = r\n        np.fill_diagonal(hat_C, 1)\n        hat_C = (hat_C ** 2) * np.log(hat_C)\n        # print(C.shape, hat_C.shape)\n        delta_C = np.concatenate(  # F+3 x F+3\n            [\n                np.concatenate([np.ones((F, 1)), C, hat_C], axis=1),  # F x F+3\n                np.concatenate([np.zeros((2, 3)), np.transpose(C)], axis=1),  # 2 x F+3\n                np.concatenate([np.zeros((1, 3)), np.ones((1, F))], axis=1)  # 1 x F+3\n            ],\n            axis=0\n        )\n        inv_delta_C = np.linalg.inv(delta_C)\n        return inv_delta_C  # F+3 x F+3\n\n    def _build_P(self, I_r_width, I_r_height):\n        I_r_grid_x = (np.arange(-I_r_width, I_r_width, 2) + 1.0) / I_r_width  # self.I_r_width\n        I_r_grid_y = (np.arange(-I_r_height, I_r_height, 2) + 1.0) / I_r_height  # self.I_r_height\n        P = np.stack(  # self.I_r_width x self.I_r_height x 2\n            np.meshgrid(I_r_grid_x, I_r_grid_y),\n            axis=2\n        )\n        return P.reshape([-1, 2])  # n (= self.I_r_width x self.I_r_height) x 2\n\n    def _build_P_hat(self, F, C, P):\n        n = P.shape[0]  # n (= self.I_r_width x self.I_r_height)\n        P_tile = np.tile(np.expand_dims(P, axis=1), (1, F, 1))  # n x 2 -> n x 1 x 2 -> n x F x 2\n        C_tile = np.expand_dims(C, axis=0)  # 1 x F x 2\n        P_diff = P_tile - C_tile  # n x F x 2\n        rbf_norm = np.linalg.norm(P_diff, ord=2, axis=2, keepdims=False)  # n x F\n        rbf = np.multiply(np.square(rbf_norm), np.log(rbf_norm + self.eps))  # n x F\n        P_hat = np.concatenate([np.ones((n, 1)), P, rbf], axis=1)\n        return P_hat  # n x F+3\n\n    def build_P_prime(self, batch_C_prime):\n        \"\"\" Generate Grid from batch_C_prime [batch_size x F x 2] \"\"\"\n        batch_size = batch_C_prime.size(0)\n        batch_inv_delta_C = self.inv_delta_C.repeat(batch_size, 1, 1)\n        batch_P_hat = self.P_hat.repeat(batch_size, 1, 1)\n        batch_C_prime_with_zeros = torch.cat((batch_C_prime, batch_C_prime.new_zeros(\n            (batch_size, 3, 2), dtype=torch.float)), dim=1)  # batch_size x F+3 x 2\n        batch_T = torch.bmm(batch_inv_delta_C, batch_C_prime_with_zeros)  # batch_size x F+3 x 2\n        batch_P_prime = torch.bmm(batch_P_hat, batch_T)  # batch_size x n x 2\n        return batch_P_prime  # batch_size x n x 2\n"
  },
  {
    "path": "src/parseq/strhub/models/utils.py",
    "content": "from pathlib import PurePath\nfrom typing import Sequence\n\nimport torch\nfrom torch import nn\n\nimport yaml\n\n\nclass InvalidModelError(RuntimeError):\n    \"\"\"Exception raised for any model-related error (creation, loading)\"\"\"\n\n\n_WEIGHTS_URL = {\n    'parseq-tiny': 'https://github.com/baudm/parseq/releases/download/v1.0.0/parseq_tiny-e7a21b54.pt',\n    'parseq': 'https://github.com/baudm/parseq/releases/download/v1.0.0/parseq-bb5792a6.pt',\n    'abinet': 'https://github.com/baudm/parseq/releases/download/v1.0.0/abinet-1d1e373e.pt',\n    'trba': 'https://github.com/baudm/parseq/releases/download/v1.0.0/trba-cfaed284.pt',\n    'vitstr': 'https://github.com/baudm/parseq/releases/download/v1.0.0/vitstr-26d0fcf4.pt',\n    'crnn': 'https://github.com/baudm/parseq/releases/download/v1.0.0/crnn-679d0e31.pt',\n}\n\n\ndef _get_config(experiment: str, **kwargs):\n    \"\"\"Emulates hydra config resolution\"\"\"\n    root = PurePath(__file__).parents[2]\n    with open(root / 'configs/main.yaml', 'r') as f:\n        config = yaml.load(f, yaml.Loader)['model']\n    with open(root / f'configs/charset/94_full.yaml', 'r') as f:\n        config.update(yaml.load(f, yaml.Loader)['model'])\n    with open(root / f'configs/experiment/{experiment}.yaml', 'r') as f:\n        exp = yaml.load(f, yaml.Loader)\n    # Apply base model config\n    model = exp['defaults'][0]['override /model']\n    with open(root / f'configs/model/{model}.yaml', 'r') as f:\n        config.update(yaml.load(f, yaml.Loader))\n    # Apply experiment config\n    if 'model' in exp:\n        config.update(exp['model'])\n    config.update(kwargs)\n    # Workaround for now: manually cast the lr to the correct type.\n    config['lr'] = float(config['lr'])\n    return config\n\n\ndef _get_model_class(key):\n    if 'abinet' in key:\n        from .abinet.system import ABINet as ModelClass\n    elif 'crnn' in key:\n        from .crnn.system import CRNN as ModelClass\n    elif 'parseq' in key:\n        from .parseq.system import PARSeq as ModelClass\n    elif 'trba' in key:\n        from .trba.system import TRBA as ModelClass\n    elif 'trbc' in key:\n        from .trba.system import TRBC as ModelClass\n    elif 'vitstr' in key:\n        from .vitstr.system import ViTSTR as ModelClass\n    else:\n        raise InvalidModelError(\"Unable to find model class for '{}'\".format(key))\n    return ModelClass\n\n\ndef get_pretrained_weights(experiment):\n    try:\n        url = _WEIGHTS_URL[experiment]\n    except KeyError:\n        raise InvalidModelError(\"No pretrained weights found for '{}'\".format(experiment)) from None\n    return torch.hub.load_state_dict_from_url(url=url, map_location='cpu', check_hash=True)\n\n\ndef create_model(experiment: str, pretrained: bool = False, **kwargs):\n    try:\n        config = _get_config(experiment, **kwargs)\n    except FileNotFoundError:\n        raise InvalidModelError(\"No configuration found for '{}'\".format(experiment)) from None\n    ModelClass = _get_model_class(experiment)\n    model = ModelClass(**config)\n    if pretrained:\n        model.load_state_dict(get_pretrained_weights(experiment))\n    return model\n\n\ndef load_from_checkpoint(checkpoint_path: str, **kwargs):\n    if checkpoint_path.startswith('pretrained='):\n        model_id = checkpoint_path.split('=', maxsplit=1)[1]\n        model = create_model(model_id, True, **kwargs)\n    else:\n        ModelClass = _get_model_class(checkpoint_path)\n        model = ModelClass.load_from_checkpoint(checkpoint_path, **kwargs)\n    return model\n\n\ndef parse_model_args(args):\n    kwargs = {}\n    arg_types = {t.__name__: t for t in [int, float, str]}\n    arg_types['bool'] = lambda v: v.lower() == 'true'  # special handling for bool\n    for arg in args:\n        name, value = arg.split('=', maxsplit=1)\n        name, arg_type = name.split(':', maxsplit=1)\n        kwargs[name] = arg_types[arg_type](value)\n    return kwargs\n\n\ndef init_weights(module: nn.Module, name: str = '', exclude: Sequence[str] = ()):\n    \"\"\"Initialize the weights using the typical initialization schemes used in SOTA models.\"\"\"\n    if any(map(name.startswith, exclude)):\n        return\n    if isinstance(module, nn.Linear):\n        nn.init.trunc_normal_(module.weight, std=.02)\n        if module.bias is not None:\n            nn.init.zeros_(module.bias)\n    elif isinstance(module, nn.Embedding):\n        nn.init.trunc_normal_(module.weight, std=.02)\n        if module.padding_idx is not None:\n            module.weight.data[module.padding_idx].zero_()\n    elif isinstance(module, nn.Conv2d):\n        nn.init.kaiming_normal_(module.weight, mode='fan_out', nonlinearity='relu')\n        if module.bias is not None:\n            nn.init.zeros_(module.bias)\n    elif isinstance(module, (nn.LayerNorm, nn.BatchNorm2d, nn.GroupNorm)):\n        nn.init.ones_(module.weight)\n        nn.init.zeros_(module.bias)\n"
  },
  {
    "path": "src/parseq/strhub/models/vitstr/__init__.py",
    "content": "r\"\"\"\nAtienza, Rowel. \"Vision Transformer for Fast and Efficient Scene Text Recognition.\"\nIn International Conference on Document Analysis and Recognition (ICDAR). 2021.\n\nhttps://arxiv.org/abs/2105.08582\n\nAll source files, except `system.py`, are based on the implementation listed below,\nand hence are released under the license of the original.\n\nSource: https://github.com/roatienza/deep-text-recognition-benchmark\nLicense: Apache License 2.0 (see LICENSE file in project root)\n\"\"\"\n"
  },
  {
    "path": "src/parseq/strhub/models/vitstr/model.py",
    "content": "\"\"\"\nImplementation of ViTSTR based on timm VisionTransformer.\n\nTODO:\n1) distilled deit backbone\n2) base deit backbone\n\nCopyright 2021 Rowel Atienza\n\"\"\"\n\nfrom timm.models.vision_transformer import VisionTransformer\n\n\nclass ViTSTR(VisionTransformer):\n    \"\"\"\n    ViTSTR is basically a ViT that uses DeiT weights.\n    Modified head to support a sequence of characters prediction for STR.\n    \"\"\"\n\n    def forward(self, x, seqlen: int = 25):\n        x = self.forward_features(x)\n        x = x[:, :seqlen]\n\n        # batch, seqlen, embsize\n        b, s, e = x.size()\n        x = x.reshape(b * s, e)\n        x = self.head(x).view(b, s, self.num_classes)\n        return x\n"
  },
  {
    "path": "src/parseq/strhub/models/vitstr/system.py",
    "content": "# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Sequence, Any, Optional\n\nimport torch\nfrom pytorch_lightning.utilities.types import STEP_OUTPUT\nfrom torch import Tensor\n\nfrom strhub.models.base import CrossEntropySystem\nfrom strhub.models.utils import init_weights\nfrom .model import ViTSTR as Model\n\n\nclass ViTSTR(CrossEntropySystem):\n\n    def __init__(self, charset_train: str, charset_test: str, max_label_length: int,\n                 batch_size: int, lr: float, warmup_pct: float, weight_decay: float,\n                 img_size: Sequence[int], patch_size: Sequence[int], embed_dim: int, num_heads: int,\n                 **kwargs: Any) -> None:\n        super().__init__(charset_train, charset_test, batch_size, lr, warmup_pct, weight_decay)\n        self.save_hyperparameters()\n        self.max_label_length = max_label_length\n        # We don't predict <bos> nor <pad>\n        self.model = Model(img_size=img_size, patch_size=patch_size, depth=12, mlp_ratio=4, qkv_bias=True,\n                           embed_dim=embed_dim, num_heads=num_heads, num_classes=len(self.tokenizer) - 2)\n        # Non-zero weight init for the head\n        self.model.head.apply(init_weights)\n\n    @torch.jit.ignore\n    def no_weight_decay(self):\n        return {'model.' + n for n in self.model.no_weight_decay()}\n\n    def forward(self, images: Tensor, max_length: Optional[int] = None) -> Tensor:\n        max_length = self.max_label_length if max_length is None else min(max_length, self.max_label_length)\n        logits = self.model.forward(images, max_length + 2)  # +2 tokens for [GO] and [s]\n        # Truncate to conform to other models. [GO] in ViTSTR is actually used as the padding (therefore, ignored).\n        # First position corresponds to the class token, which is unused and ignored in the original work.\n        logits = logits[:, 1:]\n        return logits\n\n    def training_step(self, batch, batch_idx) -> STEP_OUTPUT:\n        images, labels = batch\n        loss = self.forward_logits_loss(images, labels)[1]\n        self.log('loss', loss)\n        return loss\n"
  },
  {
    "path": "src/parseq/test.py",
    "content": "#!/usr/bin/env python3\n# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport string\nimport sys\nfrom dataclasses import dataclass\nfrom typing import List\n\nimport torch\n\nfrom tqdm import tqdm\n\nfrom strhub.data.module import SceneTextDataModule\nfrom strhub.models.utils import load_from_checkpoint, parse_model_args\n\n\n@dataclass\nclass Result:\n    dataset: str\n    num_samples: int\n    accuracy: float\n    ned: float\n    confidence: float\n    label_length: float\n\n\ndef print_results_table(results: List[Result], file=None):\n    w = max(map(len, map(getattr, results, ['dataset'] * len(results))))\n    w = max(w, len('Dataset'), len('Combined'))\n    print('| {:<{w}} | # samples | Accuracy | 1 - NED | Confidence | Label Length |'.format('Dataset', w=w), file=file)\n    print('|:{:-<{w}}:|----------:|---------:|--------:|-----------:|-------------:|'.format('----', w=w), file=file)\n    c = Result('Combined', 0, 0, 0, 0, 0)\n    for res in results:\n        c.num_samples += res.num_samples\n        c.accuracy += res.num_samples * res.accuracy\n        c.ned += res.num_samples * res.ned\n        c.confidence += res.num_samples * res.confidence\n        c.label_length += res.num_samples * res.label_length\n        print(f'| {res.dataset:<{w}} | {res.num_samples:>9} | {res.accuracy:>8.2f} | {res.ned:>7.2f} '\n              f'| {res.confidence:>10.2f} | {res.label_length:>12.2f} |', file=file)\n    c.accuracy /= c.num_samples\n    c.ned /= c.num_samples\n    c.confidence /= c.num_samples\n    c.label_length /= c.num_samples\n    print('|-{:-<{w}}-|-----------|----------|---------|------------|--------------|'.format('----', w=w), file=file)\n    print(f'| {c.dataset:<{w}} | {c.num_samples:>9} | {c.accuracy:>8.2f} | {c.ned:>7.2f} '\n          f'| {c.confidence:>10.2f} | {c.label_length:>12.2f} |', file=file)\n\n\n@torch.inference_mode()\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('checkpoint', help=\"Model checkpoint (or 'pretrained=<model_id>')\")\n    parser.add_argument('--data_root', default='data')\n    parser.add_argument('--batch_size', type=int, default=512)\n    parser.add_argument('--num_workers', type=int, default=4)\n    parser.add_argument('--cased', action='store_true', default=False, help='Cased comparison')\n    parser.add_argument('--punctuation', action='store_true', default=False, help='Check punctuation')\n    parser.add_argument('--new', action='store_true', default=False, help='Evaluate on new benchmark datasets')\n    parser.add_argument('--rotation', type=int, default=0, help='Angle of rotation (counter clockwise) in degrees.')\n    parser.add_argument('--device', default='cuda')\n    args, unknown = parser.parse_known_args()\n    kwargs = parse_model_args(unknown)\n\n    charset_test = string.digits + string.ascii_lowercase\n    if args.cased:\n        charset_test += string.ascii_uppercase\n    if args.punctuation:\n        charset_test += string.punctuation\n    kwargs.update({'charset_test': charset_test})\n    print(f'Additional keyword arguments: {kwargs}')\n\n    model = load_from_checkpoint(args.checkpoint, **kwargs).eval().to(args.device)\n    hp = model.hparams\n    datamodule = SceneTextDataModule(args.data_root, '_unused_', hp.img_size, hp.max_label_length, hp.charset_train,\n                                     hp.charset_test, args.batch_size, args.num_workers, False, rotation=args.rotation)\n\n    test_set = SceneTextDataModule.TEST_BENCHMARK_SUB + SceneTextDataModule.TEST_BENCHMARK\n    if args.new:\n        test_set += SceneTextDataModule.TEST_NEW\n    test_set = sorted(set(test_set))\n\n    results = {}\n    max_width = max(map(len, test_set))\n    for name, dataloader in datamodule.test_dataloaders(test_set).items():\n        total = 0\n        correct = 0\n        ned = 0\n        confidence = 0\n        label_length = 0\n        for imgs, labels in tqdm(iter(dataloader), desc=f'{name:>{max_width}}'):\n            res = model.test_step((imgs.to(model.device), labels), -1)['output']\n            total += res.num_samples\n            correct += res.correct\n            ned += res.ned\n            confidence += res.confidence\n            label_length += res.label_length\n        accuracy = 100 * correct / total\n        mean_ned = 100 * (1 - ned / total)\n        mean_conf = 100 * confidence / total\n        mean_label_length = label_length / total\n        results[name] = Result(name, total, accuracy, mean_ned, mean_conf, mean_label_length)\n\n    result_groups = {\n        'Benchmark (Subset)': SceneTextDataModule.TEST_BENCHMARK_SUB,\n        'Benchmark': SceneTextDataModule.TEST_BENCHMARK\n    }\n    if args.new:\n        result_groups.update({'New': SceneTextDataModule.TEST_NEW})\n    with open(args.checkpoint + '.log.txt', 'w') as f:\n        for out in [f, sys.stdout]:\n            for group, subset in result_groups.items():\n                print(f'{group} set:', file=out)\n                print_results_table([results[s] for s in subset], out)\n                print('\\n', file=out)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "src/parseq/tools/art_converter.py",
    "content": "#!/usr/bin/env python3\n\nimport json\n\nwith open('train_task2_labels.json', 'r', encoding='utf8') as f:\n    d = json.load(f)\n\nwith open('gt.txt', 'w', encoding='utf8') as f:\n    for k, v in d.items():\n        if len(v) != 1:\n            print('error', v)\n        v = v[0]\n        if v['language'].lower() != 'latin':\n            # print('Skipping non-Latin:', v)\n            continue\n        if v['illegibility']:\n            # print('Skipping unreadable:', v)\n            continue\n        label = v['transcription'].strip()\n        if not label:\n            # print('Skipping blank label')\n            continue\n        if '#' in label and label != 'LocaL#3':\n            # print('Skipping corrupted label')\n            continue\n        f.write('\\t'.join(['train_task2_images/' + k + '.jpg', label]) + '\\n')\n"
  },
  {
    "path": "src/parseq/tools/case_sensitive_str_datasets_converter.py",
    "content": "#!/usr/bin/env python3\n\nimport os.path\nimport sys\nfrom pathlib import Path\n\nd = sys.argv[1]\np = Path(d)\n\ngt = []\n\nnum_samples = len(list(p.glob('label/*.txt')))\next = 'jpg' if p.joinpath('IMG', '1.jpg').is_file() else 'png'\n\nfor i in range(1, num_samples + 1):\n    img = p.joinpath('IMG', f'{i}.{ext}')\n    name = os.path.splitext(img.name)[0]\n\n    with open(p.joinpath('label', f'{i}.txt'), 'r') as f:\n        label = f.readline()\n    gt.append((os.path.join('IMG', img.name), label))\n\nwith open(d + '/lmdb.txt', 'w', encoding='utf-8') as f:\n    for line in gt:\n        fname, label = line\n        fname = fname.strip()\n        label = label.strip()\n        f.write('\\t'.join([fname, label]) + '\\n')\n"
  },
  {
    "path": "src/parseq/tools/coco_2_converter.py",
    "content": "#!/usr/bin/env python3\nimport argparse\nimport html\nimport math\nimport os\nimport os.path as osp\nfrom functools import partial\n\nimport mmcv\nfrom PIL import Image\nfrom mmocr.utils.fileio import list_to_file\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Generate training and validation set of TextOCR '\n                    'by cropping box image.')\n    parser.add_argument('root_path', help='Root dir path of TextOCR')\n    parser.add_argument(\n        'n_proc', default=1, type=int, help='Number of processes to run')\n    args = parser.parse_args()\n    return args\n\n\ndef process_img(args, src_image_root, dst_image_root):\n    # Dirty hack for multiprocessing\n    img_idx, img_info, anns = args\n    src_img = Image.open(osp.join(src_image_root, 'train2014', img_info['file_name']))\n    src_w, src_h = src_img.size\n    labels = []\n    for ann_idx, ann in enumerate(anns):\n        text_label = html.unescape(ann['utf8_string'].strip())\n\n        # Ignore empty labels\n        if not text_label or ann['class'] != 'machine printed' or ann['language'] != 'english' or \\\n                ann['legibility'] != 'legible':\n            continue\n\n        # Some labels and images with '#' in the middle are actually good, but some aren't, so we just filter them all.\n        if text_label != '#' and '#' in text_label:\n            continue\n\n        # Some labels use '*' to denote unreadable characters\n        if text_label.startswith('*') or text_label.endswith('*'):\n            continue\n\n        pad = 2\n        x, y, w, h = ann['bbox']\n        x, y = max(0, math.floor(x) - pad), max(0, math.floor(y) - pad)\n        w, h = math.ceil(w), math.ceil(h)\n        x2, y2 = min(src_w, x + w + 2 * pad), min(src_h, y + h + 2 * pad)\n        dst_img = src_img.crop((x, y, x2, y2))\n        dst_img_name = f'img_{img_idx}_{ann_idx}.jpg'\n        dst_img_path = osp.join(dst_image_root, dst_img_name)\n        # Preserve JPEG quality\n        dst_img.save(dst_img_path, qtables=src_img.quantization)\n        labels.append(f'{osp.basename(dst_image_root)}/{dst_img_name}'\n                      f' {text_label}')\n    src_img.close()\n    return labels\n\n\ndef convert_textocr(root_path,\n                    dst_image_path,\n                    dst_label_filename,\n                    annotation_filename,\n                    img_start_idx=0,\n                    nproc=1):\n    annotation_path = osp.join(root_path, annotation_filename)\n    if not osp.exists(annotation_path):\n        raise Exception(\n            f'{annotation_path} not exists, please check and try again.')\n    src_image_root = root_path\n\n    # outputs\n    dst_label_file = osp.join(root_path, dst_label_filename)\n    dst_image_root = osp.join(root_path, dst_image_path)\n    os.makedirs(dst_image_root, exist_ok=True)\n\n    annotation = mmcv.load(annotation_path)\n    split = 'train' if 'train' in dst_label_filename else 'val'\n\n    process_img_with_path = partial(\n        process_img,\n        src_image_root=src_image_root,\n        dst_image_root=dst_image_root)\n    tasks = []\n    for img_idx, img_info in enumerate(annotation['imgs'].values()):\n        if img_info['set'] != split:\n            continue\n        ann_ids = annotation['imgToAnns'][str(img_info['id'])]\n        anns = [annotation['anns'][str(ann_id)] for ann_id in ann_ids]\n        tasks.append((img_idx + img_start_idx, img_info, anns))\n\n    labels_list = mmcv.track_parallel_progress(\n        process_img_with_path, tasks, keep_order=True, nproc=nproc)\n    final_labels = []\n    for label_list in labels_list:\n        final_labels += label_list\n    list_to_file(dst_label_file, final_labels)\n    return len(annotation['imgs'])\n\n\ndef main():\n    args = parse_args()\n    root_path = args.root_path\n    print('Processing training set...')\n    num_train_imgs = convert_textocr(\n        root_path=root_path,\n        dst_image_path='image',\n        dst_label_filename='train_label.txt',\n        annotation_filename='cocotext.v2.json',\n        nproc=args.n_proc)\n    print('Processing validation set...')\n    convert_textocr(\n        root_path=root_path,\n        dst_image_path='image_val',\n        dst_label_filename='val_label.txt',\n        annotation_filename='cocotext.v2.json',\n        img_start_idx=num_train_imgs,\n        nproc=args.n_proc)\n    print('Finish')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "src/parseq/tools/coco_text_converter.py",
    "content": "#!/usr/bin/env python3\n\nfor s in ['train', 'val']:\n    with open('{}_words_gt.txt'.format(s), 'r', encoding='utf8') as f:\n        d = f.readlines()\n\n    with open('{}_lmdb.txt'.format(s), 'w', encoding='utf8') as f:\n        for line in d:\n            try:\n                fname, label = line.split(',', maxsplit=1)\n            except ValueError:\n                continue\n            fname = '{}_words/{}.jpg'.format(s, fname.strip())\n            label = label.strip().strip('|')\n            f.write('\\t'.join([fname, label]) + '\\n')\n"
  },
  {
    "path": "src/parseq/tools/create_lmdb_dataset.py",
    "content": "#!/usr/bin/env python3\n\"\"\" a modified version of CRNN torch repository https://github.com/bgshih/crnn/blob/master/tool/create_dataset.py \"\"\"\nimport io\nimport os\n\nimport fire\nimport lmdb\nimport numpy as np\nfrom PIL import Image\n\n\ndef checkImageIsValid(imageBin):\n    if imageBin is None:\n        return False\n    img = Image.open(io.BytesIO(imageBin)).convert('RGB')\n    return np.prod(img.size) > 0\n\n\ndef writeCache(env, cache):\n    with env.begin(write=True) as txn:\n        for k, v in cache.items():\n            txn.put(k, v)\n\n\ndef createDataset(inputPath, gtFile, outputPath, checkValid=True):\n    \"\"\"\n    Create LMDB dataset for training and evaluation.\n    ARGS:\n        inputPath  : input folder path where starts imagePath\n        outputPath : LMDB output path\n        gtFile     : list of image path and label\n        checkValid : if true, check the validity of every image\n    \"\"\"\n    os.makedirs(outputPath, exist_ok=True)\n    env = lmdb.open(outputPath, map_size=1099511627776)\n\n    cache = {}\n    cnt = 1\n\n    with open(gtFile, 'r', encoding='utf-8') as f:\n        data = f.readlines()\n\n    nSamples = len(data)\n    for i, line in enumerate(data):\n        imagePath, label = line.strip().split(maxsplit=1)\n        imagePath = os.path.join(inputPath, imagePath)\n        with open(imagePath, 'rb') as f:\n            imageBin = f.read()\n        if checkValid:\n            try:\n                img = Image.open(io.BytesIO(imageBin)).convert('RGB')\n            except IOError as e:\n                with open(outputPath + '/error_image_log.txt', 'a') as log:\n                    log.write('{}-th image data occured error: {}, {}\\n'.format(i, imagePath, e))\n                continue\n            if np.prod(img.size) == 0:\n                print('%s is not a valid image' % imagePath)\n                continue\n\n        imageKey = 'image-%09d'.encode() % cnt\n        labelKey = 'label-%09d'.encode() % cnt\n        cache[imageKey] = imageBin\n        cache[labelKey] = label.encode()\n\n        if cnt % 1000 == 0:\n            writeCache(env, cache)\n            cache = {}\n            print('Written %d / %d' % (cnt, nSamples))\n        cnt += 1\n    nSamples = cnt - 1\n    cache['num-samples'.encode()] = str(nSamples).encode()\n    writeCache(env, cache)\n    env.close()\n    print('Created dataset with %d samples' % nSamples)\n\n\nif __name__ == '__main__':\n    fire.Fire(createDataset)\n"
  },
  {
    "path": "src/parseq/tools/filter_lmdb.py",
    "content": "#!/usr/bin/env python3\nimport io\nimport os\nfrom argparse import ArgumentParser\n\nimport numpy as np\nimport lmdb\nfrom PIL import Image\n\n\ndef main():\n    parser = ArgumentParser()\n    parser.add_argument('inputs', nargs='+', help='Path to input LMDBs')\n    parser.add_argument('--output', help='Path to output LMDB')\n    parser.add_argument('--min_image_dim', type=int, default=8)\n    args = parser.parse_args()\n\n    os.makedirs(args.output, exist_ok=True)\n    with lmdb.open(args.output, map_size=1099511627776) as env_out:\n        in_samples = 0\n        out_samples = 0\n        samples_per_chunk = 1000\n        for lmdb_in in args.inputs:\n            with lmdb.open(lmdb_in, readonly=True, max_readers=1, lock=False) as env_in:\n                with env_in.begin() as txn:\n                    num_samples = int(txn.get('num-samples'.encode()))\n                in_samples += num_samples\n                chunks = np.array_split(range(num_samples), num_samples // samples_per_chunk)\n                for chunk in chunks:\n                    cache = {}\n                    with env_in.begin() as txn:\n                        for index in chunk:\n                            index += 1  # lmdb starts at 1\n                            image_key = f'image-{index:09d}'.encode()\n                            image_bin = txn.get(image_key)\n                            img = Image.open(io.BytesIO(image_bin))\n                            w, h = img.size\n                            if w < args.min_image_dim or h < args.min_image_dim:\n                                print(f'Skipping: {index}, w = {w}, h = {h}')\n                                continue\n                            out_samples += 1  # increment. start at 1\n                            label_key = f'label-{index:09d}'.encode()\n                            out_label_key = f'label-{out_samples:09d}'.encode()\n                            out_image_key = f'image-{out_samples:09d}'.encode()\n                            cache[out_label_key] = txn.get(label_key)\n                            cache[out_image_key] = image_bin\n                    with env_out.begin(write=True) as txn:\n                        for k, v in cache.items():\n                            txn.put(k, v)\n                    print(f'Written samples from {chunk[0]} to {chunk[-1]}')\n        with env_out.begin(write=True) as txn:\n            txn.put('num-samples'.encode(), str(out_samples).encode())\n        print(f'Written {out_samples} samples to {args.output} out of {in_samples} input samples.')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "src/parseq/tools/lsvt_converter.py",
    "content": "#!/usr/bin/env python3\nimport argparse\nimport os\nimport os.path as osp\nimport re\nfrom functools import partial\n\nimport mmcv\nimport numpy as np\nfrom PIL import Image\nfrom mmocr.utils.fileio import list_to_file\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Generate training set of LSVT '\n                    'by cropping box image.')\n    parser.add_argument('root_path', help='Root dir path of LSVT')\n    parser.add_argument(\n        'n_proc', default=1, type=int, help='Number of processes to run')\n    args = parser.parse_args()\n    return args\n\n\ndef process_img(args, src_image_root, dst_image_root):\n    # Dirty hack for multiprocessing\n    img_idx, img_info, anns = args\n    try:\n        src_img = Image.open(osp.join(src_image_root, 'train_full_images_0/{}.jpg'.format(img_info)))\n    except IOError:\n        src_img = Image.open(osp.join(src_image_root, 'train_full_images_1/{}.jpg'.format(img_info)))\n    blacklist = ['LOFTINESS*']\n    whitelist = ['#Find YOUR Fun#', 'Story #', '*0#']\n    labels = []\n    for ann_idx, ann in enumerate(anns):\n        text_label = ann['transcription']\n\n        # Ignore illegible or words with non-Latin characters\n        if ann['illegibility'] or re.findall(r'[\\u4e00-\\u9fff]+', text_label) or text_label in blacklist or \\\n                ('#' in text_label and text_label not in whitelist):\n            continue\n\n        points = np.asarray(ann['points'])\n        x1, y1 = points.min(axis=0)\n        x2, y2 = points.max(axis=0)\n\n        dst_img = src_img.crop((x1, y1, x2, y2))\n        dst_img_name = f'img_{img_idx}_{ann_idx}.jpg'\n        dst_img_path = osp.join(dst_image_root, dst_img_name)\n        # Preserve JPEG quality\n        dst_img.save(dst_img_path, qtables=src_img.quantization)\n        labels.append(f'{osp.basename(dst_image_root)}/{dst_img_name}'\n                      f' {text_label}')\n    src_img.close()\n    return labels\n\n\ndef convert_lsvt(root_path,\n                 dst_image_path,\n                 dst_label_filename,\n                 annotation_filename,\n                 img_start_idx=0,\n                 nproc=1):\n    annotation_path = osp.join(root_path, annotation_filename)\n    if not osp.exists(annotation_path):\n        raise Exception(\n            f'{annotation_path} not exists, please check and try again.')\n    src_image_root = root_path\n\n    # outputs\n    dst_label_file = osp.join(root_path, dst_label_filename)\n    dst_image_root = osp.join(root_path, dst_image_path)\n    os.makedirs(dst_image_root, exist_ok=True)\n\n    annotation = mmcv.load(annotation_path)\n\n    process_img_with_path = partial(\n        process_img,\n        src_image_root=src_image_root,\n        dst_image_root=dst_image_root)\n    tasks = []\n    for img_idx, (img_info, anns) in enumerate(annotation.items()):\n        tasks.append((img_idx + img_start_idx, img_info, anns))\n    labels_list = mmcv.track_parallel_progress(\n        process_img_with_path, tasks, keep_order=True, nproc=nproc)\n    final_labels = []\n    for label_list in labels_list:\n        final_labels += label_list\n    list_to_file(dst_label_file, final_labels)\n    return len(annotation)\n\n\ndef main():\n    args = parse_args()\n    root_path = args.root_path\n    print('Processing training set...')\n    convert_lsvt(\n        root_path=root_path,\n        dst_image_path='image_train',\n        dst_label_filename='train_label.txt',\n        annotation_filename='train_full_labels.json',\n        nproc=args.n_proc)\n    print('Finish')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "src/parseq/tools/mlt19_converter.py",
    "content": "#!/usr/bin/env python3\n\nimport sys\n\nroot = sys.argv[1]\n\nwith open(root + '/gt.txt', 'r') as f:\n    d = f.readlines()\n\nwith open(root + '/lmdb.txt', 'w') as f:\n    for line in d:\n        img, script, label = line.split(',', maxsplit=2)\n        label = label.strip()\n        if label and script in ['Latin', 'Symbols']:\n            f.write('\\t'.join([img, label]) + '\\n')\n"
  },
  {
    "path": "src/parseq/tools/openvino_converter.py",
    "content": "#!/usr/bin/env python3\nimport math\nimport os\nimport os.path as osp\nfrom argparse import ArgumentParser\nfrom functools import partial\n\nimport mmcv\nfrom PIL import Image\n\nfrom mmocr.utils.fileio import list_to_file\n\n\ndef parse_args():\n    parser = ArgumentParser(description='Generate training and validation set '\n                            'of OpenVINO annotations for Open '\n                            'Images by cropping box image.')\n    parser.add_argument(\n        'root_path', help='Root dir containing images and annotations')\n    parser.add_argument(\n        'n_proc', default=1, type=int, help='Number of processes to run')\n    args = parser.parse_args()\n    return args\n\n\ndef process_img(args, src_image_root, dst_image_root):\n    # Dirty hack for multiprocessing\n    img_idx, img_info, anns = args\n    src_img = Image.open(osp.join(src_image_root, img_info['file_name']))\n    labels = []\n    for ann_idx, ann in enumerate(anns):\n        attrs = ann['attributes']\n        text_label = attrs['transcription']\n\n        # Ignore illegible or non-English words\n        if not attrs['legible'] or attrs['language'] != 'english':\n            continue\n\n        x, y, w, h = ann['bbox']\n        x, y = max(0, math.floor(x)), max(0, math.floor(y))\n        w, h = math.ceil(w), math.ceil(h)\n        dst_img = src_img.crop((x, y, x + w, y + h))\n        dst_img_name = f'img_{img_idx}_{ann_idx}.jpg'\n        dst_img_path = osp.join(dst_image_root, dst_img_name)\n        # Preserve JPEG quality\n        dst_img.save(dst_img_path, qtables=src_img.quantization)\n        labels.append(f'{osp.basename(dst_image_root)}/{dst_img_name}'\n                      f' {text_label}')\n    src_img.close()\n    return labels\n\n\ndef convert_openimages(root_path,\n                       dst_image_path,\n                       dst_label_filename,\n                       annotation_filename,\n                       img_start_idx=0,\n                       nproc=1):\n    annotation_path = osp.join(root_path, annotation_filename)\n    if not osp.exists(annotation_path):\n        raise Exception(\n            f'{annotation_path} not exists, please check and try again.')\n    src_image_root = root_path\n\n    # outputs\n    dst_label_file = osp.join(root_path, dst_label_filename)\n    dst_image_root = osp.join(root_path, dst_image_path)\n    os.makedirs(dst_image_root, exist_ok=True)\n\n    annotation = mmcv.load(annotation_path)\n\n    process_img_with_path = partial(\n        process_img,\n        src_image_root=src_image_root,\n        dst_image_root=dst_image_root)\n    tasks = []\n    anns = {}\n    for ann in annotation['annotations']:\n        anns.setdefault(ann['image_id'], []).append(ann)\n    for img_idx, img_info in enumerate(annotation['images']):\n        tasks.append((img_idx + img_start_idx, img_info, anns[img_info['id']]))\n    labels_list = mmcv.track_parallel_progress(\n        process_img_with_path, tasks, keep_order=True, nproc=nproc)\n    final_labels = []\n    for label_list in labels_list:\n        final_labels += label_list\n    list_to_file(dst_label_file, final_labels)\n    return len(annotation['images'])\n\n\ndef main():\n    args = parse_args()\n    root_path = args.root_path\n    print('Processing training set...')\n    num_train_imgs = 0\n    for s in '125f':\n        num_train_imgs = convert_openimages(\n            root_path=root_path,\n            dst_image_path=f'image_{s}',\n            dst_label_filename=f'train_{s}_label.txt',\n            annotation_filename=f'text_spotting_openimages_v5_train_{s}.json',\n            img_start_idx=num_train_imgs,\n            nproc=args.n_proc)\n    print('Processing validation set...')\n    convert_openimages(\n        root_path=root_path,\n        dst_image_path='image_val',\n        dst_label_filename='val_label.txt',\n        annotation_filename='text_spotting_openimages_v5_validation.json',\n        img_start_idx=num_train_imgs,\n        nproc=args.n_proc)\n    print('Finish')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "src/parseq/tools/test_abinet_lm_acc.py",
    "content": "#!/usr/bin/env python3\nimport argparse\nimport string\nimport sys\n\nimport torch\nimport torch.nn.functional as F\nfrom torch import Tensor\nfrom torch.nn.utils.rnn import pad_sequence\n\nfrom tqdm import tqdm\n\nfrom strhub.data.module import SceneTextDataModule\nfrom strhub.models.abinet.system import ABINet\n\nsys.path.insert(0, '.')\nfrom hubconf import _get_config\nfrom test import Result, print_results_table\n\n\nclass ABINetLM(ABINet):\n\n    def _encode(self, labels):\n        targets = [torch.arange(self.max_label_length + 1)]  # dummy target. used to set pad_sequence() length\n        lengths = []\n        for label in labels:\n            targets.append(torch.as_tensor([self.tokenizer._stoi[c] for c in label]))\n            lengths.append(len(label) + 1)\n        targets = pad_sequence(targets, batch_first=True, padding_value=0)[1:]  # exclude dummy target\n        lengths = torch.as_tensor(lengths, device=self.device)\n        targets = F.one_hot(targets, len(self.tokenizer._stoi))[..., :len(self.tokenizer._stoi) - 2].float().to(self.device)\n        return targets, lengths\n\n    def forward(self, labels: Tensor, max_length: int = None) -> Tensor:\n        targets, lengths = self._encode(labels)\n        return self.model.language(targets, lengths)['logits']\n\n\ndef main():\n    parser = argparse.ArgumentParser(description='Measure the word accuracy of ABINet LM using the ground truth as input')\n    parser.add_argument('checkpoint', help='Official pretrained weights for ABINet-LV (best-train-abinet.pth)')\n    parser.add_argument('--data_root', default='data')\n    parser.add_argument('--batch_size', type=int, default=512)\n    parser.add_argument('--num_workers', type=int, default=4)\n    parser.add_argument('--new', action='store_true', default=False, help='Evaluate on new benchmark datasets')\n    parser.add_argument('--device', default='cuda')\n    args = parser.parse_args()\n\n    # charset used by original ABINet\n    charset = string.ascii_lowercase + '1234567890'\n    ckpt = torch.load(args.checkpoint)\n\n    config = _get_config('abinet', charset_train=charset, charset_test=charset)\n    model = ABINetLM(**config)\n    model.model.load_state_dict(ckpt['model'])\n\n    model = model.eval().to(args.device)\n    model.freeze()  # disable autograd\n    hp = model.hparams\n    datamodule = SceneTextDataModule(args.data_root, '_unused_', hp.img_size, hp.max_label_length, hp.charset_train,\n                                     hp.charset_test, args.batch_size, args.num_workers, False)\n\n    test_set = SceneTextDataModule.TEST_BENCHMARK\n    if args.new:\n        test_set += SceneTextDataModule.TEST_NEW\n    test_set = sorted(set(test_set))\n\n    results = {}\n    max_width = max(map(len, test_set))\n    for name, dataloader in datamodule.test_dataloaders(test_set).items():\n        total = 0\n        correct = 0\n        ned = 0\n        confidence = 0\n        label_length = 0\n        for _, labels in tqdm(iter(dataloader), desc=f'{name:>{max_width}}'):\n            res = model.test_step((labels, labels), -1)['output']\n            total += res.num_samples\n            correct += res.correct\n            ned += res.ned\n            confidence += res.confidence\n            label_length += res.label_length\n        accuracy = 100 * correct / total\n        mean_ned = 100 * (1 - ned / total)\n        mean_conf = 100 * confidence / total\n        mean_label_length = label_length / total\n        results[name] = Result(name, total, accuracy, mean_ned, mean_conf, mean_label_length)\n\n    result_groups = {\n        'Benchmark': SceneTextDataModule.TEST_BENCHMARK\n    }\n    if args.new:\n        result_groups.update({'New': SceneTextDataModule.TEST_NEW})\n    for group, subset in result_groups.items():\n        print(f'{group} set:')\n        print_results_table([results[s] for s in subset])\n        print('\\n')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "src/parseq/tools/textocr_converter.py",
    "content": "#!/usr/bin/env python3\n# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport math\nimport os\nimport os.path as osp\nfrom functools import partial\n\nimport mmcv\nimport numpy as np\nfrom PIL import Image\nfrom mmocr.utils.fileio import list_to_file\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Generate training and validation set of TextOCR '\n                    'by cropping box image.')\n    parser.add_argument('root_path', help='Root dir path of TextOCR')\n    parser.add_argument(\n        'n_proc', default=1, type=int, help='Number of processes to run')\n    parser.add_argument('--rectify_pose', action='store_true',\n                        help='Fix pose of rotated text to make them horizontal')\n    args = parser.parse_args()\n    return args\n\n\ndef rectify_image_pose(image, top_left, points):\n    # Points-based heuristics for determining text orientation w.r.t. bounding box\n    points = np.asarray(points).reshape(-1, 2)\n    dist = ((points - np.asarray(top_left)) ** 2).sum(axis=1)\n    left_midpoint = (points[0] + points[-1]) / 2\n    right_corner_points = ((points - left_midpoint) ** 2).sum(axis=1).argsort()[-2:]\n    right_midpoint = points[right_corner_points].sum(axis=0) / 2\n    d_x, d_y = abs(right_midpoint - left_midpoint)\n\n    if dist[0] + dist[-1] <= dist[right_corner_points].sum():\n        if d_x >= d_y:\n            rot = 0\n        else:\n            rot = 90\n    else:\n        if d_x >= d_y:\n            rot = 180\n        else:\n            rot = -90\n    if rot:\n        image = image.rotate(rot, expand=True)\n    return image\n\n\ndef process_img(args, src_image_root, dst_image_root):\n    # Dirty hack for multiprocessing\n    img_idx, img_info, anns, rectify_pose = args\n    src_img = Image.open(osp.join(src_image_root, img_info['file_name']))\n    labels = []\n    for ann_idx, ann in enumerate(anns):\n        text_label = ann['utf8_string']\n\n        # Ignore illegible or non-English words\n        if text_label == '.':\n            continue\n\n        x, y, w, h = ann['bbox']\n        x, y = max(0, math.floor(x)), max(0, math.floor(y))\n        w, h = math.ceil(w), math.ceil(h)\n        dst_img = src_img.crop((x, y, x + w, y + h))\n        if rectify_pose:\n            dst_img = rectify_image_pose(dst_img, (x, y), ann['points'])\n        dst_img_name = f'img_{img_idx}_{ann_idx}.jpg'\n        dst_img_path = osp.join(dst_image_root, dst_img_name)\n        # Preserve JPEG quality\n        dst_img.save(dst_img_path, qtables=src_img.quantization)\n        labels.append(f'{osp.basename(dst_image_root)}/{dst_img_name}'\n                      f' {text_label}')\n    src_img.close()\n    return labels\n\n\ndef convert_textocr(root_path,\n                    dst_image_path,\n                    dst_label_filename,\n                    annotation_filename,\n                    img_start_idx=0,\n                    nproc=1,\n                    rectify_pose=False):\n    annotation_path = osp.join(root_path, annotation_filename)\n    if not osp.exists(annotation_path):\n        raise Exception(\n            f'{annotation_path} not exists, please check and try again.')\n    src_image_root = root_path\n\n    # outputs\n    dst_label_file = osp.join(root_path, dst_label_filename)\n    dst_image_root = osp.join(root_path, dst_image_path)\n    os.makedirs(dst_image_root, exist_ok=True)\n\n    annotation = mmcv.load(annotation_path)\n\n    process_img_with_path = partial(\n        process_img,\n        src_image_root=src_image_root,\n        dst_image_root=dst_image_root)\n    tasks = []\n    for img_idx, img_info in enumerate(annotation['imgs'].values()):\n        ann_ids = annotation['imgToAnns'][img_info['id']]\n        anns = [annotation['anns'][ann_id] for ann_id in ann_ids]\n        tasks.append((img_idx + img_start_idx, img_info, anns, rectify_pose))\n    labels_list = mmcv.track_parallel_progress(\n        process_img_with_path, tasks, keep_order=True, nproc=nproc)\n    final_labels = []\n    for label_list in labels_list:\n        final_labels += label_list\n    list_to_file(dst_label_file, final_labels)\n    return len(annotation['imgs'])\n\n\ndef main():\n    args = parse_args()\n    root_path = args.root_path\n    print('Processing training set...')\n    num_train_imgs = convert_textocr(\n        root_path=root_path,\n        dst_image_path='image',\n        dst_label_filename='train_label.txt',\n        annotation_filename='TextOCR_0.1_train.json',\n        nproc=args.n_proc,\n        rectify_pose=args.rectify_pose)\n    print('Processing validation set...')\n    convert_textocr(\n        root_path=root_path,\n        dst_image_path='image',\n        dst_label_filename='val_label.txt',\n        annotation_filename='TextOCR_0.1_val.json',\n        img_start_idx=num_train_imgs,\n        nproc=args.n_proc,\n        rectify_pose=args.rectify_pose)\n    print('Finish')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "src/parseq/train.py",
    "content": "#!/usr/bin/env python3\n# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pathlib import Path\n\nfrom omegaconf import DictConfig, open_dict\nimport hydra\nfrom hydra.core.hydra_config import HydraConfig\n\nfrom pytorch_lightning import Trainer\nfrom pytorch_lightning.callbacks import ModelCheckpoint, StochasticWeightAveraging\nfrom pytorch_lightning.loggers import TensorBoardLogger\nfrom pytorch_lightning.strategies import DDPStrategy\nfrom pytorch_lightning.utilities.model_summary import summarize\n\nfrom strhub.data.module import SceneTextDataModule\nfrom strhub.models.base import BaseSystem\nfrom strhub.models.utils import get_pretrained_weights\n\n\n@hydra.main(config_path='configs', config_name='main', version_base='1.2')\ndef main(config: DictConfig):\n    trainer_strategy = None\n    with open_dict(config):\n        # Resolve absolute path to data.root_dir\n        config.data.root_dir = hydra.utils.to_absolute_path(config.data.root_dir)\n        # Special handling for GPU-affected config\n        gpus = config.trainer.get('gpus', 0)\n        if gpus:\n            # Use mixed-precision training\n            config.trainer.precision = 16\n        if gpus > 1:\n            # Use DDP\n            config.trainer.strategy = 'ddp'\n            # DDP optimizations\n            trainer_strategy = DDPStrategy(find_unused_parameters=False, gradient_as_bucket_view=True)\n            # Scale steps-based config\n            config.trainer.val_check_interval //= gpus\n            if config.trainer.get('max_steps', -1) > 0:\n                config.trainer.max_steps //= gpus\n\n    # Special handling for PARseq\n    if config.model.get('perm_mirrored', False):\n        assert config.model.perm_num % 2 == 0, 'perm_num should be even if perm_mirrored = True'\n\n    model: BaseSystem = hydra.utils.instantiate(config.model)\n    # If specified, use pretrained weights to initialize the model\n    if config.pretrained is not None:\n        model.load_state_dict(get_pretrained_weights(config.pretrained))\n    print(summarize(model, max_depth=1 if model.hparams.name.startswith('parseq') else 2))\n\n    datamodule: SceneTextDataModule = hydra.utils.instantiate(config.data)\n\n    checkpoint = ModelCheckpoint(monitor='val_accuracy', mode='max', save_top_k=3, save_last=True,\n                                 filename='{epoch}-{step}-{val_accuracy:.4f}-{val_NED:.4f}')\n    swa = StochasticWeightAveraging(swa_epoch_start=0.75)\n    cwd = HydraConfig.get().runtime.output_dir if config.ckpt_path is None else \\\n        str(Path(config.ckpt_path).parents[1].absolute())\n    trainer: Trainer = hydra.utils.instantiate(config.trainer, logger=TensorBoardLogger(cwd, '', '.'),\n                                               strategy=trainer_strategy, enable_model_summary=False,\n                                               callbacks=[checkpoint, swa])\n    trainer.fit(model, datamodule=datamodule, ckpt_path=config.ckpt_path)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "src/parseq/tune.py",
    "content": "#!/usr/bin/env python3\n# Scene Text Recognition Model Hub\n# Copyright 2022 Darwin Bautista\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport logging\nimport math\nimport os\nimport shutil\nfrom pathlib import Path\n\nfrom omegaconf import DictConfig, open_dict\nimport hydra\nfrom hydra.core.hydra_config import HydraConfig\n\nimport numpy as np\n\nfrom pytorch_lightning import Trainer, LightningModule\nfrom pytorch_lightning.loggers import TensorBoardLogger\n\nfrom ray import tune\nfrom ray.tune import CLIReporter\nfrom ray.tune.integration.pytorch_lightning import TuneReportCheckpointCallback\nfrom ray.tune.ray_trial_executor import RayTrialExecutor\nfrom ray.tune.schedulers import MedianStoppingRule\nfrom ray.tune.suggest.ax import AxSearch\n\nfrom strhub.data.module import SceneTextDataModule\nfrom strhub.models.base import BaseSystem\n\nlog = logging.getLogger(__name__)\n\n\nclass MetricTracker(tune.Stopper):\n    \"\"\"Tracks the trend of the metric. Stops downward/stagnant trials. Assumes metric is being maximized.\"\"\"\n\n    def __init__(self, metric, max_t, patience: int = 3, window: int = 3) -> None:\n        super().__init__()\n        self.metric = metric\n        self.trial_history = {}\n        self.max_t = max_t\n        self.training_iteration = 0\n        self.eps = 0.01  # sensitivity\n        self.patience = patience  # number of consecutive downward/stagnant samples to trigger early stoppage.\n        self.kernel = self.gaussian_pdf(np.arange(window) - window // 2, sigma=0.6)\n        # Extra samples to keep in order to have better MAs + gradients for the middle p samples.\n        self.buffer = 2 * (len(self.kernel) // 2) + 2\n\n    @staticmethod\n    def gaussian_pdf(x, sigma=1.):\n        return np.exp(-(x / sigma)**2 / 2) / (sigma * np.sqrt(2 * np.pi))\n\n    @staticmethod\n    def moving_average(x, k):\n        return np.convolve(x, k, 'valid') / k.sum()\n\n    def __call__(self, trial_id, result):\n        self.training_iteration = result['training_iteration']\n        if np.isnan(result['loss']) or self.training_iteration >= self.max_t:\n            try:\n                del self.trial_history[trial_id]\n            except KeyError:\n                pass\n            return True\n        history = self.trial_history.get(trial_id, [])\n        # FIFO queue of metric values.\n        history = history[-(self.patience + self.buffer - 1):] + [result[self.metric]]\n        # Only start checking once we have enough data. At least one non-zero sample is required.\n        if len(history) == self.patience + self.buffer and sum(history) > 0:\n            smooth_grad = np.gradient(self.moving_average(history, self.kernel))[1:-1]  # discard edge values.\n            # Check if trend is downward or stagnant\n            if (smooth_grad < self.eps).all():\n                log.info(f'Stopping trial = {trial_id}, hist = {history}, grad = {smooth_grad}')\n                try:\n                    del self.trial_history[trial_id]\n                except KeyError:\n                    pass\n                return True\n        self.trial_history[trial_id] = history\n        return False\n\n    def stop_all(self):\n        return False\n\n\nclass TuneReportCheckpointPruneCallback(TuneReportCheckpointCallback):\n\n    def _handle(self, trainer: Trainer, pl_module: LightningModule):\n        self._checkpoint._handle(trainer, pl_module)\n        # Prune older checkpoints\n        for old in sorted(Path(tune.get_trial_dir()).glob('checkpoint_epoch=*-step=*'), key=os.path.getmtime)[:-1]:\n            log.info(f'Deleting old checkpoint: {old}')\n            shutil.rmtree(old)\n        self._report._handle(trainer, pl_module)\n\n\ndef train(hparams, config, checkpoint_dir=None):\n    with open_dict(config):\n        config.model.lr = hparams['lr']\n        # config.model.weight_decay = hparams['wd']\n\n    model: BaseSystem = hydra.utils.instantiate(config.model)\n    datamodule: SceneTextDataModule = hydra.utils.instantiate(config.data)\n\n    tune_callback = TuneReportCheckpointPruneCallback({\n        'loss': 'val_loss',\n        'NED': 'val_NED',\n        'accuracy': 'val_accuracy'\n    })\n    ckpt_path = None if checkpoint_dir is None else os.path.join(checkpoint_dir, 'checkpoint')\n    trainer: Trainer = hydra.utils.instantiate(config.trainer, enable_progress_bar=False, enable_checkpointing=False,\n                                               logger=TensorBoardLogger(save_dir=tune.get_trial_dir(), name='',\n                                                                        version='.'),\n                                               callbacks=[tune_callback])\n    trainer.fit(model, datamodule=datamodule, ckpt_path=ckpt_path)\n\n\n@hydra.main(config_path='configs', config_name='tune', version_base='1.2')\ndef main(config: DictConfig):\n    # Special handling for PARseq\n    if config.model.get('perm_mirrored', False):\n        assert config.model.perm_num % 2 == 0, 'perm_num should be even if perm_mirrored = True'\n    # Modify config\n    with open_dict(config):\n        # Use mixed-precision training\n        if config.trainer.get('gpus', 0):\n            config.trainer.precision = 16\n        # Resolve absolute path to data.root_dir\n        config.data.root_dir = hydra.utils.to_absolute_path(config.data.root_dir)\n\n    hparams = {\n        'lr': tune.loguniform(config.tune.lr.min, config.tune.lr.max),\n        # 'wd': tune.loguniform(config.tune.wd.min, config.tune.wd.max),\n    }\n\n    steps_per_epoch = len(hydra.utils.instantiate(config.data).train_dataloader())\n    val_steps = steps_per_epoch * config.trainer.max_epochs / config.trainer.val_check_interval\n    max_t = round(0.75 * val_steps)\n    warmup_t = round(config.model.warmup_pct * val_steps)\n    scheduler = MedianStoppingRule(time_attr='training_iteration', grace_period=warmup_t)\n\n    # Always start by evenly diving the range in log scale.\n    lr = hparams['lr']\n    start = np.log10(lr.lower)\n    stop = np.log10(lr.upper)\n    num = math.ceil(stop - start) + 1\n    initial_points = [{'lr': np.clip(x, lr.lower, lr.upper).item()} for x in reversed(np.logspace(start, stop, num))]\n    search_alg = AxSearch(points_to_evaluate=initial_points)\n\n    reporter = CLIReporter(\n        parameter_columns=['lr'],\n        metric_columns=['loss', 'accuracy', 'training_iteration'])\n\n    out_dir = Path(HydraConfig.get().runtime.output_dir if config.tune.resume_dir is None else config.tune.resume_dir)\n\n    analysis = tune.run(\n        tune.with_parameters(train, config=config),\n        name=out_dir.name,\n        metric='NED',\n        mode='max',\n        stop=MetricTracker('NED', max_t),\n        config=hparams,\n        resources_per_trial={\n            'cpu': 1,\n            'gpu': config.tune.gpus_per_trial\n        },\n        num_samples=config.tune.num_samples,\n        local_dir=str(out_dir.parent.absolute()),\n        search_alg=search_alg,\n        scheduler=scheduler,\n        progress_reporter=reporter,\n        resume=config.tune.resume_dir is not None,\n        trial_executor=RayTrialExecutor(result_buffer_length=0)  # disable result buffering\n    )\n\n    print('Best hyperparameters found were: ', analysis.best_config)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "test.py",
    "content": "import torch\nimport random\nimport numpy as np\nimport os\n\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom contextlib import nullcontext\nfrom os.path import join as ospj\nfrom torchvision.utils import save_image\nfrom omegaconf import OmegaConf\nfrom pytorch_lightning import seed_everything\nfrom dataset.dataloader import get_dataloader\n\nfrom util import *\nfrom metrics import calc_fid, calc_lpips\n\n\ndef predict(cfgs, model, sampler, batch):\n\n    context = nullcontext if cfgs.aae_enabled else torch.no_grad\n    \n    with context():\n        \n        batch, batch_uc_1 = prepare_batch(cfgs, batch)\n\n        c, uc_1 = model.conditioner.get_unconditional_conditioning(\n            batch,\n            batch_uc=batch_uc_1,\n            force_uc_zero_embeddings=cfgs.force_uc_zero_embeddings,\n        )\n        \n        x = sampler.get_init_noise(cfgs, model, cond=c, batch=batch, uc=uc_1)\n        samples_z = sampler(model, x, cond=c, batch=batch, uc=uc_1, init_step=0,\n                            aae_enabled = cfgs.aae_enabled, detailed = cfgs.detailed)\n\n        samples_x = model.decode_first_stage(samples_z)\n        samples = torch.clamp((samples_x + 1.0) / 2.0, min=0.0, max=1.0)\n\n        return samples, samples_z\n\n\ndef test(model, sampler, dataloader, cfgs):\n    \n    output_dir = cfgs.output_dir\n    os.system(f\"rm -rf {output_dir}\")\n    os.makedirs(output_dir, exist_ok=True)\n    real_dir = ospj(output_dir, \"real\")\n    fake_dir = ospj(output_dir, \"fake\")\n    os.makedirs(real_dir, exist_ok=True)\n    os.makedirs(fake_dir, exist_ok=True)\n\n    temp_dir = cfgs.temp_dir\n    os.system(f\"rm -rf {temp_dir}\")\n    os.makedirs(ospj(temp_dir, \"attn_map\"), exist_ok=True)\n    os.makedirs(ospj(temp_dir, \"seg_map\"), exist_ok=True)\n    os.makedirs(ospj(temp_dir, \"inters\"), exist_ok=True)\n\n    if cfgs.ocr_enabled:\n        predictor = instantiate_from_config(cfgs.predictor_config)\n        predictor.parseq = predictor.parseq.to(sampler.device)\n\n        correct_num = 0\n        total_num = 0\n\n    for idx, batch in tqdm(enumerate(dataloader), total=len(dataloader)):\n\n        if idx >= cfgs.max_iter: break\n\n        name = batch[\"name\"][0]\n        results, results_z = predict(cfgs, model, sampler, batch)\n\n        # run ocr\n        if cfgs.ocr_enabled:\n            \n            r_bbox = batch[\"r_bbox\"]\n            gt_txt = batch[\"label\"]\n            results_crop = []\n            for i, bbox in enumerate(r_bbox):\n                r_top, r_bottom, r_left, r_right = bbox\n                results_crop.append(results[i, :, r_top:r_bottom, r_left:r_right])\n            pred_txt = predictor.img2txt(results_crop)\n\n            correct_count = sum([int(pred_txt[i].lower()==gt_txt[i].lower()) for i in range(len(gt_txt))])\n            print(f\"Expected text: {batch['label']}\")\n            if correct_count < len(gt_txt):\n                print(f\"\\033[1;31m OCR Result: {pred_txt} \\033[0m\")\n            else:\n                print(f\"\\033[1;32m OCR Result: {pred_txt} \\033[0m\")\n            correct_num += correct_count\n            total_num += len(gt_txt)\n        \n        # save results\n        result = results.cpu().numpy().transpose(0, 2, 3, 1) * 255\n        result = np.concatenate(result, axis = -2)\n\n        outputs = []\n        for key in (\"image\", \"masked\", \"mask\"):\n            if key in batch:\n                output = batch[key]\n                if key != \"mask\":\n                    output = (output + 1.0) / 2.0\n                output = output.cpu().numpy().transpose(0, 2, 3, 1) * 255\n                output = np.concatenate(output, axis = -2)\n                if key == \"mask\":\n                    output = np.tile(output, (1,1,3))\n                outputs.append(output)\n\n        outputs.append(result)\n        real = Image.fromarray(outputs[0].astype(np.uint8))\n        fake = Image.fromarray(outputs[-1].astype(np.uint8))\n        real.save(ospj(output_dir, \"real\", f\"{name}.png\"))\n        fake.save(ospj(output_dir, \"fake\", f\"{name}.png\"))\n\n        output = np.concatenate(outputs, axis = 0)\n        output = Image.fromarray(output.astype(np.uint8))\n        output.save(ospj(output_dir, f\"{name}.png\"))\n\n    if cfgs.ocr_enabled:\n        print(f\"OCR test completed. Mean accuracy: {correct_num/total_num}\")\n    \n    if cfgs.quan_test:\n        calc_fid(fake_dir, real_dir)\n        calc_lpips(fake_dir, real_dir)\n\n\nif __name__ == \"__main__\":\n\n    cfgs = OmegaConf.load(\"./configs/test.yaml\")\n\n    seed = random.randint(0, 2147483647)\n    seed_everything(seed)\n\n    model = init_model(cfgs)\n    sampler = init_sampling(cfgs)\n    dataloader = get_dataloader(cfgs, \"val\")\n\n    test(model, sampler, dataloader, cfgs)\n\n"
  },
  {
    "path": "train.py",
    "content": "import os, sys\nimport torch\nimport random\nimport pytorch_lightning as pl\n\nfrom omegaconf import OmegaConf\nfrom dataset.dataloader import get_dataloader\nfrom pytorch_lightning import seed_everything\nfrom pytorch_lightning.callbacks import ModelCheckpoint\nfrom torchvision.utils import save_image\n\nfrom util import *\n\n\ndef train():\n\n    sys.path.append(os.getcwd())\n\n    # torch settings\n    torch.multiprocessing.set_start_method('spawn') # multiprocess mode\n    torch.set_float32_matmul_precision('medium') # matrix multiply precision\n\n    config_path = 'configs/train.yaml'\n    cfgs = OmegaConf.load(config_path)\n\n    seed = random.randint(0, 2147483647)\n    seed_everything(seed, workers=True)\n\n    dataloader = get_dataloader(cfgs)\n    model = init_model(cfgs)\n    model.learning_rate = cfgs.base_learning_rate\n\n    checkpoint_callback = ModelCheckpoint(dirpath = cfgs.save_ckpt_dir, every_n_epochs = cfgs.save_ckpt_freq)\n\n    trainer = pl.Trainer(callbacks = [checkpoint_callback], **cfgs.lightning)\n    trainer.fit(model = model, train_dataloaders = dataloader)\n\n\nif __name__=='__main__':\n\n    train()\n\n\n"
  },
  {
    "path": "util.py",
    "content": "import torch\nfrom omegaconf import OmegaConf\nfrom sgm.util import instantiate_from_config\nfrom sgm.modules.diffusionmodules.sampling import *\n\n\ndef init_model(cfgs):\n\n    model_cfg = OmegaConf.load(cfgs.model_cfg_path)\n    ckpt = cfgs.load_ckpt_path\n\n    model = instantiate_from_config(model_cfg.model)\n    model.init_from_ckpt(ckpt)\n\n    if cfgs.type == \"train\":\n        model.train()\n    else:\n        model.to(torch.device(\"cuda\", index=cfgs.gpu))\n        model.eval()\n        model.freeze()\n\n    return model\n\ndef init_sampling(cfgs):\n\n    discretization_config = {\n        \"target\": \"sgm.modules.diffusionmodules.discretizer.LegacyDDPMDiscretization\",\n    }\n\n    guider_config = {\n        \"target\": \"sgm.modules.diffusionmodules.guiders.VanillaCFG\",\n        \"params\": {\"scale\": cfgs.scale[0]},\n    }\n\n    sampler = EulerEDMSampler(\n        num_steps=cfgs.steps,\n        discretization_config=discretization_config,\n        guider_config=guider_config,\n        s_churn=0.0,\n        s_tmin=0.0,\n        s_tmax=999.0,\n        s_noise=1.0,\n        verbose=True,\n        device=torch.device(\"cuda\", index=cfgs.gpu)\n    )\n\n    return sampler\n\ndef deep_copy(batch):\n\n    c_batch = {}\n    for key in batch:\n        if isinstance(batch[key], torch.Tensor):\n            c_batch[key] = torch.clone(batch[key])\n        elif isinstance(batch[key], (tuple, list)): \n            c_batch[key] = batch[key].copy()\n        else:\n            c_batch[key] = batch[key]\n    \n    return c_batch\n\ndef prepare_batch(cfgs, batch):\n\n    for key in batch:\n        if isinstance(batch[key], torch.Tensor):\n            batch[key] = batch[key].to(torch.device(\"cuda\", index=cfgs.gpu))\n\n    batch_uc = deep_copy(batch)\n\n    if \"ntxt\" in batch:\n        batch_uc[\"txt\"] = batch[\"ntxt\"]\n    else:\n        batch_uc[\"txt\"] = [\"\" for _ in range(len(batch[\"txt\"]))]\n\n    if \"label\" in batch:\n        batch_uc[\"label\"] = [\"\" for _ in range(len(batch[\"label\"]))]\n\n    return batch, batch_uc"
  }
]