環境構成:vs2019、.Net FrameWork 4.8 Opencvsharp4
Nuget に最新の Opencvsharp4 をダウンロードするだけです。
私が理解している顔認識の原理について話しましょう。つまり、最初にトレーナーにいくつかのトレーニング データを与えます。つまり、これらのデータが誰に対応するかをトレーナーに伝えます。その後、トレーナーはこれらの画像の特徴を覚えて、対応する人の名前、認識中に認識画像の特徴を見つけて、それを訓練された特徴と比較し、訓練セット内でそれに最も近い特徴を見つけて、それに対応する名前を付けます。類似性、つまりスコアです。
Opencvsharp4 には対応するモジュールがあります。OpenCvSharp.Face では、FaceRecognizer が必要なトレーナーです。トレーニング セットを追加するときに与えるトレーニング データは画像と対応する ID 番号であることに注意してください。名前に変換する場合は、トレーニングセットを追加するときは、対応する名前を自分で保存し、トレーニング結果を与えるときは、与えられたID番号を使用して対応する名前を見つけ、トレーニングセットを追加するときは、同じ顔の写真を同じファイル内に置きますディレクトリ、つまり、名前は複数の異なる顔に対応できますが、顔に付けられる名前は 1 つだけです。トレーニング セットを追加して顔認識を実行する場合、画像サイズは同じである必要があります。少なくとも 2 つのセットを追加してください。トレーニングイメージ。
インターフェースの実装:
シンプルな Winform インターフェイス、すべてのコントロールが自己完結型
追加したトレーニング画像もインターフェイスに配置します。もちろん、ローカル トレーニング セットで直接変更することもできますが、画像のサイズは一貫している必要があることに注意してください。
おおよその実行結果
メインコード:
まず、トレーナーを定義します
public static FaceRecognizer faceRecongnizer = FisherFaceRecognizer.Create();
次に、画像を ID にバインドするカスタム クラスを定義します。
class yImgs
{
// 图像
public Mat Image { set; get; }
// 编号
public int ImageGroupId { set; get; }
}
トレーニング データを保存するには List<yImgs> を使用します。辞書を使用しない理由は、1. 数値が Key、画像が Value の場合、1 つの数値、つまり 1 人が 1 つの数値しか追加できません。辞書の値も面倒なのでListを直接使ったほうが良い 2. 画像をKey、数値をValueにすると画像の繰り返しができなくなるので、つまり、1 人が 2 つの同一の画像をトレーニング画像として追加することはできませんが、List<yImgs> を使用すると、2 つの同じ画像をトレーニング画像として追加できます。
このリストを取得した後、人の番号と名前をバインドする辞書を定義します。ここでは重複した名前は考慮されません。
public static Dictionary<int, string> namesDatas = new Dictionary<int, string>();
次に、トレーニング画像を追加します。現在、画像をローカルに追加し、目的の形式に設定して、トレーニング前にローカルからすべての画像情報を読み取ります。
/// <summary>
/// 添加训练集图片 即把指定图片调整为指定大小 并保存在训练集路径中
/// 同一人的照片都放在一个文件夹内 该文件夹的名字为 名字ID_名字,如:1_Lena
/// 图片名字是按顺序自己生成的 1.jpg 2.jpg....
/// </summary>
/// <param name="src">训练图像</param>
/// <param name="size">图像大小</param>
/// <param name="groupId">编号,与名字一一对应</param>
/// <param name="name">名字</param>
public static void AddTrainImg(Mat src, OpenCvSharp.Size size,int groupId,string name)
{
string path0 = groupId.ToString() + "_" + name;
string path = yVars.path + "\\" + path0 + "\\";
// 判断图像是否可以作为训练图像添加
// 即 图像包含人脸 且只有一张人脸
if (yMethods.TrainImgISOK(src) == false)
{
return;
}
try
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
}
catch (Exception ex)
{
YXH._01.yMessagebox.ShowDialogCN("路径创建失败:" + ex.Message);
return;
}
DirectoryInfo _path = new DirectoryInfo(path);
int i = 0;
do
{
i++;
} while (File.Exists(path + i.ToString() + ".jpg"));
string picname = path + i.ToString() + ".jpg";
try
{
Cv2.Resize(src, src, size);
src.SaveImage(picname);
yVars.TrainAgain = true;
YXH._01.yMessagebox.ShowDialogCN("训练图像添加成功");
}
catch (Exception ex)
{
YXH._01.yMessagebox.ShowDialogCN("训练图像添加失败:" + ex.Message);
}
}
ローカルで追加された形式は次のとおりです
トレーニング セットの画像がローカルに追加された後、ローカル情報が読み取られてトレーナーがトレーニングされます。最初に定義した List<yImgs> に値を代入し、それを使用してトレーナーをトレーニングします。
// 读取本地信息
private static bool GetInfos()
{
// 把原先的信息清空
yVars.faceDatas.Clear(); // 训练数据 List<yImgs>
yVars.namesDatas.Clear(); // 字典 Dictionary<int, string>
DirectoryInfo _path = new DirectoryInfo(yVars.path); //训练集存放路径
if (_path.GetDirectories().Length < 2)
{
YXH._01.yMessagebox.ShowDialogCN("本地训练集小于两组,请添加训练集");
return false;
}
if (yFiles.DirectoryHasTwoGroup(yVars.path) == false)
{
YXH._01.yMessagebox.ShowDialogCN("本地训练集小于两组,请添加训练集");
return false;
}
foreach (DirectoryInfo var in _path.GetDirectories())
{
string[] tempstr = var.ToString().Split('_');
int groupID = 0;
int.TryParse(tempstr[0], out groupID);
foreach (FileInfo vv in var.GetFiles())
{
if (!vv.FullName.Contains(".jpg"))
continue;
yVars.faceDatas.Add(
new yImgs
{
Image = new Mat(vv.FullName, ImreadModes.Grayscale),
ImageGroupId = groupID,
});
}
yVars.namesDatas.Add(groupID, tempstr[1]);
}
return true;
}
トレーニングはトレーナーのトレーニングメソッドを直接呼び出すだけ
yVars.faceRecongnizer.Train(yVars.faceDatas.Select(x => x.Image), yVars.faceDatas.Select(x => x.ImageGroupId));
トレーニング後に認識できるようになります。
おおよその手順は次のとおりです。
1. 認識画像からすべての顔画像を取得し、これらの顔画像をトレーニング セットと同じサイズに調整します (認識画像には複数の顔が存在する可能性がありますが、トレーニング画像には 1 つの顔のみが存在する可能性があります)。
// 从图片中获取所有的人脸图片 并调整为指定大小
private static List<Mat> GetFaces(Mat mm, OpenCvSharp.Rect[] rects, OpenCvSharp.Size size)
{
List<Mat> faces = new List<Mat>();
foreach (Rect rect in rects)
{
Mat m1 = new Mat(mm, rect);
Cv2.CvtColor(m1, m1, ColorConversionCodes.BGR2GRAY);
Cv2.Resize(m1, m1, size);
// Cv2.EqualizeHist(m1, m1);
faces.Add(m1);
}
return faces;
}
2. 上記のすべての面の位置を取得します
// 获取图像所有的人脸框
private static OpenCvSharp.Rect[] GetRects(Mat mm)
{
Mat grayImage = new Mat();
Cv2.CvtColor(mm, grayImage, ColorConversionCodes.BGR2GRAY);
Cv2.EqualizeHist(grayImage, grayImage);
string path = System.Windows.Forms.Application.StartupPath + "\\xml\\haarcascades\\" + "haarcascade_frontalface_alt.xml";
CascadeClassifier face = new CascadeClassifier(path);
Rect[] faces = face.DetectMultiScale(mm);
return faces;
}
3. 取得したすべての顔写真を認識し、対応する名前を取得します
// 获取所有名字
private static List<string> GetNames(List<Mat> mm)
{
List<string> names = new List<string>();
for (int i = 0; i < mm.Count; i++)
{
int groupId = -2;
//groupId = yVars.FaceDetect.faceRecongnizer.Predict(mm[i]);
double confidence = 0.0;
yVars.faceRecongnizer.Predict(mm[i], out groupId, out confidence);
string desName;
yVars.namesDatas.TryGetValue(groupId, out desName);
// names.Add(desName + " " + confidence.ToString("0.00"));
names.Add(desName);
}
return names;
}
4. 最後に、すべての名前を対応する位置に描画します。
// 画出所有的框和名字
public static Mat ShowFaceRects(Mat mm, Rect[] faces, List<string> names)
{
Random rnd = new Random();
int i = -1;
foreach (Rect face in faces)
{
i++;
Scalar color = new Scalar(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
Cv2.Rectangle(mm, face, color);
// 无法显示中文 可以考虑用 System.Drawing.Graphics
Cv2.PutText(mm, names[i], face.TopLeft, HersheyFonts.HersheySimplex, 0.8, new Scalar(255, 23, 0));
}
return mm;
}
名前を記述するときに Cv2.PutText を使用する場合、名前を中国語にすることはできないことに注意してください。
これにより、簡易的な顔認証が実現される。