PageRenderTime 55ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/Face/Android/Sample/app/src/main/java/com/microsoft/projectoxford/face/samples/helper/ImageHelper.java

https://gitlab.com/pymevirtual/ProjectOxford-ClientSDK
Java | 323 lines | 220 code | 38 blank | 65 comment | 23 complexity | 51f442ba019a586a0fcd6fa1b3201ab4 MD5 | raw file
  1. //
  2. // Copyright (c) Microsoft. All rights reserved.
  3. // Licensed under the MIT license.
  4. //
  5. // Microsoft Cognitive Services (formerly Project Oxford): https://www.microsoft.com/cognitive-services
  6. //
  7. // Microsoft Cognitive Services (formerly Project Oxford) GitHub:
  8. // https://github.com/Microsoft/ProjectOxford-ClientSDK
  9. //
  10. // Copyright (c) Microsoft Corporation
  11. // All rights reserved.
  12. //
  13. // MIT License:
  14. // Permission is hereby granted, free of charge, to any person obtaining
  15. // a copy of this software and associated documentation files (the
  16. // "Software"), to deal in the Software without restriction, including
  17. // without limitation the rights to use, copy, modify, merge, publish,
  18. // distribute, sublicense, and/or sell copies of the Software, and to
  19. // permit persons to whom the Software is furnished to do so, subject to
  20. // the following conditions:
  21. //
  22. // The above copyright notice and this permission notice shall be
  23. // included in all copies or substantial portions of the Software.
  24. //
  25. // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
  26. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  27. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  28. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  29. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  30. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  31. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  32. //
  33. package com.microsoft.projectoxford.face.samples.helper;
  34. import android.content.ContentResolver;
  35. import android.database.Cursor;
  36. import android.graphics.Bitmap;
  37. import android.graphics.BitmapFactory;
  38. import android.graphics.Canvas;
  39. import android.graphics.Color;
  40. import android.graphics.Matrix;
  41. import android.graphics.Paint;
  42. import android.graphics.Rect;
  43. import android.media.ExifInterface;
  44. import android.net.Uri;
  45. import android.provider.MediaStore;
  46. import com.microsoft.projectoxford.face.contract.Face;
  47. import com.microsoft.projectoxford.face.contract.FaceRectangle;
  48. import java.io.IOException;
  49. import java.io.InputStream;
  50. /**
  51. * Defined several functions to load, draw, save, resize, and rotate images.
  52. */
  53. public class ImageHelper {
  54. // The maximum side length of the image to detect, to keep the size of image less than 4MB.
  55. // Resize the image if its side length is larger than the maximum.
  56. private static final int IMAGE_MAX_SIDE_LENGTH = 1280;
  57. // Ratio to scale a detected face rectangle, the face rectangle scaled up looks more natural.
  58. private static final double FACE_RECT_SCALE_RATIO = 1.3;
  59. // Decode image from imageUri, and resize according to the expectedMaxImageSideLength
  60. // If expectedMaxImageSideLength is
  61. // (1) less than or equal to 0,
  62. // (2) more than the actual max size length of the bitmap
  63. // then return the original bitmap
  64. // Else, return the scaled bitmap
  65. public static Bitmap loadSizeLimitedBitmapFromUri(
  66. Uri imageUri,
  67. ContentResolver contentResolver) {
  68. try {
  69. // Load the image into InputStream.
  70. InputStream imageInputStream = contentResolver.openInputStream(imageUri);
  71. // For saving memory, only decode the image meta and get the side length.
  72. BitmapFactory.Options options = new BitmapFactory.Options();
  73. options.inJustDecodeBounds = true;
  74. Rect outPadding = new Rect();
  75. BitmapFactory.decodeStream(imageInputStream, outPadding, options);
  76. // Calculate shrink rate when loading the image into memory.
  77. int maxSideLength =
  78. options.outWidth > options.outHeight ? options.outWidth: options.outHeight;
  79. options.inSampleSize = 1;
  80. options.inSampleSize = calculateSampleSize(maxSideLength, IMAGE_MAX_SIDE_LENGTH);
  81. options.inJustDecodeBounds = false;
  82. if (imageInputStream != null) {
  83. imageInputStream.close();
  84. }
  85. // Load the bitmap and resize it to the expected size length
  86. imageInputStream = contentResolver.openInputStream(imageUri);
  87. Bitmap bitmap = BitmapFactory.decodeStream(imageInputStream, outPadding, options);
  88. maxSideLength = bitmap.getWidth() > bitmap.getHeight()
  89. ? bitmap.getWidth(): bitmap.getHeight();
  90. double ratio = IMAGE_MAX_SIDE_LENGTH / (double) maxSideLength;
  91. if (ratio < 1) {
  92. bitmap = Bitmap.createScaledBitmap(
  93. bitmap,
  94. (int)(bitmap.getWidth() * ratio),
  95. (int)(bitmap.getHeight() * ratio),
  96. false);
  97. }
  98. return rotateBitmap(bitmap, getImageRotationAngle(imageUri, contentResolver));
  99. } catch (Exception e) {
  100. return null;
  101. }
  102. }
  103. // Draw detected face rectangles in the original image. And return the image drawn.
  104. // If drawLandmarks is set to be true, draw the five main landmarks of each face.
  105. public static Bitmap drawFaceRectanglesOnBitmap(
  106. Bitmap originalBitmap, Face[] faces, boolean drawLandmarks) {
  107. Bitmap bitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true);
  108. Canvas canvas = new Canvas(bitmap);
  109. Paint paint = new Paint();
  110. paint.setAntiAlias(true);
  111. paint.setStyle(Paint.Style.STROKE);
  112. paint.setColor(Color.GREEN);
  113. int stokeWidth = Math.max(originalBitmap.getWidth(), originalBitmap.getHeight()) / 100;
  114. if (stokeWidth == 0) {
  115. stokeWidth = 1;
  116. }
  117. paint.setStrokeWidth(stokeWidth);
  118. if (faces != null) {
  119. for (Face face : faces) {
  120. FaceRectangle faceRectangle =
  121. calculateFaceRectangle(bitmap, face.faceRectangle, FACE_RECT_SCALE_RATIO);
  122. canvas.drawRect(
  123. faceRectangle.left,
  124. faceRectangle.top,
  125. faceRectangle.left + faceRectangle.width,
  126. faceRectangle.top + faceRectangle.height,
  127. paint);
  128. if (drawLandmarks) {
  129. int radius = face.faceRectangle.width / 30;
  130. if (radius == 0) {
  131. radius = 1;
  132. }
  133. paint.setStyle(Paint.Style.FILL);
  134. paint.setStrokeWidth(radius);
  135. canvas.drawCircle(
  136. (float) face.faceLandmarks.pupilLeft.x,
  137. (float) face.faceLandmarks.pupilLeft.y,
  138. radius,
  139. paint);
  140. canvas.drawCircle(
  141. (float) face.faceLandmarks.pupilRight.x,
  142. (float) face.faceLandmarks.pupilRight.y,
  143. radius,
  144. paint);
  145. canvas.drawCircle(
  146. (float) face.faceLandmarks.noseTip.x,
  147. (float) face.faceLandmarks.noseTip.y,
  148. radius,
  149. paint);
  150. canvas.drawCircle(
  151. (float) face.faceLandmarks.mouthLeft.x,
  152. (float) face.faceLandmarks.mouthLeft.y,
  153. radius,
  154. paint);
  155. canvas.drawCircle(
  156. (float) face.faceLandmarks.mouthRight.x,
  157. (float) face.faceLandmarks.mouthRight.y,
  158. radius,
  159. paint);
  160. paint.setStyle(Paint.Style.STROKE);
  161. paint.setStrokeWidth(stokeWidth);
  162. }
  163. }
  164. }
  165. return bitmap;
  166. }
  167. // Highlight the selected face thumbnail in face list.
  168. public static Bitmap highlightSelectedFaceThumbnail(Bitmap originalBitmap) {
  169. Bitmap bitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true);
  170. Canvas canvas = new Canvas(bitmap);
  171. Paint paint = new Paint();
  172. paint.setAntiAlias(true);
  173. paint.setStyle(Paint.Style.STROKE);
  174. paint.setColor(Color.parseColor("#3399FF"));
  175. int stokeWidth = Math.max(originalBitmap.getWidth(), originalBitmap.getHeight()) / 10;
  176. if (stokeWidth == 0) {
  177. stokeWidth = 1;
  178. }
  179. bitmap.getWidth();
  180. paint.setStrokeWidth(stokeWidth);
  181. canvas.drawRect(
  182. 0,
  183. 0,
  184. bitmap.getWidth(),
  185. bitmap.getHeight(),
  186. paint);
  187. return bitmap;
  188. }
  189. // Crop the face thumbnail out from the original image.
  190. // For better view for human, face rectangles are resized to the rate faceRectEnlargeRatio.
  191. public static Bitmap generateFaceThumbnail(
  192. Bitmap originalBitmap,
  193. FaceRectangle faceRectangle) throws IOException {
  194. FaceRectangle faceRect =
  195. calculateFaceRectangle(originalBitmap, faceRectangle, FACE_RECT_SCALE_RATIO);
  196. return Bitmap.createBitmap(
  197. originalBitmap, faceRect.left, faceRect.top, faceRect.width, faceRect.height);
  198. }
  199. // Return the number of times for the image to shrink when loading it into memory.
  200. // The SampleSize can only be a final value based on powers of 2.
  201. private static int calculateSampleSize(int maxSideLength, int expectedMaxImageSideLength) {
  202. int inSampleSize = 1;
  203. while (maxSideLength > 2 * expectedMaxImageSideLength) {
  204. maxSideLength /= 2;
  205. inSampleSize *= 2;
  206. }
  207. return inSampleSize;
  208. }
  209. // Get the rotation angle of the image taken.
  210. private static int getImageRotationAngle(
  211. Uri imageUri, ContentResolver contentResolver) throws IOException {
  212. int angle = 0;
  213. Cursor cursor = contentResolver.query(imageUri,
  214. new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);
  215. if (cursor != null) {
  216. if (cursor.getCount() == 1) {
  217. cursor.moveToFirst();
  218. angle = cursor.getInt(0);
  219. }
  220. cursor.close();
  221. } else {
  222. ExifInterface exif = new ExifInterface(imageUri.getPath());
  223. int orientation = exif.getAttributeInt(
  224. ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
  225. switch (orientation) {
  226. case ExifInterface.ORIENTATION_ROTATE_270:
  227. angle = 270;
  228. break;
  229. case ExifInterface.ORIENTATION_ROTATE_180:
  230. angle = 180;
  231. break;
  232. case ExifInterface.ORIENTATION_ROTATE_90:
  233. angle = 90;
  234. break;
  235. default:
  236. break;
  237. }
  238. }
  239. return angle;
  240. }
  241. // Rotate the original bitmap according to the given orientation angle
  242. private static Bitmap rotateBitmap(Bitmap bitmap, int angle) {
  243. // If the rotate angle is 0, then return the original image, else return the rotated image
  244. if (angle != 0) {
  245. Matrix matrix = new Matrix();
  246. matrix.postRotate(angle);
  247. return Bitmap.createBitmap(
  248. bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
  249. } else {
  250. return bitmap;
  251. }
  252. }
  253. // Resize face rectangle, for better view for human
  254. // To make the rectangle larger, faceRectEnlargeRatio should be larger than 1, recommend 1.3
  255. private static FaceRectangle calculateFaceRectangle(
  256. Bitmap bitmap, FaceRectangle faceRectangle, double faceRectEnlargeRatio) {
  257. // Get the resized side length of the face rectangle
  258. double sideLength = faceRectangle.width * faceRectEnlargeRatio;
  259. sideLength = Math.min(sideLength, bitmap.getWidth());
  260. sideLength = Math.min(sideLength, bitmap.getHeight());
  261. // Make the left edge to left more.
  262. double left = faceRectangle.left
  263. - faceRectangle.width * (faceRectEnlargeRatio - 1.0) * 0.5;
  264. left = Math.max(left, 0.0);
  265. left = Math.min(left, bitmap.getWidth() - sideLength);
  266. // Make the top edge to top more.
  267. double top = faceRectangle.top
  268. - faceRectangle.height * (faceRectEnlargeRatio - 1.0) * 0.5;
  269. top = Math.max(top, 0.0);
  270. top = Math.min(top, bitmap.getHeight() - sideLength);
  271. // Shift the top edge to top more, for better view for human
  272. double shiftTop = faceRectEnlargeRatio - 1.0;
  273. shiftTop = Math.max(shiftTop, 0.0);
  274. shiftTop = Math.min(shiftTop, 1.0);
  275. top -= 0.15 * shiftTop * faceRectangle.height;
  276. top = Math.max(top, 0.0);
  277. // Set the result.
  278. FaceRectangle result = new FaceRectangle();
  279. result.left = (int)left;
  280. result.top = (int)top;
  281. result.width = (int)sideLength;
  282. result.height = (int)sideLength;
  283. return result;
  284. }
  285. }