程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++開發人臉性別識別教程(9)——搭建MFC框架之顯示圖片

C++開發人臉性別識別教程(9)——搭建MFC框架之顯示圖片

編輯:C++入門知識

C++開發人臉性別識別教程(9)——搭建MFC框架之顯示圖片


  在之前的博客中我們已經實現讀取用戶選定的文件夾,並將其路徑保存在相應的變量中,在這篇博文中我們將介紹如何借助CvvImage類將圖片顯示在picture控件中,並自動讀取文件夾下的其他圖片。

  一、添加“下一張”按鈕

  由於我們需要讀取文件夾下的所有圖像文件,而非某一張文件,因此有必要添加一個按鈕來進行控制,具體功能就是:每單擊一次這個按鈕,程序就會自動讀取下一張圖片並顯示在界面上。由於之前已經詳細介紹了MFC中添加Button控件的方式,這裡不再贅述。添加一個按鈕,命名為“下一張”,將ID更改為IDC_BUTTON_NextImage:

\

  二、編寫遍歷函數

  在上一篇博客中我們提到,在選中文件夾之後,程序會將文件夾的路徑保存在m_Path變量之中。接下來我們就借助這個變量來進一步遍歷其路徑下的圖像文件。這裡我們專門編寫一個函數來實現“遍歷下一張圖片”的功能,命名為GetNextBigImg。因此,需要向CGenderRecognitionMFCDlg類中添加這個成員函數。在類視圖中右擊相應的類,在快捷菜單中選擇“添加->添加函數”,輸入函數的屬性:

\

  GetNextBigImg()函數主要承擔著一下幾個任務:

\

  1、開始遍歷

  這裡將GetNextBigImg()放在OnBnClickedButtonImagefile()函數中的末尾部分進行調用,用以在單擊“圖片文件夾”按鈕讀取文件夾信息之後啟用文件讀取程序。

  2、從當前目錄路徑下讀入一個文件

  這裡讀取文件主要通過readdir函數來完成,考慮到用戶可能會選擇一個空文件夾,因此這裡需要對讀取操作進行一次判斷:

    if (m_pDir && (m_pEnt = readdir(m_pDir)) != NULL)
    {

    }

  readdir()函數能夠實現對當前目錄結構(m_pDir)中的文件的無重復順序讀取,即每次讀取完成後都會自動移到下一個待讀取的文件,與指針的機制類似,readdir()函數包含在dirent.h頭文件中,之前已經添加並包含完畢。此時,m_pEnt變量中保存了文件名稱:

\

  3、判斷是否為圖像文件

  這裡采用strstr()函數來判斷文件名中是否包含對應的擴展名字符串,這裡默認的圖像格式有四種:jpg ,bmp,png:

    if (m_pDir && (m_pEnt = readdir(m_pDir)) != NULL)
    {
        /**********判斷是否為圖像文件**********/
        char* jpg = strstr(m_pEnt->d_name,".jpg");
        char* bmp = strstr(m_pEnt->d_name,".bmp");
        char* png = strstr(m_pEnt->d_name,".png");
    }

  至於“m_pEnt->d_name”這種調用格式,在dirent.h頭文件中有著明確定義,有疑問的話可以查閱相關文件。接下裡通過判斷jpg、bmp、png這幾個變量是否為空來確定文件是否是圖像文件:

    if (m_pDir && (m_pEnt = readdir(m_pDir)) != NULL)
    {
        /**********判斷是否為圖像文件**********/
        char* jpg = strstr(m_pEnt->d_name,".jpg");
        char* bmp = strstr(m_pEnt->d_name,".bmp");
        char* png = strstr(m_pEnt->d_name,".png");

        if (jpg == NULL && bmp == NULL && png == NULL)      //如果該文件不是圖像文件
        {
            GetNextBigImg();
        }
        else
        {
            /**********顯示該圖片**********/
        }
    }

  注意這裡采用了一種遞歸的方式來實現非圖像文件的輪詢,即當前文件被判定為非圖像文件時(jpg、bmp、png均為空),則調用自身GetNextBigImg(),也就意味著再次執行一遍readdir()函數,使得文件指針後移意味,層層遞歸實現最終的文件遍歷;相應的,如果當前文件為三種圖像文件中的一種,則將當前圖片繪制到picture控件中,接下來編寫繪制圖像的代碼。

  4、繪制圖像至picture控件

  此時該輪到CvvImage大顯身手了。在此之前,我們需要先為picture控件關聯一個CRect類型的矩形變量,這個變量將用來保存picture控件在客戶區所處的位置。首先,為CGenderRecognitionMFCDlg類添加成員變量m_PicCtlRect:

\

  然後,再添加一個HDC(句柄)變量m_pPicCtlHdc,用於保存控件的句柄:

\

  然後在CGenderRecognitionMFCDlg的對話框初始化函數OnInitDialog()中編寫兩行代碼,將控件、句柄、位置信息這三個變量相互關聯起來:

    /*********初始化picture控件**********/
    m_pPicCtlHdc = GetDlgItem(IDC_PICTURE)->GetDC()->GetSafeHdc();   //返回控件句柄
    GetDlgItem(IDC_PICTURE)->GetClientRect(m_PicCtlRect);            //關聯控件位置

  將這兩句代碼添加到OnInitDialog()末尾即可,這裡有三個問題需要強調:

  (1)為什麼需要用到句柄和CRect變量?原因很簡單,CvvImage類的要求。這裡我們介紹一個查看函數形參的小技巧,即在函數名的括號中輸入一個逗號,VS就會自動給出函數的形參格式:

\

  可見,DrawToHDC這個函數需要兩個參數,一個是HDC類型的,一個是RECT*類型的。

  (2)如何快速查找類的成員函數?最直接的方法就是通過類視圖,單擊對應的類來進行浏覽即可:

\

  當然,通過上方的搜索欄也是可以的。

  (3)OnInitDialog函數。這個函數在程序開始構造MFC框架時執行,因此有關控件的初始化操作都應該在這個函數中進行,而非構造函數。

  此時准備工作已經完成,可以為GetNextBigImg()函數添加正式的顯示代碼了:

            /**********顯示該圖片**********/
            IplImage* imageSrc;
            CvvImage imageSrcCvvImg;
            char imageFullName [500];                  //保存圖像文件的全路徑

            sprintf_s(imageFullName,"%s%s",m_ImageDir,m_pEnt->d_name);  //拼出文件全路徑
            imageSrc = cvLoadImage(imageFullName);
            imageSrcCvvImg.CopyOf(imageSrc);
            imageSrcCvvImg.DrawToHDC(m_pPicCtlHdc,m_PicCtlRect);
            cvReleaseImage(&imageSrc);

  此時,運行程序,通過“圖像文件夾按鈕”,選擇一個含有圖片文件的文件夾,程序正常顯示圖片:

\

  5、添加“下一張”功能

  接下來我們為界面中的“下一張”按鈕指定其功能。雙擊“下一張”按鈕,添加響應函數:

\

  由於之前我們已經將圖片輪詢、顯示操作封裝在了GetNextBigImg()函數中,在這裡我們只需調用一把這個函數即可實現“下一張”的功能:

void CGenderRecognitionMFCDlg::OnBnClickedButtonNextimage()
{
    GetNextBigImg();
    // TODO: 在此添加控件通知處理程序代碼
}

  OK,大功告成。

  三、總結

  經過這篇博文,我們的MFC框架已經具備了基本的圖像顯示功能,在下一篇博文中我們將向其中添加人臉檢測的功能。這裡有幾個問題需要注意。

  1、OpenCv2.x關於圖片顯示的問題

  大家留心觀察會發現,這裡用到的CvvImage方法是完全基於OpenCv1.x的,用IplImage變量來表示圖片。

  2、遞歸層數的問題

  這裡GetNextBigImg()函數存在一個遞歸調用的過程,存在遞歸就需要考慮遞歸深度的問題。這裡每遍歷到一個非圖像文件,遞歸的深度就增加一層,如果超過規定的遞歸深度,程序就會崩潰,從這個角度來講通過遞歸的方法來輪詢圖像文件和非圖像文件,是存在嚴重BUG隱患的,只要文件夾下有足夠多的非圖像文件,程序必然會因為無限遞歸而崩潰,相信大家有能力找到其他更安全的方法來解決這個問題。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved