//============================================== // copyright : (C) 2003-2005 by Will Stokes //============================================== // This program is free software; you can redistribute it // and/or modify it under the terms of the GNU General // Public License as published by the Free Software // Foundation; either version 2 of the License, or // (at your option) any later version. //============================================== //Systemwide includes #include #include #include //Projectwide includes #include "edgeDetect.h" #include "blur.h" #include "../enhancements/contrast.h" //---------------------------------------------- // Inputs: // ------- // QImage* image - image to find edges in // // Outputs: // -------- // QImage* getEdgeImage - returns the produced grayscale edge image // // Other information such as pixel groups etc is also availble through // various accesor method. // // Description: // ------------ // This class is the first known publically available implementation of // Kim, Lee, and Kweon's 2003 paper titled: // "Automatic edge detection using 3x3 ideal binary // pixel patterns and fuzzy-based edge thresholding" // http://rcv.kaist.ac.kr/publication/file/foreign_journal/28_DongSuKim_PRL2004.pdf // // Edge detection is an old problem, an while many use the edge detectors by // Canny, Sobel, etc, they all suffer from a common problem: the user must // tweak a series of poorly understood input parameters to get the ideal edge image. // // Album Shaper was in need of an automatic edge detector for use when // blurring and sharpening images. Having the user manually tweak such // paramters first would not only be annoying but error-prone. In an // effort to make edge detection automatic I took a stab at implementing // this paper and am quite happy with the resulsts... // // http://albumshaper.sourceforge.net/images/teasers/peaksAndValleys.jpg // // Algorithm: // ---------- // While complex, the algorithm can be broken up into a series of // fairly straightforward tasks: // // 1.) "allocateAndInitObjects()" is called to allocate and fill a // few data structures that will be used when finding image edges. // // 2.) "fillLumMapAndLumHistogram"() is called, during which an // luminance map is constructed and luminance histogram populated. // For an m x n image, the luminance map will be a m x n integer array. // // 3.) The luminance histogram is smoothed using "smoothLumHistogram" to // make peak finding less sensative to noise. // // 4.) The fourth step is a little complicated. The edge magnitude and // GSLC (Grey level similitude code) value is computed at each pixel. // The paper takes an interesting approach to edge detection by // classifying pixels into one of 9 groups by first computing the // average luminance for a 3x3 group centered about a pixel. Pixels are // then separated into one of two groups, those that have a luminance // greater than or less than the 3x3 average luminance. For example: // // X // --------- --------- ---------X // | 7 15 18 | | 0 1 1 | | 0 1 X | // | 5 17 20 | --> | 0 1 1 | --> | 0 X 1 | // | 9 8 3 | | 0 0 0 | | X 0 0 | // --------- --------- X--------- // X // // Here the average luminance is 11.333, placing the top right 4 // pixels in one group and the other remaining pixels in another. // The dominant edge diretion is from the bottom left the top right. // The GLSC code is computing by considering the 1/0 values // (1 = pixel in same group as central pixel). The central value // is ignored (it's always 1) leaving us with an 8bit = 2^8 = 256 code. // // In this case the GSLC is: // 0*2^0 + 1*2^1 + 1*2^2 + // 0*2^3 + XXXXX + 1*2^4 + // 0*2^5 + 0*2^6 + 0*2^7 = 22. // // The authors of the paper found pixels with one of five GSLC // codes (15,31,7,47,11) were most often associated with edge pixels // when producing an edge image using various competitive techinques. // By looking up the GSLC for a given pixel later one we can suppress // edges where they most likely do not belong. // // 5.) The fifth step involves grouping pixels by luminance using the // smoothed luminance histogram. This complicated step is brushed off // as being trivial in the paper. Since I struggled a bit with developing // an algorithm for this step, I'll explain my approach in detail // to avoid others suffering. // // Using the smoothed histogram, we first compute the JND or just // noticible differnce using the maximum count that was found. I'm // not sure how appropriate this is, but 2% is the usual quantity // used in other contexts, and it works well here, so 2% it is. // // Next we walk through the smoothed luminance histogram and find // the midpoint of the valleys. We accomplish this by updating an // index of the deepend last known valley. As the valley slopes // down and we move across it we update this last best known index. // Once the valley slopes up one JND above the last deepend location // found we mark that valley midpoint and move on. // // Once all valley midpoints have been marked we can quickly deduce // how many peaks must exist. We pass across the smoothed luminance // histogram again finding the peak index for each pixel between // valley midpoints. For all future work pixels one JND+- the peak // center will be used. // // 6.) The sixth step, "computeClusterStatistics()", computes various // cluster-specific statsitics that will be used to determine cluster // thresholds. The paper was rather vague in this area, but after // experimenting with various interpreatations of what they were trying // to say I think I finally got it right. // // First, we iterate over all image pixels, determine which pixel group // they belong by comparing luminance endpoints for all pixelclusters, // and update total edge mag, num pixels, and an edgeMagHistogram for // the given pixel group they belong to. // // Next we compute the average edge meganitude and most frequent edge // magnitude observed for each pixel cluster, in addition to normalizing // the cluster pixel count variable to [0,1] // // 7.) The seventh step is quite complicated and encompases computing the // edge thresholds for each pixel cluster using the 18-rule fuzzy // logic approach put forward by the paper. There is nothing ground // breaking here, just a lot of complicated fuzzy logic, although most // of the effort is put into computing the centroid at the end. I had // never touched fuzzy logic before, but found this article more than // helpful getting myself up to speed: // // http://www.doc.ic.ac.uk/~nd/surprise_96/journal/vol2/sbaa/article2.html // // 8.) The eigth and final step, actually constructing the edge image, is fairly // straight forward and employs techinques (such as non-maximum suppression or NMS) // anyone who has implemented Canny shoudl be familiar with. // // First, one looks up the ESF (edge shape factor) for a given pixel from a look-up // table. These values were computed by generating a ton of edge images by carefully // setting Canny, Sobel, etc params, then identifying how often a pixel // with a given GSLC code was judged to be an edge. Hence ESF's will fall in // the [0,1] range and help to suppress edges where no clear edge really // can be claimed to exist, e.g. // // . # . // # # # <- If there is an edge here, care to explain what the // . # . edge direction is?!! // // // If the ESF for a give pixel is 0 we skip it, it's not an edge. // // Next, we look up a pixels edge magnitude threshold by identifying the // pixel cluster is belongs to using the pixels luminance using the // luminance map. // // If the pixels edge magnitude is less than the threshold we skip the pixel, // this filtersout low lying noise. // // Finally, the direction of the pixel is looked up using its GSLC and // NMS (non-maximum suppression is applied). If the pixel has a greater // egdge magnitude than either of its neighbors along the edge direction then // the edge is marked. // // Final Remarks: // -------------- // Despite the involved complexity, the implementation appears to work // really really well. I consider this one of the secret gems of Album Shaper // and hope to make good use of it in the future for things other than // just sharpening and bluring. The only caveat is that edge detection // does take a few CPU cycles. //---------------------------------------------- //============================================== EdgeDetect::EdgeDetect( QImage* image ) { //load image this->image = image; //allocate and initialize objects used for edge detection allocateAndInitObjects(); //fill lum map and lum histogram fillLumMapAndLumHistogram(); //fill smoothed histogram smoothLumHistogram(); //compute edge magnitude and GSLC maps computeEdgeMagAndGSLCmaps(); //determine pixel clusters findPixelClusters(); computeClusterStatistics(); computeClusterThresholds(); constructEdgeImage(); } //============================================== EdgeDetect::~EdgeDetect() { deallocateObjects(); } //============================================== int EdgeDetect::getNumClusters() { return numClusters; } //============================================== PixelCluster* EdgeDetect::getClusters() { return clusters; } //============================================== int* EdgeDetect::getPeaks() { return clusterPeaks; } //============================================== int* EdgeDetect::getSmoothHist() { return smoothLumHist; } //============================================== QImage* EdgeDetect::getEdgeImage() { return image; } //============================================== int* EdgeDetect::getClusterMap() { //construct map int* clusterMap = new int[image->width() * image->height()]; //iterate over all pixels, determine cluster each pixel belongs to int i, cluster; for(i=0; iwidth()*image->height(); i++) { for(cluster=0; cluster= clusters[cluster].minLuminance && lumMap[i] <= clusters[cluster].maxLuminance ) { clusterMap[i] = cluster; break; } } //cluster } //pixel return clusterMap; } //============================================== void EdgeDetect::allocateAndInitObjects() { //initialize: //-luminosity histogram //-smoothed luminosity histogram //-identified peak regions int i; for(i=0; i<256; i++) { lumHist[i] = 0; smoothLumHist[i] = 0; clusterPeaks[i] = 0; } //allocate luminance map lumMap = new int[image->width() * image->height()]; //allocate edge magnitude map edgeMagMap = new float[image->width() * image->height()]; //allocate GSLC map GSLCmap = new int[image->width() * image->height()]; //construct LUT constructGSLClut(); } //============================================== void EdgeDetect::fillLumMapAndLumHistogram() { int x, y; QRgb* rgb; uchar* scanLine; int lumVal; for( y=0; yheight(); y++) { scanLine = image->scanLine(y); for( x=0; xwidth(); x++) { //get lum value for this pixel rgb = ((QRgb*)scanLine+x); lumVal = qGray(*rgb); //store in lum map lumMap[x + y*image->width()] = lumVal; //update lum histogram lumHist[ lumVal ]++; } } } //============================================== void EdgeDetect::smoothLumHistogram() { #define FILTER_SIZE 5 int filter[FILTER_SIZE] = {2, 5, 8, 5, 2}; int i,j; int filterIndex, sum, total; for(i = 0; i<256; i++) { sum = 0; total = 0; for( j= -FILTER_SIZE/2; j <= FILTER_SIZE/2; j++) { if( i+j > 0 && i+j < 256 ) { filterIndex = j+ FILTER_SIZE/2; total+= filter[filterIndex] * lumHist[i+j]; sum += filter[filterIndex]; } } smoothLumHist[i] = total / sum; } } //============================================== void EdgeDetect::computeEdgeMagAndGSLCmaps() { int x, y; int idealPattern[9]; int pixelLums[9]; //------- //iterate over all pixels for( y=0; yheight(); y++) { for( x=0; xwidth(); x++) { //compute pixel luminances for entire grid pixelLums[0] = pixelLum(x-1,y-1); pixelLums[1] = pixelLum(x ,y-1); pixelLums[2] = pixelLum(x+1,y-1); pixelLums[3] = pixelLum(x-1,y ); pixelLums[4] = pixelLum(x ,y ); pixelLums[5] = pixelLum(x+1,y ); pixelLums[6] = pixelLum(x-1,y+1); pixelLums[7] = pixelLum(x ,y+1); pixelLums[8] = pixelLum(x+1,y+1); //compute average float avg = 0; int i; for(i=0; i<=8; i++) { avg+= pixelLums[i]; } avg = avg / 9; //determine ideal pattern and I0 and I1 averages int centerPixelLum = pixelLums[4]; float centerDiff = centerPixelLum - avg; float I0avg = 0; int I0count = 0; float I1avg = 0; int I1count = 0; for(i=0; i<=8; i++) { if( centerDiff * (pixelLums[i]-avg) >=0 ) { I1avg+=pixelLums[i]; I1count++; idealPattern[i] = 1; } else { I0avg+=pixelLums[i]; I0count++; idealPattern[i] = 0; } } //compute and store edge magnitude if(I0count > 0) I0avg = I0avg/I0count; if(I1count > 0) I1avg = I1avg/I1count; edgeMagMap[x + y*image->width()] = QABS( I1avg - I0avg ); //compute and store GSLC int GSLC=0; int weight = 1; for(i=0; i<9; i++) { //skip center if(i == 4) continue; if(idealPattern[i] == 1) { GSLC+=weight; } weight = weight*2; } GSLCmap[x + y*image->width()] = GSLC; } //x } //y } //============================================== int EdgeDetect::pixelLum(int x, int y) { int clampedX = QMAX( QMIN( x, image->width()-1), 0); int clampedY = QMAX( QMIN( y, image->height()-1), 0); return lumMap[ clampedX + clampedY * image->width() ]; } //============================================== void EdgeDetect::findPixelClusters() { //find max count int maxCount = 0; int i; for(i=0; i<256; i++) { if(smoothLumHist[i] > maxCount) maxCount = smoothLumHist[i]; } //compute JND for histogram (2% of total spread) int histJND = maxCount/50; //construct temporary array for valley locations //1's will indicate a valley midpoint int tmpValleyArray[256]; for(i=0; i<256; i++) { tmpValleyArray[i] = 0; } //move across histogram finding valley midpoints int curTrackedMin = smoothLumHist[0]; //first and last indices tracked min was observed int firstMinIndex = 0; int lastMinIndex = 0; //only add valley midpoint if finished tracking a descent bool slopeNeg = false; for(i = 1; i<256; i++ ) { if( smoothLumHist[i] < curTrackedMin - histJND ) { //found a descent! slopeNeg = true; curTrackedMin = smoothLumHist[i]; firstMinIndex = i; } //starting to go up again, add last min to list else if( smoothLumHist[i] > curTrackedMin + histJND ) { //if finished tracing a negative slope find midpoint and set location to true if(slopeNeg) { tmpValleyArray[ (firstMinIndex + lastMinIndex)/2 ] = 1; } curTrackedMin = smoothLumHist[i]; slopeNeg = false; } else { //still tracking a min, update the right //hand index. center of valley is found //by averaging first and last min index lastMinIndex = i; } } //count valleys int numValleys = 0; for(i=0; i<256; i++) { if(tmpValleyArray[i] == 1 ) numValleys++; } //determine number of clusters numClusters = numValleys-1; if(tmpValleyArray[0] != 1) numClusters++; if(tmpValleyArray[255] != 1) numClusters++; //allocate clusters clusters = new PixelCluster[numClusters]; //automatically start first cluster int cluster=0; clusters[cluster].minLuminance = 0; //initialize left and right boundaries of all clusters for(i=1; i<256; i++) { //reached next valley, end cluster if( tmpValleyArray[i] == 1) { clusters[cluster].maxLuminance = i-1; cluster++; clusters[cluster].minLuminance = i; } //end last cluster automatically at end else if(i == 255) { clusters[cluster].maxLuminance = i; } } //determine cluster peaks for(cluster=0; cluster smoothLumHist[maxIndex]) maxIndex = i; } //mark peaks int lumJND = 255/50; for(i=QMAX(0, maxIndex-lumJND); iwidth()*image->height(); i++) { //skip pixels that don't belong to peaks if( clusterPeaks[ lumMap[i] ] != 1) continue; //determine cluster pixel belongs to int cluster; for(cluster=0; cluster= clusters[cluster].minLuminance && lumMap[i] <= clusters[cluster].maxLuminance ) { clusters[cluster].totalEdgeMagnitude+= edgeMagMap[i]; clusters[cluster].numPixels++; clusters[cluster].edgeMagHistogram[ QMIN( QMAX( (int)edgeMagMap[i], 0), 255) ]++; break; } } //cluster } //pixel i //iterate over clusters to determine min and max peak cluster sizes minClusterSize = clusters[0].numPixels; maxClusterSize = clusters[0].numPixels; for(cluster=1; cluster maxClusterSize) maxClusterSize = clusters[cluster].numPixels; } //iterate over clusters one final time to deduce normalized inputs to fuzzy logic process int JND = 255/50; for(cluster=0; cluster clusters[cluster].edgeMagHistogram[ mode ] ) mode = i; } clusters[cluster].mode = QMIN( mode, 2*JND ); clusters[cluster].pixelCount = ((float)(clusters[cluster].numPixels - minClusterSize)) / (maxClusterSize - minClusterSize); } } //============================================== //compute edge thresholds for each cluster using 18-rule fuzzy logic approach void EdgeDetect::computeClusterThresholds() { //iterate over each cluster int cluster; float S1,M1,L1; float S2,M2,L2; float S3,L3; float outS, outM, outL; int JND = 255/50; for(cluster=0; cluster 2 ) { outM += S1*S2*S3; //rule 1 //rule 2 if( clusters[cluster].meanMode < clusters[cluster].mode ) outS += S1*S2*L3; else outM += S1*S2*L3; outM += S1*M2*S3; //rule 3 outM += S1*M2*L3; //rule 4 outM += S1*L2*S3; //rule 5 outM += S1*L2*L3; //rule 6 outM += M1*S2*S3; //rule 7 outM += M1*S2*L3; //rule 8 outM += M1*M2*S3; //rule 9 outL += M1*M2*L3; //rule 10 outM += M1*L2*S3; //rule 11 outL += M1*L2*L3; //rule 12 outM += L1*S2*S3; //rule 13 outL += L1*S2*L3; //rule 14 outM += L1*M2*S3; //rule 15 outL += L1*M2*L3; //rule 16 outL += L1*L2*S3; //rule 17 outL += L1*L2*L3; //rule 18 } //Out 2 else { outL += S1*S2*S3; //rule 1 outL += S1*S2*L3; //rule 2 outM += S1*M2*S3; //rule 3 outL += S1*M2*L3; //rule 4 outM += S1*L2*S3; //rule 5 outM += S1*L2*L3; //rule 6 outL += M1*S2*S3; //rule 7 outL += M1*S2*L3; //rule 8 outL += M1*M2*S3; //rule 9 outL += M1*M2*L3; //rule 10 outL += M1*L2*S3; //rule 11 outL += M1*L2*L3; //rule 12 outL += L1*S2*S3; //rule 13 outL += L1*S2*L3; //rule 14 outL += L1*M2*S3; //rule 15 outL += L1*M2*L3; //rule 16 outL += L1*L2*S3; //rule 17 outL += L1*L2*L3; //rule 18 } //find centroid - Beta[k] float A = outM + 0.5f; float B = 2.5f - outM; float C = 1.5f * (outL + 1); float D = 1.5f * (outM + 1); float E = 2.5f - outL; //--------------------------------------------------------------- //Case 1: Both outM and outL are above intersection point of diagonals if( outM > 0.5f && outL > 0.5f ) { //find area of 7 subregions float area1 = ((A-0.5f)*outM)/2; float area2 = outM * (B-A); float area3 = ((2.1f-B) * (outM - 0.5)) / 2; float area4 = (2.1 - B) * 0.5f; float area5 = ((C - 2.1f) * (outL - 0.5)) / 2; float area6 = (C - 2.1f) * 0.5f; float area7 = (3.0f - C) * outL; //find half of total area float halfArea = (area1 + area2 + area3 + area4 + area5 + area6 + area7) / 2; //determine which region split will be within and resulting horizontal midpoint //Within area 1 if( area1 > halfArea ) { clusters[cluster].beta = 0.5f + (float)sqrt(2*halfArea); } //Within area 2 else if( area1 + area2 > halfArea ) { clusters[cluster].beta = ((halfArea - area1) / outM) + A; } //Within area 3-4 else if( area1 + area2 + area3 + area4 > halfArea ) { float a = -0.5f; float b = 2.8f; float c = area1 + area2 + area3 - halfArea - B/2 - 2.625f; clusters[cluster].beta = (-b + (float)sqrt( b*b - 4*a*c )) / (2*a); } //Within area 5-6 else if( area1 + area2 + area3 + area4 + area5 + area6 > halfArea ) { float a = 1.0f/3; float b = -0.7f; float c = area1 + area2 + area3 + area4 - halfArea; clusters[cluster].beta = (-b + (float)sqrt( b*b - 4*a*c )) / (2*a); } //Within area 7 else { clusters[cluster].beta = ((halfArea - (area1 + area2 + area3 + area4 + area5 + area6) ) / outL) + C; } } //end case 1 //--------------------------------------------------------------- //Case 2 else if ( outM < 0.5f && outL > outM ) { //find area of 5 subregions float area1 = (outM*(A-0.5f)) / 2; float area2 = (D-A) * outM; float area3 = ((C-D) * (outL - outM)) / 2; float area4 = (C-D) * outM; float area5 = (3.0f - C) * outL; //find half of total area float halfArea = (area1 + area2 + area3 + area4 + area5) / 2; //determine which region split will be within and resulting horizontal midpoint //Within area 1 if( area1 > halfArea ) { clusters[cluster].beta = 0.5f + (float)sqrt(2*halfArea); } //Within area 2 else if( area1 + area2 > halfArea ) { clusters[cluster].beta = ((halfArea - area1) / outM) + A; } //Within area 3-4 else if( area1 + area2 + area3 + area4 > halfArea ) { float a = 1.0f/3.0f; float b = outM - 0.5f - D/3; float c = area1 + area2 - D*outM + D/2 - halfArea; clusters[cluster].beta = (-b + (float)sqrt( b*b - 4*a*c )) / (2*a); } //Within area5 else { clusters[cluster].beta = ((halfArea - (area1 + area2 + area3 + area4) ) / outL) + C; } } //end case 2 //--------------------------------------------------------------- //Case 3 else { //find area of 5 subregions float area1 = (outM*(A-0.5f)) / 2; float area2 = (B-A) * outM; float area3 = ((E-B) * (outM - outL)) / 2; float area4 = (E-B) * outL; float area5 = (3.0f - E) * outL; //find half of total area float halfArea = (area1 + area2 + area3 + area4 + area5) / 2; //determine which region split will be within and resulting horizontal midpoint //Within area 1 if( area1 > halfArea ) { clusters[cluster].beta = 0.5f + (float)sqrt(2*halfArea); } //Within area 2 else if( area1 + area2 > halfArea ) { clusters[cluster].beta = ((halfArea - area1) / outM) + A; } //Within area 3-4 else if( area1 + area2 + area3 + area4 > halfArea ) { float a = -0.5f; float b = E/2 + 2.5f/2; float c = area3 - 2.5f*E/2; clusters[cluster].beta = (-b + (float)sqrt( b*b - 4*a*c )) / (2*a); } //Within area5 else { clusters[cluster].beta = ((halfArea - (area1 + area2 + area3 + area4) ) / outL) + E; } } //end case 3 //--------------------------------------------------------------- //Compute edge threshold int lumJND = 255/50; clusters[cluster].edgeThreshold = clusters[cluster].mode + clusters[cluster].beta*lumJND; } //end for cluster } //============================================== void EdgeDetect::constructEdgeImage() { int x, y; QRgb* rgb; uchar* scanLine; for( y=0; yheight(); y++) { scanLine = image->scanLine(y); for( x=0; xwidth(); x++) { //initialize pixel to black rgb = ((QRgb*)scanLine+x); *rgb = qRgb( 0, 0, 0 ); //lookup ESF for this pixel float ESF = LUT[ GSLCmap[x + y*image->width()] ].ESF; //If ESF value for this pixel is 0 skip if( ESF == 0.0f ) continue; //lookup edge magnitude threshold float lum = lumMap[x + y*image->width()]; float edgeMagThresh = -1.0f; int cluster; for(cluster=0; cluster= clusters[cluster].minLuminance && lum <= clusters[cluster].maxLuminance) { edgeMagThresh = clusters[cluster].edgeThreshold; break; } } //if cluster not found bail if( cluster >= numClusters ) { // cout << "Error! Could not find cluster pixel belonged to!\n"; continue; } //if edge mag below thresh then skip if( edgeMagMap[x + y*image->width()] < edgeMagThresh ) continue; //ok, last checks implement NMS (non-maximum supression) int direction = LUT[ GSLCmap[x + y*image->width()] ].direction; int neighborIndex1 = -1; int neighborIndex2 = -1; if( direction == 0) { if( x > 0) neighborIndex1 = x-1 + y*image->width(); if( x < image->width() - 1 ) neighborIndex2 = x+1 + y*image->width(); } else if(direction == 1) { if( x > 0 && y < image->height() - 1 ) neighborIndex1 = x-1 + (y+1)*image->width(); if( x < image->width() - 1 && y > 0 ) neighborIndex2 = x+1 + (y-1)*image->width(); } else if(direction == 2) { if( y < image->height() - 1 ) neighborIndex1 = x + (y+1)*image->width(); if( y > 0) neighborIndex2 = x + (y-1)*image->width(); } else if(direction == 3) { if( x < image->width() - 1 && y < image->height() - 1 ) neighborIndex1 = x+1 + (y+1)*image->width(); if( x > 0 && y > 0 ) neighborIndex2 = x-1 + (y-1)*image->width(); } //neighbor 1 has higher confidence, skip! if( neighborIndex1 != -1 && LUT[ GSLCmap[neighborIndex1] ].ESF * edgeMagMap[neighborIndex1] > ESF * edgeMagMap[x + y*image->width()] ) continue; //neighbor 2 has higher confidence, skip! if( neighborIndex2 != -1 && LUT[ GSLCmap[neighborIndex2] ].ESF * edgeMagMap[neighborIndex2] > ESF * edgeMagMap[x + y*image->width()] ) continue; //All tests passed! Mark edge! *rgb = qRgb( 255, 255, 255 ); } //x } //y //blur image - all of it blurImage( *image, 2.0f ); //normalize image enhanceImageContrast( image ); } //============================================== void EdgeDetect::deallocateObjects() { delete[] lumMap; lumMap = NULL; delete[] edgeMagMap; edgeMagMap = NULL; delete[] GSLCmap; GSLCmap = NULL; delete[] clusters; clusters = NULL; } //============================================== void EdgeDetect::constructGSLClut() { //---------------------- //First fill entire table with 0 ESF's and invalid directions int i; for(i=0; i<256; i++) { LUT[i].ESF = 0.0f; LUT[i].direction = -1; } //---------------------- //Next code in all pattern that are highly //likely to be on edges as described in the paper //---------------------- //Pattern (a) // ### // ##. // ... LUT[15].ESF = 0.179f; LUT[15].direction = 3; // ... // .## // ### LUT[240].ESF = 0.179f; LUT[240].direction = 3; // ### // .## // ... LUT[23].ESF = 0.179f; LUT[23].direction = 1; // ... // ##. // ### LUT[232].ESF = 0.179f; LUT[232].direction = 1; // ##. // ##. // #.. LUT[43].ESF = 0.179f; LUT[43].direction = 3; // ..# // .## // .## LUT[212].ESF = 0.179f; LUT[212].direction = 3; // #.. // ##. // ##. LUT[105].ESF = 0.179f; LUT[105].direction = 1; // .## // .## // ..# LUT[150].ESF = 0.179f; LUT[150].direction = 1; //---------------------- //Pattern (b) // ### // ### // ... LUT[31].ESF = 0.137f; LUT[31].direction = 2; // ... // ### // ### LUT[248].ESF = 0.137f; LUT[248].direction = 2; // ##. // ##. // ##. LUT[107].ESF = 0.137f; LUT[107].direction = 0; // .## // .## // .## LUT[214].ESF = 0.137f; LUT[214].direction = 0; //---------------------- //Pattern (c) // ### // .#. // ... LUT[7].ESF = 0.126f; LUT[7].direction = 2; // ... // .#. // ### LUT[224].ESF = 0.126f; LUT[224].direction = 2; // #.. // ##. // #.. LUT[41].ESF = 0.126f; LUT[41].direction = 0; // ..# // .## // ..# LUT[148].ESF = 0.126f; LUT[148].direction = 0; //---------------------- //Pattern (d) // ### // ##. // #.. LUT[47].ESF = 0.10f; LUT[47].direction = 3; // ..# // .## // ### LUT[244].ESF = 0.10f; LUT[244].direction = 3; // ### // .## // ..# LUT[151].ESF = 0.10f; LUT[151].direction = 1; // #.. // ##. // ### LUT[233].ESF = 0.10f; LUT[233].direction = 1; //---------------------- //Pattern (e) // ##. // ##. // ... LUT[11].ESF = 0.10f; LUT[11].direction = 3; // ... // .## // .## LUT[208].ESF = 0.10f; LUT[208].direction = 3; // .## // .## // ... LUT[22].ESF = 0.10f; LUT[22].direction = 1; // ... // ##. // ##. LUT[104].ESF = 0.10f; LUT[104].direction = 1; //---------------------- } //==============================================