You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1109 lines
31 KiB

#include "topology.h"
#define BKGRNDPIX 0
Mat_<double> TopoFinder::findTopology(bool shallLoad, bool isInOrder)
{
_isInOrder = isInOrder;
if (shallLoad)
{
//! load similarity matrix from disk
return loadSimilarityMat();
}
loadKeyFiles();
clock_t start_time, end_time;
start_time = clock();
cout<<"Building similarity table ...\n";
Mat_<int> similarMat = detectSimilarityByGuiding();
// Mat_<int> similarMat = detectSimilarityOnGlobal();
end_time = clock();
_totalTime = 1000*(end_time-start_time)/CLOCKS_PER_SEC; //! ms
//! write out
string savePath = Utils::baseDir + "topoInfor.txt";
ofstream fout;
fout.open(savePath.c_str(), ios::out);
fout<<_shotNum<<" in "<<_attempNum<<endl;
fout<<_matchTime<<" of "<<_totalTime<<endl;
fout.close();
saveSimilarityMat(similarMat);
return similarMat;
}
Mat_<int> TopoFinder::detectSimilarityOnGlobal()
{
Mat_<int> similarMat = Mat(_imgNum, _imgNum, CV_32SC1, Scalar(0));
for (int i = 0; i < _imgNum-1; i ++)
{
for (int j = i+1; j < _imgNum; j ++)
{
_attempNum ++;
vector<Point2d> pointSet1, pointSet2;
// Utils::loadMatchPts(i,j,pointSet1,pointSet2);
if (featureMatcher(i,j,pointSet1,pointSet2))
{
similarMat(i,j) = pointSet1.size();
similarMat(j,i) = pointSet1.size();
_shotNum ++;
}
}
}
return similarMat;
}
Mat_<int> TopoFinder::detectSimilarityByGuiding()
{
bool isTimeConsecutive = _isInOrder;
_similarityMat = Mat(_imgNum, _imgNum, CV_32SC1, Scalar(0));
_attempMap = Mat::eye(_imgNum, _imgNum, CV_16UC1);
//! find and match main chain, meanwhile edit the similarity table
if (isTimeConsecutive)
{
buildMainChain();
}
else
{
searchMainChain();
}
//! solve the aligning models of each image
Mat_<double> identMatrix = Mat::eye(3,3,CV_64FC1);
_affineMatList.push_back(identMatrix);
Quadra bar;
bar.imgSize = _Ptmatcher->_imgSizeList[_visitOrder0[0].imgNo];
bar.centroid = Point2d(bar.imgSize.width/2, bar.imgSize.height/2);
_projCoordSet.push_back(bar);
cout<<"Detecting potential overlaps ..."<<endl;
for (int i = 1; i < _visitOrder0.size(); i ++)
{
cout<<"No."<<i<<" finding ...";
int curNo = _visitOrder0[i].imgNo;
int refNo = _visitOrder0[i].refNo;
int refIndex = findNodeIndex(refNo);
vector<Point2d> pointSet1, pointSet2;
clock_t st, et;
st = clock();
Utils::loadMatchPts(refNo, curNo, pointSet1, pointSet2);
et = clock();
_matchTime -= (et-st);
Utils::pointTransform(_affineMatList[refIndex], pointSet1);
//! perform initial alignment
Mat_<double> affineMat = findFastAffine(pointSet1, pointSet2);
_affineMatList.push_back(affineMat);
//! record centroid of current image
Quadra bar;
bar.imgSize = _Ptmatcher->_imgSizeList[curNo];
Point2d centroid(bar.imgSize.width/2.0, bar.imgSize.height/2.0);
bar.centroid = Utils::pointTransform(affineMat, centroid);
_projCoordSet.push_back(bar);
//! detect potential overlaps
//! 1) recalculate aligning model; 2) modify centroid; 3) modify similarity table
detectPotentialOverlap(i, pointSet1, pointSet2);
cout<<"-->end!"<<endl;
}
drawTopoNet();
// drawTreeLevel();
// TsaveMosaicImage();
return _similarityMat;
}
Mat_<double> TopoFinder::getGuidingTable()
{
cout<<"Initializing ..."<<endl;
Mat_<double> guidingTable = Mat(_imgNum, _imgNum, CV_64FC1, Scalar(0));
Mat_<int> simiMat = Mat(_imgNum, _imgNum, CV_32SC1, Scalar(0));
for (int i = 0; i < _imgNum-1; i ++)
{
for (int j = i+1; j < _imgNum; j ++)
{
int num = calSimilarNum(i,j);
simiMat(i,j) = num;
simiMat(j,i) = num;
double cost = 6/log(num+50.0);
if (num == 0)
{
cost = -1;
}
guidingTable(i,j) = cost;
guidingTable(j,i) = cost;
}
}
saveSimilarityMat(simiMat);
cout<<"Done!"<<endl;
return guidingTable;
}
Mat_<double> TopoFinder::getGuidingTableP()
{
cout<<"Initializing ..."<<endl;
Mat_<double> guidingTable = Mat(_imgNum, _imgNum, CV_64FC1, Scalar(0));
Mat_<int> simiMat = Mat(_imgNum, _imgNum, CV_32SC1, Scalar(0));
int step = max(1,(_imgNum-1)*_imgNum/20); //! 10%
int n = 0;
for (int i = 0; i < _imgNum-1; i ++)
{
for (int j = i+1; j < _imgNum; j ++)
{
int num = calSimilarNum(i,j);
simiMat(i,j) = num;
simiMat(j,i) = num;
double cost = 6/log(num+50.0);
if (num == 0)
{
cost = -1;
}
guidingTable(i,j) = cost;
guidingTable(j,i) = cost;
n ++;
if (n%step == 0)
{
cout<<10*n/step<<"% ";
}
}
}
cout<<endl;
saveSimilarityMat(simiMat);
cout<<"Done!"<<endl;
return guidingTable;
}
void TopoFinder::buildMainChain()
{
cout<<"Building main chain according to the time consecutive order ..."<<endl;
int refeNo = _imgNum/2;
TreeNode bar(refeNo,-1,0);
_visitOrder0.push_back(bar);
int offset = 1;
while (1)
{
int no1 = refeNo-offset, no2 = refeNo+offset;
TreeNode bar1(no1,no1+1,0), bar2(no2,no2-1,0);
_visitOrder0.push_back(bar1);
_visitOrder0.push_back(bar2);
vector<Point2d> pointSet1, pointSet2;
if (Load_Matches)
{
Utils::loadMatchPts(no1, no1+1, pointSet1, pointSet2);
}
else
{
clock_t st, et;
st = clock();
if (!featureMatcher(no1, no1+1, pointSet1, pointSet2))
{
cout<<no1<<"&"<<no1+1<<"Time consecutive sequence break up!"<<endl;
exit(0);
}
et = clock();
_matchTime += (et-st);
}
_similarityMat(no1, no1+1) = pointSet1.size();
_similarityMat(no1+1, no1) = pointSet1.size();
_attempMap(no1, no1+1) = 1;
_attempMap(no1+1, no1) = 1;
if (Load_Matches)
{
Utils::loadMatchPts(no2, no2-1, pointSet1, pointSet2);
}
else
{
clock_t st, et;
st = clock();
if (!featureMatcher(no2, no2-1, pointSet1, pointSet2))
{
cout<<no2<<"&"<<no2-1<<"Time consecutive sequence break up!"<<endl;
exit(0);
}
et = clock();
_matchTime += (et-st);
}
_similarityMat(no2, no2-1) = pointSet1.size();
_similarityMat(no2-1, no2) = pointSet1.size();
_attempMap(no2, no2-1) = 1;
_attempMap(no2-1, no2) = 1;
offset ++;
if (no1 == 0 || no2 == _imgNum-1)
{
if (no1 > 0)
{
_visitOrder0.push_back(TreeNode(no1-1,no1,0));
if (Load_Matches)
{
Utils::loadMatchPts(no1-1, no1, pointSet1, pointSet2);
}
else
{
clock_t st, et;
st = clock();
if (!featureMatcher(no1-1, no1, pointSet1, pointSet2))
{
cout<<no1-1<<"&"<<no1<<"Time consecutive sequence break up!"<<endl;
exit(0);
}
et = clock();
_matchTime += (et-st);
}
_similarityMat(no1-1, no1) = pointSet1.size();
_similarityMat(no1, no1-1) = pointSet1.size();
_attempMap(no1-1, no1) = 1;
_attempMap(no1, no1-1) = 1;
}
else if (no2 < _imgNum-1)
{
_visitOrder0.push_back(TreeNode(no2+1,no2,0));
if (Load_Matches)
{
Utils::loadMatchPts(no2+1, no2, pointSet1, pointSet2);
}
else
{
clock_t st, et;
st = clock();
if (!featureMatcher(no2+1, no2, pointSet1, pointSet2))
{
cout<<no2<<"&"<<no2+1<<"Time consecutive sequence break up!"<<endl;
exit(0);
}
et = clock();
_matchTime += (et-st);
}
_similarityMat(no2+1, no2) = pointSet1.size();
_similarityMat(no2, no2+1) = pointSet1.size();
_attempMap(no2+1, no2) = 1;
_attempMap(no2, no2+1) = 1;
}
break;
}
}
_attempNum = _imgNum-1;
_shotNum = _imgNum-1;
cout<<"Completed!"<<endl;
}
void TopoFinder::searchMainChain()
{
//! overlapping probability of image pairs
Mat_<double> guidingTable = getGuidingTableP();
int iter = 0, maxIter = max(int(_imgNum*0.2), 20);
while (1)
{
cout<<"Searching main chain ... (attempt: "<<++iter<<")"<<endl;
Mat_<int> imgPairs= Graph::extractMSTree(guidingTable);
int pairNo = 0;
for (pairNo = 0; pairNo < imgPairs.rows; pairNo ++)
{
int no1 = imgPairs(pairNo,0), no2 = imgPairs(pairNo,1);
//! avoiding repeating matching which is done in last iteration
if (_attempMap(no1,no2) != 0)
{
continue;
}
_attempMap(no1,no2) = 1;
_attempMap(no2,no1) = 1;
vector<Point2d> pointSet1, pointSet2;
_attempNum ++;
clock_t st, et;
st = clock();
bool yeah = featureMatcher(no1,no2,pointSet1,pointSet2);
et = clock();
_matchTime += (et-st);
if (yeah)
{
_shotNum ++;
_similarityMat(no1,no2) = pointSet1.size();
_similarityMat(no2,no1) = pointSet1.size();
guidingTable(no1,no2) = 0.0;
guidingTable(no2,no1) = 0.0;
}
else
{
//! matching failed : cost as infinite
guidingTable(no1,no2) = 999;
guidingTable(no2,no1) = 999;
if (iter == maxIter)
{
cout<<"Poor image sequence! exit out."<<endl;
exit(0);
}
cout<<no1<<" Linking "<<no2<<" failed! < built: "<<pairNo+1<<" edges."<<endl;
break;
}
}
if (pairNo == _imgNum-1)
{
break;
}
}
cout<<"Succeed!"<<endl;
Mat_<double> costGraph = Utils::buildCostGraph(_similarityMat);
_visitOrder0 = Graph::FloydForPath(costGraph);
}
void TopoFinder::detectPotentialOverlap(int curIndex, vector<Point2d> &pointSet1, vector<Point2d> &pointSet2)
{
int curRefNo = _visitOrder0[curIndex].refNo;
int curNo = _visitOrder0[curIndex].imgNo;
Point2d iniPos = _projCoordSet[curIndex].centroid;
int width = _projCoordSet[curIndex].imgSize.width;
int height = _projCoordSet[curIndex].imgSize.height;
//! accelerate : build a KD-tree for all centroids and retrieve
bool isGot = false;
for (int i = 0; i < _projCoordSet.size(); i ++)
{
int testNo = _visitOrder0[i].imgNo;
if (_attempMap(curNo,testNo))
{
continue;
}
Quadra testObj = _projCoordSet[i];
double threshold = 0.5*(max(width,height) + max(testObj.imgSize.width, testObj.imgSize.height));
double dist = Utils::calPointDist(iniPos, testObj.centroid);
if (dist > threshold*0.8)
{
continue;
}
_attempNum ++;
vector<Point2d> newPtSet1, newPtSet2;
//! for debug test
if (Load_Matches)
{
if (Utils::loadMatchPts(testNo,curNo,newPtSet1,newPtSet2))
{
_similarityMat(testNo,curNo) = newPtSet1.size();
_similarityMat(curNo,testNo) = newPtSet1.size();
Utils::pointTransform(_affineMatList[i], newPtSet1);
for (int t = 0; t < newPtSet1.size(); t ++)
{
pointSet1.push_back(newPtSet1[t]);
pointSet2.push_back(newPtSet2[t]);
}
_shotNum ++;
isGot = true;
}
}
else
{
if (dist < threshold*0.8)
{
clock_t st, et;
st = clock();
bool yeah = featureMatcher(testNo,curNo,newPtSet1,newPtSet2);
et = clock();
_matchTime += (et-st);
if (yeah)
{
_similarityMat(testNo,curNo) = newPtSet1.size();
_similarityMat(curNo,testNo) = newPtSet1.size();
Utils::pointTransform(_affineMatList[i], newPtSet1);
for (int t = 0; t < newPtSet1.size(); t ++)
{
pointSet1.push_back(newPtSet1[t]);
pointSet2.push_back(newPtSet2[t]);
}
_shotNum ++;
isGot = true;
}
}
//else
//{
// if (_Ptmatcher->tentativeMatcher(testNo,curNo))
// {
// clock_t st, et;
// st = clock();
// bool yeah = _Ptmatcher->featureMatcher(testNo,curNo,newPtSet1,newPtSet2);
// et = clock();
// _matchTime += (et-st);
// _similarityMat(testNo,curNo) = newPtSet1.size();
// _similarityMat(curNo,testNo) = newPtSet1.size();
// Utils::pointTransform(_affineMatList[i], newPtSet1);
// for (int t = 0; t < newPtSet1.size(); t ++)
// {
// pointSet1.push_back(newPtSet1[t]);
// pointSet2.push_back(newPtSet2[t]);
// }
// _shotNum ++;
// isGot = true;
// }
//}
}
}
if (!isGot)
{
return;
}
//! modify the affine model parameter
_affineMatList[curIndex] = findFastAffine(pointSet1, pointSet2);
//! modify the centroid
_projCoordSet[curIndex].centroid = Utils::pointTransform(_affineMatList[curIndex], Point2d(width/2,height/2));
}
int TopoFinder::findNodeIndex(int imgNo)
{
int imgIndex = 0;
for (int i = 0; i < _imgNum; i ++)
{
if (_visitOrder0[i].imgNo == imgNo)
{
imgIndex = i;
}
}
return imgIndex;
}
Mat_<int> TopoFinder::loadSimilarityMat()
{
string filePath = Utils::baseDir + "Cache/similarityMat.txt";
ifstream fin;
fin.open(filePath.c_str(), ios::in);
if (!fin.is_open())
{
cout<<"File not found!\n";
exit(0);
}
int nodeNum = _imgNum;
Mat_<double> similarityMat = Mat(nodeNum, nodeNum, CV_64FC1, Scalar(0));
for (int i = 0; i < nodeNum; i ++)
{
//! avoid to read repeated data
//int offset = sizeof(int)*i;
//fin.seekg(offset, ios::cur);
for (int j = 0; j < nodeNum; j ++)
{
int Ptnum = 0;
fin>>Ptnum;
similarityMat(i,j) = Ptnum;
}
}
fin.close();
return similarityMat;
}
void TopoFinder::saveSimilarityMat(const Mat_<int> &similarityMat)
{
string savePath = Utils::baseDir + "Cache/similarityMat.txt";
ofstream fout;
fout.open(savePath.c_str(), ios::out);
if (!fout.is_open())
{
cout<<"Path not exists!\n";
exit(0);
}
int nodeNum = similarityMat.cols;
// fout<<fixed<<setprecision(2);
for (int i = 0; i < nodeNum; i ++)
{
for (int j = 0; j < nodeNum; j ++)
{
int Ptnum = similarityMat(i,j);
fout<<setw(4)<<Ptnum<<" ";
}
fout<<endl;
}
fout.close();
}
Mat_<double> TopoFinder::findFastAffine(vector<Point2d> pointSet1, vector<Point2d> pointSet2)
{
int step = max(1, int(pointSet1.size()/500));
int pointNum = pointSet1.size()/step;
Mat_<double> affineMat(3, 3, CV_64FC1);
Mat A(2*pointNum, 6, CV_64FC1, Scalar(0));
Mat L(2*pointNum, 1, CV_64FC1);
for (int i = 0; i < pointNum; i ++)
{
double x1 = pointSet1[i*step].x, y1 = pointSet1[i*step].y;
double x2 = pointSet2[i*step].x, y2 = pointSet2[i*step].y;
A.at<double>(i*2,0) = x2; A.at<double>(i*2,1) = y2; A.at<double>(i*2,2) = 1;
A.at<double>(i*2+1,3) = x2; A.at<double>(i*2+1,4) = y2; A.at<double>(i*2+1,5) = 1;
L.at<double>(i*2,0) = x1;
L.at<double>(i*2+1,0) = y1;
}
Mat_<double> X = (A.t()*A).inv()*(A.t()*L);
affineMat(0,0) = X(0); affineMat(0,1) = X(1); affineMat(0,2) = X(2);
affineMat(1,0) = X(3); affineMat(1,1) = X(4); affineMat(1,2) = X(5);
affineMat(2,0) = 0; affineMat(2,1) = 0; affineMat(2,2) = 1;
double var = 0;
//Utils::pointTransform(affineMat, pointSet2);
//for (int i = 0; i < pointNum; i ++)
//{
// double bias = (pointSet1[i].x-pointSet2[i].x)*(pointSet1[i].x-pointSet2[i].x) +
// (pointSet1[i].y-pointSet2[i].y)*(pointSet1[i].y-pointSet2[i].y);
// var += bias;
//}
//var /= (pointNum-6);
return affineMat;
}
void TopoFinder::loadKeyFiles()
{
cout<<"Loading key files ..."<<endl;
for (int i = 0; i < _imgNum; i ++)
{
int imgIndex = i;
char filePath[1024];
sprintf(filePath, "Cache/keyPtfile/keys%d", imgIndex);
string filePath_ = Utils::baseDir + string(filePath);
cout<<"key "<<i<<endl;
Keys bar;
vector<int> subIndexList; //! features of the target ocatve
FILE *fin = fopen(filePath_.c_str(), "r");
int PtNum = 0;
fscanf(fin, "%d", &PtNum);
for (int j = 0; j < PtNum; j ++)
{
Point2d point;
int octave = 0;
fscanf(fin, "%lf%lf%d", &point.x, &point.y, &octave);
bar.pts.push_back(point);
if (octave == Target_Octave)
{
subIndexList.push_back(j);
}
}
if (subIndexList.size() == 0) //! special case
{
//cout<<"Waring : the feature subset of image "<<i<<" is empty!"<<endl;
for (int j = 0; j < PtNum; j ++)
{
Point2d point;
int octave = 0;
fscanf(fin, "%lf%lf%d", &point.x, &point.y, &octave);
bar.pts.push_back(point);
int Gesus = max(0,Target_Octave-1);
if (octave == Gesus)
{
subIndexList.push_back(j);
}
}
}
Mat descriptors = Mat(PtNum, 64, CV_32FC1, Scalar(0));
int cnt = 0;
for (int j = 0; j < PtNum; j ++) //write feature descriptor data
{
for (int k = 0; k < 64; k ++)
{
float temp;
fscanf(fin, "%f", &temp);
descriptors.at<float>(j,k) = temp;
}
}
bar.descriptors = descriptors;
fclose(fin);
_keyList.push_back(bar);
_subKeyIndexList.push_back(subIndexList);
}
cout<<"Completed!"<<endl;
}
bool TopoFinder::featureMatcher(int imgIndex1, int imgIndex2, vector<Point2d> &pointSet1, vector<Point2d> &pointSet2)
{
pointSet1.clear();
pointSet2.clear();
vector<Point2d> keyPts1, keyPts2;
keyPts1 = _keyList[imgIndex1].pts;
keyPts2 = _keyList[imgIndex2].pts;
Mat descriptors1, descriptors2;
descriptors1 = _keyList[imgIndex1].descriptors;
descriptors2 = _keyList[imgIndex2].descriptors;
// Matching descriptor vectors using FLANN matcher
vector<DMatch> m_Matches;
FlannBasedMatcher matcher;
vector<vector<DMatch>> knnmatches;
int num1 = keyPts1.size(), num2 = keyPts2.size();
int kn = min(min(num1, num2), 5);
matcher.knnMatch(descriptors1, descriptors2, knnmatches, kn);
int i, j;
double minimaDsit = 99999;
for (i = 0; i < knnmatches.size(); i ++)
{
double dist = knnmatches[i][1].distance;
if (dist < minimaDsit)
{
minimaDsit = dist;
}
}
double fitedThreshold = minimaDsit * 5;
int keypointsize = knnmatches.size();
for (i = 0; i < keypointsize; i ++)
{
const DMatch nearDist1 = knnmatches[i][0];
const DMatch nearDist2 = knnmatches[i][1];
double distanceRatio = nearDist1.distance / nearDist2.distance;
if (nearDist1.distance < fitedThreshold && distanceRatio < 0.7)
{
m_Matches.push_back(nearDist1);
}
}
vector<Point2d> iniPts1, iniPts2;
for (i = 0; i < m_Matches.size(); i ++) //get initial match pairs
{
int queryIndex = m_Matches[i].queryIdx;
int trainIndex = m_Matches[i].trainIdx;
Point2d tempPt1 = keyPts1[queryIndex];
Point2d tempPt2 = keyPts2[trainIndex];
iniPts1.push_back(tempPt1);
iniPts2.push_back(tempPt2);
}
if (iniPts1.size() < 10)
{
return false;
}
vector<uchar> status;
//! utilize epi-polar geometry constraints to delete missing matches
Mat Fmatrix = findFundamentalMat(iniPts1, iniPts2, CV_RANSAC, 1.5, 0.99, status);
for (i = 0; i < status.size(); i ++)
{
if (status[i] == 1)
{
pointSet1.push_back(iniPts1[i]);
pointSet2.push_back(iniPts2[i]);
}
}
if (pointSet1.size() < 10)
{
return false;
}
Mat homoMat = findHomography(iniPts2, iniPts1, CV_RANSAC, 2.5); //! Pt1 = homoMat*Pt2
vector<Point2d> goodPts1, goodPts2;
for (i = 0; i < pointSet1.size(); i ++) //mean value
{
Point2d warpedPt;
Utils::pointTransform(homoMat, pointSet2[i], warpedPt);
double dist = 0;
dist = sqrt((warpedPt.x-pointSet1[i].x)*(warpedPt.x-pointSet1[i].x) + (warpedPt.y-pointSet1[i].y)*(warpedPt.y-pointSet1[i].y));
if (dist < 2.0)
{
goodPts1.push_back(pointSet1[i]);
goodPts2.push_back(pointSet2[i]);
}
}
pointSet1 = goodPts1;
pointSet2 = goodPts2;
if (pointSet1.size() < 10) //! modify as 20
{
return false;
}
cout<<"Image "<<imgIndex1<<" and image "<<imgIndex2<<" matched "<<pointSet1.size()<<" points"<<endl;
_Ptmatcher->saveMatchPts(imgIndex1, imgIndex2, pointSet1, pointSet2);
// _Ptmatcher->drawMatches(imgIndex1, imgIndex2, pointSet1, pointSet2);
return true;
}
int TopoFinder::calSimilarNum(int imgIndex1, int imgIndex2)
{
vector<Point2d> orgKeyPts1 = _keyList[imgIndex1].pts;
vector<Point2d> orgKeyPts2 = _keyList[imgIndex2].pts;
Mat orgDescriptors1, orgDescriptors2;
orgDescriptors1 = _keyList[imgIndex1].descriptors;
orgDescriptors2 = _keyList[imgIndex2].descriptors;
vector<Point2d> keyPts1, keyPts2;
Mat descriptors1, descriptors2;
vector<int> subSet1 = _subKeyIndexList[imgIndex1], subSet2 = _subKeyIndexList[imgIndex2];
int realNum1 = subSet1.size(), realNum2 = subSet2.size();
//! sample subset 1
for (int i = 0; i < realNum1; i ++)
{
int no = subSet1[i];
keyPts1.push_back(orgKeyPts1[no]);
}
descriptors1 = Mat(realNum1, 64, CV_32FC1);
for (int i = 0; i < realNum1; i ++)
{
int no = subSet1[i];
orgDescriptors1.row(no).copyTo(descriptors1.row(i));
}
//! sample subset 2
for (int i = 0; i < realNum2; i ++)
{
int no = subSet2[i];
keyPts2.push_back(orgKeyPts2[no]);
}
descriptors2 = Mat(realNum2, 64, CV_32FC1);
for (int i = 0; i < realNum2; i ++)
{
int no = subSet2[i];
orgDescriptors2.row(no).copyTo(descriptors2.row(i));
}
// Matching descriptor vectors using FLANN matcher
vector<DMatch> m_Matches;
FlannBasedMatcher matcher;
vector<vector<DMatch>> knnmatches;
int num1 = keyPts1.size(), num2 = keyPts2.size();
int kn = min(min(num1, num2), 5);
matcher.knnMatch(descriptors1, descriptors2, knnmatches, kn);
double minimaDsit = 99999;
for (int i = 0; i < knnmatches.size(); i ++)
{
double dist = knnmatches[i][0].distance;
if (dist < minimaDsit)
{
minimaDsit = dist;
}
}
double fitedThreshold = minimaDsit * 5;
int keypointsize = knnmatches.size();
for (int i = 0; i < keypointsize; i ++)
{
const DMatch nearDist1 = knnmatches[i][0];
const DMatch nearDist2 = knnmatches[i][1];
double distanceRatio = nearDist1.distance / nearDist2.distance;
if (nearDist1.distance < fitedThreshold && distanceRatio < 0.7)
{
m_Matches.push_back(nearDist1);
}
}
int num = m_Matches.size();
return num;
}
//! ========= temperate funcs ===========
void TopoFinder::drawTopoNet()
{
int i, j, k;
int minX = 999, minY = 999, maxX = 0, maxY = 0;
for (i = 0; i < _projCoordSet.size(); i ++)
{
Point2d tmpPt = _projCoordSet[i].centroid;
int x = int(fabs(tmpPt.x)+1)*(tmpPt.x < 0 ? -1 : 1);
int y = int(fabs(tmpPt.y)+1)*(tmpPt.y < 0 ? -1 : 1);
if (x > maxX)
maxX = x;
if (x < minX)
minX = x;
if (y > maxY)
maxY = y;
if (y < minY)
minY = y;
}
double width = maxX - minX;
double height = maxY - minY;
int imageRange = 2000; //maximum side_length
int edgeRange = 60;
double cvtScale = imageRange/min(height,width);
int imageRow = height * cvtScale + edgeRange*2; // add an edge space of 20 pixel
int imageCol = width * cvtScale + edgeRange*2;
Mat displayPlane(imageRow, imageCol, CV_8UC3, Scalar(255,255,255));
CvFont font;
double hScale = 1;
double vScale = 1;
cvInitFont(&font,CV_FONT_HERSHEY_PLAIN, hScale,vScale,0,1); //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
vector<Point2i> dotPtList;
for (i = 0; i < _visitOrder0.size(); i ++)
{
Point2d point = _projCoordSet[i].centroid;
int c = int((point.x-minX) * cvtScale + 1) + edgeRange;
int r = int((point.y-minY) * cvtScale + 1) + edgeRange;
dotPtList.push_back(Point2i(c,r));
circle(displayPlane, Point2i(c,r), 24, Scalar(255,0,0), -1);
//circle(displayPlane, Point2i(c,r), 2, Scalar(255,255,0), -1);
int imgNo = _visitOrder0[i].imgNo;
char text[100];
sprintf(text,"%d", imgNo);
Point2i dotPt(c+3, r+3);
// cv::putText(displayPlane, text, dotPt, 2, 1, Scalar(0,0,0));
}
for (i = 0; i < _visitOrder0.size()-1; i ++) //draw all related lines
{
for (j = i+1; j < _visitOrder0.size(); j ++)
{
int nodeNo1 = _visitOrder0[i].imgNo;
int nodeNo2 = _visitOrder0[j].imgNo;
int PtNum = _similarityMat(nodeNo1,nodeNo2);
if (PtNum > 0)
{
Point2i startPt = dotPtList[i];
Point2i endPt = dotPtList[j];
if (PtNum < 100 || 1)
{
line(displayPlane, startPt, endPt, Scalar(128,128,128), 2);
}
else
{
line(displayPlane, startPt, endPt, Scalar(0,255,0), 2);
}
}
}
}
/* for (i = 1; i < _visitOrder0.size(); i ++) //draw the related lines in MST
{
int refNo = _visitOrder0[i].refNo;
int refIndex = findNodeIndex(refNo);
Point2i startPt = dotPtList[i];
Point2i endPt = dotPtList[refIndex];
line(displayPlane, startPt, endPt, Scalar(0,0,255), 3);
}*/
string savePath = Utils::baseDir + "/topoGraph.png";
imwrite(savePath, displayPlane);
cout<<"The topology graph of images is saved!"<<endl;
}
Rect TopoFinder::TsetImageSize()
{
vector<Point2d> marginPtList;
int i, j;
for (i = 0; i < _visitOrder0.size(); i ++)
{
Mat_<double> homoMat = _affineMatList[i];
int curImgNo = _visitOrder0[i].imgNo;
Size imgSize = _Ptmatcher->_imgSizeList[curImgNo];
int height = imgSize.height, width = imgSize.width;
Point2d srcPt00(0,0), srcPt01(width,0), srcPt10(0,height), srcPt11(width,height);
Point2d dstPt00, dstPt01, dstPt10, dstPt11;
Utils::pointTransform(homoMat, srcPt00, dstPt00);
Utils::pointTransform(homoMat, srcPt01, dstPt01);
Utils::pointTransform(homoMat, srcPt10, dstPt10);
Utils::pointTransform(homoMat, srcPt11, dstPt11);
marginPtList.push_back(dstPt00);
marginPtList.push_back(dstPt01);
marginPtList.push_back(dstPt10);
marginPtList.push_back(dstPt11);
}
int minX = 999, minY = 999, maxX = 0, maxY = 0;
for (i = 0; i < marginPtList.size(); i ++)
{
Point2d tmpPt = marginPtList[i];
int x = int(fabs(tmpPt.x)+1)*(tmpPt.x < 0 ? -1 : 1);
int y = int(fabs(tmpPt.y)+1)*(tmpPt.y < 0 ? -1 : 1);
if (x > maxX)
maxX = x;
if (x < minX)
minX = x;
if (y > maxY)
maxY = y;
if (y < minY)
minY = y;
}
Rect mosaicRect;
mosaicRect.x = minX; mosaicRect.y = minY;
mosaicRect.width = maxX-minX+1; mosaicRect.height = maxY-minY+1;
return mosaicRect;
}
void TopoFinder::TsaveMosaicImage()
{
bool shallEach = false;
Rect mosaicRect = TsetImageSize();
int newRow = mosaicRect.height, newCol = mosaicRect.width;
int i, j;
Rect newImgRect;
Mat stitchImage(newRow, newCol, CV_8UC3, Scalar(BKGRNDPIX,BKGRNDPIX,BKGRNDPIX));
for (i = 0; i < _visitOrder0.size(); i ++)
{
int curImgNo = _visitOrder0[i].imgNo;
cout<<"Warping Image: "<<curImgNo<<"..."<<endl;
Mat_<double> homoMat = _affineMatList[i];
Size imgSize = _Ptmatcher->_imgSizeList[curImgNo];
int height = imgSize.height, width = imgSize.width;
Point2d srcPt00(0,0), srcPt01(width,0), srcPt10(0,height), srcPt11(width,height);
Point2d dstPt00, dstPt01, dstPt10, dstPt11;
Utils::pointTransform(homoMat, srcPt00, dstPt00);
Utils::pointTransform(homoMat, srcPt01, dstPt01);
Utils::pointTransform(homoMat, srcPt10, dstPt10);
Utils::pointTransform(homoMat, srcPt11, dstPt11);
double fminX, fminY, fmaxX, fmaxY;
fminX = min(min(dstPt00.x, dstPt01.x), min(dstPt10.x, dstPt11.x));
fmaxX = max(max(dstPt00.x, dstPt01.x), max(dstPt10.x, dstPt11.x));
fminY = min(min(dstPt00.y, dstPt01.y), min(dstPt10.y, dstPt11.y));
fmaxY = max(max(dstPt00.y, dstPt01.y), max(dstPt10.y, dstPt11.y));
int minX, minY, maxX, maxY;
minX = int(fabs(fminX)+1)*(fminX < 0 ? -1 : 1);
maxX = int(fabs(fmaxX)+1)*(fmaxX < 0 ? -1 : 1);
minY = int(fabs(fminY)+1)*(fminY < 0 ? -1 : 1);
maxY = int(fabs(fmaxY)+1)*(fmaxY < 0 ? -1 : 1);
int startX = minX-mosaicRect.x; int endX = startX+maxX-minX;
int startY = minY-mosaicRect.y; int endY = startY+maxY-minY;
int r, c;
Mat warpedImage(newRow, newCol, CV_8UC3, Scalar(BKGRNDPIX,BKGRNDPIX,BKGRNDPIX));
string filePath = _Ptmatcher->_imgNameList[curImgNo];
Mat image = imread(filePath);
Mat_<double> invHomoMat = homoMat.inv();
for (r = startY; r < endY; r ++)
{
for (c = startX; c < endX; c ++)
{
int grayValueR, grayValueG, grayValueB;
Point2d dstPt(c+mosaicRect.x,r+mosaicRect.y), srcPt(0,0);
Utils::pointTransform(invHomoMat, dstPt, srcPt);
int u = int(srcPt.x), v = int(srcPt.y);
if (0 < u && width-1 > u && 0 < v && height-1 > v)
{
int grayValueR1 = BKGRNDPIX, grayValueR2 = BKGRNDPIX;
int grayValueG1 = BKGRNDPIX, grayValueG2 = BKGRNDPIX;
int grayValueB1 = BKGRNDPIX, grayValueB2 = BKGRNDPIX;
//bilinear interpolation
grayValueR1 = (image.at<Vec3b>(v,u)[0]) * (1 - (srcPt.x-u)) + (image.at<Vec3b>(v,u+1)[0]) * (srcPt.x-u);
grayValueG1 = (image.at<Vec3b>(v,u)[1]) * (1 - (srcPt.x-u)) + (image.at<Vec3b>(v,u+1)[1]) * (srcPt.x-u);
grayValueB1 = (image.at<Vec3b>(v,u)[2]) * (1 - (srcPt.x-u)) + (image.at<Vec3b>(v,u+1)[2]) * (srcPt.x-u);
grayValueR2 = (image.at<Vec3b>(v+1,u)[0]) * (1 - (srcPt.x-u)) + (image.at<Vec3b>(v+1,u+1)[0]) * (srcPt.x-u);
grayValueG2 = (image.at<Vec3b>(v+1,u)[1]) * (1 - (srcPt.x-u)) + (image.at<Vec3b>(v+1,u+1)[1]) * (srcPt.x-u);
grayValueB2 = (image.at<Vec3b>(v+1,u)[2]) * (1 - (srcPt.x-u)) + (image.at<Vec3b>(v+1,u+1)[2]) * (srcPt.x-u);
grayValueR = grayValueR1*(1 - (srcPt.y-v)) + grayValueR2*(srcPt.y-v);
grayValueG = grayValueG1*(1 - (srcPt.y-v)) + grayValueG2*(srcPt.y-v);
grayValueB = grayValueB1*(1 - (srcPt.y-v)) + grayValueB2*(srcPt.y-v);
warpedImage.at<Vec3b>(r,c)[0] = grayValueR;
warpedImage.at<Vec3b>(r,c)[1] = grayValueG;
warpedImage.at<Vec3b>(r,c)[2] = grayValueB;
stitchImage.at<Vec3b>(r,c)[0] = grayValueR;
stitchImage.at<Vec3b>(r,c)[1] = grayValueG;
stitchImage.at<Vec3b>(r,c)[2] = grayValueB;
}
}
}
if (!shallEach)
{
continue;
}
char name[512];
sprintf(name,"/Masks/warp%d.png", curImgNo);
string savePath = Utils::baseDir + string(name);
imwrite(savePath, warpedImage);
}
string filePath = Utils::baseDir + "/topoMosaic.png";
imwrite(filePath, stitchImage);
}
void TopoFinder::drawTreeLevel()
{
Mat_<double> costGraph = Utils::buildCostGraph(_similarityMat);
vector<TreeNode> newVisits = Graph::FloydForPath(costGraph);
vector<Point2d> coreLocations;
int i, j, k;
//! update the order of visit list
for (i = 0; i < newVisits.size(); i ++)
{
int imgNo = newVisits[i].imgNo;
int orgIndex = findNodeIndex(imgNo);
coreLocations.push_back(_projCoordSet[orgIndex].centroid);
}
//! label levels of spinning tree
vector<int> groupCusorList;
groupCusorList.push_back(0);
for (int i = 1; i < newVisits.size(); i ++)
{
if (newVisits[i].level != newVisits[i-1].level)
{
groupCusorList.push_back(i);
}
}
int groupNum = groupCusorList.size();
if (groupCusorList[groupNum-1] < _imgNum-1)
{
groupCusorList.push_back(_imgNum-1);
}
int minX = 999, minY = 999, maxX = 0, maxY = 0;
for (i = 0; i < _projCoordSet.size(); i ++)
{
Point2d tmpPt = _projCoordSet[i].centroid;
int x = int(fabs(tmpPt.x)+1)*(tmpPt.x < 0 ? -1 : 1);
int y = int(fabs(tmpPt.y)+1)*(tmpPt.y < 0 ? -1 : 1);
if (x > maxX)
maxX = x;
if (x < minX)
minX = x;
if (y > maxY)
maxY = y;
if (y < minY)
minY = y;
}
double width = maxX - minX;
double height = maxY - minY;
int imageRange = 1000; //maximum side_length
int edgeRange = 30;
double cvtScale = imageRange/min(height,width);
int imageRow = height * cvtScale + edgeRange*2; // add an edge space of 20 pixel
int imageCol = width * cvtScale + edgeRange*2;
Mat displayPlane(imageRow, imageCol, CV_8UC3, Scalar(255,255,255));
//! label aligning group
vector<Point2i> dotPtList;
for (i = 0; i < groupCusorList.size(); i ++)
{
int sIndex = 0, eIndex = 0;
if (i != 0)
{
sIndex = groupCusorList[i-1]+1;
eIndex = groupCusorList[i];
}
int r = rand()%255;
int g = rand()%255;
int b = rand()%255;
for (j = sIndex; j <= eIndex; j ++)
{
Point2d point = coreLocations[j];
int c = int((point.x-minX) * cvtScale + 1) + edgeRange;
int r1 = int((point.y-minY) * cvtScale + 1) + edgeRange;
dotPtList.push_back(Point2i(c,r1));
circle(displayPlane, Point2i(c,r1), 25, Scalar(r,g,b), -1);
}
}
//for (i = 0; i < _imgNum-1; i ++) //draw all related lines
//{
// for (j = i+1; j < _imgNum; j ++)
// {
// int imgNo1 = _visitOrder0[i].imgNo;
// int imgNo2 = _visitOrder0[j].imgNo;
// int PtNum = _similarityMat(imgNo1,imgNo2);
// if (PtNum != 0)
// {
// Point2i startPt = dotPtList[i];
// Point2i endPt = dotPtList[j];
// if (PtNum < 100)
// {
// line(displayPlane, startPt, endPt, Scalar(128,128,128), 1);
// }
// else
// {
// line(displayPlane, startPt, endPt, Scalar(0,255,0), 1);
// }
// }
// }
//}
for (i = 1; i < newVisits.size(); i ++) //draw the related lines in MST
{
int refNo = newVisits[i].refNo;
int refIndex = findNodeIndex(refNo);
Point2i startPt = dotPtList[i];
Point2d refNoPos = _projCoordSet[refIndex].centroid;
int c = int((refNoPos.x-minX) * cvtScale + 1) + edgeRange;
int r1 = int((refNoPos.y-minY) * cvtScale + 1) + edgeRange;
Point2i endPt(c,r1);
line(displayPlane, startPt, endPt, Scalar(0,0,255), 2);
}
string savePath = Utils::baseDir + "/spinningTree.jpg";
imwrite(savePath, displayPlane);
}