public FaceViewModel(FaceLocation faceLocation, string imagePath) { _imagePath = imagePath; Title = imagePath; Application.Current.Dispatcher.Invoke(() => { BitmapImage src = new BitmapImage(); src.BeginInit(); src.UriSource = new Uri(imagePath, UriKind.Absolute); src.CacheOption = BitmapCacheOption.OnLoad; src.EndInit(); DrawingVisual dv = new DrawingVisual(); using (DrawingContext dc = dv.RenderOpen()) { dc.DrawImage(src, new Rect(0, 0, src.PixelWidth, src.PixelHeight)); dc.DrawRectangle(System.Windows.Media.Brushes.Green, null, new Rect(faceLocation.Left, faceLocation.Top, faceLocation.Right - faceLocation.Left, faceLocation.Bottom - faceLocation.Top)); } RenderTargetBitmap rtb = new RenderTargetBitmap(src.PixelWidth, src.PixelHeight, 96, 96, PixelFormats.Pbgra32); rtb.Render(dv); Image = rtb; }); }
int GetRectanglesIntersectionSquare(FaceLocation a, FaceLocation b) { int xIntersection = GetSegmentsIntersectionLength(a.Left, a.Right, b.Left, b.Right); int yIntersection = GetSegmentsIntersectionLength(a.Top, a.Bottom, b.Top, b.Bottom); return(xIntersection * yIntersection); }
FaceLocation CalculateNormalFaceLocation(Rectangle faceRect, int origWidth, int origHeight, double scaleFactor, double faceClippingRatio) { FaceLocation faceLocation = new FaceLocation(); double halhOfWidth = Math.Round((faceRect.Right - faceRect.Left) * scaleFactor * faceClippingRatio / 2); double halfOfHeight = Math.Round((faceRect.Bottom - faceRect.Top) * scaleFactor * faceClippingRatio / 2); double delta = 0; if (halhOfWidth > halfOfHeight) { delta = halhOfWidth; } else { delta = halfOfHeight; } faceLocation.Angle = 0; faceLocation.Left = (int)Math.Round(faceRect.Center.X * scaleFactor - delta); if (faceLocation.Left < 0) { faceLocation.Left = 0; } faceLocation.Right = (int)Math.Round(faceRect.Center.X * scaleFactor + delta); if (faceLocation.Right >= origWidth) { faceLocation.Right = origWidth - 1; } faceLocation.Top = (int)Math.Round(faceRect.Center.Y * scaleFactor - delta); if (faceLocation.Top < 0) { faceLocation.Top = 0; } faceLocation.Bottom = (int)Math.Round(faceRect.Center.Y * scaleFactor + delta); if (faceLocation.Bottom >= origHeight) { faceLocation.Bottom = origHeight - 1; } return(faceLocation); }
FaceLocation CalculateRotatedFaceLocation(double baseX, double baseY, Rectangle faceRect, double angle, int origWidth, int origHeight, double scaleFactor, double faceClippingRatio) { FaceLocation faceLocation = new FaceLocation(); double resultX = (faceRect.Center.X - baseX) * Math.Cos(angle * Math.PI / 180) - (faceRect.Center.Y - baseY) * Math.Sin(angle * Math.PI / 180) + baseX; double resultY = (faceRect.Center.X - baseX) * Math.Sin(angle * Math.PI / 180) + (faceRect.Center.Y - baseY) * Math.Cos(angle * Math.PI / 180) + baseY; double distanceToCenterX = (baseX - resultX) * scaleFactor; double distanceToCenterY = (baseY - resultY) * scaleFactor; double halhOfWidth = Math.Round((faceRect.Right - faceRect.Left) * scaleFactor * faceClippingRatio / 2); double halfOfHeight = Math.Round((faceRect.Bottom - faceRect.Top) * scaleFactor * faceClippingRatio / 2); double delta = 0; if (halhOfWidth > halfOfHeight) { delta = halhOfWidth; } else { delta = halfOfHeight; } faceLocation.Angle = angle; faceLocation.Left = (int)Math.Round((double)origWidth / 2 - distanceToCenterX - delta); if (faceLocation.Left < 0) { faceLocation.Left = 0; } faceLocation.Right = (int)Math.Round((double)origWidth / 2 - distanceToCenterX + delta); if (faceLocation.Right >= origWidth) { faceLocation.Right = origWidth - 1; } faceLocation.Top = (int)Math.Round((double)origHeight / 2 - distanceToCenterY - delta); if (faceLocation.Top < 0) { faceLocation.Top = 0; } faceLocation.Bottom = (int)Math.Round((double)origHeight / 2 - distanceToCenterY + delta); if (faceLocation.Bottom >= origHeight) { faceLocation.Bottom = origHeight - 1; } return(faceLocation); }
List <FaceLocation> GetCorrelatedFaceList(List <FaceLocation> faceLocationList, int additionalFacesCounter) { if (faceLocationList == null || faceLocationList.Count == 0) { return(null); } if (additionalFacesCounter > 0) // дополнительные лица, найденные в процессе дополнительного вращения. Есть смысл коррелировать { if (faceLocationList.Count == 1) { return(faceLocationList); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! надо обязательно повернуть на нужный угол !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! } else { List <List <FaceLocation> > preCorrelatedList = new List <List <FaceLocation> >(); List <FaceLocation> correlatedList = new List <FaceLocation>(); for (int i = 0; i < faceLocationList.Count; i++) { if (preCorrelatedList.Count == 0) { List <FaceLocation> list = new List <FaceLocation>(); list.Add(faceLocationList[i]); preCorrelatedList.Add(list); } else { bool isCorrelated = false; for (int listId = 0; listId < preCorrelatedList.Count; listId++) { int square = GetRectanglesIntersectionSquare(preCorrelatedList[listId][0], faceLocationList[i]); if (square > 0) // пересекаются { if (square > ((preCorrelatedList[listId][0].Right - preCorrelatedList[listId][0].Left) * (preCorrelatedList[listId][0].Bottom - preCorrelatedList[listId][0].Top)) * 0.4) { preCorrelatedList[listId].Add(faceLocationList[i]); isCorrelated = true; break; } } } if (!isCorrelated) { List <FaceLocation> list = new List <FaceLocation>(); list.Add(faceLocationList[i]); preCorrelatedList.Add(list); } } } if (preCorrelatedList.Count > 0) { for (int preList = 0; preList < preCorrelatedList.Count; preList++) { if (preCorrelatedList[preList] != null && preCorrelatedList[preList].Count > 0) { if (preCorrelatedList[preList].Count == 1) { correlatedList.Add(preCorrelatedList[preList][0]); } else { double angelValue = 0; int left = 0; int right = 0; int top = 0; int bottom = 0; for (int t = 0; t < preCorrelatedList[preList].Count; t++) { angelValue = angelValue + preCorrelatedList[preList][t].Angle; left = left + preCorrelatedList[preList][t].Left; right = right + preCorrelatedList[preList][t].Right; top = top + preCorrelatedList[preList][t].Top; bottom = bottom + preCorrelatedList[preList][t].Bottom; } FaceLocation avg = new FaceLocation() { Angle = angelValue / preCorrelatedList[preList].Count, Left = (int)((double)left / preCorrelatedList[preList].Count), Right = (int)((double)right / preCorrelatedList[preList].Count), Top = (int)((double)top / preCorrelatedList[preList].Count), Bottom = (int)((double)bottom / preCorrelatedList[preList].Count) }; correlatedList.Add(avg); } } } } return(correlatedList); } } else // нет смысла коррелировать лица - бесполезно. Все лица из фотографии без вращения { return(faceLocationList); } }
public SearchFaces(byte[] photoArray, int[] additionalRotateAngles = null, int photoJpegQuality = 95, double faceClippingRatio = 1.2) { if (photoArray != null) { try { using (Stream input = new MemoryStream(photoArray)) { photoArray = null; using (var inputStream = new SKManagedStream(input)) using (var codec = SKCodec.Create(inputStream)) using (var original = SKBitmap.Decode(codec)) { // ЧИТАЕМ EXIF-ИНФОРМАЦИЮ input.Position = 0; try { using (ExifReader reader = new ExifReader(input)) { reader.GetTagValue(ExifTags.DateTime, out photoDateTime); reader.GetTagValue(ExifTags.BodySerialNumber, out cameraSerialNumber); } } catch (Exception exc) { } // НОРМАЛИЗУЕМ ИЗОБРАЖЕНИE ПО ВРАЩЕНИЮ SKBitmap normalized = AdjustOrientation(original, codec.EncodedOrigin); double scaleFactor = 2; // ПОЛУЧАЕМ ДЕТЕКТИРУЕМОЕ НА ЛИЦА ИЗОБРАЖЕНИЕ using (var scanned = normalized.Resize(new SKImageInfo((int)Math.Round((double)normalized.Width / scaleFactor), (int)Math.Round((double)normalized.Height / scaleFactor)), SKFilterQuality.High)) { if (scanned == null) { return; } int additionalFacesCounter = 0; List <FaceLocation> faceLocationList = new List <FaceLocation>(); using (var fd = Dlib.GetFrontalFaceDetector()) { DlibDotNet.Rectangle[] faces = null; using (var array2D = Dlib.LoadImageData <RgbPixel>(ImagePixelFormat.Rgba, scanned.Bytes, (uint)scanned.Height, (uint)scanned.Width, (uint)(scanned.Bytes.Length / scanned.Height))) faces = fd.Operator(array2D); if (faces != null && faces.Length > 0) { for (int f = 0; f < faces.Length; f++) { #region обрезаем лицо до квадрата Point center = faces[f].Center; int radius = 0; if (faces[f].Width < faces[f].Height) { radius = (int)faces[f].Width / 2; } else { radius = (int)faces[f].Height / 2; } faces[f].Left = center.X - radius; faces[f].Right = center.X + radius; faces[f].Top = center.Y - radius; faces[f].Bottom = center.Y + radius; #endregion обрезаем лицо до квадрата FaceLocation faceLocation = CalculateNormalFaceLocation(faces[f], normalized.Width, normalized.Height, scaleFactor, faceClippingRatio); faceLocationList.Add(faceLocation); } } if (additionalRotateAngles != null && additionalRotateAngles.Length > 0) { for (int r = 0; r < additionalRotateAngles.Length; r++) { if (additionalRotateAngles[r] != 0) { DlibDotNet.Rectangle[] addFaces = null; SKBitmap rotatedScanned = Rotate(scanned, additionalRotateAngles[r]); using (var array2D = Dlib.LoadImageData <RgbPixel>(ImagePixelFormat.Rgba, rotatedScanned.Bytes, (uint)rotatedScanned.Height, (uint)rotatedScanned.Width, (uint)(rotatedScanned.Bytes.Length / rotatedScanned.Height))) addFaces = fd.Operator(array2D); if (addFaces != null && addFaces.Length > 0) { for (int i = 0; i < addFaces.Length; i++) { #region обрезаем лицо до квадрата Point center = addFaces[i].Center; int radius = 0; if (addFaces[i].Width < addFaces[i].Height) { radius = (int)addFaces[i].Width / 2; } else { radius = (int)addFaces[i].Height / 2; } addFaces[i].Left = center.X - radius; addFaces[i].Right = center.X + radius; addFaces[i].Top = center.Y - radius; addFaces[i].Bottom = center.Y + radius; #endregion обрезаем лицо до квадрата FaceLocation faceLocation = CalculateRotatedFaceLocation((double)rotatedScanned.Width / 2, (double)rotatedScanned.Height / 2, addFaces[i], -additionalRotateAngles[r], normalized.Width, normalized.Height, scaleFactor, faceClippingRatio); additionalFacesCounter++; faceLocationList.Add(faceLocation); } } } } } } if (faceLocationList.Count > 0) { List <FaceLocation> correlatedFaceList = GetCorrelatedFaceList(faceLocationList, additionalFacesCounter); // пропускаем через коррелятор лиц для избавления от дублей и уменьшения бокового наклона if (correlatedFaceList != null && correlatedFaceList.Count > 0) { for (int i = 0; i < correlatedFaceList.Count; i++) { //var cropRect = new SKRectI { Left = correlatedFaceList[i].Left, Top = correlatedFaceList[i].Top, Right = correlatedFaceList[i].Right, Bottom = correlatedFaceList[i].Bottom }; var cropRect = new SKRectI(); int w = correlatedFaceList[i].Right - correlatedFaceList[i].Left; int h = correlatedFaceList[i].Bottom - correlatedFaceList[i].Top; int centerX = correlatedFaceList[i].Left + w / 2; int centerY = correlatedFaceList[i].Top + h / 2; if (w > h) { cropRect.Left = centerX - h / 2; cropRect.Right = centerX + h / 2; cropRect.Top = centerY - h / 2; cropRect.Bottom = centerY + h / 2; } else if (w < h) { cropRect.Left = centerX - w / 2; cropRect.Right = centerX + w / 2; cropRect.Top = centerY - w / 2; cropRect.Bottom = centerY + w / 2; } else { cropRect.Left = correlatedFaceList[i].Left; cropRect.Top = correlatedFaceList[i].Top; cropRect.Right = correlatedFaceList[i].Right; cropRect.Bottom = correlatedFaceList[i].Bottom; } var faceBitmap = new SKBitmap(cropRect.Width, cropRect.Height); normalized.ExtractSubset(faceBitmap, cropRect); //// ТЕПЕРЬ БУДЕМ ПОВОРАЧИВАТЬ SKBitmap rotated = Rotate(faceBitmap, -correlatedFaceList[i].Angle); // ТЕПЕРЬ НАКЛАДЫВАЕМ МАСКУ НА ЛИЦО В ВИДЕ КРУГА double radius = 0; if (cropRect.Width < cropRect.Height) { radius = (double)cropRect.Width / 2 * (1 + 0.5 / 2); } else { radius = (double)cropRect.Height / 2 * (1 + 0.5 / 2); } using (SKCanvas canvas = new SKCanvas(rotated)) { canvas.DrawBitmap(rotated, 0, 0); SKPaint paint = new SKPaint(); paint.Color = maskColor; paint.Style = SKPaintStyle.Stroke; paint.StrokeWidth = (float)(radius * 0.4); canvas.DrawCircle((float)rotated.Width / 2, (float)rotated.Height / 2, (float)radius, paint); canvas.Flush(); } // ВЫРЕЗАЕМ ИТОГ double x = (double)rotated.Width / 2; double y = (double)rotated.Height / 2; var finalCropRect = new SKRectI { Left = (int)(x - (double)faceBitmap.Width / 2), Top = (int)(y - (double)faceBitmap.Height / 2), Right = (int)(x + (double)faceBitmap.Width / 2), Bottom = (int)(y + (double)faceBitmap.Height / 2) }; faceBitmap.Dispose(); using (SKBitmap face = new SKBitmap(finalCropRect.Width, finalCropRect.Height)) { rotated.ExtractSubset(face, finalCropRect); try { if (face.Width > 600 * scaleFactor) { using (var scaled = face.Resize(new SKImageInfo((int)Math.Round(400 * scaleFactor), (int)Math.Round(400 * scaleFactor)), SKFilterQuality.High)) { if (scaled != null) { using (var image = SKImage.FromBitmap(scaled)) using (var data = image.Encode(SKEncodedImageFormat.Jpeg, 90)) { if (facesByteArrays == null) { facesByteArrays = new List <byte[]>(); } facesByteArrays.Add(data.ToArray()); } } } } else { using (var image = SKImage.FromBitmap(face)) using (var data = image.Encode(SKEncodedImageFormat.Jpeg, 90)) { if (facesByteArrays == null) { facesByteArrays = new List <byte[]>(); } facesByteArrays.Add(data.ToArray()); } } } catch (Exception exc) { }; } } normalized.Dispose(); correlatedFaceList = null; } faceLocationList = null; } } } isSuccess = true; } } catch (Exception exc) { try { isSuccess = false; if (exc.StackTrace != null && exc.StackTrace != "") { log.Debug(String.Format("SearchFaces. Exception: {0}", exc.StackTrace)); } else if (exc.Message != null && exc.Message != "") { log.Debug(String.Format("SearchFaces. Exception: {0}", exc.Message)); } } catch (Exception ex) { } } } else { log.Debug("SearchFaces. Null request."); } }