├── GOODBYE_LAVER.ipynb
├── README.md
├── hent-AI
├── ColabESRGAN
│ ├── LICENSE
│ ├── QA.md
│ ├── architecture.py
│ ├── block.py
│ ├── models
│ │ └── README.md
│ ├── net_interp.py
│ └── test.py
├── LICENSE
├── README.md
├── detector.py
├── green_mask_project_mosaic_resolution.py
├── main.py
├── mrcnn
│ ├── __init__.py
│ ├── config.py
│ ├── model.py
│ ├── parallel_model.py
│ ├── utils.py
│ └── visualize.py
├── requirements.txt
├── samples
│ └── hentai
│ │ └── hentai.py
├── setup.cfg
├── setup.py
└── test_combined_generator.py
├── inpainting.py
├── lama.py
└── lama_cleaner
├── Dockerfile
├── LICENSE
├── README.md
├── __init__.py
├── app
├── .env
├── .eslintrc.json
├── .gitignore
├── .prettierrc
├── LICENSE
├── build
│ ├── asset-manifest.json
│ ├── index.html
│ └── static
│ │ ├── css
│ │ └── main.4201b632.chunk.css
│ │ ├── js
│ │ ├── 2.2d367d07.chunk.js
│ │ ├── 2.2d367d07.chunk.js.LICENSE.txt
│ │ ├── main.18cd2cfc.chunk.js
│ │ └── runtime-main.5e86ac81.js
│ │ └── media
│ │ ├── WorkSans-Black.67c2c5a1.ttf
│ │ ├── WorkSans-Bold.2bea7a7f.ttf
│ │ ├── WorkSans-Regular.bb287b89.ttf
│ │ └── WorkSans-SemiBold.1e98db4e.ttf
├── package.json
├── public
│ └── index.html
├── src
│ ├── App.tsx
│ ├── adapters
│ │ └── inpainting.ts
│ ├── components
│ │ ├── Editor
│ │ │ ├── Editor.scss
│ │ │ ├── Editor.tsx
│ │ │ ├── SizeSelector.tsx
│ │ │ └── Slider.tsx
│ │ ├── FileSelect
│ │ │ ├── FileSelect.scss
│ │ │ └── FileSelect.tsx
│ │ ├── Header
│ │ │ ├── Header.scss
│ │ │ ├── Header.tsx
│ │ │ ├── ThemeChanger.scss
│ │ │ └── ThemeChanger.tsx
│ │ ├── LandingPage
│ │ │ ├── LandingPage.scss
│ │ │ └── LandingPage.tsx
│ │ ├── Settings
│ │ │ ├── HDSettingBlock.scss
│ │ │ ├── HDSettingBlock.tsx
│ │ │ ├── ManualRunInpaintingSettingBlock.tsx
│ │ │ ├── ModelSettingBlock.scss
│ │ │ ├── ModelSettingBlock.tsx
│ │ │ ├── NumberInputSetting.tsx
│ │ │ ├── SettingBlock.scss
│ │ │ ├── SettingBlock.tsx
│ │ │ ├── SettingIcon.tsx
│ │ │ ├── Settings.scss
│ │ │ └── SettingsModal.tsx
│ │ ├── Shortcuts
│ │ │ ├── Shortcuts.scss
│ │ │ ├── Shortcuts.tsx
│ │ │ └── ShortcutsModal.tsx
│ │ ├── Workspace.tsx
│ │ └── shared
│ │ │ ├── Button.scss
│ │ │ ├── Button.tsx
│ │ │ ├── Link.tsx
│ │ │ ├── Modal.scss
│ │ │ ├── Modal.tsx
│ │ │ ├── NumberInput.scss
│ │ │ ├── NumberInput.tsx
│ │ │ ├── Selector.scss
│ │ │ ├── Selector.tsx
│ │ │ ├── Switch.scss
│ │ │ ├── Switch.tsx
│ │ │ ├── Toast.scss
│ │ │ └── Toast.tsx
│ ├── hooks
│ │ ├── useInputImage.tsx
│ │ └── useResolution.tsx
│ ├── index.tsx
│ ├── media
│ │ └── fonts
│ │ │ └── Work_Sans
│ │ │ ├── WorkSans-Black.ttf
│ │ │ ├── WorkSans-BlackItalic.ttf
│ │ │ ├── WorkSans-Bold.ttf
│ │ │ ├── WorkSans-BoldItalic.ttf
│ │ │ ├── WorkSans-ExtraBold.ttf
│ │ │ ├── WorkSans-ExtraBoldItalic.ttf
│ │ │ ├── WorkSans-ExtraLight.ttf
│ │ │ ├── WorkSans-ExtraLightItalic.ttf
│ │ │ ├── WorkSans-Italic.ttf
│ │ │ ├── WorkSans-Light.ttf
│ │ │ ├── WorkSans-LightItalic.ttf
│ │ │ ├── WorkSans-Medium.ttf
│ │ │ ├── WorkSans-MediumItalic.ttf
│ │ │ ├── WorkSans-Regular.ttf
│ │ │ ├── WorkSans-SemiBold.ttf
│ │ │ ├── WorkSans-SemiBoldItalic.ttf
│ │ │ ├── WorkSans-Thin.ttf
│ │ │ └── WorkSans-ThinItalic.ttf
│ ├── react-app-env.d.ts
│ ├── setupTests.ts
│ ├── store
│ │ └── Atoms.tsx
│ ├── styles
│ │ ├── App.scss
│ │ ├── Mixins
│ │ │ ├── _MediaQueries.scss
│ │ │ ├── _Mixins.scss
│ │ │ └── _index.scss
│ │ ├── _Animations.scss
│ │ ├── _Colors.scss
│ │ ├── _ColorsDark.scss
│ │ ├── _Fonts.scss
│ │ └── _index.scss
│ └── utils.ts
├── tsconfig.json
└── yarn.lock
├── benchmark.py
├── helper.py
├── model
├── __init__.py
├── __pycache__
│ ├── __init__.cpython-38.pyc
│ ├── base.cpython-38.pyc
│ ├── lama.cpython-38.pyc
│ ├── ldm.cpython-38.pyc
│ └── utils.cpython-38.pyc
├── base.py
├── lama.py
├── ldm.py
└── utils.py
├── model_manager.py
├── parse_args.py
├── publish.sh
├── requirements-dev.txt
├── requirements.txt
├── schema.py
├── server.py
├── setup.py
└── tests
├── __init__.py
├── image.png
├── lama_crop_result.png
├── lama_original_result.png
├── lama_resize_result.png
├── ldm_crop_result.png
├── ldm_original_result.png
├── ldm_resize_result.png
├── mask.png
└── test_model.py
/GOODBYE_LAVER.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": null,
6 | "metadata": {
7 | "id": "95a87y7f8t0z"
8 | },
9 | "outputs": [],
10 | "source": [
11 | "# Clone GoodbyeLaver\n",
12 | "!git clone https://github.com/kdrkdrkdr/GoodbyeLaver\n",
13 | "\n",
14 | "# Install hent-AI\n",
15 | "%cd /content/GoodbyeLaver/hent-AI/\n",
16 | "!wget https://github.com/kdrkdrkdr/GoodbyeLaver/releases/download/1.0/4x_FatalPixels_340000_G.pth\n",
17 | "!wget https://github.com/kdrkdrkdr/GoodbyeLaver/releases/download/1.0/weights.h5\n",
18 | "!pip install --upgrade pip\n",
19 | "!pip install -r requirements.txt\n",
20 | "\n",
21 | "# Downgrade tensorflow and keras\n",
22 | "!pip uninstall tensorflow -y\n",
23 | "!pip uninstall keras -y\n",
24 | "!pip install keras==2.3.1\n",
25 | "!pip install tensorflow-gpu==1.15\n",
26 | "!apt install --allow-change-held-packages libcudnn7=7.4.1.5-1+cuda10.0\n",
27 | "\n",
28 | "!python setup.py install\n",
29 | "!add-apt-repository ppa:jon-severinsson/ffmpeg\n",
30 | "!apt-get update\n",
31 | "!apt-get install ffmpeg\n",
32 | "\n",
33 | "# Install lama_cleaner\n",
34 | "%cd /content/GoodbyeLaver/lama_cleaner/\n",
35 | "!pip install -r requirements.txt\n",
36 | "\n",
37 | "# Make directory\n",
38 | "!mkdir /content/image_original/\n",
39 | "!mkdir /content/image_detected/\n",
40 | "!mkdir /content/image_uncensored/\n",
41 | "\n",
42 | "# Run lama server\n",
43 | "%cd /content/GoodbyeLaver/\n",
44 | "!nohup python lama.py --port 5003 &\n",
45 | "\n",
46 | "# Load CUDA\n",
47 | "%cd /content/GoodbyeLaver/hent-AI/\n",
48 | "from detector import Detector\n",
49 | "d = Detector('weights.h5')"
50 | ]
51 | },
52 | {
53 | "cell_type": "code",
54 | "execution_count": null,
55 | "metadata": {
56 | "id": "FdonSouNSo2l"
57 | },
58 | "outputs": [],
59 | "source": [
60 | "# Run Decensoring!\n",
61 | "# Upload the censored image to the \"/content/image_original/\" folder, and run this cell.\n",
62 | "!rm -rf /content/image_detected/*\n",
63 | "!rm -rf /content/image_uncensored/*\n",
64 | "!rm -rf /content/uncensored_image.zip\n",
65 | "\n",
66 | "# Convert image files to png files.\n",
67 | "import os\n",
68 | "from PIL import Image\n",
69 | "files = os.listdir(\"/content/image_original/\")\n",
70 | "for filename in files:\n",
71 | " file_wo_ext, file_ext = os.path.splitext(filename)\n",
72 | " if file_ext.lower() != \".png\":\n",
73 | " img_path = f'/content/image_original/{filename}'\n",
74 | " Image.open(img_path).save(f'/content/image_original/{file_wo_ext}.png', 'PNG')\n",
75 | " os.remove(img_path) \n",
76 | "\n",
77 | "# Detecting bar censored\n",
78 | "%cd /content/GoodbyeLaver/hent-AI/\n",
79 | "d.run_on_folder(\n",
80 | " input_folder='/content/image_original/',\n",
81 | " output_folder='/content/image_detected/', \n",
82 | " is_video=False, \n",
83 | " is_mosaic=False,\n",
84 | " dilation=3\n",
85 | ")\n",
86 | "\n",
87 | "# Inpainting image\n",
88 | "%cd /content/GoodbyeLaver/\n",
89 | "!python inpainting.py\n",
90 | "\n",
91 | "# Download Uncensored Image\n",
92 | "!zip -r /content/uncensored_image.zip /content/image_uncensored/\n",
93 | "from google.colab import files\n",
94 | "files.download(\"/content/uncensored_image.zip\")"
95 | ]
96 | },
97 | {
98 | "cell_type": "code",
99 | "execution_count": 6,
100 | "metadata": {
101 | "id": "cRm-J9-nofNX"
102 | },
103 | "outputs": [],
104 | "source": [
105 | "# Run this cell if you want to delete all uploaded images.\n",
106 | "!rm -rf /content/image_original/*"
107 | ]
108 | }
109 | ],
110 | "metadata": {
111 | "accelerator": "GPU",
112 | "colab": {
113 | "collapsed_sections": [],
114 | "name": "hentai.ipynb",
115 | "provenance": []
116 | },
117 | "kernelspec": {
118 | "display_name": "Python 3",
119 | "name": "python3"
120 | },
121 | "language_info": {
122 | "name": "python"
123 | }
124 | },
125 | "nbformat": 4,
126 | "nbformat_minor": 0
127 | }
128 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GoodbyeLaver
2 | Laver means censored bar.
3 |
4 | ### Project Purpose
5 | This project is designed to automate the process of eliminating censorship through lama-inpainting and hent-AI.
6 |
7 | ### Usage
8 | You must set runtime as GPU.
9 | Open the [GOODBYE_LAVER.ipynb](https://colab.research.google.com/drive/1MJ6w3nCzVfAX2bwEdxkPfgzQgAa12oFu?usp=sharing) file in colab.
--------------------------------------------------------------------------------
/hent-AI/ColabESRGAN/QA.md:
--------------------------------------------------------------------------------
1 | # Frequently Asked Questions
2 |
3 | ### 1. How to reproduce your results in the [PIRM18-SR Challenge](https://www.pirm2018.org/PIRM-SR.html) (with low perceptual index)?
4 |
5 | First, the released ESRGAN model in the GitHub (`RRDB_ESRGAN_x4.pth`) is **different** from the model we submitted in the competition.
6 | We found that the lower perceptual index does not always guarantee a better visual quality.
7 | The aims for the competition and our ESRGAN work will be a bit different.
8 | We think the aim for the competition is the lower perceptual index and the aim for our ESRGAN work is the better visual quality.
9 | > More analyses can be found in Sec 4.1 and Sec 5 in [PIRM18-SR Chanllenge report](https://arxiv.org/pdf/1809.07517.pdf).
10 | > It points out that PI (perceptual index) is well correlated with the human-opinion-scores on a coarse scale, but it is not always well-correlated with these scores on a finer scale. This highlights the urgent need for better perceptual quality metrics.)
11 |
12 | Therefore, in the PIRM18-SR Challenge competition, we used several tricks for the best perceptual index (see Section 4.5 in the [paper](https://arxiv.org/abs/1809.00219)).
13 |
14 | Here, we provid the models and codes used in the competition, which is able to produce the results on the `PIRM test dataset` (we use MATLAB 2016b/2017a):
15 |
16 | | Group | Perceptual index | RMSE |
17 | | ------------- |:-------------:| -----:|
18 | | SuperSR | 1.978 | 15.30 |
19 |
20 | > 1. Download the model and codes from [GoogleDrive](https://drive.google.com/file/d/1l0gBRMqhVLpL_-7R7aN-q-3hnv5ADFSM/view?usp=sharing)
21 | > 2. Put LR input images in the `LR` folder
22 | > 3. Run `python test.py`
23 | > 4. Run `main_reverse_filter.m` in MATLAB as a post processing
24 | > 5. The results on my computer are: Perceptual index: **1.9777** and RMSE: **15.304**
25 |
26 |
27 | ### 2. How do you get the perceptual index in your ESRGAN paper?
28 | In our paper, we provide the perceptual index in two places.
29 |
30 | 1). In the Fig. 2, the perceptual index on PIRM self validation dataset is obtained with the **model we submitted in the competition**.
31 | Since the pupose of this figure is to show the perception-distortion plane. And we also use the post-precessing here same as in the competition.
32 |
33 | 2). In the Fig.7, the perceptual indexs are provided as references and they are tested on the data generated by the released ESRGAN model `RRDB_ESRGAN_x4.pth` in the GiuHub.
34 | Also, there is **no** post-processing when testing the ESRGAN model for better visual quality.
35 |
--------------------------------------------------------------------------------
/hent-AI/ColabESRGAN/architecture.py:
--------------------------------------------------------------------------------
1 | import math
2 | import torch
3 | import torch.nn as nn
4 | import block as B
5 |
6 |
7 | class RRDB_Net(nn.Module):
8 | def __init__(self, in_nc, out_nc, nf, nb, gc=32, upscale=4, norm_type=None, act_type='leakyrelu', \
9 | mode='CNA', res_scale=1, upsample_mode='upconv'):
10 | super(RRDB_Net, self).__init__()
11 | n_upscale = int(math.log(upscale, 2))
12 | if upscale == 3:
13 | n_upscale = 1
14 |
15 | fea_conv = B.conv_block(in_nc, nf, kernel_size=3, norm_type=None, act_type=None)
16 | rb_blocks = [B.RRDB(nf, kernel_size=3, gc=32, stride=1, bias=True, pad_type='zero', \
17 | norm_type=norm_type, act_type=act_type, mode='CNA') for _ in range(nb)]
18 | LR_conv = B.conv_block(nf, nf, kernel_size=3, norm_type=norm_type, act_type=None, mode=mode)
19 |
20 | if upsample_mode == 'upconv':
21 | upsample_block = B.upconv_blcok
22 | elif upsample_mode == 'pixelshuffle':
23 | upsample_block = B.pixelshuffle_block
24 | else:
25 | raise NotImplementedError('upsample mode [%s] is not found' % upsample_mode)
26 | if upscale == 3:
27 | upsampler = upsample_block(nf, nf, 3, act_type=act_type)
28 | else:
29 | upsampler = [upsample_block(nf, nf, act_type=act_type) for _ in range(n_upscale)]
30 | HR_conv0 = B.conv_block(nf, nf, kernel_size=3, norm_type=None, act_type=act_type)
31 | HR_conv1 = B.conv_block(nf, out_nc, kernel_size=3, norm_type=None, act_type=None)
32 |
33 | self.model = B.sequential(fea_conv, B.ShortcutBlock(B.sequential(*rb_blocks, LR_conv)),\
34 | *upsampler, HR_conv0, HR_conv1)
35 |
36 | def forward(self, x):
37 | x = self.model(x)
38 | return x
39 |
--------------------------------------------------------------------------------
/hent-AI/ColabESRGAN/block.py:
--------------------------------------------------------------------------------
1 | from collections import OrderedDict
2 | import torch
3 | import torch.nn as nn
4 |
5 | ####################
6 | # Basic blocks
7 | ####################
8 |
9 |
10 | def act(act_type, inplace=True, neg_slope=0.2, n_prelu=1):
11 | # helper selecting activation
12 | # neg_slope: for leakyrelu and init of prelu
13 | # n_prelu: for p_relu num_parameters
14 | act_type = act_type.lower()
15 | if act_type == 'relu':
16 | layer = nn.ReLU(inplace)
17 | elif act_type == 'leakyrelu':
18 | layer = nn.LeakyReLU(neg_slope, inplace)
19 | elif act_type == 'prelu':
20 | layer = nn.PReLU(num_parameters=n_prelu, init=neg_slope)
21 | else:
22 | raise NotImplementedError('activation layer [%s] is not found' % act_type)
23 | return layer
24 |
25 |
26 | def norm(norm_type, nc):
27 | # helper selecting normalization layer
28 | norm_type = norm_type.lower()
29 | if norm_type == 'batch':
30 | layer = nn.BatchNorm2d(nc, affine=True)
31 | elif norm_type == 'instance':
32 | layer = nn.InstanceNorm2d(nc, affine=False)
33 | else:
34 | raise NotImplementedError('normalization layer [%s] is not found' % norm_type)
35 | return layer
36 |
37 |
38 | def pad(pad_type, padding):
39 | # helper selecting padding layer
40 | # if padding is 'zero', do by conv layers
41 | pad_type = pad_type.lower()
42 | if padding == 0:
43 | return None
44 | if pad_type == 'reflect':
45 | layer = nn.ReflectionPad2d(padding)
46 | elif pad_type == 'replicate':
47 | layer = nn.ReplicationPad2d(padding)
48 | else:
49 | raise NotImplementedError('padding layer [%s] is not implemented' % pad_type)
50 | return layer
51 |
52 |
53 | def get_valid_padding(kernel_size, dilation):
54 | kernel_size = kernel_size + (kernel_size - 1) * (dilation - 1)
55 | padding = (kernel_size - 1) // 2
56 | return padding
57 |
58 |
59 | class ConcatBlock(nn.Module):
60 | # Concat the output of a submodule to its input
61 | def __init__(self, submodule):
62 | super(ConcatBlock, self).__init__()
63 | self.sub = submodule
64 |
65 | def forward(self, x):
66 | output = torch.cat((x, self.sub(x)), dim=1)
67 | return output
68 |
69 | def __repr__(self):
70 | tmpstr = 'Identity .. \n|'
71 | modstr = self.sub.__repr__().replace('\n', '\n|')
72 | tmpstr = tmpstr + modstr
73 | return tmpstr
74 |
75 |
76 | class ShortcutBlock(nn.Module):
77 | #Elementwise sum the output of a submodule to its input
78 | def __init__(self, submodule):
79 | super(ShortcutBlock, self).__init__()
80 | self.sub = submodule
81 |
82 | def forward(self, x):
83 | output = x + self.sub(x)
84 | return output
85 |
86 | def __repr__(self):
87 | tmpstr = 'Identity + \n|'
88 | modstr = self.sub.__repr__().replace('\n', '\n|')
89 | tmpstr = tmpstr + modstr
90 | return tmpstr
91 |
92 |
93 | def sequential(*args):
94 | # Flatten Sequential. It unwraps nn.Sequential.
95 | if len(args) == 1:
96 | if isinstance(args[0], OrderedDict):
97 | raise NotImplementedError('sequential does not support OrderedDict input.')
98 | return args[0] # No sequential is needed.
99 | modules = []
100 | for module in args:
101 | if isinstance(module, nn.Sequential):
102 | for submodule in module.children():
103 | modules.append(submodule)
104 | elif isinstance(module, nn.Module):
105 | modules.append(module)
106 | return nn.Sequential(*modules)
107 |
108 |
109 | def conv_block(in_nc, out_nc, kernel_size, stride=1, dilation=1, groups=1, bias=True,
110 | pad_type='zero', norm_type=None, act_type='relu', mode='CNA'):
111 | """
112 | Conv layer with padding, normalization, activation
113 | mode: CNA --> Conv -> Norm -> Act
114 | NAC --> Norm -> Act --> Conv (Identity Mappings in Deep Residual Networks, ECCV16)
115 | """
116 | assert mode in ['CNA', 'NAC', 'CNAC'], 'Wong conv mode [%s]' % mode
117 | padding = get_valid_padding(kernel_size, dilation)
118 | p = pad(pad_type, padding) if pad_type and pad_type != 'zero' else None
119 | padding = padding if pad_type == 'zero' else 0
120 |
121 | c = nn.Conv2d(in_nc, out_nc, kernel_size=kernel_size, stride=stride, padding=padding, \
122 | dilation=dilation, bias=bias, groups=groups)
123 | a = act(act_type) if act_type else None
124 | if 'CNA' in mode:
125 | n = norm(norm_type, out_nc) if norm_type else None
126 | return sequential(p, c, n, a)
127 | elif mode == 'NAC':
128 | if norm_type is None and act_type is not None:
129 | a = act(act_type, inplace=False)
130 | # Important!
131 | # input----ReLU(inplace)----Conv--+----output
132 | # |________________________|
133 | # inplace ReLU will modify the input, therefore wrong output
134 | n = norm(norm_type, in_nc) if norm_type else None
135 | return sequential(n, a, p, c)
136 |
137 |
138 | ####################
139 | # Useful blocks
140 | ####################
141 |
142 |
143 | class ResNetBlock(nn.Module):
144 | """
145 | ResNet Block, 3-3 style
146 | with extra residual scaling used in EDSR
147 | (Enhanced Deep Residual Networks for Single Image Super-Resolution, CVPRW 17)
148 | """
149 |
150 | def __init__(self, in_nc, mid_nc, out_nc, kernel_size=3, stride=1, dilation=1, groups=1, \
151 | bias=True, pad_type='zero', norm_type=None, act_type='relu', mode='CNA', res_scale=1):
152 | super(ResNetBlock, self).__init__()
153 | conv0 = conv_block(in_nc, mid_nc, kernel_size, stride, dilation, groups, bias, pad_type, \
154 | norm_type, act_type, mode)
155 | if mode == 'CNA':
156 | act_type = None
157 | if mode == 'CNAC': # Residual path: |-CNAC-|
158 | act_type = None
159 | norm_type = None
160 | conv1 = conv_block(mid_nc, out_nc, kernel_size, stride, dilation, groups, bias, pad_type, \
161 | norm_type, act_type, mode)
162 | # if in_nc != out_nc:
163 | # self.project = conv_block(in_nc, out_nc, 1, stride, dilation, 1, bias, pad_type, \
164 | # None, None)
165 | # print('Need a projecter in ResNetBlock.')
166 | # else:
167 | # self.project = lambda x:x
168 | self.res = sequential(conv0, conv1)
169 | self.res_scale = res_scale
170 |
171 | def forward(self, x):
172 | res = self.res(x).mul(self.res_scale)
173 | return x + res
174 |
175 |
176 | class ResidualDenseBlock_5C(nn.Module):
177 | """
178 | Residual Dense Block
179 | style: 5 convs
180 | The core module of paper: (Residual Dense Network for Image Super-Resolution, CVPR 18)
181 | """
182 |
183 | def __init__(self, nc, kernel_size=3, gc=32, stride=1, bias=True, pad_type='zero', \
184 | norm_type=None, act_type='leakyrelu', mode='CNA'):
185 | super(ResidualDenseBlock_5C, self).__init__()
186 | # gc: growth channel, i.e. intermediate channels
187 | self.conv1 = conv_block(nc, gc, kernel_size, stride, bias=bias, pad_type=pad_type, \
188 | norm_type=norm_type, act_type=act_type, mode=mode)
189 | self.conv2 = conv_block(nc+gc, gc, kernel_size, stride, bias=bias, pad_type=pad_type, \
190 | norm_type=norm_type, act_type=act_type, mode=mode)
191 | self.conv3 = conv_block(nc+2*gc, gc, kernel_size, stride, bias=bias, pad_type=pad_type, \
192 | norm_type=norm_type, act_type=act_type, mode=mode)
193 | self.conv4 = conv_block(nc+3*gc, gc, kernel_size, stride, bias=bias, pad_type=pad_type, \
194 | norm_type=norm_type, act_type=act_type, mode=mode)
195 | if mode == 'CNA':
196 | last_act = None
197 | else:
198 | last_act = act_type
199 | self.conv5 = conv_block(nc+4*gc, nc, 3, stride, bias=bias, pad_type=pad_type, \
200 | norm_type=norm_type, act_type=last_act, mode=mode)
201 |
202 | def forward(self, x):
203 | x1 = self.conv1(x)
204 | x2 = self.conv2(torch.cat((x, x1), 1))
205 | x3 = self.conv3(torch.cat((x, x1, x2), 1))
206 | x4 = self.conv4(torch.cat((x, x1, x2, x3), 1))
207 | x5 = self.conv5(torch.cat((x, x1, x2, x3, x4), 1))
208 | return x5.mul(0.2) + x
209 |
210 |
211 | class RRDB(nn.Module):
212 | """
213 | Residual in Residual Dense Block
214 | """
215 |
216 | def __init__(self, nc, kernel_size=3, gc=32, stride=1, bias=True, pad_type='zero', \
217 | norm_type=None, act_type='leakyrelu', mode='CNA'):
218 | super(RRDB, self).__init__()
219 | self.RDB1 = ResidualDenseBlock_5C(nc, kernel_size, gc, stride, bias, pad_type, \
220 | norm_type, act_type, mode)
221 | self.RDB2 = ResidualDenseBlock_5C(nc, kernel_size, gc, stride, bias, pad_type, \
222 | norm_type, act_type, mode)
223 | self.RDB3 = ResidualDenseBlock_5C(nc, kernel_size, gc, stride, bias, pad_type, \
224 | norm_type, act_type, mode)
225 |
226 | def forward(self, x):
227 | out = self.RDB1(x)
228 | out = self.RDB2(out)
229 | out = self.RDB3(out)
230 | return out.mul(0.2) + x
231 |
232 |
233 | ####################
234 | # Upsampler
235 | ####################
236 |
237 |
238 | def pixelshuffle_block(in_nc, out_nc, upscale_factor=2, kernel_size=3, stride=1, bias=True,
239 | pad_type='zero', norm_type=None, act_type='relu'):
240 | """
241 | Pixel shuffle layer
242 | (Real-Time Single Image and Video Super-Resolution Using an Efficient Sub-Pixel Convolutional
243 | Neural Network, CVPR17)
244 | """
245 | conv = conv_block(in_nc, out_nc * (upscale_factor ** 2), kernel_size, stride, bias=bias,
246 | pad_type=pad_type, norm_type=None, act_type=None)
247 | pixel_shuffle = nn.PixelShuffle(upscale_factor)
248 |
249 | n = norm(norm_type, out_nc) if norm_type else None
250 | a = act(act_type) if act_type else None
251 | return sequential(conv, pixel_shuffle, n, a)
252 |
253 |
254 | def upconv_blcok(in_nc, out_nc, upscale_factor=2, kernel_size=3, stride=1, bias=True,
255 | pad_type='zero', norm_type=None, act_type='relu', mode='nearest'):
256 | # Up conv
257 | # described in https://distill.pub/2016/deconv-checkerboard/
258 | upsample = nn.Upsample(scale_factor=upscale_factor, mode=mode)
259 | conv = conv_block(in_nc, out_nc, kernel_size, stride, bias=bias,
260 | pad_type=pad_type, norm_type=norm_type, act_type=act_type)
261 | return sequential(upsample, conv)
262 |
--------------------------------------------------------------------------------
/hent-AI/ColabESRGAN/models/README.md:
--------------------------------------------------------------------------------
1 | ## Place pretrained models here.
2 |
3 | We provide two pretrained models:
4 |
5 | 1. `RRDB_ESRGAN_x4.pth`: the final ESRGAN model we used in our [paper](https://arxiv.org/abs/1809.00219).
6 | 2. `RRDB_PSNR_x4.pth`: the PSNR-oriented model with **high PSNR performance**.
7 |
8 | *Note that* the pretrained models are trained under the `MATLAB bicubic` kernel.
9 | If the downsampled kernel is different from that, the results may have artifacts.
10 |
--------------------------------------------------------------------------------
/hent-AI/ColabESRGAN/net_interp.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import torch
3 | from collections import OrderedDict
4 |
5 | alpha = float(sys.argv[1])
6 |
7 | net_PSNR_path = './models/RRDB_PSNR_x4.pth'
8 | net_ESRGAN_path = './models/RRDB_ESRGAN_x4.pth'
9 | net_interp_path = './models/interp_{:02d}.pth'.format(int(alpha*10))
10 |
11 | net_PSNR = torch.load(net_PSNR_path)
12 | net_ESRGAN = torch.load(net_ESRGAN_path)
13 | net_interp = OrderedDict()
14 |
15 | print('Interpolating with alpha = ', alpha)
16 |
17 | for k, v_PSNR in net_PSNR.items():
18 | v_ESRGAN = net_ESRGAN[k]
19 | net_interp[k] = (1 - alpha) * v_PSNR + alpha * v_ESRGAN
20 |
21 | torch.save(net_interp, net_interp_path)
22 |
--------------------------------------------------------------------------------
/hent-AI/ColabESRGAN/test.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os.path
3 | import cv2
4 | import numpy as np
5 | import torch
6 | import architecture
7 | import math
8 |
9 | # ESRGAN class allows abstraction of warmup and inference.
10 | class esrgan():
11 | # hw = cpu, or cuda
12 | def __init__(self, model_path=None, hw='cpu'):
13 | assert model_path
14 | if hw=='cpu':
15 | self.device = torch.device('cpu')
16 | if hw=='cuda':
17 | self.device = torch.device('cuda')
18 | self.model = architecture.RRDB_Net(3, 3, 64, 23, gc=32, upscale=4, norm_type=None, act_type='leakyrelu', \
19 | mode='CNA', res_scale=1, upsample_mode='upconv')
20 | self.model.load_state_dict(torch.load(model_path), strict=True)
21 | self.model.eval()
22 | for k, v in self.model.named_parameters():
23 | v.requires_grad = False
24 | self.model = self.model.to(self.device)
25 | print('Model warmup complete')
26 |
27 | # Function to run esrgan on single image, and single output.
28 | def run_esrgan(self, test_img_folder=None, out_filename=None, mosaic_res=1):
29 | assert out_filename
30 | assert test_img_folder
31 |
32 | img = cv2.imdecode(np.fromfile(test_img_folder, np.uint8), cv2.IMREAD_UNCHANGED)
33 | # from alex: codes to shrink image if memory is an issue
34 | # GPUmem = torch.cuda.get_device_properties(0).total_memory
35 | # Sx = int(1.2*img.shape[0]/mosaic_res)
36 | # Sy = int(1.2*img.shape[1]/mosaic_res)
37 | # maxres = math.sqrt((Sx*Sy)/(GPUmem*0.00008))
38 | # if maxres < 1:
39 | # maxres = 1
40 | # img = cv2.resize(img, (int(Sx/maxres),int(Sy/maxres)))
41 |
42 | img = img * 1.0 / 255
43 | img = torch.from_numpy(np.transpose(img[:, :, [2, 1, 0]], (2, 0, 1))).float()
44 | img_LR = img.unsqueeze(0)
45 |
46 | # image to device
47 | img_LR = img_LR.to(self.device)
48 |
49 | output = self.model(img_LR).data.squeeze().float().cpu().clamp_(0, 1).numpy()
50 | output = np.transpose(output[[2, 1, 0], :, :], (1, 2, 0))
51 | output = (output * 255.0).round()
52 | # cv2.imwrite(out_filename, output)
53 | is_success, im_buf_arr = cv2.imencode(".png", output)
54 | im_buf_arr.tofile(out_filename)
55 |
--------------------------------------------------------------------------------
/hent-AI/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Nate
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/hent-AI/green_mask_project_mosaic_resolution.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import numpy as np
3 | import os
4 | import glob
5 | from scipy.signal import argrelextrema
6 | from PIL import Image
7 |
8 | # Conversion from file based script to individual image usage
9 | def get_mosaic_res(root_img=None):
10 | # assert root_img
11 | #You can change those folder paths
12 | # os.makedirs(root_img, exist_ok=True)
13 |
14 | # files = glob.glob(rootdir + '/**/*.png', recursive=True)
15 | # files_jpg = glob.glob(rootdir + '/**/*.jpg', recursive=True)
16 | # files.extend(files_jpg)
17 | f = root_img # use input image path
18 | #-----------------------Logic-----------------------
19 | GBlur = 5
20 | CannyTr1 = 20
21 | CannyTr2 = 100
22 | LowRange = 2
23 | HighRange = 24
24 | DetectionTr = 0.32
25 |
26 | pattern = [None] * (HighRange+2)
27 | for masksize in range(HighRange+2, LowRange+1, -1):
28 | maskimg = 2+masksize+masksize-1+2
29 | screen = (maskimg, maskimg)
30 | img = Image.new('RGB', screen, (255,255,255))
31 | pix = img.load()
32 | for i in range(2,maskimg,masksize-1):
33 | for j in range(2,maskimg,masksize-1):
34 | for k in range(0,maskimg):
35 | pix[i, k] = (0,0,0)
36 | pix[k, j] = (0,0,0)
37 | pattern[masksize-2] = img
38 |
39 | #Working with files
40 | # for f in files:
41 | #-----------------------Files-----------------------
42 | img_C = Image.fromarray(f).convert("RGBA")
43 | x, y = img_C.size
44 | card = Image.new("RGBA", (x, y), (255, 255, 255, 0))
45 | cvI = Image.alpha_composite(card, img_C)
46 | cvI = np.array(cvI)
47 | img_rgb = cv2.cvtColor(cvI, cv2.COLOR_BGRA2RGBA)
48 |
49 | img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
50 | img_gray = cv2.Canny(img_gray,CannyTr1,CannyTr2)
51 | img_gray = 255-img_gray
52 | img_gray = cv2.GaussianBlur(img_gray,(GBlur,GBlur),0)
53 |
54 | #-----------------------Detection-----------------------
55 | resolutions = [-1] * (HighRange+2)
56 | for masksize in range(HighRange+2, LowRange+1, -1):
57 | template = cv2.cvtColor(np.array(pattern[masksize-2]), cv2.COLOR_BGR2GRAY)
58 | w, h = pattern[masksize-2].size[::-1]
59 |
60 | img_detection = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
61 | loc = np.where(img_detection >= DetectionTr)
62 | rects = 0
63 | for pt in zip(*loc[::-1]):
64 | rects += 1 #increase rectangle count of single resolution
65 | # cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,255,0), 1) #DEBUG To see regions on image
66 | resolutions[masksize-1] = rects
67 |
68 | resolutions.append(0)
69 | # print(resolutions) #DEBUG Resolutions array
70 | extremaMIN = argrelextrema(np.array(resolutions), np.less, axis=0)[0]
71 | extremaMIN = np.insert(extremaMIN,0,LowRange)
72 | extremaMIN = np.append(extremaMIN,HighRange+2)
73 |
74 | Extremas = []
75 | for i, ExtGroup in enumerate(extremaMIN[:-1]):
76 | Extremas.append((ExtGroup, resolutions[extremaMIN[i]:extremaMIN[i+1]+1]))
77 |
78 | ExtremasSum = []
79 | BigExtrema = [0,0,[0,0]]
80 | for i, _ in enumerate(Extremas):
81 | ExtremasSum.append(sum(Extremas[i][1]))
82 | if BigExtrema[0] <= sum(Extremas[i][1])+int(sum(Extremas[i][1])*0.05): #5% precedency for smaller resolution
83 | BigExtrema = [sum(Extremas[i][1]),Extremas[i][0],Extremas[i][1]]
84 | MosaicResolutionOfImage = BigExtrema[1]+BigExtrema[2].index(max(BigExtrema[2])) #Output
85 | if MosaicResolutionOfImage == 0: #If nothing found - set resolution as smallest
86 | MosaicResolutionOfImage = HighRange+1
87 | # print('Mosaic Resolution of "' + os.path.basename(f) + '" is: ' + str(MosaicResolutionOfImage)) #The Resolution of Mosaiced Image
88 | return MosaicResolutionOfImage
89 |
90 | #DEBUG Show image
91 | # cv2.imshow('image',img_rgb)
92 | # cv2.waitKey(0)
93 | # cv2.destroyAllWindows()
94 |
--------------------------------------------------------------------------------
/hent-AI/mrcnn/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/hent-AI/mrcnn/config.py:
--------------------------------------------------------------------------------
1 | """
2 | Mask R-CNN
3 | Base Configurations class.
4 |
5 | Copyright (c) 2017 Matterport, Inc.
6 | Licensed under the MIT License (see LICENSE for details)
7 | Written by Waleed Abdulla
8 | """
9 |
10 | import numpy as np
11 |
12 |
13 | # Base Configuration Class
14 | # Don't use this class directly. Instead, sub-class it and override
15 | # the configurations you need to change.
16 |
17 | class Config(object):
18 | """Base configuration class. For custom configurations, create a
19 | sub-class that inherits from this one and override properties
20 | that need to be changed.
21 | """
22 | # Name the configurations. For example, 'COCO', 'Experiment 3', ...etc.
23 | # Useful if your code needs to do things differently depending on which
24 | # experiment is running.
25 | NAME = None # Override in sub-classes
26 |
27 | # NUMBER OF GPUs to use. When using only a CPU, this needs to be set to 1.
28 | GPU_COUNT = 1
29 |
30 | # Number of images to train with on each GPU. A 12GB GPU can typically
31 | # handle 2 images of 1024x1024px.
32 | # Adjust based on your GPU memory and image sizes. Use the highest
33 | # number that your GPU can handle for best performance.
34 | IMAGES_PER_GPU = 1
35 |
36 | # Number of training steps per epoch
37 | # This doesn't need to match the size of the training set. Tensorboard
38 | # updates are saved at the end of each epoch, so setting this to a
39 | # smaller number means getting more frequent TensorBoard updates.
40 | # Validation stats are also calculated at each epoch end and they
41 | # might take a while, so don't set this too small to avoid spending
42 | # a lot of time on validation stats.
43 | STEPS_PER_EPOCH = 1000
44 |
45 | # Number of validation steps to run at the end of every training epoch.
46 | # A bigger number improves accuracy of validation stats, but slows
47 | # down the training.
48 | VALIDATION_STEPS = 150
49 |
50 | # Backbone network architecture
51 | # Supported values are: resnet50, resnet101.
52 | # You can also provide a callable that should have the signature
53 | # of model.resnet_graph. If you do so, you need to supply a callable
54 | # to COMPUTE_BACKBONE_SHAPE as well
55 | BACKBONE = "resnet101"
56 |
57 | # Only useful if you supply a callable to BACKBONE. Should compute
58 | # the shape of each layer of the FPN Pyramid.
59 | # See model.compute_backbone_shapes
60 | COMPUTE_BACKBONE_SHAPE = None
61 |
62 | # The strides of each layer of the FPN Pyramid. These values
63 | # are based on a Resnet101 backbone.
64 | BACKBONE_STRIDES = [4, 8, 16, 32, 64]
65 |
66 | # Size of the fully-connected layers in the classification graph
67 | FPN_CLASSIF_FC_LAYERS_SIZE = 1024
68 |
69 | # Size of the top-down layers used to build the feature pyramid
70 | TOP_DOWN_PYRAMID_SIZE = 256
71 |
72 | # Number of classification classes (including background)
73 | NUM_CLASSES = 1 # Override in sub-classes
74 |
75 | # Length of square anchor side in pixels
76 | RPN_ANCHOR_SCALES = (32, 64, 128, 256, 512)
77 |
78 | # Ratios of anchors at each cell (width/height)
79 | # A value of 1 represents a square anchor, and 0.5 is a wide anchor
80 | RPN_ANCHOR_RATIOS = [0.5, 1, 2]
81 |
82 | # Anchor stride
83 | # If 1 then anchors are created for each cell in the backbone feature map.
84 | # If 2, then anchors are created for every other cell, and so on.
85 | RPN_ANCHOR_STRIDE = 1
86 |
87 | # Non-max suppression threshold to filter RPN proposals.
88 | # You can increase this during training to generate more propsals.
89 | RPN_NMS_THRESHOLD = 0.7
90 |
91 | # How many anchors per image to use for RPN training
92 | RPN_TRAIN_ANCHORS_PER_IMAGE = 256
93 |
94 | # ROIs kept after tf.nn.top_k and before non-maximum suppression
95 | PRE_NMS_LIMIT = 6000
96 |
97 | # ROIs kept after non-maximum suppression (training and inference)
98 | POST_NMS_ROIS_TRAINING = 2000
99 | POST_NMS_ROIS_INFERENCE = 2000
100 |
101 | # If enabled, resizes instance masks to a smaller size to reduce
102 | # memory load. Recommended when using high-resolution images.
103 | USE_MINI_MASK = True
104 | MINI_MASK_SHAPE = (56, 56) # (height, width) of the mini-mask
105 |
106 | # Input image resizing
107 | # Generally, use the "square" resizing mode for training and predicting
108 | # and it should work well in most cases. In this mode, images are scaled
109 | # up such that the small side is = IMAGE_MIN_DIM, but ensuring that the
110 | # scaling doesn't make the long side > IMAGE_MAX_DIM. Then the image is
111 | # padded with zeros to make it a square so multiple images can be put
112 | # in one batch.
113 | # Available resizing modes:
114 | # none: No resizing or padding. Return the image unchanged.
115 | # square: Resize and pad with zeros to get a square image
116 | # of size [max_dim, max_dim].
117 | # pad64: Pads width and height with zeros to make them multiples of 64.
118 | # If IMAGE_MIN_DIM or IMAGE_MIN_SCALE are not None, then it scales
119 | # up before padding. IMAGE_MAX_DIM is ignored in this mode.
120 | # The multiple of 64 is needed to ensure smooth scaling of feature
121 | # maps up and down the 6 levels of the FPN pyramid (2**6=64).
122 | # crop: Picks random crops from the image. First, scales the image based
123 | # on IMAGE_MIN_DIM and IMAGE_MIN_SCALE, then picks a random crop of
124 | # size IMAGE_MIN_DIM x IMAGE_MIN_DIM. Can be used in training only.
125 | # IMAGE_MAX_DIM is not used in this mode.
126 | IMAGE_RESIZE_MODE = "square"
127 | IMAGE_MIN_DIM = 800
128 | IMAGE_MAX_DIM = 1024
129 | # Minimum scaling ratio. Checked after MIN_IMAGE_DIM and can force further
130 | # up scaling. For example, if set to 2 then images are scaled up to double
131 | # the width and height, or more, even if MIN_IMAGE_DIM doesn't require it.
132 | # However, in 'square' mode, it can be overruled by IMAGE_MAX_DIM.
133 | IMAGE_MIN_SCALE = 0
134 | # Number of color channels per image. RGB = 3, grayscale = 1, RGB-D = 4
135 | # Changing this requires other changes in the code. See the WIKI for more
136 | # details: https://github.com/matterport/Mask_RCNN/wiki
137 | IMAGE_CHANNEL_COUNT = 3
138 |
139 | # Image mean (RGB)
140 | MEAN_PIXEL = np.array([123.7, 116.8, 103.9])
141 |
142 | # Number of ROIs per image to feed to classifier/mask heads
143 | # The Mask RCNN paper uses 512 but often the RPN doesn't generate
144 | # enough positive proposals to fill this and keep a positive:negative
145 | # ratio of 1:3. You can increase the number of proposals by adjusting
146 | # the RPN NMS threshold.
147 | TRAIN_ROIS_PER_IMAGE = 200
148 |
149 | # Percent of positive ROIs used to train classifier/mask heads
150 | ROI_POSITIVE_RATIO = 0.33
151 |
152 | # Pooled ROIs
153 | POOL_SIZE = 7
154 | MASK_POOL_SIZE = 14
155 |
156 | # Shape of output mask
157 | # To change this you also need to change the neural network mask branch
158 | MASK_SHAPE = [28, 28]
159 |
160 | # Maximum number of ground truth instances to use in one image
161 | MAX_GT_INSTANCES = 30
162 |
163 | # Bounding box refinement standard deviation for RPN and final detections.
164 | RPN_BBOX_STD_DEV = np.array([0.1, 0.1, 0.2, 0.2])
165 | BBOX_STD_DEV = np.array([0.1, 0.1, 0.2, 0.2])
166 |
167 | # Max number of final detections
168 | DETECTION_MAX_INSTANCES = 35
169 |
170 | # Minimum probability value to accept a detected instance
171 | # ROIs below this threshold are skipped
172 | DETECTION_MIN_CONFIDENCE = 0.7
173 |
174 | # Non-maximum suppression threshold for detection
175 | DETECTION_NMS_THRESHOLD = 0.3
176 |
177 | # Learning rate and momentum
178 | # The Mask RCNN paper uses lr=0.02, but on TensorFlow it causes
179 | # weights to explode. Likely due to differences in optimizer
180 | # implementation.
181 | LEARNING_RATE = 0.001
182 | LEARNING_MOMENTUM = 0.9
183 |
184 | # Weight decay regularization
185 | WEIGHT_DECAY = 0.0001
186 |
187 | # Loss weights for more precise optimization.
188 | # Can be used for R-CNN training setup.
189 | LOSS_WEIGHTS = {
190 | "rpn_class_loss": 1.,
191 | "rpn_bbox_loss": 1.,
192 | "mrcnn_class_loss": 1.,
193 | "mrcnn_bbox_loss": 1.,
194 | "mrcnn_mask_loss": 1.
195 | }
196 |
197 | # Use RPN ROIs or externally generated ROIs for training
198 | # Keep this True for most situations. Set to False if you want to train
199 | # the head branches on ROI generated by code rather than the ROIs from
200 | # the RPN. For example, to debug the classifier head without having to
201 | # train the RPN.
202 | USE_RPN_ROIS = True
203 |
204 | # Train or freeze batch normalization layers
205 | # None: Train BN layers. This is the normal mode
206 | # False: Freeze BN layers. Good when using a small batch size
207 | # True: (don't use). Set layer in training mode even when predicting
208 | TRAIN_BN = False # Defaulting to False since batch size is often small
209 |
210 | # Gradient norm clipping
211 | GRADIENT_CLIP_NORM = 5.0
212 |
213 | def __init__(self):
214 | """Set values of computed attributes."""
215 | # Effective batch size
216 | self.BATCH_SIZE = self.IMAGES_PER_GPU * self.GPU_COUNT
217 |
218 | # Input image size
219 | if self.IMAGE_RESIZE_MODE == "crop":
220 | self.IMAGE_SHAPE = np.array([self.IMAGE_MIN_DIM, self.IMAGE_MIN_DIM,
221 | self.IMAGE_CHANNEL_COUNT])
222 | else:
223 | self.IMAGE_SHAPE = np.array([self.IMAGE_MAX_DIM, self.IMAGE_MAX_DIM,
224 | self.IMAGE_CHANNEL_COUNT])
225 |
226 | # Image meta data length
227 | # See compose_image_meta() for details
228 | self.IMAGE_META_SIZE = 1 + 3 + 3 + 4 + 1 + self.NUM_CLASSES
229 |
230 | def display(self):
231 | """Display Configuration values."""
232 | print("\nConfigurations:")
233 | for a in dir(self):
234 | if not a.startswith("__") and not callable(getattr(self, a)):
235 | print("{:30} {}".format(a, getattr(self, a)))
236 | print("\n")
237 |
--------------------------------------------------------------------------------
/hent-AI/mrcnn/parallel_model.py:
--------------------------------------------------------------------------------
1 | """
2 | Mask R-CNN
3 | Multi-GPU Support for Keras.
4 |
5 | Copyright (c) 2017 Matterport, Inc.
6 | Licensed under the MIT License (see LICENSE for details)
7 | Written by Waleed Abdulla
8 |
9 | Ideas and a small code snippets from these sources:
10 | https://github.com/fchollet/keras/issues/2436
11 | https://medium.com/@kuza55/transparent-multi-gpu-training-on-tensorflow-with-keras-8b0016fd9012
12 | https://github.com/avolkov1/keras_experiments/blob/master/keras_exp/multigpu/
13 | https://github.com/fchollet/keras/blob/master/keras/utils/training_utils.py
14 | """
15 |
16 | import tensorflow as tf
17 | import keras.backend as K
18 | import keras.layers as KL
19 | import keras.models as KM
20 |
21 |
22 | class ParallelModel(KM.Model):
23 | """Subclasses the standard Keras Model and adds multi-GPU support.
24 | It works by creating a copy of the model on each GPU. Then it slices
25 | the inputs and sends a slice to each copy of the model, and then
26 | merges the outputs together and applies the loss on the combined
27 | outputs.
28 | """
29 |
30 | def __init__(self, keras_model, gpu_count):
31 | """Class constructor.
32 | keras_model: The Keras model to parallelize
33 | gpu_count: Number of GPUs. Must be > 1
34 | """
35 | self.inner_model = keras_model
36 | self.gpu_count = gpu_count
37 | merged_outputs = self.make_parallel()
38 | super(ParallelModel, self).__init__(inputs=self.inner_model.inputs,
39 | outputs=merged_outputs)
40 |
41 | def __getattribute__(self, attrname):
42 | """Redirect loading and saving methods to the inner model. That's where
43 | the weights are stored."""
44 | if 'load' in attrname or 'save' in attrname:
45 | return getattr(self.inner_model, attrname)
46 | return super(ParallelModel, self).__getattribute__(attrname)
47 |
48 | def summary(self, *args, **kwargs):
49 | """Override summary() to display summaries of both, the wrapper
50 | and inner models."""
51 | super(ParallelModel, self).summary(*args, **kwargs)
52 | self.inner_model.summary(*args, **kwargs)
53 |
54 | def make_parallel(self):
55 | """Creates a new wrapper model that consists of multiple replicas of
56 | the original model placed on different GPUs.
57 | """
58 | # Slice inputs. Slice inputs on the CPU to avoid sending a copy
59 | # of the full inputs to all GPUs. Saves on bandwidth and memory.
60 | input_slices = {name: tf.split(x, self.gpu_count)
61 | for name, x in zip(self.inner_model.input_names,
62 | self.inner_model.inputs)}
63 |
64 | output_names = self.inner_model.output_names
65 | outputs_all = []
66 | for i in range(len(self.inner_model.outputs)):
67 | outputs_all.append([])
68 |
69 | # Run the model call() on each GPU to place the ops there
70 | for i in range(self.gpu_count):
71 | with tf.device('/gpu:%d' % i):
72 | with tf.name_scope('tower_%d' % i):
73 | # Run a slice of inputs through this replica
74 | zipped_inputs = zip(self.inner_model.input_names,
75 | self.inner_model.inputs)
76 | inputs = [
77 | KL.Lambda(lambda s: input_slices[name][i],
78 | output_shape=lambda s: (None,) + s[1:])(tensor)
79 | for name, tensor in zipped_inputs]
80 | # Create the model replica and get the outputs
81 | outputs = self.inner_model(inputs)
82 | if not isinstance(outputs, list):
83 | outputs = [outputs]
84 | # Save the outputs for merging back together later
85 | for l, o in enumerate(outputs):
86 | outputs_all[l].append(o)
87 |
88 | # Merge outputs on CPU
89 | with tf.device('/cpu:0'):
90 | merged = []
91 | for outputs, name in zip(outputs_all, output_names):
92 | # Concatenate or average outputs?
93 | # Outputs usually have a batch dimension and we concatenate
94 | # across it. If they don't, then the output is likely a loss
95 | # or a metric value that gets averaged across the batch.
96 | # Keras expects losses and metrics to be scalars.
97 | if K.int_shape(outputs[0]) == ():
98 | # Average
99 | m = KL.Lambda(lambda o: tf.add_n(o) / len(outputs), name=name)(outputs)
100 | else:
101 | # Concatenate
102 | m = KL.Concatenate(axis=0, name=name)(outputs)
103 | merged.append(m)
104 | return merged
105 |
106 |
107 | if __name__ == "__main__":
108 | # Testing code below. It creates a simple model to train on MNIST and
109 | # tries to run it on 2 GPUs. It saves the graph so it can be viewed
110 | # in TensorBoard. Run it as:
111 | #
112 | # python3 parallel_model.py
113 |
114 | import os
115 | import numpy as np
116 | import keras.optimizers
117 | from keras.datasets import mnist
118 | from keras.preprocessing.image import ImageDataGenerator
119 |
120 | GPU_COUNT = 2
121 |
122 | # Root directory of the project
123 | ROOT_DIR = os.path.abspath("../")
124 |
125 | # Directory to save logs and trained model
126 | MODEL_DIR = os.path.join(ROOT_DIR, "logs")
127 |
128 | def build_model(x_train, num_classes):
129 | # Reset default graph. Keras leaves old ops in the graph,
130 | # which are ignored for execution but clutter graph
131 | # visualization in TensorBoard.
132 | tf.reset_default_graph()
133 |
134 | inputs = KL.Input(shape=x_train.shape[1:], name="input_image")
135 | x = KL.Conv2D(32, (3, 3), activation='relu', padding="same",
136 | name="conv1")(inputs)
137 | x = KL.Conv2D(64, (3, 3), activation='relu', padding="same",
138 | name="conv2")(x)
139 | x = KL.MaxPooling2D(pool_size=(2, 2), name="pool1")(x)
140 | x = KL.Flatten(name="flat1")(x)
141 | x = KL.Dense(128, activation='relu', name="dense1")(x)
142 | x = KL.Dense(num_classes, activation='softmax', name="dense2")(x)
143 |
144 | return KM.Model(inputs, x, "digit_classifier_model")
145 |
146 | # Load MNIST Data
147 | (x_train, y_train), (x_test, y_test) = mnist.load_data()
148 | x_train = np.expand_dims(x_train, -1).astype('float32') / 255
149 | x_test = np.expand_dims(x_test, -1).astype('float32') / 255
150 |
151 | print('x_train shape:', x_train.shape)
152 | print('x_test shape:', x_test.shape)
153 |
154 | # Build data generator and model
155 | datagen = ImageDataGenerator()
156 | model = build_model(x_train, 10)
157 |
158 | # Add multi-GPU support.
159 | model = ParallelModel(model, GPU_COUNT)
160 |
161 | optimizer = keras.optimizers.SGD(lr=0.01, momentum=0.9, clipnorm=5.0)
162 |
163 | model.compile(loss='sparse_categorical_crossentropy',
164 | optimizer=optimizer, metrics=['accuracy'])
165 |
166 | model.summary()
167 |
168 | # Train
169 | model.fit_generator(
170 | datagen.flow(x_train, y_train, batch_size=64),
171 | steps_per_epoch=50, epochs=10, verbose=1,
172 | validation_data=(x_test, y_test),
173 | callbacks=[keras.callbacks.TensorBoard(log_dir=MODEL_DIR,
174 | write_graph=True)]
175 | )
176 |
--------------------------------------------------------------------------------
/hent-AI/requirements.txt:
--------------------------------------------------------------------------------
1 | h5py==2.10.0
2 | ffmpeg-python
--------------------------------------------------------------------------------
/hent-AI/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
3 | license-file = LICENSE
4 | requirements-file = requirements.txt
--------------------------------------------------------------------------------
/hent-AI/setup.py:
--------------------------------------------------------------------------------
1 | """
2 | The build/compilations setup
3 |
4 | >> pip install -r requirements.txt
5 | >> python setup.py install
6 | """
7 | import pip
8 | import logging
9 | import pkg_resources
10 | try:
11 | from setuptools import setup
12 | except ImportError:
13 | from distutils.core import setup
14 |
15 |
16 | def _parse_requirements(file_path):
17 | pip_ver = pkg_resources.get_distribution('pip').version
18 | pip_version = list(map(int, pip_ver.split('.')[:2]))
19 | if pip_version >= [6, 0]:
20 | raw = pip.req.parse_requirements(file_path,
21 | session=pip.download.PipSession())
22 | else:
23 | raw = pip.req.parse_requirements(file_path)
24 | return [str(i.req) for i in raw]
25 |
26 |
27 | # parse_requirements() returns generator of pip.req.InstallRequirement objects
28 | try:
29 | install_reqs = _parse_requirements("requirements.txt")
30 | except Exception:
31 | logging.warning('Fail load requirements file, so using default ones.')
32 | install_reqs = []
33 |
34 | setup(
35 | name='mask-rcnn',
36 | version='2.1',
37 | url='https://github.com/matterport/Mask_RCNN',
38 | author='Matterport',
39 | author_email='waleed.abdulla@gmail.com',
40 | license='MIT',
41 | description='Mask R-CNN for object detection and instance segmentation',
42 | packages=["mrcnn"],
43 | install_requires=install_reqs,
44 | include_package_data=True,
45 | python_requires='>=3.4',
46 | long_description="""This is an implementation of Mask R-CNN on Python 3, Keras, and TensorFlow.
47 | The model generates bounding boxes and segmentation masks for each instance of an object in the image.
48 | It's based on Feature Pyramid Network (FPN) and a ResNet101 backbone.""",
49 | classifiers=[
50 | "Development Status :: 5 - Production/Stable",
51 | "Environment :: Console",
52 | "Intended Audience :: Developers",
53 | "Intended Audience :: Information Technology",
54 | "Intended Audience :: Education",
55 | "Intended Audience :: Science/Research",
56 | "License :: OSI Approved :: MIT License",
57 | "Natural Language :: English",
58 | "Operating System :: OS Independent",
59 | "Topic :: Scientific/Engineering :: Artificial Intelligence",
60 | "Topic :: Scientific/Engineering :: Image Recognition",
61 | "Topic :: Scientific/Engineering :: Visualization",
62 | "Topic :: Scientific/Engineering :: Image Segmentation",
63 | 'Programming Language :: Python :: 3.4',
64 | 'Programming Language :: Python :: 3.5',
65 | 'Programming Language :: Python :: 3.6',
66 | ],
67 | keywords="image instance segmentation object detection mask rcnn r-cnn tensorflow keras",
68 | )
69 |
--------------------------------------------------------------------------------
/inpainting.py:
--------------------------------------------------------------------------------
1 | import requests
2 | from PIL import Image
3 | from os import listdir
4 |
5 | def run_inpaint(image_path, mask_path, output_path):
6 | w, h = Image.open(image_path).size
7 | r = requests.post(
8 | url = 'http://127.0.0.1:5003/inpaint',
9 | files={
10 | 'image':open(image_path, 'rb'),
11 | 'mask':open(mask_path, 'rb'),
12 | },
13 | data={
14 | 'ldmSteps':'50',
15 | 'hdStrategy':'Resize',
16 | 'hdStrategyCropMargin':'128',
17 | 'hdStrategyCropTrigerSize':'2048',
18 | 'hdStrategyResizeLimit':'2048',
19 | 'sizeLimit': '%s' %(w if w>h else h),
20 | },
21 | )
22 | open(output_path, 'wb').write(r.content)
23 |
24 |
25 | def make_mask(detected_image):
26 | img = Image.open(detected_image)
27 | img_pixels = img.load()
28 |
29 | for i in range(img.size[0]):
30 | for j in range(img.size[1]):
31 | if img_pixels[i, j] == (0, 255, 0):
32 | img_pixels[i, j] = (255, 255, 255)
33 | else:
34 | img_pixels[i, j] = (0, 0, 0)
35 |
36 | mask_fname = '%s_mask.png' %detected_image[:-4]
37 | img.save(mask_fname)
38 | return mask_fname
39 |
40 |
41 |
42 |
43 |
44 |
45 | if __name__ == '__main__':
46 |
47 | original_image = [i for i in listdir('/content/image_original/') if i.endswith('.png')]
48 |
49 | for o in original_image:
50 | try:
51 | img = '/content/image_original/{}'.format(o)
52 | print("original_image ->", img)
53 | mask = make_mask('/content/image_detected/{}.png'.format(o[:-4]))
54 | print('masked completed ->', mask)
55 |
56 | run_inpaint(
57 | image_path=img,
58 | mask_path=mask,
59 | output_path='/content/image_uncensored/{}'.format(o)
60 | )
61 | print('inpaint completed ->', '/content/image_uncensored/{}'.format(o), '\n')
62 |
63 |
64 | except Exception as e:
65 | print(e, '\n')
--------------------------------------------------------------------------------
/lama.py:
--------------------------------------------------------------------------------
1 | from lama_cleaner import entry_point
2 |
3 | if __name__ == "__main__":
4 | entry_point()
5 |
--------------------------------------------------------------------------------
/lama_cleaner/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # Lama Cleaner Dockerfile
3 | # @author Loreto Parisi (loretoparisi at gmail dot com)
4 | #
5 |
6 | FROM python:3.7.4-slim-buster
7 |
8 | LABEL maintainer Loreto Parisi loretoparisi@gmail.com
9 |
10 | WORKDIR app
11 |
12 | RUN apt-get update && apt-get install -y --no-install-recommends \
13 | software-properties-common \
14 | libsm6 libxext6 ffmpeg libfontconfig1 libxrender1 libgl1-mesa-glx \
15 | curl \
16 | npm
17 |
18 | # python requirements
19 | COPY . .
20 | COPY requirements.txt /etc/tmp/requirements.txt
21 | RUN pip install -r /etc/tmp/requirements.txt
22 |
23 | # nodejs
24 | RUN npm install n -g && \
25 | n lts
26 | # yarn
27 | RUN npm install -g yarn
28 |
29 | # webapp
30 | RUN cd lama_cleaner/app/ && \
31 | yarn && \
32 | yarn build
33 |
34 | EXPOSE 8080
35 |
36 | CMD ["bash"]
--------------------------------------------------------------------------------
/lama_cleaner/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/lama_cleaner/README.md:
--------------------------------------------------------------------------------
1 | # Lama-cleaner: Image inpainting tool powered by SOTA AI model
2 |
3 | 
4 | 
5 |
6 | https://user-images.githubusercontent.com/3998421/153323093-b664bb68-2928-480b-b59b-7c1ee24a4507.mp4
7 |
8 | - [x] Support multiple model architectures
9 | 1. [LaMa](https://github.com/saic-mdal/lama)
10 | 1. [LDM](https://github.com/CompVis/latent-diffusion)
11 | - [x] High resolution support
12 | - [x] Run as a desktop APP
13 | - [x] Multi stroke support. Press and hold the `cmd/ctrl` key to enable multi stroke mode.
14 | - [x] Zoom & Pan
15 | - [ ] Keep image EXIF data
16 |
17 | ## Install
18 |
19 | ```bash
20 | pip install lama-cleaner
21 |
22 | lama-cleaner --device=cpu --port=8080
23 | ```
24 |
25 | Available commands:
26 |
27 | | Name | Description | Default |
28 | | ---------- | ------------------------------------------------ | -------- |
29 | | --model | lama or ldm. See details in **Model Comparison** | lama |
30 | | --device | cuda or cpu | cuda |
31 | | --gui | Launch lama-cleaner as a desktop application | |
32 | | --gui_size | Set the window size for the application | 1200 900 |
33 | | --input | Path to image you want to load by default | None |
34 | | --port | Port for flask web server | 8080 |
35 | | --debug | Enable debug mode for flask web server | |
36 |
37 | ## Model Comparison
38 |
39 | Diffusion model(ldm) is **MUCH MORE** slower than GANs(lama)(1080x720 image takes 8s on 3090), but it's possible to get better
40 | result, see below example:
41 |
42 | | Original Image | LaMa | LDM |
43 | | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
44 | |  |  |  |
45 |
46 | Blogs about diffusion models:
47 |
48 | - https://lilianweng.github.io/posts/2021-07-11-diffusion-models/
49 | - https://yang-song.github.io/blog/2021/score/
50 |
51 | ## Development
52 |
53 | Only needed if you plan to modify the frontend and recompile yourself.
54 |
55 | ### Frontend
56 |
57 | Frontend code are modified from [cleanup.pictures](https://github.com/initml/cleanup.pictures), You can experience their
58 | great online services [here](https://cleanup.pictures/).
59 |
60 | - Install dependencies:`cd lama_cleaner/app/ && yarn`
61 | - Start development server: `yarn start`
62 | - Build: `yarn build`
63 |
64 | ## Docker
65 |
66 | Run within a Docker container. Set the `CACHE_DIR` to models location path. Optionally add a `-d` option to
67 | the `docker run` command below to run as a daemon.
68 |
69 | ### Build Docker image
70 |
71 | ```
72 | docker build -f Dockerfile -t lamacleaner .
73 | ```
74 |
75 | ### Run Docker (cpu)
76 |
77 | ```
78 | docker run -p 8080:8080 -e CACHE_DIR=/app/models -v $(pwd)/models:/app/models -v $(pwd):/app --rm lamacleaner python3 main.py --device=cpu --port=8080
79 | ```
80 |
81 | ### Run Docker (gpu)
82 |
83 | ```
84 | docker run --gpus all -p 8080:8080 -e CACHE_DIR=/app/models -v $(pwd)/models:/app/models -v $(pwd):/app --rm lamacleaner python3 main.py --device=cuda --port=8080
85 | ```
86 |
87 | Then open [http://localhost:8080](http://localhost:8080)
88 |
89 | ## Like My Work?
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/lama_cleaner/__init__.py:
--------------------------------------------------------------------------------
1 | from lama_cleaner.parse_args import parse_args
2 | from lama_cleaner.server import main
3 |
4 |
5 | def entry_point():
6 | args = parse_args()
7 | main(args)
8 |
--------------------------------------------------------------------------------
/lama_cleaner/app/.env:
--------------------------------------------------------------------------------
1 | REACT_APP_INPAINTING_URL=""
--------------------------------------------------------------------------------
/lama_cleaner/app/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "airbnb",
4 | "airbnb/hooks",
5 | "plugin:@typescript-eslint/recommended",
6 | "prettier",
7 | "plugin:prettier/recommended"
8 | ],
9 | "plugins": ["@typescript-eslint", "react", "react-hooks", "prettier"],
10 | "parser": "@typescript-eslint/parser",
11 | "parserOptions": {
12 | "ecmaFeatures": {
13 | "jsx": true
14 | },
15 | "ecmaVersion": 2018,
16 | "sourceType": "module",
17 | "project": "./tsconfig.json"
18 | },
19 | "rules": {
20 | "jsx-a11y/click-events-have-key-events": 0,
21 | "react/jsx-props-no-spreading": 0,
22 | "import/no-unresolved": 0,
23 | "react/jsx-no-bind": "off",
24 | "react/jsx-filename-extension": [
25 | 1,
26 | {
27 | "extensions": [".ts", ".tsx"]
28 | }
29 | ],
30 | "prettier/prettier": [
31 | "error",
32 | {
33 | "singleQuote": true,
34 | "arrowParens": "avoid",
35 | "endOfLine": "auto"
36 | }
37 | ],
38 | "consistent-return": "off",
39 | "no-use-before-define": "off",
40 | "import/extensions": "off",
41 | "react/prop-types": 0,
42 | "react/require-default-props": "off",
43 | "no-shadow": "off",
44 | "@typescript-eslint/ban-ts-comment": "off",
45 | "@typescript-eslint/no-shadow": ["error"],
46 | "@typescript-eslint/no-explicit-any": "off",
47 | "@typescript-eslint/explicit-function-return-type": "off",
48 | "@typescript-eslint/explicit-module-boundary-types": "off",
49 | "react-hooks/rules-of-hooks": "error",
50 | "react-hooks/exhaustive-deps": "warn"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/lama_cleaner/app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 |
13 | # misc
14 | .DS_Store
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
24 | # Tailwind processed CSS
25 | index.css
26 |
27 | .firebase
28 |
--------------------------------------------------------------------------------
/lama_cleaner/app/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "semi": false,
4 | "trailingComma": "es5",
5 | "arrowParens": "avoid"
6 | }
7 |
--------------------------------------------------------------------------------
/lama_cleaner/app/build/asset-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": {
3 | "main.css": "/static/css/main.4201b632.chunk.css",
4 | "main.js": "/static/js/main.18cd2cfc.chunk.js",
5 | "runtime-main.js": "/static/js/runtime-main.5e86ac81.js",
6 | "static/js/2.2d367d07.chunk.js": "/static/js/2.2d367d07.chunk.js",
7 | "index.html": "/index.html",
8 | "static/js/2.2d367d07.chunk.js.LICENSE.txt": "/static/js/2.2d367d07.chunk.js.LICENSE.txt",
9 | "static/media/_index.scss": "/static/media/WorkSans-SemiBold.1e98db4e.ttf"
10 | },
11 | "entrypoints": [
12 | "static/js/runtime-main.5e86ac81.js",
13 | "static/js/2.2d367d07.chunk.js",
14 | "static/css/main.4201b632.chunk.css",
15 | "static/js/main.18cd2cfc.chunk.js"
16 | ]
17 | }
--------------------------------------------------------------------------------
/lama_cleaner/app/build/index.html:
--------------------------------------------------------------------------------
1 |
lama-cleaner - Image inpainting powered by LaMa
--------------------------------------------------------------------------------
/lama_cleaner/app/build/static/js/2.2d367d07.chunk.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*
2 | object-assign
3 | (c) Sindre Sorhus
4 | @license MIT
5 | */
6 |
7 | /** @license React v0.20.2
8 | * scheduler.production.min.js
9 | *
10 | * Copyright (c) Facebook, Inc. and its affiliates.
11 | *
12 | * This source code is licensed under the MIT license found in the
13 | * LICENSE file in the root directory of this source tree.
14 | */
15 |
16 | /** @license React v17.0.2
17 | * react-dom.production.min.js
18 | *
19 | * Copyright (c) Facebook, Inc. and its affiliates.
20 | *
21 | * This source code is licensed under the MIT license found in the
22 | * LICENSE file in the root directory of this source tree.
23 | */
24 |
25 | /** @license React v17.0.2
26 | * react-jsx-runtime.production.min.js
27 | *
28 | * Copyright (c) Facebook, Inc. and its affiliates.
29 | *
30 | * This source code is licensed under the MIT license found in the
31 | * LICENSE file in the root directory of this source tree.
32 | */
33 |
34 | /** @license React v17.0.2
35 | * react.production.min.js
36 | *
37 | * Copyright (c) Facebook, Inc. and its affiliates.
38 | *
39 | * This source code is licensed under the MIT license found in the
40 | * LICENSE file in the root directory of this source tree.
41 | */
42 |
--------------------------------------------------------------------------------
/lama_cleaner/app/build/static/js/runtime-main.5e86ac81.js:
--------------------------------------------------------------------------------
1 | !function(e){function r(r){for(var n,l,a=r[0],f=r[1],i=r[2],p=0,s=[];p0.2%",
39 | "not dead",
40 | "not op_mini all"
41 | ],
42 | "development": [
43 | "last 1 chrome version",
44 | "last 1 firefox version",
45 | "last 1 safari version"
46 | ]
47 | },
48 | "devDependencies": {
49 | "@typescript-eslint/eslint-plugin": "^5.1.0",
50 | "eslint-config-airbnb": "^18.2.1",
51 | "eslint-config-prettier": "^8.3.0",
52 | "eslint-plugin-import": "^2.25.2",
53 | "eslint-plugin-jsx-a11y": "^6.4.1",
54 | "eslint-plugin-prettier": "^4.0.0",
55 | "eslint-plugin-react": "^7.26.1",
56 | "eslint-plugin-react-hooks": "^4.2.0",
57 | "prettier": "^2.4.1",
58 | "sass": "^1.49.9"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/lama_cleaner/app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
20 | lama-cleaner - Image inpainting powered by LaMa
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/lama_cleaner/app/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { useKeyPressEvent } from 'react-use'
3 | import { useRecoilState } from 'recoil'
4 | import useInputImage from './hooks/useInputImage'
5 | import LandingPage from './components/LandingPage/LandingPage'
6 | import { themeState } from './components/Header/ThemeChanger'
7 | import Workspace from './components/Workspace'
8 | import { fileState } from './store/Atoms'
9 | import { keepGUIAlive } from './utils'
10 | import Header from './components/Header/Header'
11 |
12 | // Keeping GUI Window Open
13 | keepGUIAlive()
14 |
15 | function App() {
16 | const [file, setFile] = useRecoilState(fileState)
17 | const [theme, setTheme] = useRecoilState(themeState)
18 | const userInputImage = useInputImage()
19 |
20 | // Set Input Image
21 | useEffect(() => {
22 | setFile(userInputImage)
23 | }, [userInputImage, setFile])
24 |
25 | // Dark Mode Hotkey
26 | useKeyPressEvent('D', ev => {
27 | ev?.preventDefault()
28 | const newTheme = theme === 'light' ? 'dark' : 'light'
29 | setTheme(newTheme)
30 | })
31 |
32 | return (
33 |
83 | Crop masking area from the original image to do inpainting, and paste
84 | the result back. Mainly for performance and memory reasons on high
85 | resolution image.
86 |