/**Each row of img is projected onto the vertical axis. Resulting data length will be equal to the height of img. The profile is a summation of the grayscale values in each row. If fNormalize is true, then each value is divided by img.width() so it is the average grayscale value for the row instead of the sum. If fNormalize is true, the resulting profile values are divided by the image width. */ void DProfile::getImageVerticalProfile(const DImage &img, bool fNormalize){ int w, h; w = img.width(); h = img.height(); // allocate the rgProf array if(NULL == rgProf){ rgProf = (double*)malloc(h * sizeof(double)); D_CHECKPTR(rgProf); len = h; } else{ if(len != h){ rgProf = (double*)realloc(rgProf,h*sizeof(double)); D_CHECKPTR(rgProf); len = h; } } switch(img.getImageType()){ case DImage::DImage_u8: { D_uint8 *pu8; pu8=img.dataPointer_u8(); for(int y = 0, idx=0; y < h; ++y){ rgProf[y] = 0.; for(int x = 0; x < w; ++x, ++idx){ rgProf[y] += pu8[idx]; } if(fNormalize) rgProf[y] /= w; } } break; case DImage::DImage_flt_multi: { float *pflt; if(img.numChannels() > 1){ fprintf(stderr,"DProfile::getImageVerticalProfile() floats only " "supported with a single channel\n"); abort(); } pflt=img.dataPointer_flt(0); for(int y = 0, idx=0; y < h; ++y){ rgProf[y] = 0.; for(int x = 0; x < w; ++x, ++idx){ rgProf[y] += pflt[idx]; } if(fNormalize) rgProf[y] /= w; } } break; default: fprintf(stderr, "Not yet implemented!\n"); abort(); }//end switch(img.getImageType()) }
/**Max runlength in each column of img is projected onto the horizontal axis. Resulting data length will be equal to the width of img. If fNormalize is true, each profile value will be divided by image height, so the value is a fraction of the image height instead of a number of pixels. */ void DProfile::getHorizMaxRunlengthProfile(const DImage &img, D_uint32 rgbVal, bool fNormalize){ int w, h; unsigned int *rgRunlengths; w = img.width(); h = img.height(); // allocate the rgProf array if(NULL == rgProf){ rgProf = (double*)malloc(w * sizeof(double)); D_CHECKPTR(rgProf); len = w; } else{ if(len != w){ rgProf = (double*)realloc(rgProf,w*sizeof(double)); D_CHECKPTR(rgProf); len = w; } } rgRunlengths = (unsigned int*)malloc(sizeof(unsigned int)*w); D_CHECKPTR(rgRunlengths); memset(rgRunlengths, 0, sizeof(unsigned int)*w); memset(rgProf, 0, sizeof(double) * w); switch(img.getImageType()){ case DImage::DImage_u8: { D_uint8 *pu8; pu8=img.dataPointer_u8(); for(int y = 0, idx=0; y < h; ++y){ for(int x = 0; x < w; ++x, ++idx){ if((D_uint8)rgbVal == pu8[idx]){//increment run length for this col ++(rgRunlengths[x]); if(rgRunlengths[x] > rgProf[x]) rgProf[x] = (double)rgRunlengths[x]; } else{ rgRunlengths[x] = 0; } } } if(fNormalize){ for(int x = 0; x < w; ++x) rgProf[x] /= h; } } break; default: fprintf(stderr, "Not yet implemented!\n"); abort(); }//end switch(img.getImageType()) free(rgRunlengths); }
/**Avg runlength in each column of img is projected onto the vertical axis. Resulting data length will be equal to the height of img. If fNormalize is true, each profile value will be divided by image width, so the value is a fraction of the image width instead of a number of pixels. */ void DProfile::getVertAvgRunlengthProfile(const DImage &img, D_uint32 rgbVal, bool fNormalize){ int w, h; unsigned int runLength, numRuns; w = img.width(); h = img.height(); // allocate the rgProf array if(NULL == rgProf){ rgProf = (double*)malloc(h * sizeof(double)); D_CHECKPTR(rgProf); len = h; } else{ if(len != h){ rgProf = (double*)realloc(rgProf,h*sizeof(double)); D_CHECKPTR(rgProf); len = h; } } switch(img.getImageType()){ case DImage::DImage_u8: { D_uint8 *pu8; pu8=img.dataPointer_u8(); for(int y = 0, idx=0; y < h; ++y){ rgProf[y] = 0.; runLength = 0; numRuns = 0; for(int x = 0; x < w; ++x, ++idx){ if((D_uint8)rgbVal == pu8[idx]){//increment run length for this row if(0==runLength) ++numRuns; ++runLength; ++(rgProf[y]); } else{ runLength = 0; } } if(numRuns > 0) rgProf[y] /= numRuns; //(we have sum and need to divide for avg) if(fNormalize) rgProf[y] /= w; } } break; default: fprintf(stderr, "Not yet implemented!\n"); abort(); }//end switch(img.getImageType()) }
/**Each column of img is projected onto the horizontal axis. Resulting data length will be equal to the width of img. If fNormalize is true, the resulting profile values are divided by the image height. */ void DProfile::getImageHorizontalProfile(const DImage &img, bool fNormalize){ int w, h; w = img.width(); h = img.height(); // allocate the rgProf array if(NULL == rgProf){ rgProf = (double*)malloc(w * sizeof(double)); D_CHECKPTR(rgProf); len = w; } else{ if(len != w){ rgProf = (double*)realloc(rgProf,w*sizeof(double)); D_CHECKPTR(rgProf); len = w; } } memset(rgProf, 0, sizeof(double) * w); switch(img.getImageType()){ case DImage::DImage_u8: { D_uint8 *pu8; pu8=img.dataPointer_u8(); for(int y = 0, idx=0; y < h; ++y){ for(int x = 0; x < w; ++x, ++idx){ rgProf[x] += pu8[idx]; } } if(fNormalize){ for(int x = 0; x < w; ++x) rgProf[x] /= h; } } break; default: fprintf(stderr, "Not yet implemented!\n"); abort(); }//end switch(img.getImageType()) }
///Max filter imgSrc with current settings and store result in imgDst void DMaxFilter::filterImage_(DImage &imgDst, const DImage &imgSrc, bool fAlreadyPadded, DProgress *pProg){ DMaxFiltType filtType; DImage *pImgPad; int wKern, hKern; int numKernPxls; int wUnpad, hUnpad; #ifndef D_NOTHREADS MAX_HUANG_8_THREAD_PARAMS_T rgParms[MAX_MAXFILT_THREADS]; pthread_t rgThreadID[MAX_MAXFILT_THREADS]; #endif filtType = _maxFiltType; pImgPad = (DImage*)&imgSrc; if(!fAlreadyPadded){ pImgPad = new DImage(); imgSrc.padEdges_(*pImgPad, _radiusX, _radiusX, _radiusY, _radiusY, DImage::DImagePadReplicate); } wUnpad = pImgPad->width()-(_radiusX*2); hUnpad = pImgPad->height()-(_radiusY*2); wKern = _radiusX * 2 + 1; hKern = _radiusY * 2 + 1; if(NULL == rgKern){ rgKern = (unsigned char*)malloc(sizeof(unsigned char) * wKern * hKern); if(!rgKern){ fprintf(stderr, "DMaxFilter::filterImage_() out of memory\n"); exit(1); } rgRightEdge = (int*)malloc(sizeof(int)*hKern); if(!rgRightEdge){ fprintf(stderr, "DMaxFilter::filterImage_() out of memory\n"); exit(1); } if(DMaxFilt_circle == filtType){ fill_circle_kern_offsets(_radiusX, _radiusY, rgKern, rgRightEdge, &numKernPxls); } else{ fill_square_kern_offsets(_radiusX, _radiusY, rgKern, rgRightEdge, &numKernPxls); } } switch(imgSrc.getImageType()){ case DImage::DImage_u8: { imgDst.create(wUnpad, hUnpad, DImage::DImage_u8, 1, imgSrc.getAllocMethod()); #ifndef D_NOTHREADS for(int tnum = 1; tnum < _numThreads; ++tnum){ rgParms[tnum].pImgDst = &imgDst; rgParms[tnum].pImgSrc = pImgPad; rgParms[tnum].radiusX = _radiusX; rgParms[tnum].radiusY = _radiusY; rgParms[tnum].wKern = wKern; rgParms[tnum].hKern = hKern; rgParms[tnum].rgKern = rgKern; rgParms[tnum].numKernPxls = numKernPxls; rgParms[tnum].rgRightEdge = rgRightEdge; rgParms[tnum].pProg = NULL; rgParms[tnum].progStart = 0; rgParms[tnum].progMax = 1; rgParms[tnum].threadNumber = tnum; rgParms[tnum].numThreads = _numThreads; if(0 != pthread_create(&rgThreadID[tnum], NULL, DMaxFilter::DMaxFilter_Huang8threadWrap, &rgParms[tnum])){ fprintf(stderr, "DMaxFilter::filterImage_() failed to spawn " "thread #%d. Exiting.\n", tnum); exit(1); } } #endif maxFiltHuang_u8(imgDst, *pImgPad, _radiusX, _radiusY, wKern, hKern, rgKern, numKernPxls, rgRightEdge, pProg, 0, hUnpad+1, 0, _numThreads); #ifndef D_NOTHREADS for(int tnum = 1; tnum < _numThreads; ++tnum){ if(pthread_join(rgThreadID[tnum],NULL)) fprintf(stderr, "DMaxFilter::filterImage_() failed to join " "thread %d\n", tnum); } #endif if(NULL != pProg){ pProg->reportStatus(hUnpad+1, 0, hUnpad+1);//report progress complete } } break; case DImage::DImage_RGB: { DImage imgR, imgG, imgB; DImage imgRDst, imgGDst, imgBDst; imgRDst.create(wUnpad, hUnpad, DImage::DImage_u8, 1, imgSrc.getAllocMethod()); imgGDst.create(wUnpad, hUnpad, DImage::DImage_u8, 1, imgSrc.getAllocMethod()); imgBDst.create(wUnpad, hUnpad, DImage::DImage_u8, 1, imgSrc.getAllocMethod()); pImgPad->splitRGB(imgR, imgG, imgB); #ifndef D_NOTHREADS for(int tnum = 1; tnum < _numThreads; ++tnum){ rgParms[tnum].pImgDst = &imgRDst; rgParms[tnum].pImgSrc = &imgR; rgParms[tnum].radiusX = _radiusX; rgParms[tnum].radiusY = _radiusY; rgParms[tnum].wKern = wKern; rgParms[tnum].hKern = hKern; rgParms[tnum].rgKern = rgKern; rgParms[tnum].numKernPxls = numKernPxls; rgParms[tnum].rgRightEdge = rgRightEdge; rgParms[tnum].pProg = NULL; rgParms[tnum].progStart = 0; rgParms[tnum].progMax = 1; rgParms[tnum].threadNumber = tnum; rgParms[tnum].numThreads = _numThreads; if(0 != pthread_create(&rgThreadID[tnum], NULL, DMaxFilter::DMaxFilter_Huang8threadWrap, &rgParms[tnum])){ fprintf(stderr, "DMaxFilter::filterImage_() failed to spawn " "thread #%d. Exiting.\n",tnum); exit(1); } } #endif maxFiltHuang_u8(imgRDst, imgR, _radiusX, _radiusY, wKern, hKern, rgKern, numKernPxls, rgRightEdge, pProg, 0, 3 * hUnpad); #ifndef D_NOTHREADS for(int tnum = 1; tnum < _numThreads; ++tnum){ if(pthread_join(rgThreadID[tnum],NULL)) fprintf(stderr, "DMaxFilter::filterImage_() failed to join " "thread %d\n", tnum); } for(int tnum = 1; tnum < _numThreads; ++tnum){ rgParms[tnum].pImgDst = &imgGDst; rgParms[tnum].pImgSrc = &imgG; if(0 != pthread_create(&rgThreadID[tnum], NULL, DMaxFilter::DMaxFilter_Huang8threadWrap, &rgParms[tnum])){ fprintf(stderr, "DMaxFilter::filterImage_() failed to spawn " "thread #%d. Exiting.\n",tnum); exit(1); } } #endif maxFiltHuang_u8(imgGDst, imgG, _radiusX, _radiusY, wKern, hKern, rgKern, numKernPxls, rgRightEdge, pProg, hUnpad, 3 * hUnpad); #ifndef D_NOTHREADS for(int tnum = 1; tnum < _numThreads; ++tnum){ if(pthread_join(rgThreadID[tnum],NULL)) fprintf(stderr, "DMaxFilter::filterImage_() failed to join " "thread %d\n", tnum); } for(int tnum = 1; tnum < _numThreads; ++tnum){ rgParms[tnum].pImgDst = &imgBDst; rgParms[tnum].pImgSrc = &imgB; if(0 != pthread_create(&rgThreadID[tnum], NULL, DMaxFilter::DMaxFilter_Huang8threadWrap, &rgParms[tnum])){ fprintf(stderr, "DMaxFilter::filterImage_() failed to spawn " "thread #%d. Exiting.\n",tnum); exit(1); } } #endif maxFiltHuang_u8(imgBDst, imgB, _radiusX, _radiusY, wKern, hKern, rgKern, numKernPxls, rgRightEdge, pProg, 2 * hUnpad, 3 * hUnpad+1); #ifndef D_NOTHREADS for(int tnum = 1; tnum < _numThreads; ++tnum){ if(pthread_join(rgThreadID[tnum],NULL)) fprintf(stderr, "DMaxFilter::filterImage_() failed to join " "thread %d\n", tnum); } #endif if(NULL != pProg){ pProg->reportStatus(3*hUnpad+1,0,3*hUnpad+1);//report complete } imgDst.combineRGB(imgRDst, imgGDst, imgBDst); } break; case DImage::DImage_dbl_multi: { int w, h, wm1, hm1; // width, height of padded image double *pDst; double *pPad; double *rgWindowBuff; fprintf(stderr, "DMaxFilter::filterImage_() performing brute-force " "(slow) max filter on double image... NOT YET IMPLEMENTED!\n"); exit(1); // w = pImgPad->width(); // h = pImgPad->height(); // wm1=w-1; // hm1 = h-1; // imgDst.create(wUnpad, hUnpad, DImage::DImage_dbl_multi, // imgSrc.numChannels(), imgSrc.getAllocMethod()); // rgWindowBuff = (double*)malloc(sizeof(double)*wKern*hKern); // D_CHECKPTR(rgWindowBuff); // for(int chan = 0; chan < imgSrc.numChannels(); ++chan){ // pDst = imgDst.dataPointer_dbl(chan); // pPad = pImgPad->dataPointer_dbl(chan); // for(int y = 0, idxDst = 0; y < hUnpad; ++y){ // int idxPad; // idxPad = (y+_radiusY)*w+_radiusX; // for(int x=0; x < wUnpad; ++x, ++idxDst, ++idxPad){ // int count; // count = 0; // for(int dy = -_radiusY; dy <= _radiusY; ++dy){ // for(int dx = -_radiusX; dx <= _radiusX; ++dx){ // rgWindowBuff[count] = pPad[idxPad+(dy*w)+dx]; // ++count; // } // } // // find max // qsort((void*)rgWindowBuff, count, sizeof(double),compareDoubles); // pDst[idxDst] = rgWindowBuff[count / 2]; // }//end for(x... // }//end for(y... // }//end for(chan... // free(rgWindowBuff); }//end block in case DImage_dbl_multi break; default: //TODO: finish implementing this fprintf(stderr, "DMaxFilter::filterImage_() not yet implemented for " "some image types\n"); exit(1); break; } if(!fAlreadyPadded){ delete pImgPad; pImgPad = NULL; } }
/**Computes the projection profile of img onto an axis with angle axisAngDeg. Horizontal and vertical profiles (when axisAngDeg is 0 or 90 degress, respectively) are special-cased for speed by calling getImageVerticalProfile() or getImageHorizontalProfile(). I have seen inconsistent usage of the terms "vertical profile" and "horizontal profile" since some people describe the direction of projection instead of the direction of the axis onto which the image is projected. It seems more common, however, to use the direction of the axis. Therefore, when I say "vertical profile," I mean that the profile length is equal to the height of the image, and the Rth value in the profile array is the sum of the values in the Rth row of the image. Likewise, a horizontal profile has length equal to the image width, and each value is the projection of the corresponding image column onto the horizontal axis. For a horizontal profile, set axisAngDeg to 0. For a vertical profile, set axisAngDeg to 90. Note that angles increase clockwise since the y-coordinate of images increases from top to bottom. For RGB images and multi-channel float or double images, the sum of all channels is used (a single profile is calculated). If this is not what is desired, you could create separate images for each channel and take the profiles seperately. Complex images are not supported directly, so they must be converted into a different type before a profile can be taken. If fInterp is false (default), then the nearest image pixel value is used when the position the profile passes through is between pixels. If fInterp is true, bilinear interpolation is used to estimate the value that should go in the profile.*/ void DProfile::getImageProfile(const DImage &img, double axisAngDeg, bool fNormalize, bool fInterp){ int w, h; double wm1, hm1; int numPixels; double dblSum; double theta; double dblTmp; int alen;//length of anchor segment. The anchor segment is the //segment that passes through the center of the image and //is oriented perpendicular to axisAngDeg. The length of //the profile will be equal to alen. int olen;//length of offset segments. The offset segments are the //integration paths parallel to axisAngDeg (one segment per //profile value. double asx, asy;//start (x,y) of the anchor segment double adx, ady;//deltaX and deltaY of the anchor segment double ax, ay; // current anchor point (x,y along the anchor segment) double osx, osy;//starty x,y offset from ax,ay for an offset segment double odx, ody;//deltaX and deltaY of offset segments double ox, oy; // current offset point (x,y along the offset segment) if(DImage::DImage_cmplx == img.getImageType()){ fprintf(stderr, "DProfile::getImageProfile() does not support complex images\n"); abort(); } // if(((axisAngDeg > -0.00001) && (axisAngDeg < 0.00001)) || // ((axisAngDeg > 179.00001) && (axisAngDeg < 180.00001)) || // ((axisAngDeg > 359.00001) && (axisAngDeg < 360.00001))){ // getImageHorizontalProfile(img, fNormalize); // return; // } // if(((axisAngDeg > 89.00001) && (axisAngDeg < 90.00001)) || // ((axisAngDeg > 269.00001) && (axisAngDeg < 270.00001)) || // ((axisAngDeg < -269.00001) && (axisAngDeg > -270.00001)) || // ((axisAngDeg < -89.00001) && (axisAngDeg > -90.00001))){ // getImageVerticalProfile(img, fNormalize); // return; // } w = img.width(); h = img.height(); wm1 = w-1; hm1 = h-1; theta = DMath::degreesToRadians(axisAngDeg); //figure out how long rgProf should be and how wide the integration should be odx = cos(theta); ody = sin(theta); adx = -1. * ody; ady = odx; olen = (int)(2*DMath::distPointLine(0, 0, w/2., h/2., w/2.+adx, h/2.+ady)); dblTmp = 2*DMath::distPointLine(w, 0, w/2., h/2., w/2.+adx, h/2.+ady); if(dblTmp > olen) olen = (int)dblTmp; alen = (int)(2*DMath::distPointLine(0, 0, w/2., h/2., w/2.+odx, h/2.+ody)); dblTmp = 2*DMath::distPointLine(w, 0, w/2., h/2., w/2.+odx, h/2.+ody); if(dblTmp > alen) alen = (int)dblTmp; // printf("w=%d h=%d\n", w, h); // printf("adx=%.2f ady=%.2f alen=%d\n", adx, ady, alen); // printf("odx=%.2f ody=%.2f olen=%d\n", odx, ody, olen); // allocate the rgProf array if(NULL == rgProf){ rgProf = (double*)malloc(alen * sizeof(double)); D_CHECKPTR(rgProf); len = alen; } else{ if(len != alen){ rgProf = (double*)realloc(rgProf,alen*sizeof(double)); D_CHECKPTR(rgProf); len = alen; } } ax = asx = w/2.-(alen/2*adx); ay = asy = h/2.-(alen/2*ady); osx = -(olen/2*odx); osy = -(olen/2*ody); switch(img.getImageType()){ case DImage::DImage_u8: { D_uint8 *pu8; pu8=img.dataPointer_u8(); if(fInterp){ fprintf(stderr, "WARNING! DProfile::getImageProfile() may be buggy " "when fInterp is true\n"); for(int aa=0; aa < alen; ++aa){ int ix, iy; double w1, w2; //weight of left vs right pixels double w3, w4;//weight of top vs bottom pixels double left, right; int oidx; ox = ax+osx; oy = ay+osy; numPixels = 0; dblSum = 0.; for(int oo=0; oo < olen; ++oo){ if( (ox>=0) && (ox < wm1) && (oy>=0) && (oy<hm1)){ ix = (int)ox; iy = (int)oy; oidx = iy*w + ix; w2 = ox-ix; w1 = 1.-w2; w4 = oy-iy; w3 = 1.-w4; left = (pu8[oidx] * w3 + pu8[oidx+w] * w4); right = (pu8[oidx+1] * w3 + pu8[oidx+w+1] * w4); dblSum += left*w1 + right*w2; ++numPixels; } else if((ox==wm1) || (oy == hm1)){ // don't interpolate on the edges, just approximate ix = (int)ox; iy = (int)oy; oidx = iy*w + ix; dblSum += pu8[oidx]; ++numPixels; } ox += odx; oy += ody; } ax += adx; ay += ady; if(fNormalize && (numPixels>0)) rgProf[aa] = dblSum / numPixels; else rgProf[aa] = dblSum; } } else{ for(int aa=0; aa < alen; ++aa){ int ix, iy; int oidx; ox = ax+osx; oy = ay+osy; numPixels = 0; dblSum = 0.; for(int oo=0; oo < olen; ++oo){ if( (ox>=0) && (ox <w) && (oy>=0) && (oy<h)){ ix = (int)ox; iy = (int)oy; oidx = iy*w + ix; dblSum += pu8[oidx]; ++numPixels; } ox += odx; oy += ody; } ax += adx; ay += ady; if(fNormalize && (numPixels>0)) rgProf[aa] = dblSum / numPixels; else rgProf[aa] = dblSum; } } } break; default: fprintf(stderr, "Not yet implemented!\n"); abort(); }//end switch(img.getImageType()) }
/**This function calculates the vertical profile (the length of * profile will be the same as the height of the image). However, * instead of projecting pixels straight across to the vertical axis, * the projection is taken using lines angled through the middle * (x=width/2) of the image. Linear interpolation is used for * sampling the image pixel values since the y-position on the * projection lines will normally be "between" two pixels for any * given x-position. The function is intended only for angles between * -45 and 45 degrees, since otherwise the slope will be too steep for * the assumption I am making (I am steping through the x-values and * calculating the y-values. If the slope is steeper, I should do the * opposite. As a "to-do," maybe we should just check the angle and * handle the two cases individually). */ void DProfile::getAngledVertProfile(const DImage &img, double ang, int fNormalize){ double m; int x; double y, val; int hm1; double xc; int numPixels; D_uint8 *pimg; int w, h; int initialOffset=0; int *rgYoffsets; double *rgBotWeights; int yTop; int yTopPrev=0; int imglen; if(DImage::DImage_u8 != img.getImageType()){ fprintf(stderr, "DProfile::getAngledVertProfile() currently only supports 8-bit " "grayscale images\n"); abort(); } w=img.width(); h=img.height(); pimg = img.dataPointer_u8(); if(NULL == rgProf){ rgProf = (double*)malloc(h * sizeof(double)); D_CHECKPTR(rgProf); len = h; } else{ if(len != h){ rgProf = (double*)realloc(rgProf,h*sizeof(double)); D_CHECKPTR(rgProf); len = h; } } // memset(rgProf, 0, sizeof(double) * h); rgYoffsets = (int*)malloc(sizeof(int)*w); D_CHECKPTR(rgYoffsets); rgBotWeights = (double*)malloc(sizeof(double)*w); D_CHECKPTR(rgBotWeights); m = 1 * tan(DMath::degreesToRadians(ang)); /* dy per dx=1 */ xc = w / 2.; // initialOffset is the y-offset of first pixel, rgYoffsets[i] for i=1...w-1 // are the relative offsets from rgYoffsets[i-1] of the top pixel // rgBotWeights[i] is the interpolation weight of the bottom pixel, while // 1.-rgBotWeights[i] is the interpolation weight of the top pixel. for(x = 0; x < w; ++x){ y = m * ((double)x - xc); yTop = (int)(floor(y)); rgBotWeights[x] = y - (double)(yTop); if(0 == x){ rgYoffsets[0] = 0; initialOffset = yTop; } else rgYoffsets[x] = (yTop - yTopPrev); yTopPrev = yTop; } hm1 = h - 1; imglen = w * hm1; for(int i = 0; i < h; ++i){ /* profile index */ int idx; rgProf[i] = 0.; numPixels = 0; idx = w * (i + initialOffset); for(x = 0; x < w; ++x, ++idx){ idx += (w*rgYoffsets[x]);// go to next row if needed if((idx <0) || (idx >= imglen)) // out of bounds continue; ++numPixels; val = rgBotWeights[x] * pimg[idx] + (1.-rgBotWeights[x]) * pimg[idx+w]; rgProf[i] += val; } if((numPixels > 0) && fNormalize) rgProf[i] /= numPixels; } free(rgYoffsets); free(rgBotWeights); }