1
+ from __future__ import print_function
2
+ from __future__ import division
3
+
4
+ import cv2
5
+ import argparse
6
+ import os
7
+
8
+ cascade_path = os .path .dirname (__file__ ) + '/cascades/haarcascade_frontalface_default.xml'
9
+
10
+ def center_from_faces (matrix ):
11
+ face_cascade = cv2 .CascadeClassifier (cascade_path )
12
+ faces = face_cascade .detectMultiScale (matrix , 1.3 , 5 )
13
+
14
+ x = 0
15
+ y = 0
16
+ weight = 0
17
+
18
+ for (x , y , w , h ) in faces :
19
+ print ('Face detected at ' , x , y , w , h )
20
+ weight += w * h
21
+ x += (x + w / 2 ) * w * h
22
+ y += (y + h / 2 ) * w * h
23
+
24
+ if len (faces ) == 0 :
25
+ return False
26
+
27
+ return {
28
+ 'x' : x / weight ,
29
+ 'y' : y / weight
30
+ }
31
+
32
+
33
+ def center_from_good_features (matrix ):
34
+ x = 0
35
+ y = 0
36
+ weight = 0
37
+
38
+ max_corners = 50
39
+ quality_level = 0.1
40
+ min_distance = 10
41
+
42
+ corners = cv2 .goodFeaturesToTrack (matrix , max_corners , quality_level , min_distance )
43
+
44
+ for point in corners :
45
+ weight += 1
46
+ x += point [0 ][0 ]
47
+ y += point [0 ][1 ]
48
+
49
+ return {
50
+ 'x' : x / weight ,
51
+ 'y' : y / weight
52
+ }
53
+
54
+
55
+ def crop_vertical_pos (center , original_width , original_height , target_width , target_height ):
56
+ new_height = target_height / target_width * original_width
57
+ top = max (round (center ['y' ] - new_height / 2 ), 0 )
58
+ bottom = min (top + new_height , original_height )
59
+
60
+ return {
61
+ 'left' : 0 ,
62
+ 'right' : original_width ,
63
+ 'top' : top ,
64
+ 'bottom' : bottom
65
+ }
66
+
67
+
68
+ def crop_horizontal_pos (center , original_width , original_height , target_width , target_height ):
69
+ new_width = target_width / target_height * original_height
70
+ left = max (round (center ['x' ] - new_width / 2 ), 0 )
71
+ right = min (left + new_width , original_width )
72
+
73
+ return {
74
+ 'left' : left ,
75
+ 'right' : right ,
76
+ 'top' : 0 ,
77
+ 'bottom' : original_height
78
+ }
79
+
80
+
81
+ def smart_crop (image , target_width , target_height , destination ):
82
+ # read grayscale image
83
+ original = cv2 .imread (image )
84
+
85
+ if original is None :
86
+ print ("Could not read source image" )
87
+ exit (1 )
88
+
89
+ height , width , depth = original .shape
90
+
91
+ target_height = int (target_height )
92
+ target_width = int (target_width )
93
+
94
+ matrix = cv2 .imread (image , cv2 .CV_LOAD_IMAGE_GRAYSCALE )
95
+
96
+ ratio = max (target_width / width , target_height / height )
97
+
98
+ width , height = original .shape [1 ] * ratio , original .shape [0 ] * ratio
99
+ original = cv2 .resize (original , (int (width ), int (height )))
100
+ matrix = cv2 .resize (matrix , (int (width ), int (height )))
101
+
102
+ center = center_from_faces (matrix )
103
+
104
+ if not center :
105
+ print ('Using Good Feature Tracking method' )
106
+ center = center_from_good_features (matrix )
107
+
108
+ print ('Found center: ' , center )
109
+
110
+ if width / height < target_width / target_height :
111
+ crop_pos = crop_vertical_pos (center , width , height , target_width , target_height )
112
+ else :
113
+ crop_pos = crop_horizontal_pos (center , width , height , target_width , target_height )
114
+
115
+ print ('Crop: ' , crop_pos )
116
+
117
+ cropped = original [crop_pos ['top' ]: crop_pos ['bottom' ], crop_pos ['left' ]: crop_pos ['right' ]]
118
+ cv2 .imwrite (destination , cropped )
119
+
120
+
121
+ def main ():
122
+ ap = argparse .ArgumentParser ()
123
+ ap .add_argument ("-W" , "--width" , required = True , help = "Target width" )
124
+ ap .add_argument ("-H" , "--height" , required = True , help = "Target height" )
125
+ ap .add_argument ("-i" , "--image" , required = True , help = "Image to crop" )
126
+ ap .add_argument ("-o" , "--output" , required = True , help = "Output" )
127
+
128
+ args = vars (ap .parse_args ())
129
+
130
+ smart_crop (args ["image" ], args ["width" ], args ["height" ], args ["output" ])
131
+
132
+
133
+ if __name__ == '__main__' :
134
+ main ()
0 commit comments