C#开发的人脸左右相似度计算软件源码分析

作者:小萝莉 时间:2023-08-26 05:18:41 

本文实例讲述了C#开发的人脸左右相似度计算软件。分享给大家供大家参考。具体分析如下:

模仿湖南卫视快乐大本营中所使用的一款人脸左右对称相似度计算软件,自己写的一个小软件,使用语言是C#,希望跟喜欢这个软件的同志们共享!

1. FaceClass类程序


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace FaceSmile
{
class FaceClass
{
 /// <summary>
 /// 左脸对称函数
 /// </summary>
 /// <param name="a"></param>
 /// <returns></returns>
 public static Bitmap FaceFlipLeft(Bitmap a)
 {
  Rectangle rect = new Rectangle(0, 0, a.Width, a.Height);
  System.Drawing.Imaging.BitmapData srcData = a.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, a.PixelFormat);
  IntPtr ptr = srcData.Scan0;
  int bytes = 0;
  bytes = srcData.Stride * a.Height;
  byte[] grayValues = new byte[bytes];
  System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes);
  byte[] temp = new byte[bytes];
  temp = (byte[])grayValues.Clone();
  for (int j = 0; j < a.Height; j++)
  {
   for (int i = 0; i < (int)(a.Width/2); i++)
   {
    temp[(a.Width - 2 - i) * 3 + j * srcData.Stride] = temp[i * 3 + j * srcData.Stride];
    temp[(a.Width - 2 - i) * 3 + 1 + j * srcData.Stride] = temp[i * 3 + 1 + j * srcData.Stride];
    temp[(a.Width - 2 - i) * 3 + 2 + j * srcData.Stride] = temp[i * 3 + 2 + j * srcData.Stride];
   }
  }
  grayValues = (byte[])temp.Clone();
   System.Runtime.InteropServices.Marshal.Copy(grayValues, 0, ptr, bytes);
  a.UnlockBits(srcData);
  return a;
 }
 /// <summary>
 /// 右脸对称函数
 /// </summary>
 /// <param name="a"></param>
 /// <returns></returns>
 public static Bitmap FaceFlipRight(Bitmap a)
 {
  Rectangle rect = new Rectangle(0, 0, a.Width, a.Height);
  System.Drawing.Imaging.BitmapData srcData = a.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, a.PixelFormat);
  IntPtr ptr = srcData.Scan0;
  int bytes = 0;
  bytes = srcData.Stride * a.Height;
  byte[] grayValues = new byte[bytes];
  System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes);
  byte[] temp = new byte[bytes];
  temp = (byte[])grayValues.Clone();
  for (int j = 0; j < a.Height; j++)
  {
   for (int i = 0; i < (int)(a.Width / 2); i++)
   {
    temp[i * 3 + j * srcData.Stride] = temp[(a.Width - 2 - i) * 3 + j * srcData.Stride];
    temp[i * 3 + 1 + j * srcData.Stride] = temp[(a.Width - 2 - i) * 3 + 1 + j * srcData.Stride];
    temp[i * 3 + 2 + j * srcData.Stride] = temp[(a.Width - 2 - i) * 3 + 2 + j * srcData.Stride];
   }
  }
  grayValues = (byte[])temp.Clone();
  System.Runtime.InteropServices.Marshal.Copy(grayValues, 0, ptr, bytes);
  a.UnlockBits(srcData);
  return a;
 }
 /// <summary>
 /// 定义肤色检测函数
 /// </summary>
 /// <param name="a"></param>
 /// <returns></returns>
 public static Bitmap SkinDetect(Bitmap a)
 {
  Rectangle rect = new Rectangle(0, 0, a.Width, a.Height);
  System.Drawing.Imaging.BitmapData bmpData = a.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
  int stride = bmpData.Stride;
  unsafe
  {
   byte* pIn = (byte*)bmpData.Scan0.ToPointer();
   byte* P;
   int R, G, B;
   double r, g, Fupr, Flor, Wrg;
   for (int y = 0; y < a.Height; y++)
   {
    for (int x = 0; x < a.Width; x++)
    {
     P = pIn;
     B = P[0];
     G = P[1];
     R = P[2];
     if (R + G + B == 0)
     {
      r = 0;
      g = 0;
     }
     else
     {
      r = (R / (R + G + B));
      g = (G / (R + G + B));
     }
     Fupr = (1.0743 * r + 0.1452 - 1.3767 * r * r);
     Flor = (0.5601 * r + 0.1766 - 0.776 * r * r);
     Wrg = (r - 0.33) * (r - 0.33) + (g - 0.33) * (g - 0.33);
     if ((R - G >= 45) && ((R > G) && (G > B)) && (Fupr > g) && (Wrg >= 0.0004))
     {
      P[0] = (byte)B;
      P[1] = (byte)G;
      P[2] = (byte)R;
     }
     else
     {
      P[0] = 0;
      P[1] = 0;
      P[2] = 0;
     }
     pIn += 3;
    }
    pIn += stride - a.Width * 3;
   }
  }
  a.UnlockBits(bmpData);
  return a;
 }
 /// <summary>
 /// 定义图像灰度化函数
 /// </summary>
 /// <param name="src"></param>
 /// <returns></returns>
 public static Bitmap ImageGray(Bitmap src)
 {
  int w = src.Width;
  int h = src.Height;
  //构建与原图像大小一样的模版图像
  Bitmap dstBitmap = new Bitmap(src.Width, src.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
  //将原图像存入内存
  System.Drawing.Imaging.BitmapData srcData = src.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
  System.Drawing.Imaging.BitmapData dstData = dstBitmap.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
  unsafe
  {
   byte* pIn = (byte*)srcData.Scan0.ToPointer();
   byte* pOut = (byte*)dstData.Scan0.ToPointer();
   byte* p;
   int stride = srcData.Stride;
   int r, g, b;
   for (int y = 0; y < h; y++)
   {
    for (int x = 0; x < w; x++)
    {
     p = pIn;
     r = p[2];
     g = p[1];
     b = p[0];
     //调用图像灰度化公式
     pOut[0] = pOut[1] = pOut[2] = (byte)(b * 0.114 + g * 0.587 + r * 0.299);
     pIn += 3;
     pOut += 3;
    }
    pIn += srcData.Stride - w * 3;
    pOut += srcData.Stride - w * 3;
   }
   src.UnlockBits(srcData);
   dstBitmap.UnlockBits(dstData);
   return dstBitmap;
  }
 }
}
}

2. SameRatioClass类程序


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace FaceSmile
{
class SameRatioClass
{
 /// <summary>
 /// 左右脸相似度函数
 /// </summary>
 /// <param name="src"></param>
 /// <param name="dst"></param>
 /// <returns></returns>
 public static double SameRatio(Bitmap src, Bitmap dst)
 {
  byte[] srcData = GetBytes(src);
  byte[] dstData = GetBytes(dst);
  double ratio = 0;
  int sum = 0;
  int std=0;
  for (int i = 0; i < srcData.Length; i++)
  {
   sum += Math.Abs(srcData[i] - dstData[i]);
   std += srcData[i];
  }
  ratio = 100-(double)(100*sum / std);
  return ratio;
 }
 /// <summary>
 /// 得到图像信息函数
 /// </summary>
 /// <param name="src"></param>
 /// <returns></returns>
 private static byte[] GetBytes(Bitmap src)
 {
  int w = src.Width;
  int h = src.Height;
  byte[] dataImage = new byte[w * h];
  System.Drawing.Imaging.BitmapData srcData = src.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);  
  unsafe
  {
   byte* pIn = (byte*)srcData.Scan0.ToPointer();    
   byte* p;
   int stride = srcData.Stride;
   int r, g, b;
   for (int y = 0; y < h; y++)
   {
    for (int x = 0; x < w; x++)
    {
     p = pIn;
     r = p[2];
     g = p[1];
     b = p[0];
     dataImage[x + y * x] = (byte)((r + g + b) / 3);
     pIn += 3;      
    }
    pIn += srcData.Stride - w * 3;    
   }
   src.UnlockBits(srcData);
   return dataImage;
  }
 }
}
}

3. 主窗体程序


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace FaceSmile
{
public partial class Form1 : Form
{
 public Form1()
 {
  InitializeComponent();
  groupBox1.Visible = true;
  groupBox2.Visible = false;
 }
 #region 全局变量定义
 //定义原始图像变量
 private Bitmap src ;
 //定义图像相似度变量
 private double ratio = 0;
 //定义图像路径变量
 private string curFileName;
 //定义人脸位置图像调整变量
 private int numAdjust = 0;
 #endregion
 #region 软件操作
 //左脸对称
 private void button1_Click(object sender, EventArgs e)
 {
  if (src != null)
  {
   Bitmap temp = (Bitmap)src.Clone();
   Bitmap a = FaceClass.FaceFlipLeft(temp);
   pictureBox1.Image = (Image)a;
   ratio = SameRatioClass.SameRatio(a, src);
   label1.Text = ratio.ToString();
  }
  else
  {
   MessageBox.Show("Please open one image!");
  }
 }
 //右脸对称
 private void button2_Click(object sender, EventArgs e)
 {
  if (src != null)
  {
   Bitmap temp = (Bitmap)src.Clone();
   Bitmap a = FaceClass.FaceFlipRight(temp);
   pictureBox1.Image = (Image)a;
   ratio = SameRatioClass.SameRatio(a, src);
   label1.Text = ratio.ToString();
  }
  else
  {
   MessageBox.Show("Please open one image!");
  }
 }
 //打开图像
 private void button3_Click(object sender, EventArgs e)
 {
  OpenImage();
  if (src != null)
  {
   pictureBox1.Image = (Image)src;
   pictureBox1.Width = src.Width;
   pictureBox1.Height = src.Height;
  }
  else
  {
   MessageBox.Show("Please open one image!");
  }
 }
 //保存图像
 private void button4_Click(object sender, EventArgs e)
 {
  SaveImage();
 }
 //图像打开函数
 private void OpenImage()
 {
  try
  {
   ofd.Filter = "All files (*.*)|*.*|bmp files (*.bmp)|*.bmp|jpeg files (*.jpg)|*.jpg|png files (*.png)|*.png";
   ofd.Title = "打开";
   ofd.ShowHelp = true;
   if (ofd.ShowDialog() == DialogResult.OK)
   {
    curFileName = ofd.FileName;
    src = new Bitmap(curFileName);
   }
  }
  catch (Exception ex)
  {
   MessageBox.Show(ex.Message);
  }
 }
 //图像保存函数
 private void SaveImage()
 {
  try
  {
   sfd.Filter = "保存(*.bmp)|*.bmp";
   sfd.Title = "保存";
   sfd.ShowHelp = true;
   if (sfd.ShowDialog() == DialogResult.OK)
   {
    Bitmap temp = (Bitmap)pictureBox1.Image;
    temp.Save(sfd.FileName, System.Drawing.Imaging.ImageFormat.Bmp);
   }
  }
  catch (Exception ex)
  {
   MessageBox.Show(ex.Message);
  }
 }
 //其他操作
 private void button5_Click(object sender, EventArgs e)
 {
  groupBox2.Location = new Point(groupBox1.Location.X, groupBox1.Location.Y);
  groupBox2.Visible = true;
  groupBox1.Visible = false;
 }
 //肤色检测
 private void button6_Click(object sender, EventArgs e)
 {
  if (pictureBox1.Image != null)
  {
   pictureBox1.Image = (Image)FaceClass.SkinDetect((Bitmap)pictureBox1.Image);
  }
  else
  {
   MessageBox.Show("Please open one image!");
  }
 }
 //返回操作
 private void button7_Click(object sender, EventArgs e)
 {
  groupBox1.Visible = true;
  groupBox2.Visible = false;
 }
 //灰度化
 private void button8_Click(object sender, EventArgs e)
 {
  if (pictureBox1.Image != null)
  {
   pictureBox1.Image = (Image)FaceClass.ImageGray((Bitmap)pictureBox1.Image);
  }
  else
  {
   MessageBox.Show("Please open one image!");
  }
 }
 //博客连接
 private void label2_Click(object sender, EventArgs e)
 {
  System.Diagnostics.Process.Start("IEXPLORE.EXE", "http://dongtingyueh.blog.163.com/");
 }
 //修正人脸位置
 private void button9_Click(object sender, EventArgs e)
 {
  if (numAdjust != 0)
  {
   int a = numAdjust;
   int b = src.Width - a;
   int result = a < b ? a : b;
   if (result == b)
   {
    src = src.Clone(new Rectangle(src.Width - 2 * result, 0, 2 * result, src.Height), src.PixelFormat);
   }
   else
   {
    src = src.Clone(new Rectangle(0, 0, 2 * result, src.Height), src.PixelFormat);
   }
   pictureBox1.Image = (Image)src;
   pictureBox1.Width = src.Width;
   pictureBox1.Height = src.Height;
  }
  trackBar1.Value = 0;
  label4.Text = "0";
 }
 #endregion
 #region 人脸位置修正
 private void trackBar1_Scroll(object sender, EventArgs e)
 {
  if (src != null)
  {
   trackBar1.Maximum = src.Width;
   trackBar1.Minimum = 0;
   numAdjust = trackBar1.Value;
   label4.Text = numAdjust.ToString();
  }
  else
  {
   MessageBox.Show("Please open one image!");
  }
 }
 private void trackBar1_ValueChanged(object sender, EventArgs e)
 {
  pictureBox1.Invalidate();
 }
 private void trackBar1_MouseUp(object sender, MouseEventArgs e)
 {
  if (src != null)
  {
   Graphics g = pictureBox1.CreateGraphics();
   g.DrawLine(new Pen(Color.Red, 2), new Point((int)(numAdjust), 0), new Point((int)(numAdjust), src.Height));
   g.Dispose();
  }
  else
  {
   MessageBox.Show("Please open one image!");
  }
 }
 #endregion
}
}

希望本文所述对大家的C#程序设计有所帮助。

标签:C#,相似度,计算
0
投稿

猜你喜欢

  • 解决springboot环境切换失效的问题

    2023-11-11 20:40:35
  • kotlin 定义接口并实现回调的例子

    2022-12-06 14:30:19
  • C# PDF Page操作设置页面切换按钮的方法

    2021-11-18 04:33:43
  • Redis监听过期的key实现流程详解

    2023-12-12 02:41:28
  • Java8 Comparator: 列表排序的深入讲解

    2022-12-11 08:30:01
  • C#将图片和字节流互相转换并显示到页面上

    2021-08-18 23:41:31
  • SpringBoot实现阿里云短信发送的示例代码

    2023-05-15 21:08:54
  • Android Studio项目中导入开源库的方法

    2022-02-02 02:40:27
  • 浅谈Spring6中的反射机制

    2022-06-04 13:23:33
  • C#中的try catch finally用法分析

    2021-06-20 18:31:28
  • 解决SpringBoot web项目启动后立即关闭的问题

    2023-07-26 02:33:37
  • MyBatis学习教程(三)-MyBatis配置优化

    2023-10-31 21:13:51
  • JavaScript中栈和队列应用详情

    2023-05-01 00:25:52
  • 详解Android中的ActivityThread和APP启动过程

    2021-08-20 22:51:25
  • java byte数组与int,long,short,byte的转换实现方法

    2023-09-08 12:26:43
  • Spring Cloud Zuul路由网关服务过滤实现代码

    2021-08-26 10:56:07
  • 详解android 人脸检测你一定会遇到的坑

    2023-03-10 15:24:34
  • 详解Java中while和do-while循环、break的使用

    2022-10-24 13:37:04
  • Android进阶Hook拦截系统实例化View过程实现App换肤功能

    2023-12-01 21:22:12
  • Java Fluent Mybatis实战之构建项目与代码生成篇下

    2023-11-24 10:59:52
  • asp之家 软件编程 m.aspxhome.com