From 9281cbfd2b1d56eb4208f618ccd62f00bea5ff5b Mon Sep 17 00:00:00 2001 From: lluisgomez Date: Fri, 22 Jan 2016 22:29:05 +0100 Subject: [PATCH] ERFilter python bindings --- .../text/include/opencv2/text/erfilter.hpp | 15 +++--- modules/text/samples/detect_er_chars.py | 39 ++++++++++++++ modules/text/src/erfilter.cpp | 54 ++++++++++++++++++- 3 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 modules/text/samples/detect_er_chars.py diff --git a/modules/text/include/opencv2/text/erfilter.hpp b/modules/text/include/opencv2/text/erfilter.hpp index 7b4f4c1dc..10c1a938b 100644 --- a/modules/text/include/opencv2/text/erfilter.hpp +++ b/modules/text/include/opencv2/text/erfilter.hpp @@ -115,7 +115,7 @@ public: Extracts the component tree (if needed) and filter the extremal regions (ER's) by using a given classifier. */ -class CV_EXPORTS ERFilter : public Algorithm +class CV_EXPORTS_W ERFilter : public Algorithm { public: @@ -124,7 +124,7 @@ public: By doing it we hide SVM, Boost etc. Developers can provide their own classifiers to the ERFilter algorithm. */ - class CV_EXPORTS Callback + class CV_EXPORTS_W Callback { public: virtual ~Callback() { } @@ -207,7 +207,7 @@ the probability P(er|character) are selected (if the local maximum of the probab global limit pmin and the difference between local maximum and local minimum is greater than minProbabilityDiff). */ -CV_EXPORTS Ptr createERFilterNM1(const Ptr& cb, +CV_EXPORTS_W Ptr createERFilterNM1(const Ptr& cb, int thresholdDelta = 1, float minArea = 0.00025, float maxArea = 0.13, float minProbability = 0.4, bool nonMaxSuppression = true, @@ -224,7 +224,7 @@ non-character classes using more informative but also more computationally expen classifier uses all the features calculated in the first stage and the following additional features: hole area ratio, convex hull ratio, and number of outer inflexion points. */ -CV_EXPORTS Ptr createERFilterNM2(const Ptr& cb, +CV_EXPORTS_W Ptr createERFilterNM2(const Ptr& cb, float minProbability = 0.3); @@ -234,7 +234,7 @@ CV_EXPORTS Ptr createERFilterNM2(const Ptr& cb, returns a pointer to ERFilter::Callback. */ -CV_EXPORTS Ptr loadClassifierNM1(const std::string& filename); +CV_EXPORTS_W Ptr loadClassifierNM1(const String& filename); /** @brief Allow to implicitly load the default classifier when creating an ERFilter object. @@ -242,7 +242,7 @@ CV_EXPORTS Ptr loadClassifierNM1(const std::string& filename returns a pointer to ERFilter::Callback. */ -CV_EXPORTS Ptr loadClassifierNM2(const std::string& filename); +CV_EXPORTS_W Ptr loadClassifierNM2(const String& filename); //! computeNMChannels operation modes @@ -343,6 +343,9 @@ An example of MSERsToERStats in use can be found in the text detection webcam_de CV_EXPORTS void MSERsToERStats(InputArray image, std::vector > &contours, std::vector > ®ions); +// Utility funtion for scripting +CV_EXPORTS_W void detectRegions(InputArray image, const Ptr& er_filter1, const Ptr& er_filter2, CV_OUT std::vector< std::vector >& regions); + //! @} } diff --git a/modules/text/samples/detect_er_chars.py b/modules/text/samples/detect_er_chars.py new file mode 100644 index 000000000..6cd765f92 --- /dev/null +++ b/modules/text/samples/detect_er_chars.py @@ -0,0 +1,39 @@ +#!/usr/bin/python + +import sys +import os + +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:' +print ' Neumann L., Matas J.: Real-Time Scene Text Localization and Recognition, CVPR 2012\n' + + +if (len(sys.argv) < 2): + print ' (ERROR) You must call this script with an argument (path_to_image_to_be_processed)\n' + quit() + +pathname = os.path.dirname(sys.argv[0]) + +img = cv2.imread(str(sys.argv[1])) +gray = cv2.imread(str(sys.argv[1]),0) + +erc1 = cv2.text.loadClassifierNM1(pathname+'/trained_classifierNM1.xml') +er1 = cv2.text.createERFilterNM1(erc1) + +erc2 = cv2.text.loadClassifierNM2(pathname+'/trained_classifierNM2.xml') +er2 = cv2.text.createERFilterNM2(erc2) + +regions = cv2.text.detectRegions(gray,er1,er2) + +#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() \ No newline at end of file diff --git a/modules/text/src/erfilter.cpp b/modules/text/src/erfilter.cpp index b5f58b76c..064a3fd2b 100644 --- a/modules/text/src/erfilter.cpp +++ b/modules/text/src/erfilter.cpp @@ -1161,7 +1161,7 @@ Ptr createERFilterNM2(const Ptr& cb, float minProb The function takes as parameter the XML or YAML file with the classifier model (e.g. trained_classifierNM1.xml) returns a pointer to ERFilter::Callback. */ -Ptr loadClassifierNM1(const string& filename) +Ptr loadClassifierNM1(const String& filename) { return makePtr(filename); @@ -1172,7 +1172,7 @@ Ptr loadClassifierNM1(const string& filename) The function takes as parameter the XML or YAML file with the classifier model (e.g. trained_classifierNM2.xml) returns a pointer to ERFilter::Callback. */ -Ptr loadClassifierNM2(const string& filename) +Ptr loadClassifierNM2(const String& filename) { return makePtr(filename); } @@ -4167,5 +4167,55 @@ void MSERsToERStats(InputArray image, vector > &contours, vector& er_filter1, const Ptr& er_filter2, CV_OUT vector< vector >& regions) +{ + // assert correct image type + CV_Assert( image.getMat().type() == CV_8UC1 ); + // at least one ERFilter must be passed + CV_Assert( !er_filter1.empty() ); + + vector ers; + + er_filter1->run(image, ers); + + if (!er_filter2.empty()) + { + er_filter2->run(image, ers); + } + + //Convert each ER to vector and push it to output regions + Mat src = image.getMat(); + Mat region_mask = Mat::zeros(src.rows+2, src.cols+2, CV_8UC1); + for (size_t i=0; i < ers.size(); i++) + { + 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); + 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); + + vector > contours; + vector 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(); + + regions.push_back(contours[0]); + } + +} + } }