| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- 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
|