using Microsoft.ML.OnnxRuntime.Tensors; using SixLabors.ImageSharp; using ZeroLevel.NN.Models; //https://github.com/iwatake2222/play_with_tflite/blob/master/pj_tflite_face_dbface/image_processor/face_detection_engine.cpp namespace ZeroLevel.NN { public sealed class DBFace : SSDNN , IFaceDetector { private const int STRIDE = 4; private const int INPUT_WIDTH = 1216; private const int INPUT_HEIGHT = 960; private const float THESHOLD = 0.4f; private const float IOU_THESHOLD = 0.6f; private static float[] MEAN = new[] { 0.408f, 0.447f, 0.47f }; private static float[] STD = new[] { 0.289f, 0.274f, 0.278f }; public DBFace(string model_path) :base(model_path) { } private static float exp(float v) { var gate = 1.0f; var _base = Math.Exp(gate); if (Math.Abs(v) < gate) return (float)(v * _base); if (v > 0) { return (float)Math.Exp(v); } return (float)-Math.Exp(-v); } private static FacePoint Landmark(float cx, float cy, float x, float y, float scale_w, float scale_h) { var p = new FacePoint(); p.X = (exp(x * 4) + cx) * STRIDE * scale_w; p.Y = (exp(y * 4) + cy) * STRIDE * scale_h; return p; } private List Parse(Tensor hm, Tensor boxes, Tensor landmarks, int width, int height) { float x, y, r, b; float scale_w = width / (float)(INPUT_WIDTH); float scale_h = height / (float)(INPUT_HEIGHT); List bbox_list = new List(); for (int cx = 0; cx < hm.Dimensions[3]; cx++) { for (int cy = 0; cy < hm.Dimensions[2]; cy++) { float score = hm[0, 0, cy, cx]; if (score >= THESHOLD) { x = boxes[0, 0, cy, cx]; y = boxes[0, 1, cy, cx]; r = boxes[0, 2, cy, cx]; b = boxes[0, 3, cy, cx]; x = (cx - x) * STRIDE; y = (cy - y) * STRIDE; r = (cx + r) * STRIDE; b = (cy + b) * STRIDE; var bbox = new Face(); bbox.X1 = (int)(x * scale_w); bbox.Y1 = (int)(y * scale_h); bbox.X2 = (int)(r * scale_w); bbox.Y2 = (int)(b * scale_h); bbox.Score = score; bbox.Landmarks.LeftEye = Landmark(cx, cy, landmarks[0, 0, cy, cx], landmarks[0, 5, cy, cx], scale_w, scale_h); bbox.Landmarks.RightEye = Landmark(cx, cy, landmarks[0, 1, cy, cx], landmarks[0, 6, cy, cx], scale_w, scale_h); bbox.Landmarks.Nose = Landmark(cx, cy, landmarks[0, 2, cy, cx], landmarks[0, 7, cy, cx], scale_w, scale_h); bbox.Landmarks.LeftMouth = Landmark(cx, cy, landmarks[0, 3, cy, cx], landmarks[0, 8, cy, cx], scale_w, scale_h); bbox.Landmarks.RightMouth = Landmark(cx, cy, landmarks[0, 4, cy, cx], landmarks[0, 9, cy, cx], scale_w, scale_h); bbox_list.Add(bbox); } } } return bbox_list; } public IList Predict(Image image) { var input = MakeInput(image, new ImagePreprocessorOptions(INPUT_WIDTH, INPUT_HEIGHT, PredictorChannelType.ChannelFirst) .ApplyNormilization() .ApplyCorrection(MEAN, STD) .ApplyAxeInversion()); List result = null; Extract(new Dictionary> { { "input", input } }, output => { var hm = output["hm"]; var boxes = output["boxes"]; var landmark = output["landmarks"]; result = Parse(hm, boxes, landmark, image.Width, image.Height); }); var cleaned_result = Face.Nms(result, IOU_THESHOLD, false); return cleaned_result; } } }