00001 #ifndef CVD_INCLUDE_MORPHOLOGY_H
00002 #define CVD_INCLUDE_MORPHOLOGY_H
00003
00004 #include <cvd/vision_exceptions.h>
00005 #include <cvd/vision.h>
00006 #include <cvd/image.h>
00007 #include <map>
00008 #include <vector>
00009
00010
00011 namespace CVD
00012 {
00013 namespace Internal
00014 {
00015 namespace MorphologyHelpers
00016 {
00017 using namespace std;
00018
00019
00020 template<class T> vector<ptrdiff_t> offsets(const vector<ImageRef>& v, const SubImage<T>& s)
00021 {
00022 vector<ptrdiff_t> off;
00023
00024 for(unsigned int i=0; i < v.size(); i++)
00025 off.push_back(v[i].x + v[i].y * s.row_stride()-1);
00026 return off;
00027 }
00028
00029
00030 vector<vector<ImageRef> > row_split(const vector<ImageRef>& v, int y_lo, int y_hi)
00031 {
00032 vector<vector<ImageRef> > rows(y_hi - y_lo + 1);
00033
00034 for(unsigned int i=0; i < v.size(); i++)
00035 rows[v[i].y - y_lo].push_back(v[i]);
00036
00037 return rows;
00038 }
00039 }
00040 }
00041
00081 template<class Accumulator, class T>
00082 void morphology(const SubImage<T>& in, const std::vector<ImageRef>& selem, const Accumulator& a_, SubImage<T>& out)
00083 {
00084 using Internal::MorphologyHelpers::offsets;
00085 using Internal::MorphologyHelpers::row_split;
00086 using std::min;
00087 using std::max;
00088 using std::vector;
00089
00090 if(in.size() != out.size())
00091 throw Exceptions::Vision::IncompatibleImageSizes(__FUNCTION__);
00092
00093
00094
00095
00096
00097
00098
00099
00101
00102 int x_lo = selem[0].x;
00103 int x_hi = selem[0].x;
00104 int y_lo = selem[0].y;
00105 int y_hi = selem[0].y;
00106
00107 for(unsigned int i=0; i < selem.size(); i++)
00108 {
00109 x_lo = min(x_lo, selem[i].x);
00110 x_hi = max(x_hi, selem[i].x);
00111 y_lo = min(y_lo, selem[i].y);
00112 y_hi = max(y_hi, selem[i].y);
00113 }
00114
00116
00117 vector<ImageRef> structure_element = selem;
00118 vector<ImageRef> shifted;
00119
00120 sort(structure_element.begin(), structure_element.end());
00121 for(unsigned int i=0; i < structure_element.size(); i++)
00122 shifted.push_back(structure_element[i] + ImageRef(1, 0));
00123
00124 vector<ImageRef> add, remove;
00125 set_difference(shifted.begin(), shifted.end(), structure_element.begin(), structure_element.end(), back_inserter(add));
00126 set_difference(structure_element.begin(), structure_element.end(), shifted.begin(), shifted.end(), back_inserter(remove));
00127
00129
00130
00131 vector<ptrdiff_t> add_off = offsets(add, in);
00132 vector<ptrdiff_t> remove_off = offsets(remove, in);
00133
00134
00136
00137
00138
00139
00140 vector<vector<ImageRef> > split_selem = row_split(structure_element, y_lo, y_hi);
00141 vector<vector<ImageRef> > split_add = row_split(add, y_lo, y_hi);
00142 vector<vector<ImageRef> > split_remove= row_split(remove, y_lo, y_hi);
00143
00144 Accumulator acc(a_);
00145
00146 if(x_hi - x_lo + 1 <= in.size().x)
00147 for(int y=0; y < in.size().y; y++)
00148 {
00149
00150 int startrow = max(0, - y_lo - y);
00151 int endrow = split_selem.size() - max(0, y + y_hi - in.size().y+1);
00152
00153
00154 int x_first_full = max(0, -x_lo);
00155 int x_after_last_full = min(in.size().x, in.size().x - x_hi);
00156
00157
00158 acc.clear();
00159
00160
00161 for(int i=startrow; i < endrow; i++)
00162 for(int j=(int)split_selem[i].size()-1; j >=0 && split_selem[i][j].x >=0; j--)
00163 acc.insert(in[y + split_selem[i][0].y][split_selem[i][j].x]);
00164
00165 out[y][0] = acc.get();
00166
00167
00168
00169
00170 for(int x=1; x <= x_first_full ; x++)
00171 {
00172 for(int i=startrow; i < endrow; i++)
00173 for(int j=(int)split_remove[i].size()-1; j >=0 && split_remove[i][j].x+x-1 >=0; j--)
00174 acc.remove(in[y + split_remove[i][0].y][x+split_remove[i][j].x-1]);
00175
00176 for(int i=startrow; i < endrow; i++)
00177 for(int j=(int)split_add[i].size()-1; j >=0 && split_add[i][j].x+x-1 >=0; j--)
00178 acc.insert(in[y + split_add[i][0].y][x+split_add[i][j].x-1]);
00179
00180 out[y][x] = acc.get();
00181 }
00182
00183
00184
00185
00186 int add_start=0, add_end=0, remove_start=0, remove_end=0;
00187 for(int i=0; i < startrow; i++)
00188 {
00189 add_start+=split_add[i].size();
00190 remove_start+=split_remove[i].size();
00191 }
00192 for(int i=0; i < endrow; i++)
00193 {
00194 add_end+=split_add[i].size();
00195 remove_end+=split_remove[i].size();
00196 }
00197
00198
00199 for(int x=max(0, -x_lo+1); x < x_after_last_full; x++)
00200 {
00201 for(int i=remove_start; i < remove_end; i++)
00202 acc.remove(*(in[y] + x + remove_off[i]));
00203
00204 for(int i=add_start; i < add_end; i++)
00205 acc.insert(*(in[y] + x + add_off[i]));
00206
00207 out[y][x] = acc.get();
00208 }
00209
00210
00211 for(int x=x_after_last_full; x < in.size().x ; x++)
00212 {
00213 for(int i=startrow; i < endrow; i++)
00214 for(int j=0; j < (int)split_remove[i].size() && split_remove[i][j].x+x-1 < in.size().x; j++)
00215 acc.remove(in[y + split_remove[i][0].y][x+split_remove[i][j].x-1]);
00216
00217 for(int i=startrow; i < endrow; i++)
00218 for(int j=0; j < (int)split_add[i].size() && split_add[i][j].x+x-1 < in.size().x; j++)
00219 acc.insert(in[y + split_add[i][0].y][x+split_add[i][j].x-1]);
00220
00221 out[y][x] = acc.get();
00222 }
00223 }
00224 else
00225 {
00226
00227
00228 for(int y=0; y < in.size().y; y++)
00229 {
00230
00231 int startrow = max(0, - y_lo - y);
00232 int endrow = split_selem.size() - max(0, y + y_hi - in.size().y+1);
00233
00234
00235 acc.clear();
00236
00237
00238 for(int i=startrow; i < endrow; i++)
00239 for(int j=0; j < (int)split_selem[i].size(); j++)
00240 {
00241 int xp = split_selem[i][j].x;
00242 if(xp >= 0 && xp < in.size().x)
00243 acc.insert(in[y + split_selem[i][0].y][xp]);
00244 }
00245
00246 out[y][0] = acc.get();
00247
00248
00249 for(int x=1; x < in.size().x ; x++)
00250 {
00251 for(int i=startrow; i < endrow; i++)
00252 for(int j=0; j < (int)split_remove[i].size(); j++)
00253 {
00254 int xp = x + split_remove[i][j].x - 1;
00255 if(xp >= 0 && xp < in.size().x)
00256 acc.remove(in[y + split_add[i][0].y][xp]);
00257 }
00258
00259 for(int i=startrow; i < endrow; i++)
00260 for(int j=0; j < (int)split_add[i].size(); j++)
00261 {
00262 int xp = x + split_add[i][j].x - 1;
00263 if(xp >= 0 && xp < in.size().x)
00264 acc.insert(in[y + split_add[i][0].y][xp]);
00265 }
00266
00267 out[y][x] = acc.get();
00268 }
00269 }
00270 }
00271 }
00272
00273 #ifndef DOXYGEN_IGNORE_INTERNAL
00274 namespace Internal
00275 {
00276 template<class C, class D> class PerformMorphology{};
00277
00278 template<class C, class D> struct ImagePromise<PerformMorphology<C, D> >
00279 {
00280 ImagePromise(const SubImage<C>& im, const D& acc, const std::vector<ImageRef>& s_)
00281 :i(im),a(acc),s(s_)
00282 {}
00283
00284 const SubImage<C>& i;
00285 const D& a;
00286 const std::vector<ImageRef>& s;
00287
00288 template<class E> void execute(Image<E>& j)
00289 {
00290 j.resize(i.size());
00291 morphology(i, s, a, j);
00292 }
00293 };
00294 };
00295
00296 template<class C, class D> Internal::ImagePromise<Internal::PerformMorphology<C, D> > morphology(const SubImage<C>& c, const std::vector<ImageRef>& selem, const D& a)
00297 {
00298 return Internal::ImagePromise<Internal::PerformMorphology<C, D> >(c, a, selem);
00299 }
00300 #else
00301
00309 Image<T> morphology(const SubImage<T>& in, const std::vector<ImageRef>& selem, const Accumulator& a_);
00310
00311 #endif
00312
00315 namespace Morphology
00316 {
00321 template<class T, template<class> class Cmp> struct BasicGray
00322 {
00323 private:
00324 std::map<T, int, Cmp<T> > pix;
00325
00326 public:
00327 void clear()
00328 {
00329 pix.clear();
00330 }
00331 void insert(const T& t)
00332 {
00333 pix[t]++;
00334 }
00335
00336 void remove(const T& t)
00337 {
00338 --pix[t];
00339 }
00340
00341 T get()
00342 {
00343 typedef typename std::map<T, int, Cmp<T> >::iterator it;
00344
00345 for(it i=pix.begin(); i != pix.end();)
00346 {
00347 it old = i;
00348 i++;
00349
00350 if(old->second == 0)
00351 pix.erase(old);
00352 else
00353 return old->first;
00354 }
00355
00356 assert(0);
00357 return 0;
00358 }
00359 };
00360
00361
00364 template<class T> class Erode: public BasicGray<T, std::less>
00365 {
00366 };
00367
00368
00371 template<class T> class Dilate: public BasicGray<T, std::greater>
00372 {
00373 };
00374
00375
00378 template<class T> class Percentile;
00379
00382 template<class T> class Median;
00383
00388 struct BasicGrayByte
00389 {
00390 protected:
00391 int histogram[256];
00392 int total;
00393
00394 public:
00395 BasicGrayByte()
00396 {
00397 clear();
00398 }
00399
00400 void clear()
00401 {
00402 total=0;
00403 for(int i=0; i < 256; i++)
00404 histogram[i] = 0;
00405 }
00406
00407 void insert(byte t)
00408 {
00409 total++;
00410 histogram[t]++;
00411 }
00412
00413 void remove(byte t)
00414 {
00415 total--;
00416 histogram[t]--;
00417 }
00418 };
00419
00420
00423 template<> class Erode<byte>: public BasicGrayByte
00424 {
00425 public:
00426 byte get()
00427 {
00428 for(int j=0; j < 256; j++)
00429 if(histogram[j])
00430 return j;
00431
00432 assert(0);
00433 return 0;
00434 }
00435 };
00436
00439 template<> class Dilate<byte>: public BasicGrayByte
00440 {
00441 public:
00442 byte get()
00443 {
00444 for(int j=255; j >=0 ; j--)
00445 if(histogram[j])
00446 return j;
00447
00448 assert(0);
00449 return 0;
00450 }
00451 };
00452
00455 template<> class Percentile<byte>: public BasicGrayByte
00456 {
00457 private:
00458 double ptile;
00459
00460 public:
00461 Percentile(double p)
00462 :ptile(p)
00463 {
00464 }
00465
00466 byte get()
00467 {
00468 using std::max;
00469
00470 if(ptile < 0.5)
00471 {
00472 int sum=0;
00473
00474
00475
00476 int threshold = max(0, (int)floor(total * ptile+.5)- 1);
00477
00478 for(int j=0; j < 255; j++)
00479 {
00480 sum += histogram[j];
00481
00482 if(sum > threshold)
00483 return j;
00484 }
00485
00486 return 255;
00487 }
00488 else
00489 {
00490
00491 int sum=0;
00492 int threshold = max(0, (int)floor(total * (1-ptile)+.5)- 1);
00493
00494 for(int j=255; j > 0; j--)
00495 {
00496 sum += histogram[j];
00497
00498 if(sum > threshold)
00499 return j;
00500 }
00501
00502 return 0;
00503 }
00504 }
00505 };
00506
00507
00510 template<> class Median<byte>: public Percentile<byte>
00511 {
00512 public:
00513 Median()
00514 :Percentile<byte>(0.5)
00515 {
00516 }
00517 };
00518
00522 template<class T> struct BasicBinary
00523 {
00524 int t, f;
00525 BasicBinary()
00526 :t(0),f(0)
00527 {}
00528
00529 void insert(bool b)
00530 {
00531 if(b)
00532 t++;
00533 else
00534 f++;
00535 }
00536
00537 void remove(bool b)
00538 {
00539 if(b)
00540 t--;
00541 else
00542 f--;
00543 }
00544
00545 void clear()
00546 {
00547 t=f=0;
00548 }
00549 };
00550
00553 template<class T=bool> struct BinaryErode : public BasicBinary<T>
00554 {
00555 using BasicBinary<T>::f;
00556 T get()
00557 {
00558 return !f;
00559 }
00560 };
00561
00564 template<class T=bool> struct BinaryDilate : public BasicBinary<T>
00565 {
00566 using BasicBinary<T>::t;
00567 T get()
00568 {
00569 return t!= 0;
00570 }
00571 };
00572
00575 template<class T=bool> struct BinaryMedian : public BasicBinary<T>
00576 {
00577 using BasicBinary<T>::t;
00578 using BasicBinary<T>::f;
00579 T get()
00580 {
00581 return (t > f);
00582 }
00583 };
00584 }
00585
00586 namespace median{
00587
00588 template<class T> T median4(T a, T b, T c, T d)
00589 {
00590 int v[4] = {a, b, c, d};
00591 std::nth_element(v, v+2, v+4);
00592 return v[2];
00593 }
00594
00595 template<class T> T median4(const SubImage<T>& im, int r, int c)
00596 {
00597 return median4(im[r][c], im[r][c+1], im[r+1][c], im[r+1][c+1]);
00598 }
00599
00600 template<class T> T median6(T a, T b, T c, T d, T e, T f)
00601 {
00602 int v[6] = {a, b, c, d, e, f};
00603 std::nth_element(v, v+3, v+6);
00604 return v[3];
00605 }
00606
00607 template<class T> T median6_row(const SubImage<T>& im, int r, int c)
00608 {
00609 return median6(im[r][c], im[r][c+1], im[r][c+2], im[r+1][c], im[r+1][c+1], im[r+1][c+2]);
00610 }
00611 template<class T> T median6_col(const SubImage<T>& im, int r, int c)
00612 {
00613 return median6(im[r][c], im[r][c+1], im[r+1][c], im[r+1][c+1], im[r+2][c], im[r+2][c+1]);
00614 }
00615
00616 };
00617
00618 void morphology(const SubImage<byte>& in, const std::vector<ImageRef>& selem, const Morphology::Median<byte>& m, SubImage<byte>& out)
00619 {
00620
00621
00622 if(selem.size() == 9)
00623 {
00624 std::vector<ImageRef> s = selem;
00625 std::sort(s.begin(), s.end());
00626 ImageRef box[9] = {
00627 ImageRef(-1, -1),
00628 ImageRef( 0, -1),
00629 ImageRef( 1, -1),
00630 ImageRef(-1, 0),
00631 ImageRef( 0, 0),
00632 ImageRef( 1, 0),
00633 ImageRef(-1, 1),
00634 ImageRef( 0, 1),
00635 ImageRef( 1, 1)};
00636
00637 if(std::equal(s.begin(), s.end(), box))
00638 {
00639 median_filter_3x3(in, out);
00640
00641
00642
00643
00644 using median::median4;
00645 using median::median6_row;
00646 using median::median6_col;
00647 out[0][0] = median4(in, 0, 0);
00648 out[0][in.size().x-1] = median4(in, 0, in.size().x-2);
00649 out[in.size().y-1][0] = median4(in, in.size().y-2, 0);
00650 out[in.size().y-1][in.size().x-1] = median4(in, in.size().y-2, in.size().x-2);
00651
00652 int vals[6];
00653
00654 for(int i=1; i < in.size().x-1; i++)
00655 out[0][i] = median6_row(in, 0, i-1);
00656
00657 for(int i=1; i < in.size().x-1; i++)
00658 out[in.size().y-1][i] = median6_row(in, in.size().y-2, i-1);
00659
00660 for(int i=1; i < in.size().y-1; i++)
00661 out[i][0] = median6_col(in, i-1, 0);
00662
00663 for(int i=1; i < in.size().y-1; i++)
00664 out[i][in.size().x-1] = median6_col(in, i-1, in.size().x-2);
00665 }
00666 else
00667 morphology<Morphology::Median<byte>, byte >(in , selem, m, out);
00668 }
00669 else
00670 morphology<Morphology::Median<byte>, byte >(in , selem, m, out);
00671 }
00672
00673 }
00674
00675 #endif