Advertisement
boolit

PhotoModeration

Jun 13th, 2023 (edited)
1,125
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.58 KB | None | 0 0
  1. ''' import libraries '''
  2. import ssl
  3. import face_alignment
  4. import numpy as np
  5. import matplotlib.image as mpimg
  6. from skimage import io
  7. from sklearn.cluster import OPTICS
  8.  
  9. ssl._create_default_https_context = ssl._create_unverified_context
  10.  
  11. class PhotoModeration:
  12.     def __init__(
  13.         self,
  14.         image_path: str,
  15.                 ) -> None:
  16.         self.image_path = image_path        
  17.         self.face_model = face_alignment.FaceAlignment(face_alignment.LandmarksType.TWO_D, flip_input=False, device='cpu')
  18.         self.image = io.imread(image_path, pilmode='RGB')
  19.        
  20.         self.error = None
  21. #         self.final_image = None
  22.         self.num_landmarks = None
  23.         self.real_width_ratio = None
  24.         self.real_height_ratio = None
  25.         self.X_face_center = None
  26.         self.Y_face_center = None
  27.         self.X_img_center = int(self.image.shape[1]/2)
  28.         self.Y_img_center = int(self.image.shape[0]/2)
  29.        
  30.     def find_non_crossed_intervals(self, landmarks):
  31.         ''' find uncrossed landmarks by x-axis '''
  32.         ''' compute distence between center of image and center of 1 landmark '''
  33.         x_arr0 = landmarks[0][:,0]
  34.         y_arr0 = landmarks[0][:,1]
  35.         x_center_arr0 = np.mean(x_arr0)
  36.         y_center_arr0 = np.mean(y_arr0)
  37.         ''' bound of unit width of crossed min max face width '''
  38.         min_x0, max_x0 = min(x_arr0), max(x_arr0)
  39.         ind_2_and_more_faces = 0
  40.         dist0 = np.sqrt( (x_center_arr0 - self.X_img_center)**2 + (y_center_arr0 - self.Y_img_center)**2 )
  41.         ''' id of landmark which is the nearest to photo center '''
  42.         target_nearest_landmark_ind = 0
  43.         ''' compute distence between center of image and center of other landmarks '''
  44.         for i in range(1, len(landmarks)):
  45.             x_arr1 = landmarks[i][:,0]
  46.             y_arr1 = landmarks[i][:,1]
  47.             x_center_arr1 = np.mean(x_arr1)
  48.             y_center_arr1 = np.mean(y_arr1)
  49.             dist1 = np.sqrt( (x_center_arr1 - self.X_img_center)**2 + (y_center_arr1 - self.Y_img_center)**2 )
  50.             if dist1 < dist0:
  51.                 target_nearest_landmark_ind = i
  52.                 dist0 = dist1
  53.             min_x1, max_x1 = min(x_arr1), max(x_arr1)
  54.             ''' if width of min max faces is crossed along x-axis recompute min max of unit width '''
  55.             if min_x0 < min_x1 and min_x1 < max_x0 and max_x1 > max_x0:
  56.                 min_x0, max_x0 = min_x0, max_x1
  57.                
  58.             elif min_x0 < min_x1 and min_x1 < max_x0 and max_x1 < max_x0:
  59.                 min_x0, max_x0 = min_x0, max_x0
  60.  
  61.             elif min_x1 < min_x0 and min_x0 < max_x1 and max_x0 > max_x1:
  62.                 min_x0, max_x0 = min_x1, max_x0
  63.  
  64.             elif min_x1 < min_x0 and min_x0 < max_x1 and max_x0 < max_x1:
  65.                 min_x0, max_x0 = min_x1, max_x1
  66.             else:
  67.                 ''' if width of min max faces is  uncrossed along x-axis make ind_2_and_more_faces + 1 '''
  68.                 ind_2_and_more_faces += 1
  69.         ''' return ind_2_and_more_faces and id ladmark which is the nearest to the center '''  
  70.         return ind_2_and_more_faces, target_nearest_landmark_ind
  71.  
  72.    
  73.     def if_landmark_only_one(self, landmarks):
  74.         ''' compute X_face_center and Y_face_center over found landmark '''
  75.         x_array = landmarks[:,0]
  76.         y_array = landmarks[:,1]
  77.         self.X_face_center = np.mean(x_array)
  78.         self.Y_face_center = np.mean(y_array)
  79.         ''' compute width and height of the uploaded photo '''
  80.         W = self.image.shape[1]
  81.         H = self.image.shape[0]
  82.         ''' compute the ratio of X_face_center to W '''
  83.         x_center_to_w = self.X_face_center/W
  84.         ''' compute the ratio of Y_face_center to H '''
  85.         y_center_to_H = 1 - self.Y_face_center/H
  86.         ''' set ellipse parameters, ellipse must include face center '''
  87.         ''' parameters were selected empirically based on 7000 photos '''
  88.         u = 0.4984#np.mean(x_center_to_w)     #x-position of the center
  89.         v = 0.5783#np.mean([1-i for i in y_center_to_H])    #y-position of the center
  90.         a =.12#radius on the x-axis
  91.         b =.2#radius on the y-axis
  92.         ''' if center of face is outside the min max values of ellipse return error 2 - The detected face is not centered'''
  93.         if x_center_to_w <= u - a or x_center_to_w >= u + a or y_center_to_H <= v - b or y_center_to_H >= v + b:
  94.             self.error =  'The detected face is not centered'
  95.         else:
  96.             y0 = v + np.sqrt( (1 - (x_center_to_w - u)**2/a**2)*b**2 )
  97.             y1 = v - np.sqrt( (1 - (x_center_to_w - u)**2/a**2)*b**2 )
  98.             x0 = u + np.sqrt( (1 - (y_center_to_H - v)**2/b**2)*a**2 )
  99.             x1 = u - np.sqrt( (1 - (y_center_to_H - v)**2/b**2)*a**2 )
  100.             ''' if center of landmark is located outside ellipse return error 2 - The detected face is not centered '''
  101.             if y_center_to_H < y1 or y_center_to_H > y0 or x_center_to_w < x1 or x_center_to_w > x0:
  102.                 self.error =  'The detected face is not centered'
  103.                 ''' if center of landmark is located inside ellipse '''
  104.             else:
  105.                 ''' compute the ratio landmark width to photo width '''
  106.                 self.real_width_ratio = abs(max(x_array) - min(x_array))/W
  107.                 ''' compute the ratio landmark height to photo height '''
  108.                 self.real_height_ratio = abs(max(y_array) - min(y_array))/H
  109.                 ''' if the photo is oriented horizontally return error 3 - The height of the photos is less than the width '''
  110.                 if W > H:
  111.                     self.error =  'The height of the photos is less than the width'
  112.                     ''' if real_width_ratio < 0.25 and real_height_ratio <= real_width_ratio error 4 - The detected face is too small on the photo '''
  113.                 elif self.real_width_ratio < 0.25:
  114.                     self.error =  'The detected face is too small on the photo'
  115.                 elif self.real_width_ratio > 0.6:
  116.                     if self.real_height_ratio <= self.real_width_ratio and self.real_height_ratio > 0.56*self.real_width_ratio:
  117.                         ''' if real_width_ratio > 0.6 and real_height_ratio <= real_width_ratio an real_height_ratio > 0.56*real_width_ratio error 5 - The face is too large on the photo, the photo size is standard '''
  118.                         self.error =  'The face is too large on the photo, the photo size is standard'
  119.                         ''' if real_width_ratio > 0.6 and real_height_ratio <= 0.56*real_width_ratio error 6 - The face is too large on the photo, the photo is elongated vertically '''
  120.                     elif self.real_height_ratio <= 0.56*self.real_width_ratio:
  121.                         self.error =  'The face is too large on the photo, the photo is elongated vertically'              
  122.                 elif self.real_width_ratio >= 0.25 and self.real_width_ratio <= 0.6:
  123.                     ''' if real_width_ratio >= 0.25 and real_width_ratio <= 0.6 and real_height_ratio < 0.56*real_width_ratio error 7 - The face is standard size on the photo, the photo is elongated vertically '''
  124.                     if self.real_height_ratio < 0.56*self.real_width_ratio:
  125.                         self.error =  'The face is standard size on the photo, the photo is elongated vertically'
  126.                         ''' if real_width_ratio >= 0.25 and real_width_ratio <= 0.6 and real_height_ratio >= 0.56*real_width_ratio
  127.                        photo size is standard and face size is standard '''
  128.                     elif self.real_height_ratio >= 0.56*self.real_width_ratio:
  129.                         ''' search dark glasses '''
  130.                         ''' select landmark dots which are located upper face center along y axis '''
  131.                         indexes_of_up_landmarks = [i for i, x in enumerate(y_array) if x < self.Y_face_center]
  132.                         x_up_landmarks = landmarks[:,0][indexes_of_up_landmarks]
  133.                         y_up_landmarks = landmarks[:,1][indexes_of_up_landmarks]
  134.                         landmarks_eye = np.vstack([x_up_landmarks, y_up_landmarks]).T
  135.                         ''' split up landmark dots into 2 clusters, main idea: 1 cluster - 1 eye '''
  136.                         clustering = OPTICS(min_samples = 5).fit(landmarks_eye)
  137.                         ''' dark ratio around each eye '''
  138.                         cluster_domain_dark_ratio = []
  139.                         min_x, max_x = int(min(x_array)), int(max(x_array))
  140.                         face_scale_x = np.abs(max_x - min_x)
  141.                         for cluster in range(0, len(set(clustering.labels_))):
  142.                             ind_of_labels = [i for i, x in enumerate(clustering.labels_) if x == cluster]
  143.                             if len(ind_of_labels) > 0:                
  144.                                 sub_x = landmarks_eye[:,0][ind_of_labels]
  145.                                 sub_y = landmarks_eye[:,1][ind_of_labels]
  146.                                 k = 0.2
  147.                                 h = 0.15
  148.                                 min_sub_x = int(np.mean(sub_x) - face_scale_x*k)
  149.                                 max_sub_x = int(np.mean(sub_x) + face_scale_x*k)
  150.  
  151.                                 min_sub_y = int(np.mean(sub_y) - face_scale_x*h)
  152.                                 max_sub_y = int(np.mean(sub_y) + face_scale_x*h)
  153.                                 normalized_img = self.image/self.image.max()
  154.                                 cropped_image_glass = normalized_img[min_sub_y:max_sub_y, min_sub_x:max_sub_x]              
  155.                                 color_bound = 0.3
  156.                                 cropped_image_glass[cropped_image_glass[:,:,0] > color_bound] = 1
  157.                                 cropped_image_glass[cropped_image_glass[:,:,1] > color_bound] = 1
  158.                                 cropped_image_glass[cropped_image_glass[:,:,2] > color_bound] = 1
  159.                                 #compute_dark_ratio
  160.                                 unique_x, counts_x = np.unique(cropped_image_glass[:,:,0], return_counts = True)
  161.                                 unique_y, counts_y = np.unique(cropped_image_glass[:,:,1], return_counts = True)
  162.                                 unique_z, counts_z = np.unique(cropped_image_glass[:,:,2], return_counts = True)
  163.                                 if 1 in unique_x or 1 in unique_y or 1 in unique_z:
  164.                                     x_count = dict(zip(unique_x, counts_x))[1]
  165.                                     y_count = dict(zip(unique_y, counts_y))[1]
  166.                                     z_count = dict(zip(unique_z, counts_z))[1]
  167.                                     dark_ratio = (1 - (x_count + y_count + z_count)/(3*cropped_image_glass.shape[0]*cropped_image_glass.shape[1]))
  168.                                     cluster_domain_dark_ratio.append(dark_ratio)
  169.                         ''' min dark ratio >= 0.42 error 8 - The area around the eyes is dark or you have dark glasses '''
  170.                         if min(cluster_domain_dark_ratio) >= 0.42:
  171.                             self.error = 'The area around the eyes is dark or you have dark glasses'
  172.                             ''' min dark ratio < 0.42 the area around the eyes is not dark or you don't have dark glasses'''
  173. #                         else:
  174. #                             const_x = 0.44
  175. #                             const_y = 0.32
  176. #                             delta_x = (max(x_array) - min(x_array))/const_x/2
  177. #                             delta_y = (max(y_array) - min(y_array))/const_y/2
  178. #                             left_bound_x = int(self.X_face_center - delta_x)
  179. #                             if left_bound_x < 0:
  180. #                                 left_bound_x = 0
  181. #                             up_bound_y = int(self.Y_face_center - delta_y)
  182. #                             if up_bound_y < 0:
  183. #                                 up_bound_y = 0
  184. #                             ''' crop photo 3 to 4 ratio around landmark '''
  185. #                             cropped_image = self.image[up_bound_y:int(self.Y_face_center + delta_y), left_bound_x:int(self.X_face_center + delta_x), :]#.astype(int)
  186. #                             ''' if width of cropped photo < 200 px error 9 - Low quality of photo (cropprd 3 to 4 face landmark) '''
  187. #                             if cropped_image.shape[0] < 200:
  188. #                                 self.error = 'Low quality of photo (cropprd 3 to 4 face landmark)'
  189.     #                         else:
  190.     #                             ''' if width of cropped photo > 200 px return cropped final image '''
  191.     #                             self.image = mpimg.imread(self.image_path)
  192.     #                             self.final_image = self.image[up_bound_y:int(self.Y_face_center + delta_y), left_bound_x:int(self.X_face_center + delta_x), :]#.astype(int)
  193.         return self.error#, self.final_image
  194.  
  195.     def moderation_photo(self):
  196.         ''' if width or height of photo < 300 px error 9 - One of photo side < 300 px '''
  197.         if self.image.shape[0] < 300 or self.image.shape[1] < 300:
  198.             self.error = 'One of photo side < 300 px'
  199.         else:
  200.             '''check if min(width, hight) > 3000 px'''
  201.             min_side_size = sorted([i for i in self.image.shape])[1]
  202.             if min_side_size  > 3000:
  203.                 width = self.image.shape[0]
  204.                 hight = self.image.shape[1]
  205.                 cut_y = 0.2
  206.                 cut_x = 0.2
  207.                 ''' cut out 80% of width and hight '''
  208.                 image_cropped = self.image[int((cut_y-0.1)*width):-int((cut_y+0.1)*width), int(cut_x*hight):-int(cut_x*hight)]
  209.                 ''' search landmarks by using face_alignment '''
  210.                 landmarks = self.face_model.get_landmarks(image_cropped)
  211.                 ''' if len(landmarks) > 0 transform searched landmarks over initial sizes of photo with higher resolution '''
  212.                 if landmarks != None:
  213.                     for i in range(0, len(landmarks)):
  214.                         landmarks[i][:,0] = landmarks[i][:,0] + int(cut_x*hight)
  215.                         landmarks[i][:,1] = landmarks[i][:,1] + int((cut_y-0.1)*width)            
  216.                         ''' if min(width, hight) >= 3000 px'''
  217.             else:
  218.                 ''' search landmarks by using face_alignment '''
  219.                 landmarks = self.face_model.get_landmarks(self.image)
  220.             ''' if there is no landmarks, return error 0 - No faces were found on the photo '''
  221.             if landmarks == None:
  222.                 self.error = 'No faces were found on the photo'
  223.                 ''' if there is 1 landmark only run if_landmark_only_one() '''
  224.             elif len(landmarks) == 1:
  225.     #             self.error, self.final_image = self.if_landmark_only_one(landmarks[0])
  226.                 self.error = self.if_landmark_only_one(landmarks[0])
  227.                 ''' if there is more than 1 landmark '''
  228.             elif len(landmarks) > 1:
  229.                 ''' remove landmarks which are 10 times smaller in width than the widest landmark '''
  230.                 times_smaller = 10
  231.                 widths_of_landmarks = [max(landmarks[r][:,0]) - min(landmarks[r][:,0]) for r in range(0, len(landmarks))]
  232.                 max_width_landmarks = max(widths_of_landmarks)
  233.                 bound = max_width_landmarks/times_smaller
  234.                 index_of_big_landmarks = [(t) for t, p in enumerate(widths_of_landmarks) if p > bound]
  235.                 landmarks = [landmarks[s] for s in index_of_big_landmarks]
  236.                 ''' check if w/h in [a,b] where w - face width, h - face hight. w and h are computed based on found landmarks '''
  237.                 a = 0.86
  238.                 b = 1.25
  239.                 good_landmarks_indexes = []
  240.                 for ind, ld in enumerate(landmarks):
  241.                     w = abs(max(ld[:,0]) - min(ld[:,0]))
  242.                     h = abs(max(ld[:,1]) - min(ld[:,1]))
  243.                     if w/h >= a and w/h <= b:
  244.                         good_landmarks_indexes.append(ind)
  245.                         ''' if number of landmarks in [a,b] = 0 return error 0 - No faces were found on the photo '''
  246.                 if len(good_landmarks_indexes) == 0:
  247.                     self.error = 'No faces were found on the photo'            
  248.                     ''' if number of landmarks in [a,b] = 1 run if_landmark_only_one() '''
  249.                 elif len(good_landmarks_indexes) == 1:
  250.     #                 self.error, self.final_image = self.if_landmark_only_one(landmarks[ind])
  251.                     self.error = self.if_landmark_only_one(landmarks[ind])
  252.                     ''' if number of landmarks in [a,b] > 1 '''
  253.                 elif len(good_landmarks_indexes) > 1:
  254.                     ''' choose landmarks in [a,b] '''
  255.                     landmarks = [landmarks[ind] for ind in good_landmarks_indexes]              
  256.                     ''' run find_non_crossed_intervals() '''
  257.                     indicator_2_and_more_faces, target_average_landmark_ind = self.find_non_crossed_intervals(landmarks)
  258.                     ''' if there is any uncrossed interval return error 1 - Several people were found on the photo '''
  259.                     if indicator_2_and_more_faces >= 1:
  260.                         self.num_landmarks = len(landmarks)
  261.                         self.error = 'Several people were found on the photo'
  262.                         ''' if there is all crossed interval run if_landmark_only_one() '''
  263.                     elif indicator_2_and_more_faces == 0:
  264.     #                     self.error, self.final_image = self.if_landmark_only_one(landmarks[target_average_landmark_ind])
  265.                         self.error = self.if_landmark_only_one(landmarks[target_average_landmark_ind])
  266.  
  267.         return self.error#, self.final_image
Advertisement
Comments
  • alexjohn1991
    1 year (edited)
    # text 0.11 KB | 0 0
    1. [url=https://yellowstones-jackets.com/product/yellowstone-rip-wheeler-black-vest/]Rip wheeler cotton jacket[/url]
Add Comment
Please, Sign In to add comment
Advertisement