/* Copyright 2005, 2006 by Gerald Friedland, Kristian Jantz and Lars Knipping Conversion to C++ for Inkscape by Bob Jamison Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "siox.h" #include #include #include #include namespace org { namespace siox { //######################################################################## //# C L A B //######################################################################## /** * Convert integer A, R, G, B values into an pixel value. */ static unsigned long getRGB(int a, int r, int g, int b) { if (a<0) a=0; else if (a>255) a=255; if (r<0) r=0; else if (r>255) r=255; if (g<0) g=0; else if (g>255) g=255; if (b<0) b=0; else if (b>255) b=255; return (a<<24)|(r<<16)|(g<<8)|b; } /** * Convert float A, R, G, B values (0.0-1.0) into an pixel value. */ static unsigned long getRGB(float a, float r, float g, float b) { return getRGB((int)(a * 256.0), (int)(r * 256.0), (int)(g * 256.0), (int)(b * 256.0)); } //######################################### //# Root approximations for large speedup. //# By njh! //######################################### static const int ROOT_TAB_SIZE = 16; static float cbrt_table[ROOT_TAB_SIZE +1]; double CieLab::cbrt(double x) { double y = cbrt_table[int(x*ROOT_TAB_SIZE )]; // assuming x \in [0, 1] y = (2.0 * y + x/(y*y))/3.0; y = (2.0 * y + x/(y*y))/3.0; // polish twice return y; } static float qn_table[ROOT_TAB_SIZE +1]; double CieLab::qnrt(double x) { double y = qn_table[int(x*ROOT_TAB_SIZE )]; // assuming x \in [0, 1] double Y = y*y; y = (4.0*y + x/(Y*Y))/5.0; Y = y*y; y = (4.0*y + x/(Y*Y))/5.0; // polish twice return y; } double CieLab::pow24(double x) { double onetwo = x*qnrt(x); return onetwo*onetwo; } static bool _clab_inited_ = false; void CieLab::init() { if (!_clab_inited_) { cbrt_table[0] = pow(float(1)/float(ROOT_TAB_SIZE*2), 0.3333); qn_table[0] = pow(float(1)/float(ROOT_TAB_SIZE*2), 0.2); for(int i = 1; i < ROOT_TAB_SIZE +1; i++) { cbrt_table[i] = pow(float(i)/float(ROOT_TAB_SIZE), 0.3333); qn_table[i] = pow(float(i)/float(ROOT_TAB_SIZE), 0.2); } _clab_inited_ = true; } } /** * Construct this CieLab from a packed-pixel ARGB value */ CieLab::CieLab(unsigned long rgb) { init(); int ir = (rgb>>16) & 0xff; int ig = (rgb>> 8) & 0xff; int ib = (rgb ) & 0xff; float fr = ((float)ir) / 255.0; float fg = ((float)ig) / 255.0; float fb = ((float)ib) / 255.0; //printf("fr:%f fg:%f fb:%f\n", fr, fg, fb); if (fr > 0.04045) //fr = (float) pow((fr + 0.055) / 1.055, 2.4); fr = (float) pow24((fr + 0.055) / 1.055); else fr = fr / 12.92; if (fg > 0.04045) //fg = (float) pow((fg + 0.055) / 1.055, 2.4); fg = (float) pow24((fg + 0.055) / 1.055); else fg = fg / 12.92; if (fb > 0.04045) //fb = (float) pow((fb + 0.055) / 1.055, 2.4); fb = (float) pow24((fb + 0.055) / 1.055); else fb = fb / 12.92; // Use white = D65 const float x = fr * 0.4124 + fg * 0.3576 + fb * 0.1805; const float y = fr * 0.2126 + fg * 0.7152 + fb * 0.0722; const float z = fr * 0.0193 + fg * 0.1192 + fb * 0.9505; float vx = x / 0.95047; float vy = y; float vz = z / 1.08883; //printf("vx:%f vy:%f vz:%f\n", vx, vy, vz); if (vx > 0.008856) //vx = (float) pow(vx, 0.3333); vx = (float) cbrt(vx); else vx = (7.787 * vx) + (16.0 / 116.0); if (vy > 0.008856) //vy = (float) pow(vy, 0.3333); vy = (float) cbrt(vy); else vy = (7.787 * vy) + (16.0 / 116.0); if (vz > 0.008856) //vz = (float) pow(vz, 0.3333); vz = (float) cbrt(vz); else vz = (7.787 * vz) + (16.0 / 116.0); C = 0; L = 116.0 * vy - 16.0; A = 500.0 * (vx - vy); B = 200.0 * (vy - vz); } /** * Return this CieLab's value converted to a packed-pixel ARGB value */ unsigned long CieLab::toRGB() { float vy = (L + 16.0) / 116.0; float vx = A / 500.0 + vy; float vz = vy - B / 200.0; float vx3 = vx * vx * vx; float vy3 = vy * vy * vy; float vz3 = vz * vz * vz; if (vy3 > 0.008856) vy = vy3; else vy = (vy - 16.0 / 116.0) / 7.787; if (vx3 > 0.008856) vx = vx3; else vx = (vx - 16.0 / 116.0) / 7.787; if (vz3 > 0.008856) vz = vz3; else vz = (vz - 16.0 / 116.0) / 7.787; vx *= 0.95047; //use white = D65 vz *= 1.08883; float vr =(float)(vx * 3.2406 + vy * -1.5372 + vz * -0.4986); float vg =(float)(vx * -0.9689 + vy * 1.8758 + vz * 0.0415); float vb =(float)(vx * 0.0557 + vy * -0.2040 + vz * 1.0570); if (vr > 0.0031308) vr = (float)(1.055 * pow(vr, (1.0 / 2.4)) - 0.055); else vr = 12.92 * vr; if (vg > 0.0031308) vg = (float)(1.055 * pow(vg, (1.0 / 2.4)) - 0.055); else vg = 12.92 * vg; if (vb > 0.0031308) vb = (float)(1.055 * pow(vb, (1.0 / 2.4)) - 0.055); else vb = 12.92 * vb; return getRGB(0.0, vr, vg, vb); } /** * Squared Euclidian distance between this and another color */ float CieLab::diffSq(const CieLab &other) { float sum=0.0; sum += (L - other.L) * (L - other.L); sum += (A - other.A) * (A - other.A); sum += (B - other.B) * (B - other.B); return sum; } /** * Computes squared euclidian distance in CieLab space for two colors * given as RGB values. */ float CieLab::diffSq(unsigned int rgb1, unsigned int rgb2) { CieLab c1(rgb1); CieLab c2(rgb2); float euclid = c1.diffSq(c2); return euclid; } /** * Computes squared euclidian distance in CieLab space for two colors * given as RGB values. */ float CieLab::diff(unsigned int rgb0, unsigned int rgb1) { return (float) sqrt(diffSq(rgb0, rgb1)); } //######################################################################## //# T U P E L //######################################################################## /** * Helper class for storing the minimum distances to a cluster centroid * in background and foreground and the index to the centroids in each * signature for a given color. */ class Tupel { public: Tupel() { minBgDist = 0.0f; indexMinBg = 0; minFgDist = 0.0f; indexMinFg = 0; } Tupel(float minBgDistArg, long indexMinBgArg, float minFgDistArg, long indexMinFgArg) { minBgDist = minBgDistArg; indexMinBg = indexMinBgArg; minFgDist = minFgDistArg; indexMinFg = indexMinFgArg; } Tupel(const Tupel &other) { minBgDist = other.minBgDist; indexMinBg = other.indexMinBg; minFgDist = other.minFgDist; indexMinFg = other.indexMinFg; } Tupel &operator=(const Tupel &other) { minBgDist = other.minBgDist; indexMinBg = other.indexMinBg; minFgDist = other.minFgDist; indexMinFg = other.indexMinFg; return *this; } virtual ~Tupel() {} float minBgDist; long indexMinBg; float minFgDist; long indexMinFg; }; //######################################################################## //# S I O X I M A G E //######################################################################## /** * Create an image with the given width and height */ SioxImage::SioxImage(unsigned int widthArg, unsigned int heightArg) { init(width, height); } /** * Copy constructor */ SioxImage::SioxImage(const SioxImage &other) { pixdata = NULL; cmdata = NULL; assign(other); } /** * Assignment */ SioxImage &SioxImage::operator=(const SioxImage &other) { assign(other); return *this; } /** * Clean up after use. */ SioxImage::~SioxImage() { if (pixdata) delete[] pixdata; if (cmdata) delete[] cmdata; } /** * Returns true if the previous operation on this image * was successful, else false. */ bool SioxImage::isValid() { return valid; } /** * Sets whether an operation was successful, and whether * this image should be considered a valid one. * was successful, else false. */ void SioxImage::setValid(bool val) { valid = val; } /** * Set a pixel at the x,y coordinates to the given value. * If the coordinates are out of range, do nothing. */ void SioxImage::setPixel(unsigned int x, unsigned int y, unsigned int pixval) { if (x > width || y > height) return; unsigned long offset = width * y + x; pixdata[offset] = pixval; } /** * Set a pixel at the x,y coordinates to the given r, g, b values. * If the coordinates are out of range, do nothing. */ void SioxImage::setPixel(unsigned int x, unsigned int y, unsigned int a, unsigned int r, unsigned int g, unsigned int b) { if (x > width || y > height) return; unsigned long offset = width * y + x; unsigned int pixval = ((a << 24) & 0xff000000) | ((r << 16) & 0x00ff0000) | ((g << 8) & 0x0000ff00) | ((b ) & 0x000000ff); pixdata[offset] = pixval; } /** * Get a pixel at the x,y coordinates given. If * the coordinates are out of range, return 0; */ unsigned int SioxImage::getPixel(unsigned int x, unsigned int y) { if (x > width || y > height) return 0L; unsigned long offset = width * y + x; return pixdata[offset]; } /** * Return the image data buffer */ unsigned int *SioxImage::getImageData() { return pixdata; } /** * Set a confidence value at the x,y coordinates to the given value. * If the coordinates are out of range, do nothing. */ void SioxImage::setConfidence(unsigned int x, unsigned int y, float confval) { if (x > width || y > height) return; unsigned long offset = width * y + x; cmdata[offset] = confval; } /** * Get a confidence valueat the x,y coordinates given. If * the coordinates are out of range, return 0; */ float SioxImage::getConfidence(unsigned int x, unsigned int y) { if (x > width || y > height) return 0.0; unsigned long offset = width * y + x; return cmdata[offset]; } /** * Return the confidence data buffer */ float *SioxImage::getConfidenceData() { return cmdata; } /** * Return the width of this image */ int SioxImage::getWidth() { return width; } /** * Return the height of this image */ int SioxImage::getHeight() { return height; } /** * Initialize values. Used by constructors */ void SioxImage::init(unsigned int widthArg, unsigned int heightArg) { valid = true; width = widthArg; height = heightArg; imageSize = width * height; pixdata = new unsigned int[imageSize]; cmdata = new float[imageSize]; for (unsigned long i=0 ; i>24) & 0xff; unsigned int r = ((rgb>>16) & 0xff); unsigned int g = ((rgb>> 8) & 0xff); unsigned int b = ((rgb ) & 0xff); fputc((unsigned char) r, f); fputc((unsigned char) g, f); fputc((unsigned char) b, f); } } fclose(f); return true; } #ifdef HAVE_GLIB /** * Create an image from a GdkPixbuf */ SioxImage::SioxImage(GdkPixbuf *buf) { if (!buf) return; unsigned int width = gdk_pixbuf_get_width(buf); unsigned int height = gdk_pixbuf_get_height(buf); init(width, height); //DO THIS NOW!! guchar *pixldata = gdk_pixbuf_get_pixels(buf); int rowstride = gdk_pixbuf_get_rowstride(buf); int n_channels = gdk_pixbuf_get_n_channels(buf); //### Fill in the cells with RGB values int row = 0; for (unsigned int y=0 ; y> 16) & 0xff;//r p[1] = (rgb >> 8) & 0xff;//g p[2] = (rgb ) & 0xff;//b if ( n_channels > 3 ) { p[3] = (rgb >> 24) & 0xff;//a } p += n_channels; } row += rowstride; } return buf; } #endif /* GLIB */ //######################################################################## //# S I O X //######################################################################## //############## //## PUBLIC //############## /** * Confidence corresponding to a certain foreground region (equals one). */ const float Siox::CERTAIN_FOREGROUND_CONFIDENCE=1.0f; /** * Confidence for a region likely being foreground. */ const float Siox::FOREGROUND_CONFIDENCE=0.8f; /** * Confidence for foreground or background type being equally likely. */ const float Siox::UNKNOWN_REGION_CONFIDENCE=0.5f; /** * Confidence for a region likely being background. */ const float Siox::BACKGROUND_CONFIDENCE=0.1f; /** * Confidence corresponding to a certain background reagion (equals zero). */ const float Siox::CERTAIN_BACKGROUND_CONFIDENCE=0.0f; /** * Construct a Siox engine */ Siox::Siox() { sioxObserver = NULL; init(); } /** * Construct a Siox engine */ Siox::Siox(SioxObserver *observer) { init(); sioxObserver = observer; } /** * */ Siox::~Siox() { cleanup(); } /** * Error logging */ void Siox::error(char *fmt, ...) { char msgbuf[256]; va_list args; va_start(args, fmt); vsnprintf(msgbuf, 255, fmt, args); va_end(args) ; #ifdef HAVE_GLIB g_warning("Siox error: %s\n", msgbuf); #else fprintf(stderr, "Siox error: %s\n", msgbuf); #endif } /** * Trace logging */ void Siox::trace(char *fmt, ...) { char msgbuf[256]; va_list args; va_start(args, fmt); vsnprintf(msgbuf, 255, fmt, args); va_end(args) ; #ifdef HAVE_GLIB g_message("Siox: %s\n", msgbuf); #else fprintf(stdout, "Siox: %s\n", msgbuf); #endif } /** * Progress reporting */ bool Siox::progressReport(float percentCompleted) { if (!sioxObserver) return true; bool ret = sioxObserver->progress(percentCompleted); if (!ret) { trace("User selected abort"); keepGoing = false; } return ret; } /** * Extract the foreground of the original image, according * to the values in the confidence matrix. */ SioxImage Siox::extractForeground(const SioxImage &originalImage, unsigned int backgroundFillColor) { trace("### Start"); init(); keepGoing = true; SioxImage workImage = originalImage; //# fetch some info from the image width = workImage.getWidth(); height = workImage.getHeight(); pixelCount = width * height; image = workImage.getImageData(); cm = workImage.getConfidenceData(); labelField = new int[pixelCount]; trace("### Creating signatures"); //#### create color signatures std::vector knownBg; std::vector knownFg; CieLab *imageClab = new CieLab[pixelCount]; for (unsigned long i=0 ; i= FOREGROUND_CONFIDENCE) knownFg.push_back(lab); } /* std::vector imageClab; for (int y = 0 ; y < workImage.getHeight() ; y++) for (int x = 0 ; x < workImage.getWidth() ; x++) { float cm = workImage.getConfidence(x, y); unsigned int pix = workImage.getPixel(x, y); CieLab lab(pix); imageClab.push_back(lab); if (cm <= BACKGROUND_CONFIDENCE) knownBg.push_back(lab); //note: uses CieLab(rgb) else if (cm >= FOREGROUND_CONFIDENCE) knownFg.push_back(lab); } */ if (!progressReport(10.0)) { error("User aborted"); workImage.setValid(false); delete[] imageClab; delete[] labelField; return workImage; } trace("knownBg:%d knownFg:%d", knownBg.size(), knownFg.size()); std::vector bgSignature ; if (!colorSignature(knownBg, bgSignature, 3)) { error("Could not create background signature"); workImage.setValid(false); delete[] imageClab; delete[] labelField; return workImage; } if (!progressReport(30.0)) { error("User aborted"); workImage.setValid(false); delete[] imageClab; delete[] labelField; return workImage; } std::vector fgSignature ; if (!colorSignature(knownFg, fgSignature, 3)) { error("Could not create foreground signature"); workImage.setValid(false); delete[] imageClab; delete[] labelField; return workImage; } //trace("### bgSignature:%d", bgSignature.size()); if (bgSignature.size() < 1) { // segmentation impossible error("Signature size is < 1. Segmentation is impossible"); workImage.setValid(false); delete[] imageClab; delete[] labelField; return workImage; } if (!progressReport(30.0)) { error("User aborted"); workImage.setValid(false); delete[] imageClab; delete[] labelField; return workImage; } // classify using color signatures, // classification cached in hashmap for drb and speedup purposes trace("### Analyzing image"); std::map hs; unsigned int progressResolution = pixelCount / 10; for (unsigned int i=0; i= FOREGROUND_CONFIDENCE) { cm[i] = CERTAIN_FOREGROUND_CONFIDENCE; } else if (cm[i] <= BACKGROUND_CONFIDENCE) { cm[i] = CERTAIN_BACKGROUND_CONFIDENCE; } else // somewhere in between { bool isBackground = true; std::map::iterator iter = hs.find(i); if (iter != hs.end()) //found { Tupel tupel = iter->second; isBackground = tupel.minBgDist <= tupel.minFgDist; } else { CieLab lab = imageClab[i]; float minBg = lab.diffSq(bgSignature[0]); int minIndex = 0; for (unsigned int j=1; j= UNKNOWN_REGION_CONFIDENCE) cm[i] = CERTAIN_FOREGROUND_CONFIDENCE; else cm[i] = CERTAIN_BACKGROUND_CONFIDENCE; } keepOnlyLargeComponents(UNKNOWN_REGION_CONFIDENCE, 1.5/*sizeFactorToKeep*/); fillColorRegions(); dilate(cm, width, height); if (!progressReport(100.0)) { error("User aborted"); delete[] labelField; workImage.setValid(false); return workImage; } //#### We are done. Now clear everything but the background for (unsigned long i = 0; i max) max = curval; } //Do the Rubner-rule split (sounds like a dance) if (max - min > limits[currentDim]) { float pivotPoint = (min + max) / 2.0; //average unsigned int left = leftBase; unsigned int right = rightBase - 1; //# partition points according to the dimension while (true) { while ( true ) { point = points[left]; if (point(currentDim) > pivotPoint) break; left++; } while ( true ) { point = points[right]; if (point(currentDim) <= pivotPoint) break; right--; } if (left > right) break; point = points[left]; points[left] = points[right]; points[right] = point; left++; right--; } //# Recurse and create sub-trees colorSignatureStage1(points, leftBase, left, recursionDepth + 1, clusterCount, dims); colorSignatureStage1(points, left, rightBase, recursionDepth + 1, clusterCount, dims); } else { //create a leaf CieLab newpoint; newpoint.C = rightBase - leftBase; for (; leftBase < rightBase ; leftBase++) { newpoint.add(points[leftBase]); } //printf("clusters:%d\n", *clusters); if (newpoint.C != 0) newpoint.mul(1.0 / (float)newpoint.C); points[*clusterCount] = newpoint; (*clusterCount)++; } } /** * Stage 2 of the color signature work */ void Siox::colorSignatureStage2(CieLab *points, unsigned int leftBase, unsigned int rightBase, unsigned int recursionDepth, unsigned int *clusterCount, const float threshold, const unsigned int dims) { unsigned int currentDim = recursionDepth % dims; CieLab point = points[leftBase]; float min = point(currentDim); float max = min; for (unsigned int i = leftBase+ 1; i < rightBase; i++) { point = points[i]; float curval = point(currentDim); if (curval < min) min = curval; if (curval > max) max = curval; } //Do the Rubner-rule split (sounds like a dance) if (max - min > limits[currentDim]) { float pivotPoint = (min + max) / 2.0; //average unsigned int left = leftBase; unsigned int right = rightBase - 1; //# partition points according to the dimension while (true) { while ( true ) { point = points[left]; if (point(currentDim) > pivotPoint) break; left++; } while ( true ) { point = points[right]; if (point(currentDim) <= pivotPoint) break; right--; } if (left > right) break; point = points[left]; points[left] = points[right]; points[right] = point; left++; right--; } //# Recurse and create sub-trees colorSignatureStage2(points, leftBase, left, recursionDepth + 1, clusterCount, threshold, dims); colorSignatureStage2(points, left, rightBase, recursionDepth + 1, clusterCount, threshold, dims); } else { //### Create a leaf unsigned int sum = 0; for (unsigned int i = leftBase; i < rightBase; i++) sum += points[i].C; if ((float)sum >= threshold) { float scale = (float)(rightBase - leftBase); CieLab newpoint; for (; leftBase < rightBase; leftBase++) newpoint.add(points[leftBase]); if (scale != 0.0) newpoint.mul(1.0 / scale); points[*clusterCount] = newpoint; (*clusterCount)++; } } } /** * Main color signature method */ bool Siox::colorSignature(const std::vector &inputVec, std::vector &result, const unsigned int dims) { unsigned int length = inputVec.size(); if (length < 1) // no error. just don't do anything return true; CieLab *input = (CieLab *) malloc(length * sizeof(CieLab)); if (!input) { error("Could not allocate buffer for signature"); return false; } for (unsigned int i=0 ; i < length ; i++) input[i] = inputVec[i]; unsigned int stage1length = 0; colorSignatureStage1(input, 0, length, 0, &stage1length, dims); unsigned int stage2length = 0; colorSignatureStage2(input, 0, stage1length, 0, &stage2length, length * 0.001, dims); result.clear(); for (unsigned int i=0 ; i < stage2length ; i++) result.push_back(input[i]); free(input); return true; } /** * */ void Siox::keepOnlyLargeComponents(float threshold, double sizeFactorToKeep) { for (unsigned long idx = 0 ; idx labelSizes; for (unsigned long i=0 ; i= threshold) { regionCount = depthFirstSearch(i, threshold, curlabel++); labelSizes.push_back(regionCount); } if (regionCount>maxregion) { maxregion = regionCount; maxblob = curlabel-1; } } for (unsigned int i=0 ; i pixelsToVisit; int componentSize = 0; if (labelField[startPos]==-1 && cm[startPos]>=threshold) { labelField[startPos] = curLabel; componentSize++; pixelsToVisit.push_back(startPos); } while (pixelsToVisit.size() > 0) { int pos = pixelsToVisit[pixelsToVisit.size() - 1]; pixelsToVisit.erase(pixelsToVisit.end() - 1); unsigned int x = pos % width; unsigned int y = pos / width; // check all four neighbours int left = pos - 1; if (((int)x)-1>=0 && labelField[left]==-1 && cm[left]>=threshold) { labelField[left]=curLabel; componentSize++; pixelsToVisit.push_back(left); } int right = pos + 1; if (x+1 < width && labelField[right]==-1 && cm[right]>=threshold) { labelField[right]=curLabel; componentSize++; pixelsToVisit.push_back(right); } int top = pos - width; if (((int)y)-1>=0 && labelField[top]==-1 && cm[top]>=threshold) { labelField[top]=curLabel; componentSize++; pixelsToVisit.push_back(top); } int bottom = pos + width; if (y+1 < height && labelField[bottom]==-1 && cm[bottom]>=threshold) { labelField[bottom]=curLabel; componentSize++; pixelsToVisit.push_back(bottom); } } return componentSize; } /** * */ void Siox::fillColorRegions() { for (unsigned long idx = 0 ; idx pixelsToVisit; for (unsigned long i=0; i 0) { int pos = pixelsToVisit[pixelsToVisit.size() - 1]; pixelsToVisit.erase(pixelsToVisit.end() - 1); unsigned int x=pos % width; unsigned int y=pos / width; // check all four neighbours int left = pos-1; if (((int)x)-1 >= 0 && labelField[left] == -1 && CieLab::diff(image[left], origColor)<1.0) { labelField[left]=curLabel; cm[left]=CERTAIN_FOREGROUND_CONFIDENCE; // ++componentSize; pixelsToVisit.push_back(left); } int right = pos+1; if (x+1 < width && labelField[right]==-1 && CieLab::diff(image[right], origColor)<1.0) { labelField[right]=curLabel; cm[right]=CERTAIN_FOREGROUND_CONFIDENCE; // ++componentSize; pixelsToVisit.push_back(right); } int top = pos - width; if (((int)y)-1>=0 && labelField[top]==-1 && CieLab::diff(image[top], origColor)<1.0) { labelField[top]=curLabel; cm[top]=CERTAIN_FOREGROUND_CONFIDENCE; // ++componentSize; pixelsToVisit.push_back(top); } int bottom = pos + width; if (y+1 < height && labelField[bottom]==-1 && CieLab::diff(image[bottom], origColor)<1.0) { labelField[bottom]=curLabel; cm[bottom]=CERTAIN_FOREGROUND_CONFIDENCE; // ++componentSize; pixelsToVisit.push_back(bottom); } } //if (componentSize>maxRegion) { // maxRegion=componentSize; //} } } /** * Applies the morphological dilate operator. * * Can be used to close small holes in the given confidence matrix. */ void Siox::dilate(float *cm, int xres, int yres) { for (int y=0; ycm[idx]) cm[idx]=cm[idx+1]; } } for (int y=0; y=1; x--) { int idx=(y*xres)+x; if (cm[idx-1]>cm[idx]) cm[idx]=cm[idx-1]; } } for (int y=0; y cm[idx]) cm[idx]=cm[((y+1)*xres)+x]; } } for (int y=yres-1; y>=1; y--) { for (int x=0; x cm[idx]) cm[idx]=cm[((y-1)*xres)+x]; } } } /** * Applies the morphological erode operator. */ void Siox::erode(float *cm, int xres, int yres) { for (int y=0; y=1; x--) { int idx=(y*xres)+x; if (cm[idx-1] < cm[idx]) cm[idx]=cm[idx-1]; } } for (int y=0; y=1; y--) { for (int x=0; x=2; x--) { int idx=(y*xres)+x; cm[idx]=f3*cm[idx-2]+f2*cm[idx-1]+f1*cm[idx]; } } for (int y=0; y=2; y--) { for (int x=0; x