Browse Source

first source code commit, nervous!

tags/v1.4.4
DeepMan 1 year ago
parent
commit
843b215ff4
59 changed files with 13388 additions and 8 deletions
  1. 5
    0
      .gitignore
  2. 42
    0
      .vscode/settings.json
  3. 24
    8
      README.md
  4. 5
    0
      src/.gitignore
  5. 9
    0
      src/build.bat
  6. 21
    0
      src/cli/.gitignore
  7. 1
    0
      src/cli/build.bat
  8. 338
    0
      src/cli/gan.py
  9. BIN
      src/cli/input.png
  10. 58
    0
      src/cli/main.py
  11. 0
    0
      src/cli/opencv_transform/__init__.py
  12. 17
    0
      src/cli/opencv_transform/annotation.py
  13. 64
    0
      src/cli/opencv_transform/dress_to_correct.py
  14. 41
    0
      src/cli/opencv_transform/mask_to_maskref.py
  15. 519
    0
      src/cli/opencv_transform/maskdet_to_maskfin.py
  16. 29
    0
      src/cli/opencv_transform/nude_to_watermark.py
  17. 5
    0
      src/cli/requirements.txt
  18. 175
    0
      src/cli/run.py
  19. 70
    0
      src/gui/.eslintrc.json
  20. 88
    0
      src/gui/.gitignore
  21. 7
    0
      src/gui/.prettierrc
  22. 22
    0
      src/gui/README.md
  23. 7
    0
      src/gui/assets/README.md
  24. 22
    0
      src/gui/assets/css/base/_reset.scss
  25. 4
    0
      src/gui/assets/css/components/_all.scss
  26. 38
    0
      src/gui/assets/css/components/_button.scss
  27. 16
    0
      src/gui/assets/css/components/_container.scss
  28. 15
    0
      src/gui/assets/css/components/_form.scss
  29. 22
    0
      src/gui/assets/css/components/_section.scss
  30. 1
    0
      src/gui/assets/css/fonts.scss
  31. 6
    0
      src/gui/assets/css/tailwind.scss
  32. 0
    0
      src/gui/assets/css/utilities/_all.scss
  33. 5
    0
      src/gui/assets/css/vendor.scss
  34. 79
    0
      src/gui/components/Logo.vue
  35. 7
    0
      src/gui/components/README.md
  36. 1
    0
      src/gui/components/index.js
  37. 7
    0
      src/gui/layouts/README.md
  38. 8
    0
      src/gui/layouts/default.vue
  39. 74
    0
      src/gui/main.js
  40. 8
    0
      src/gui/middleware/README.md
  41. 7
    0
      src/gui/middleware/nudity.js
  42. 11
    0
      src/gui/mixins/BaseMixin.js
  43. 85
    0
      src/gui/nuxt.config.js
  44. 79
    0
      src/gui/package.json
  45. 6
    0
      src/gui/pages/README.md
  46. 85
    0
      src/gui/pages/index.vue
  47. 61
    0
      src/gui/pages/nudity/results.vue
  48. 227
    0
      src/gui/pages/nudity/settings.vue
  49. 7
    0
      src/gui/plugins/README.md
  50. 37
    0
      src/gui/plugins/boot.client.js
  51. 150
    0
      src/gui/preload.js
  52. 11
    0
      src/gui/static/README.md
  53. BIN
      src/gui/static/favicon.ico
  54. BIN
      src/gui/static/icon.png
  55. 10
    0
      src/gui/store/README.md
  56. 46
    0
      src/gui/store/nudity.js
  57. 42
    0
      src/gui/tailwind.config.js
  58. 10653
    0
      src/gui/yarn.lock
  59. 11
    0
      src/install.bat

+ 5
- 0
.gitignore View File

@@ -0,0 +1,5 @@
# Distribution
dist/

# Demo photos
demo/

+ 42
- 0
.vscode/settings.json View File

@@ -0,0 +1,42 @@
{
"editor.tabSize": 2,

"files.eol": "\n",
"files.trimTrailingWhitespace": true,
"files.trimFinalNewlines": false,

"[python]": {
"editor.tabSize": 4
},

"emmet.includeLanguages": {
"vue": "html"
},

"html.format.enable": false,
"css.validate": false,
"scss.validate": false,

"eslint.autoFixOnSave": true,
"eslint.enable": true,
"eslint.packageManager": "yarn",
"eslint.run": "onType",
"eslint.validate": [
{
"language": "javascript"
},
{
"language": "vue",
"autoFix": true
}
],

"prettier.disableLanguages": [],
"prettier.eslintIntegration": true,
"prettier.stylelintIntegration": true,
"vetur.validation.template": false,
"vetur.format.defaultFormatter.html": "none",
"vetur.format.defaultFormatter.js": "prettier-eslint",
"vetur.format.defaultFormatter.scss": "prettier",
"vetur.trace.server": "messages"
}

+ 24
- 8
README.md View File

@@ -1,10 +1,10 @@
# Easy-DeepNude

Easy-DeepNude offers a **CLI** and **GUI** version of the artificial intelligence project [DeepNude](https://github.com/deepinstruction/deepnude_official).
Easy-DeepNude is a new implementation of the [DeepNude](https://github.com/deepinstruction/deepnude_official) project, the goal of Easy-DeepNude is to offer a **CLI** to process photos from the console and a new **GUI** (Graphical user interface) to use the program easily.

The CLI version allows you to process photos using only the console, use the `--help` argument to get more information about its use. **Note:** A cropping system is not included, to use the program properly crop the photo to be processed to dimensions *512x512*.
- The CLI version allows you to transform photos using commands in a console, with this you can create automated systems such as *Bots, web pages or a new GUI*. Use the argument `--help` to get more information about usage, keep in mind that the CLI by itself **does not have** a cropping system so you will have to manually resize your photos to 512x512

The GUI version allows you to process photos with a friendly interface, this version includes a cropping system so it is only necessary to have the photos you are going to process.
- The GUI version is a new friendly interface that includes a cropping system, so you only have to tell the program which photo you want to transform and you will get results in a few clicks.

## Preview

@@ -20,7 +20,7 @@ I will try to help in any problem you have.

## Requirements

* [CUDA 10.0](https://developer.nvidia.com/cuda-10.0-download-archive) (If you are going to use GPU processing)
* [Latest NVIDIA drivers](https://www.nvidia.com/Download/index.aspx) or [CUDA 10.0](https://developer.nvidia.com/cuda-10.0-download-archive) (If you are going to use GPU processing. CUDA can be optional on the latest graphics cards)

## Download

@@ -35,12 +35,28 @@ If you are not a developer or are only interested in using the program, these ar
* If you want to use only the console version, download the **CLI** and **CLI Checkpoints** zip files.
* If you want to use the version with graphical interface download all 3 zip files.
* Create a folder called "easydeepnude" (where you want), open it and inside place the zip files you have downloaded.
* Unzip the **CLI.zip** and **GUI.zip** file (if you downloaded it) so that the `/cli/` and `/gui/` folders remain.
* Unzip the contents of **checkpoints.zip** inside the `/cli/` folder so that in the end there is `/cli/checkpoints/`
* Congratulations, if you use the CLI version you can now run the `/cli/cli.exe` file from a terminal, but if you use the GUI version open the program `/gui/EasyDeepNude.exe`
* Unzip the **CLI.zip** and **GUI.zip** file (if you downloaded it) In the end you should have the folders `cli/` and `gui/`.
* Unzip the contents of **checkpoints.zip** inside the `cli/` folder. At the end you must have the folder `cli/checkpoints` folder with 3 .lib files inside.
* Congratulations, now you can use the console version when executing the `cli/cli.exe` file from... a console. If you want to use the graphical interface, you should only run the `gui/EasyDeepNude.exe` file

# Development

*Work in progress...*

I need to make some adjustments so that everyone can use the code without problems.
## Requirements

* [CUDA 10.0](https://developer.nvidia.com/cuda-10.0-download-archive)
* [Python 3.6.8](https://www.python.org/downloads/release/python-368/)
* [PyInstaller](https://www.pyinstaller.org/)
* [NodeJS](https://nodejs.org/en/) (In theory any recent version works.)
* [Yarn](https://yarnpkg.com/en/docs/install)

## Preparation

`The following instructions should be applied within the src/ folder`

Install the necessary libraries and packages to compile the project by running the `install.bat` file

Compile the project by running the `build.bat` file, this should generate the `dist/` folder with the projects ready for use. Run `dist/gui/win-unpacked/EasyDeepNude.exe` once and close it, now transfer all files from `dist/gui/win-unpacked/` to `dist/gui/`

With this you should have a functional version

+ 5
- 0
src/.gitignore View File

@@ -0,0 +1,5 @@
# Telegram Bot not ready!
telegram-bot/

# Personal file
notes.txt

+ 9
- 0
src/build.bat View File

@@ -0,0 +1,9 @@
:: CLI
cd cli
pyinstaller main.py -y --onedir --name "cli" --distpath "../../dist"

:: GUI
cd ../gui
yarn build

pause

+ 21
- 0
src/cli/.gitignore View File

@@ -0,0 +1,21 @@
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Cache
__pycache__/

# Checkpoints
checkpoints/

# Build
build/

# Build Configuration
cli.spec

+ 1
- 0
src/cli/build.bat View File

@@ -0,0 +1 @@
pyinstaller main.py -y --onedir --name "cli" --distpath "../../dist"

+ 338
- 0
src/cli/gan.py View File

@@ -0,0 +1,338 @@
from PIL import Image
import numpy as np
import cv2
import torchvision.transforms as transforms
import torch
import io
import os
import functools


class DataLoader:
def __init__(self, opt, cv_img):
super(DataLoader, self).__init__()

self.dataset = Dataset()
self.dataset.initialize(opt, cv_img)

self.dataloader = torch.utils.data.DataLoader(
self.dataset,
batch_size=opt.batchSize,
shuffle=not opt.serial_batches,
num_workers=int(opt.nThreads),
)

def load_data(self):
return self.dataloader

def __len__(self):
return 1


class Dataset(torch.utils.data.Dataset):
def __init__(self):
super(Dataset, self).__init__()

def initialize(self, opt, cv_img):
self.opt = opt
self.root = opt.dataroot

self.A = Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB))
self.dataset_size = 1

def __getitem__(self, index):

transform_A = get_transform(self.opt)
A_tensor = transform_A(self.A.convert("RGB"))

B_tensor = inst_tensor = feat_tensor = 0

input_dict = {
"label": A_tensor,
"inst": inst_tensor,
"image": B_tensor,
"feat": feat_tensor,
"path": "",
}

return input_dict

def __len__(self):
return 1


class DeepModel(torch.nn.Module):
def initialize(self, opt, gpu_id):

self.opt = opt

if gpu_id is None:
self.gpu_ids = []
else:
self.gpu_ids = [gpu_id]

self.netG = self.__define_G(
opt.input_nc,
opt.output_nc,
opt.ngf,
opt.netG,
opt.n_downsample_global,
opt.n_blocks_global,
opt.n_local_enhancers,
opt.n_blocks_local,
opt.norm,
self.gpu_ids,
)

# load networks
self.__load_network(self.netG)

def inference(self, label, inst):

# Encode Inputs
input_label, inst_map, _, _ = self.__encode_input(label, inst, infer=True)

# Fake Generation
input_concat = input_label

with torch.no_grad():
fake_image = self.netG.forward(input_concat)

return fake_image

# helper loading function that can be used by subclasses
def __load_network(self, network):

save_path = os.path.join(self.opt.checkpoints_dir)

network.load_state_dict(torch.load(save_path))

def __encode_input(
self, label_map, inst_map=None, real_image=None, feat_map=None, infer=False
):
if len(self.gpu_ids) > 0:
input_label = label_map.data.cuda() # GPU
else:
input_label = label_map.data # CPU

return input_label, inst_map, real_image, feat_map

def __weights_init(self, m):
classname = m.__class__.__name__
if classname.find("Conv") != -1:
m.weight.data.normal_(0.0, 0.02)
elif classname.find("BatchNorm2d") != -1:
m.weight.data.normal_(1.0, 0.02)
m.bias.data.fill_(0)

def __define_G(
self,
input_nc,
output_nc,
ngf,
netG,
n_downsample_global=3,
n_blocks_global=9,
n_local_enhancers=1,
n_blocks_local=3,
norm="instance",
gpu_ids=[],
):
norm_layer = self.__get_norm_layer(norm_type=norm)
netG = GlobalGenerator(
input_nc, output_nc, ngf, n_downsample_global, n_blocks_global, norm_layer
)

if len(gpu_ids) > 0:
netG.cuda(gpu_ids[0])
netG.apply(self.__weights_init)
return netG

def __get_norm_layer(self, norm_type="instance"):
norm_layer = functools.partial(torch.nn.InstanceNorm2d, affine=False)
return norm_layer


##############################################################################
# Generator
##############################################################################


class GlobalGenerator(torch.nn.Module):
def __init__(
self,
input_nc,
output_nc,
ngf=64,
n_downsampling=3,
n_blocks=9,
norm_layer=torch.nn.BatchNorm2d,
padding_type="reflect",
):
assert n_blocks >= 0
super(GlobalGenerator, self).__init__()
activation = torch.nn.ReLU(True)

model = [
torch.nn.ReflectionPad2d(3),
torch.nn.Conv2d(input_nc, ngf, kernel_size=7, padding=0),
norm_layer(ngf),
activation,
]
# downsample
for i in range(n_downsampling):
mult = 2 ** i
model += [
torch.nn.Conv2d(
ngf * mult, ngf * mult * 2, kernel_size=3, stride=2, padding=1
),
norm_layer(ngf * mult * 2),
activation,
]

# resnet blocks
mult = 2 ** n_downsampling
for i in range(n_blocks):
model += [
ResnetBlock(
ngf * mult,
padding_type=padding_type,
activation=activation,
norm_layer=norm_layer,
)
]

# upsample
for i in range(n_downsampling):
mult = 2 ** (n_downsampling - i)
model += [
torch.nn.ConvTranspose2d(
ngf * mult,
int(ngf * mult / 2),
kernel_size=3,
stride=2,
padding=1,
output_padding=1,
),
norm_layer(int(ngf * mult / 2)),
activation,
]
model += [
torch.nn.ReflectionPad2d(3),
torch.nn.Conv2d(ngf, output_nc, kernel_size=7, padding=0),
torch.nn.Tanh(),
]
self.model = torch.nn.Sequential(*model)

def forward(self, input):
return self.model(input)


# Define a resnet block


class ResnetBlock(torch.nn.Module):
def __init__(
self,
dim,
padding_type,
norm_layer,
activation=torch.nn.ReLU(True),
use_dropout=False,
):
super(ResnetBlock, self).__init__()
self.conv_block = self.__build_conv_block(
dim, padding_type, norm_layer, activation, use_dropout
)

def __build_conv_block(
self, dim, padding_type, norm_layer, activation, use_dropout
):
conv_block = []
p = 0
if padding_type == "reflect":
conv_block += [torch.nn.ReflectionPad2d(1)]
elif padding_type == "replicate":
conv_block += [torch.nn.ReplicationPad2d(1)]
elif padding_type == "zero":
p = 1
else:
raise NotImplementedError("padding [%s] is not implemented" % padding_type)

conv_block += [
torch.nn.Conv2d(dim, dim, kernel_size=3, padding=p),
norm_layer(dim),
activation,
]
if use_dropout:
conv_block += [torch.nn.Dropout(0.5)]

p = 0
if padding_type == "reflect":
conv_block += [torch.nn.ReflectionPad2d(1)]
elif padding_type == "replicate":
conv_block += [torch.nn.ReplicationPad2d(1)]
elif padding_type == "zero":
p = 1
else:
raise NotImplementedError("padding [%s] is not implemented" % padding_type)
conv_block += [
torch.nn.Conv2d(dim, dim, kernel_size=3, padding=p),
norm_layer(dim),
]

return torch.nn.Sequential(*conv_block)

def forward(self, x):
out = x + self.conv_block(x)
return out


# Data utils:


def get_transform(opt, method=Image.BICUBIC, normalize=True):
transform_list = []

base = float(2 ** opt.n_downsample_global)
if opt.netG == "local":
base *= 2 ** opt.n_local_enhancers
transform_list.append(
transforms.Lambda(lambda img: __make_power_2(img, base, method))
)

transform_list += [transforms.ToTensor()]

if normalize:
transform_list += [transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
return transforms.Compose(transform_list)


def __make_power_2(img, base, method=Image.BICUBIC):
ow, oh = img.size
h = int(round(oh / base) * base)
w = int(round(ow / base) * base)
if (h == oh) and (w == ow):
return img
return img.resize((w, h), method)


# Converts a Tensor into a Numpy array
# |imtype|: the desired type of the converted numpy array


def tensor2im(image_tensor, imtype=np.uint8, normalize=True):
if isinstance(image_tensor, list):
image_numpy = []
for i in range(len(image_tensor)):
image_numpy.append(tensor2im(image_tensor[i], imtype, normalize))
return image_numpy
image_numpy = image_tensor.cpu().float().numpy()
if normalize:
image_numpy = (np.transpose(image_numpy, (1, 2, 0)) + 1) / 2.0 * 255.0
else:
image_numpy = np.transpose(image_numpy, (1, 2, 0)) * 255.0
image_numpy = np.clip(image_numpy, 0, 255)
if image_numpy.shape[2] == 1 or image_numpy.shape[2] > 3:
image_numpy = image_numpy[:, :, 0]
return image_numpy.astype(imtype)

BIN
src/cli/input.png View File


+ 58
- 0
src/cli/main.py View File

@@ -0,0 +1,58 @@
import sys
import argparse
import cv2
from run import process

parser = argparse.ArgumentParser()
parser.add_argument(
"-i", "--input", default="input.png", help="path of the photo to transform"
)
parser.add_argument(
"-o",
"--output",
default="output.png",
help="path where the transformed photo will be saved. (default: output.png)",
)
parser.add_argument(
"--cpu",
default=False,
action="store_true",
help="force photo processing with CPU (slower)",
)
parser.add_argument(
"--gpu",
default=0,
help="ID of the GPU to use for processing. This argument will be ignored if --cpu is active. (default: 0)",
)
args = parser.parse_args()

"""
main.py

How to run:
python3 main.py

"""

# ------------------------------------------------- main()
def main():
# Read input image
image = cv2.imread(args.input)

gpu_id = int(args.gpu)

if args.cpu:
gpu_id = None

# Process
result = process(image, gpu_id)

# Write output image
cv2.imwrite(args.output, result)

# Exit
sys.exit()


if __name__ == "__main__":
main()

+ 0
- 0
src/cli/opencv_transform/__init__.py View File


+ 17
- 0
src/cli/opencv_transform/annotation.py View File

@@ -0,0 +1,17 @@

#Object annotation class:
class BodyPart:
def __init__(self, name, xmin, ymin, xmax, ymax, x, y, w, h):
self.name = name
#Bounding Box:
self.xmin = xmin
self.ymin = ymin
self.xmax = xmax
self.ymax = ymax
#Center:
self.x = x
self.y = y
#Dimensione:
self.w = w
self.h = h

+ 64
- 0
src/cli/opencv_transform/dress_to_correct.py View File

@@ -0,0 +1,64 @@
import cv2
import math
import numpy as np
import os

# create_correct ===============================================================
# return:
# (<Boolean> True/False), depending on the transformation process
def create_correct(cv_dress):

#Production dir:
return correct_color(cv_dress, 5)

# correct_color ==============================================================================
# return:
# <RGB> image corrected
def correct_color(img, percent):

assert img.shape[2] == 3
assert percent > 0 and percent < 100

half_percent = percent / 200.0

channels = cv2.split(img)

out_channels = []
for channel in channels:
assert len(channel.shape) == 2
# find the low and high precentile values (based on the input percentile)
height, width = channel.shape
vec_size = width * height
flat = channel.reshape(vec_size)

assert len(flat.shape) == 1

flat = np.sort(flat)

n_cols = flat.shape[0]

low_val = flat[math.floor(n_cols * half_percent)]
high_val = flat[math.ceil( n_cols * (1.0 - half_percent))]

# saturate below the low percentile and above the high percentile
thresholded = apply_threshold(channel, low_val, high_val)
# scale the channel
normalized = cv2.normalize(thresholded, thresholded.copy(), 0, 255, cv2.NORM_MINMAX)
out_channels.append(normalized)

return cv2.merge(out_channels)

#Color correction utils
def apply_threshold(matrix, low_value, high_value):
low_mask = matrix < low_value
matrix = apply_mask(matrix, low_mask, low_value)

high_mask = matrix > high_value
matrix = apply_mask(matrix, high_mask, high_value)

return matrix

#Color correction utils
def apply_mask(matrix, mask, fill_value):
masked = np.ma.array(matrix, mask=mask, fill_value=fill_value)
return masked.filled()

+ 41
- 0
src/cli/opencv_transform/mask_to_maskref.py View File

@@ -0,0 +1,41 @@
import numpy as np
import cv2
import os

###
#
# maskdet_to_maskfin
#
#
###

# create_maskref ===============================================================
# return:
# maskref image
def create_maskref(cv_mask, cv_correct):

#Create a total green image
green = np.zeros((512,512,3), np.uint8)
green[:,:,:] = (0,255,0) # (B, G, R)

#Define the green color filter
f1 = np.asarray([0, 250, 0]) # green color filter
f2 = np.asarray([10, 255, 10])
#From mask, extrapolate only the green mask
green_mask = cv2.inRange(cv_mask, f1, f2) #green is 0

# (OPTIONAL) Apply dilate and open to mask
kernel = np.ones((5,5),np.uint8) #Try change it?
green_mask = cv2.dilate(green_mask, kernel, iterations = 1)
#green_mask = cv2.morphologyEx(green_mask, cv2.MORPH_OPEN, kernel)

# Create an inverted mask
green_mask_inv = cv2.bitwise_not(green_mask)

# Cut correct and green image, using the green_mask & green_mask_inv
res1 = cv2.bitwise_and(cv_correct, cv_correct, mask = green_mask_inv)
res2 = cv2.bitwise_and(green, green, mask = green_mask)

# Compone:
return cv2.add(res1, res2)

+ 519
- 0
src/cli/opencv_transform/maskdet_to_maskfin.py View File

@@ -0,0 +1,519 @@
import numpy as np
import cv2
import os
import random

#My library:
from opencv_transform.annotation import BodyPart

###
#
# maskdet_to_maskfin
#
# steps:
# 1. Extract annotation
# 1.a: Filter by color
# 1.b: Find ellipses
# 1.c: Filter out ellipses by max size, and max total numbers
# 1.d: Detect Problems
# 1.e: Resolve the problems, or discard the transformation
# 2. With the body list, draw maskfin, using maskref
#
###

# create_maskfin ==============================================================================
# return:
# (<Boolean> True/False), depending on the transformation process
def create_maskfin(maskref, maskdet):
#Create a total green image, in which draw details ellipses
details = np.zeros((512,512,3), np.uint8)
details[:,:,:] = (0,255,0) # (B, G, R)

#Extract body part features:
bodypart_list = extractAnnotations(maskdet);

#Check if the list is not empty:
if bodypart_list:
#Draw body part in details image:
for obj in bodypart_list:

if obj.w < obj.h:
aMax = int(obj.h/2) #asse maggiore
aMin = int(obj.w/2) #asse minore
angle = 0 #angle
else:
aMax = int(obj.w/2)
aMin = int(obj.h/2)
angle = 90

x = int(obj.x)
y = int(obj.y)

#Draw ellipse
if obj.name == "tit":
cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(0,205,0),-1) #(0,0,0,50)
elif obj.name == "aur":
cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(0,0,255),-1) #red
elif obj.name == "nip":
cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(255,255,255),-1) #white
elif obj.name == "belly":
cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(255,0,255),-1) #purple
elif obj.name == "vag":
cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(255,0,0),-1) #blue
elif obj.name == "hair":
xmin = x - int(obj.w/2)
ymin = y - int(obj.h/2)
xmax = x + int(obj.w/2)
ymax = y + int(obj.h/2)
cv2.rectangle(details,(xmin,ymin),(xmax,ymax),(100,100,100),-1)

#Define the green color filter
f1 = np.asarray([0, 250, 0]) # green color filter
f2 = np.asarray([10, 255, 10])
#From maskref, extrapolate only the green mask
green_mask = cv2.bitwise_not(cv2.inRange(maskref, f1, f2)) #green is 0

# Create an inverted mask
green_mask_inv = cv2.bitwise_not(green_mask)

# Cut maskref and detail image, using the green_mask & green_mask_inv
res1 = cv2.bitwise_and(maskref, maskref, mask = green_mask)
res2 = cv2.bitwise_and(details, details, mask = green_mask_inv)

# Compone:
maskfin = cv2.add(res1, res2)
return maskfin
# extractAnnotations ==============================================================================
# input parameter:
# (<string> maskdet_img): relative path of the single maskdet image (es: testimg1/maskdet/1.png)
# return:
# (<BodyPart []> bodypart_list) - for failure/error, return an empty list []
def extractAnnotations(maskdet):

#Load the image
#image = cv2.imread(maskdet_img)

#Find body part
tits_list = findBodyPart(maskdet, "tit")
aur_list = findBodyPart(maskdet, "aur")
vag_list = findBodyPart(maskdet, "vag")
belly_list = findBodyPart(maskdet, "belly")

#Filter out parts basing on dimension (area and aspect ratio):
aur_list = filterDimParts(aur_list, 100, 1000, 0.5, 3);
tits_list = filterDimParts(tits_list, 1000, 60000, 0.2, 3);
vag_list = filterDimParts(vag_list, 10, 1000, 0.2, 3);
belly_list = filterDimParts(belly_list, 10, 1000, 0.2, 3);

#Filter couple (if parts are > 2, choose only 2)
aur_list = filterCouple(aur_list);
tits_list = filterCouple(tits_list);

#Detect a missing problem:
missing_problem = detectTitAurMissingProblem(tits_list, aur_list) #return a Number (code of the problem)

#Check if problem is SOLVEABLE:
if (missing_problem in [3,6,7,8]):
resolveTitAurMissingProblems(tits_list, aur_list, missing_problem)
#Infer the nips:
nip_list = inferNip(aur_list)

#Infer the hair:
hair_list = inferHair(vag_list)

#Return a combined list:
return tits_list + aur_list + nip_list + vag_list + hair_list + belly_list

# findBodyPart ==============================================================================
# input parameters:
# (<RGB>image, <string>part_name)
# return
# (<BodyPart[]>list)
def findBodyPart(image, part_name):

bodypart_list = [] #empty BodyPart list

#Get the correct color filter:
if part_name == "tit":
#Use combined color filter
f1 = np.asarray([0, 0, 0]) # tit color filter
f2 = np.asarray([10, 10, 10])
f3 = np.asarray([0, 0, 250]) # aur color filter
f4 = np.asarray([0, 0, 255])
color_mask1 = cv2.inRange(image, f1, f2)
color_mask2 = cv2.inRange(image, f3, f4)
color_mask = cv2.bitwise_or(color_mask1, color_mask2) #combine
elif part_name == "aur":
f1 = np.asarray([0, 0, 250]) # aur color filter
f2 = np.asarray([0, 0, 255])
color_mask = cv2.inRange(image, f1, f2)
elif part_name == "vag":
f1 = np.asarray([250, 0, 0]) # vag filter
f2 = np.asarray([255, 0, 0])
color_mask = cv2.inRange(image, f1, f2)

elif part_name == "belly":
f1 = np.asarray([250, 0, 250]) # belly filter
f2 = np.asarray([255, 0, 255])
color_mask = cv2.inRange(image, f1, f2)

#find contours:
contours, hierarchy = cv2.findContours(color_mask,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

#for every contour:
for cnt in contours:

if len(cnt)>5: #at least 5 points to fit ellipse

#(x, y), (MA, ma), angle = cv2.fitEllipse(cnt)
ellipse = cv2.fitEllipse(cnt)

#Fit Result:
x = ellipse[0][0] #center x
y = ellipse[0][1] #center y
angle = ellipse[2] #angle
aMin = ellipse[1][0]; #asse minore
aMax = ellipse[1][1]; #asse maggiore

#Detect direction:
if angle == 0:
h = aMax
w = aMin
else:
h = aMin
w = aMax
#Normalize the belly size:
if part_name == "belly":
if w<15:
w *= 2
if h<15:
h *= 2

#Normalize the vag size:
if part_name == "vag":
if w<15:
w *= 2
if h<15:
h *= 2

#Calculate Bounding Box:
xmin = int(x - (w/2))
xmax = int(x + (w/2))
ymin = int(y - (h/2))
ymax = int(y + (h/2))

bodypart_list.append(BodyPart(part_name, xmin, ymin, xmax, ymax, x, y, w, h ))

return bodypart_list

# filterDimParts ==============================================================================
# input parameters:
# (<BodyPart[]>list, <num> minimum area of part, <num> max area, <num> min aspect ratio, <num> max aspect ratio)
def filterDimParts(bp_list, min_area, max_area, min_ar, max_ar):

b_filt = []

for obj in bp_list:

a = obj.w*obj.h #Object AREA
if ((a > min_area)and(a < max_area)):

ar = obj.w/obj.h #Object ASPECT RATIO

if ((ar>min_ar)and(ar<max_ar)):

b_filt.append(obj)

return b_filt

# filterCouple ==============================================================================
# input parameters:
# (<BodyPart[]>list)
def filterCouple(bp_list):

#Remove exceed parts
if (len(bp_list)>2):

#trovare coppia (a,b) che minimizza bp_list[a].y-bp_list[b].y
min_a = 0
min_b = 1
min_diff = abs(bp_list[min_a].y-bp_list[min_b].y)
for a in range(0,len(bp_list)):
for b in range(0,len(bp_list)):
#TODO: avoid repetition (1,0) (0,1)
if a != b:
diff = abs(bp_list[a].y-bp_list[b].y)
if diff<min_diff:
min_diff = diff
min_a = a
min_b = b
b_filt = []

b_filt.append(bp_list[min_a])
b_filt.append(bp_list[min_b])

return b_filt
else:
#No change
return bp_list



# detectTitAurMissingProblem ==============================================================================
# input parameters:
# (<BodyPart[]> tits list, <BodyPart[]> aur list)
# return
# (<num> problem code)
# TIT | AUR | code | SOLVE? |
# 0 | 0 | 1 | NO |
# 0 | 1 | 2 | NO |
# 0 | 2 | 3 | YES |
# 1 | 0 | 4 | NO |
# 1 | 1 | 5 | NO |
# 1 | 2 | 6 | YES |
# 2 | 0 | 7 | YES |
# 2 | 1 | 8 | YES |
def detectTitAurMissingProblem(tits_list, aur_list):

t_len = len(tits_list)
a_len = len(aur_list)

if (t_len == 0):
if (a_len == 0):
return 1
elif (a_len == 1):
return 2
elif (a_len == 2):
return 3
else:
return -1
elif (t_len == 1):
if (a_len == 0):
return 4
elif (a_len == 1):
return 5
elif (a_len == 2):
return 6
else:
return -1
elif (t_len == 2):
if (a_len == 0):
return 7
elif (a_len == 1):
return 8
else:
return -1
else:
return -1

# resolveTitAurMissingProblems ==============================================================================
# input parameters:
# (<BodyPart[]> tits list, <BodyPart[]> aur list, problem code)
# return
# none
def resolveTitAurMissingProblems(tits_list, aur_list, problem_code):

if problem_code == 3:

random_tit_factor = random.randint(2, 5) #TOTEST

#Add the first tit:
new_w = aur_list[0].w * random_tit_factor #TOTEST
new_x = aur_list[0].x
new_y = aur_list[0].y

xmin = int(new_x - (new_w/2))
xmax = int(new_x + (new_w/2))
ymin = int(new_y - (new_w/2))
ymax = int(new_y + (new_w/2))

tits_list.append(BodyPart("tit", xmin, ymin, xmax, ymax, new_x, new_y, new_w, new_w ))

#Add the second tit:
new_w = aur_list[1].w * random_tit_factor #TOTEST
new_x = aur_list[1].x
new_y = aur_list[1].y

xmin = int(new_x - (new_w/2))
xmax = int(new_x + (new_w/2))
ymin = int(new_y - (new_w/2))
ymax = int(new_y + (new_w/2))

tits_list.append(BodyPart("tit", xmin, ymin, xmax, ymax, new_x, new_y, new_w, new_w ))

elif problem_code == 6:

#Find wich aur is full:
d1 = abs(tits_list[0].x - aur_list[0].x)
d2 = abs(tits_list[0].x - aur_list[1].x)

if d1 > d2:
#aur[0] is empty
new_x = aur_list[0].x
new_y = aur_list[0].y
else:
#aur[1] is empty
new_x = aur_list[1].x
new_y = aur_list[1].y

#Calculate Bounding Box:
xmin = int(new_x - (tits_list[0].w/2))
xmax = int(new_x + (tits_list[0].w/2))
ymin = int(new_y - (tits_list[0].w/2))
ymax = int(new_y + (tits_list[0].w/2))

tits_list.append(BodyPart("tit", xmin, ymin, xmax, ymax, new_x, new_y, tits_list[0].w, tits_list[0].w ))

elif problem_code == 7:

#Add the first aur:
new_w = tits_list[0].w * random.uniform(0.03, 0.1) #TOTEST
new_x = tits_list[0].x
new_y = tits_list[0].y

xmin = int(new_x - (new_w/2))
xmax = int(new_x + (new_w/2))
ymin = int(new_y - (new_w/2))
ymax = int(new_y + (new_w/2))

aur_list.append(BodyPart("aur", xmin, ymin, xmax, ymax, new_x, new_y, new_w, new_w ))

#Add the second aur:
new_w = tits_list[1].w * random.uniform(0.03, 0.1) #TOTEST
new_x = tits_list[1].x
new_y = tits_list[1].y

xmin = int(new_x - (new_w/2))
xmax = int(new_x + (new_w/2))
ymin = int(new_y - (new_w/2))
ymax = int(new_y + (new_w/2))
aur_list.append(BodyPart("aur", xmin, ymin, xmax, ymax, new_x, new_y, new_w, new_w ))

elif problem_code == 8:

#Find wich tit is full:
d1 = abs(aur_list[0].x - tits_list[0].x)
d2 = abs(aur_list[0].x - tits_list[1].x)

if d1 > d2:
#tit[0] is empty
new_x = tits_list[0].x
new_y = tits_list[0].y
else:
#tit[1] is empty
new_x = tits_list[1].x
new_y = tits_list[1].y

#Calculate Bounding Box:
xmin = int(new_x - (aur_list[0].w/2))
xmax = int(new_x + (aur_list[0].w/2))
ymin = int(new_y - (aur_list[0].w/2))
ymax = int(new_y + (aur_list[0].w/2))
aur_list.append(BodyPart("aur", xmin, ymin, xmax, ymax, new_x, new_y, aur_list[0].w, aur_list[0].w ))

# detectTitAurPositionProblem ==============================================================================
# input parameters:
# (<BodyPart[]> tits list, <BodyPart[]> aur list)
# return
# (<Boolean> True/False)
def detectTitAurPositionProblem(tits_list, aur_list):

diffTitsX = abs(tits_list[0].x - tits_list[1].x)
if diffTitsX < 40:
print("diffTitsX")
#Tits too narrow (orizontally)
return True

diffTitsY = abs(tits_list[0].y - tits_list[1].y)
if diffTitsY > 120:
#Tits too distanced (vertically)
print("diffTitsY")
return True

diffTitsW = abs(tits_list[0].w - tits_list[1].w)
if ((diffTitsW < 0.1)or(diffTitsW>60)):
print("diffTitsW")
#Tits too equals, or too different (width)
return True

#Check if body position is too low (face not covered by watermark)
if aur_list[0].y > 350: #tits too low
#Calculate the ratio between y and aurs distance
rapp = aur_list[0].y/(abs(aur_list[0].x - aur_list[1].x))
if rapp > 2.8:
print("aurDown")
return True

return False

# inferNip ==============================================================================
# input parameters:
# (<BodyPart[]> aur list)
# return
# (<BodyPart[]> nip list)
def inferNip(aur_list):
nip_list = []

for aur in aur_list:

#Nip rules:
# - circle (w == h)
# - min dim: 5
# - bigger if aur is bigger
nip_dim = int(5 + aur.w*random.uniform(0.03, 0.09))

#center:
x = aur.x
y = aur.y

#Calculate Bounding Box:
xmin = int(x - (nip_dim/2))
xmax = int(x + (nip_dim/2))
ymin = int(y - (nip_dim/2))
ymax = int(y + (nip_dim/2))

nip_list.append(BodyPart("nip", xmin, ymin, xmax, ymax, x, y, nip_dim, nip_dim ))

return nip_list

# inferHair (TOTEST) ==============================================================================
# input parameters:
# (<BodyPart[]> vag list)
# return
# (<BodyPart[]> hair list)
def inferHair(vag_list):
hair_list = []

#70% of chanche to add hair
if random.uniform(0.0, 1.0) > 0.3:

for vag in vag_list:

#Hair rules:
hair_w = vag.w*random.uniform(0.4, 1.5)
hair_h = vag.h*random.uniform(0.4, 1.5)

#center:
x = vag.x
y = vag.y - (hair_h/2) - (vag.h/2)

#Calculate Bounding Box:
xmin = int(x - (hair_w/2))
xmax = int(x + (hair_w/2))
ymin = int(y - (hair_h/2))
ymax = int(y + (hair_h/2))

hair_list.append(BodyPart("hair", xmin, ymin, xmax, ymax, x, y, hair_w, hair_h ))

return hair_list

+ 29
- 0
src/cli/opencv_transform/nude_to_watermark.py View File

@@ -0,0 +1,29 @@
import cv2
import numpy as np
import os

# create_watermark ===============================================================
# return:
# (<Boolean> True/False), depending on the transformation process
def create_watermark(nude):

# Add alpha channel if missing
if nude.shape[2] < 4:
nude = np.dstack([nude, np.ones((512, 512), dtype="uint8") * 255])

return nude

watermark = cv2.imread("fake.png", cv2.IMREAD_UNCHANGED)

f1 = np.asarray([0, 0, 0, 250]) # red color filter
f2 = np.asarray([255, 255, 255, 255])
mask = cv2.bitwise_not(cv2.inRange(watermark, f1, f2))
mask_inv = cv2.bitwise_not(mask)

res1 = cv2.bitwise_and(nude, nude, mask=mask)
res2 = cv2.bitwise_and(watermark, watermark, mask=mask_inv)
res = cv2.add(res1, res2)

alpha = 0.6
return cv2.addWeighted(res, alpha, nude, 1 - alpha, 0)


+ 5
- 0
src/cli/requirements.txt View File

@@ -0,0 +1,5 @@
numpy==1.16.4
opencv-python==4.1.0.25
rsa==4.0
https://download.pytorch.org/whl/cu100/torch-1.1.0-cp36-cp36m-win_amd64.whl
torchvision==0.2.2.post3

+ 175
- 0
src/cli/run.py View File

@@ -0,0 +1,175 @@
import cv2
import os

# Import Neural Network Model
from gan import DataLoader, DeepModel, tensor2im

# OpenCv Transform:
from opencv_transform.mask_to_maskref import create_maskref
from opencv_transform.maskdet_to_maskfin import create_maskfin
from opencv_transform.dress_to_correct import create_correct
from opencv_transform.nude_to_watermark import create_watermark

"""
run.py

This script manage the entire transormation.

Transformation happens in 6 phases:
0: dress -> correct [opencv] dress_to_correct
1: correct -> mask: [GAN] correct_to_mask
2: mask -> maskref [opencv] mask_to_maskref
3: maskref -> maskdet [GAN] maskref_to_maskdet
4: maskdet -> maskfin [opencv] maskdet_to_maskfin
5: maskfin -> nude [GAN] maskfin_to_nude
6: nude -> watermark [opencv] nude_to_watermark

"""

phases = [
"dress_to_correct",
"correct_to_mask",
"mask_to_maskref",
"maskref_to_maskdet",
"maskdet_to_maskfin",
"maskfin_to_nude",
"nude_to_watermark",
] # ,


class Options:
# Init options with default values
def __init__(self):

# experiment specifics
self.norm = "batch" # instance normalization or batch normalization
self.use_dropout = False # use dropout for the generator
self.data_type = 32 # Supported data type i.e. 8, 16, 32 bit

# input/output sizes
self.batchSize = 1 # input batch size
self.input_nc = 3 # of input image channels
self.output_nc = 3 # of output image channels

# for setting inputs
# if true, takes images in order to make batches, otherwise takes them randomly
self.serial_batches = True
self.nThreads = 0 # threads for loading data (???)
# Maximum number of samples allowed per dataset. If the dataset directory contains more than max_dataset_size, only a subset is loaded.
self.max_dataset_size = 1

# for generator
self.netG = "global" # selects model to use for netG
self.ngf = 64 # of gen filters in first conv layer
self.n_downsample_global = 4 # number of downsampling layers in netG
self.n_blocks_global = (
9
) # number of residual blocks in the global generator network
self.n_blocks_local = (
0
) # number of residual blocks in the local enhancer network
self.n_local_enhancers = 0 # number of local enhancers to use
# number of epochs that we only train the outmost local enhancer
self.niter_fix_global = 0

# Phase specific options
self.checkpoints_dir = ""
self.dataroot = ""

# Changes options accordlying to actual phase
def updateOptions(self, phase):
directory = os.path.dirname(os.path.realpath(__file__))

if phase == "correct_to_mask":
self.checkpoints_dir = os.path.join(directory, "checkpoints", "cm.lib")

elif phase == "maskref_to_maskdet":
self.checkpoints_dir = os.path.join(directory, "checkpoints", "mm.lib")

elif phase == "maskfin_to_nude":
self.checkpoints_dir = os.path.join(directory, "checkpoints", "mn.lib")


# process(cv_img, mode)
# return:
# watermark image


def process(cv_img, gpu_id):

# InMemory cv2 images:
dress = cv_img
correct = None
mask = None
maskref = None
maskfin = None
maskdet = None
nude = None
watermark = None

print("GPU ID: " + str(gpu_id), flush=True)

for index, phase in enumerate(phases):
print("Executing phase: " + phase, flush=True)

# GAN phases:
if (
(phase == "correct_to_mask")
or (phase == "maskref_to_maskdet")
or (phase == "maskfin_to_nude")
):

# Load global option
opt = Options()

# Load custom phase options:
opt.updateOptions(phase)

# Load Data
if phase == "correct_to_mask":
data_loader = DataLoader(opt, correct)
elif phase == "maskref_to_maskdet":
data_loader = DataLoader(opt, maskref)
elif phase == "maskfin_to_nude":
data_loader = DataLoader(opt, maskfin)

dataset = data_loader.load_data()

# Create Model
model = DeepModel()
model.initialize(opt, gpu_id)

# Run for every image:
for i, data in enumerate(dataset):

generated = model.inference(data["label"], data["inst"])

im = tensor2im(generated.data[0])

# Save Data
if phase == "correct_to_mask":
mask = cv2.cvtColor(im, cv2.COLOR_RGB2BGR)

elif phase == "maskref_to_maskdet":
maskdet = cv2.cvtColor(im, cv2.COLOR_RGB2BGR)

elif phase == "maskfin_to_nude":
nude = cv2.cvtColor(im, cv2.COLOR_RGB2BGR)

# Correcting:
elif phase == "dress_to_correct":
correct = create_correct(dress)

# mask_ref phase (opencv)
elif phase == "mask_to_maskref":
maskref = create_maskref(mask, correct)

# mask_fin phase (opencv)
elif phase == "maskdet_to_maskfin":
maskfin = create_maskfin(maskref, maskdet)

# nude_to_watermark phase (opencv)
elif phase == "nude_to_watermark":
watermark = create_watermark(nude)

return watermark

+ 70
- 0
src/gui/.eslintrc.json View File

@@ -0,0 +1,70 @@
{
"plugins": ["lodash", "vue", "prettier"],
"extends": [
"@nuxtjs",
"plugin:nuxt/recommended",
"plugin:lodash/recommended",
"plugin:vue/recommended",
"airbnb-base",
"prettier"
],
"env": {
"shared-node-browser": true,
"es6": true,
"browser": true,
"node": true
},
"parserOptions": {
"parser": "babel-eslint",
"ecmaVersion": 8,
"sourceType": "module"
},
"rules": {
"class-methods-use-this": "off",
"global-require": "off",
"import/no-unresolved": "off",
"padded-blocks": ["error", "never"],
"no-await-in-loop": "warn",
"no-console": "off",
"no-continue": "off",
"no-param-reassign": "off",
"no-restricted-syntax": "off",
"no-useless-constructor": "warn",
"no-underscore-dangle": "off",
"no-shadow": "off",
"no-unused-vars": "warn",
"no-debugger": "warn",
"no-restricted-globals": "warn",
"no-unreachable": "warn",
"object-shorthand": ["error", "always"],
"quote-props": ["error", "as-needed"],
"spaced-comment": "warn",
"vue/html-indent": ["warn", 2],
"vue/html-self-closing": "off",
"vue/html-closing-bracket-newline": [
"warn",
{
"singleline": "never",
"multiline": "never"
}
],
"lodash/import-scope": ["warn", "full"],
"lodash/prefer-lodash-method": "warn",
"lodash/prefer-immutable-method": "warn",
"lodash/prefer-noop": "off",
"lodash/prefer-includes": "warn",
"lodash/prefer-lodash-typecheck": "warn",
"lodash/prefer-constant": "off",
"import/order": "warn",
"import/no-extraneous-dependencies": [
"off",
{
"devDependencies": true,
"optionalDependencies": false,
"peerDependencies": false
}
],
"nuxt/no-globals-in-created": "off"
},
"globals": {}
}

+ 88
- 0
src/gui/.gitignore View File

@@ -0,0 +1,88 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# parcel-bundler cache (https://parceljs.org/)
.cache

# next.js build output
.next

# nuxt.js build output
.nuxt

# Nuxt generate
dist

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless

# IDE / Editor
.idea
.editorconfig

# Service worker
sw.*

# Mac OSX
.DS_Store

+ 7
- 0
src/gui/.prettierrc View File

@@ -0,0 +1,7 @@
{
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true,
"endOfLine": "lf"
}

+ 22
- 0
src/gui/README.md View File

@@ -0,0 +1,22 @@
# easy-deep-nude-gui

> My smashing Nuxt.js project

## Build Setup

``` bash
# install dependencies
$ yarn install

# serve with hot reload at localhost:3000
$ yarn dev

# build for production and launch server
$ yarn build
$ yarn start

# generate static project
$ yarn generate
```

For detailed explanation on how things work, checkout [Nuxt.js docs](https://nuxtjs.org).

+ 7
- 0
src/gui/assets/README.md View File

@@ -0,0 +1,7 @@
# ASSETS

**This directory is not required, you can delete it if you don't want to use it.**

This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.

More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked).

+ 22
- 0
src/gui/assets/css/base/_reset.scss View File

@@ -0,0 +1,22 @@
*,
*:before,
*:after {
box-sizing: border-box;
margin: 0;
}

html {
font-family: theme('fontFamily.sans');
font-size: 16px;
word-spacing: 1px;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
box-sizing: border-box;
}

body,
html {
@apply overflow-hidden;
}

+ 4
- 0
src/gui/assets/css/components/_all.scss View File

@@ -0,0 +1,4 @@
@import './button';
@import './section';
@import './container';
@import './form';

+ 38
- 0
src/gui/assets/css/components/_button.scss View File

@@ -0,0 +1,38 @@
.button {
@apply border
border-gray-300
flex
items-center
justify-center
px-3
rounded;

height: 40px;
transition: all 0.1s ease-in-out;
outline: none !important;

&:hover {
@apply bg-gray-200;
}

&.is-primary {
@apply bg-primary text-white;

&:hover {
@apply bg-primary-600;
}
}

&.is-danger {
@apply bg-danger text-white;

&:hover {
@apply bg-danger;
}
}

&.is-xl {
@apply px-6 text-xl;
height: 50px;
}
}

+ 16
- 0
src/gui/assets/css/components/_container.scss View File

@@ -0,0 +1,16 @@
.placeholder-container {
@apply h-screen flex flex-col justify-center;

.container-body {
@apply flex-1;
}

.container-footer {
@apply flex justify-center items-center border-t border-gray-300;
min-height: 80px;

.button {
@apply mx-3;
}
}
}

+ 15
- 0
src/gui/assets/css/components/_form.scss View File

@@ -0,0 +1,15 @@
.field {
@apply mb-4;

.field-help {
@apply block text-xs text-gray-600;
}
}

.label {
@apply block font-bold;
}

.input {
@apply border border-gray-300 p-1 block;
}

+ 22
- 0
src/gui/assets/css/components/_section.scss View File

@@ -0,0 +1,22 @@
.section {
&:not(:last-child) {
@apply mb-6;
}

&.box {
@apply bg-white
shadow
border
border-gray-300
p-4
rounded;
}

.section-header {
@apply mb-3;
}

.section-header-title {
@apply font-bold text-lg;
}
}

+ 1
- 0
src/gui/assets/css/fonts.scss View File

@@ -0,0 +1 @@
@import url('https://fonts.googleapis.com/css?family=Nunito:300,400,600,700,800,900');

+ 6
- 0
src/gui/assets/css/tailwind.scss View File

@@ -0,0 +1,6 @@
@tailwind base;
@import './base/reset';
@tailwind components;
@import './components/all';
@tailwind utilities;
@import './utilities/all';

+ 0
- 0
src/gui/assets/css/utilities/_all.scss View File


+ 5
- 0
src/gui/assets/css/vendor.scss View File

@@ -0,0 +1,5 @@
// tippy.js
@import 'tippy.js/index.css';
@import 'tippy.js/themes/light.css';

@import 'cropperjs/dist/cropper.css';

+ 79
- 0
src/gui/components/Logo.vue View File

@@ -0,0 +1,79 @@
<template>
<div class="VueToNuxtLogo">
<div class="Triangle Triangle--two" />
<div class="Triangle Triangle--one" />
<div class="Triangle Triangle--three" />
<div class="Triangle Triangle--four" />
</div>
</template>

<style>
.VueToNuxtLogo {
display: inline-block;
animation: turn 2s linear forwards 1s;
transform: rotateX(180deg);
position: relative;
overflow: hidden;
height: 180px;
width: 245px;
}

.Triangle {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
}

.Triangle--one {
border-left: 105px solid transparent;
border-right: 105px solid transparent;
border-bottom: 180px solid #41b883;
}

.Triangle--two {
top: 30px;
left: 35px;
animation: goright 0.5s linear forwards 3.5s;
border-left: 87.5px solid transparent;
border-right: 87.5px solid transparent;
border-bottom: 150px solid #3b8070;
}

.Triangle--three {
top: 60px;
left: 35px;
animation: goright 0.5s linear forwards 3.5s;
border-left: 70px solid transparent;
border-right: 70px solid transparent;
border-bottom: 120px solid #35495e;
}

.Triangle--four {
top: 120px;
left: 70px;
animation: godown 0.5s linear forwards 3s;
border-left: 35px solid transparent;
border-right: 35px solid transparent;
border-bottom: 60px solid #fff;
}

@keyframes turn {
100% {
transform: rotateX(0deg);
}
}

@keyframes godown {
100% {
top: 180px;
}
}

@keyframes goright {
100% {
left: 70px;
}
}
</style>

+ 7
- 0
src/gui/components/README.md View File

@@ -0,0 +1,7 @@
# COMPONENTS

**This directory is not required, you can delete it if you don't want to use it.**

The components directory contains your Vue.js Components.

_Nuxt.js doesn't supercharge these components._

+ 1
- 0
src/gui/components/index.js View File

@@ -0,0 +1 @@
import Vue from 'vue'

+ 7
- 0
src/gui/layouts/README.md View File

@@ -0,0 +1,7 @@
# LAYOUTS

**This directory is not required, you can delete it if you don't want to use it.**

This directory contains your Application Layouts.

More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts).

+ 8
- 0
src/gui/layouts/default.vue View File

@@ -0,0 +1,8 @@
<template>
<div class="layout">
<nuxt />
</div>
</template>

<style>
</style>

+ 74
- 0
src/gui/main.js View File

@@ -0,0 +1,74 @@
const { app, BrowserWindow } = require('electron')
const { Nuxt } = require('nuxt')
const http = require('http')
const path = require('path')
const config = require('./nuxt.config')

config.rootDir = __dirname

function startNuxtApp() {
const nuxt = new Nuxt(config)

console.log(config.dev)

if (!config.dev) {
const server = http.createServer(nuxt.render)
server.listen()

return `http://localhost:${server.address().port}`
}

return `http://localhost:3000`
}

function createWindow() {
const url = startNuxtApp()

// Create the browser window.
const window = new BrowserWindow({
width: 700,
height: 800,
minWidth: 700,
minHeight: 800,
webPreferences: {
preload: path.join(app.getAppPath(), 'preload.js')
}
})

if (config.dev) {
const {
default: installExtension,
VUEJS_DEVTOOLS
} = require('electron-devtools-installer')

installExtension(VUEJS_DEVTOOLS.id)
.then(name => {
console.log(`Added Extension: ${name}`)
window.webContents.openDevTools()
})
.catch(err => console.log('An error occurred: ', err))

window.loadURL(url)
} else {
const pollServer = () => {
console.log('pollServer')

http
.get(url, res => {
if (res.statusCode === 200) {
console.log('ready')
window.loadURL(url)
} else {
setTimeout(pollServer, 300)
}
})
.on('error', pollServer)
}

pollServer()
}
}

app.on('ready', createWindow)
app.on('window-all-closed', () => app.quit())
//app.on('activate', () => win === null && newWin())

+ 8
- 0
src/gui/middleware/README.md View File

@@ -0,0 +1,8 @@
# MIDDLEWARE

**This directory is not required, you can delete it if you don't want to use it.**

This directory contains your application middleware.
Middleware let you define custom functions that can be run before rendering either a page or a group of pages.

More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware).

+ 7
- 0
src/gui/middleware/nudity.js View File

@@ -0,0 +1,7 @@
import _ from 'lodash'

export default function({ store, redirect }) {
if (_.isNil(store.state.nudity.filepath)) {
redirect('/')
}
}

+ 11
- 0
src/gui/mixins/BaseMixin.js View File

@@ -0,0 +1,11 @@
export default {
filters: {},

data: () => ({
app: {
name: process.env.APP_NAME,
version: process.env.npm_package_version,
status: process.env.APP_STATUS
}
})
}

+ 85
- 0
src/gui/nuxt.config.js View File

@@ -0,0 +1,85 @@
require('dotenv').config()

module.exports = {
mode: 'spa',
/*
** Headers of the page
*/
head: {
title: `${process.env.APP_NAME} ${process.env.npm_package_version} [${process.env.APP_STATUS}]`,

meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ 'http-equiv': 'Content-Security-Policy', content: "'unsafe-inline'" }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
{
rel: 'stylesheet',
href: 'https://use.fontawesome.com/releases/v5.8.1/css/all.css',
integrity:
'sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf',
crossorigin: 'anonymous'
}
]
},

/*
** Customize the progress-bar color
*/
loading: { color: '#fff' },

/*
** Global CSS
*/
css: [
'~/assets/css/tailwind.scss',
'~/assets/css/fonts.scss',
'~/assets/css/vendor.scss'
],

/*
** Plugins to load before mounting the App
*/
plugins: ['~/plugins/boot.client.js', '~/components'],

/*
** Nuxt.js modules
*/
modules: [
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
'@nuxtjs/pwa',
//'@nuxtjs/eslint-module',
'@nuxtjs/dotenv'
],

/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {},

dev: process.env.NODE_ENV === 'DEV',

/*
** Build configuration
*/
build: {
postcss: {
plugins: {
tailwindcss: './tailwind.config.js'
}
},

/*
** You can extend webpack config here
*/
extend(config, { isClient }) {
if (isClient) {
//config.target = 'electron-renderer'
}
}
}
}

+ 79
- 0
src/gui/package.json View File

@@ -0,0 +1,79 @@
{
"name": "easy-deep-nude-gui",
"version": "0.0.1",
"description": "Easy-DeepNude GUI",
"author": "deep-man-yy",
"private": true,
"main": "main.js",
"build": {
"appId": "com.deepmanyy.edp-gui",
"productName": "EasyDeepNude",
"directories": {
"output": "../../dist/gui"
},
"nsis": {
"oneClick": false
}
},
"scripts": {
"lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
"precommit": "yarn lint",
"dev": "cross-env NODE_ENV=DEV nuxt",
"dev-gui": "cross-env NODE_ENV=DEV electron .",
"production-gui": "cross-env NODE_ENV=PRODUCTION electron .",
"build-old": "cross-env NODE_ENV=PRODUCTION nuxt build && cross-env NODE_ENV=PRODUCTION electron-builder",
"build": "cross-env NODE_ENV=PRODUCTION electron-builder",
"pack": "electron-builder --dir"
},
"dependencies": {
"@nuxtjs/axios": "^5.3.6",
"@nuxtjs/pwa": "^2.6.0",
"@nuxtjs/dotenv": "^1.3.0",
"cropperjs": "^1.5.2",
"image-to-base64": "^2.0.1",
"js-event-bus": "^1.0.0",
"mime-types": "^2.1.24",
"nuxt": "^2.0.0",
"clipboard": "^2.0.4",
"debug": "^4.1.1",
"downloadjs": "^1.4.7",
"lodash": "^4.17.11",
"md5": "^2.2.1",
"moment": "^2.24.0",
"queue": "^6.0.1",
"randomcolor": "^0.5.4",
"supports-color": "^7.0.0",
"sweetalert": "^2.1.2",
"tippy.js": "^4.3.4",
"uuid": "^3.3.2"
},
"devDependencies": {
"@babel/cli": "^7.5.0",
"@babel/core": "^7.5.0",