之前学习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
发表评论