|
|
|
@ -275,24 +275,18 @@ void ERFilterNM::er_tree_extract( InputArray image ) |
|
|
|
|
// the component stack
|
|
|
|
|
vector<ERStat*> er_stack; |
|
|
|
|
|
|
|
|
|
//the quads for euler number calculation
|
|
|
|
|
unsigned char quads[3][4]; |
|
|
|
|
quads[0][0] = 1 << 3; |
|
|
|
|
quads[0][1] = 1 << 2; |
|
|
|
|
quads[0][2] = 1 << 1; |
|
|
|
|
quads[0][3] = 1; |
|
|
|
|
quads[1][0] = (1<<2)|(1<<1)|(1); |
|
|
|
|
quads[1][1] = (1<<3)|(1<<1)|(1); |
|
|
|
|
quads[1][2] = (1<<3)|(1<<2)|(1); |
|
|
|
|
quads[1][3] = (1<<3)|(1<<2)|(1<<1); |
|
|
|
|
quads[2][0] = (1<<2)|(1<<1); |
|
|
|
|
quads[2][1] = (1<<3)|(1); |
|
|
|
|
// quads[2][2] and quads[2][3] are never used so no need to initialize them.
|
|
|
|
|
// the quads for euler number calculation
|
|
|
|
|
// quads[2][2] and quads[2][3] are never used.
|
|
|
|
|
// The four lowest bits in each quads[i][j] correspond to the 2x2 binary patterns
|
|
|
|
|
// Q_1, Q_2, Q_3 in the Neumann and Matas CVPR 2012 paper
|
|
|
|
|
// (see in page 4 at the end of first column).
|
|
|
|
|
// Q_1 and Q_2 have four patterns, while Q_3 has only two.
|
|
|
|
|
|
|
|
|
|
const int quads[3][4] = |
|
|
|
|
{ |
|
|
|
|
{ 1<<3 , 1<<2 , 1<<1 , 1<<0 }, |
|
|
|
|
{ (1<<2)|(1<<1)|(1), (1<<3)| (1<<1)|(1), (1<<3)|(1<<2)| (1), (1<<3)|(1<<2)|(1<<1) }, |
|
|
|
|
{ (1<<2)|(1<<1) , (1<<3)| (1), /*unused*/-1, /*unused*/-1 } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// masks to know if a pixel is accessible and if it has been already added to some region
|
|
|
|
|
vector<bool> accessible_pixel_mask(width * height); |
|
|
|
@ -392,8 +386,8 @@ void ERFilterNM::er_tree_extract( InputArray image ) |
|
|
|
|
int non_boundary_neighbours = 0; |
|
|
|
|
int non_boundary_neighbours_horiz = 0; |
|
|
|
|
|
|
|
|
|
unsigned char quad_before[4] = {0,0,0,0}; |
|
|
|
|
unsigned char quad_after[4] = {0,0,0,0}; |
|
|
|
|
int quad_before[4] = {0,0,0,0}; |
|
|
|
|
int quad_after[4] = {0,0,0,0}; |
|
|
|
|
quad_after[0] = 1<<1; |
|
|
|
|
quad_after[1] = 1<<3; |
|
|
|
|
quad_after[2] = 1<<2; |
|
|
|
@ -542,9 +536,9 @@ void ERFilterNM::er_tree_extract( InputArray image ) |
|
|
|
|
current_edge = boundary_edges[threshold_level].back(); |
|
|
|
|
boundary_edges[threshold_level].erase(boundary_edges[threshold_level].end()-1); |
|
|
|
|
|
|
|
|
|
while (boundary_pixes[threshold_level].empty() && (threshold_level < (255/thresholdDelta)+1)) |
|
|
|
|
threshold_level++; |
|
|
|
|
|
|
|
|
|
for (; threshold_level < (255/thresholdDelta)+1; threshold_level++) |
|
|
|
|
if (!boundary_pixes[threshold_level].empty()) |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
int new_level = image_data[current_pixel]; |
|
|
|
|
|
|
|
|
@ -784,28 +778,27 @@ ERStat* ERFilterNM::er_save( ERStat *er, ERStat *parent, ERStat *prev ) |
|
|
|
|
// recursively walk the tree and filter (remove) regions using the callback classifier
|
|
|
|
|
ERStat* ERFilterNM::er_tree_filter ( InputArray image, ERStat * stat, ERStat *parent, ERStat *prev ) |
|
|
|
|
{ |
|
|
|
|
Mat src = image.getMat(); |
|
|
|
|
// assert correct image type
|
|
|
|
|
CV_Assert( src.type() == CV_8UC1 ); |
|
|
|
|
CV_Assert( image.type() == CV_8UC1 ); |
|
|
|
|
|
|
|
|
|
Mat src = image.getMat(); |
|
|
|
|
|
|
|
|
|
//Fill the region and calculate 2nd stage features
|
|
|
|
|
Mat region = region_mask(Rect(Point(stat->rect.x,stat->rect.y),Point(stat->rect.br().x+2,stat->rect.br().y+2))); |
|
|
|
|
Mat region = region_mask(Rect(stat->rect.tl(), stat->rect.br() + Point(2,2))); |
|
|
|
|
region = Scalar(0); |
|
|
|
|
int newMaskVal = 255; |
|
|
|
|
int flags = 4 + (newMaskVal << 8) + FLOODFILL_FIXED_RANGE + FLOODFILL_MASK_ONLY; |
|
|
|
|
Rect rect; |
|
|
|
|
|
|
|
|
|
floodFill( src(Rect(Point(stat->rect.x,stat->rect.y),Point(stat->rect.br().x,stat->rect.br().y))), |
|
|
|
|
region, Point(stat->pixel%src.cols - stat->rect.x, stat->pixel/src.cols - stat->rect.y), |
|
|
|
|
floodFill( src(stat->rect), |
|
|
|
|
region, Point(stat->pixel%src.cols, stat->pixel/src.cols) - stat->rect.tl(), |
|
|
|
|
Scalar(255), &rect, Scalar(stat->level), Scalar(0), flags ); |
|
|
|
|
rect.width += 2; |
|
|
|
|
rect.height += 2; |
|
|
|
|
region = region(rect); |
|
|
|
|
region = region(Rect(1, 1, rect.width, rect.height)); |
|
|
|
|
|
|
|
|
|
vector<vector<Point> > contours; |
|
|
|
|
vector<Point> contour_poly; |
|
|
|
|
vector<Vec4i> hierarchy; |
|
|
|
|
findContours( region(Rect(1, 1, region.cols - 2, region.rows - 2)), contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point(1, 1) ); |
|
|
|
|
findContours( region, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0) ); |
|
|
|
|
//TODO check epsilon parameter of approxPolyDP (set empirically) : we want more precission
|
|
|
|
|
// if the region is very small because otherwise we'll loose all the convexities
|
|
|
|
|
approxPolyDP( Mat(contours[0]), contour_poly, (float)min(rect.width,rect.height)/17, true ); |
|
|
|
@ -2859,9 +2852,7 @@ bool guo_hall_thinning(const Mat1b & img, Mat& skeleton) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float extract_features(Mat &grey, Mat& channel, vector<ERStat> ®ions, vector<ERFeatures> &features); |
|
|
|
|
|
|
|
|
|
float extract_features(Mat &grey, Mat& channel, vector<ERStat> ®ions, vector<ERFeatures> &features) |
|
|
|
|
static float extract_features(Mat &grey, Mat& channel, vector<ERStat> ®ions, vector<ERFeatures> &features) |
|
|
|
|
{ |
|
|
|
|
// assert correct image type
|
|
|
|
|
CV_Assert(( channel.type() == CV_8UC1 ) && ( grey.type() == CV_8UC1 )); |
|
|
|
@ -2890,18 +2881,15 @@ float extract_features(Mat &grey, Mat& channel, vector<ERStat> ®ions, vector< |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
//Fill the region and calculate features
|
|
|
|
|
Mat region = region_mask(Rect(Point(stat->rect.x,stat->rect.y), |
|
|
|
|
Point(stat->rect.br().x+2,stat->rect.br().y+2))); |
|
|
|
|
Mat region = region_mask(Rect(stat->rect.tl(), |
|
|
|
|
stat->rect.br() + Point(2,2))); |
|
|
|
|
region = Scalar(0); |
|
|
|
|
int newMaskVal = 255; |
|
|
|
|
int flags = 4 + (newMaskVal << 8) + FLOODFILL_FIXED_RANGE + FLOODFILL_MASK_ONLY; |
|
|
|
|
Rect rect; |
|
|
|
|
|
|
|
|
|
floodFill( channel(Rect(Point(stat->rect.x,stat->rect.y),Point(stat->rect.br().x,stat->rect.br().y))), |
|
|
|
|
floodFill( channel(stat->rect), |
|
|
|
|
region, Point(stat->pixel%channel.cols - stat->rect.x, stat->pixel/channel.cols - stat->rect.y), |
|
|
|
|
Scalar(255), &rect, Scalar(stat->level), Scalar(0), flags ); |
|
|
|
|
rect.width += 2; |
|
|
|
|
rect.height += 2; |
|
|
|
|
Scalar(255), NULL, Scalar(stat->level), Scalar(0), flags ); |
|
|
|
|
Mat rect_mask = region_mask(Rect(stat->rect.x+1,stat->rect.y+1,stat->rect.width,stat->rect.height)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -2911,7 +2899,7 @@ float extract_features(Mat &grey, Mat& channel, vector<ERStat> ®ions, vector< |
|
|
|
|
f.intensity_std = (float)std[0]; |
|
|
|
|
|
|
|
|
|
Mat tmp,bw; |
|
|
|
|
region_mask(Rect(stat->rect.x+1,stat->rect.y+1,stat->rect.width,stat->rect.height)).copyTo(bw); |
|
|
|
|
rect_mask.copyTo(bw); |
|
|
|
|
distanceTransform(bw, tmp, DIST_L1,3); //L1 gives distance in round integers while L2 floats
|
|
|
|
|
|
|
|
|
|
// Add border because if region span all the image size skeleton will crash
|
|
|
|
@ -3513,19 +3501,16 @@ bool isValidPair(Mat &grey, Mat &lab, Mat &mask, vector<Mat> &channels, vector< |
|
|
|
|
i = ®ions[idx1[0]][idx1[1]]; |
|
|
|
|
j = ®ions[idx2[0]][idx2[1]]; |
|
|
|
|
|
|
|
|
|
Mat region = mask(Rect(Point(i->rect.x,i->rect.y), |
|
|
|
|
Point(i->rect.br().x+2,i->rect.br().y+2))); |
|
|
|
|
Mat region = mask(Rect(i->rect.tl(), |
|
|
|
|
i->rect.br()+ Point(2,2))); |
|
|
|
|
region = Scalar(0); |
|
|
|
|
|
|
|
|
|
int newMaskVal = 255; |
|
|
|
|
int flags = 4 + (newMaskVal << 8) + FLOODFILL_FIXED_RANGE + FLOODFILL_MASK_ONLY; |
|
|
|
|
Rect rect; |
|
|
|
|
|
|
|
|
|
floodFill( channels[idx1[0]](Rect(Point(i->rect.x,i->rect.y),Point(i->rect.br().x,i->rect.br().y))), |
|
|
|
|
region, Point(i->pixel%grey.cols - i->rect.x, i->pixel/grey.cols - i->rect.y), |
|
|
|
|
Scalar(255), &rect, Scalar(i->level), Scalar(0), flags); |
|
|
|
|
rect.width += 2; |
|
|
|
|
rect.height += 2; |
|
|
|
|
floodFill( channels[idx1[0]](i->rect), |
|
|
|
|
region, Point(i->pixel%grey.cols, i->pixel/grey.cols) - i->rect.tl(), |
|
|
|
|
Scalar(255), NULL, Scalar(i->level), Scalar(0), flags); |
|
|
|
|
Mat rect_mask = mask(Rect(i->rect.x+1,i->rect.y+1,i->rect.width,i->rect.height)); |
|
|
|
|
|
|
|
|
|
Scalar mean,std; |
|
|
|
@ -3535,15 +3520,12 @@ bool isValidPair(Mat &grey, Mat &lab, Mat &mask, vector<Mat> &channels, vector< |
|
|
|
|
float a_mean1 = (float)mean[1]; |
|
|
|
|
float b_mean1 = (float)mean[2]; |
|
|
|
|
|
|
|
|
|
region = mask(Rect(Point(j->rect.x,j->rect.y), |
|
|
|
|
Point(j->rect.br().x+2,j->rect.br().y+2))); |
|
|
|
|
region = mask(Rect(j->rect.tl(), j->rect.br()+ Point(2,2))); |
|
|
|
|
region = Scalar(0); |
|
|
|
|
|
|
|
|
|
floodFill( channels[idx2[0]](Rect(Point(j->rect.x,j->rect.y),Point(j->rect.br().x,j->rect.br().y))), |
|
|
|
|
region, Point(j->pixel%grey.cols - j->rect.x, j->pixel/grey.cols - j->rect.y), |
|
|
|
|
Scalar(255), &rect, Scalar(j->level), Scalar(0), flags); |
|
|
|
|
rect.width += 2; |
|
|
|
|
rect.height += 2; |
|
|
|
|
floodFill( channels[idx2[0]](j->rect), |
|
|
|
|
region, Point(j->pixel%grey.cols, j->pixel/grey.cols) - j->rect.tl(), |
|
|
|
|
Scalar(255), NULL, Scalar(j->level), Scalar(0), flags); |
|
|
|
|
rect_mask = mask(Rect(j->rect.x+1,j->rect.y+1,j->rect.width,j->rect.height)); |
|
|
|
|
|
|
|
|
|
meanStdDev(grey(j->rect),mean,std,rect_mask); |
|
|
|
@ -4181,7 +4163,7 @@ void MSERsToERStats(InputArray image, vector<vector<Point> > &contours, vector<v |
|
|
|
|
void detectRegions(InputArray image, const Ptr<ERFilter>& er_filter1, const Ptr<ERFilter>& er_filter2, CV_OUT vector< vector<Point> >& regions) |
|
|
|
|
{ |
|
|
|
|
// assert correct image type
|
|
|
|
|
CV_Assert( image.getMat().type() == CV_8UC1 ); |
|
|
|
|
CV_Assert( image.type() == CV_8UC1 ); |
|
|
|
|
// at least one ERFilter must be passed
|
|
|
|
|
CV_Assert( !er_filter1.empty() ); |
|
|
|
|
|
|
|
|
@ -4195,36 +4177,33 @@ void detectRegions(InputArray image, const Ptr<ERFilter>& er_filter1, const Ptr< |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//Convert each ER to vector<Point> and push it to output regions
|
|
|
|
|
Mat src = image.getMat(); |
|
|
|
|
Mat region_mask = Mat::zeros(src.rows+2, src.cols+2, CV_8UC1); |
|
|
|
|
const Mat src = image.getMat(); |
|
|
|
|
for (size_t i=1; i < ers.size(); i++) //start from 1 to deprecate root region
|
|
|
|
|
{ |
|
|
|
|
ERStat* stat = &ers[i]; |
|
|
|
|
|
|
|
|
|
//Fill the region and calculate 2nd stage features
|
|
|
|
|
Mat region = region_mask(Rect(Point(stat->rect.x,stat->rect.y),Point(stat->rect.br().x+2,stat->rect.br().y+2))); |
|
|
|
|
region = Scalar(0); |
|
|
|
|
Mat region_mask(Size(stat->rect.width + 2, stat->rect.height + 2), CV_8UC1, Scalar(0)); |
|
|
|
|
Mat region = region_mask(Rect(1, 1, stat->rect.width, stat->rect.height)); |
|
|
|
|
|
|
|
|
|
int newMaskVal = 255; |
|
|
|
|
int flags = 4 + (newMaskVal << 8) + FLOODFILL_FIXED_RANGE + FLOODFILL_MASK_ONLY; |
|
|
|
|
Rect rect; |
|
|
|
|
|
|
|
|
|
floodFill( src(Rect(Point(stat->rect.x,stat->rect.y),Point(stat->rect.br().x,stat->rect.br().y))), |
|
|
|
|
region, Point(stat->pixel%src.cols - stat->rect.x, stat->pixel/src.cols - stat->rect.y), |
|
|
|
|
Scalar(255), &rect, Scalar(stat->level), Scalar(0), flags ); |
|
|
|
|
rect.width += 2; |
|
|
|
|
rect.height += 2; |
|
|
|
|
region = region(rect); |
|
|
|
|
const Point seed_pt(stat->pixel%src.cols, stat->pixel/src.cols); |
|
|
|
|
uchar seed_v = src.at<uchar>(seed_pt); |
|
|
|
|
CV_Assert((int)seed_v <= stat->level); |
|
|
|
|
|
|
|
|
|
floodFill( src(stat->rect), |
|
|
|
|
region_mask, |
|
|
|
|
seed_pt - stat->rect.tl(), |
|
|
|
|
Scalar(255), NULL, Scalar(/*stat->level*/255), Scalar(0), flags ); |
|
|
|
|
|
|
|
|
|
vector<vector<Point> > contours; |
|
|
|
|
vector<Vec4i> hierarchy; |
|
|
|
|
findContours( region, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0) ); |
|
|
|
|
|
|
|
|
|
for (size_t j=0; j < contours[0].size(); j++) |
|
|
|
|
contours[0][j] += (stat->rect.tl()-Point(1,1)); |
|
|
|
|
findContours( region, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, stat->rect.tl() ); |
|
|
|
|
|
|
|
|
|
regions.push_back(contours[0]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|