c#中 在webapi中 使用opencvSharp4以及ssim算法 完成人脸检测 以此实现以图搜图
通过把上传的图片中的人物人脸剪切出来跟图库作比较
最终跟相似度做比较 把符合的筛选出来
优化
1.ssim算法消耗进程内存过多
2.opencvsharp可以考虑替换成开源框架FaceRecognitionDotNet
整体代码如下
using Emgu.CV;
using Emgu.CV.Structure;
using Microsoft.AspNetCore.Mvc;
using OpenCvSharp;
using System.Drawing;
using CascadeClassifier = Emgu.CV.CascadeClassifier;
using Mat = OpenCvSharp.Mat;
using Size = System.Drawing.Size;
namespace WebApplication5.Controllers
{
[ApiController]
[Route("api/[controller]/[action]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
/// <summary>
/// 剪切图片中的人脸并保存
/// </summary>
[HttpGet(Name = "Getface")]
public void GetFace()
{
var face = new CascadeClassifier(@"C:\Users\2023\Desktop\haarcascade_frontalface_alt.xml");
//加载要识别的图片
var img = new Image<Bgr, byte>(@"C:\Users\2023\Desktop\test\38.png");
//在这一步就已经识别出来了,返回的是人脸所在的位置和大小
var facesDetected = face.DetectMultiScale(img, 1.2, 10, new Size(60, 60));
//循环把人脸部分切出来并保存
int count = 0;
var b = img.ToBitmap();
foreach (var item in facesDetected)
{
count++;
var bmpOut = new Bitmap(item.Width, item.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
var g = Graphics.FromImage(bmpOut);
g.DrawImage(b, new Rectangle(0, 0, item.Width, item.Height), new Rectangle(item.X, item.Y, item.Width, item.Height), GraphicsUnit.Pixel);
g.Dispose();
bmpOut.Save($"{count}.png", System.Drawing.Imaging.ImageFormat.Png);
bmpOut.Dispose();
}
//释放资源退出
b.Dispose();
img.Dispose();
face.Dispose();
}
/// <summary>
/// 根据生成的人脸进行比对并输出符合条件的图片
/// </summary>
[HttpGet(Name = "GetResultImage")]
public void getResultImage()
{
var localImages = Directory.GetFiles(@"C:\Users\2023\Desktop\test", "*.png");
String generatedImage = @"C:\Users\2023\Desktop\6.png";
double Minshold = 0.38731;
double Maxhold = 0.4401;
Parallel.ForEach(localImages, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
(i) =>
{
// 进行比对的代码
double ssim = (double)CompareImagesSSIM(i, generatedImage);
if (ssim >= Minshold && ssim <= Maxhold)
{
var outputDirectory = @"C:\Users\2023\Desktop\testPro\newDir";
if (!Directory.Exists(outputDirectory))
{
Directory.CreateDirectory(outputDirectory);
}
var outputFilePath = Path.Combine(outputDirectory, Path.GetFileName(i));
using (var sourceStream = new FileStream(i, FileMode.Open))
using (var destinationStream = new FileStream(outputFilePath, FileMode.Create))
{
sourceStream.CopyTo(destinationStream);
}
}
}
);
}
/// <summary>
/// 计算ssim精确度
/// </summary>
/// <param name="imgFile1"></param>
/// <param name="imgFile2"></param>
/// <returns></returns>
[HttpPost(Name = "GetSsim")]
public Scalar CompareImagesSSIM(string imgFile1, string imgFile2)
{
using (var image1 = Cv2.ImRead(imgFile1))
using (var image2Tmp = Cv2.ImRead(imgFile2))
{
// 将两个图片处理成同样大小,否则会有错误:
var image2 = new Mat();
Cv2.Resize(image2Tmp, image2, new OpenCvSharp.Size(image1.Size().Width, image1.Size().Height));
double C1 = 6.5025, C2 = 58.5225;
var validImage1 = new Mat();
var validImage2 = new Mat();
Parallel.Invoke(
() =>{ image1.ConvertTo(validImage1, MatType.CV_32F); },
() => { image2.ConvertTo(validImage2, MatType.CV_32F); }
);
//image1.ConvertTo(validImage1, MatType.CV_32F); //数据类型转换为 float,防止后续计算出现错误
//image2.ConvertTo(validImage2, MatType.CV_32F);
Mat image1_1 = validImage1.Mul(validImage1); //图像乘积
Mat image2_2 = validImage2.Mul(validImage2);
Mat image1_2 = validImage1.Mul(validImage2);
Mat gausBlur1 = new Mat(), gausBlur2 = new Mat(), gausBlur12 = new Mat();
Cv2.GaussianBlur(validImage1, gausBlur1, new OpenCvSharp.Size(11, 11), 1.5); //高斯卷积核计算图像均值
Cv2.GaussianBlur(validImage2, gausBlur2, new OpenCvSharp.Size(11, 11), 1.5);
Cv2.GaussianBlur(image1_2, gausBlur12, new OpenCvSharp.Size(11, 11), 1.5);
Parallel.Invoke(
() => { }
);
Mat imageAvgProduct = gausBlur1.Mul(gausBlur2); //均值乘积
Mat u1Squre = gausBlur1.Mul(gausBlur1); //各自均值的平方
Mat u2Squre = gausBlur2.Mul(gausBlur2);
Mat imageConvariance = new Mat(), imageVariance1 = new Mat(), imageVariance2 = new Mat();
Mat squreAvg1 = new Mat(), squreAvg2 = new Mat();
Cv2.GaussianBlur(image1_1, squreAvg1, new OpenCvSharp.Size(11, 11), 1.5); //图像平方的均值
Cv2.GaussianBlur(image2_2, squreAvg2, new OpenCvSharp.Size(11, 11), 1.5);
imageConvariance = gausBlur12 - gausBlur1.Mul(gausBlur2);// 计算协方差
imageVariance1 = squreAvg1 - gausBlur1.Mul(gausBlur1); //计算方差
imageVariance2 = squreAvg2 - gausBlur2.Mul(gausBlur2);
var member = ((2 * gausBlur1.Mul(gausBlur2) + C1).Mul(2 * imageConvariance + C2));
var denominator = ((u1Squre + u2Squre + C1).Mul(imageVariance1 + imageVariance2 + C2));
Mat ssim = new Mat();
Cv2.Divide(member, denominator, ssim);
var sclar = Cv2.Mean(ssim);
validImage1.Dispose();
validImage2.Dispose();
image1_1.Dispose();
image2_2.Dispose();
image1_2.Dispose();
gausBlur1.Dispose();
gausBlur2.Dispose();
gausBlur12.Dispose();
imageAvgProduct.Dispose();
u1Squre.Dispose();
u2Squre.Dispose();
imageConvariance.Dispose();
imageVariance1.Dispose();
imageVariance2.Dispose();
squreAvg1.Dispose();
squreAvg2.Dispose();
ssim.Dispose();
GC.Collect();
return sclar; // 变化率,即差异
}
}
}
}