【Win10 應用開發】人臉識別


可能你會認為人臉識別用起來會很復雜,老周當初也這么想,但通過實際操作后,我發現非然。

經過微軟封裝的東西,向來都是復雜問題簡單化,只要用得舒心,代碼越少越好,用最少的代碼做最多的事情,此為大師境界也。

好,屁話不說,先介紹一下如何完成人臉識別(或者叫人臉檢測,隨你怎么翻譯,反正知道是怎么一回事就行)。核心的類是FaceDetector,不要問我這個類在哪里,自己打開對象瀏覽器搜索。

第一步,訪問靜態屬性IsSupported,看看當前平台是不是支持人臉識別,如果返回false,那就沒戲了。

第二步,調用靜態方法CreateAsync得到一個FaceDetector實例,所以該類不需要手動實例化,初始化過程由運行庫來完成,然后把初始化好的實例返回給咱們,然后就可以用它來干活了。

第三步,調用實例方法DetectFacesAsync進行識別,識別完后會返回一個DetectedFace列表,每個DetectedFace表示一張臉,因為你用來識別的照片中可能包含N張臉。FaceBox屬性包含了人臉的矩形區域,就是人臉位於整張照片的哪個位置,可通過X,Y坐標描述矩形的左上角位置,並用寬度和高度來表明那張臉的大小。

DetectFacesAsync方法需要一個SoftwareBitmap類型的參數,該參數就是你要用來識別人臉的圖像。

可能大家已經知道,通過BitmapDecoder類的GetSoftwareBitmapAsync方法可以返回一個SoftwareBitmap實例,不過要注意的是,FaceDetector在進行檢測時並不是所有像素格式都支持,可以調用GetSupportedBitmapPixelFormats方法來獲取所有受支持的像素格式列表,經老周測試,該方法返回Nv12和Gray8,也就是當前只支持這兩種格式。當然,你也可以通過IsBitmapPixelFormatSupported方法來驗證一下某個像素格式是否被支持。

 

好了,基本用法已經說完了,確實不是很復雜。下面,老周給大家演示一個例子,該例子允許你選擇一張照片,然后識別出照片上的人臉,並用一個矩形來標記。

先看看UI的設計,主要的XAML如下:

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="auto"/>
            </Grid.RowDefinitions>
            <Button Content="選擇要識別的照片" Click="OnClick"/>
            <Viewbox  Grid.Row="1" Margin="5" Stretch="Uniform" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <Canvas Name="cv" Width="{x:Bind img.Width,Mode=OneWay}" Height="{x:Bind img.Height,Mode=OneWay}" >
                    <Image Name="img" Stretch="None"/>
                </Canvas>
            </Viewbox>
            
            <TextBlock Name="tbMessage" Foreground="Red" FontSize="16" Grid.Row="2"/>
        </Grid>

為啥我要用一個ViewBox呢,因為這個控件有一個好處,就是會自動將它里面的可視化對象進行縮放,待會兒我要在Image上顯示圖片,而且還要用到Rectangle元素來標記人臉的位置,為了讓絕對坐標計算起來能與原圖相等,就把這些內容都放在ViewBox中,讓Viewbox來進行縮放,這樣一來,就能夠根據窗口的大小自動調整顯示區域了。

之所以用Canvas,是因它是絕對坐標定位的,這方便我稍后放置Rectangle元素。

 

處理按鈕事件,通過OpenFilePicker來打開圖像文件。

            FileOpenPicker picker = new FileOpenPicker();
            picker.FileTypeFilter.Add(".jpg");
            picker.FileTypeFilter.Add(".jpeg");
            picker.FileTypeFilter.Add(".png");

            StorageFile file = await picker.PickSingleFileAsync();

記得以前跟大家講過,picker調用后要掛起當前應用,並通過響應應用激活來處理獲取的文件,這是在WP 8.1的文章中說過,但是,現在不用了,很簡單,因為Windows Phone App和Windows App已經完全統一了,所以不必再考慮平台隔離的代碼了。

下面代碼完成兩件事:1、在Image控件上顯示圖像;2、進行識別,並用矩形標記人臉位置。

            if (file != null)
            {
                using (IRandomAccessStream streamIn= await file.OpenReadAsync())
                {
                    // 對圖像文件進行解碼
                    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(streamIn);
                    // 獲取圖像內容
                    SoftwareBitmap sbmp = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight);

                    WriteableBitmap bmp = new WriteableBitmap(sbmp.PixelWidth, sbmp.PixelHeight);
                    sbmp.CopyToBuffer(bmp.PixelBuffer);
                    this.img.Source = bmp;
                    img.Width = bmp.PixelWidth;
                    img.Height = bmp.PixelHeight;

                    // 通過循環,嘗試以各種受支持的格式來進行識別
                    // 如果識別順利,就跳出循環
                    // 否則進入下一輪循環
                    int n = 0;
                    while (n < formats.Count)
                    {
                        if (FaceDetector.IsBitmapPixelFormatSupported(formats[n]))
                        {
                            FaceDetector detector = await FaceDetector.CreateAsync();
                            try
                            {
                                // 轉換圖像像素格式
                                SoftwareBitmap sbmp2 = SoftwareBitmap.Convert(sbmp, formats[n]);
                                // 進行檢測
                                IList<DetectedFace> results = await detector.DetectFacesAsync(sbmp2);

                                // 清理Canvas中的矩形
                                while (cv.Children.Count > 1)
                                    cv.Children.RemoveAt(cv.Children.Count - 1);
                                // 在界面上添加矩形
                                for (int i = 0; i < results.Count; i++)
                                {
                                    DetectedFace dface = results[i];
                                    Rectangle rect = new Rectangle();

                                    rect.Stroke = new SolidColorBrush(Colors.Yellow);
                                    rect.StrokeThickness = 5d;
                                    // 定位矩形
                                    Canvas.SetLeft(rect, dface.FaceBox.X);
                                    Canvas.SetTop(rect, dface.FaceBox.Y);
                                    rect.Width = dface.FaceBox.Width;
                                    rect.Height = dface.FaceBox.Height;
                                    cv.Children.Add(rect);
                                }
                                tbMessage.Text = "人臉識別完成。";
                                break;
                            }
                            catch
                            {
                                tbMessage.Text = "人臉識別失敗。";
                                n++;
                            }
                            //cv.InvalidateArrange();
                        }
                    }

                }


因為我要在Image上顯示圖像,所以從decoder中得到的SoftwareBitmap不能直接用於識別,原因是我剛才說了,目前SDK的人臉識別只支持少量的像素格式,Bgra8是不受支持的,所以可以用SoftwareBitmap的Convert方法轉換格式,並返回轉換后的SoftwareBitmap對象。

這里我用一個while循環來完成識別:

                    int n = 0;
                    while (n < formats.Count)
                    {
                        if (FaceDetector.IsBitmapPixelFormatSupported(formats[n]))
                        {
                            FaceDetector detector = await FaceDetector.CreateAsync();
                            try
                            {
                                ……
                                break;
                            }
                            catch
                            {
                                ……
                                n++;
                            }
                        }

意思是,我用FaceDetector所支持的所有像素格式都去嘗試進行識別,只要其中有一種格式能夠順利完成識別,就終止循環(break);如果第一種格式不能識別,就把n++來使用第二種格式來識別。

=============================================

示例的大致情況就是這樣,做完后我們當然要來試試效果了。

首先,來檢測一下八戒的豬臉。

很顯然,豬臉也能檢測出來,不錯。

接着,我又請神仙妹妹來試鏡。發現效果甚好。

    

然后,我又找來一位MM再試,效果也甚佳。

 

怎么樣,這姿勢不錯吧。

示例源碼下載:http://files.cnblogs.com/files/tcjiaan/FaceDetApp.zip

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM