package com.rubenvandeven.emotionhero; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.PointF; import android.graphics.RectF; import android.util.Base64; import android.util.Log; import com.affectiva.android.affdex.sdk.detector.Face; import com.google.gson.annotations.Expose; import java.io.ByteArrayOutputStream; import java.util.Arrays; import java.util.HashMap; import java.util.Map; /** * Created by ruben on 02/09/16. */ public class Hit { public long id; public Scenario.Target target; @Expose(serialize = false, deserialize = false) public Game game; public float score; public float bonus; public Map emotions = new HashMap<>(7); public boolean glasses; public String ethnicity; public String age; public String gender; public Map expressions = new HashMap<>(21); // also includes measurements public PointF[] points = new PointF[34]; public String remoteId; public Bitmap frame; /** * Constructor for game moment */ public Hit(Scenario.Target target, Game game, Face face) { this.target = target; this.game = game; for(Emotion emotion: Emotion.values()) { emotions.put(emotion,emotion.getValueFromFace(face)); } this.gender = face.appearance.getGender() == Face.GENDER.UNKNOWN ? "U" : (face.appearance.getGender() == Face.GENDER.MALE ? "M" : "F"); this.glasses = face.appearance.getGlasses() == Face.GLASSES.YES ? true : false; this.ethnicity = face.appearance.getEthnicity().name(); this.age = face.appearance.getAge().name(); for(Expression exp: Expression.values()) { this.expressions.put(exp, exp.getValueFromFace(face)); } this.points = face.getFacePoints().clone(); // this.frame = frame; this.score = calculateScore(); this.bonus = calculateBonus(); // this in a way is strange behaviour: // preferably do the game.addHit after creating hit, and call hit.game = game from there. game.addHit(this); } /** * Constructor from database entity */ public Hit(Game game, Cursor c) { this.id = c.getLong(c.getColumnIndex("id")); this.score = c.getFloat(c.getColumnIndex("score")); this.bonus = c.getFloat(c.getColumnIndex("bonus")); String rId = c.getString(c.getColumnIndex("remoteId")); this.remoteId = (rId == null || rId.length() == 0) ? null : c.getString(c.getColumnIndex("remoteId")); this.target = game.scenario.getTargetByIndex(c.getInt(c.getColumnIndex("target_index"))); this.glasses = c.getInt(c.getColumnIndex("glasses")) == 1; this.ethnicity = c.getString(c.getColumnIndex("ethnicity")); this.age = c.getString(c.getColumnIndex("age")); this.gender = c.getString(c.getColumnIndex("gender")); this.emotions.put(Emotion.ANGER, c.getFloat(c.getColumnIndex("anger"))); this.emotions.put(Emotion.CONTEMPT, c.getFloat(c.getColumnIndex("contempt"))); this.emotions.put(Emotion.DISGUST, c.getFloat(c.getColumnIndex("disgust"))); this.emotions.put(Emotion.FEAR, c.getFloat(c.getColumnIndex("fear"))); this.emotions.put(Emotion.JOY, c.getFloat(c.getColumnIndex("joy"))); this.emotions.put(Emotion.SADNESS, c.getFloat(c.getColumnIndex("sadness"))); this.emotions.put(Emotion.SURPRISE, c.getFloat(c.getColumnIndex("surprise"))); for(Expression exp: Expression.values()) { this.expressions.put(exp, c.getFloat( c.getColumnIndex( exp.getDbName() ))); } this.points[0] = new PointF(c.getFloat(c.getColumnIndex("point_0x")), c.getFloat(c.getColumnIndex("point_0y"))); this.points[1] = new PointF(c.getFloat(c.getColumnIndex("point_1x")), c.getFloat(c.getColumnIndex("point_1y"))); this.points[2] = new PointF(c.getFloat(c.getColumnIndex("point_2x")), c.getFloat(c.getColumnIndex("point_2y"))); this.points[3] = new PointF(c.getFloat(c.getColumnIndex("point_3x")), c.getFloat(c.getColumnIndex("point_3y"))); this.points[4] = new PointF(c.getFloat(c.getColumnIndex("point_4x")), c.getFloat(c.getColumnIndex("point_4y"))); this.points[5] = new PointF(c.getFloat(c.getColumnIndex("point_5x")), c.getFloat(c.getColumnIndex("point_5y"))); this.points[6] = new PointF(c.getFloat(c.getColumnIndex("point_6x")), c.getFloat(c.getColumnIndex("point_6y"))); this.points[7] = new PointF(c.getFloat(c.getColumnIndex("point_7x")), c.getFloat(c.getColumnIndex("point_7y"))); this.points[8] = new PointF(c.getFloat(c.getColumnIndex("point_8x")), c.getFloat(c.getColumnIndex("point_8y"))); this.points[9] = new PointF(c.getFloat(c.getColumnIndex("point_9x")), c.getFloat(c.getColumnIndex("point_9y"))); this.points[10] = new PointF(c.getFloat(c.getColumnIndex("point_10x")), c.getFloat(c.getColumnIndex("point_10y"))); this.points[11] = new PointF(c.getFloat(c.getColumnIndex("point_11x")), c.getFloat(c.getColumnIndex("point_11y"))); this.points[12] = new PointF(c.getFloat(c.getColumnIndex("point_12x")), c.getFloat(c.getColumnIndex("point_12y"))); this.points[13] = new PointF(c.getFloat(c.getColumnIndex("point_13x")), c.getFloat(c.getColumnIndex("point_13y"))); this.points[14] = new PointF(c.getFloat(c.getColumnIndex("point_14x")), c.getFloat(c.getColumnIndex("point_14y"))); this.points[15] = new PointF(c.getFloat(c.getColumnIndex("point_15x")), c.getFloat(c.getColumnIndex("point_15y"))); this.points[16] = new PointF(c.getFloat(c.getColumnIndex("point_16x")), c.getFloat(c.getColumnIndex("point_16y"))); this.points[17] = new PointF(c.getFloat(c.getColumnIndex("point_17x")), c.getFloat(c.getColumnIndex("point_17y"))); this.points[18] = new PointF(c.getFloat(c.getColumnIndex("point_18x")), c.getFloat(c.getColumnIndex("point_18y"))); this.points[19] = new PointF(c.getFloat(c.getColumnIndex("point_19x")), c.getFloat(c.getColumnIndex("point_19y"))); this.points[20] = new PointF(c.getFloat(c.getColumnIndex("point_20x")), c.getFloat(c.getColumnIndex("point_20y"))); this.points[21] = new PointF(c.getFloat(c.getColumnIndex("point_21x")), c.getFloat(c.getColumnIndex("point_21y"))); this.points[22] = new PointF(c.getFloat(c.getColumnIndex("point_22x")), c.getFloat(c.getColumnIndex("point_22y"))); this.points[23] = new PointF(c.getFloat(c.getColumnIndex("point_23x")), c.getFloat(c.getColumnIndex("point_23y"))); this.points[24] = new PointF(c.getFloat(c.getColumnIndex("point_24x")), c.getFloat(c.getColumnIndex("point_24y"))); this.points[25] = new PointF(c.getFloat(c.getColumnIndex("point_25x")), c.getFloat(c.getColumnIndex("point_25y"))); this.points[26] = new PointF(c.getFloat(c.getColumnIndex("point_26x")), c.getFloat(c.getColumnIndex("point_26y"))); this.points[27] = new PointF(c.getFloat(c.getColumnIndex("point_27x")), c.getFloat(c.getColumnIndex("point_27y"))); this.points[28] = new PointF(c.getFloat(c.getColumnIndex("point_28x")), c.getFloat(c.getColumnIndex("point_28y"))); this.points[29] = new PointF(c.getFloat(c.getColumnIndex("point_29x")), c.getFloat(c.getColumnIndex("point_29y"))); this.points[30] = new PointF(c.getFloat(c.getColumnIndex("point_30x")), c.getFloat(c.getColumnIndex("point_30y"))); this.points[31] = new PointF(c.getFloat(c.getColumnIndex("point_31x")), c.getFloat(c.getColumnIndex("point_31y"))); this.points[32] = new PointF(c.getFloat(c.getColumnIndex("point_32x")), c.getFloat(c.getColumnIndex("point_32y"))); this.points[33] = new PointF(c.getFloat(c.getColumnIndex("point_33x")), c.getFloat(c.getColumnIndex("point_33y"))); // skip for now, unused and bad for performance ;-) // byte[] image = c.getBlob(c.getColumnIndex("image")); // if(image != null) // { // frame = BitmapFactory.decodeByteArray(image, 0, image.length); // } game.addHit(this); } public static RectF getBoundingboxForPoints(PointF[] points) { // a bit of a hack to be as quick as possible // we know which point is where on the face, however, the face might be tilted: float[] minXes = {points[0].x,points[1].x,points[5].x}; float[] maxXes = {points[4].x,points[3].x,points[10].x}; float[] maxYs = {points[1].y,points[2].y,points[3].y}; float[] minYs = {points[5].y,points[6].y,points[7].y,points[8].y,points[9].y,points[10].y}; Arrays.sort(minXes); Arrays.sort(maxXes); Arrays.sort(maxYs); Arrays.sort(minYs); return new RectF(minXes[0], minYs[0], maxXes[maxXes.length-1], maxYs[maxYs.length-1]); } // to store in db public static String bitmapToBase64(Bitmap bitmap) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); bitmap.compress(Bitmap.CompressFormat.JPEG, 90, byteArrayOutputStream); byte[] byteArray = byteArrayOutputStream .toByteArray(); return Base64.encodeToString(byteArray, Base64.DEFAULT); } // to retreive from db public static Bitmap base64ToBitmap(String b64) { byte[] imageAsBytes = Base64.decode(b64.getBytes(), Base64.DEFAULT); return BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.length); } public byte[] getFrameAsByteArray() { if(frame == null) return null; ByteArrayOutputStream stream = new ByteArrayOutputStream(); frame.compress(Bitmap.CompressFormat.JPEG, 90, stream); return stream.toByteArray(); } public float calculateScore() { float scored_value = emotions.get(target.emotion); float required_value = target.value; float diff = Math.abs(scored_value-required_value); return 100 - diff; } public float calculateBonus() { float scored_value = emotions.get(target.emotion); float required_value = target.value; float diff = Math.abs(scored_value-required_value); float bonus = 0; // precision score if(diff < 25) { bonus = ((25-diff)/25) * 50; } // double score if its exact!! if(diff < 0.001) { bonus += 50; } Log.e("HIT", "bonus: " + bonus + " (diff "+diff+")"); return bonus; } public boolean hasPrecisionBonus() { return (this.bonus > 0); } }