You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Zero/ZeroLevel.NN/Architectures/FaceSeacrhService.cs

167 lines
6.7 KiB

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using ZeroLevel.NN;
using ZeroLevel.NN.Models;
namespace Zero.NN.Services
{
public class FaceSeacrhService
{
private readonly IFaceDetector _detector;
private readonly IEncoder _encoder;
private readonly bool _useFaceAlign;
public FaceSeacrhService(IFaceDetector detector, IEncoder encoder, bool useFaceAlign = true)
{
_useFaceAlign = useFaceAlign;
_detector = detector;
_encoder = encoder;
}
public static Image MakeEyesHorizontal(Image source, Face face)
{
// положение глаз для определения угла поворота
var leftEye = face.Landmarks.LeftEye;
var rightEye = face.Landmarks.RightEye;
var dY = rightEye.Y - leftEye.Y;
var dX = rightEye.X - leftEye.X;
// угол на который нужно повернуть изображение чтбы выравнять глаза
var ra = (float)Math.Atan2(dY, dX);
// определить размеры и центр лица
var minX = face.Landmarks.Left();
var minY = face.Landmarks.Top();
var maxX = face.Landmarks.Right();
var maxY = face.Landmarks.Bottom();
var centerFaceX = (maxX + minX) / 2.0f;
var centerFaceY = (maxY + minY) / 2.0f;
// определить описывающий лицо прямоугольник с центром в centerFaceX;centerFaceY
var distanceX = face.X2 - face.X1;
var distanceY = face.Y2 - face.Y1;
var dx = (face.X1 + distanceX / 2.0f) - centerFaceX;
var dy = (face.Y1 + distanceY / 2.0f) - centerFaceY;
var x1 = face.X1 - dx;
var y1 = face.Y1 - dy;
var x2 = face.X2 - dx;
var y2 = face.Y2 - dy;
// определить квадрат описывающий прямоугольник с лицом повернутый на 45 градусов
var radius = (float)Math.Sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) / 2.0f;
x1 = centerFaceX - radius;
x2 = centerFaceX + radius;
y1 = centerFaceY - radius;
y2 = centerFaceY + radius;
var cropDx = radius - distanceX / 2.0f;
var cropDy = radius - distanceY / 2.0f;
using (var fullCrop = ImagePreprocessor.Crop(source, x1, y1, x2, y2))
{
fullCrop.Mutate(img => img.Rotate((float)(-ra * (180.0f / Math.PI)), KnownResamplers.Bicubic));
var crop = ImagePreprocessor.Crop(fullCrop, cropDx, cropDy, fullCrop.Width - cropDx, fullCrop.Height - cropDy);
crop.Mutate(img => img.Resize(112, 112, KnownResamplers.Bicubic));
return crop;
}
}
private Image SpecialCrop(Image image, Face face)
{
var left = face.Landmarks.Left(); // 0.3
var right = face.Landmarks.Right(); // 0.7
var top = face.Landmarks.Top(); // 0.4
var bottom = face.Landmarks.Bottom(); // 0.8
var newWidth = (right - left) / 0.4f;
var newHeight = (bottom - top) / 0.4f;
// привести к квадрату !
var cx1 = left - (newWidth * 0.3f);
var cy1 = top - (newHeight * 0.4f);
var cx2 = cx1 + newWidth;
var cy2 = cy1 + newHeight;
var clipX = new Func<float, float>(x =>
{
if (x < 0) return 0;
if (x > image.Width) return image.Width;
return x;
});
var clipY = new Func<float, float>(y =>
{
if (y < 0) return 0;
if (y > image.Height) return image.Height;
return y;
});
cx1 = clipX(cx1);
cx2 = clipX(cx2);
cy1 = clipY(cy1);
cy2 = clipY(cy2);
return ImagePreprocessor.Crop(image, cx1, cy1, cx2, cy2);
}
public IEnumerable<FaceEmbedding> GetEmbeddings(Image<Rgb24> image)
{
int width = image.Width;
int heigth = image.Height;
var faces = _detector.Predict(image);
foreach (var face in faces)
{
Face.FixInScreen(face, width, heigth);
float[] vector;
if (_useFaceAlign)
{
int x = (int)face.X1;
int y = (int)face.Y1;
int w = (int)(face.X2 - face.X1);
int h = (int)(face.Y2 - face.Y1);
var radius = (float)Math.Sqrt(w * w + h * h) / 2f;
var centerFaceX = (face.X2 + face.X1) / 2.0f;
var centerFaceY = (face.Y2 + face.Y1) / 2.0f;
var around_x1 = centerFaceX - radius;
var around_x2 = centerFaceX + radius;
var around_y1 = centerFaceY - radius;
var around_y2 = centerFaceY + radius;
using (var faceImage = ImagePreprocessor.Crop(image, around_x1, around_y1, around_x2, around_y2))
{
var matrix = Face.GetTransformMatrix(face);
var builder = new AffineTransformBuilder();
builder.AppendMatrix(matrix);
faceImage.Mutate(x => x.Transform(builder, KnownResamplers.Bicubic));
vector = _encoder.Predict(faceImage);
/*var aligned_faces = detector.Predict(faceImage);
if (aligned_faces != null && aligned_faces.Count == 1)
{
using (var ci = SpecialCrop(faceImage, aligned_faces[0]))
{
vector = encoder.Predict(faceImage);
}
}
else
{
vector = encoder.Predict(faceImage);
}*/
}
}
else
{
using (var faceImage = ImagePreprocessor.Crop(image, face.X1, face.Y1, face.X2, face.Y2))
{
vector = _encoder.Predict(faceImage);
}
}
yield return new FaceEmbedding
{
Face = face,
Vector = vector
};
}
}
}
}

Powered by TurnKey Linux.