|
1.逐行扫描图像,把每一行连续的非零目标像素组成一个序列称为一个块(block),并加下它的起点start、它的终点end以及它所在的行号。 |
|
void fillBlockVectors(const Mat& img,Lanes& lanes){ |
|
for(int i=0; i < img.rows; ++i){ |
|
const uchar* rowData = img.ptr<uchar>(i); |
|
if(rowData[0] != 0){ |
|
lanes.NumOfBlocks++; |
|
lanes.stBlock.push_back(0); |
|
lanes.rowBlock.push_back(i); |
|
lanes.lane_type.push_back(rowData[0]); |
|
} |
|
for(int j=1; j < img.cols; ++j){ |
|
if(rowData[j-1] == 0 && rowData[j] != 0){// new block |
|
lanes.NumOfBlocks++; |
|
lanes.stBlock.push_back(j); |
|
lanes.rowBlock.push_back(i); |
|
lanes.lane_type.push_back(rowData[j]); |
|
}else if(rowData[j-1] != 0 && rowData[j] == 0){//end block:prev node != 0, cur node == 0 |
|
lanes.enBlock.push_back(j-1); |
|
} |
|
} |
|
if(rowData[img.cols-1]){ |
|
lanes.enBlock.push_back(img.cols-1); |
|
}//last col |
|
} |
|
} |
|
团是比小块更小的区域,小块为最初每行填入的块,大块为重叠区域合并过的块。 |
|
2.对于除了第一行(第一次出现块的那一行称为第一行)外的所有行里的小块,如果他与前一行的所有小块都没有重合区域,则给该小块一个新的标号blockLabels,标签行的同时记下该新大块的左上角坐标,以及线的类别,然后新大块与四周近距离大块的标签计入等价对equivalences用于合并;如果它仅与上一行中的一个块有重合,则将上一行的那个块的标签赋给它,同时记录该块的右下角坐标;如果它与上一行的2个以上的块有重叠区域,则给当前块赋一个相连块的最小标号,并将上一行的这几个块写入等价对,说明它们属于一类块。 |
|
void firstPass(Lanes& lanes,int col_offset,int row_offset) |
|
{ |
|
lanes.blockLabels.assign(lanes.NumOfBlocks,0); |
|
int idxLabel = 1; |
|
int curRowIdx = 0; |
|
|
|
int firstBlockOnCur = 0; |
|
int firstBlockOnPre = 0; |
|
int lastBlockOnPre = -1; |
|
for(int i=0;i < lanes.NumOfBlocks;++i){ |
|
if(lanes.rowBlock[i] != curRowIdx){//一行的所有块遍历完,进入重新保存当前行号 |
|
curRowIdx = lanes.rowBlock[i]; // every block row id |
|
firstBlockOnPre = firstBlockOnCur;//上一次遍历的块尾+1 |
|
lastBlockOnPre = i - 1;//新的一行 |
|
firstBlockOnCur = i; |
|
} |
|
for(int j=firstBlockOnPre; j <= lastBlockOnPre; ++j){ |
|
|
|
if (lanes.stBlock[i] <= lanes.enBlock[j] + col_offset && lanes.enBlock[i] >= lanes.stBlock[j] - col_offset /*&& lanes.rowBlock[i] == lanes.rowBlock[j]+1*/) |
|
{//判断是否与上一行左右两侧的其他blockLabels有重合 |
|
if (lanes.blockLabels[i] == 0) // 没有被标号过,1个块有重叠 |
|
{ |
|
lanes.blockLabels[i] = lanes.blockLabels[j]; |
|
lanes.blockLabels_berc[lanes.blockLabels[i]-1][1] = Point(lanes.enBlock[i],lanes.rowBlock[i]); |
|
} |
|
else if (lanes.blockLabels[i] != lanes.blockLabels[j])// 已经被标号,2个以上的块有重叠 |
|
lanes.equivalences.push_back(make_pair(lanes.blockLabels[i], lanes.blockLabels[j])); // 保存等价对 |
|
} |
|
} |
|
if (lanes.blockLabels[i] == 0) // 与前一行+offset的任何block没有重合 |
|
{ |
|
lanes.blockLabels_berc.push_back(vector<Point>(2,Point(lanes.stBlock[i],lanes.rowBlock[i])));//新块左上角坐标 |
|
lanes.blockLabels_type.push_back(lanes.lane_type[i]); |
|
lanes.blockLabels[i] = idxLabel++;//新块 |
|
|
|
int lastBlockSize = lanes.blockLabels_berc.size()-1; |
|
for(int k=0;k<lastBlockSize;++k){//新块与四周近距离块合并 |
|
if(lanes.blockLabels_berc[lastBlockSize][0].y >= lanes.blockLabels_berc[k][1].y-row_offset |
|
&& lanes.blockLabels_berc[lastBlockSize][0].y <= lanes.blockLabels_berc[k][1].y+row_offset |
|
&& lanes.blockLabels_berc[lastBlockSize][0].x >= lanes.blockLabels_berc[k][1].x-col_offset |
|
&& lanes.blockLabels_berc[lastBlockSize][0].x <= lanes.blockLabels_berc[k][1].x+col_offset){ |
|
lanes.equivalences.push_back(make_pair(lanes.blockLabels[i], k+1)); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//统计线的点集 |
|
int maxLabel = *max_element(lanes.blockLabels.begin(),lanes.blockLabels.end()); |
|
lanes.lane_num = maxLabel; |
|
lanes.pts.assign(maxLabel,vector<Point>(0)); |
|
for (int i = 1; i <= lanes.lane_num; ++i) |
|
{ |
|
for(int j=0;j<lanes.blockLabels.size();++j){ |
|
if(lanes.blockLabels[j] == i){//总共有lane_num根线,匹配这个范围内的线,blockLabels值是相同,则放入同一个线的点集中 |
|
for(int k=lanes.stBlock[j];k<=lanes.enBlock[j];++k) |
|
// lanes.pts[i-1].push_back(Point(k,lanes.rowBlock[j])); // x is var |
|
lanes.pts[i-1].push_back(Point(lanes.rowBlock[j],k)); // y is var |
|
} |
|
} |
|
} |
|
} |
|
|
|
3.将等价对转换为等价序列,每个序列需要给一相同的标号,因为它们都是等价的。从1开始,给每个等价序列一个标号。并对同一个等价序列内的点集,左上右下坐标集进行合并。相当于小块生成大块 |
|
4.第一次合并,遍历开始块的标记,查找等价序列,给予它们新的标号。 |
|
5.第二次合并相当于排错,对合并以后的大块再进行一次合并,块不同(其实可能是同一类块,但是在前期被分到不同的块了),线类别相同,合并块的点少于阈值的合并 |
|
6.将每个块的所有像素的坐标点分别计入每个线lane中 |
|
//块合并 lanes.equivalences.size() != 0时执行 |
|
void mergeSameLabel(Lanes& lanes){ |
|
cout<< "equivalences"<<endl; |
|
int maxLabel = *max_element(lanes.blockLabels.begin(),lanes.blockLabels.end());//标签数量 |
|
vector<vector<bool>> eqTab(maxLabel, vector<bool>(maxLabel, false)); |
|
vector<pair<int, int>>::iterator vecPairIt = lanes.equivalences.begin(); |
|
while (vecPairIt != lanes.equivalences.end())//要合并的标签方阵,相互置为True |
|
{ |
|
eqTab[vecPairIt->first - 1][vecPairIt->second - 1] = true; |
|
eqTab[vecPairIt->second - 1][vecPairIt->first - 1] = true; |
|
vecPairIt++; |
|
} |
|
vector<int> labelFlag(maxLabel, 0); |
|
vector<vector<int>> equaList; |
|
vector<int> tempList; |
|
cout << maxLabel << endl; |
|
for (int i = 1; i <= maxLabel; i++)//转成列表容器(同一个容器索引号中报错要合并的块的所有标签) |
|
{ |
|
if (labelFlag[i - 1])//已经有标签,则进入下一个标签的设置 |
|
{ |
|
continue; |
|
} |
|
labelFlag[i - 1] = equaList.size() + 1;//"新标签" |
|
tempList.push_back(i); |
|
for (vector<int>::size_type j = 0; j < tempList.size(); j++) |
|
{ |
|
for (vector<bool>::size_type k = 0; k != eqTab[tempList[j] - 1].size(); k++) |
|
{ |
|
if (eqTab[tempList[j] - 1][k] && !labelFlag[k])//满足eqTab为true和labelFlag为0,则为同一个类别的标签 |
|
{ |
|
tempList.push_back(k + 1);//标签和索引相差1,所以加1 |
|
labelFlag[k] = equaList.size() + 1;//合并的标签与上面的"新标签"相同 |
|
} |
|
} |
|
} |
|
equaList.push_back(tempList); |
|
tempList.clear(); |
|
} |
|
cout << equaList.size() << endl; |
|
for (vector<int>::size_type i = 0; i != lanes.blockLabels.size(); i++) |
|
{ |
|
lanes.blockLabels[i] = labelFlag[lanes.blockLabels[i] - 1]; |
|
} |
|
//合并块的点进行合并 |
|
for(int i=0;i<equaList.size();++i){ |
|
int equaListSize = equaList[i].size(); |
|
if(equaListSize != 1){//大于1代表有两个及以上标签的大块要合并 |
|
auto it_pts = lanes.pts.begin()+equaList[i][0]-1; //要合并的第一个大块的索引 |
|
auto it_blockLabels_berc = lanes.blockLabels_berc.begin()+equaList[i][0]-1; |
|
auto it_blockLabels_type = lanes.blockLabels_type.begin()+equaList[i][0]-1; |
|
for(int j=1;j<equaListSize;++j){ |
|
auto it_pts_obj = it_pts+equaList[i][j]-equaList[i][0];//要合并的后面大块的索引 |
|
auto it_blockLabels_berc_obj = it_blockLabels_berc+equaList[i][j]-equaList[i][0]; |
|
auto it_blockLabels_type_obj = it_blockLabels_type+equaList[i][j]-equaList[i][0]; |
|
it_pts->insert(it_pts->end(),it_pts_obj->begin(),it_pts_obj->end());//将后面的大块的点集插入到第一个大块的末尾 |
|
it_pts_obj->resize(0);//已经合并的后面的大块的点集数量设置为0,在后面如果容器容积为0剔除该线 |
|
it_blockLabels_berc->at(1) = it_blockLabels_berc_obj->at(1);//合并的后面的大块的左下角赋值给第一个大块的左下角,代表生成了一个新的更大的块 |
|
it_blockLabels_berc_obj->resize(0);//已经合并的后面的大块的左上右下坐标集数量设置为0,在后面如果容器容积为0剔除该线 |
|
*it_blockLabels_type = *it_blockLabels_type_obj;//这里是合并线类型,但是好像不必要赋值,因为如果线类型不同就不会合并 |
|
*it_blockLabels_type_obj = 0; |
|
} |
|
} |
|
} |
|
for(auto it=lanes.blockLabels_type.begin();it!=lanes.blockLabels_type.end();){ |
|
if(*it == 0){ |
|
it=lanes.blockLabels_type.erase(it); |
|
}else{ |
|
++it; |
|
} |
|
} |
|
for(auto it=lanes.blockLabels_berc.begin();it!=lanes.blockLabels_berc.end();){ |
|
if(it->size() == 0){ |
|
it=lanes.blockLabels_berc.erase(it); |
|
}else{ |
|
++it; |
|
} |
|
} |
|
|
|
} |
|
void secondReplaceSameLabel(Lanes& lanes,int col_offset,int row_offset,int point_num_thresh) |
|
{ |
|
|
|
if(lanes.equivalences.size() != 0){ |
|
mergeSameLabel(lanes); |
|
//剔除点数小于阈值的线 |
|
for(auto it=lanes.pts.begin();it!=lanes.pts.end();){ |
|
if(it->size() == 0){ |
|
it=lanes.pts.erase(it); |
|
lanes.lane_num = lanes.pts.size(); |
|
}else{ |
|
++it; |
|
} |
|
} |
|
} |
|
|
|
//行方向块合并 |
|
lanes.equivalences.resize(0); |
|
for(int i=0; i < lanes.blockLabels_berc.size();++i){ |
|
//float cur_k = (blockLabels_berc[i][1].y-blockLabels_berc[i][0].y+1e-10)/(blockLabels_berc[i][1].x-blockLabels_berc[i][0].x+1e-10); |
|
int cur_x_end = lanes.blockLabels_berc[i][1].x; |
|
int cur_y_end = lanes.blockLabels_berc[i][1].y; |
|
for(int j=i+1;j< lanes.blockLabels_berc.size();++j){ |
|
//float pre_k =(blockLabels_berc[j][1].y-blockLabels_berc[j][0].y+1e-10)/(blockLabels_berc[j][1].x-blockLabels_berc[j][0].x+1e-10); |
|
if(/*(cur_k <= pre_k + 0.2 || cur_k>=pre_k-0.2) &&*/cur_y_end >=lanes.blockLabels_berc[j][0].y-row_offset |
|
&& cur_y_end<=lanes.blockLabels_berc[j][0].y+row_offset |
|
&& cur_x_end>=lanes.blockLabels_berc[j][0].x-col_offset |
|
&& cur_x_end<=lanes.blockLabels_berc[j][0].x+col_offset){ |
|
// cout <<" "<<i <<" "<<j <<"min: "<< min(lanes.pts[i].size(),lanes.pts[j].size()) <<endl; |
|
if (i+1 != j+1 && lanes.blockLabels_type[i] == lanes.blockLabels_type[j] && min(lanes.pts[i].size(),lanes.pts[j].size()) < row_offset){//块不同,线类别相同,合并块的点少于阈值,才合并 |
|
// cout <<" pair "<< lanes.pts[i].size()<<" "<<lanes.pts[j].size() <<endl; |
|
lanes.equivalences.push_back(make_pair(i+1, j+1)); // 保存等价对 |
|
// lanes.blockLabels_berc[i][1].x = lanes.blockLabels_berc[j][1].x; |
|
// lanes.blockLabels_berc[i][1].y = lanes.blockLabels_berc[j][1].y; |
|
// lanes.blockLabels_berc[j][0] = Point(0,0); |
|
// lanes.blockLabels_berc[j][1] = Point(0,0); |
|
} |
|
} |
|
} |
|
} |
|
|
|
if(lanes.equivalences.size() != 0){ |
|
mergeSameLabel(lanes); |
|
//剔除点数小于阈值的线 |
|
for(auto it=lanes.pts.begin();it!=lanes.pts.end();){ |
|
if(it->size() < point_num_thresh){ |
|
it=lanes.pts.erase(it); |
|
lanes.lane_num = lanes.pts.size(); |
|
}else{ |
|
++it; |
|
} |
|
} |
|
}else{ |
|
//剔除点数小于阈值的线 |
|
for(auto it=lanes.pts.begin();it!=lanes.pts.end();){ |
|
if(it->size() < 3){ |
|
it=lanes.pts.erase(it); |
|
lanes.lane_num = lanes.pts.size(); |
|
}else{ |
|
++it; |
|
} |
|
} |
|
} |
|
} |
|
7.结束 |