Spaces:
Running
Running
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 | |