1818
1919import android .content .Context ;
2020import android .graphics .Bitmap ;
21+ import android .graphics .BitmapFactory ;
22+ import android .graphics .Canvas ;
2123import android .util .Log ;
2224import android .util .SparseArray ;
2325import android .widget .Toast ;
@@ -30,18 +32,18 @@ class Emojifier {
3032
3133 private static final String LOG_TAG = Emojifier .class .getSimpleName ();
3234
35+ private static final float EMOJI_SCALE_FACTOR = .9f ;
3336 private static final double SMILING_PROB_THRESHOLD = .15 ;
3437 private static final double EYE_OPEN_PROB_THRESHOLD = .5 ;
3538
3639 /**
37- * Method for detecting faces in a bitmap.
40+ * Method for detecting faces in a bitmap, and drawing emoji depending on the facial
41+ * expression.
3842 *
3943 * @param context The application context.
4044 * @param picture The picture in which to detect the faces.
4145 */
42- static void detectFaces (Context context , Bitmap picture ) {
43-
44- // TODO (3): Change the name of the detectFaces() method to detectFacesAndOverlayEmoji() and the return type from void to Bitmap
46+ static Bitmap detectFacesandOverlayEmoji (Context context , Bitmap picture ) {
4547
4648 // Create the face detector, disable tracking and enable classifications
4749 FaceDetector detector = new FaceDetector .Builder (context )
@@ -58,29 +60,67 @@ static void detectFaces(Context context, Bitmap picture) {
5860 // Log the number of faces
5961 Log .d (LOG_TAG , "detectFaces: number of faces = " + faces .size ());
6062
61- // TODO (7): Create a variable called resultBitmap and initialize it to the original picture bitmap passed into the detectFacesAndOverlayEmoji() method
63+ // Initialize result bitmap to original picture
64+ Bitmap resultBitmap = picture ;
65+
6266 // If there are no faces detected, show a Toast message
63- if (faces .size () == 0 ) {
67+ if (faces .size () == 0 ) {
6468 Toast .makeText (context , R .string .no_faces_message , Toast .LENGTH_SHORT ).show ();
6569 } else {
6670
6771 // Iterate through the faces
6872 for (int i = 0 ; i < faces .size (); ++i ) {
6973 Face face = faces .valueAt (i );
70- // Get the appropriate emoji for each face
71- whichEmoji (face );
72-
73- // TODO (4): Create a variable called emojiBitmap to hold the appropriate Emoji bitmap and remove the call to whichEmoji()
74- // TODO (5): Create a switch statement on the result of the whichEmoji() call, and assign the proper emoji bitmap to the variable you created
75- // TODO (8): Call addBitmapToFace(), passing in the resultBitmap, the emojiBitmap and the Face object, and assigning the result to resultBitmap
7674
75+ Bitmap emojiBitmap ;
76+ switch (whichEmoji (face )) {
77+ case SMILE :
78+ emojiBitmap = BitmapFactory .decodeResource (context .getResources (),
79+ R .drawable .smile );
80+ break ;
81+ case FROWN :
82+ emojiBitmap = BitmapFactory .decodeResource (context .getResources (),
83+ R .drawable .frown );
84+ break ;
85+ case LEFT_WINK :
86+ emojiBitmap = BitmapFactory .decodeResource (context .getResources (),
87+ R .drawable .leftwink );
88+ break ;
89+ case RIGHT_WINK :
90+ emojiBitmap = BitmapFactory .decodeResource (context .getResources (),
91+ R .drawable .rightwink );
92+ break ;
93+ case LEFT_WINK_FROWN :
94+ emojiBitmap = BitmapFactory .decodeResource (context .getResources (),
95+ R .drawable .leftwinkfrown );
96+ break ;
97+ case RIGHT_WINK_FROWN :
98+ emojiBitmap = BitmapFactory .decodeResource (context .getResources (),
99+ R .drawable .rightwinkfrown );
100+ break ;
101+ case CLOSED_EYE_SMILE :
102+ emojiBitmap = BitmapFactory .decodeResource (context .getResources (),
103+ R .drawable .closed_smile );
104+ break ;
105+ case CLOSED_EYE_FROWN :
106+ emojiBitmap = BitmapFactory .decodeResource (context .getResources (),
107+ R .drawable .closed_frown );
108+ break ;
109+ default :
110+ emojiBitmap = null ;
111+ Toast .makeText (context , R .string .no_emoji , Toast .LENGTH_SHORT ).show ();
112+ }
113+
114+ // Add the emojiBitmap to the proper position in the original image
115+ resultBitmap = addBitmapToFace (resultBitmap , emojiBitmap , face );
77116 }
78117 }
79118
80119
81120 // Release the detector
82121 detector .release ();
83- // TODO (9): Return the resultBitmap
122+
123+ return resultBitmap ;
84124 }
85125
86126
@@ -91,9 +131,7 @@ static void detectFaces(Context context, Bitmap picture) {
91131 * @param face The face for which you pick an emoji.
92132 */
93133
94- private static void whichEmoji (Face face ) {
95-
96- // TODO (1): Change the return type of the whichEmoji() method from void to Emoji.
134+ private static Emoji whichEmoji (Face face ) {
97135 // Log all the probabilities
98136 Log .d (LOG_TAG , "whichEmoji: smilingProb = " + face .getIsSmilingProbability ());
99137 Log .d (LOG_TAG , "whichEmoji: leftEyeOpenProb = "
@@ -136,10 +174,50 @@ private static void whichEmoji(Face face) {
136174 // Log the chosen Emoji
137175 Log .d (LOG_TAG , "whichEmoji: " + emoji .name ());
138176
139- // TODO (2): Have the method return the selected Emoji type.
177+ return emoji ;
178+ }
179+
180+ /**
181+ * Combines the original picture with the emoji bitmaps
182+ *
183+ * @param backgroundBitmap The original picture
184+ * @param emojiBitmap The chosen emoji
185+ * @param face The detected face
186+ * @return The final bitmap, including the emojis over the faces
187+ */
188+ private static Bitmap addBitmapToFace (Bitmap backgroundBitmap , Bitmap emojiBitmap , Face face ) {
189+
190+ // Initialize the results bitmap to be a mutable copy of the original image
191+ Bitmap resultBitmap = Bitmap .createBitmap (backgroundBitmap .getWidth (),
192+ backgroundBitmap .getHeight (), backgroundBitmap .getConfig ());
193+
194+ // Scale the emoji so it looks better on the face
195+ float scaleFactor = EMOJI_SCALE_FACTOR ;
196+
197+ // Determine the size of the emoji to match the width of the face and preserve aspect ratio
198+ int newEmojiWidth = (int ) (face .getWidth () * scaleFactor );
199+ int newEmojiHeight = (int ) (emojiBitmap .getHeight () *
200+ newEmojiWidth / emojiBitmap .getWidth () * scaleFactor );
201+
202+
203+ // Scale the emoji
204+ emojiBitmap = Bitmap .createScaledBitmap (emojiBitmap , newEmojiWidth , newEmojiHeight , false );
205+
206+ // Determine the emoji position so it best lines up with the face
207+ float emojiPositionX =
208+ (face .getPosition ().x + face .getWidth () / 2 ) - emojiBitmap .getWidth () / 2 ;
209+ float emojiPositionY =
210+ (face .getPosition ().y + face .getHeight () / 2 ) - emojiBitmap .getHeight () / 3 ;
211+
212+ // Create the canvas and draw the bitmaps to it
213+ Canvas canvas = new Canvas (resultBitmap );
214+ canvas .drawBitmap (backgroundBitmap , 0 , 0 , null );
215+ canvas .drawBitmap (emojiBitmap , emojiPositionX , emojiPositionY , null );
216+
217+ return resultBitmap ;
140218 }
219+
141220
142- // TODO (6) Create a method called addBitmapToFace() which takes the background bitmap, the Emoji bitmap, and a Face object as arguments and returns the combined bitmap with the Emoji over the face.
143221 // Enum for all possible Emojis
144222 private enum Emoji {
145223 SMILE ,
0 commit comments