顔画像の識別


複数枚の顔画像を記録して、示された画像の番号を当てます。
顔認証システムの基礎のそのまた基礎です。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. 顔画像を識別する image.cs のソースコードです。
    //★ Win32 API  顔画像の識別    前田 稔
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    
    struct BITMAPINFOHEADER
    {
        public UInt32 biSize;
        public Int32  biWidth;
        public Int32  biHeight;
        public UInt16 biPlanes;
        public UInt16 biBitCount;
        public UInt32 biCompression;
        public UInt32 biSizeImage;
        public Int32  biXPelsPerMeter;
        public Int32  biYPelsPerMeter;
        public UInt32 biClrUsed;
        public UInt32 biClrImportant;
    };
    unsafe struct BITMAPINFO
    {
        public BITMAPINFOHEADER bmih;
        public fixed UInt32 bmiColors[1];
    };
    
    public class MyForm : Form
    {   Face    App;
        bool    flag = false;
        Point[,]    m_t = new Point[10,7];  //Mark Table
        int         m_idx;                  //m_t Index
        string      m_str;
    
        public MyForm()
        {
            App = new Face();
            m_idx = 0;
            m_str = "";
            Paint += new PaintEventHandler(MyHandler);
            MouseDown += new MouseEventHandler(OnMyMouseDown);
        }
    
        private void MyHandler(object sender, PaintEventArgs e)
        {
            Graphics g; 
            g = e.Graphics;
            if (flag==false)
            {   g.DrawString("顔画像の識別プログラムです。", Font, Brushes.Black, new PointF(40f, 60f));
                g.DrawString("左クリックで登録します。", Font, Brushes.Black, new PointF(40f, 100f));
                g.DrawString("右クリックで識別します。", Font, Brushes.Black, new PointF(40f, 140f));
            }
            else
            {   App.View(g,0,0);
                App.DrawMark(g);
                if (m_str!=string.Empty)    MessageBox.Show(m_str);
            }
        }
    
        private void OnMyMouseDown(object sender, MouseEventArgs e)
        {   int i,j;
            if (e.Button == MouseButtons.Left)  //マウスの左ボタン
            {
                App.OpenFile();
                SetMark();
                Width = App.m_bmp.Width;
                Height = App.m_bmp.Height+32;
                flag = true;
                for(i=0; i<7; i++)  m_t[m_idx,i]= App.m_pt[i];
                m_idx++;
                m_str = "顔の画像を登録します";
            }
            if (e.Button == MouseButtons.Right) //マウスの右ボタン
            {
                App.OpenFile();
                SetMark();
                for(j=0; j<m_idx; j++)
                {
                    for(i=0; i<7; i++)
                        if (m_t[j,i].X != App.m_pt[i].X || m_t[j,i].Y != App.m_pt[i].Y) break;
                    if (i>=7) break;
                }
                if (j < m_idx)
                    m_str = "画像の登録番号は: " + (j + 1) + " 番です";
                else
                    m_str = "登録されていません";
            }
            Invalidate();
        }
    
        // Land Mark を検索
        public void SetMark()
        {
            App.InitDib();
            App.CopyBmp(App.m_DibDC);
            App.CopyBmp(App.m_MaskDC);
            App.Edge(33000);
            App.LandMark();
        }
        
        public void VerMsg(string msg, int v)
        {
            string wstr;
            wstr = msg + v.ToString();
            this.Text = wstr;
        }
    }
    
    //☆ Face Object Class
    unsafe class Face
    {
        ・・・
    
         Face Object Class のソースは Mark Rect を参照して下さい。
         
        ・・・
    
  2. 顔画像の識別は、色やサイズで識別することは出来ないので、パーツの相対位置や大きさや特徴を利用します。
    Face Object Class のソースは Mark Rect に掲載しています。
    flag は画像が入力されたときのフラグで、起動時は概要を説明するメッセージを表示します。
    m_t は最大10人分の Land Mark のテーブルで、左クリックで選択された画像を登録します。
    m_str は MessageBox.Show() で表示するメッセージです。
    {   Face    App;
        bool    flag = false;
        Point[,]    m_t = new Point[10,7];  //Mark Table
        int         m_idx;                  //m_t Index
        string      m_str;
    
  3. MyForm では Face Class を生成して Initialize します。
        public MyForm()
        {
            App = new Face();
            m_idx = 0;
            m_str = "";
            Paint += new PaintEventHandler(MyHandler);
            MouseDown += new MouseEventHandler(OnMyMouseDown);
        }
    
  4. MyHandler() では flag が false のときは、概要を説明するメッセージを表示します。
    flag が true のときは App.View(g,0,0); で入力した顔の画像を描画します。
    顔の画像に重ねて App.DrawMark(g); で、検出された Land Mark を表示します。
    また m_str にメッセージが格納されているときは MessageBox を表示します。
        private void MyHandler(object sender, PaintEventArgs e)
        {
            Graphics g; 
            g = e.Graphics;
            if (flag==false)
            {   g.DrawString("顔画像の識別プログラムです。", Font, Brushes.Black, new PointF(40f, 60f));
                g.DrawString("左クリックで登録します。", Font, Brushes.Black, new PointF(40f, 100f));
                g.DrawString("右クリックで識別します。", Font, Brushes.Black, new PointF(40f, 140f));
            }
            else
            {   App.View(g,0,0);
                App.DrawMark(g);
                if (m_str!=string.Empty)    MessageBox.Show(m_str);
            }
        }
    
  5. マウスの左クリックで顔の画像を選択して入力します。
    SetMark() 関数で、入力した画像の Land Mark を検出して App.m_pt に7個のマークを格納します。
    顔の画像によっては、ランドマークが正しく設定されていない場合もありますが、顔画像を識別するだけのときは気にしなくても構いません。
    App.m_pt から m_t に Land Mark をコピーして登録します。
        private void OnMyMouseDown(object sender, MouseEventArgs e)
        {   int i,j;
            if (e.Button == MouseButtons.Left)  //マウスの左ボタン
            {
                App.OpenFile();
                SetMark();
                Width = App.m_bmp.Width;
                Height = App.m_bmp.Height+32;
                flag = true;
                for(i=0; i<7; i++)  m_t[m_idx,i]= App.m_pt[i];
                m_idx++;
                m_str = "顔の画像を登録します";
            }
    
  6. マウスの右クリックで顔の画像を選択して、登録済の画像と照合します。
    SetMark() で Land Mark を調べて m_t と一致すれば登録済の画像で、見つからなければ新しい画像です。
            if (e.Button == MouseButtons.Right) //マウスの右ボタン
            {
                App.OpenFile();
                SetMark();
                for(j=0; j<m_idx; j++)
                {
                    for(i=0; i<7; i++)
                        if (m_t[j,i].X != App.m_pt[i].X || m_t[j,i].Y != App.m_pt[i].Y) break;
                    if (i>=7) break;
                }
                if (j < m_idx)
                    m_str = "画像の登録番号は: " + (j + 1) + " 番です";
                else
                    m_str = "登録されていません";
            }
            Invalidate();
        }
    
  7. プログラムを起動して、左クリックから3枚の画像を登録して下さい。
    右クリックで登録済の画像を選択すると、登録した番号が表示されます。
    4枚目の画像を選択すると「登録されていません」が表示されます。
    左クリックから4枚目の画像を登録すると識別されるようになります。
    5個のランドマーク(鼻と眉間は決まっている)を使っているのですが、画像を識別するには十分なようです。
  8. 顔認証のプログラムは、ここからが本番です。
    顔認証に利用するには、正確に Land Mark を設定しなければなりません。
    Land Mark では14個の Land Mark を設定していますが、実用化するには数百個の Land Mark が必要になるようです。
    左右の対称性や座標の妥当性などもチェックする必要があるでしょう。
    用途によっては、正面以外の顔や髪や眼鏡で隠れた場合の対策も必要です。
    顔の表情によって、マークが移動する許容範囲を考慮することも必要です。
    人物の違いにより、マーク間の距離や相対座標のバラツキも調べる必要があり、実用的なシステムでは数千枚(数万枚以上)の顔の画像を調べて 「正しく認証されるか、違った人物を認証しないか」をテストする必要があるようです。
    C言語(Windows)のプログラムは 顔画像の識別 を参照して下さい。

[Previous Chapter ↑] Land Mark

超初心者のプログラム入門(C# Frame Work)