Spaces:
Running
Running
Upload 27 files
Browse files- .gitattributes +4 -0
- app.py +197 -0
- checkpoints_not empty.txt +0 -0
- cm.lib +3 -0
- example1.png +3 -0
- example10.webp +0 -0
- example2.png +0 -0
- example3.png +0 -0
- example4.png +0 -0
- example5.webp +0 -0
- example6.webp +0 -0
- example7.jpeg +0 -0
- example8.webp +0 -0
- example9.webp +0 -0
- gan.py +240 -0
- input.jpg +0 -0
- input.png +0 -0
- input_not empty.txt +0 -0
- mm.lib +3 -0
- mn.lib +3 -0
- opencv_transform___init__.py +0 -0
- opencv_transform_annotation.py +17 -0
- opencv_transform_dress_to_correct.py +64 -0
- opencv_transform_mask_to_maskref.py +41 -0
- opencv_transform_maskdet_to_maskfin.py +519 -0
- opencv_transform_nude_to_watermark.py +28 -0
- requirements.txt +9 -0
- run.py +150 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
cm.lib filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
example1.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
mm.lib filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
mn.lib filter=lfs diff=lfs merge=lfs -text
|
app.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from run import process
|
| 2 |
+
import time
|
| 3 |
+
import subprocess
|
| 4 |
+
import os
|
| 5 |
+
import argparse
|
| 6 |
+
import cv2
|
| 7 |
+
import sys
|
| 8 |
+
from PIL import Image
|
| 9 |
+
import torch
|
| 10 |
+
import gradio as gr
|
| 11 |
+
|
| 12 |
+
TESTdevice = "cpu"
|
| 13 |
+
index = 1
|
| 14 |
+
|
| 15 |
+
def mainTest(inputpath, outpath):
|
| 16 |
+
watermark = deep_nude_process(inputpath)
|
| 17 |
+
watermark1 = cv2.cvtColor(watermark, cv2.COLOR_BGRA2RGBA)
|
| 18 |
+
return watermark1
|
| 19 |
+
|
| 20 |
+
def deep_nude_process(inputpath):
|
| 21 |
+
dress = cv2.imread(inputpath)
|
| 22 |
+
h = dress.shape[0]
|
| 23 |
+
w = dress.shape[1]
|
| 24 |
+
dress = cv2.resize(dress, (512, 512), interpolation=cv2.INTER_CUBIC)
|
| 25 |
+
watermark = process(dress)
|
| 26 |
+
watermark = cv2.resize(watermark, (w, h), interpolation=cv2.INTER_CUBIC)
|
| 27 |
+
return watermark
|
| 28 |
+
|
| 29 |
+
def inference(img):
|
| 30 |
+
global index
|
| 31 |
+
bgra = cv2.cvtColor(img, cv2.COLOR_RGBA2BGRA)
|
| 32 |
+
inputpath = f"input_{index}.jpg"
|
| 33 |
+
cv2.imwrite(inputpath, bgra)
|
| 34 |
+
|
| 35 |
+
outputpath = f"out_{index}.jpg"
|
| 36 |
+
index += 1
|
| 37 |
+
print(time.strftime("START!!!!!!!!! %Y-%m-%d %H:%M:%S", time.localtime()))
|
| 38 |
+
output = mainTest(inputpath, outputpath)
|
| 39 |
+
print(time.strftime("Finish!!!!!!!!! %Y-%m-%d %H:%M:%S", time.localtime()))
|
| 40 |
+
return output
|
| 41 |
+
|
| 42 |
+
def load_image_from_file(file_path, new_height=None):
|
| 43 |
+
"""
|
| 44 |
+
Load an image from a file and optionally resize it while maintaining the aspect ratio.
|
| 45 |
+
|
| 46 |
+
Args:
|
| 47 |
+
file_path (str): The path to the image file.
|
| 48 |
+
new_height (int, optional): The new height for the image. If None, the image is not resized.
|
| 49 |
+
|
| 50 |
+
Returns:
|
| 51 |
+
Image: The loaded (and optionally resized) image.
|
| 52 |
+
"""
|
| 53 |
+
try:
|
| 54 |
+
img = Image.open(file_path)
|
| 55 |
+
|
| 56 |
+
if (new_height is not None):
|
| 57 |
+
# Calculate new width to maintain aspect ratio
|
| 58 |
+
aspect_ratio = img.width / img.height
|
| 59 |
+
new_width = int(new_height * aspect_ratio)
|
| 60 |
+
|
| 61 |
+
# Resize the image
|
| 62 |
+
img = img.resize((new_width, new_height), Image.LANCZOS)
|
| 63 |
+
|
| 64 |
+
return img
|
| 65 |
+
except FileNotFoundError:
|
| 66 |
+
print(f"File not found: {file_path}")
|
| 67 |
+
return None
|
| 68 |
+
except Image.UnidentifiedImageError:
|
| 69 |
+
print(f"Cannot identify image file: {file_path}")
|
| 70 |
+
return None
|
| 71 |
+
except Exception as e:
|
| 72 |
+
print(f"Error loading image from file: {e}")
|
| 73 |
+
return None
|
| 74 |
+
|
| 75 |
+
title = "Undress AI"
|
| 76 |
+
description = "⛔ Input photos of people, similar to the test picture at the bottom, and undress pictures will be produced. You may have to wait 30 seconds for a picture. 🔞 Do not upload personal photos 🔞 There is a queue system. According to the logic of first come, first served, only one picture will be made at a time. Must be able to at least see the outline of a human body ⛔"
|
| 77 |
+
|
| 78 |
+
examples = [
|
| 79 |
+
[load_image_from_file('example9.webp')],
|
| 80 |
+
[load_image_from_file('example2.png')],
|
| 81 |
+
[load_image_from_file('example1.png')],
|
| 82 |
+
[load_image_from_file('example5.webp')],
|
| 83 |
+
[load_image_from_file('example6.webp')],
|
| 84 |
+
[load_image_from_file('example8.webp')],
|
| 85 |
+
]
|
| 86 |
+
|
| 87 |
+
js='''
|
| 88 |
+
<script>
|
| 89 |
+
window.cur_process_step = "";
|
| 90 |
+
function getEnvInfo() {
|
| 91 |
+
const result = {};
|
| 92 |
+
// Get URL parameters
|
| 93 |
+
const urlParams = new URLSearchParams(window.location.search);
|
| 94 |
+
for (const [key, value] of urlParams) {
|
| 95 |
+
result[key] = value;
|
| 96 |
+
}
|
| 97 |
+
// Get current domain and convert to lowercase
|
| 98 |
+
result["__domain"] = window.location.hostname.toLowerCase();
|
| 99 |
+
// Get iframe parent domain, if any, and convert to lowercase
|
| 100 |
+
try {
|
| 101 |
+
if (window.self !== window.top) {
|
| 102 |
+
result["__iframe_domain"] = document.referrer
|
| 103 |
+
? new URL(document.referrer).hostname.toLowerCase()
|
| 104 |
+
: "unable to get iframe parent domain";
|
| 105 |
+
}else{
|
| 106 |
+
result["__iframe_domain"] = "";
|
| 107 |
+
}
|
| 108 |
+
} catch (e) {
|
| 109 |
+
result["__iframe_domain"] = "unable to access iframe parent domain";
|
| 110 |
+
}
|
| 111 |
+
return result;
|
| 112 |
+
}
|
| 113 |
+
function isValidEnv(){
|
| 114 |
+
envInfo = getEnvInfo();
|
| 115 |
+
return envInfo["e"] == "1" ||
|
| 116 |
+
envInfo["__domain"].indexOf("nsfwais.io") != -1 ||
|
| 117 |
+
envInfo["__iframe_domain"].indexOf("nsfwais.io") != -1 ||
|
| 118 |
+
envInfo["__domain"].indexOf("127.0.0.1") != -1 ||
|
| 119 |
+
envInfo["__iframe_domain"].indexOf("127.0.0.1") != -1;
|
| 120 |
+
}
|
| 121 |
+
window.postMessageToParent = function(img, event, source, value) {
|
| 122 |
+
// Construct the message object with the provided parameters
|
| 123 |
+
console.log("post start",event, source, value);
|
| 124 |
+
const message = {
|
| 125 |
+
event: event,
|
| 126 |
+
source: source,
|
| 127 |
+
value: value
|
| 128 |
+
};
|
| 129 |
+
|
| 130 |
+
// Post the message to the parent window
|
| 131 |
+
window.parent.postMessage(message, '*');
|
| 132 |
+
console.log("post finish");
|
| 133 |
+
window.cur_process_step = "process";
|
| 134 |
+
return img;
|
| 135 |
+
}
|
| 136 |
+
function uploadImage(image, event, source, value) {
|
| 137 |
+
// Ensure we're in an iframe
|
| 138 |
+
if (window.cur_process_step != "process"){
|
| 139 |
+
return;
|
| 140 |
+
}
|
| 141 |
+
window.cur_process_step = "";
|
| 142 |
+
console.log("uploadImage", image ? image.url : null, event, source, value);
|
| 143 |
+
// Get the first image from the gallery (assuming it's an array)
|
| 144 |
+
let imageUrl = image ? image.url : null;
|
| 145 |
+
if (window.self !== window.top) {
|
| 146 |
+
// Post the message to the parent window
|
| 147 |
+
// Prepare the data to send
|
| 148 |
+
let data = {
|
| 149 |
+
event: event,
|
| 150 |
+
source: source,
|
| 151 |
+
value: imageUrl
|
| 152 |
+
};
|
| 153 |
+
window.parent.postMessage(data, '*');
|
| 154 |
+
} else if (isValidEnv()){
|
| 155 |
+
try{
|
| 156 |
+
sendCustomEventToDataLayer({},event,source,{"image":imageUrl})
|
| 157 |
+
} catch (error) {
|
| 158 |
+
console.error("Error in sendCustomEventToDataLayer:", error);
|
| 159 |
+
}
|
| 160 |
+
}else{
|
| 161 |
+
console.log("Not in an iframe, can't post to parent");
|
| 162 |
+
}
|
| 163 |
+
return;
|
| 164 |
+
}
|
| 165 |
+
window.onDemoLoad = function(x){
|
| 166 |
+
let envInfo = getEnvInfo();
|
| 167 |
+
console.log(envInfo);
|
| 168 |
+
if (isValidEnv()){
|
| 169 |
+
var element = document.getElementById("pitch_desc_html_code");
|
| 170 |
+
if (element) {
|
| 171 |
+
element.parentNode.removeChild(element);
|
| 172 |
+
}
|
| 173 |
+
}
|
| 174 |
+
return "";
|
| 175 |
+
}
|
| 176 |
+
</script>
|
| 177 |
+
'''
|
| 178 |
+
|
| 179 |
+
with gr.Blocks(head=js, theme="Nymbo/Alyx_Theme") as demo:
|
| 180 |
+
width=240
|
| 181 |
+
height=340
|
| 182 |
+
|
| 183 |
+
with gr.Row(equal_height=False):
|
| 184 |
+
with gr.Column(min_width=240): # Adjust scale for proper sizing
|
| 185 |
+
image_input = gr.Image(type="numpy", label="", height=height)
|
| 186 |
+
gr.Examples(examples=examples, inputs=image_input, examples_per_page=10, elem_id="example_img")
|
| 187 |
+
process_button = gr.Button("Run", size="sm")
|
| 188 |
+
|
| 189 |
+
def update_status(img):
|
| 190 |
+
processed_img = inference(img)
|
| 191 |
+
return processed_img
|
| 192 |
+
|
| 193 |
+
image_input.change(fn=lambda x: x, inputs=[image_input], outputs=[gr.State([])], js='''(img) => window.uploadImage(img, "process_finished", "demo_hf_deepnude_gan_card", "")''')
|
| 194 |
+
process_button.click(update_status, inputs=image_input, outputs=image_input, js='''(i) => window.postMessageToParent(i, "process_started", "demo_hf_deepnude_gan_card", "click_nude")''')
|
| 195 |
+
demo.load(fn=lambda x: x, inputs=[gr.State([])], outputs=[gr.State([])], js='''(x) => window.onDemoLoad(x)''')
|
| 196 |
+
demo.queue(max_size=10)
|
| 197 |
+
demo.launch()
|
checkpoints_not empty.txt
ADDED
|
File without changes
|
cm.lib
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6fa7ac307a818e99671a9cfa053d23301fe59c38c6337cb994a4b1bce4321856
|
| 3 |
+
size 729785444
|
example1.png
ADDED
|
Git LFS Details
|
example10.webp
ADDED
|
example2.png
ADDED
|
example3.png
ADDED
|
example4.png
ADDED
|
example5.webp
ADDED
|
example6.webp
ADDED
|
example7.jpeg
ADDED
|
example8.webp
ADDED
|
example9.webp
ADDED
|
gan.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from PIL import Image
|
| 2 |
+
import numpy as np
|
| 3 |
+
import cv2
|
| 4 |
+
import torchvision.transforms as transforms
|
| 5 |
+
import torch
|
| 6 |
+
import io
|
| 7 |
+
import os
|
| 8 |
+
import functools
|
| 9 |
+
|
| 10 |
+
class DataLoader():
|
| 11 |
+
|
| 12 |
+
def __init__(self, opt, cv_img):
|
| 13 |
+
super(DataLoader, self).__init__()
|
| 14 |
+
|
| 15 |
+
self.dataset = Dataset()
|
| 16 |
+
self.dataset.initialize(opt, cv_img)
|
| 17 |
+
|
| 18 |
+
self.dataloader = torch.utils.data.DataLoader(
|
| 19 |
+
self.dataset,
|
| 20 |
+
batch_size=opt.batchSize,
|
| 21 |
+
shuffle=not opt.serial_batches,
|
| 22 |
+
num_workers=int(opt.nThreads))
|
| 23 |
+
|
| 24 |
+
def load_data(self):
|
| 25 |
+
return self.dataloader
|
| 26 |
+
|
| 27 |
+
def __len__(self):
|
| 28 |
+
return 1
|
| 29 |
+
|
| 30 |
+
class Dataset(torch.utils.data.Dataset):
|
| 31 |
+
def __init__(self):
|
| 32 |
+
super(Dataset, self).__init__()
|
| 33 |
+
|
| 34 |
+
def initialize(self, opt, cv_img):
|
| 35 |
+
self.opt = opt
|
| 36 |
+
self.root = opt.dataroot
|
| 37 |
+
|
| 38 |
+
self.A = Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB))
|
| 39 |
+
self.dataset_size = 1
|
| 40 |
+
|
| 41 |
+
def __getitem__(self, index):
|
| 42 |
+
|
| 43 |
+
transform_A = get_transform(self.opt)
|
| 44 |
+
A_tensor = transform_A(self.A.convert('RGB'))
|
| 45 |
+
|
| 46 |
+
B_tensor = inst_tensor = feat_tensor = 0
|
| 47 |
+
|
| 48 |
+
input_dict = {'label': A_tensor, 'inst': inst_tensor, 'image': B_tensor,
|
| 49 |
+
'feat': feat_tensor, 'path': ""}
|
| 50 |
+
|
| 51 |
+
return input_dict
|
| 52 |
+
|
| 53 |
+
def __len__(self):
|
| 54 |
+
return 1
|
| 55 |
+
|
| 56 |
+
class DeepModel(torch.nn.Module):
|
| 57 |
+
|
| 58 |
+
def initialize(self, opt):
|
| 59 |
+
|
| 60 |
+
torch.cuda.empty_cache()
|
| 61 |
+
|
| 62 |
+
self.opt = opt
|
| 63 |
+
|
| 64 |
+
self.gpu_ids = [] #FIX CPU
|
| 65 |
+
|
| 66 |
+
self.netG = self.__define_G(opt.input_nc, opt.output_nc, opt.ngf, opt.netG,
|
| 67 |
+
opt.n_downsample_global, opt.n_blocks_global, opt.n_local_enhancers,
|
| 68 |
+
opt.n_blocks_local, opt.norm, self.gpu_ids)
|
| 69 |
+
|
| 70 |
+
# load networks
|
| 71 |
+
self.__load_network(self.netG)
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def inference(self, label, inst):
|
| 76 |
+
|
| 77 |
+
# Encode Inputs
|
| 78 |
+
input_label, inst_map, _, _ = self.__encode_input(label, inst, infer=True)
|
| 79 |
+
|
| 80 |
+
# Fake Generation
|
| 81 |
+
input_concat = input_label
|
| 82 |
+
|
| 83 |
+
with torch.no_grad():
|
| 84 |
+
fake_image = self.netG.forward(input_concat)
|
| 85 |
+
|
| 86 |
+
return fake_image
|
| 87 |
+
|
| 88 |
+
# helper loading function that can be used by subclasses
|
| 89 |
+
def __load_network(self, network):
|
| 90 |
+
|
| 91 |
+
save_path = os.path.join(self.opt.checkpoints_dir)
|
| 92 |
+
|
| 93 |
+
network.load_state_dict(torch.load(save_path))
|
| 94 |
+
|
| 95 |
+
def __encode_input(self, label_map, inst_map=None, real_image=None, feat_map=None, infer=False):
|
| 96 |
+
if (len(self.gpu_ids) > 0):
|
| 97 |
+
input_label = label_map.data.cuda() #GPU
|
| 98 |
+
else:
|
| 99 |
+
input_label = label_map.data #CPU
|
| 100 |
+
|
| 101 |
+
return input_label, inst_map, real_image, feat_map
|
| 102 |
+
|
| 103 |
+
def __weights_init(self, m):
|
| 104 |
+
classname = m.__class__.__name__
|
| 105 |
+
if classname.find('Conv') != -1:
|
| 106 |
+
m.weight.data.normal_(0.0, 0.02)
|
| 107 |
+
elif classname.find('BatchNorm2d') != -1:
|
| 108 |
+
m.weight.data.normal_(1.0, 0.02)
|
| 109 |
+
m.bias.data.fill_(0)
|
| 110 |
+
|
| 111 |
+
def __define_G(self, input_nc, output_nc, ngf, netG, n_downsample_global=3, n_blocks_global=9, n_local_enhancers=1,
|
| 112 |
+
n_blocks_local=3, norm='instance', gpu_ids=[]):
|
| 113 |
+
norm_layer = self.__get_norm_layer(norm_type=norm)
|
| 114 |
+
netG = GlobalGenerator(input_nc, output_nc, ngf, n_downsample_global, n_blocks_global, norm_layer)
|
| 115 |
+
|
| 116 |
+
if len(gpu_ids) > 0:
|
| 117 |
+
netG.cuda(gpu_ids[0])
|
| 118 |
+
netG.apply(self.__weights_init)
|
| 119 |
+
return netG
|
| 120 |
+
|
| 121 |
+
def __get_norm_layer(self, norm_type='instance'):
|
| 122 |
+
norm_layer = functools.partial(torch.nn.InstanceNorm2d, affine=False)
|
| 123 |
+
return norm_layer
|
| 124 |
+
|
| 125 |
+
##############################################################################
|
| 126 |
+
# Generator
|
| 127 |
+
##############################################################################
|
| 128 |
+
class GlobalGenerator(torch.nn.Module):
|
| 129 |
+
def __init__(self, input_nc, output_nc, ngf=64, n_downsampling=3, n_blocks=9, norm_layer=torch.nn.BatchNorm2d,
|
| 130 |
+
padding_type='reflect'):
|
| 131 |
+
assert(n_blocks >= 0)
|
| 132 |
+
super(GlobalGenerator, self).__init__()
|
| 133 |
+
activation = torch.nn.ReLU(True)
|
| 134 |
+
|
| 135 |
+
model = [torch.nn.ReflectionPad2d(3), torch.nn.Conv2d(input_nc, ngf, kernel_size=7, padding=0), norm_layer(ngf), activation]
|
| 136 |
+
### downsample
|
| 137 |
+
for i in range(n_downsampling):
|
| 138 |
+
mult = 2**i
|
| 139 |
+
model += [torch.nn.Conv2d(ngf * mult, ngf * mult * 2, kernel_size=3, stride=2, padding=1),
|
| 140 |
+
norm_layer(ngf * mult * 2), activation]
|
| 141 |
+
|
| 142 |
+
### resnet blocks
|
| 143 |
+
mult = 2**n_downsampling
|
| 144 |
+
for i in range(n_blocks):
|
| 145 |
+
model += [ResnetBlock(ngf * mult, padding_type=padding_type, activation=activation, norm_layer=norm_layer)]
|
| 146 |
+
|
| 147 |
+
### upsample
|
| 148 |
+
for i in range(n_downsampling):
|
| 149 |
+
mult = 2**(n_downsampling - i)
|
| 150 |
+
model += [torch.nn.ConvTranspose2d(ngf * mult, int(ngf * mult / 2), kernel_size=3, stride=2, padding=1, output_padding=1),
|
| 151 |
+
norm_layer(int(ngf * mult / 2)), activation]
|
| 152 |
+
model += [torch.nn.ReflectionPad2d(3), torch.nn.Conv2d(ngf, output_nc, kernel_size=7, padding=0), torch.nn.Tanh()]
|
| 153 |
+
self.model = torch.nn.Sequential(*model)
|
| 154 |
+
|
| 155 |
+
def forward(self, input):
|
| 156 |
+
return self.model(input)
|
| 157 |
+
|
| 158 |
+
# Define a resnet block
|
| 159 |
+
class ResnetBlock(torch.nn.Module):
|
| 160 |
+
def __init__(self, dim, padding_type, norm_layer, activation=torch.nn.ReLU(True), use_dropout=False):
|
| 161 |
+
super(ResnetBlock, self).__init__()
|
| 162 |
+
self.conv_block = self.__build_conv_block(dim, padding_type, norm_layer, activation, use_dropout)
|
| 163 |
+
|
| 164 |
+
def __build_conv_block(self, dim, padding_type, norm_layer, activation, use_dropout):
|
| 165 |
+
conv_block = []
|
| 166 |
+
p = 0
|
| 167 |
+
if padding_type == 'reflect':
|
| 168 |
+
conv_block += [torch.nn.ReflectionPad2d(1)]
|
| 169 |
+
elif padding_type == 'replicate':
|
| 170 |
+
conv_block += [torch.nn.ReplicationPad2d(1)]
|
| 171 |
+
elif padding_type == 'zero':
|
| 172 |
+
p = 1
|
| 173 |
+
else:
|
| 174 |
+
raise NotImplementedError('padding [%s] is not implemented' % padding_type)
|
| 175 |
+
|
| 176 |
+
conv_block += [torch.nn.Conv2d(dim, dim, kernel_size=3, padding=p),
|
| 177 |
+
norm_layer(dim),
|
| 178 |
+
activation]
|
| 179 |
+
if use_dropout:
|
| 180 |
+
conv_block += [torch.nn.Dropout(0.5)]
|
| 181 |
+
|
| 182 |
+
p = 0
|
| 183 |
+
if padding_type == 'reflect':
|
| 184 |
+
conv_block += [torch.nn.ReflectionPad2d(1)]
|
| 185 |
+
elif padding_type == 'replicate':
|
| 186 |
+
conv_block += [torch.nn.ReplicationPad2d(1)]
|
| 187 |
+
elif padding_type == 'zero':
|
| 188 |
+
p = 1
|
| 189 |
+
else:
|
| 190 |
+
raise NotImplementedError('padding [%s] is not implemented' % padding_type)
|
| 191 |
+
conv_block += [torch.nn.Conv2d(dim, dim, kernel_size=3, padding=p),
|
| 192 |
+
norm_layer(dim)]
|
| 193 |
+
|
| 194 |
+
return torch.nn.Sequential(*conv_block)
|
| 195 |
+
|
| 196 |
+
def forward(self, x):
|
| 197 |
+
out = x + self.conv_block(x)
|
| 198 |
+
return out
|
| 199 |
+
|
| 200 |
+
# Data utils:
|
| 201 |
+
def get_transform(opt, method=Image.BICUBIC, normalize=True):
|
| 202 |
+
transform_list = []
|
| 203 |
+
|
| 204 |
+
base = float(2 ** opt.n_downsample_global)
|
| 205 |
+
if opt.netG == 'local':
|
| 206 |
+
base *= (2 ** opt.n_local_enhancers)
|
| 207 |
+
transform_list.append(transforms.Lambda(lambda img: __make_power_2(img, base, method)))
|
| 208 |
+
|
| 209 |
+
transform_list += [transforms.ToTensor()]
|
| 210 |
+
|
| 211 |
+
if normalize:
|
| 212 |
+
transform_list += [transforms.Normalize((0.5, 0.5, 0.5),
|
| 213 |
+
(0.5, 0.5, 0.5))]
|
| 214 |
+
return transforms.Compose(transform_list)
|
| 215 |
+
|
| 216 |
+
def __make_power_2(img, base, method=Image.BICUBIC):
|
| 217 |
+
ow, oh = img.size
|
| 218 |
+
h = int(round(oh / base) * base)
|
| 219 |
+
w = int(round(ow / base) * base)
|
| 220 |
+
if (h == oh) and (w == ow):
|
| 221 |
+
return img
|
| 222 |
+
return img.resize((w, h), method)
|
| 223 |
+
|
| 224 |
+
# Converts a Tensor into a Numpy array
|
| 225 |
+
# |imtype|: the desired type of the converted numpy array
|
| 226 |
+
def tensor2im(image_tensor, imtype=np.uint8, normalize=True):
|
| 227 |
+
if isinstance(image_tensor, list):
|
| 228 |
+
image_numpy = []
|
| 229 |
+
for i in range(len(image_tensor)):
|
| 230 |
+
image_numpy.append(tensor2im(image_tensor[i], imtype, normalize))
|
| 231 |
+
return image_numpy
|
| 232 |
+
image_numpy = image_tensor.cpu().float().numpy()
|
| 233 |
+
if normalize:
|
| 234 |
+
image_numpy = (np.transpose(image_numpy, (1, 2, 0)) + 1) / 2.0 * 255.0
|
| 235 |
+
else:
|
| 236 |
+
image_numpy = np.transpose(image_numpy, (1, 2, 0)) * 255.0
|
| 237 |
+
image_numpy = np.clip(image_numpy, 0, 255)
|
| 238 |
+
if image_numpy.shape[2] == 1 or image_numpy.shape[2] > 3:
|
| 239 |
+
image_numpy = image_numpy[:,:,0]
|
| 240 |
+
return image_numpy.astype(imtype)
|
input.jpg
ADDED
|
input.png
ADDED
|
input_not empty.txt
ADDED
|
File without changes
|
mm.lib
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e38d34f7b3d500352592f7fbe0b361e6d8b2b87ab2dfcbaab62326effc79174a
|
| 3 |
+
size 729785444
|
mn.lib
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5f3610f4c641187e7e0ef868859fbe9aaa8aa0ef17b37bea6a3b92562af95df0
|
| 3 |
+
size 729785444
|
opencv_transform___init__.py
ADDED
|
File without changes
|
opencv_transform_annotation.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
#Object annotation class:
|
| 3 |
+
class BodyPart:
|
| 4 |
+
|
| 5 |
+
def __init__(self, name, xmin, ymin, xmax, ymax, x, y, w, h):
|
| 6 |
+
self.name = name
|
| 7 |
+
#Bounding Box:
|
| 8 |
+
self.xmin = xmin
|
| 9 |
+
self.ymin = ymin
|
| 10 |
+
self.xmax = xmax
|
| 11 |
+
self.ymax = ymax
|
| 12 |
+
#Center:
|
| 13 |
+
self.x = x
|
| 14 |
+
self.y = y
|
| 15 |
+
#Dimensione:
|
| 16 |
+
self.w = w
|
| 17 |
+
self.h = h
|
opencv_transform_dress_to_correct.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
import math
|
| 3 |
+
import numpy as np
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
# create_correct ===============================================================
|
| 7 |
+
# return:
|
| 8 |
+
# (<Boolean> True/False), depending on the transformation process
|
| 9 |
+
def create_correct(cv_dress):
|
| 10 |
+
|
| 11 |
+
#Production dir:
|
| 12 |
+
return correct_color(cv_dress, 5)
|
| 13 |
+
|
| 14 |
+
# correct_color ==============================================================================
|
| 15 |
+
# return:
|
| 16 |
+
# <RGB> image corrected
|
| 17 |
+
def correct_color(img, percent):
|
| 18 |
+
|
| 19 |
+
assert img.shape[2] == 3
|
| 20 |
+
assert percent > 0 and percent < 100
|
| 21 |
+
|
| 22 |
+
half_percent = percent / 200.0
|
| 23 |
+
|
| 24 |
+
channels = cv2.split(img)
|
| 25 |
+
|
| 26 |
+
out_channels = []
|
| 27 |
+
for channel in channels:
|
| 28 |
+
assert len(channel.shape) == 2
|
| 29 |
+
# find the low and high precentile values (based on the input percentile)
|
| 30 |
+
height, width = channel.shape
|
| 31 |
+
vec_size = width * height
|
| 32 |
+
flat = channel.reshape(vec_size)
|
| 33 |
+
|
| 34 |
+
assert len(flat.shape) == 1
|
| 35 |
+
|
| 36 |
+
flat = np.sort(flat)
|
| 37 |
+
|
| 38 |
+
n_cols = flat.shape[0]
|
| 39 |
+
|
| 40 |
+
low_val = flat[math.floor(n_cols * half_percent)]
|
| 41 |
+
high_val = flat[math.ceil( n_cols * (1.0 - half_percent))]
|
| 42 |
+
|
| 43 |
+
# saturate below the low percentile and above the high percentile
|
| 44 |
+
thresholded = apply_threshold(channel, low_val, high_val)
|
| 45 |
+
# scale the channel
|
| 46 |
+
normalized = cv2.normalize(thresholded, thresholded.copy(), 0, 255, cv2.NORM_MINMAX)
|
| 47 |
+
out_channels.append(normalized)
|
| 48 |
+
|
| 49 |
+
return cv2.merge(out_channels)
|
| 50 |
+
|
| 51 |
+
#Color correction utils
|
| 52 |
+
def apply_threshold(matrix, low_value, high_value):
|
| 53 |
+
low_mask = matrix < low_value
|
| 54 |
+
matrix = apply_mask(matrix, low_mask, low_value)
|
| 55 |
+
|
| 56 |
+
high_mask = matrix > high_value
|
| 57 |
+
matrix = apply_mask(matrix, high_mask, high_value)
|
| 58 |
+
|
| 59 |
+
return matrix
|
| 60 |
+
|
| 61 |
+
#Color correction utils
|
| 62 |
+
def apply_mask(matrix, mask, fill_value):
|
| 63 |
+
masked = np.ma.array(matrix, mask=mask, fill_value=fill_value)
|
| 64 |
+
return masked.filled()
|
opencv_transform_mask_to_maskref.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import cv2
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
###
|
| 6 |
+
#
|
| 7 |
+
# maskdet_to_maskfin
|
| 8 |
+
#
|
| 9 |
+
#
|
| 10 |
+
###
|
| 11 |
+
|
| 12 |
+
# create_maskref ===============================================================
|
| 13 |
+
# return:
|
| 14 |
+
# maskref image
|
| 15 |
+
def create_maskref(cv_mask, cv_correct):
|
| 16 |
+
|
| 17 |
+
#Create a total green image
|
| 18 |
+
green = np.zeros((512,512,3), np.uint8)
|
| 19 |
+
green[:,:,:] = (0,255,0) # (B, G, R)
|
| 20 |
+
|
| 21 |
+
#Define the green color filter
|
| 22 |
+
f1 = np.asarray([0, 250, 0]) # green color filter
|
| 23 |
+
f2 = np.asarray([10, 255, 10])
|
| 24 |
+
|
| 25 |
+
#From mask, extrapolate only the green mask
|
| 26 |
+
green_mask = cv2.inRange(cv_mask, f1, f2) #green is 0
|
| 27 |
+
|
| 28 |
+
# (OPTIONAL) Apply dilate and open to mask
|
| 29 |
+
kernel = np.ones((5,5),np.uint8) #Try change it?
|
| 30 |
+
green_mask = cv2.dilate(green_mask, kernel, iterations = 1)
|
| 31 |
+
#green_mask = cv2.morphologyEx(green_mask, cv2.MORPH_OPEN, kernel)
|
| 32 |
+
|
| 33 |
+
# Create an inverted mask
|
| 34 |
+
green_mask_inv = cv2.bitwise_not(green_mask)
|
| 35 |
+
|
| 36 |
+
# Cut correct and green image, using the green_mask & green_mask_inv
|
| 37 |
+
res1 = cv2.bitwise_and(cv_correct, cv_correct, mask = green_mask_inv)
|
| 38 |
+
res2 = cv2.bitwise_and(green, green, mask = green_mask)
|
| 39 |
+
|
| 40 |
+
# Compone:
|
| 41 |
+
return cv2.add(res1, res2)
|
opencv_transform_maskdet_to_maskfin.py
ADDED
|
@@ -0,0 +1,519 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import cv2
|
| 3 |
+
import os
|
| 4 |
+
import random
|
| 5 |
+
|
| 6 |
+
#My library:
|
| 7 |
+
from opencv_transform.annotation import BodyPart
|
| 8 |
+
|
| 9 |
+
###
|
| 10 |
+
#
|
| 11 |
+
# maskdet_to_maskfin
|
| 12 |
+
#
|
| 13 |
+
# steps:
|
| 14 |
+
# 1. Extract annotation
|
| 15 |
+
# 1.a: Filter by color
|
| 16 |
+
# 1.b: Find ellipses
|
| 17 |
+
# 1.c: Filter out ellipses by max size, and max total numbers
|
| 18 |
+
# 1.d: Detect Problems
|
| 19 |
+
# 1.e: Resolve the problems, or discard the transformation
|
| 20 |
+
# 2. With the body list, draw maskfin, using maskref
|
| 21 |
+
#
|
| 22 |
+
###
|
| 23 |
+
|
| 24 |
+
# create_maskfin ==============================================================================
|
| 25 |
+
# return:
|
| 26 |
+
# (<Boolean> True/False), depending on the transformation process
|
| 27 |
+
def create_maskfin(maskref, maskdet):
|
| 28 |
+
|
| 29 |
+
#Create a total green image, in which draw details ellipses
|
| 30 |
+
details = np.zeros((512,512,3), np.uint8)
|
| 31 |
+
details[:,:,:] = (0,255,0) # (B, G, R)
|
| 32 |
+
|
| 33 |
+
#Extract body part features:
|
| 34 |
+
bodypart_list = extractAnnotations(maskdet);
|
| 35 |
+
|
| 36 |
+
#Check if the list is not empty:
|
| 37 |
+
if bodypart_list:
|
| 38 |
+
|
| 39 |
+
#Draw body part in details image:
|
| 40 |
+
for obj in bodypart_list:
|
| 41 |
+
|
| 42 |
+
if obj.w < obj.h:
|
| 43 |
+
aMax = int(obj.h/2) #asse maggiore
|
| 44 |
+
aMin = int(obj.w/2) #asse minore
|
| 45 |
+
angle = 0 #angle
|
| 46 |
+
else:
|
| 47 |
+
aMax = int(obj.w/2)
|
| 48 |
+
aMin = int(obj.h/2)
|
| 49 |
+
angle = 90
|
| 50 |
+
|
| 51 |
+
x = int(obj.x)
|
| 52 |
+
y = int(obj.y)
|
| 53 |
+
|
| 54 |
+
#Draw ellipse
|
| 55 |
+
if obj.name == "tit":
|
| 56 |
+
cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(0,205,0),-1) #(0,0,0,50)
|
| 57 |
+
elif obj.name == "aur":
|
| 58 |
+
cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(0,0,255),-1) #red
|
| 59 |
+
elif obj.name == "nip":
|
| 60 |
+
cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(255,255,255),-1) #white
|
| 61 |
+
elif obj.name == "belly":
|
| 62 |
+
cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(255,0,255),-1) #purple
|
| 63 |
+
elif obj.name == "vag":
|
| 64 |
+
cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(255,0,0),-1) #blue
|
| 65 |
+
elif obj.name == "hair":
|
| 66 |
+
xmin = x - int(obj.w/2)
|
| 67 |
+
ymin = y - int(obj.h/2)
|
| 68 |
+
xmax = x + int(obj.w/2)
|
| 69 |
+
ymax = y + int(obj.h/2)
|
| 70 |
+
cv2.rectangle(details,(xmin,ymin),(xmax,ymax),(100,100,100),-1)
|
| 71 |
+
|
| 72 |
+
#Define the green color filter
|
| 73 |
+
f1 = np.asarray([0, 250, 0]) # green color filter
|
| 74 |
+
f2 = np.asarray([10, 255, 10])
|
| 75 |
+
|
| 76 |
+
#From maskref, extrapolate only the green mask
|
| 77 |
+
green_mask = cv2.bitwise_not(cv2.inRange(maskref, f1, f2)) #green is 0
|
| 78 |
+
|
| 79 |
+
# Create an inverted mask
|
| 80 |
+
green_mask_inv = cv2.bitwise_not(green_mask)
|
| 81 |
+
|
| 82 |
+
# Cut maskref and detail image, using the green_mask & green_mask_inv
|
| 83 |
+
res1 = cv2.bitwise_and(maskref, maskref, mask = green_mask)
|
| 84 |
+
res2 = cv2.bitwise_and(details, details, mask = green_mask_inv)
|
| 85 |
+
|
| 86 |
+
# Compone:
|
| 87 |
+
maskfin = cv2.add(res1, res2)
|
| 88 |
+
return maskfin
|
| 89 |
+
|
| 90 |
+
# extractAnnotations ==============================================================================
|
| 91 |
+
# input parameter:
|
| 92 |
+
# (<string> maskdet_img): relative path of the single maskdet image (es: testimg1/maskdet/1.png)
|
| 93 |
+
# return:
|
| 94 |
+
# (<BodyPart []> bodypart_list) - for failure/error, return an empty list []
|
| 95 |
+
def extractAnnotations(maskdet):
|
| 96 |
+
|
| 97 |
+
#Load the image
|
| 98 |
+
#image = cv2.imread(maskdet_img)
|
| 99 |
+
|
| 100 |
+
#Find body part
|
| 101 |
+
tits_list = findBodyPart(maskdet, "tit")
|
| 102 |
+
aur_list = findBodyPart(maskdet, "aur")
|
| 103 |
+
vag_list = findBodyPart(maskdet, "vag")
|
| 104 |
+
belly_list = findBodyPart(maskdet, "belly")
|
| 105 |
+
|
| 106 |
+
#Filter out parts basing on dimension (area and aspect ratio):
|
| 107 |
+
aur_list = filterDimParts(aur_list, 100, 1000, 0.5, 3);
|
| 108 |
+
tits_list = filterDimParts(tits_list, 1000, 60000, 0.2, 3);
|
| 109 |
+
vag_list = filterDimParts(vag_list, 10, 1000, 0.2, 3);
|
| 110 |
+
belly_list = filterDimParts(belly_list, 10, 1000, 0.2, 3);
|
| 111 |
+
|
| 112 |
+
#Filter couple (if parts are > 2, choose only 2)
|
| 113 |
+
aur_list = filterCouple(aur_list);
|
| 114 |
+
tits_list = filterCouple(tits_list);
|
| 115 |
+
|
| 116 |
+
#Detect a missing problem:
|
| 117 |
+
missing_problem = detectTitAurMissingProblem(tits_list, aur_list) #return a Number (code of the problem)
|
| 118 |
+
|
| 119 |
+
#Check if problem is SOLVEABLE:
|
| 120 |
+
if (missing_problem in [3,6,7,8]):
|
| 121 |
+
resolveTitAurMissingProblems(tits_list, aur_list, missing_problem)
|
| 122 |
+
|
| 123 |
+
#Infer the nips:
|
| 124 |
+
nip_list = inferNip(aur_list)
|
| 125 |
+
|
| 126 |
+
#Infer the hair:
|
| 127 |
+
hair_list = inferHair(vag_list)
|
| 128 |
+
|
| 129 |
+
#Return a combined list:
|
| 130 |
+
return tits_list + aur_list + nip_list + vag_list + hair_list + belly_list
|
| 131 |
+
|
| 132 |
+
# findBodyPart ==============================================================================
|
| 133 |
+
# input parameters:
|
| 134 |
+
# (<RGB>image, <string>part_name)
|
| 135 |
+
# return
|
| 136 |
+
# (<BodyPart[]>list)
|
| 137 |
+
def findBodyPart(image, part_name):
|
| 138 |
+
|
| 139 |
+
bodypart_list = [] #empty BodyPart list
|
| 140 |
+
|
| 141 |
+
#Get the correct color filter:
|
| 142 |
+
if part_name == "tit":
|
| 143 |
+
#Use combined color filter
|
| 144 |
+
f1 = np.asarray([0, 0, 0]) # tit color filter
|
| 145 |
+
f2 = np.asarray([10, 10, 10])
|
| 146 |
+
f3 = np.asarray([0, 0, 250]) # aur color filter
|
| 147 |
+
f4 = np.asarray([0, 0, 255])
|
| 148 |
+
color_mask1 = cv2.inRange(image, f1, f2)
|
| 149 |
+
color_mask2 = cv2.inRange(image, f3, f4)
|
| 150 |
+
color_mask = cv2.bitwise_or(color_mask1, color_mask2) #combine
|
| 151 |
+
|
| 152 |
+
elif part_name == "aur":
|
| 153 |
+
f1 = np.asarray([0, 0, 250]) # aur color filter
|
| 154 |
+
f2 = np.asarray([0, 0, 255])
|
| 155 |
+
color_mask = cv2.inRange(image, f1, f2)
|
| 156 |
+
|
| 157 |
+
elif part_name == "vag":
|
| 158 |
+
f1 = np.asarray([250, 0, 0]) # vag filter
|
| 159 |
+
f2 = np.asarray([255, 0, 0])
|
| 160 |
+
color_mask = cv2.inRange(image, f1, f2)
|
| 161 |
+
|
| 162 |
+
elif part_name == "belly":
|
| 163 |
+
f1 = np.asarray([250, 0, 250]) # belly filter
|
| 164 |
+
f2 = np.asarray([255, 0, 255])
|
| 165 |
+
color_mask = cv2.inRange(image, f1, f2)
|
| 166 |
+
|
| 167 |
+
#find contours:
|
| 168 |
+
contours, hierarchy = cv2.findContours(color_mask,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
|
| 169 |
+
|
| 170 |
+
#for every contour:
|
| 171 |
+
for cnt in contours:
|
| 172 |
+
|
| 173 |
+
if len(cnt)>5: #at least 5 points to fit ellipse
|
| 174 |
+
|
| 175 |
+
#(x, y), (MA, ma), angle = cv2.fitEllipse(cnt)
|
| 176 |
+
ellipse = cv2.fitEllipse(cnt)
|
| 177 |
+
|
| 178 |
+
#Fit Result:
|
| 179 |
+
x = ellipse[0][0] #center x
|
| 180 |
+
y = ellipse[0][1] #center y
|
| 181 |
+
angle = ellipse[2] #angle
|
| 182 |
+
aMin = ellipse[1][0]; #asse minore
|
| 183 |
+
aMax = ellipse[1][1]; #asse maggiore
|
| 184 |
+
|
| 185 |
+
#Detect direction:
|
| 186 |
+
if angle == 0:
|
| 187 |
+
h = aMax
|
| 188 |
+
w = aMin
|
| 189 |
+
else:
|
| 190 |
+
h = aMin
|
| 191 |
+
w = aMax
|
| 192 |
+
|
| 193 |
+
#Normalize the belly size:
|
| 194 |
+
if part_name == "belly":
|
| 195 |
+
if w<15:
|
| 196 |
+
w *= 2
|
| 197 |
+
if h<15:
|
| 198 |
+
h *= 2
|
| 199 |
+
|
| 200 |
+
#Normalize the vag size:
|
| 201 |
+
if part_name == "vag":
|
| 202 |
+
if w<15:
|
| 203 |
+
w *= 2
|
| 204 |
+
if h<15:
|
| 205 |
+
h *= 2
|
| 206 |
+
|
| 207 |
+
#Calculate Bounding Box:
|
| 208 |
+
xmin = int(x - (w/2))
|
| 209 |
+
xmax = int(x + (w/2))
|
| 210 |
+
ymin = int(y - (h/2))
|
| 211 |
+
ymax = int(y + (h/2))
|
| 212 |
+
|
| 213 |
+
bodypart_list.append(BodyPart(part_name, xmin, ymin, xmax, ymax, x, y, w, h ))
|
| 214 |
+
|
| 215 |
+
return bodypart_list
|
| 216 |
+
|
| 217 |
+
# filterDimParts ==============================================================================
|
| 218 |
+
# input parameters:
|
| 219 |
+
# (<BodyPart[]>list, <num> minimum area of part, <num> max area, <num> min aspect ratio, <num> max aspect ratio)
|
| 220 |
+
def filterDimParts(bp_list, min_area, max_area, min_ar, max_ar):
|
| 221 |
+
|
| 222 |
+
b_filt = []
|
| 223 |
+
|
| 224 |
+
for obj in bp_list:
|
| 225 |
+
|
| 226 |
+
a = obj.w*obj.h #Object AREA
|
| 227 |
+
|
| 228 |
+
if ((a > min_area)and(a < max_area)):
|
| 229 |
+
|
| 230 |
+
ar = obj.w/obj.h #Object ASPECT RATIO
|
| 231 |
+
|
| 232 |
+
if ((ar>min_ar)and(ar<max_ar)):
|
| 233 |
+
|
| 234 |
+
b_filt.append(obj)
|
| 235 |
+
|
| 236 |
+
return b_filt
|
| 237 |
+
|
| 238 |
+
# filterCouple ==============================================================================
|
| 239 |
+
# input parameters:
|
| 240 |
+
# (<BodyPart[]>list)
|
| 241 |
+
def filterCouple(bp_list):
|
| 242 |
+
|
| 243 |
+
#Remove exceed parts
|
| 244 |
+
if (len(bp_list)>2):
|
| 245 |
+
|
| 246 |
+
#trovare coppia (a,b) che minimizza bp_list[a].y-bp_list[b].y
|
| 247 |
+
min_a = 0
|
| 248 |
+
min_b = 1
|
| 249 |
+
min_diff = abs(bp_list[min_a].y-bp_list[min_b].y)
|
| 250 |
+
|
| 251 |
+
for a in range(0,len(bp_list)):
|
| 252 |
+
for b in range(0,len(bp_list)):
|
| 253 |
+
#TODO: avoid repetition (1,0) (0,1)
|
| 254 |
+
if a != b:
|
| 255 |
+
diff = abs(bp_list[a].y-bp_list[b].y)
|
| 256 |
+
if diff<min_diff:
|
| 257 |
+
min_diff = diff
|
| 258 |
+
min_a = a
|
| 259 |
+
min_b = b
|
| 260 |
+
b_filt = []
|
| 261 |
+
|
| 262 |
+
b_filt.append(bp_list[min_a])
|
| 263 |
+
b_filt.append(bp_list[min_b])
|
| 264 |
+
|
| 265 |
+
return b_filt
|
| 266 |
+
else:
|
| 267 |
+
#No change
|
| 268 |
+
return bp_list
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
# detectTitAurMissingProblem ==============================================================================
|
| 273 |
+
# input parameters:
|
| 274 |
+
# (<BodyPart[]> tits list, <BodyPart[]> aur list)
|
| 275 |
+
# return
|
| 276 |
+
# (<num> problem code)
|
| 277 |
+
# TIT | AUR | code | SOLVE? |
|
| 278 |
+
# 0 | 0 | 1 | NO |
|
| 279 |
+
# 0 | 1 | 2 | NO |
|
| 280 |
+
# 0 | 2 | 3 | YES |
|
| 281 |
+
# 1 | 0 | 4 | NO |
|
| 282 |
+
# 1 | 1 | 5 | NO |
|
| 283 |
+
# 1 | 2 | 6 | YES |
|
| 284 |
+
# 2 | 0 | 7 | YES |
|
| 285 |
+
# 2 | 1 | 8 | YES |
|
| 286 |
+
def detectTitAurMissingProblem(tits_list, aur_list):
|
| 287 |
+
|
| 288 |
+
t_len = len(tits_list)
|
| 289 |
+
a_len = len(aur_list)
|
| 290 |
+
|
| 291 |
+
if (t_len == 0):
|
| 292 |
+
if (a_len == 0):
|
| 293 |
+
return 1
|
| 294 |
+
elif (a_len == 1):
|
| 295 |
+
return 2
|
| 296 |
+
elif (a_len == 2):
|
| 297 |
+
return 3
|
| 298 |
+
else:
|
| 299 |
+
return -1
|
| 300 |
+
elif (t_len == 1):
|
| 301 |
+
if (a_len == 0):
|
| 302 |
+
return 4
|
| 303 |
+
elif (a_len == 1):
|
| 304 |
+
return 5
|
| 305 |
+
elif (a_len == 2):
|
| 306 |
+
return 6
|
| 307 |
+
else:
|
| 308 |
+
return -1
|
| 309 |
+
elif (t_len == 2):
|
| 310 |
+
if (a_len == 0):
|
| 311 |
+
return 7
|
| 312 |
+
elif (a_len == 1):
|
| 313 |
+
return 8
|
| 314 |
+
else:
|
| 315 |
+
return -1
|
| 316 |
+
else:
|
| 317 |
+
return -1
|
| 318 |
+
|
| 319 |
+
# resolveTitAurMissingProblems ==============================================================================
|
| 320 |
+
# input parameters:
|
| 321 |
+
# (<BodyPart[]> tits list, <BodyPart[]> aur list, problem code)
|
| 322 |
+
# return
|
| 323 |
+
# none
|
| 324 |
+
def resolveTitAurMissingProblems(tits_list, aur_list, problem_code):
|
| 325 |
+
|
| 326 |
+
if problem_code == 3:
|
| 327 |
+
|
| 328 |
+
random_tit_factor = random.randint(2, 5) #TOTEST
|
| 329 |
+
|
| 330 |
+
#Add the first tit:
|
| 331 |
+
new_w = aur_list[0].w * random_tit_factor #TOTEST
|
| 332 |
+
new_x = aur_list[0].x
|
| 333 |
+
new_y = aur_list[0].y
|
| 334 |
+
|
| 335 |
+
xmin = int(new_x - (new_w/2))
|
| 336 |
+
xmax = int(new_x + (new_w/2))
|
| 337 |
+
ymin = int(new_y - (new_w/2))
|
| 338 |
+
ymax = int(new_y + (new_w/2))
|
| 339 |
+
|
| 340 |
+
tits_list.append(BodyPart("tit", xmin, ymin, xmax, ymax, new_x, new_y, new_w, new_w ))
|
| 341 |
+
|
| 342 |
+
#Add the second tit:
|
| 343 |
+
new_w = aur_list[1].w * random_tit_factor #TOTEST
|
| 344 |
+
new_x = aur_list[1].x
|
| 345 |
+
new_y = aur_list[1].y
|
| 346 |
+
|
| 347 |
+
xmin = int(new_x - (new_w/2))
|
| 348 |
+
xmax = int(new_x + (new_w/2))
|
| 349 |
+
ymin = int(new_y - (new_w/2))
|
| 350 |
+
ymax = int(new_y + (new_w/2))
|
| 351 |
+
|
| 352 |
+
tits_list.append(BodyPart("tit", xmin, ymin, xmax, ymax, new_x, new_y, new_w, new_w ))
|
| 353 |
+
|
| 354 |
+
elif problem_code == 6:
|
| 355 |
+
|
| 356 |
+
#Find wich aur is full:
|
| 357 |
+
d1 = abs(tits_list[0].x - aur_list[0].x)
|
| 358 |
+
d2 = abs(tits_list[0].x - aur_list[1].x)
|
| 359 |
+
|
| 360 |
+
if d1 > d2:
|
| 361 |
+
#aur[0] is empty
|
| 362 |
+
new_x = aur_list[0].x
|
| 363 |
+
new_y = aur_list[0].y
|
| 364 |
+
else:
|
| 365 |
+
#aur[1] is empty
|
| 366 |
+
new_x = aur_list[1].x
|
| 367 |
+
new_y = aur_list[1].y
|
| 368 |
+
|
| 369 |
+
#Calculate Bounding Box:
|
| 370 |
+
xmin = int(new_x - (tits_list[0].w/2))
|
| 371 |
+
xmax = int(new_x + (tits_list[0].w/2))
|
| 372 |
+
ymin = int(new_y - (tits_list[0].w/2))
|
| 373 |
+
ymax = int(new_y + (tits_list[0].w/2))
|
| 374 |
+
|
| 375 |
+
tits_list.append(BodyPart("tit", xmin, ymin, xmax, ymax, new_x, new_y, tits_list[0].w, tits_list[0].w ))
|
| 376 |
+
|
| 377 |
+
elif problem_code == 7:
|
| 378 |
+
|
| 379 |
+
#Add the first aur:
|
| 380 |
+
new_w = tits_list[0].w * random.uniform(0.03, 0.1) #TOTEST
|
| 381 |
+
new_x = tits_list[0].x
|
| 382 |
+
new_y = tits_list[0].y
|
| 383 |
+
|
| 384 |
+
xmin = int(new_x - (new_w/2))
|
| 385 |
+
xmax = int(new_x + (new_w/2))
|
| 386 |
+
ymin = int(new_y - (new_w/2))
|
| 387 |
+
ymax = int(new_y + (new_w/2))
|
| 388 |
+
|
| 389 |
+
aur_list.append(BodyPart("aur", xmin, ymin, xmax, ymax, new_x, new_y, new_w, new_w ))
|
| 390 |
+
|
| 391 |
+
#Add the second aur:
|
| 392 |
+
new_w = tits_list[1].w * random.uniform(0.03, 0.1) #TOTEST
|
| 393 |
+
new_x = tits_list[1].x
|
| 394 |
+
new_y = tits_list[1].y
|
| 395 |
+
|
| 396 |
+
xmin = int(new_x - (new_w/2))
|
| 397 |
+
xmax = int(new_x + (new_w/2))
|
| 398 |
+
ymin = int(new_y - (new_w/2))
|
| 399 |
+
ymax = int(new_y + (new_w/2))
|
| 400 |
+
|
| 401 |
+
aur_list.append(BodyPart("aur", xmin, ymin, xmax, ymax, new_x, new_y, new_w, new_w ))
|
| 402 |
+
|
| 403 |
+
elif problem_code == 8:
|
| 404 |
+
|
| 405 |
+
#Find wich tit is full:
|
| 406 |
+
d1 = abs(aur_list[0].x - tits_list[0].x)
|
| 407 |
+
d2 = abs(aur_list[0].x - tits_list[1].x)
|
| 408 |
+
|
| 409 |
+
if d1 > d2:
|
| 410 |
+
#tit[0] is empty
|
| 411 |
+
new_x = tits_list[0].x
|
| 412 |
+
new_y = tits_list[0].y
|
| 413 |
+
else:
|
| 414 |
+
#tit[1] is empty
|
| 415 |
+
new_x = tits_list[1].x
|
| 416 |
+
new_y = tits_list[1].y
|
| 417 |
+
|
| 418 |
+
#Calculate Bounding Box:
|
| 419 |
+
xmin = int(new_x - (aur_list[0].w/2))
|
| 420 |
+
xmax = int(new_x + (aur_list[0].w/2))
|
| 421 |
+
ymin = int(new_y - (aur_list[0].w/2))
|
| 422 |
+
ymax = int(new_y + (aur_list[0].w/2))
|
| 423 |
+
aur_list.append(BodyPart("aur", xmin, ymin, xmax, ymax, new_x, new_y, aur_list[0].w, aur_list[0].w ))
|
| 424 |
+
|
| 425 |
+
# detectTitAurPositionProblem ==============================================================================
|
| 426 |
+
# input parameters:
|
| 427 |
+
# (<BodyPart[]> tits list, <BodyPart[]> aur list)
|
| 428 |
+
# return
|
| 429 |
+
# (<Boolean> True/False)
|
| 430 |
+
def detectTitAurPositionProblem(tits_list, aur_list):
|
| 431 |
+
|
| 432 |
+
diffTitsX = abs(tits_list[0].x - tits_list[1].x)
|
| 433 |
+
if diffTitsX < 40:
|
| 434 |
+
print("diffTitsX")
|
| 435 |
+
#Tits too narrow (orizontally)
|
| 436 |
+
return True
|
| 437 |
+
|
| 438 |
+
diffTitsY = abs(tits_list[0].y - tits_list[1].y)
|
| 439 |
+
if diffTitsY > 120:
|
| 440 |
+
#Tits too distanced (vertically)
|
| 441 |
+
print("diffTitsY")
|
| 442 |
+
return True
|
| 443 |
+
|
| 444 |
+
diffTitsW = abs(tits_list[0].w - tits_list[1].w)
|
| 445 |
+
if ((diffTitsW < 0.1)or(diffTitsW>60)):
|
| 446 |
+
print("diffTitsW")
|
| 447 |
+
#Tits too equals, or too different (width)
|
| 448 |
+
return True
|
| 449 |
+
|
| 450 |
+
#Check if body position is too low (face not covered by watermark)
|
| 451 |
+
if aur_list[0].y > 350: #tits too low
|
| 452 |
+
#Calculate the ratio between y and aurs distance
|
| 453 |
+
rapp = aur_list[0].y/(abs(aur_list[0].x - aur_list[1].x))
|
| 454 |
+
if rapp > 2.8:
|
| 455 |
+
print("aurDown")
|
| 456 |
+
return True
|
| 457 |
+
|
| 458 |
+
return False
|
| 459 |
+
|
| 460 |
+
# inferNip ==============================================================================
|
| 461 |
+
# input parameters:
|
| 462 |
+
# (<BodyPart[]> aur list)
|
| 463 |
+
# return
|
| 464 |
+
# (<BodyPart[]> nip list)
|
| 465 |
+
def inferNip(aur_list):
|
| 466 |
+
nip_list = []
|
| 467 |
+
|
| 468 |
+
for aur in aur_list:
|
| 469 |
+
|
| 470 |
+
#Nip rules:
|
| 471 |
+
# - circle (w == h)
|
| 472 |
+
# - min dim: 5
|
| 473 |
+
# - bigger if aur is bigger
|
| 474 |
+
nip_dim = int(5 + aur.w*random.uniform(0.03, 0.09))
|
| 475 |
+
|
| 476 |
+
#center:
|
| 477 |
+
x = aur.x
|
| 478 |
+
y = aur.y
|
| 479 |
+
|
| 480 |
+
#Calculate Bounding Box:
|
| 481 |
+
xmin = int(x - (nip_dim/2))
|
| 482 |
+
xmax = int(x + (nip_dim/2))
|
| 483 |
+
ymin = int(y - (nip_dim/2))
|
| 484 |
+
ymax = int(y + (nip_dim/2))
|
| 485 |
+
|
| 486 |
+
nip_list.append(BodyPart("nip", xmin, ymin, xmax, ymax, x, y, nip_dim, nip_dim ))
|
| 487 |
+
|
| 488 |
+
return nip_list
|
| 489 |
+
|
| 490 |
+
# inferHair (TOTEST) ==============================================================================
|
| 491 |
+
# input parameters:
|
| 492 |
+
# (<BodyPart[]> vag list)
|
| 493 |
+
# return
|
| 494 |
+
# (<BodyPart[]> hair list)
|
| 495 |
+
def inferHair(vag_list):
|
| 496 |
+
hair_list = []
|
| 497 |
+
|
| 498 |
+
#70% of chanche to add hair
|
| 499 |
+
if random.uniform(0.0, 1.0) > 0.3:
|
| 500 |
+
|
| 501 |
+
for vag in vag_list:
|
| 502 |
+
|
| 503 |
+
#Hair rules:
|
| 504 |
+
hair_w = vag.w*random.uniform(0.4, 1.5)
|
| 505 |
+
hair_h = vag.h*random.uniform(0.4, 1.5)
|
| 506 |
+
|
| 507 |
+
#center:
|
| 508 |
+
x = vag.x
|
| 509 |
+
y = vag.y - (hair_h/2) - (vag.h/2)
|
| 510 |
+
|
| 511 |
+
#Calculate Bounding Box:
|
| 512 |
+
xmin = int(x - (hair_w/2))
|
| 513 |
+
xmax = int(x + (hair_w/2))
|
| 514 |
+
ymin = int(y - (hair_h/2))
|
| 515 |
+
ymax = int(y + (hair_h/2))
|
| 516 |
+
|
| 517 |
+
hair_list.append(BodyPart("hair", xmin, ymin, xmax, ymax, x, y, hair_w, hair_h ))
|
| 518 |
+
|
| 519 |
+
return hair_list
|
opencv_transform_nude_to_watermark.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
import numpy as np
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
# create_watermark ===============================================================
|
| 6 |
+
# return:
|
| 7 |
+
# (<Boolean> True/False), depending on the transformation process
|
| 8 |
+
def create_watermark(nude):
|
| 9 |
+
|
| 10 |
+
# Add alpha channel if missing
|
| 11 |
+
# if nude.shape[2] < 4:
|
| 12 |
+
# nude = np.dstack([nude, np.ones((512, 512), dtype="uint8") * 255])
|
| 13 |
+
|
| 14 |
+
# watermark = cv2.imread("fake.png", cv2.IMREAD_UNCHANGED)
|
| 15 |
+
|
| 16 |
+
# f1 = np.asarray([0, 0, 0, 250]) # red color filter
|
| 17 |
+
# f2 = np.asarray([255, 255, 255, 255])
|
| 18 |
+
# mask = cv2.bitwise_not(cv2.inRange(watermark, f1, f2))
|
| 19 |
+
# mask_inv = cv2.bitwise_not(mask)
|
| 20 |
+
|
| 21 |
+
# res1 = cv2.bitwise_and(nude, nude, mask = mask)
|
| 22 |
+
# # res2 = cv2.bitwise_and(nude, nude, mask = mask)
|
| 23 |
+
# # res2 = cv2.bitwise_and(watermark, watermark, mask = mask_inv)
|
| 24 |
+
# res = res1
|
| 25 |
+
|
| 26 |
+
# alpha = 0.6
|
| 27 |
+
# return cv2.addWeighted(res, alpha, nude, 1 - alpha, 0)
|
| 28 |
+
return nude
|
requirements.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
numpy
|
| 2 |
+
Pillow
|
| 3 |
+
setuptools
|
| 4 |
+
six
|
| 5 |
+
opencv-python
|
| 6 |
+
torch
|
| 7 |
+
torchvision
|
| 8 |
+
wheel
|
| 9 |
+
gradio
|
run.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
|
| 3 |
+
#Import Neural Network Model
|
| 4 |
+
from gan import DataLoader, DeepModel, tensor2im
|
| 5 |
+
|
| 6 |
+
#OpenCv Transform:
|
| 7 |
+
from opencv_transform.mask_to_maskref import create_maskref
|
| 8 |
+
from opencv_transform.maskdet_to_maskfin import create_maskfin
|
| 9 |
+
from opencv_transform.dress_to_correct import create_correct
|
| 10 |
+
from opencv_transform.nude_to_watermark import create_watermark
|
| 11 |
+
|
| 12 |
+
"""
|
| 13 |
+
run.py
|
| 14 |
+
|
| 15 |
+
This script manage the entire transormation.
|
| 16 |
+
|
| 17 |
+
Transformation happens in 6 phases:
|
| 18 |
+
0: dress -> correct [opencv] dress_to_correct
|
| 19 |
+
1: correct -> mask: [GAN] correct_to_mask
|
| 20 |
+
2: mask -> maskref [opencv] mask_to_maskref
|
| 21 |
+
3: maskref -> maskdet [GAN] maskref_to_maskdet
|
| 22 |
+
4: maskdet -> maskfin [opencv] maskdet_to_maskfin
|
| 23 |
+
5: maskfin -> nude [GAN] maskfin_to_nude
|
| 24 |
+
6: nude -> watermark [opencv] nude_to_watermark
|
| 25 |
+
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
phases = ["dress_to_correct", "correct_to_mask", "mask_to_maskref", "maskref_to_maskdet", "maskdet_to_maskfin", "maskfin_to_nude", "nude_to_watermark"]
|
| 29 |
+
|
| 30 |
+
class Options():
|
| 31 |
+
|
| 32 |
+
#Init options with default values
|
| 33 |
+
def __init__(self):
|
| 34 |
+
|
| 35 |
+
# experiment specifics
|
| 36 |
+
self.norm = 'batch' #instance normalization or batch normalization
|
| 37 |
+
self.use_dropout = False #use dropout for the generator
|
| 38 |
+
self.data_type = 32 #Supported data type i.e. 8, 16, 32 bit
|
| 39 |
+
|
| 40 |
+
# input/output sizes
|
| 41 |
+
self.batchSize = 1 #input batch size
|
| 42 |
+
self.input_nc = 3 # of input image channels
|
| 43 |
+
self.output_nc = 3 # of output image channels
|
| 44 |
+
|
| 45 |
+
# for setting inputs
|
| 46 |
+
self.serial_batches = True #if true, takes images in order to make batches, otherwise takes them randomly
|
| 47 |
+
self.nThreads = 1 ## threads for loading data (???)
|
| 48 |
+
self.max_dataset_size = 1 #Maximum number of samples allowed per dataset. If the dataset directory contains more than max_dataset_size, only a subset is loaded.
|
| 49 |
+
|
| 50 |
+
# for generator
|
| 51 |
+
self.netG = 'global' #selects model to use for netG
|
| 52 |
+
self.ngf = 64 ## of gen filters in first conv layer
|
| 53 |
+
self.n_downsample_global = 4 #number of downsampling layers in netG
|
| 54 |
+
self.n_blocks_global = 9 #number of residual blocks in the global generator network
|
| 55 |
+
self.n_blocks_local = 0 #number of residual blocks in the local enhancer network
|
| 56 |
+
self.n_local_enhancers = 0 #number of local enhancers to use
|
| 57 |
+
self.niter_fix_global = 0 #number of epochs that we only train the outmost local enhancer
|
| 58 |
+
|
| 59 |
+
#Phase specific options
|
| 60 |
+
self.checkpoints_dir = ""
|
| 61 |
+
self.dataroot = ""
|
| 62 |
+
|
| 63 |
+
#Changes options accordlying to actual phase
|
| 64 |
+
def updateOptions(self, phase):
|
| 65 |
+
|
| 66 |
+
if phase == "correct_to_mask":
|
| 67 |
+
self.checkpoints_dir = "checkpoints/cm.lib"
|
| 68 |
+
|
| 69 |
+
elif phase == "maskref_to_maskdet":
|
| 70 |
+
self.checkpoints_dir = "checkpoints/mm.lib"
|
| 71 |
+
|
| 72 |
+
elif phase == "maskfin_to_nude":
|
| 73 |
+
self.checkpoints_dir = "checkpoints/mn.lib"
|
| 74 |
+
|
| 75 |
+
# process(cv_img, mode)
|
| 76 |
+
# return:
|
| 77 |
+
# watermark image
|
| 78 |
+
def process(cv_img):
|
| 79 |
+
|
| 80 |
+
#InMemory cv2 images:
|
| 81 |
+
dress = cv_img
|
| 82 |
+
correct = None
|
| 83 |
+
mask = None
|
| 84 |
+
maskref = None
|
| 85 |
+
maskfin = None
|
| 86 |
+
maskdet = None
|
| 87 |
+
nude = None
|
| 88 |
+
watermark = None
|
| 89 |
+
|
| 90 |
+
for index, phase in enumerate(phases):
|
| 91 |
+
|
| 92 |
+
print("Executing phase: " + phase)
|
| 93 |
+
|
| 94 |
+
#GAN phases:
|
| 95 |
+
if (phase == "correct_to_mask") or (phase == "maskref_to_maskdet") or (phase == "maskfin_to_nude"):
|
| 96 |
+
|
| 97 |
+
#Load global option
|
| 98 |
+
opt = Options()
|
| 99 |
+
|
| 100 |
+
#Load custom phase options:
|
| 101 |
+
opt.updateOptions(phase)
|
| 102 |
+
|
| 103 |
+
#Load Data
|
| 104 |
+
if (phase == "correct_to_mask"):
|
| 105 |
+
data_loader = DataLoader(opt, correct)
|
| 106 |
+
elif (phase == "maskref_to_maskdet"):
|
| 107 |
+
data_loader = DataLoader(opt, maskref)
|
| 108 |
+
elif (phase == "maskfin_to_nude"):
|
| 109 |
+
data_loader = DataLoader(opt, maskfin)
|
| 110 |
+
|
| 111 |
+
dataset = data_loader.load_data()
|
| 112 |
+
|
| 113 |
+
#Create Model
|
| 114 |
+
model = DeepModel()
|
| 115 |
+
model.initialize(opt)
|
| 116 |
+
|
| 117 |
+
#Run for every image:
|
| 118 |
+
for i, data in enumerate(dataset):
|
| 119 |
+
|
| 120 |
+
generated = model.inference(data['label'], data['inst'])
|
| 121 |
+
|
| 122 |
+
im = tensor2im(generated.data[0])
|
| 123 |
+
|
| 124 |
+
#Save Data
|
| 125 |
+
if (phase == "correct_to_mask"):
|
| 126 |
+
mask = cv2.cvtColor(im, cv2.COLOR_RGB2BGR)
|
| 127 |
+
|
| 128 |
+
elif (phase == "maskref_to_maskdet"):
|
| 129 |
+
maskdet = cv2.cvtColor(im, cv2.COLOR_RGB2BGR)
|
| 130 |
+
|
| 131 |
+
elif (phase == "maskfin_to_nude"):
|
| 132 |
+
nude = cv2.cvtColor(im, cv2.COLOR_RGB2BGR)
|
| 133 |
+
|
| 134 |
+
#Correcting:
|
| 135 |
+
elif (phase == 'dress_to_correct'):
|
| 136 |
+
correct = create_correct(dress)
|
| 137 |
+
|
| 138 |
+
#mask_ref phase (opencv)
|
| 139 |
+
elif (phase == "mask_to_maskref"):
|
| 140 |
+
maskref = create_maskref(mask, correct)
|
| 141 |
+
|
| 142 |
+
#mask_fin phase (opencv)
|
| 143 |
+
elif (phase == "maskdet_to_maskfin"):
|
| 144 |
+
maskfin = create_maskfin(maskref, maskdet)
|
| 145 |
+
|
| 146 |
+
#nude_to_watermark phase (opencv)
|
| 147 |
+
elif (phase == "nude_to_watermark"):
|
| 148 |
+
watermark = create_watermark(nude)
|
| 149 |
+
|
| 150 |
+
return watermark
|