Skip to content

Commit 9765e23

Browse files
feet: add FaceRecognition.CropFaces and Image.Save method
1 parent 146121a commit 9765e23

File tree

9 files changed

+266
-3
lines changed

9 files changed

+266
-3
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ This package supports cross platform, Windows, Linux and MacOSX!!
3939
|face_landmarks|FaceLandmarks|And support **Helen dataset** :warning:|
4040
|face_locations|FaceLocations||
4141
|load_image_file|LoadImageFile||
42+
|-|CropFaces|Crop image with specified locations|
4243
|-|LoadImage|From memory data|
4344
|-|PredictAge|Use **Adience Benchmark Of Unfiltered Faces For Gender And Age Classification dataset** :warning:|
4445
|-|PredictGender|Use **UTKFace dataset** :warning:|

src/FaceRecognitionDotNet/FaceRecognition.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,53 @@ public static FaceRecognition Create(string directory)
216216
return new FaceRecognition(directory);
217217
}
218218

219+
/// <summary>
220+
/// Crop a specified image with enumerable collection of face locations.
221+
/// </summary>
222+
/// <param name="image">The image contains a face.</param>
223+
/// <param name="locations">The enumerable collection of location rectangle for faces.</param>
224+
/// <returns></returns>
225+
/// <exception cref="ArgumentNullException"><paramref name="image"/> or <paramref name="locations"/> is null.</exception>
226+
/// <exception cref="ObjectDisposedException"><paramref name="image"/> is disposed.</exception>
227+
public static IEnumerable<Image> CropFaces(Image image, IEnumerable<Location> locations)
228+
{
229+
if (image == null)
230+
throw new ArgumentNullException(nameof(image));
231+
if (locations == null)
232+
throw new ArgumentNullException(nameof(locations));
233+
234+
image.ThrowIfDisposed();
235+
236+
foreach (var location in locations)
237+
{
238+
var rect = new Rectangle(location.Left, location.Top, location.Right, location.Bottom);
239+
var dPoint = new[]
240+
{
241+
new DPoint(rect.Left, rect.Top),
242+
new DPoint(rect.Right, rect.Top),
243+
new DPoint(rect.Left, rect.Bottom),
244+
new DPoint(rect.Right, rect.Bottom),
245+
};
246+
247+
var width = (int)rect.Width;
248+
var height = (int)rect.Height;
249+
250+
switch (image.Mode)
251+
{
252+
case Mode.Rgb:
253+
var rgb = image.Matrix as Matrix<RgbPixel>;
254+
yield return new Image(DlibDotNet.Dlib.ExtractImage4Points(rgb, dPoint, width, height),
255+
Mode.Rgb);
256+
break;
257+
case Mode.Greyscale:
258+
var gray = image.Matrix as Matrix<byte>;
259+
yield return new Image(DlibDotNet.Dlib.ExtractImage4Points(gray, dPoint, width, height),
260+
Mode.Rgb);
261+
break;
262+
}
263+
}
264+
}
265+
219266
/// <summary>
220267
/// Compare a face encoding to a known face encoding and get a euclidean distance for comparison face.
221268
/// </summary>

src/FaceRecognitionDotNet/Image.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.IO;
23
using DlibDotNet;
34

45
namespace FaceRecognitionDotNet
@@ -62,6 +63,35 @@ public int Width
6263

6364
#region Methods
6465

66+
/// <summary>
67+
/// Saves this <see cref="Image"/> to the specified file.
68+
/// </summary>
69+
/// <param name="filename">A string that contains the name of the file to which to save this <see cref="Image"/>.</param>
70+
/// <param name="format">The <see cref="ImageFormat"/> for this <see cref="Image"/>.</param>
71+
/// <exception cref="ArgumentNullException"><paramref name="filename"/> is null.</exception>
72+
public void Save(string filename, ImageFormat format)
73+
{
74+
if (filename == null)
75+
throw new ArgumentNullException(nameof(filename));
76+
77+
var directory = Path.GetDirectoryName(filename);
78+
if (!Directory.Exists(directory) && !string.IsNullOrWhiteSpace(directory))
79+
Directory.CreateDirectory(directory);
80+
81+
switch (format)
82+
{
83+
case ImageFormat.Bmp:
84+
DlibDotNet.Dlib.SaveBmp(this._Matrix, filename);
85+
break;
86+
case ImageFormat.Jpeg:
87+
DlibDotNet.Dlib.SaveJpeg(this._Matrix, filename);
88+
break;
89+
case ImageFormat.Png:
90+
DlibDotNet.Dlib.SavePng(this._Matrix, filename);
91+
break;
92+
}
93+
}
94+
6595
#region Overrides
6696

6797
/// <summary>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
namespace FaceRecognitionDotNet
2+
{
3+
4+
/// <summary>
5+
/// Specifies the file format of the image.
6+
/// </summary>
7+
public enum ImageFormat
8+
{
9+
10+
/// <summary>
11+
/// Specifies that the bitmap (BMP) image format.
12+
/// </summary>
13+
Bmp,
14+
15+
/// <summary>
16+
/// Specifies that the Joint Photographic Experts Group (JPEG) image format.
17+
/// </summary>
18+
Jpeg,
19+
20+
/// <summary>
21+
/// Specifies that the W3C Portable Network Graphics (PNG) image format.
22+
/// </summary>
23+
Png,
24+
25+
}
26+
27+
}

src/FaceRecognitionDotNet/docs/FaceRecognitionDotNet.xml

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/FaceRecognitionDotNet/docs/ja/FaceRecognitionDotNet.xml

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/FaceRecognitionDotNet.Tests/FaceRecognitionTest.cs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,44 @@ public void CompareFacesTrue()
229229
Assert.True(false, "Assert check did not execute");
230230
}
231231

232+
[Fact]
233+
public void CropFaces()
234+
{
235+
const string testName = nameof(this.CropFaces);
236+
237+
var path = Path.Combine(ImageDirectory, TwoPersonFile);
238+
if (!File.Exists(path))
239+
{
240+
var binary = new HttpClient().GetByteArrayAsync($"{TwoPersonUrl}/{TwoPersonFile}").Result;
241+
242+
Directory.CreateDirectory(ImageDirectory);
243+
File.WriteAllBytes(path, binary);
244+
}
245+
246+
foreach (var mode in new[] { Mode.Rgb, Mode.Greyscale })
247+
{
248+
using (var image = FaceRecognition.LoadImageFile(path, mode))
249+
{
250+
var locations = this._FaceRecognition.FaceLocations(image).ToArray();
251+
Assert.True(locations.Length == 2, $"{mode}");
252+
253+
var images = FaceRecognition.CropFaces(image, locations).ToArray();
254+
for (var index = 1; index <= images.Length; index++)
255+
{
256+
var croppedImage = images[index - 1];
257+
258+
var directory = Path.Combine(ResultDirectory, testName);
259+
Directory.CreateDirectory(directory);
260+
261+
var dst = Path.Combine(directory, $"{mode}-{index}.jpg");
262+
croppedImage.Save(dst, ImageFormat.Jpeg);
263+
264+
croppedImage.Dispose();
265+
}
266+
}
267+
}
268+
}
269+
232270
[Fact]
233271
public void CreateFail1()
234272
{
@@ -1734,7 +1772,7 @@ private void FaceLandmark(string testName, PredictorModel model, bool useKnownLo
17341772
Directory.CreateDirectory(directory);
17351773

17361774
var dst = Path.Combine(directory, $"{facePart}-{mode}-known_{useKnownLocation}.bmp");
1737-
bitmap.Save(dst, ImageFormat.Bmp);
1775+
bitmap.Save(dst, System.Drawing.Imaging.ImageFormat.Bmp);
17381776
}
17391777
}
17401778

@@ -1752,7 +1790,7 @@ private void FaceLandmark(string testName, PredictorModel model, bool useKnownLo
17521790
Directory.CreateDirectory(directory);
17531791

17541792
var dst = Path.Combine(directory, $"All-{mode}-known_{useKnownLocation}.bmp");
1755-
bitmap.Save(dst, ImageFormat.Bmp);
1793+
bitmap.Save(dst, System.Drawing.Imaging.ImageFormat.Bmp);
17561794
}
17571795
}
17581796
}
@@ -1786,7 +1824,7 @@ private void FaceLocation(string testName, int numberOfTimesToUpsample, Model mo
17861824
Directory.CreateDirectory(directory);
17871825

17881826
var dst = Path.Combine(directory, $"All-{mode}-{numberOfTimesToUpsample}.bmp");
1789-
bitmap.Save(dst, ImageFormat.Bmp);
1827+
bitmap.Save(dst, System.Drawing.Imaging.ImageFormat.Bmp);
17901828
}
17911829
}
17921830
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System.IO;
2+
using Xunit;
3+
4+
namespace FaceRecognitionDotNet.Tests
5+
{
6+
7+
public class ImageTest
8+
{
9+
10+
#region Fields
11+
12+
private const string ResultDirectory = "Result";
13+
14+
#endregion
15+
16+
[Fact]
17+
public void Save()
18+
{
19+
const string testName = nameof(this.Save);
20+
21+
var targets = new[]
22+
{
23+
new { Name = "saved.bmp", Format = ImageFormat.Bmp },
24+
new { Name = "saved.jpg", Format = ImageFormat.Jpeg },
25+
new { Name = "saved.png", Format = ImageFormat.Png },
26+
};
27+
28+
using (var img = FaceRecognition.LoadImageFile(Path.Combine("TestImages", "obama.jpg")))
29+
{
30+
var directory = Path.Combine(ResultDirectory, testName);
31+
Directory.CreateDirectory(directory);
32+
33+
foreach (var target in targets)
34+
{
35+
var path = Path.Combine(directory, target.Name);
36+
img.Save(path, target.Format);
37+
}
38+
}
39+
}
40+
41+
}
42+
43+
}

tools/HelenTraining/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using DlibDotNet;
1111
using FaceRecognitionDotNet;
1212
using Microsoft.Extensions.CommandLineUtils;
13+
using ImageFormat = System.Drawing.Imaging.ImageFormat;
1314
using Point = DlibDotNet.Point;
1415

1516
namespace HelenTraining

0 commit comments

Comments
 (0)