Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions modules/text/samples/detect_er_chars.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import cv2
import numpy as np
from matplotlib import pyplot as plt

print('\ndetect_er_chars.py')
print(' A simple demo script using the Extremal Region Filter algorithm described in:')
Expand All @@ -32,8 +31,8 @@
#Visualization
rects = [cv2.boundingRect(p.reshape(-1, 1, 2)) for p in regions]
for rect in rects:
cv2.rectangle(img, rect[0:2], (rect[0]+rect[2],rect[1]+rect[3]), (0, 0, 255), 2)
img = img[:,:,::-1] #flip the colors dimension from BGR to RGB
plt.imshow(img)
plt.xticks([]), plt.yticks([]) # to hide tick values on X and Y axis
plt.show()
cv2.rectangle(img, rect[0:2], (rect[0]+rect[2],rect[1]+rect[3]), (0, 0, 0), 2)
for rect in rects:
cv2.rectangle(img, rect[0:2], (rect[0]+rect[2],rect[1]+rect[3]), (255, 255, 255), 1)
cv2.imshow("Text detection result", img)
cv2.waitKey(0)
5 changes: 2 additions & 3 deletions modules/text/samples/textdetection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ int main(int argc, const char * argv[])

if (argc < 2) show_help_and_exit(argv[0]);

namedWindow("grouping",WINDOW_NORMAL);
Mat src = imread(argv[1]);

// Extract channels to be processed individually
Expand Down Expand Up @@ -70,8 +69,8 @@ int main(int argc, const char * argv[])
imshow("grouping",src);

cout << "Done!" << endl << endl;
cout << "Press 'e' to show the extracted Extremal Regions, any other key to exit." << endl << endl;
if( waitKey (-1) == 101)
cout << "Press 'space' to show the extracted Extremal Regions, any other key to exit." << endl << endl;
if ((waitKey()&0xff) == ' ')
er_show(channels,regions);

// memory clean-up
Expand Down
10 changes: 4 additions & 6 deletions modules/text/samples/textdetection.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import cv2
import numpy as np
from matplotlib import pyplot as plt

print('\ntextdetection.py')
print(' A demo script of the Extremal Region Filter algorithm described in:')
Expand Down Expand Up @@ -50,11 +49,10 @@
#Visualization
for r in range(0,np.shape(rects)[0]):
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
vis = vis[:,:,::-1] #flip the colors dimension from BGR to RGB
plt.imshow(vis)
plt.xticks([]), plt.yticks([]) # to hide tick values on X and Y axis
plt.show()
cv2.imshow("Text detection result", vis)
cv2.waitKey(0)
121 changes: 50 additions & 71 deletions modules/text/src/erfilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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];

Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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);

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)
{
// assert correct image type
CV_Assert(( channel.type() == CV_8UC1 ) && ( grey.type() == CV_8UC1 ));
Expand Down Expand Up @@ -2890,18 +2881,15 @@ float extract_features(Mat &grey, Mat& channel, vector<ERStat> &regions, 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));


Expand All @@ -2911,7 +2899,7 @@ float extract_features(Mat &grey, Mat& channel, vector<ERStat> &regions, 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
Expand Down Expand Up @@ -3513,19 +3501,16 @@ bool isValidPair(Mat &grey, Mat &lab, Mat &mask, vector<Mat> &channels, vector<
i = &regions[idx1[0]][idx1[1]];
j = &regions[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;
Expand All @@ -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);
Expand Down Expand Up @@ -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() );

Expand All @@ -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]);
}

}

}
Expand Down