之前学习ViewFaceCore时采用Panel控件和GDI+将图片及识别出的人脸方框和关键点绘制出来,本文将其修改为基于SKControl和SKCanvas实现相同的显示效果并支持保存为本地图片。
新建Winform项目,在Nuget包管理器中搜索并安装一下SkiaSharp和ViewFaceCore包,同时在主界面添加SKControl控件。
使用SKBitmap类的Decode方法加载本地图片文件,然后在skControl1的PaintSurface事件中调用SKCanvas.DrawBitmap函数绘制图形,函数原型如下所示:
对于ViewFaceCore识别出的人脸区域及关键点坐标,调用SKCanvas的DrawRect、DrawCircle绘制矩形和圆形,同时调用DrawText函数绘制人脸区域顺序号,这些函数在GDI+的Graphics类中都有对应的函数,但是函数名又不完全相同,看着很别扭。还有感觉很怪的是DrawRect函数(如下图所示),如果是直接输入矩形坐标(第一行重载函数),参数格式是左上角坐标及矩形宽和高,而输入矩形对象(第二行重载函数),构建矩形对象时输入的是矩形左上角和右下角坐标,同时绘制圆形时输入参数是圆心坐标和半径,并没有像Graphics类中的绘图函数参数模式那么一致(也可能是用GDI+习惯了,还没有转变过来)。
主要绘图代码和运行效果如下所示:
SKCanvas canvas = e.Surface.Canvas;
canvas.Clear();
if (m_image != null)
{
canvas.DrawBitmap(m_image, new SKRect(m_startX, m_startY, m_startX + m_image.Width * m_scale, m_startY + m_image.Height * m_scale));
if (m_faces.Count > 0)
{
using var paint = new SKPaint
{
Color = SKColors.Red,
Style = SKPaintStyle.Stroke,
IsAntialias = true,
StrokeWidth = 2
};
for (int i = 0; i < m_faces.Count; i++)
{
canvas.DrawRect(m_startX + m_faces[i].Face.Location.X * m_scale,
m_startY + m_faces[i].Face.Location.Y * m_scale,
m_faces[i].Face.Location.Width * m_scale,
m_faces[i].Face.Location.Height * m_scale,
paint);
canvas.DrawText(Convert.ToString(i + 1),
m_startX + m_faces[i].Face.Location.X * m_scale,
m_startY + m_faces[i].Face.Location.Y * m_scale - skControl1.Font.Height * 1.5f,
paint);
if (m_faces[i].MarkPoints != null && m_faces[i].MarkPoints.Length > 0)
{
foreach (FaceMarkPoint mp in m_faces[i].MarkPoints)
{
canvas.DrawCircle(m_startX + Convert.ToInt32(mp.X) * m_scale, m_startY + Convert.ToInt32(mp.Y) * m_scale, 3 * m_scale, paint);
}
}
}
}
}
将SKBitmap对象保存到本地的包括以下步骤:
&emsp1)新建SKBitmap对象;
&emsp2)基于SKBitmap对象创建SKCanvas对象;
&emsp3)依次绘制原始图片、人脸区域及关键点坐标;
&emsp4)将SKBitmap对象数据保存到本地文件,主要有两种方式,第一种是使用bitmap.Encode方法以指定格式写入本地文件流,测试过程中能以png格式保存,其它格式报错,暂时不清楚原因;第二种方式,安装的SkiaSharp.Views包的SkiaSharp.Views.Desktop提供有扩展函数ToBitmap,支持将SKBitmap对象转换为System.Drawing.Bitmap对象,再调用Bitmap对象的Save函数保存为需要的格式即可。主要代码如下:
using SKBitmap bitmap=new SKBitmap(m_image.Width, m_image.Height);
using SKCanvas canvas = new SKCanvas(bitmap);
canvas.DrawBitmap(m_image, 0, 0);
if (m_faces.Count > 0)
{
...
...
}
canvas.Flush();
bitmap.ToBitmap().Save(sfd.FileName, ImageFormat.Bmp);
//using (FileStream fsStream = new FileStream(sfd.FileName,FileMode.CreateNew,FileAccess.Write))
//{
// if(bitmap.Encode(fsStream,SKEncodedImageFormat.Png, 100))
// {
// MessageBox.Show("保存成功");
// }
//}
参考文献:
[1]https://github.com/mono/SkiaSharp
[2]https://learn.microsoft.com/en-us/dotnet/api/skiasharp?view=skiasharp-2.88
[3]https://blog.csdn.net/ken0online/article/details/132363856
[4]https://www.cnblogs.com/bigben0123/p/14984984.html
[5]https://www.jb51.net/article/257125.htm