我們經常遇到這樣的需求:我們在VS寫好的程序,需要在一個沒有裝opencv甚至沒有裝vs的電腦下運行,跑出效果。比如,你在你的電腦用opencv+vs2015寫出一個程序,然后老師叫你把程序發給他,他要看看功能實現的怎么樣。老師的電腦肯定沒有整套的開發環境的,如果你想只把代碼發給他,讓他自己編譯,肯定會出現問題。所以,我們需要掌握如何生成一個不依賴開發環境的exe的方法。
下面將以一個實際例子說明如何生成一個不依賴開發環境的exe的方法。
比如我現在在VS2015下實現了一個圖像拼接功能的程序
#include "highgui/highgui.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "opencv2/legacy/legacy.hpp"
#include <iostream>
using namespace cv;
using namespace std;
void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst);
typedef struct
{
Point2f left_top;
Point2f left_bottom;
Point2f right_top;
Point2f right_bottom;
}four_corners_t;
four_corners_t corners;
void CalcCorners(const Mat& H, const Mat& src)
{
double v2[] = { 0, 0, 1 };//左上角
double v1[3];//變換后的坐標值
Mat V2 = Mat(3, 1, CV_64FC1, v2); //列向量
Mat V1 = Mat(3, 1, CV_64FC1, v1); //列向量
V1 = H * V2;
//左上角(0,0,1)
cout << "V2: " << V2 << endl;
cout << "V1: " << V1 << endl;
corners.left_top.x = v1[0] / v1[2];
corners.left_top.y = v1[1] / v1[2];
//左下角(0,src.rows,1)
v2[0] = 0;
v2[1] = src.rows;
v2[2] = 1;
V2 = Mat(3, 1, CV_64FC1, v2); //列向量
V1 = Mat(3, 1, CV_64FC1, v1); //列向量
V1 = H * V2;
corners.left_bottom.x = v1[0] / v1[2];
corners.left_bottom.y = v1[1] / v1[2];
//右上角(src.cols,0,1)
v2[0] = src.cols;
v2[1] = 0;
v2[2] = 1;
V2 = Mat(3, 1, CV_64FC1, v2); //列向量
V1 = Mat(3, 1, CV_64FC1, v1); //列向量
V1 = H * V2;
corners.right_top.x = v1[0] / v1[2];
corners.right_top.y = v1[1] / v1[2];
//右下角(src.cols,src.rows,1)
v2[0] = src.cols;
v2[1] = src.rows;
v2[2] = 1;
V2 = Mat(3, 1, CV_64FC1, v2); //列向量
V1 = Mat(3, 1, CV_64FC1, v1); //列向量
V1 = H * V2;
corners.right_bottom.x = v1[0] / v1[2];
corners.right_bottom.y = v1[1] / v1[2];
}
int main(int argc, char *argv[])
{
Mat image01 = imread(".\\src_pic\\right.jpg", 1); //右圖
Mat image02 = imread(".\\src_pic\\left.jpg", 1); //左圖
imshow("p2", image01);
imshow("p1", image02);
//灰度圖轉換
Mat image1, image2;
cvtColor(image01, image1, CV_RGB2GRAY);
cvtColor(image02, image2, CV_RGB2GRAY);
//提取特征點
SurfFeatureDetector surfDetector(800); // 海塞矩陣閾值,在這里調整精度,值越大點越少,越精准
vector<KeyPoint> keyPoint1, keyPoint2;
surfDetector.detect(image1, keyPoint1);
surfDetector.detect(image2, keyPoint2);
//特征點描述,為下邊的特征點匹配做准備
SurfDescriptorExtractor SurfDescriptor;
Mat imageDesc1, imageDesc2;
SurfDescriptor.compute(image1, keyPoint1, imageDesc1);
SurfDescriptor.compute(image2, keyPoint2, imageDesc2);
//獲得匹配特征點,並提取最優配對
FlannBasedMatcher matcher;
vector<DMatch> matchePoints;
matcher.match(imageDesc1, imageDesc2, matchePoints, Mat());
cout << "total match points: " << matchePoints.size() << endl;
// sort(matchePoints.begin(), matchePoints.end()); //特征點排序
Mat img_match;
drawMatches(image01, keyPoint1, image02, keyPoint2, matchePoints, img_match);
imshow("match points",img_match);
//獲取排在前N個的最優匹配特征點
vector<Point2f> imagePoints1, imagePoints2;
for (int i = 0; i<matchePoints.size(); i++)
{
imagePoints1.push_back(keyPoint1[matchePoints[i].queryIdx].pt);
imagePoints2.push_back(keyPoint2[matchePoints[i].trainIdx].pt);
}
//獲取圖像1到圖像2的投影映射矩陣 尺寸為3*3
Mat homo = findHomography(imagePoints1, imagePoints2, CV_RANSAC);
////也可以使用getPerspectiveTransform方法獲得透視變換矩陣,不過要求只能有4個點,效果稍差
//Mat homo=getPerspectiveTransform(imagePoints1,imagePoints2);
cout << "變換矩陣為:\n" << homo << endl << endl; //輸出映射矩陣
//計算配准圖的四個頂點坐標
CalcCorners(homo, image01);
cout << "left_top:" << corners.left_top << endl;
cout << "left_bottom:" << corners.left_bottom << endl;
cout << "right_top:" << corners.right_top << endl;
cout << "right_bottom:" << corners.right_bottom << endl;
//圖像配准
Mat imageTransform1, imageTransform2;
warpPerspective(image01, imageTransform1, homo, Size(MAX(corners.right_top.x, corners.right_bottom.x),image02.rows));
//warpPerspective(image01, imageTransform2, adjustMat*homo, Size(image02.cols*1.3, image02.rows*1.8));
imshow("直接經過透視矩陣變換", imageTransform1);
imwrite(".\\dst_pic\\trans1.jpg", imageTransform1);
//創建拼接后的圖,需提前計算圖的大小
int dst_width = imageTransform1.cols; //取最右點的長度為拼接圖的長度
int dst_height = image02.rows;
Mat dst(dst_height, dst_width,CV_8UC3);
dst.setTo(0);
imageTransform1.copyTo(dst(Rect(0, 0, imageTransform1.cols, imageTransform1.rows)));
image02.copyTo(dst(Rect(0, 0, image02.cols, image02.rows)));
OptimizeSeam(image02, imageTransform1, dst);
imshow("dst", dst);
imwrite(".\\dst_pic\\dst.jpg", dst);
waitKey();
return 0;
}
//優化兩圖的連接處,使得拼接自然
void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst)
{
int start = MIN(corners.left_top.x, corners.left_bottom.x);//開始位置,即重疊區域的左邊界
double processWidth = img1.cols - start;//重疊區域的寬度
int rows = dst.rows;
int cols = img1.cols; //注意,是列數*通道數
double alpha = 1;//img1中像素的權重
for (int i = 0; i < rows; i++)
{
uchar* p = img1.ptr<uchar>(i); //獲取第i行的首地址
uchar* t = trans.ptr<uchar>(i);
uchar* d = dst.ptr<uchar>(i);
for (int j = start; j < cols; j++)
{
if (t[j*3] == 0 && t[j*3+1] == 0 && t[j*3+2] == 0)
{
alpha = 1;
}
else
{
alpha = (processWidth - (j - start)) / processWidth;
}
d[j*3] = p[j*3] * alpha + t[j*3] * (1 - alpha);
d[j*3+1] = p[j*3+1] * alpha + t[j*3+1] * (1 - alpha);
d[j*3+2] = p[j*3+2] * alpha + t[j*3+2] * (1 - alpha);
}
}
}
那么怎樣才可以生成一個不依賴於環境的可執行程序exe呢?
1.選擇release方式
為什么要選擇release而不選擇debug模式?因為debug模式我也嘗試過了,因為debug模式要加入某些vs的debug dll,可能比較難找,就不使用debug模式了,relase模式更為方便。
2.重新生成解決方案
3.找到生成的exe的存放位置
因為我生成的是x64文件,所以就選擇X64。生成X86的就選X86文件夾。
我們選relsease文件夾
發現有四項東西
3.建立自己的文件夾
自己新建一個文件夾(我命名為my_exe),以后所有東西都放這里了。並將上面提到的四項東西拷貝到這里。並根據我們程序寫的讀取圖片和存儲圖片的位置,生成如下的兩個文件夾src_pic和dst_pic。
4.找出opencv dll庫的位置
將里面的東西全選,並拷貝到剛新建的文件夾內。
5.根據程序寫的讀取圖片的位置放入待處理的圖片
6.運行exe文件
這個exe文件是你從vs工程copy過來的那個exe,別弄錯了。
完美運行。
再看看dst_pic文件夾,生成的圖片已經如我們所願存進去了!
可能遇到的問題
在實際操作中可能遇到exe無法執行或者出錯的情況,這時應第一時間查看依賴項是否填寫正確。
因為我們選擇的是release版本,所以依賴項填寫的是不帶d的!這個要確認清楚。