Created
July 5, 2017 14:57
-
-
Save gylns/10ddce082dca917e6d1902272a13092f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "mser.hpp" | |
#include <opencv2/imgcodecs.hpp> | |
#include <opencv2/highgui.hpp> | |
#include <vector> | |
#include <iostream> | |
using namespace std; | |
using namespace cv; | |
vector<Point> selectPoints(const String &video_name, const Mat &frame) | |
{ | |
struct Data | |
{ | |
vector<Point> points; | |
static void mouseHandler(int event, int x, int y, int flags, void *param) | |
{ | |
Data *data = (Data*)param; | |
switch (event) | |
{ | |
// start to select the bounding box | |
case EVENT_LBUTTONDOWN: | |
data->points.push_back(Point(x, y)); | |
break; | |
// update the selected bounding box | |
case EVENT_MOUSEMOVE: | |
break; | |
// cleaning up the selected bounding box | |
case EVENT_LBUTTONUP: | |
break; | |
case EVENT_RBUTTONDOWN: | |
data->points.clear(); | |
break; | |
} | |
} | |
} data; | |
namedWindow(video_name); | |
setMouseCallback(video_name, Data::mouseHandler, &data); | |
while (waitKey(10) != ' ') | |
{ | |
Mat draw; | |
cvtColor(frame, draw, CV_GRAY2BGR); | |
for (const auto& it : data.points) | |
circle(draw, it, 2, Scalar(0, 255, 0)); | |
imshow(video_name, draw); | |
} | |
destroyWindow(video_name); | |
return data.points; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
Mat imgOrig, img; | |
cv::CommandLineParser parser(argc, argv, "{ @input | | }"); | |
vector<Point> seeds; | |
string input = parser.get<string>("@input"); | |
if (!input.empty()) | |
{ | |
imgOrig = imread(input, IMREAD_GRAYSCALE); | |
imgOrig.copyTo(img); | |
} | |
else | |
{ | |
cerr << "Usage: " << argv[0] << " input_image" << endl; | |
return 1; | |
} | |
seeds = selectPoints("MSER", img); | |
Mat result(img.rows, img.cols, CV_8UC3); | |
cvtColor(img, result, CV_GRAY2BGR); | |
MSERFeat::Params para; | |
if (img.cols <= 100 && img.rows <= 100) | |
para.maxArea = img.cols * img.rows / 2; | |
else | |
para.maxArea = 5000; | |
try | |
{ | |
// We can detect regions using detectRegions method | |
vector<Rect> zone; | |
vector<vector <Point> > region; | |
MSERFeat erf(para); | |
erf.detectRegions(img, region, zone,seeds); | |
int i = 0; | |
int nbPixelInMSER = 0; | |
for (vector<vector <Point> >::iterator itr = region.begin(); itr != region.end(); ++itr, ++i) | |
{ | |
Mat m = Mat::zeros(img.size(), img.type()); | |
m = 255; | |
for (vector <Point>::iterator itp = region[i].begin(); itp != region[i].end(); ++itp) | |
{ | |
// all pixels belonging to region become blue | |
result.at<Vec3b>(itp->y, itp->x) = Vec3b(128, 0, 0); | |
m.at<uchar>(itp->y, itp->x) = img.at<uchar>(itp->y, itp->x); | |
nbPixelInMSER++; | |
} | |
} | |
cout << "Number of MSER region " << region.size() << " Number of pixels in all MSER region : " << nbPixelInMSER << "\n"; | |
namedWindow("MSER", WINDOW_AUTOSIZE); | |
imshow("MSER", result); | |
} | |
catch (Exception& e) | |
{ | |
cout << e.msg << endl; | |
} | |
waitKey(); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Redistribution and use in source and binary forms, with or | |
* without modification, are permitted provided that the following | |
* conditions are met: | |
* Redistributions of source code must retain the above | |
* copyright notice, this list of conditions and the following | |
* disclaimer. | |
* Redistributions in binary form must reproduce the above | |
* copyright notice, this list of conditions and the following | |
* disclaimer in the documentation and/or other materials | |
* provided with the distribution. | |
* The name of Contributor may not be used to endorse or | |
* promote products derived from this software without | |
* specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | |
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | |
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | |
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | |
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY | |
* OF SUCH DAMAGE. | |
* Copyright© 2009, Liu Liu All rights reserved. | |
* | |
* OpenCV functions for MSER extraction | |
* | |
* 1. there are two different implementation of MSER, one for gray image, one for color image | |
* 2. the gray image algorithm is taken from: Linear Time Maximally Stable Extremal Regions; | |
* the paper claims to be faster than union-find method; | |
* it actually get 1.5~2m/s on my centrino L7200 1.2GHz laptop. | |
* 3. the color image algorithm is taken from: Maximally Stable Colour Regions for Recognition and Match; | |
* it should be much slower than gray image method ( 3~4 times ); | |
* the chi_table.h file is taken directly from paper's source code which is distributed under GPL. | |
* 4. though the name is *contours*, the result actually is a list of point set. | |
*/ | |
#include <limits> | |
#include <opencv2/imgproc.hpp> | |
#include <opencv2/highgui.hpp> | |
namespace cv | |
{ | |
using std::stringstream; | |
using std::vector; | |
class MSERFeat | |
{ | |
public: | |
struct Params | |
{ | |
Params(int _delta = 5, int _min_area = 60, int _max_area = 14400, | |
double _max_variation = 0.25, double _min_diversity = .2) | |
{ | |
delta = _delta; | |
minArea = _min_area; | |
maxArea = _max_area; | |
maxVariation = _max_variation; | |
minDiversity = _min_diversity; | |
pass2Only = false; | |
} | |
int delta; | |
int minArea; | |
int maxArea; | |
double maxVariation; | |
double minDiversity; | |
bool pass2Only; | |
}; | |
explicit MSERFeat(const Params& _params) : params(_params) {} | |
virtual ~MSERFeat() {} | |
void setDelta(int delta) { params.delta = delta; } | |
int getDelta() const { return params.delta; } | |
void setMinArea(int minArea) { params.minArea = minArea; } | |
int getMinArea() const { return params.minArea; } | |
void setMaxArea(int maxArea) { params.maxArea = maxArea; } | |
int getMaxArea() const { return params.maxArea; } | |
void setPass2Only(bool f) { params.pass2Only = f; } | |
bool getPass2Only() const { return params.pass2Only; } | |
enum { DIR_SHIFT = 29, NEXT_MASK = ((1 << DIR_SHIFT) - 1) }; | |
struct Pixel | |
{ | |
Pixel() : val(0) {} | |
Pixel(int _val) : val(_val) {} | |
int getGray(const Pixel* ptr0, const uchar* imgptr0, int mask) const | |
{ | |
return imgptr0[this - ptr0] ^ mask; | |
} | |
int getNext() const { return (val & NEXT_MASK); } | |
void setNext(int next) { val = (val & ~NEXT_MASK) | next; } | |
int getDir() const { return (int)((unsigned)val >> DIR_SHIFT); } | |
void setDir(int dir) { val = (val & NEXT_MASK) | (dir << DIR_SHIFT); } | |
bool isVisited() const { return (val & ~NEXT_MASK) != 0; } | |
int val; | |
}; | |
typedef int PPixel; | |
struct WParams | |
{ | |
Params p; | |
vector<vector<Point> >* msers; | |
vector<Rect>* bboxvec; | |
Pixel* pix0; | |
int step; | |
Mat img; | |
vector<PPixel> seeds; | |
}; | |
// the history of region grown | |
struct CompHistory | |
{ | |
CompHistory() | |
{ | |
parent_ = child_ = next_ = 0; | |
val = size = 0; | |
var = -1.f; | |
head = 0; | |
checked = false; | |
isMSER = false; | |
bbox_ = Rect(); | |
} | |
void updateTree(WParams& wp, CompHistory** _h0, CompHistory** _h1, bool force) | |
{ | |
if (var >= 0.f) | |
return; | |
int delta = wp.p.delta; | |
CompHistory* h0_ = 0, *h1_ = 0; | |
CompHistory* c = child_; | |
if (size >= wp.p.minArea) | |
{ | |
for (; c != 0; c = c->next_) | |
{ | |
if (c->var < 0.f) | |
c->updateTree(wp, c == child_ ? &h0_ : 0, c == child_ ? &h1_ : 0, force); | |
if (c->var < 0.f) | |
return; | |
} | |
} | |
// find h0 and h1 such that: | |
// h0->val >= h->val - delta and (h0->parent == 0 or h0->parent->val < h->val - delta) | |
// h1->val <= h->val + delta and (h1->child == 0 or h1->child->val < h->val + delta) | |
// then we will adjust h0 and h1 as h moves towards latest | |
CompHistory* h0 = this, *h1 = h1_ && h1_->size > size ? h1_ : this; | |
if (h0_) | |
{ | |
for (h0 = h0_; h0 != this && h0->val < val - delta; h0 = h0->parent_) | |
; | |
} | |
else | |
{ | |
for (; h0->child_ && h0->child_->val >= val - delta; h0 = h0->child_) | |
; | |
} | |
for (; h1->parent_ && h1->parent_->val <= val + delta; h1 = h1->parent_) | |
; | |
if (_h0) *_h0 = h0; | |
if (_h1) *_h1 = h1; | |
// when we do not well-defined ER(h->val + delta), we stop | |
// the process of computing variances unless we are at the final step | |
if (!force && !h1->parent_ && h1->val < val + delta) | |
return; | |
var = (float)(h1->size - h0->size) / size; | |
c = child_; | |
for (; c != 0; c = c->next_) | |
c->checkAndCapture(wp); | |
if (force && !parent_) | |
checkAndCapture(wp); | |
} | |
void checkAndCapture(WParams& wp) | |
{ | |
if (checked) | |
return; | |
checked = true; | |
if (size < wp.p.minArea || size > wp.p.maxArea || var < 0.f || var > wp.p.maxVariation) | |
return; | |
if (child_) | |
{ | |
CompHistory* c = child_; | |
for (; c != 0; c = c->next_) | |
{ | |
if (c->var >= 0.f && var > c->var) | |
return; | |
} | |
} | |
if (var > 0.f && parent_ && parent_->var >= 0.f && var >= parent_->var) | |
return; | |
int j = 0; | |
wp.msers->push_back(vector<Point>()); | |
vector<Point>& region = wp.msers->back(); | |
region.resize(size); | |
const Pixel* pix0 = wp.pix0; | |
int step = wp.step; | |
for (PPixel pix = head; j < size; j++, pix = pix0[pix].getNext()) | |
{ | |
int y = pix / step; | |
int x = pix - y*step; | |
region[j] = Point(x, y); | |
} | |
wp.bboxvec->push_back(bbox_); | |
isMSER = true; | |
} | |
CompHistory* child_; | |
CompHistory* parent_; | |
CompHistory* next_; | |
int val; | |
int size; | |
float var; | |
PPixel head; | |
bool checked; | |
bool isMSER; | |
Rect bbox_; | |
}; | |
struct ConnectedComp | |
{ | |
ConnectedComp() | |
{ | |
init(0); | |
} | |
void init(int gray) | |
{ | |
head = tail = 0; | |
history = 0; | |
size = 0; | |
gray_level = gray; | |
xmin = ymin = INT_MAX; | |
xmax = ymax = INT_MIN; | |
} | |
// add history chunk to a connected component | |
void growHistory(CompHistory*& hptr, WParams& wp, int new_gray_level, bool final) | |
{ | |
if (new_gray_level < gray_level) | |
new_gray_level = gray_level; | |
CompHistory *h; | |
if (history && history->val == gray_level) | |
{ | |
h = history; | |
} | |
else | |
{ | |
h = hptr++; | |
h->parent_ = 0; | |
h->child_ = history; | |
h->next_ = 0; | |
if (history) | |
{ | |
history->parent_ = h; | |
} | |
} | |
h->val = gray_level; | |
h->size = size; | |
h->head = head; | |
h->var = FLT_MAX; | |
h->checked = true; | |
h->isMSER = false; | |
if (h->size > 0) | |
{ | |
h->bbox_ = Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); | |
} | |
if (h->size >= wp.p.minArea) | |
{ | |
h->var = -1.f; | |
h->checked = false; | |
} | |
gray_level = new_gray_level; | |
history = h; | |
if (history && history->val != gray_level) | |
{ | |
history->updateTree(wp, 0, 0, final); | |
} | |
} | |
// merging two connected components | |
void merge(ConnectedComp* comp1, ConnectedComp* comp2, | |
CompHistory*& hptr, WParams& wp) | |
{ | |
if (comp1->gray_level < comp2->gray_level) | |
std::swap(comp1, comp2); | |
gray_level = comp1->gray_level; | |
comp1->growHistory(hptr, wp, gray_level, false); | |
comp2->growHistory(hptr, wp, gray_level, false); | |
if (comp1->size == 0) | |
{ | |
head = comp2->head; | |
tail = comp2->tail; | |
xmin = comp2->xmin; | |
xmax = comp2->xmax; | |
ymin = comp2->ymin; | |
ymax = comp2->ymax; | |
} | |
else | |
{ | |
head = comp1->head; | |
wp.pix0[comp1->tail].setNext(comp2->head); | |
tail = comp2->tail; | |
xmin = std::min(comp1->xmin, comp2->xmin); | |
ymin = std::min(comp1->ymin, comp2->ymin); | |
xmax = std::max(comp1->xmax, comp2->xmax); | |
ymax = std::max(comp1->ymax, comp2->ymax); | |
} | |
size = comp1->size + comp2->size; | |
history = comp1->history; | |
CompHistory *h1 = history->child_; | |
CompHistory *h2 = comp2->history; | |
// the child_'s size should be the large one | |
if (h1 && h1->size > h2->size) | |
{ | |
// add h2 as a child only if its size is large enough | |
if (h2->size >= wp.p.minArea) | |
{ | |
h2->next_ = h1->next_; | |
h1->next_ = h2; | |
} | |
} | |
else | |
{ | |
history->child_ = h2; | |
// reserve h1 as a child only if its size is large enough | |
if (h1 && h1->size >= wp.p.minArea) | |
{ | |
h2->next_ = h1; | |
} | |
} | |
h2->parent_ = history; | |
} | |
PPixel head; | |
PPixel tail; | |
CompHistory* history; | |
int gray_level; | |
int size; | |
int xmin, ymin; | |
int xmax, ymax; | |
vector<PPixel> seeds; | |
}; | |
void detectRegions(InputArray image, | |
std::vector<std::vector<Point> >& msers, | |
std::vector<Rect>& bboxes, vector<Point> seeds = vector<Point>()); | |
void preprocess1(const Mat& img, int* level_size) | |
{ | |
memset(level_size, 0, 256 * sizeof(level_size[0])); | |
int i, j, cols = img.cols, rows = img.rows; | |
int step = cols; | |
pixbuf.resize(step*rows); | |
heapbuf.resize(cols*rows + 256); | |
histbuf.resize(cols*rows); | |
pixHist.resize(cols*rows); | |
Pixel borderpix; | |
borderpix.setDir(5); | |
for (j = 0; j < step; j++) | |
{ | |
pixbuf[j] = pixbuf[j + (rows - 1)*step] = borderpix; | |
pixHist[j] = pixHist[j + (rows - 1)*step] = 0; | |
} | |
for (i = 1; i < rows - 1; i++) | |
{ | |
const uchar* imgptr = img.ptr(i); | |
Pixel* pptr = &pixbuf[i*step]; | |
CompHistory **histptr = &pixHist[i*step]; | |
pptr[0] = pptr[cols - 1] = borderpix; | |
histptr[0] = histptr[cols - 1] = 0; | |
for (j = 1; j < cols - 1; j++) | |
{ | |
int val = imgptr[j]; | |
level_size[val]++; | |
pptr[j].val = 0; | |
histptr[j] = 0; | |
} | |
} | |
} | |
void preprocess2(const Mat& img, int* level_size) | |
{ | |
int i; | |
for (i = 0; i < 128; i++) | |
std::swap(level_size[i], level_size[255 - i]); | |
if (!params.pass2Only) | |
{ | |
int j, cols = img.cols, rows = img.rows; | |
int step = cols; | |
for (i = 1; i < rows - 1; i++) | |
{ | |
Pixel* pptr = &pixbuf[i*step]; | |
CompHistory **histptr = &pixHist[i*step]; | |
for (j = 1; j < cols - 1; j++) | |
{ | |
pptr[j].val = 0; | |
histptr[j] = 0; | |
} | |
} | |
} | |
} | |
void pass(const Mat& img, vector<vector<Point> >& msers, vector<Rect>& bboxvec, | |
Size size, const int* level_size, int mask, vector<Point> seeds = vector<Point>()) | |
{ | |
CompHistory* histptr = &histbuf[0]; | |
int step = size.width; | |
Pixel *ptr0 = &pixbuf[0], *ptr = &ptr0[step + 1]; | |
const uchar* imgptr0 = img.ptr(); | |
Pixel** heap[256]; | |
ConnectedComp comp[257]; | |
ConnectedComp* comptr = &comp[0]; | |
ConnectedComp* seedComp = 0; | |
WParams wp; | |
wp.p = params; | |
wp.msers = &msers; | |
wp.bboxvec = &bboxvec; | |
wp.pix0 = ptr0; | |
wp.step = step; | |
wp.img = img ^ mask; | |
heap[0] = &heapbuf[0]; | |
heap[0][0] = 0; | |
for (int i = 1; i < 256; i++) | |
{ | |
heap[i] = heap[i - 1] + level_size[i - 1] + 1; | |
heap[i][0] = 0; | |
} | |
comptr->gray_level = 256; | |
comptr++; | |
comptr->gray_level = ptr->getGray(ptr0, imgptr0, mask); | |
ptr->setDir(1); | |
int dir[] = { 0, 1, step, -1, -step }; | |
for (;;) | |
{ | |
int curr_gray = ptr->getGray(ptr0, imgptr0, mask); | |
int nbr_idx = ptr->getDir(); | |
// take tour of all the 4 directions | |
for (; nbr_idx <= 4; nbr_idx++) | |
{ | |
// get the neighbor | |
Pixel* ptr_nbr = ptr + dir[nbr_idx]; | |
if (!ptr_nbr->isVisited()) | |
{ | |
// set dir=1, next=0 | |
ptr_nbr->val = 1 << DIR_SHIFT; | |
int nbr_gray = ptr_nbr->getGray(ptr0, imgptr0, mask); | |
if (nbr_gray < curr_gray) | |
{ | |
// when the value of neighbor smaller than current | |
// push current to boundary heap and make the neighbor to be the current one | |
// create an empty comp | |
*(++heap[curr_gray]) = ptr; | |
ptr->val = (nbr_idx + 1) << DIR_SHIFT; | |
ptr = ptr_nbr; | |
comptr++; | |
comptr->init(nbr_gray); | |
curr_gray = nbr_gray; | |
nbr_idx = 0; | |
continue; | |
} | |
// otherwise, push the neighbor to boundary heap | |
*(++heap[nbr_gray]) = ptr_nbr; | |
} | |
} | |
// set dir = nbr_idx, next = 0 | |
ptr->val = nbr_idx << DIR_SHIFT; | |
int ptrofs = (int)(ptr - ptr0); | |
CV_Assert(ptrofs != 0); | |
// add a pixel to the pixel list | |
if (comptr->tail) | |
ptr0[comptr->tail].setNext(ptrofs); | |
else | |
comptr->head = ptrofs; | |
comptr->tail = ptrofs; | |
comptr->size++; | |
// update the bbox | |
{ | |
int y = ptrofs / step; | |
int x = ptrofs - y*step; | |
comptr->xmin = std::min(comptr->xmin, x); | |
comptr->xmax = std::max(comptr->xmax, x); | |
comptr->ymin = std::min(comptr->ymin, y); | |
comptr->ymax = std::max(comptr->ymax, y); | |
} | |
comptr->seeds.push_back(ptrofs); | |
// get the next pixel from boundary heap | |
if (*heap[curr_gray]) | |
{ | |
ptr = *heap[curr_gray]; | |
heap[curr_gray]--; | |
} | |
else | |
{ | |
for (curr_gray++; curr_gray < 256; curr_gray++) | |
{ | |
if (*heap[curr_gray]) | |
break; | |
} | |
if (curr_gray >= 256) | |
break; | |
ptr = *heap[curr_gray]; | |
heap[curr_gray]--; | |
if (curr_gray < comptr[-1].gray_level) | |
{ | |
comptr->growHistory(histptr, wp, curr_gray, false); | |
CV_DbgAssert(comptr->size == comptr->history->size); | |
for (const auto& it: comptr->seeds) | |
{ | |
CV_DbgAssert(pixHist[it] == 0); | |
pixHist[it] = comptr->history; | |
CV_DbgAssert(pixHist[it]->val == pixbuf[it].getGray(ptr0, imgptr0, mask)); | |
} | |
comptr->seeds.clear(); | |
} | |
else | |
{ | |
// there must one pixel with the second component's gray level in the heap, | |
// so curr_gray is not large than the second component's gray level | |
comptr--; | |
CV_DbgAssert(curr_gray == comptr->gray_level); | |
comptr->merge(comptr, comptr + 1, histptr, wp); | |
CV_DbgAssert(curr_gray == comptr->gray_level); | |
for (const auto& it : (comptr+1)->seeds) | |
{ | |
CV_DbgAssert(pixHist[it] == 0); | |
pixHist[it] = (comptr+1)->history; | |
CV_DbgAssert(pixHist[it]->val == pixbuf[it].getGray(ptr0, imgptr0, mask)); | |
} | |
(comptr+1)->seeds.clear(); | |
} | |
} | |
} | |
for (; comptr->gray_level != 256; comptr--) | |
{ | |
comptr->growHistory(histptr, wp, 256, true); | |
} | |
for (const auto& it : seeds) | |
{ | |
wp.seeds.push_back(it.y*step + it.x); | |
} | |
drawERs(img, mask, wp.seeds, ptr0); | |
} | |
void drawERs(const Mat& img, int mask, const vector<PPixel>& seeds, Pixel *ptr0) | |
{ | |
if (seeds.size() > 0) | |
{ | |
vector<CompHistory *> seedComps; | |
vector<CompHistory *> drawHists; | |
seedComps.resize(seeds.size()); | |
for (int i = 0; i < seeds.size(); i++) | |
{ | |
seedComps[i] = pixHist[seeds[i]]; | |
} | |
sort(seedComps.begin(), seedComps.end(), [](CompHistory *a, CompHistory *b) { | |
if (b == 0) return true; | |
else if (a == 0) return false; | |
else return a->val < b->val; | |
}); | |
bool paused = false; | |
for (;;) | |
{ | |
Mat drawImg = img ^ mask; | |
cvtColor(drawImg, drawImg, CV_GRAY2BGR); | |
bool drawed = false; | |
for (auto it = drawHists.begin(); it != drawHists.end();) | |
{ | |
if ((*it) && (*it)->parent_ == seedComps[0]) | |
it = drawHists.erase(it); | |
else | |
it++; | |
} | |
drawHists.push_back(seedComps[0]); | |
for (auto& pHist : drawHists) | |
{ | |
if (pHist == 0) | |
continue; | |
drawed = true; | |
stringstream ss; | |
PPixel pp = pHist->head; | |
for (int i = 0; i < pHist->size; i++) | |
{ | |
int y = pp / img.cols; | |
int x = pp - y*img.cols; | |
if (!pHist->isMSER) | |
drawImg.at<Vec3b>(y, x) = Vec3b(0, 255, 0); | |
else | |
drawImg.at<Vec3b>(y, x) = Vec3b(0, 0, 255); | |
Pixel p = ptr0[pp]; | |
pp = p.getNext(); | |
} | |
rectangle(drawImg, pHist->bbox_, Scalar(0, 255, 255)); | |
if (seeds.size() == 1) | |
{ | |
Mat res = Mat::zeros(30, drawImg.cols, CV_8UC3); | |
ss.clear(); | |
ss.str(""); | |
ss << "v: " << pHist->val << " s: " << pHist->size; | |
putText(res, ss.str(), Point(0, 10), FONT_HERSHEY_SIMPLEX, .4, Scalar(0, 255, 255)); | |
//putText(drawImg, ss.str(), pHist->bbox_.tl() + Point(0, -10), FONT_HERSHEY_SIMPLEX, .4, Scalar(0, 0, 128)); | |
ss.clear(); | |
ss.str(""); | |
ss << "var: " << pHist->var; | |
putText(res, ss.str(), Point(0, 25), FONT_HERSHEY_SIMPLEX, .4, Scalar(0, 255, 255)); | |
//putText(drawImg, ss.str(), pHist->bbox_.tl(), FONT_HERSHEY_SIMPLEX, .4, Scalar(0, 0, 128)); | |
vector<Mat> matrices = { drawImg, res }; | |
vconcat(matrices, drawImg); | |
} | |
} | |
imshow("MSER", drawImg); | |
int c; | |
if (paused == false) | |
c = waitKey(200); | |
else | |
c = waitKey(); | |
if (c == ' ') | |
paused = !paused; | |
else if (c == 'q') | |
break; | |
if (drawed == false) | |
break; | |
CompHistory *temp = seedComps[0]->parent_; | |
seedComps.erase(seedComps.begin()); | |
auto it = seedComps.begin(); | |
for (; it != seedComps.end(); it++) | |
{ | |
if (*it == 0 || (temp && temp->val <= (*it)->val)) | |
break; | |
} | |
seedComps.insert(it, temp); | |
} | |
} | |
} | |
Mat tempsrc; | |
vector<Pixel> pixbuf; | |
vector<Pixel*> heapbuf; | |
vector<CompHistory> histbuf; | |
vector<CompHistory*> pixHist; | |
Params params; | |
}; | |
void MSERFeat::detectRegions(InputArray _src, vector<vector<Point> >& msers, vector<Rect>& bboxes, vector<Point> seeds) | |
{ | |
Mat src = _src.getMat(); | |
msers.clear(); | |
bboxes.clear(); | |
if (src.rows < 3 || src.cols < 3) | |
CV_Error(Error::StsBadArg, "Input image is too small. Expected at least 3x3"); | |
Size size = src.size(); | |
if (src.type() == CV_8U) | |
{ | |
int level_size[256]; | |
if (!src.isContinuous()) | |
{ | |
src.copyTo(tempsrc); | |
src = tempsrc; | |
} | |
// darker to brighter (MSER+) | |
preprocess1(src, level_size); | |
if (!params.pass2Only) | |
pass(src, msers, bboxes, size, level_size, 0, seeds); | |
// brighter to darker (MSER-) | |
preprocess2(src, level_size); | |
pass(src, msers, bboxes, size, level_size, 255, seeds); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment