Commit 57648c2c authored by Alexander Alekhin's avatar Alexander Alekhin

Merge pull request #876 from alalek:fix_contrib_872

parents bd619c55 31d11378
...@@ -5,7 +5,6 @@ import os ...@@ -5,7 +5,6 @@ import os
import cv2 import cv2
import numpy as np import numpy as np
from matplotlib import pyplot as plt
print('\ndetect_er_chars.py') print('\ndetect_er_chars.py')
print(' A simple demo script using the Extremal Region Filter algorithm described in:') print(' A simple demo script using the Extremal Region Filter algorithm described in:')
...@@ -32,8 +31,8 @@ regions = cv2.text.detectRegions(gray,er1,er2) ...@@ -32,8 +31,8 @@ regions = cv2.text.detectRegions(gray,er1,er2)
#Visualization #Visualization
rects = [cv2.boundingRect(p.reshape(-1, 1, 2)) for p in regions] rects = [cv2.boundingRect(p.reshape(-1, 1, 2)) for p in regions]
for rect in rects: for rect in rects:
cv2.rectangle(img, rect[0:2], (rect[0]+rect[2],rect[1]+rect[3]), (0, 0, 255), 2) cv2.rectangle(img, rect[0:2], (rect[0]+rect[2],rect[1]+rect[3]), (0, 0, 0), 2)
img = img[:,:,::-1] #flip the colors dimension from BGR to RGB for rect in rects:
plt.imshow(img) cv2.rectangle(img, rect[0:2], (rect[0]+rect[2],rect[1]+rect[3]), (255, 255, 255), 1)
plt.xticks([]), plt.yticks([]) # to hide tick values on X and Y axis cv2.imshow("Text detection result", img)
plt.show() cv2.waitKey(0)
...@@ -32,7 +32,6 @@ int main(int argc, const char * argv[]) ...@@ -32,7 +32,6 @@ int main(int argc, const char * argv[])
if (argc < 2) show_help_and_exit(argv[0]); if (argc < 2) show_help_and_exit(argv[0]);
namedWindow("grouping",WINDOW_NORMAL);
Mat src = imread(argv[1]); Mat src = imread(argv[1]);
// Extract channels to be processed individually // Extract channels to be processed individually
...@@ -70,8 +69,8 @@ int main(int argc, const char * argv[]) ...@@ -70,8 +69,8 @@ int main(int argc, const char * argv[])
imshow("grouping",src); imshow("grouping",src);
cout << "Done!" << endl << endl; cout << "Done!" << endl << endl;
cout << "Press 'e' to show the extracted Extremal Regions, any other key to exit." << endl << endl; cout << "Press 'space' to show the extracted Extremal Regions, any other key to exit." << endl << endl;
if( waitKey (-1) == 101) if ((waitKey()&0xff) == ' ')
er_show(channels,regions); er_show(channels,regions);
// memory clean-up // memory clean-up
......
...@@ -5,7 +5,6 @@ import os ...@@ -5,7 +5,6 @@ import os
import cv2 import cv2
import numpy as np import numpy as np
from matplotlib import pyplot as plt
print('\ntextdetection.py') print('\ntextdetection.py')
print(' A demo script of the Extremal Region Filter algorithm described in:') print(' A demo script of the Extremal Region Filter algorithm described in:')
...@@ -50,11 +49,10 @@ for channel in channels: ...@@ -50,11 +49,10 @@ for channel in channels:
#Visualization #Visualization
for r in range(0,np.shape(rects)[0]): for r in range(0,np.shape(rects)[0]):
rect = rects[r] rect = rects[r]
cv2.rectangle(vis, (rect[0],rect[1]), (rect[0]+rect[2],rect[1]+rect[3]), (0, 255, 255), 2) cv2.rectangle(vis, (rect[0],rect[1]), (rect[0]+rect[2],rect[1]+rect[3]), (0, 0, 0), 2)
cv2.rectangle(vis, (rect[0],rect[1]), (rect[0]+rect[2],rect[1]+rect[3]), (255, 255, 255), 1)
#Visualization #Visualization
vis = vis[:,:,::-1] #flip the colors dimension from BGR to RGB cv2.imshow("Text detection result", vis)
plt.imshow(vis) cv2.waitKey(0)
plt.xticks([]), plt.yticks([]) # to hide tick values on X and Y axis
plt.show()
...@@ -275,24 +275,18 @@ void ERFilterNM::er_tree_extract( InputArray image ) ...@@ -275,24 +275,18 @@ void ERFilterNM::er_tree_extract( InputArray image )
// the component stack // the component stack
vector<ERStat*> er_stack; vector<ERStat*> er_stack;
//the quads for euler number calculation // the quads for euler number calculation
unsigned char quads[3][4]; // quads[2][2] and quads[2][3] are never used.
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 four lowest bits in each quads[i][j] correspond to the 2x2 binary patterns // 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 // Q_1, Q_2, Q_3 in the Neumann and Matas CVPR 2012 paper
// (see in page 4 at the end of first column). // (see in page 4 at the end of first column).
// Q_1 and Q_2 have four patterns, while Q_3 has only two. // 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 // 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); vector<bool> accessible_pixel_mask(width * height);
...@@ -392,8 +386,8 @@ void ERFilterNM::er_tree_extract( InputArray image ) ...@@ -392,8 +386,8 @@ void ERFilterNM::er_tree_extract( InputArray image )
int non_boundary_neighbours = 0; int non_boundary_neighbours = 0;
int non_boundary_neighbours_horiz = 0; int non_boundary_neighbours_horiz = 0;
unsigned char quad_before[4] = {0,0,0,0}; int quad_before[4] = {0,0,0,0};
unsigned char quad_after[4] = {0,0,0,0}; int quad_after[4] = {0,0,0,0};
quad_after[0] = 1<<1; quad_after[0] = 1<<1;
quad_after[1] = 1<<3; quad_after[1] = 1<<3;
quad_after[2] = 1<<2; quad_after[2] = 1<<2;
...@@ -542,9 +536,9 @@ void ERFilterNM::er_tree_extract( InputArray image ) ...@@ -542,9 +536,9 @@ void ERFilterNM::er_tree_extract( InputArray image )
current_edge = boundary_edges[threshold_level].back(); current_edge = boundary_edges[threshold_level].back();
boundary_edges[threshold_level].erase(boundary_edges[threshold_level].end()-1); boundary_edges[threshold_level].erase(boundary_edges[threshold_level].end()-1);
while (boundary_pixes[threshold_level].empty() && (threshold_level < (255/thresholdDelta)+1)) for (; threshold_level < (255/thresholdDelta)+1; threshold_level++)
threshold_level++; if (!boundary_pixes[threshold_level].empty())
break;
int new_level = image_data[current_pixel]; int new_level = image_data[current_pixel];
...@@ -784,28 +778,27 @@ ERStat* ERFilterNM::er_save( ERStat *er, ERStat *parent, ERStat *prev ) ...@@ -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 // 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 ) ERStat* ERFilterNM::er_tree_filter ( InputArray image, ERStat * stat, ERStat *parent, ERStat *prev )
{ {
Mat src = image.getMat();
// assert correct image type // 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 //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); region = Scalar(0);
int newMaskVal = 255; int newMaskVal = 255;
int flags = 4 + (newMaskVal << 8) + FLOODFILL_FIXED_RANGE + FLOODFILL_MASK_ONLY; int flags = 4 + (newMaskVal << 8) + FLOODFILL_FIXED_RANGE + FLOODFILL_MASK_ONLY;
Rect rect; Rect rect;
floodFill( src(Rect(Point(stat->rect.x,stat->rect.y),Point(stat->rect.br().x,stat->rect.br().y))), floodFill( src(stat->rect),
region, Point(stat->pixel%src.cols - stat->rect.x, stat->pixel/src.cols - stat->rect.y), region, Point(stat->pixel%src.cols, stat->pixel/src.cols) - stat->rect.tl(),
Scalar(255), &rect, Scalar(stat->level), Scalar(0), flags ); Scalar(255), &rect, Scalar(stat->level), Scalar(0), flags );
rect.width += 2; region = region(Rect(1, 1, rect.width, rect.height));
rect.height += 2;
region = region(rect);
vector<vector<Point> > contours; vector<vector<Point> > contours;
vector<Point> contour_poly; vector<Point> contour_poly;
vector<Vec4i> hierarchy; 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 //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 // 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 ); 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) ...@@ -2859,9 +2852,7 @@ bool guo_hall_thinning(const Mat1b & img, Mat& skeleton)
} }
float extract_features(Mat &grey, Mat& channel, vector<ERStat> &regions, vector<ERFeatures> &features); static float extract_features(Mat &grey, Mat& channel, vector<ERStat> &regions, vector<ERFeatures> &features)
float extract_features(Mat &grey, Mat& channel, vector<ERStat> &regions, vector<ERFeatures> &features)
{ {
// assert correct image type // assert correct image type
CV_Assert(( channel.type() == CV_8UC1 ) && ( grey.type() == CV_8UC1 )); CV_Assert(( channel.type() == CV_8UC1 ) && ( grey.type() == CV_8UC1 ));
...@@ -2890,18 +2881,15 @@ float extract_features(Mat &grey, Mat& channel, vector<ERStat> &regions, vector< ...@@ -2890,18 +2881,15 @@ float extract_features(Mat &grey, Mat& channel, vector<ERStat> &regions, vector<
{ {
//Fill the region and calculate features //Fill the region and calculate features
Mat region = region_mask(Rect(Point(stat->rect.x,stat->rect.y), Mat region = region_mask(Rect(stat->rect.tl(),
Point(stat->rect.br().x+2,stat->rect.br().y+2))); stat->rect.br() + Point(2,2)));
region = Scalar(0); region = Scalar(0);
int newMaskVal = 255; int newMaskVal = 255;
int flags = 4 + (newMaskVal << 8) + FLOODFILL_FIXED_RANGE + FLOODFILL_MASK_ONLY; 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), 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 ); Scalar(255), NULL, Scalar(stat->level), Scalar(0), flags );
rect.width += 2;
rect.height += 2;
Mat rect_mask = region_mask(Rect(stat->rect.x+1,stat->rect.y+1,stat->rect.width,stat->rect.height)); 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> &regions, vector< ...@@ -2911,7 +2899,7 @@ float extract_features(Mat &grey, Mat& channel, vector<ERStat> &regions, vector<
f.intensity_std = (float)std[0]; f.intensity_std = (float)std[0];
Mat tmp,bw; 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 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 // 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< ...@@ -3513,19 +3501,16 @@ bool isValidPair(Mat &grey, Mat &lab, Mat &mask, vector<Mat> &channels, vector<
i = &regions[idx1[0]][idx1[1]]; i = &regions[idx1[0]][idx1[1]];
j = &regions[idx2[0]][idx2[1]]; j = &regions[idx2[0]][idx2[1]];
Mat region = mask(Rect(Point(i->rect.x,i->rect.y), Mat region = mask(Rect(i->rect.tl(),
Point(i->rect.br().x+2,i->rect.br().y+2))); i->rect.br()+ Point(2,2)));
region = Scalar(0); region = Scalar(0);
int newMaskVal = 255; int newMaskVal = 255;
int flags = 4 + (newMaskVal << 8) + FLOODFILL_FIXED_RANGE + FLOODFILL_MASK_ONLY; 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))), floodFill( channels[idx1[0]](i->rect),
region, Point(i->pixel%grey.cols - i->rect.x, i->pixel/grey.cols - i->rect.y), region, Point(i->pixel%grey.cols, i->pixel/grey.cols) - i->rect.tl(),
Scalar(255), &rect, Scalar(i->level), Scalar(0), flags); Scalar(255), NULL, Scalar(i->level), Scalar(0), flags);
rect.width += 2;
rect.height += 2;
Mat rect_mask = mask(Rect(i->rect.x+1,i->rect.y+1,i->rect.width,i->rect.height)); Mat rect_mask = mask(Rect(i->rect.x+1,i->rect.y+1,i->rect.width,i->rect.height));
Scalar mean,std; Scalar mean,std;
...@@ -3535,15 +3520,12 @@ bool isValidPair(Mat &grey, Mat &lab, Mat &mask, vector<Mat> &channels, vector< ...@@ -3535,15 +3520,12 @@ bool isValidPair(Mat &grey, Mat &lab, Mat &mask, vector<Mat> &channels, vector<
float a_mean1 = (float)mean[1]; float a_mean1 = (float)mean[1];
float b_mean1 = (float)mean[2]; float b_mean1 = (float)mean[2];
region = mask(Rect(Point(j->rect.x,j->rect.y), region = mask(Rect(j->rect.tl(), j->rect.br()+ Point(2,2)));
Point(j->rect.br().x+2,j->rect.br().y+2)));
region = Scalar(0); region = Scalar(0);
floodFill( channels[idx2[0]](Rect(Point(j->rect.x,j->rect.y),Point(j->rect.br().x,j->rect.br().y))), floodFill( channels[idx2[0]](j->rect),
region, Point(j->pixel%grey.cols - j->rect.x, j->pixel/grey.cols - j->rect.y), region, Point(j->pixel%grey.cols, j->pixel/grey.cols) - j->rect.tl(),
Scalar(255), &rect, Scalar(j->level), Scalar(0), flags); Scalar(255), NULL, Scalar(j->level), Scalar(0), flags);
rect.width += 2;
rect.height += 2;
rect_mask = mask(Rect(j->rect.x+1,j->rect.y+1,j->rect.width,j->rect.height)); 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); meanStdDev(grey(j->rect),mean,std,rect_mask);
...@@ -4181,7 +4163,7 @@ void MSERsToERStats(InputArray image, vector<vector<Point> > &contours, vector<v ...@@ -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) void detectRegions(InputArray image, const Ptr<ERFilter>& er_filter1, const Ptr<ERFilter>& er_filter2, CV_OUT vector< vector<Point> >& regions)
{ {
// assert correct image type // assert correct image type
CV_Assert( image.getMat().type() == CV_8UC1 ); CV_Assert( image.type() == CV_8UC1 );
// at least one ERFilter must be passed // at least one ERFilter must be passed
CV_Assert( !er_filter1.empty() ); CV_Assert( !er_filter1.empty() );
...@@ -4195,36 +4177,33 @@ void detectRegions(InputArray image, const Ptr<ERFilter>& er_filter1, const Ptr< ...@@ -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 //Convert each ER to vector<Point> and push it to output regions
Mat src = image.getMat(); const Mat src = image.getMat();
Mat region_mask = Mat::zeros(src.rows+2, src.cols+2, CV_8UC1);
for (size_t i=1; i < ers.size(); i++) //start from 1 to deprecate root region for (size_t i=1; i < ers.size(); i++) //start from 1 to deprecate root region
{ {
ERStat* stat = &ers[i]; ERStat* stat = &ers[i];
//Fill the region and calculate 2nd stage features //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_mask(Size(stat->rect.width + 2, stat->rect.height + 2), CV_8UC1, Scalar(0));
region = Scalar(0); Mat region = region_mask(Rect(1, 1, stat->rect.width, stat->rect.height));
int newMaskVal = 255; int newMaskVal = 255;
int flags = 4 + (newMaskVal << 8) + FLOODFILL_FIXED_RANGE + FLOODFILL_MASK_ONLY; 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))), const Point seed_pt(stat->pixel%src.cols, stat->pixel/src.cols);
region, Point(stat->pixel%src.cols - stat->rect.x, stat->pixel/src.cols - stat->rect.y), uchar seed_v = src.at<uchar>(seed_pt);
Scalar(255), &rect, Scalar(stat->level), Scalar(0), flags ); CV_Assert((int)seed_v <= stat->level);
rect.width += 2;
rect.height += 2; floodFill( src(stat->rect),
region = region(rect); region_mask,
seed_pt - stat->rect.tl(),
Scalar(255), NULL, Scalar(/*stat->level*/255), Scalar(0), flags );
vector<vector<Point> > contours; vector<vector<Point> > contours;
vector<Vec4i> hierarchy; vector<Vec4i> hierarchy;
findContours( region, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0) ); findContours( region, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, stat->rect.tl() );
for (size_t j=0; j < contours[0].size(); j++)
contours[0][j] += (stat->rect.tl()-Point(1,1));
regions.push_back(contours[0]); regions.push_back(contours[0]);
} }
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment