如何使用OpenCV在“Microsoft Visual Studio”中構(gòu)建應(yīng)用程序

2019-03-11 19:34 更新

我在這里描述的一切將適用于C\C++ OpenCV 的界面。我從假設(shè)你已經(jīng)閱讀并成功完成了Windows中的安裝教程。因此,在您進(jìn)一步之前,請(qǐng)確保您具有包含OpenCV頭文件和二進(jìn)制文件的OpenCV目錄,并按照此處所述設(shè)置環(huán)境變量設(shè)置OpenCV環(huán)境變量并將其添加到系統(tǒng)路徑。

OpenCV_Install_Directory

由我們分發(fā)的Microsoft Windows操作系統(tǒng)上的OpenCV庫位于動(dòng)態(tài)鏈接庫(DLL)中。這些優(yōu)點(diǎn)是,庫的所有內(nèi)容只能在運(yùn)行時(shí)按需加載,并且無數(shù)程序可能使用相同的庫文件。這意味著如果您有十個(gè)使用OpenCV庫的應(yīng)用程序,則不需要為每個(gè)應(yīng)用程序提供每個(gè)應(yīng)用程序。當(dāng)然,您需要在要運(yùn)行應(yīng)用程序的所有系統(tǒng)上安裝OpenCV 的DLL。

另一種方法是使用具有l(wèi)ib擴(kuò)展名的靜態(tài)庫。您可以通過使用我們的源文件來構(gòu)建它們,如Windows中的“ 安裝”教程中所述。當(dāng)你使用這個(gè)庫將內(nèi)置在您的exe文件。因此,由于某些原因,用戶將無法刪除它們。作為一個(gè)缺點(diǎn),您的應(yīng)用程序?qū)⑹歉蟮囊粋€(gè),因?yàn)樗鼘⑿枰嗟臅r(shí)間來加載它在啟動(dòng)期間。

要使用OpenCV構(gòu)建應(yīng)用程序,您需要做兩件事情:

  • 告訴編譯器OpenCV庫的外觀。您可以通過顯示頭文件來執(zhí)行此操作。
  • 告訴鏈接器從何處獲取OpenCV的功能或數(shù)據(jù)結(jié)構(gòu)。

如果使用lib系統(tǒng),則必須設(shè)置庫文件所在的路徑,并指定其中的哪一個(gè)。在構(gòu)建期間,鏈接器將查找這些庫,并將所有使用的函數(shù)和數(shù)據(jù)結(jié)構(gòu)的定義和實(shí)現(xiàn)添加到可執(zhí)行文件中。

如果您使用DLL系統(tǒng),則必須再次指定所有這些,但是由于其他原因。這是一個(gè)Microsoft操作系統(tǒng)特定的東西??磥?,鏈接器需要知道在DLL中哪里可以在運(yùn)行時(shí)搜索數(shù)據(jù)結(jié)構(gòu)或函數(shù)。此信息存儲(chǔ)在lib文件中。不過,它們不是靜態(tài)庫。它們是所謂的導(dǎo)入庫。這就是為什么當(dāng)你在Windows中制作一些DLL時(shí),你也會(huì)得到一些lib擴(kuò)展庫。很重要的是在運(yùn)行時(shí)只需要DLL。

要將所有這些信息傳遞給Visual Studio IDE,您可以在全局范圍內(nèi)執(zhí)行(所有未來的項(xiàng)目都將獲得此信息)或本地(因此僅適用于當(dāng)前項(xiàng)目)。全局的優(yōu)勢(shì)是你只需要做一次; 但是,所有這些信息都可能不合時(shí)宜地聚集所有的項(xiàng)目。在全局的情況下,如何執(zhí)行此操作取決于您使用的Microsoft Visual Studio。有2008年和以前的版本和2010年的做法。在本教程的全局部分中,我將展示主要區(qū)別。

Visual Studio中項(xiàng)目的基礎(chǔ)項(xiàng)目是一個(gè)解決方案。解決方案可能包含多個(gè)項(xiàng)目。項(xiàng)目是應(yīng)用程序的構(gòu)建塊。每個(gè)項(xiàng)目都會(huì)實(shí)現(xiàn)一些東西,你將有一個(gè)主要的項(xiàng)目,你可以把這個(gè)項(xiàng)目的難題放在一起。在許多簡單的應(yīng)用程序(如許多教程將是)的情況下,您不需要將應(yīng)用程序分解為模塊。在這些情況下,您的主要項(xiàng)目將是唯一的現(xiàn)有項(xiàng)目?,F(xiàn)在,通過File - > New - > Project菜單選項(xiàng),在Visual Studio中創(chuàng)建一個(gè)新的解決方案。選擇Win32控制臺(tái)應(yīng)用程序作為類型。輸入其名稱并選擇要?jiǎng)?chuàng)建它的路徑。然后在即將到來的對(duì)話框中確保創(chuàng)建一個(gè)空項(xiàng)目。

NewProjectVisualStudio

The local method

每個(gè)項(xiàng)目都是與其他項(xiàng)目分開構(gòu)建的。由于這個(gè)每個(gè)項(xiàng)目都有自己的規(guī)則包。在這個(gè)規(guī)則中,包中存儲(chǔ)了IDE需要知道的所有信息來構(gòu)建項(xiàng)目。對(duì)于任何應(yīng)用程序,至少有兩種構(gòu)建模式:發(fā)布和調(diào)試。在調(diào)試有許多功能,存在,因此,你可以找到并解決您的應(yīng)用程序中更容易錯(cuò)誤。相比之下,Release是一個(gè)優(yōu)化的版本,其目標(biāo)是使應(yīng)用程序盡可能快地運(yùn)行或盡可能小。您可能會(huì)認(rèn)為,這些模式在構(gòu)建期間也需要使用不同的規(guī)則。因此,每個(gè)構(gòu)建模式都存在不同的規(guī)則包。這些規(guī)則包在IDE中作為項(xiàng)目屬性調(diào)用,您可以使用Property Manager查看和修改它們。您可以使用View - > Property Pages(對(duì)于Visual Studio 2013起,進(jìn)入View - > Other Windows - > Property Manager)。展開它,您可以看到現(xiàn)有的規(guī)則包(稱為屬性表)。

PropertyPageExample

這些真正有用的東西是您可以創(chuàng)建一個(gè)規(guī)則包一次,然后可以將其添加到新的項(xiàng)目中。創(chuàng)建一次并稍后重用。我們要?jiǎng)?chuàng)建一個(gè)新的屬性表,其中包含編譯器和鏈接器需要知道的所有規(guī)則。當(dāng)然,我們將需要一個(gè)單獨(dú)的調(diào)試和發(fā)布版本。啟動(dòng)調(diào)試一,如下圖所示:

AddNewPropertySheet (1)

使用OpenCV_Debug名稱。然后選擇表格右鍵 - >屬性。在下面我將展示在本地設(shè)置OpenCV規(guī)則,因?yàn)槲野l(fā)現(xiàn)不必用不使用自定義規(guī)則來污染項(xiàng)目。去C ++組通用條目和*“其他包含目錄”*添加OpenCV包含的路徑。如果沒有*“C / C ++”*組,則應(yīng)該將任何.c / .cpp文件添加到項(xiàng)目中。

$(OPENCV_DIR)\include

PropertySheetOpenCVInclude


當(dāng)添加第三方庫設(shè)置時(shí),通常使用環(huán)境變量背后的權(quán)力是一個(gè)好主意。OpenCV庫的完整位置可能會(huì)在每個(gè)系統(tǒng)上更改。此外,由于某些原因,您甚至可能會(huì)自動(dòng)移動(dòng)安裝目錄。如果您在屬性表中給出明確的路徑,那么當(dāng)您將其進(jìn)一步傳遞給具有不同OpenCV安裝路徑的其他人時(shí),您的項(xiàng)目將最終不起作用。此外,修復(fù)這將需要手動(dòng)修改每個(gè)顯式路徑。一個(gè)更優(yōu)雅的解決方案是使用環(huán)境變量。任何您放入括號(hào)內(nèi)的任何以美元符號(hào)開頭的內(nèi)容將在運(yùn)行時(shí)替換為當(dāng)前環(huán)境變量值。這里介紹了我們以前的教程中已經(jīng)做出的環(huán)境變量設(shè)置OpenCV環(huán)境變量并將其添加到系統(tǒng)路徑。

下一步去鏈接器 - >常規(guī)下的*“附加庫目錄”*添加libs目錄:

$(OPENCV_DIR)\ LIB

PropertySheetOpenCVLib

然后,您需要指定鏈接器應(yīng)查看的庫。要執(zhí)行此操作,請(qǐng)轉(zhuǎn)到鏈接器 - >輸入,并在*“附加依賴關(guān)系”*條目下添加要使用的所有模塊的名稱:

PropertySheetOpenCVLibrariesDebug

the libraries的命名如下:

opencv_(The Name of the module)(The version Number of the library you use)d.lib

完整列表,最新版本將包含:

opencv_calib3d300d.lib
opencv_core300d.lib
opencv_features2d300d.lib
opencv_flann300d.lib
opencv_highgui300d.lib
opencv_imgcodecs300d.lib
opencv_imgproc300d.lib
opencv_ml300d.lib
opencv_objdetect300d.lib
opencv_photo300d.lib
opencv_shape300d.lib
opencv_stitching300d.lib
opencv_superres300d.lib
opencv_ts300d.lib
opencv_video300d.lib
opencv_videoio300d.lib
opencv_videostab300d.lib

最后的字母d表示這些是調(diào)試所需的庫?,F(xiàn)在點(diǎn)擊確定保存,并在發(fā)布規(guī)則部分內(nèi)使用新的屬性。確保省略庫名稱中的d個(gè)字母,并保存屬性表與其上方的保存圖標(biāo)。

PropertySheetOpenCVLibrariesRelease

您可以在項(xiàng)目目錄中找到屬性表。在這一點(diǎn)上,無論何時(shí)創(chuàng)建OpenCV項(xiàng)目,將它們備份到一些特殊的目錄中,以便在將來隨時(shí)隨地掌握它,這是一個(gè)明智的決定。請(qǐng)注意,對(duì)于Visual Studio 2010,文件擴(kuò)展名是道具,而在2008年,這是vsprops

PropertySheetInsideFolder

下一次當(dāng)您創(chuàng)建一個(gè)新的OpenCV項(xiàng)目時(shí),只需使用屬性管理器中的“添加現(xiàn)有屬性表...”菜單條目即可輕松添加OpenCV構(gòu)建規(guī)則。

PropertyPageAddExisting

The global method

如果您發(fā)現(xiàn)將屬性頁面添加到每個(gè)項(xiàng)目中都太麻煩,您還可以將此規(guī)則添加到*“全局屬性頁”*。但是,這僅適用于附加的include和library目錄。使用的庫的名稱仍然需要通過使用例如:屬性頁手動(dòng)指定。

在Visual Studio 2008中,您可以在“工具” - >“選項(xiàng)” - >“項(xiàng)目和解決方案” - >“VC ++目錄”下找到該文件。

VCDirectories2008

在Visual Studio 2010中,它已被移動(dòng)到全局屬性表,該屬性表會(huì)自動(dòng)添加到您創(chuàng)建的每個(gè)項(xiàng)目中:

VCDirectories2010

該過程與本地方法的情況相同。只需使用環(huán)境變量OPENCV_DIR添加包含目錄。

Test it!

現(xiàn)在嘗試下載我們的小測(cè)試源代碼,或從OpenCV源的示例代碼文件夾中獲取。將其添加到您的項(xiàng)目并構(gòu)建它。以下是其內(nèi)容:

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main( int argc, char** argv )
{
    if( argc != 2)
    {
     cout <<" Usage: display_image ImageToLoadAndDisplay" << endl;
     return -1;
    }
    Mat image;
    image = imread(argv[1], IMREAD_COLOR); // Read the file
    if( image.empty() ) // Check for invalid input
    {
        cout << "Could not open or find the image" << std::endl ;
        return -1;
    }
    namedWindow( "Display window", WINDOW_AUTOSIZE ); // Create a window for display.
    imshow( "Display window", image ); // Show our image inside it.
    waitKey(0); // Wait for a keystroke in the window
    return 0;
}

或者使用以下代碼:

// Video Image PSNR and SSIM
#include <iostream> // for standard I/O
#include <string>   // for strings
#include <iomanip>  // for controlling float print precision
#include <sstream>  // string to number conversion

#include <opencv2/imgproc/imgproc.hpp>  // Gaussian Blur
#include <opencv2/core/core.hpp>        // Basic OpenCV structures (cv::Mat, Scalar)
#include <opencv2/highgui/highgui.hpp>  // OpenCV window I/O

using namespace std;
using namespace cv;

double getPSNR ( const Mat& I1, const Mat& I2);
Scalar getMSSIM( const Mat& I1, const Mat& I2);

void help()
{
    cout
        << "\n--------------------------------------------------------------------------" << endl
        << "This program shows how to read a video file with OpenCV. In addition, it tests the"
        << " similarity of two input videos first with PSNR, and for the frames below a PSNR "  << endl
        << "trigger value, also with MSSIM."<< endl
        << "Usage:"                                                                       << endl
        << "./video-source referenceVideo useCaseTestVideo PSNR_Trigger_Value Wait_Between_Frames " << endl
        << "--------------------------------------------------------------------------"   << endl
        << endl;
}
int main(int argc, char *argv[], char *window_name)
{
    help();
    if (argc != 5)
    {
        cout << "Not enough parameters" << endl;
        return -1;
    }
    stringstream conv;

    const string sourceReference = argv[1],sourceCompareWith = argv[2];
    int psnrTriggerValue, delay;
    conv << argv[3] << argv[4];       // put in the strings
    conv >> psnrTriggerValue >> delay;// take out the numbers

    char c;
    int frameNum = -1;          // Frame counter

    VideoCapture captRefrnc(sourceReference),
                 captUndTst(sourceCompareWith);

    if ( !captRefrnc.isOpened())
    {
        cout  << "Could not open reference " << sourceReference << endl;
        return -1;
    }

    if( !captUndTst.isOpened())
    {
        cout  << "Could not open case test " << sourceCompareWith << endl;
        return -1;
    }

    Size refS = Size((int) captRefrnc.get(CV_CAP_PROP_FRAME_WIDTH),
                     (int) captRefrnc.get(CV_CAP_PROP_FRAME_HEIGHT)),
         uTSi = Size((int) captUndTst.get(CV_CAP_PROP_FRAME_WIDTH),
                     (int) captUndTst.get(CV_CAP_PROP_FRAME_HEIGHT));

    if (refS != uTSi)
    {
        cout << "Inputs have different size!!! Closing." << endl;
        return -1;
    }

    const char* WIN_UT = "Under Test";
    const char* WIN_RF = "Reference";

    // Windows
            namedWindow(WIN_RF, CV_WINDOW_AUTOSIZE );
            namedWindow(WIN_UT, CV_WINDOW_AUTOSIZE );
            cvMoveWindow(WIN_RF, 400       ,            0);      //750,  2 (bernat =0)
            cvMoveWindow(WIN_UT, refS.width,            0);      //1500, 2

    cout << "Frame resolution: Width=" << refS.width << "  Height=" << refS.height
         << " of nr#: " << captRefrnc.get(CV_CAP_PROP_FRAME_COUNT) << endl;

    cout << "PSNR trigger value " <<
          setiosflags(ios::fixed) << setprecision(3) << psnrTriggerValue << endl;

    Mat frameReference, frameUnderTest;
    double psnrV;
    Scalar mssimV;

    while( true) //Show the image captured in the window and repeat
    {
        captRefrnc >> frameReference;
        captUndTst >> frameUnderTest;

        if( frameReference.empty()  || frameUnderTest.empty())
        {
            cout << " < < <  Game over!  > > > ";
            break;
        }

        ++frameNum;
        cout <<"Frame:" << frameNum;

        ///////////////////////////////// PSNR ////////////////////////////////////////////////////
        psnrV = getPSNR(frameReference,frameUnderTest);                 //get PSNR
        cout << setiosflags(ios::fixed) << setprecision(3) << psnrV << "dB";

        //////////////////////////////////// MSSIM /////////////////////////////////////////////////
        if (psnrV < psnrTriggerValue)
        {
            mssimV = getMSSIM(frameReference,frameUnderTest);

            cout << " MSSIM: "
                 << "R" << setiosflags(ios::fixed) << setprecision(3) << mssimV.val[2] * 100
                 << "G" << setiosflags(ios::fixed) << setprecision(3) << mssimV.val[1] * 100
                 << "B" << setiosflags(ios::fixed) << setprecision(3) << mssimV.val[0] * 100;
        }

       cout << endl;

        ////////////////////////////////// Show Image /////////////////////////////////////////////
        imshow( WIN_RF, frameReference);
        imshow( WIN_UT, frameUnderTest);

        c = cvWaitKey(delay);
        if (c == 27) break;
    }

    return 0;
}

double getPSNR(const Mat& I1, const Mat& I2)
{
    Mat s1;
    absdiff(I1, I2, s1);       // |I1 - I2|
    s1.convertTo(s1, CV_32F);  // cannot make a square on 8 bits
    s1 = s1.mul(s1);           // |I1 - I2|^2

    Scalar s = sum(s1);         // sum elements per channel

    double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels

    if( sse <= 1e-10) // for small values return zero
        return 0;
    else
    {
        double  mse =sse /(double)(I1.channels() * I1.total());
        double psnr = 10.0*log10((255*255)/mse);
        return psnr;
    }
}

Scalar getMSSIM( const Mat& i1, const Mat& i2)
{
    const double C1 = 6.5025, C2 = 58.5225;
    /***************************** INITS **********************************/
    int d     = CV_32F;

    Mat I1, I2;
    i1.convertTo(I1, d);           // cannot calculate on one byte large values
    i2.convertTo(I2, d);

    Mat I2_2   = I2.mul(I2);        // I2^2
    Mat I1_2   = I1.mul(I1);        // I1^2
    Mat I1_I2  = I1.mul(I2);        // I1 * I2

    /*************************** END INITS **********************************/

    Mat mu1, mu2;   // PRELIMINARY COMPUTING
    GaussianBlur(I1, mu1, Size(11, 11), 1.5);
    GaussianBlur(I2, mu2, Size(11, 11), 1.5);

    Mat mu1_2   =   mu1.mul(mu1);
    Mat mu2_2   =   mu2.mul(mu2);
    Mat mu1_mu2 =   mu1.mul(mu2);

    Mat sigma1_2, sigma2_2, sigma12;

    GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
    sigma1_2 -= mu1_2;

    GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
    sigma2_2 -= mu2_2;

    GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);
    sigma12 -= mu1_mu2;

    ///////////////////////////////// FORMULA ////////////////////////////////
    Mat t1, t2, t3;

    t1 = 2 * mu1_mu2 + C1;
    t2 = 2 * sigma12 + C2;
    t3 = t1.mul(t2);              // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))

    t1 = mu1_2 + mu2_2 + C1;
    t2 = sigma1_2 + sigma2_2 + C2;
    t1 = t1.mul(t2);               // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))

    Mat ssim_map;
    divide(t3, t1, ssim_map);      // ssim_map =  t3./t1;

    Scalar mssim = mean( ssim_map ); // mssim = average of ssim map
    return mssim;
}

您可以從兩個(gè)地方啟動(dòng)Visual Studio構(gòu)建。從IDE(鍵盤組合:Control-F5)或通過導(dǎo)航到您的構(gòu)建目錄并雙擊啟動(dòng)應(yīng)用程序。抓住的是這兩個(gè)不一樣。當(dāng)您從IDE啟動(dòng)它的當(dāng)前工作目錄是項(xiàng)目目錄,否則它是應(yīng)用程序文件當(dāng)前的文件夾(通常是您的構(gòu)建目錄)。此外,如果從IDE啟動(dòng),控制臺(tái)窗口一旦完成就不會(huì)關(guān)閉。它會(huì)等待你的擊鍵。

當(dāng)您在代碼中打開代碼并保存命令時(shí),這一點(diǎn)很重要。您的資源將相對(duì)于您的工作目錄保存(并查詢打開!!!)。這是除非您給出一個(gè)完整的,顯式的路徑作為I / O功能的參數(shù)。在上面的代碼中,我們打開了OpenCV標(biāo)志。啟動(dòng)應(yīng)用程序之前,請(qǐng)確保將映像文件放在當(dāng)前的工作目錄中。修改代碼中的圖像文件名,以便在其他圖像上進(jìn)行嘗試。運(yùn)行它并且voá:

SuccessVisualStudioWindows

Visual Studio的命令行參數(shù)

在我們將來的一些教程中,您將看到程序主輸入法將通過給出一個(gè)運(yùn)行時(shí)參數(shù)。為此,您可以啟動(dòng)命令窗口(在開始菜單中為cmd + Enter),導(dǎo)航到可執(zhí)行文件并使用參數(shù)啟動(dòng)它。所以例如在我的上層項(xiàng)目的情況下,這將是:

D:
CD OpenCV \ MySolutionName \ Release
MySolutionName.exe exampleImage.jpg

在這里,我首先改變了我的驅(qū)動(dòng)器(如果您的項(xiàng)目不在OS本地驅(qū)動(dòng)器上),則導(dǎo)航到我的項(xiàng)目并以示例圖像參數(shù)啟動(dòng)它。而在Linux系統(tǒng)下,常見的是微軟Windows上的控制臺(tái)窗口,很多人幾乎從不使用它。此外,在測(cè)試您的應(yīng)用程序時(shí),一次又一次添加相同的參數(shù),這在某種程度上是一個(gè)繁瑣的任務(wù)。幸運(yùn)的是,在Visual Studio中有一個(gè)菜單可以自動(dòng)化所有這些:

VisualStudioCommandLineArguments

在這里指定輸入的名稱,當(dāng)您從Visual Studio環(huán)境啟動(dòng)應(yīng)用程序時(shí),您將自動(dòng)參數(shù)傳遞。在下一個(gè)介紹性教程中,您將看到對(duì)源代碼較高的深入解釋:加載和顯示圖像。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)