maskdet_to_maskfin.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. import numpy as np
  2. import cv2
  3. import os
  4. import random
  5. #My library:
  6. from opencv_transform.annotation import BodyPart
  7. ###
  8. #
  9. # maskdet_to_maskfin
  10. #
  11. # steps:
  12. # 1. Extract annotation
  13. # 1.a: Filter by color
  14. # 1.b: Find ellipses
  15. # 1.c: Filter out ellipses by max size, and max total numbers
  16. # 1.d: Detect Problems
  17. # 1.e: Resolve the problems, or discard the transformation
  18. # 2. With the body list, draw maskfin, using maskref
  19. #
  20. ###
  21. # create_maskfin ==============================================================================
  22. # return:
  23. # (<Boolean> True/False), depending on the transformation process
  24. def create_maskfin(maskref, maskdet):
  25. #Create a total green image, in which draw details ellipses
  26. details = np.zeros((512,512,3), np.uint8)
  27. details[:,:,:] = (0,255,0) # (B, G, R)
  28. #Extract body part features:
  29. bodypart_list = extractAnnotations(maskdet);
  30. #Check if the list is not empty:
  31. if bodypart_list:
  32. #Draw body part in details image:
  33. for obj in bodypart_list:
  34. if obj.w < obj.h:
  35. aMax = int(obj.h/2) #asse maggiore
  36. aMin = int(obj.w/2) #asse minore
  37. angle = 0 #angle
  38. else:
  39. aMax = int(obj.w/2)
  40. aMin = int(obj.h/2)
  41. angle = 90
  42. x = int(obj.x)
  43. y = int(obj.y)
  44. #Draw ellipse
  45. if obj.name == "tit":
  46. cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(0,205,0),-1) #(0,0,0,50)
  47. elif obj.name == "aur":
  48. cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(0,0,255),-1) #red
  49. elif obj.name == "nip":
  50. cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(255,255,255),-1) #white
  51. elif obj.name == "belly":
  52. cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(255,0,255),-1) #purple
  53. elif obj.name == "vag":
  54. cv2.ellipse(details,(x,y),(aMax,aMin),angle,0,360,(255,0,0),-1) #blue
  55. elif obj.name == "hair":
  56. xmin = x - int(obj.w/2)
  57. ymin = y - int(obj.h/2)
  58. xmax = x + int(obj.w/2)
  59. ymax = y + int(obj.h/2)
  60. cv2.rectangle(details,(xmin,ymin),(xmax,ymax),(100,100,100),-1)
  61. #Define the green color filter
  62. f1 = np.asarray([0, 250, 0]) # green color filter
  63. f2 = np.asarray([10, 255, 10])
  64. #From maskref, extrapolate only the green mask
  65. green_mask = cv2.bitwise_not(cv2.inRange(maskref, f1, f2)) #green is 0
  66. # Create an inverted mask
  67. green_mask_inv = cv2.bitwise_not(green_mask)
  68. # Cut maskref and detail image, using the green_mask & green_mask_inv
  69. res1 = cv2.bitwise_and(maskref, maskref, mask = green_mask)
  70. res2 = cv2.bitwise_and(details, details, mask = green_mask_inv)
  71. # Compone:
  72. maskfin = cv2.add(res1, res2)
  73. return maskfin
  74. # extractAnnotations ==============================================================================
  75. # input parameter:
  76. # (<string> maskdet_img): relative path of the single maskdet image (es: testimg1/maskdet/1.png)
  77. # return:
  78. # (<BodyPart []> bodypart_list) - for failure/error, return an empty list []
  79. def extractAnnotations(maskdet):
  80. #Load the image
  81. #image = cv2.imread(maskdet_img)
  82. #Find body part
  83. tits_list = findBodyPart(maskdet, "tit")
  84. aur_list = findBodyPart(maskdet, "aur")
  85. vag_list = findBodyPart(maskdet, "vag")
  86. belly_list = findBodyPart(maskdet, "belly")
  87. #Filter out parts basing on dimension (area and aspect ratio):
  88. aur_list = filterDimParts(aur_list, 100, 1000, 0.5, 3);
  89. tits_list = filterDimParts(tits_list, 1000, 60000, 0.2, 3);
  90. vag_list = filterDimParts(vag_list, 10, 1000, 0.2, 3);
  91. belly_list = filterDimParts(belly_list, 10, 1000, 0.2, 3);
  92. #Filter couple (if parts are > 2, choose only 2)
  93. aur_list = filterCouple(aur_list);
  94. tits_list = filterCouple(tits_list);
  95. #Detect a missing problem:
  96. missing_problem = detectTitAurMissingProblem(tits_list, aur_list) #return a Number (code of the problem)
  97. #Check if problem is SOLVEABLE:
  98. if (missing_problem in [3,6,7,8]):
  99. resolveTitAurMissingProblems(tits_list, aur_list, missing_problem)
  100. #Infer the nips:
  101. nip_list = inferNip(aur_list)
  102. #Infer the hair:
  103. hair_list = inferHair(vag_list)
  104. #Return a combined list:
  105. return tits_list + aur_list + nip_list + vag_list + hair_list + belly_list
  106. # findBodyPart ==============================================================================
  107. # input parameters:
  108. # (<RGB>image, <string>part_name)
  109. # return
  110. # (<BodyPart[]>list)
  111. def findBodyPart(image, part_name):
  112. bodypart_list = [] #empty BodyPart list
  113. #Get the correct color filter:
  114. if part_name == "tit":
  115. #Use combined color filter
  116. f1 = np.asarray([0, 0, 0]) # tit color filter
  117. f2 = np.asarray([10, 10, 10])
  118. f3 = np.asarray([0, 0, 250]) # aur color filter
  119. f4 = np.asarray([0, 0, 255])
  120. color_mask1 = cv2.inRange(image, f1, f2)
  121. color_mask2 = cv2.inRange(image, f3, f4)
  122. color_mask = cv2.bitwise_or(color_mask1, color_mask2) #combine
  123. elif part_name == "aur":
  124. f1 = np.asarray([0, 0, 250]) # aur color filter
  125. f2 = np.asarray([0, 0, 255])
  126. color_mask = cv2.inRange(image, f1, f2)
  127. elif part_name == "vag":
  128. f1 = np.asarray([250, 0, 0]) # vag filter
  129. f2 = np.asarray([255, 0, 0])
  130. color_mask = cv2.inRange(image, f1, f2)
  131. elif part_name == "belly":
  132. f1 = np.asarray([250, 0, 250]) # belly filter
  133. f2 = np.asarray([255, 0, 255])
  134. color_mask = cv2.inRange(image, f1, f2)
  135. #find contours:
  136. contours, hierarchy = cv2.findContours(color_mask,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
  137. #for every contour:
  138. for cnt in contours:
  139. if len(cnt)>5: #at least 5 points to fit ellipse
  140. #(x, y), (MA, ma), angle = cv2.fitEllipse(cnt)
  141. ellipse = cv2.fitEllipse(cnt)
  142. #Fit Result:
  143. x = ellipse[0][0] #center x
  144. y = ellipse[0][1] #center y
  145. angle = ellipse[2] #angle
  146. aMin = ellipse[1][0]; #asse minore
  147. aMax = ellipse[1][1]; #asse maggiore
  148. #Detect direction:
  149. if angle == 0:
  150. h = aMax
  151. w = aMin
  152. else:
  153. h = aMin
  154. w = aMax
  155. #Normalize the belly size:
  156. if part_name == "belly":
  157. if w<15:
  158. w *= 2
  159. if h<15:
  160. h *= 2
  161. #Normalize the vag size:
  162. if part_name == "vag":
  163. if w<15:
  164. w *= 2
  165. if h<15:
  166. h *= 2
  167. #Calculate Bounding Box:
  168. xmin = int(x - (w/2))
  169. xmax = int(x + (w/2))
  170. ymin = int(y - (h/2))
  171. ymax = int(y + (h/2))
  172. bodypart_list.append(BodyPart(part_name, xmin, ymin, xmax, ymax, x, y, w, h ))
  173. return bodypart_list
  174. # filterDimParts ==============================================================================
  175. # input parameters:
  176. # (<BodyPart[]>list, <num> minimum area of part, <num> max area, <num> min aspect ratio, <num> max aspect ratio)
  177. def filterDimParts(bp_list, min_area, max_area, min_ar, max_ar):
  178. b_filt = []
  179. for obj in bp_list:
  180. a = obj.w*obj.h #Object AREA
  181. if ((a > min_area)and(a < max_area)):
  182. ar = obj.w/obj.h #Object ASPECT RATIO
  183. if ((ar>min_ar)and(ar<max_ar)):
  184. b_filt.append(obj)
  185. return b_filt
  186. # filterCouple ==============================================================================
  187. # input parameters:
  188. # (<BodyPart[]>list)
  189. def filterCouple(bp_list):
  190. #Remove exceed parts
  191. if (len(bp_list)>2):
  192. #trovare coppia (a,b) che minimizza bp_list[a].y-bp_list[b].y
  193. min_a = 0
  194. min_b = 1
  195. min_diff = abs(bp_list[min_a].y-bp_list[min_b].y)
  196. for a in range(0,len(bp_list)):
  197. for b in range(0,len(bp_list)):
  198. #TODO: avoid repetition (1,0) (0,1)
  199. if a != b:
  200. diff = abs(bp_list[a].y-bp_list[b].y)
  201. if diff<min_diff:
  202. min_diff = diff
  203. min_a = a
  204. min_b = b
  205. b_filt = []
  206. b_filt.append(bp_list[min_a])
  207. b_filt.append(bp_list[min_b])
  208. return b_filt
  209. else:
  210. #No change
  211. return bp_list
  212. # detectTitAurMissingProblem ==============================================================================
  213. # input parameters:
  214. # (<BodyPart[]> tits list, <BodyPart[]> aur list)
  215. # return
  216. # (<num> problem code)
  217. # TIT | AUR | code | SOLVE? |
  218. # 0 | 0 | 1 | NO |
  219. # 0 | 1 | 2 | NO |
  220. # 0 | 2 | 3 | YES |
  221. # 1 | 0 | 4 | NO |
  222. # 1 | 1 | 5 | NO |
  223. # 1 | 2 | 6 | YES |
  224. # 2 | 0 | 7 | YES |
  225. # 2 | 1 | 8 | YES |
  226. def detectTitAurMissingProblem(tits_list, aur_list):
  227. t_len = len(tits_list)
  228. a_len = len(aur_list)
  229. if (t_len == 0):
  230. if (a_len == 0):
  231. return 1
  232. elif (a_len == 1):
  233. return 2
  234. elif (a_len == 2):
  235. return 3
  236. else:
  237. return -1
  238. elif (t_len == 1):
  239. if (a_len == 0):
  240. return 4
  241. elif (a_len == 1):
  242. return 5
  243. elif (a_len == 2):
  244. return 6
  245. else:
  246. return -1
  247. elif (t_len == 2):
  248. if (a_len == 0):
  249. return 7
  250. elif (a_len == 1):
  251. return 8
  252. else:
  253. return -1
  254. else:
  255. return -1
  256. # resolveTitAurMissingProblems ==============================================================================
  257. # input parameters:
  258. # (<BodyPart[]> tits list, <BodyPart[]> aur list, problem code)
  259. # return
  260. # none
  261. def resolveTitAurMissingProblems(tits_list, aur_list, problem_code):
  262. if problem_code == 3:
  263. random_tit_factor = random.randint(2, 5) #TOTEST
  264. #Add the first tit:
  265. new_w = aur_list[0].w * random_tit_factor #TOTEST
  266. new_x = aur_list[0].x
  267. new_y = aur_list[0].y
  268. xmin = int(new_x - (new_w/2))
  269. xmax = int(new_x + (new_w/2))
  270. ymin = int(new_y - (new_w/2))
  271. ymax = int(new_y + (new_w/2))
  272. tits_list.append(BodyPart("tit", xmin, ymin, xmax, ymax, new_x, new_y, new_w, new_w ))
  273. #Add the second tit:
  274. new_w = aur_list[1].w * random_tit_factor #TOTEST
  275. new_x = aur_list[1].x
  276. new_y = aur_list[1].y
  277. xmin = int(new_x - (new_w/2))
  278. xmax = int(new_x + (new_w/2))
  279. ymin = int(new_y - (new_w/2))
  280. ymax = int(new_y + (new_w/2))
  281. tits_list.append(BodyPart("tit", xmin, ymin, xmax, ymax, new_x, new_y, new_w, new_w ))
  282. elif problem_code == 6:
  283. #Find wich aur is full:
  284. d1 = abs(tits_list[0].x - aur_list[0].x)
  285. d2 = abs(tits_list[0].x - aur_list[1].x)
  286. if d1 > d2:
  287. #aur[0] is empty
  288. new_x = aur_list[0].x
  289. new_y = aur_list[0].y
  290. else:
  291. #aur[1] is empty
  292. new_x = aur_list[1].x
  293. new_y = aur_list[1].y
  294. #Calculate Bounding Box:
  295. xmin = int(new_x - (tits_list[0].w/2))
  296. xmax = int(new_x + (tits_list[0].w/2))
  297. ymin = int(new_y - (tits_list[0].w/2))
  298. ymax = int(new_y + (tits_list[0].w/2))
  299. tits_list.append(BodyPart("tit", xmin, ymin, xmax, ymax, new_x, new_y, tits_list[0].w, tits_list[0].w ))
  300. elif problem_code == 7:
  301. #Add the first aur:
  302. new_w = tits_list[0].w * random.uniform(0.03, 0.1) #TOTEST
  303. new_x = tits_list[0].x
  304. new_y = tits_list[0].y
  305. xmin = int(new_x - (new_w/2))
  306. xmax = int(new_x + (new_w/2))
  307. ymin = int(new_y - (new_w/2))
  308. ymax = int(new_y + (new_w/2))
  309. aur_list.append(BodyPart("aur", xmin, ymin, xmax, ymax, new_x, new_y, new_w, new_w ))
  310. #Add the second aur:
  311. new_w = tits_list[1].w * random.uniform(0.03, 0.1) #TOTEST
  312. new_x = tits_list[1].x
  313. new_y = tits_list[1].y
  314. xmin = int(new_x - (new_w/2))
  315. xmax = int(new_x + (new_w/2))
  316. ymin = int(new_y - (new_w/2))
  317. ymax = int(new_y + (new_w/2))
  318. aur_list.append(BodyPart("aur", xmin, ymin, xmax, ymax, new_x, new_y, new_w, new_w ))
  319. elif problem_code == 8:
  320. #Find wich tit is full:
  321. d1 = abs(aur_list[0].x - tits_list[0].x)
  322. d2 = abs(aur_list[0].x - tits_list[1].x)
  323. if d1 > d2:
  324. #tit[0] is empty
  325. new_x = tits_list[0].x
  326. new_y = tits_list[0].y
  327. else:
  328. #tit[1] is empty
  329. new_x = tits_list[1].x
  330. new_y = tits_list[1].y
  331. #Calculate Bounding Box:
  332. xmin = int(new_x - (aur_list[0].w/2))
  333. xmax = int(new_x + (aur_list[0].w/2))
  334. ymin = int(new_y - (aur_list[0].w/2))
  335. ymax = int(new_y + (aur_list[0].w/2))
  336. aur_list.append(BodyPart("aur", xmin, ymin, xmax, ymax, new_x, new_y, aur_list[0].w, aur_list[0].w ))
  337. # detectTitAurPositionProblem ==============================================================================
  338. # input parameters:
  339. # (<BodyPart[]> tits list, <BodyPart[]> aur list)
  340. # return
  341. # (<Boolean> True/False)
  342. def detectTitAurPositionProblem(tits_list, aur_list):
  343. diffTitsX = abs(tits_list[0].x - tits_list[1].x)
  344. if diffTitsX < 40:
  345. print("diffTitsX")
  346. #Tits too narrow (orizontally)
  347. return True
  348. diffTitsY = abs(tits_list[0].y - tits_list[1].y)
  349. if diffTitsY > 120:
  350. #Tits too distanced (vertically)
  351. print("diffTitsY")
  352. return True
  353. diffTitsW = abs(tits_list[0].w - tits_list[1].w)
  354. if ((diffTitsW < 0.1)or(diffTitsW>60)):
  355. print("diffTitsW")
  356. #Tits too equals, or too different (width)
  357. return True
  358. #Check if body position is too low (face not covered by watermark)
  359. if aur_list[0].y > 350: #tits too low
  360. #Calculate the ratio between y and aurs distance
  361. rapp = aur_list[0].y/(abs(aur_list[0].x - aur_list[1].x))
  362. if rapp > 2.8:
  363. print("aurDown")
  364. return True
  365. return False
  366. # inferNip ==============================================================================
  367. # input parameters:
  368. # (<BodyPart[]> aur list)
  369. # return
  370. # (<BodyPart[]> nip list)
  371. def inferNip(aur_list):
  372. nip_list = []
  373. for aur in aur_list:
  374. #Nip rules:
  375. # - circle (w == h)
  376. # - min dim: 5
  377. # - bigger if aur is bigger
  378. nip_dim = int(5 + aur.w*random.uniform(0.03, 0.09))
  379. #center:
  380. x = aur.x
  381. y = aur.y
  382. #Calculate Bounding Box:
  383. xmin = int(x - (nip_dim/2))
  384. xmax = int(x + (nip_dim/2))
  385. ymin = int(y - (nip_dim/2))
  386. ymax = int(y + (nip_dim/2))
  387. nip_list.append(BodyPart("nip", xmin, ymin, xmax, ymax, x, y, nip_dim, nip_dim ))
  388. return nip_list
  389. # inferHair (TOTEST) ==============================================================================
  390. # input parameters:
  391. # (<BodyPart[]> vag list)
  392. # return
  393. # (<BodyPart[]> hair list)
  394. def inferHair(vag_list):
  395. hair_list = []
  396. #70% of chanche to add hair
  397. if random.uniform(0.0, 1.0) > 0.3:
  398. for vag in vag_list:
  399. #Hair rules:
  400. hair_w = vag.w*random.uniform(0.4, 1.5)
  401. hair_h = vag.h*random.uniform(0.4, 1.5)
  402. #center:
  403. x = vag.x
  404. y = vag.y - (hair_h/2) - (vag.h/2)
  405. #Calculate Bounding Box:
  406. xmin = int(x - (hair_w/2))
  407. xmax = int(x + (hair_w/2))
  408. ymin = int(y - (hair_h/2))
  409. ymax = int(y + (hair_h/2))
  410. hair_list.append(BodyPart("hair", xmin, ymin, xmax, ymax, x, y, hair_w, hair_h ))
  411. return hair_list