diff --git a/README.md b/README.md index d43a367..6118a87 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ This package supports cross platform, Windows, Linux and MacOSX!! |face_landmarks|FaceLandmarks|And support **Helen dataset** :warning:| |face_locations|FaceLocations|| |load_image_file|LoadImageFile|| +|-|CropFaces|Crop image with specified locations| |-|LoadImage|From memory data| |-|PredictAge|Use **Adience Benchmark Of Unfiltered Faces For Gender And Age Classification dataset** :warning:| |-|PredictGender|Use **UTKFace dataset** :warning:| diff --git a/src/FaceRecognitionDotNet/FaceRecognition.cs b/src/FaceRecognitionDotNet/FaceRecognition.cs index 20921f1..3271eab 100644 --- a/src/FaceRecognitionDotNet/FaceRecognition.cs +++ b/src/FaceRecognitionDotNet/FaceRecognition.cs @@ -216,6 +216,53 @@ public static FaceRecognition Create(string directory) return new FaceRecognition(directory); } + /// + /// Crop a specified image with enumerable collection of face locations. + /// + /// The image contains a face. + /// The enumerable collection of location rectangle for faces. + /// + /// or is null. + /// is disposed. + public static IEnumerable CropFaces(Image image, IEnumerable locations) + { + if (image == null) + throw new ArgumentNullException(nameof(image)); + if (locations == null) + throw new ArgumentNullException(nameof(locations)); + + image.ThrowIfDisposed(); + + foreach (var location in locations) + { + var rect = new Rectangle(location.Left, location.Top, location.Right, location.Bottom); + var dPoint = new[] + { + new DPoint(rect.Left, rect.Top), + new DPoint(rect.Right, rect.Top), + new DPoint(rect.Left, rect.Bottom), + new DPoint(rect.Right, rect.Bottom), + }; + + var width = (int)rect.Width; + var height = (int)rect.Height; + + switch (image.Mode) + { + case Mode.Rgb: + var rgb = image.Matrix as Matrix; + yield return new Image(DlibDotNet.Dlib.ExtractImage4Points(rgb, dPoint, width, height), + Mode.Rgb); + break; + case Mode.Greyscale: + var gray = image.Matrix as Matrix; + yield return new Image(DlibDotNet.Dlib.ExtractImage4Points(gray, dPoint, width, height), + Mode.Rgb); + break; + } + } + } + /// /// Compare a face encoding to a known face encoding and get a euclidean distance for comparison face. /// diff --git a/src/FaceRecognitionDotNet/Image.cs b/src/FaceRecognitionDotNet/Image.cs index e1b5009..0cd867f 100644 --- a/src/FaceRecognitionDotNet/Image.cs +++ b/src/FaceRecognitionDotNet/Image.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using DlibDotNet; namespace FaceRecognitionDotNet @@ -62,6 +63,35 @@ public int Width #region Methods + /// + /// Saves this to the specified file. + /// + /// A string that contains the name of the file to which to save this . + /// The for this . + /// is null. + public void Save(string filename, ImageFormat format) + { + if (filename == null) + throw new ArgumentNullException(nameof(filename)); + + var directory = Path.GetDirectoryName(filename); + if (!Directory.Exists(directory) && !string.IsNullOrWhiteSpace(directory)) + Directory.CreateDirectory(directory); + + switch (format) + { + case ImageFormat.Bmp: + DlibDotNet.Dlib.SaveBmp(this._Matrix, filename); + break; + case ImageFormat.Jpeg: + DlibDotNet.Dlib.SaveJpeg(this._Matrix, filename); + break; + case ImageFormat.Png: + DlibDotNet.Dlib.SavePng(this._Matrix, filename); + break; + } + } + #region Overrides /// diff --git a/src/FaceRecognitionDotNet/ImageFormat.cs b/src/FaceRecognitionDotNet/ImageFormat.cs new file mode 100644 index 0000000..5b83270 --- /dev/null +++ b/src/FaceRecognitionDotNet/ImageFormat.cs @@ -0,0 +1,27 @@ +namespace FaceRecognitionDotNet +{ + + /// + /// Specifies the file format of the image. + /// + public enum ImageFormat + { + + /// + /// Specifies that the bitmap (BMP) image format. + /// + Bmp, + + /// + /// Specifies that the Joint Photographic Experts Group (JPEG) image format. + /// + Jpeg, + + /// + /// Specifies that the W3C Portable Network Graphics (PNG) image format. + /// + Png, + + } + +} \ No newline at end of file diff --git a/src/FaceRecognitionDotNet/docs/FaceRecognitionDotNet.xml b/src/FaceRecognitionDotNet/docs/FaceRecognitionDotNet.xml index f949674..6636142 100644 --- a/src/FaceRecognitionDotNet/docs/FaceRecognitionDotNet.xml +++ b/src/FaceRecognitionDotNet/docs/FaceRecognitionDotNet.xml @@ -383,6 +383,16 @@ The model file is not found. The specified directory path is not found. + + + Crop a specified image with enumerable collection of face locations. + + The image contains a face. + The enumerable collection of location rectangle for faces. + + or is null. + is disposed. + Compare a face encoding to a known face encoding and get a euclidean distance for comparison face. @@ -548,11 +558,39 @@ Gets the width of the image. + + + Saves this to the specified file. + + A string that contains the name of the file to which to save this . + The for this . + is null. + Releases all unmanaged resources. + + + Specifies the file format of the image. + + + + + Specifies that the bitmap (BMP) image format. + + + + + Specifies that the Joint Photographic Experts Group (JPEG) image format. + + + + + Specifies that the W3C Portable Network Graphics (PNG) image format. + + Describes the left, top, right and bottom location of a face. This class cannot be inherited. diff --git a/src/FaceRecognitionDotNet/docs/ja/FaceRecognitionDotNet.xml b/src/FaceRecognitionDotNet/docs/ja/FaceRecognitionDotNet.xml index 41ff6f9..e37e8d7 100644 --- a/src/FaceRecognitionDotNet/docs/ja/FaceRecognitionDotNet.xml +++ b/src/FaceRecognitionDotNet/docs/ja/FaceRecognitionDotNet.xml @@ -383,6 +383,16 @@ モデルファイルが見つかりません。 指定したディレクトリ パスが見つかりません。 + + + 顔の位置の列挙可能なコレクションで指定された画像を切り抜きます。 + + 顔を含む画像。 + 顔に対する矩形位置の列挙可能なコレクション。 + + または が null です。 + は破棄されています。 + 既知の顔エンコーディングと比較し、顔ユークリッド距離を取得します。 @@ -548,11 +558,39 @@ 画像の幅を取得します。 + + + この を、指定した形式で指定したファイルに保存します。 + + この の保存先ファイルの名前を格納している文字列。 + この 。 + が null です。 + アンマネージ リソースを解放します。 + + + イメージのファイル形式を指定します。 + + + + + ビットマップ (BMP) イメージ形式を指定します。 + + + + + JPEG (Joint Photographic Experts Group) イメージ形式を指定します。 + + + + + W3C PNG (Portable Network Graphics) イメージ形式を指定します。 + + 顔の左端、上端、右端、下端位置を説明します。このクラスは継承できません。 diff --git a/test/FaceRecognitionDotNet.Tests/FaceRecognitionTest.cs b/test/FaceRecognitionDotNet.Tests/FaceRecognitionTest.cs index 38fe9b8..0d9bb4d 100644 --- a/test/FaceRecognitionDotNet.Tests/FaceRecognitionTest.cs +++ b/test/FaceRecognitionDotNet.Tests/FaceRecognitionTest.cs @@ -229,6 +229,44 @@ public void CompareFacesTrue() Assert.True(false, "Assert check did not execute"); } + [Fact] + public void CropFaces() + { + const string testName = nameof(this.CropFaces); + + var path = Path.Combine(ImageDirectory, TwoPersonFile); + if (!File.Exists(path)) + { + var binary = new HttpClient().GetByteArrayAsync($"{TwoPersonUrl}/{TwoPersonFile}").Result; + + Directory.CreateDirectory(ImageDirectory); + File.WriteAllBytes(path, binary); + } + + foreach (var mode in new[] { Mode.Rgb, Mode.Greyscale }) + { + using (var image = FaceRecognition.LoadImageFile(path, mode)) + { + var locations = this._FaceRecognition.FaceLocations(image).ToArray(); + Assert.True(locations.Length == 2, $"{mode}"); + + var images = FaceRecognition.CropFaces(image, locations).ToArray(); + for (var index = 1; index <= images.Length; index++) + { + var croppedImage = images[index - 1]; + + var directory = Path.Combine(ResultDirectory, testName); + Directory.CreateDirectory(directory); + + var dst = Path.Combine(directory, $"{mode}-{index}.jpg"); + croppedImage.Save(dst, ImageFormat.Jpeg); + + croppedImage.Dispose(); + } + } + } + } + [Fact] public void CreateFail1() { @@ -1734,7 +1772,7 @@ private void FaceLandmark(string testName, PredictorModel model, bool useKnownLo Directory.CreateDirectory(directory); var dst = Path.Combine(directory, $"{facePart}-{mode}-known_{useKnownLocation}.bmp"); - bitmap.Save(dst, ImageFormat.Bmp); + bitmap.Save(dst, System.Drawing.Imaging.ImageFormat.Bmp); } } @@ -1752,7 +1790,7 @@ private void FaceLandmark(string testName, PredictorModel model, bool useKnownLo Directory.CreateDirectory(directory); var dst = Path.Combine(directory, $"All-{mode}-known_{useKnownLocation}.bmp"); - bitmap.Save(dst, ImageFormat.Bmp); + bitmap.Save(dst, System.Drawing.Imaging.ImageFormat.Bmp); } } } @@ -1786,7 +1824,7 @@ private void FaceLocation(string testName, int numberOfTimesToUpsample, Model mo Directory.CreateDirectory(directory); var dst = Path.Combine(directory, $"All-{mode}-{numberOfTimesToUpsample}.bmp"); - bitmap.Save(dst, ImageFormat.Bmp); + bitmap.Save(dst, System.Drawing.Imaging.ImageFormat.Bmp); } } } diff --git a/test/FaceRecognitionDotNet.Tests/ImageText.cs b/test/FaceRecognitionDotNet.Tests/ImageText.cs new file mode 100644 index 0000000..10a6966 --- /dev/null +++ b/test/FaceRecognitionDotNet.Tests/ImageText.cs @@ -0,0 +1,43 @@ +using System.IO; +using Xunit; + +namespace FaceRecognitionDotNet.Tests +{ + + public class ImageTest + { + + #region Fields + + private const string ResultDirectory = "Result"; + + #endregion + + [Fact] + public void Save() + { + const string testName = nameof(this.Save); + + var targets = new[] + { + new { Name = "saved.bmp", Format = ImageFormat.Bmp }, + new { Name = "saved.jpg", Format = ImageFormat.Jpeg }, + new { Name = "saved.png", Format = ImageFormat.Png }, + }; + + using (var img = FaceRecognition.LoadImageFile(Path.Combine("TestImages", "obama.jpg"))) + { + var directory = Path.Combine(ResultDirectory, testName); + Directory.CreateDirectory(directory); + + foreach (var target in targets) + { + var path = Path.Combine(directory, target.Name); + img.Save(path, target.Format); + } + } + } + + } + +} \ No newline at end of file diff --git a/tools/HelenTraining/Program.cs b/tools/HelenTraining/Program.cs index 0ef60ef..9c45e20 100644 --- a/tools/HelenTraining/Program.cs +++ b/tools/HelenTraining/Program.cs @@ -10,6 +10,7 @@ using DlibDotNet; using FaceRecognitionDotNet; using Microsoft.Extensions.CommandLineUtils; +using ImageFormat = System.Drawing.Imaging.ImageFormat; using Point = DlibDotNet.Point; namespace HelenTraining