我在這里描述的一切將適用于C\C++ OpenCV 的界面。我從假設你已經(jīng)閱讀并成功完成了Windows中的安裝教程。因此,在您進一步之前,請確保您具有包含OpenCV頭文件和二進制文件的OpenCV目錄,并按照此處所述設置環(huán)境變量設置OpenCV環(huán)境變量并將其添加到系統(tǒng)路徑。
由我們分發(fā)的Microsoft Windows操作系統(tǒng)上的OpenCV庫位于動態(tài)鏈接庫(DLL)中。這些優(yōu)點是,庫的所有內(nèi)容只能在運行時按需加載,并且無數(shù)程序可能使用相同的庫文件。這意味著如果您有十個使用OpenCV庫的應用程序,則不需要為每個應用程序提供每個應用程序。當然,您需要在要運行應用程序的所有系統(tǒng)上安裝OpenCV 的DLL。
另一種方法是使用具有l(wèi)ib擴展名的靜態(tài)庫。您可以通過使用我們的源文件來構(gòu)建它們,如Windows中的“ 安裝”教程中所述。當你使用這個庫將內(nèi)置在您的exe文件。因此,由于某些原因,用戶將無法刪除它們。作為一個缺點,您的應用程序?qū)⑹歉蟮囊粋€,因為它將需要更多的時間來加載它在啟動期間。
要使用OpenCV構(gòu)建應用程序,您需要做兩件事情:
如果使用lib系統(tǒng),則必須設置庫文件所在的路徑,并指定其中的哪一個。在構(gòu)建期間,鏈接器將查找這些庫,并將所有使用的函數(shù)和數(shù)據(jù)結(jié)構(gòu)的定義和實現(xiàn)添加到可執(zhí)行文件中。
如果您使用DLL系統(tǒng),則必須再次指定所有這些,但是由于其他原因。這是一個Microsoft操作系統(tǒng)特定的東西??磥恚溄悠餍枰涝贒LL中哪里可以在運行時搜索數(shù)據(jù)結(jié)構(gòu)或函數(shù)。此信息存儲在lib文件中。不過,它們不是靜態(tài)庫。它們是所謂的導入庫。這就是為什么當你在Windows中制作一些DLL時,你也會得到一些lib擴展庫。很重要的是在運行時只需要DLL。
要將所有這些信息傳遞給Visual Studio IDE,您可以在全局范圍內(nèi)執(zhí)行(所有未來的項目都將獲得此信息)或本地(因此僅適用于當前項目)。全局的優(yōu)勢是你只需要做一次; 但是,所有這些信息都可能不合時宜地聚集所有的項目。在全局的情況下,如何執(zhí)行此操作取決于您使用的Microsoft Visual Studio。有2008年和以前的版本和2010年的做法。在本教程的全局部分中,我將展示主要區(qū)別。
Visual Studio中項目的基礎(chǔ)項目是一個解決方案。解決方案可能包含多個項目。項目是應用程序的構(gòu)建塊。每個項目都會實現(xiàn)一些東西,你將有一個主要的項目,你可以把這個項目的難題放在一起。在許多簡單的應用程序(如許多教程將是)的情況下,您不需要將應用程序分解為模塊。在這些情況下,您的主要項目將是唯一的現(xiàn)有項目?,F(xiàn)在,通過File - > New - > Project菜單選項,在Visual Studio中創(chuàng)建一個新的解決方案。選擇Win32控制臺應用程序作為類型。輸入其名稱并選擇要創(chuàng)建它的路徑。然后在即將到來的對話框中確保創(chuàng)建一個空項目。
每個項目都是與其他項目分開構(gòu)建的。由于這個每個項目都有自己的規(guī)則包。在這個規(guī)則中,包中存儲了IDE需要知道的所有信息來構(gòu)建項目。對于任何應用程序,至少有兩種構(gòu)建模式:發(fā)布和調(diào)試。在調(diào)試有許多功能,存在,因此,你可以找到并解決您的應用程序中更容易錯誤。相比之下,Release是一個優(yōu)化的版本,其目標是使應用程序盡可能快地運行或盡可能小。您可能會認為,這些模式在構(gòu)建期間也需要使用不同的規(guī)則。因此,每個構(gòu)建模式都存在不同的規(guī)則包。這些規(guī)則包在IDE中作為項目屬性調(diào)用,您可以使用Property Manager查看和修改它們。您可以使用View - > Property Pages(對于Visual Studio 2013起,進入View - > Other Windows - > Property Manager)。展開它,您可以看到現(xiàn)有的規(guī)則包(稱為屬性表)。
這些真正有用的東西是您可以創(chuàng)建一個規(guī)則包一次,然后可以將其添加到新的項目中。創(chuàng)建一次并稍后重用。我們要創(chuàng)建一個新的屬性表,其中包含編譯器和鏈接器需要知道的所有規(guī)則。當然,我們將需要一個單獨的調(diào)試和發(fā)布版本。啟動調(diào)試一,如下圖所示:
使用OpenCV_Debug名稱。然后選擇表格右鍵 - >屬性。在下面我將展示在本地設置OpenCV規(guī)則,因為我發(fā)現(xiàn)不必用不使用自定義規(guī)則來污染項目。去C ++組通用條目和*“其他包含目錄”*添加OpenCV包含的路徑。如果沒有*“C / C ++”*組,則應該將任何.c / .cpp文件添加到項目中。
$(OPENCV_DIR)\include
當添加第三方庫設置時,通常使用環(huán)境變量背后的權(quán)力是一個好主意。OpenCV庫的完整位置可能會在每個系統(tǒng)上更改。此外,由于某些原因,您甚至可能會自動移動安裝目錄。如果您在屬性表中給出明確的路徑,那么當您將其進一步傳遞給具有不同OpenCV安裝路徑的其他人時,您的項目將最終不起作用。此外,修復這將需要手動修改每個顯式路徑。一個更優(yōu)雅的解決方案是使用環(huán)境變量。任何您放入括號內(nèi)的任何以美元符號開頭的內(nèi)容將在運行時替換為當前環(huán)境變量值。這里介紹了我們以前的教程中已經(jīng)做出的環(huán)境變量設置OpenCV環(huán)境變量并將其添加到系統(tǒng)路徑。
下一步去鏈接器 - >常規(guī)下的*“附加庫目錄”*添加libs目錄:
$(OPENCV_DIR)\ LIB
然后,您需要指定鏈接器應查看的庫。要執(zhí)行此操作,請轉(zhuǎn)到鏈接器 - >輸入,并在*“附加依賴關(guān)系”*條目下添加要使用的所有模塊的名稱:
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)在點擊確定保存,并在發(fā)布規(guī)則部分內(nèi)使用新的屬性。確保省略庫名稱中的d個字母,并保存屬性表與其上方的保存圖標。
您可以在項目目錄中找到屬性表。在這一點上,無論何時創(chuàng)建OpenCV項目,將它們備份到一些特殊的目錄中,以便在將來隨時隨地掌握它,這是一個明智的決定。請注意,對于Visual Studio 2010,文件擴展名是道具,而在2008年,這是vsprops。
下一次當您創(chuàng)建一個新的OpenCV項目時,只需使用屬性管理器中的“添加現(xiàn)有屬性表...”菜單條目即可輕松添加OpenCV構(gòu)建規(guī)則。
如果您發(fā)現(xiàn)將屬性頁面添加到每個項目中都太麻煩,您還可以將此規(guī)則添加到*“全局屬性頁”*。但是,這僅適用于附加的include和library目錄。使用的庫的名稱仍然需要通過使用例如:屬性頁手動指定。
在Visual Studio 2008中,您可以在“工具” - >“選項” - >“項目和解決方案” - >“VC ++目錄”下找到該文件。
在Visual Studio 2010中,它已被移動到全局屬性表,該屬性表會自動添加到您創(chuàng)建的每個項目中:
該過程與本地方法的情況相同。只需使用環(huán)境變量OPENCV_DIR添加包含目錄。
現(xiàn)在嘗試下載我們的小測試源代碼,或從OpenCV源的示例代碼文件夾中獲取。將其添加到您的項目并構(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;
}
您可以從兩個地方啟動Visual Studio構(gòu)建。從IDE(鍵盤組合:Control-F5)或通過導航到您的構(gòu)建目錄并雙擊啟動應用程序。抓住的是這兩個不一樣。當您從IDE啟動它的當前工作目錄是項目目錄,否則它是應用程序文件當前的文件夾(通常是您的構(gòu)建目錄)。此外,如果從IDE啟動,控制臺窗口一旦完成就不會關(guān)閉。它會等待你的擊鍵。
當您在代碼中打開代碼并保存命令時,這一點很重要。您的資源將相對于您的工作目錄保存(并查詢打開!!!)。這是除非您給出一個完整的,顯式的路徑作為I / O功能的參數(shù)。在上面的代碼中,我們打開了OpenCV標志。啟動應用程序之前,請確保將映像文件放在當前的工作目錄中。修改代碼中的圖像文件名,以便在其他圖像上進行嘗試。運行它并且voá:
在我們將來的一些教程中,您將看到程序主輸入法將通過給出一個運行時參數(shù)。為此,您可以啟動命令窗口(在開始菜單中為cmd + Enter),導航到可執(zhí)行文件并使用參數(shù)啟動它。所以例如在我的上層項目的情況下,這將是:
D:
CD OpenCV \ MySolutionName \ Release
MySolutionName.exe exampleImage.jpg
在這里,我首先改變了我的驅(qū)動器(如果您的項目不在OS本地驅(qū)動器上),則導航到我的項目并以示例圖像參數(shù)啟動它。而在Linux系統(tǒng)下,常見的是微軟Windows上的控制臺窗口,很多人幾乎從不使用它。此外,在測試您的應用程序時,一次又一次添加相同的參數(shù),這在某種程度上是一個繁瑣的任務。幸運的是,在Visual Studio中有一個菜單可以自動化所有這些:
在這里指定輸入的名稱,當您從Visual Studio環(huán)境啟動應用程序時,您將自動參數(shù)傳遞。在下一個介紹性教程中,您將看到對源代碼較高的深入解釋:加載和顯示圖像。
更多建議: