Advertisement
boolit

photo_nn_new

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