/** Takes profiles of numStrips vertical strips (plus numStrips-1
    overlapping strips) and uses them to estimate the avg textline
    height **/
int DTextlineSeparator::estimateAvgHeight2(DImage &imgBinary,
					   int numStrips,
					   char *stDebugBaseName){
  int w, h;
  D_uint8 *pu8;
  DProfile prof;
  DProfile *rgProfs;// profiles of overlapping strips of image
  DProfile *rgProfsRL;//avg white RL profile
  DProfile *rgProfsSmear;// profiles of overlapping strips of image after smear
  char stTmp[1024];
  int *rgPeakThresh;
  int *rgPeakThreshRL;
  double *rgPeakLineOffs;
  

  rgProfs = new DProfile[numStrips*2-1];
  D_CHECKPTR(rgProfs);
  rgProfsRL = new DProfile[numStrips*2-1];
  D_CHECKPTR(rgProfsRL);
  rgProfsSmear = new DProfile[numStrips*2-1];
  D_CHECKPTR(rgProfsSmear);
  rgPeakThresh = new int[numStrips*2-1];
  D_CHECKPTR(rgPeakThresh);
  rgPeakThreshRL = new int[numStrips*2-1];
  D_CHECKPTR(rgPeakThreshRL);
  rgPeakLineOffs = new double[numStrips*2-1];
  D_CHECKPTR(rgPeakLineOffs);

  w = imgBinary.width();
  h = imgBinary.height();
  pu8 = imgBinary.dataPointer_u8();
  for(int y=0, idx=0; y < h; ++y){
    for(int x=0; x < w; ++x, ++idx){
      if((pu8[idx] > 0) && (pu8[idx] < 255)){
  	fprintf(stderr, "DTextlineSeparator::estimateAvgHeight() requires "
  		"BINARY image with values of 0 or 255!\n");
  	exit(1);
      }
    }
  }

  DImage imgStrip;
  int stripW, stripLeft;
  DProfile profWeightedStrokeDist;
  int **rgBlackSpacingHist;

  rgBlackSpacingHist = new int*[numStrips*2-1];
  D_CHECKPTR(rgBlackSpacingHist);
  rgBlackSpacingHist[0]=new int[200*(numStrips*2-1)];
  D_CHECKPTR(rgBlackSpacingHist[0]);
  memset(rgBlackSpacingHist[0],0,sizeof(int)*200*(numStrips*2-1));
  for(int i=1; i < (numStrips*2-1); ++i){
    rgBlackSpacingHist[i] = &(rgBlackSpacingHist[i-1][200]);//only 2-199 are valid spacings
  }
  
  int **rgPeakYs;
  int *rgNumPeaks;
  int **rgValleyYs;
  int *rgNumValleys;

  rgPeakYs = new int*[numStrips*2-1];
  D_CHECKPTR(rgPeakYs);
  rgPeakYs[0] = new int[(numStrips*2-1)*h];
  D_CHECKPTR(rgPeakYs[0]);
  rgValleyYs = new int*[numStrips*2-1];
  D_CHECKPTR(rgValleyYs);
  rgValleyYs[0] = new int[(numStrips*2-1)*h];
  D_CHECKPTR(rgValleyYs);
  for(int i = 1; i < (numStrips*2-1); ++i){
    rgPeakYs[i] = &(rgPeakYs[i-1][h]);
    rgValleyYs[i] = &(rgValleyYs[i-1][h]);
  }

  rgNumPeaks = new int[numStrips*2-1];
  D_CHECKPTR(rgNumPeaks);
  rgNumValleys = new int[numStrips*2-1];
  D_CHECKPTR(rgNumValleys);
  for(int i=0; i < (numStrips*2-1); ++i){
    rgNumPeaks[i] = 0;
    rgNumValleys[i] = 0;
  }

  stripW = (w + numStrips-1) / numStrips;
  printf("w=%d h=%d stripW=%d\n",w,h,stripW);
  for(int i=0; i < numStrips*2-1; ++i){
    stripLeft = i * stripW/2;
    if(i == numStrips*2-2){//last strip may have slightly different width
      stripW = w - stripLeft - 1;
    }
    imgBinary.copy_(imgStrip, stripLeft, 0, stripW, h);
    rgProfs[i].getImageVerticalProfile(imgStrip,false);
    rgProfs[i].smoothAvg(2);
    rgProfsRL[i].getVertAvgRunlengthProfile(imgStrip,0xff,true);
    rgProfsRL[i].smoothAvg(2);

    double *pdbl;
    pdbl = rgProfs[i].dataPointer();
    for(int j=0; j < h; ++j)
      pdbl[j] /= 255; // now the profile is number of white pixels (was GS prof)

    unsigned int profMax;
    profMax = (unsigned int)rgProfs[i].max();

    //use original image to create histogram of horizontal foreground spacing
    //(distance from black pixel to next black pixel) weighted by profile value
    //inverse (number of black pixels instead of white pixels)
    for(int y=2; y < (h-2); ++y){//ignore 2 on each end (smoothing boundaries)
      int lastBlackX;
      int runlength;
      int x;
      int weight;

      x = stripLeft-199;
      if(x < 0)
	x=0;
      lastBlackX = x;
      runlength = 0;
      for( ; (x<stripLeft+stripW+199) && (x < w); ++x){
	if(pu8[y*w+x] == 0){//black
	  runlength = x - lastBlackX;
	  if((runlength >= 2) && (runlength < 200)){
	    weight = (int)profMax - (int)pdbl[y];//inverse of profile value at y
	    rgBlackSpacingHist[0/*i*/][runlength] += weight;
	  }
	  lastBlackX=x;
	}
      }
    }

    //now multiply the values by the avg runlength
    double *pdblRL;
    pdblRL = rgProfsRL[i].dataPointer();
    // for(int j=0; j < h; ++j)
    //   pdbl[j] *= pdblRL[j];


    //now get a histogram of the profile values and use otsu to determine
    //a threshold between peaks and valleys
    unsigned int *rgProfHist;
    double peakThresh;
    rgProfHist = (unsigned int*)calloc(profMax+1,sizeof(unsigned int));
    D_CHECKPTR(rgProfHist);
    for(int j=0; j < h; ++j)
      ++(rgProfHist[(int)(pdbl[j])]);
    peakThresh = DThresholder::getOtsuThreshVal(rgProfHist, profMax+1);
    rgPeakLineOffs[i] = peakThresh / (double)stripW;//now a fraction of stripW


    //choose a threshold between peaks and valleys as the thresh that maximizes
    //how many peaks there are that are between 2 and 200 pixels high
    

    // unsigned int max,min;
    // max = 0;
    // min = rgProfHist[0];
    // for(int j=0; j < stripW; ++j){
    //   if(rgProfHist[j] > max)
    // 	max = rgProfHist[j];
    //   if(rgProfHist[j] < min)
    // 	min = rgProfHist[j];
    // }
    // rgPeakLineOffs[i] = peakThresh / (double)max;
    // printf("peakThresh=%lf  rgPeakLineOffs=%f\n",
    // 	   peakThresh,rgPeakLineOffs[i]);
    free(rgProfHist);
    rgPeakThresh[i] = (int)peakThresh;
  }

  //to get the spacing estimate, get the max, then find the next position
  //that is less than 1/3 of the max.  Use that as the estimate to determine
  //scale
  int spacingMax;
  int spacingEstimate;
  
  spacingMax = 2;
  for(int j=3; j<200; ++j){
    if(rgBlackSpacingHist[0][j] > rgBlackSpacingHist[0][spacingMax])
      spacingMax = j;
  }
  spacingEstimate = spacingMax;
  for(int j=spacingMax+1; j < 200; ++j){
    if(rgBlackSpacingHist[0][j] < (rgBlackSpacingHist[0][spacingMax] / 3)){
      spacingEstimate = j;
      break;
    }
  }
  printf(" spacing estimate =        *** %d pixels\n",spacingEstimate);


  // now smear the image based on the spacing estimate, then take new profiles
  DImage imgSmear;
  D_uint8 *psmear;
  imgSmear = imgBinary;
  psmear = imgSmear.dataPointer_u8();
  
  for(int y=0; y < h; ++y){
    int lastBlackX;
    int runlength;
    
    lastBlackX = w;
    for(int x=0; x < w; ++x){
      if(pu8[y*w+x] == 0){//black
	runlength = x - lastBlackX;
	if((runlength < 2*spacingEstimate) && (runlength >0)){
	  // fill in the white since last black pixel with black
	  for(int xp=lastBlackX+1; xp < x; ++xp){
	    psmear[(y*w+xp)] = 128;
	  }
	}
	lastBlackX = x;
      }
    }
  }
  sprintf(stTmp,"%s_smear.ppm",stDebugBaseName);
  imgSmear.save(stTmp);

  // now recalculate all of the profiles
  stripW = (w + numStrips-1) / numStrips;
  int *rgSmearThresh;
  rgSmearThresh = new int[numStrips*2-1];
  D_CHECKPTR(rgSmearThresh);
  for(int i=0; i < numStrips*2-1; ++i){
    double *pdbl;
    unsigned int profMax;
    stripLeft = i * stripW/2;
    if(i == numStrips*2-2){//last strip may have slightly different width
      stripW = w - stripLeft - 1;
    }
    // imgSmear.copy_(imgStrip, stripLeft, 0, stripW, h);
    imgBinary.copy_(imgStrip, stripLeft, 0, stripW, h);
    rgProfsSmear[i].getImageVerticalProfile(imgStrip,false);


    // invert the profile so black is 255 and white is zero before smoothing
    pdbl = rgProfsSmear[i].dataPointer();
    profMax = (unsigned int)rgProfsSmear[i].max();
    for(int y=0; y < h; ++y)
      pdbl[y] = profMax - pdbl[y];

    rgProfsSmear[i].smoothAvg(spacingEstimate*2/3);
    profMax = (unsigned int)rgProfsSmear[i].max();//new max after smoothing





    // decide where peak/valleys in profile are
    {
      int prevSign = 0;
      double deriv;
      double *pdbl;
      int numZeros = 0;

      pdbl = rgProfsSmear[i].dataPointer();
      //use profile derivative and dist from last peak/valley
      //to decide where peaks and valleys are
      for(int y=1; y < (h-1); ++y){
	deriv = pdbl[y+1] - pdbl[y-1];
	if(deriv > 0.){//rising
	  if(prevSign <= 0){//valley
	    rgValleyYs[i][rgNumValleys[i]] = y-numZeros/2;//(middle of plateaus)
	    ++(rgNumValleys[i]);
	  }
	  prevSign = 1;
	  numZeros = 0;
	}
	else if(deriv < 0.){//falling
	  if(prevSign >= 0){//peak
	    rgPeakYs[i][rgNumPeaks[i]] = y-numZeros/2;//(middle of plateaus)
	    ++(rgNumPeaks[i]);
	  }
	  prevSign = -1;
	  numZeros = 0;
	}
	else{ // zero slope
	  ++numZeros;
	}
      }//end for(y=...
    }

    // combine peaks that are too close to each other
    {
      int numPeaksRemoved = 0;
      bool fRemoved;
      fRemoved = true;
      while(fRemoved && (rgNumPeaks[i]>1)){
	fRemoved = false;
	int deletePeak=0;
	int deleteValley=0;
	for(int j=1; j < rgNumPeaks[i]; ++j){
	  if(abs(rgPeakYs[i][j-1]-rgPeakYs[i][j])<spacingEstimate*2/3){//too close
	    if(pdbl[rgPeakYs[i][j]] > pdbl[rgPeakYs[i][j-1]]){
	      printf("  A remove peak %d at y=%d\n",j-1,(int)pdbl[rgPeakYs[i][j-1]]);
	      deletePeak = j-1;
	    }
	    else{
	      printf("  B remove peak %d at y=%d\n",j,(int)pdbl[rgPeakYs[i][j]]);
	      deletePeak = j;
	    }
	    deleteValley = -1;

	    if(rgNumValleys[i] > 0){
	      if(rgPeakYs[i][0] < rgValleyYs[i][0]){//peak was first
		deleteValley = deletePeak;
	      }
	      else{//valley was first
		deleteValley = deletePeak+1;
	      }
	    }

	    //delete the peak
	    for(int k=deletePeak+1; k < rgNumPeaks[i]; ++k){
	      rgPeakYs[i][k-1] = rgPeakYs[i][k];
	    }
	    --(rgNumPeaks[i]);
	    //delete the valley (if in range)
	    if((deleteValley>=0) && (deleteValley < rgNumValleys[i])){
	      for(int k=deleteValley+1; k < rgNumValleys[i]; ++k){
		rgValleyYs[i][k-1] = rgValleyYs[i][k];
	      }
	    }
	    fRemoved = true;
	    ++numPeaksRemoved;
	    break;
	  }//if(abs(...
	}//for(int j=...
      }//while(fRemoved)
    }



#if 0
    rgSmearThresh[i] = 0;
    //choose threshold that maximizes the number of peaks
    int bestNumPeaks;
    int bestNumPeaksThresh;
    int peaksThresh;
    int numPeaks;
    //    double peaksProfMax = 0.;
    // for(int y=spacingEstimate/2-1; y < h-(spacingEstimate/2-1); ++y)
    //   if(pdbl[y] > peaksProfMax)
    // 	peaksProfMax = pdbl[y];
    bestNumPeaksThresh = 0;
    bestNumPeaks = 0;
    numPeaks = 1;
    for(int peaksThresh = 0; peaksThresh <= profMax; ++peaksThresh){
      numPeaks = 0;
      int fLead = -1;
      for(int y=0; y < h;++y){
	if(fLead>=0){
	  if((pdbl[y] <= peaksThresh)){
	    if((y-fLead) >= spacingEstimate/2)
	      ++numPeaks;
	    fLead = -1;
	  }
	}
	else{
	  if(pdbl[y] > peaksThresh)
	    fLead = y;
	}
      }
      if(numPeaks >= bestNumPeaks){
	bestNumPeaks = numPeaks;
	bestNumPeaksThresh = peaksThresh;
      }
    }
    rgSmearThresh[i] = bestNumPeaksThresh;
#endif


  }

  // now get x-height estimate using profiles (or black runlengths of smears)


  //debug: save an image with all of the profiles
  {
    DImage imgProfsAll;
    DImage imgProfsRLAll;
    imgProfsAll.create(w,h,DImage::DImage_u8);
    stripW = (w + numStrips-1) / numStrips;
    for(int i=0; i < numStrips*2-1; ++i){
      DImage imgTmp;
      imgTmp = rgProfs[i].toDImage(stripW/2,true);
      imgProfsAll.pasteFromImage(i*stripW/2,0,imgTmp,0,0,stripW/2,h);
    }
    imgProfsAll = imgProfsAll.convertedImgType(DImage::DImage_RGB);
    for(int i=0; i < numStrips*2-1; ++i){
      int peakLineOffs;
      // peakLineOffs = stripW/2 * rgPeakThresh[i] / rgProfs[i].max();
      peakLineOffs = (int)(rgPeakLineOffs[i]*stripW/2);
      // printf(" rgPeakLineOffs[%d]=%lf peakLineOffs=%d\n",i,rgPeakLineOffs[i],
      // 	     peakLineOffs);
      imgProfsAll.drawLine(i*stripW/2 + peakLineOffs+1, 0,
			   i*stripW/2 + peakLineOffs+1, h-1, 255-i,0,0);
      imgProfsAll.drawLine(i*stripW/2 + peakLineOffs, 0,
			   i*stripW/2 + peakLineOffs, h-1, 255-i,0,0);
      imgProfsAll.drawLine(i*stripW/2, 0,
			   i*stripW/2, h-1, 0, 255-i,0);
    }

    sprintf(stTmp,"%s_allprofs.pgm",stDebugBaseName);
    imgProfsAll.save(stTmp);
  }

  //debug: save an image with all of the smeared profiles
  {
    DImage imgProfsAll;
    DImage imgProfsRLAll;
    imgProfsAll.create(w,h,DImage::DImage_u8);
    stripW = (w + numStrips-1) / numStrips;
    for(int i=0; i < numStrips*2-1; ++i){
      DImage imgTmp;
      imgTmp = rgProfsSmear[i].toDImage(stripW/2,true);
      imgProfsAll.pasteFromImage(i*stripW/2,0,imgTmp,0,0,stripW/2,h);
    }
    imgProfsAll = imgProfsAll.convertedImgType(DImage::DImage_RGB);
    for(int i=0; i < numStrips*2-1; ++i){

      for(int j=0; j < rgNumPeaks[i]; ++j){
	int ypos;
	ypos = rgPeakYs[i][j];
	imgProfsAll.drawLine(i*stripW/2,ypos,(i+1)*stripW/2-1,ypos,255,0,0);
      }

      for(int j=0; j < rgNumValleys[i]; ++j){
	int ypos;
	ypos = rgValleyYs[i][j];
	imgProfsAll.drawLine(i*stripW/2,ypos,(i+1)*stripW/2-1,ypos,0,255,0);
      }



#if 0
      int prevSign = 0;
      int lastPeakY, lastValleyY, lastTurnY;
      double deriv;
      double *pdbl;

      pdbl = rgProfsSmear[i].dataPointer();
      lastPeakY = lastValleyY = lastTurnY = 0-spacingEstimate;
      //use profile derivative and dist from last peak/valley
      //to decide where peaks and valleys are
      for(int y=1; y < (h-1); ++y){
	deriv = pdbl[y+1] - pdbl[y-1];
	if(deriv > 0.){//rising
	  // imgProfsAll.setPixel(i*stripW/2,y,0,255,0);
	  if((prevSign <= 0) && ((y-lastTurnY)>spacingEstimate/2)){//valley
	    imgProfsAll.drawLine(i*stripW/2,y,(i+1)*stripW/2-1,y,0,255,0);
	    lastValleyY = lastTurnY = y;
	    prevSign = 1;
	  }
	}
	else if(deriv < 0.){//falling
	  // imgProfsAll.setPixel(i*stripW/2,y,255,0,0);
	  if(prevSign >= 0){
	    if(((y-lastTurnY)>spacingEstimate/2) ||
	       ((lastPeakY>=0)&&(pdbl[y] > pdbl[lastPeakY]))){//peak
	      if((lastPeakY>=0)&&(pdbl[y] > pdbl[lastPeakY])){//correct previous
		imgProfsAll.drawLine(i*stripW/2,lastPeakY,
				     (i+1)*stripW/2-1,lastPeakY,0,0,0);
	      }
	      imgProfsAll.drawLine(i*stripW/2,y,(i+1)*stripW/2-1,y,255,0,0);
	      lastPeakY = lastTurnY = y;
	      prevSign = -1;
	    }
	  }
	}
	else{ // zero slope (do nothing)
	  // imgProfsAll.setPixel(i*stripW/2,y,0,0,0);
	  // do nothing
	}

      }

      // int peakLineOffs;
      // peakLineOffs = rgSmearThresh[i] * stripW/2 / rgProfsSmear[i].max();
      // imgProfsAll.drawLine(i*stripW/2 + peakLineOffs+1, 0,
      // 			   i*stripW/2 + peakLineOffs+1, h-1, 255-i,0,0);
      // imgProfsAll.drawLine(i*stripW/2 + peakLineOffs, 0,
      // 			   i*stripW/2 + peakLineOffs, h-1, 255-i,0,0);
      // imgProfsAll.drawLine(i*stripW/2, 0,
      // 			   i*stripW/2, h-1, 0, 255-i,0);
#endif
    }
    sprintf(stTmp,"%s_allsmearprofs.pgm",stDebugBaseName);
    imgProfsAll.save(stTmp);
  }



  //debug: save an image with all of the RL profiles
  {
    DImage imgProfsAll;
    imgProfsAll.create(w,h,DImage::DImage_u8);
    stripW = (w + numStrips-1) / numStrips;
    for(int i=0; i < numStrips*2-1; ++i){
      DImage imgTmp;
      imgTmp = rgProfsRL[i].toDImage(stripW/2,true);
      imgProfsAll.pasteFromImage(i*stripW/2,0,imgTmp,0,0,stripW/2,h);
    }
    imgProfsAll = imgProfsAll.convertedImgType(DImage::DImage_RGB);

    sprintf(stTmp,"%s_allprofsRL.pgm",stDebugBaseName);
    imgProfsAll.save(stTmp);
  }

  //debug: save a gnuplot of the histograms of black spacing weighted by profile
  // the image has the histogram for each strip followed by the sum histogram
  // a value of -10 is placed at positions 0,1 of each histogram as a separator
  {
    DImage imgSpacingHists;
    FILE *fout;

    sprintf(stTmp,"%s_spacing_profs.dat",stDebugBaseName);
    fout = fopen(stTmp,"wb");
    if(!fout){
      fprintf(stderr, "couldn't open debug file '%s' for output\n",stTmp);
      exit(1);
    }
    for(int i=0; i < 1/*numStrips*2-1*/; ++i){
      for(int j=0; j < 200; ++j){
	int val;
	val = rgBlackSpacingHist[i][j];
	if(j<2)
	  val = -10;
	fprintf(fout,"%d\t%d\n",i*200+j, val);
      }
    }
    fclose(fout);
  }


  // now at the otsu x-position in the profile, get avg black runlength to
  // guess at peak (textline) height.
  // Do the same for white to guess at valley (spacing) height.
  // After getting it for each strip's profile, take the avg for the whole
  // page.  Use that to determine a smoothing value and a window size for the
  // transition count map (TCM). (maybe use median instead of avg?)

  delete [] rgPeakYs[0];
  delete [] rgPeakYs;
  delete [] rgNumPeaks;
  delete [] rgValleyYs[0];
  delete [] rgValleyYs;
  delete [] rgNumValleys;


  delete [] rgProfs;
  delete [] rgProfsRL;
  delete [] rgPeakThresh;
  delete [] rgPeakThreshRL;
  delete [] rgPeakLineOffs;
  delete rgBlackSpacingHist[0];
  delete [] rgBlackSpacingHist;
  //  exit(1);


  // prof.getImageVerticalProfile(imgROI,true);
  // DImage imgTmp;
  // imgTmp = prof.toDImage(100,true);
  // sprintf(stTmp,"%s_prof.pgm",stDebugBaseName);
  // imgTmp.save(stTmp);
  // prof.smoothAvg(2);
  // imgTmp = prof.toDImage(100,true);
  // sprintf(stTmp,"%s_prof_smooth.pgm",stDebugBaseName);
  // imgTmp.save(stTmp);



  // prof.getVertAvgRunlengthProfile(imgROI,0x00,false);
  // imgTmp = prof.toDImage(100,true);
  // sprintf(stTmp,"%s_prof_rle.pgm",stDebugBaseName);
  // imgTmp.save(stTmp);
  // prof.smoothAvg(2);
  // imgTmp = prof.toDImage(100,true);
  // sprintf(stTmp,"%s_prof_rle_smooth.pgm",stDebugBaseName);
  // imgTmp.save(stTmp);


  // // find a radiusX that gives a good histogram from the TCM
  // // (we want the TCM to give responses of about
  // printf("  *creating TCM histograms...\n");fflush(stdout);

  // int rgHists[40][256];
  // memset(rgHists,0,sizeof(int)*40*256);

  // for(int rx = 10; rx < 400; rx +=10){
  //   DImage imgTCM;
  //   D_uint8 *p8;
  //   int max = 0;
  //   int ry;
  //   ry = rx/6;
  //   if(ry < 1)
  //     ry = 1;
  //   DTCM::getImageTCM_(imgTCM, imgROI, rx,ry, false,NULL);
  //   p8 = imgTCM.dataPointer_u8();
  //   for(int y = 0, idx=0; y < h; ++y){
  //     for(int x = 0; x < w; ++x,++idx){
  // 	rgHists[rx/10][p8[idx]] += 1;
  //     }
  //   }
  //   rgHists[rx/10][0] = 0;
  //   max = 0;
  //   for(int i=0;i<256;++i)
  //     if(rgHists[rx/10][i] > max)
  // 	max =rgHists[rx/10][i];
  //   for(int i=0;i<256;++i){//scale from 0 to 255
  //     rgHists[rx/10][i] = rgHists[rx/10][i] * 255 / max;
  //   }
  // }
  // //now save the histograms as an image
  // DImage imgTCMhists;
  // imgTCMhists.create(256,40,DImage::DImage_u8);
  // D_uint8 *p8;
  // p8 = imgTCMhists.dataPointer_u8();
  // for(int y=0, idx=0; y < 40; ++y){
  //   for(int x=0; x < 256; ++x, ++idx){
  //     p8[idx] = (D_uint8)(rgHists[y][x]);
  //   }
  // }
  // sprintf(stTmp, "%s_tcmhist.pgm",stDebugBaseName);
  // imgTCMhists.save(stTmp);
  // printf("  *done creating TCM histograms...\n");
  

  // int radiusX, radiusY;
  // radiusX = imgROI.width() / 20;
  // if(radiusX < 10)
  //   radiusX = 10;
  // if(radiusX > 200)
  //   radiusX = 200;
  // radiusY = radiusX / 5;
  // //  if(radiusY < 2)
  //   radiusY = 2;
  // printf("  TCM radiusX=%d radiusY=%d\n", radiusX,radiusY);
  // DTCM::getImageTCM_(imgTmp, imgROI, radiusX,radiusY, false,stDebugBaseName);
  // //  DTCM::getImageTCM_(imgTmp, imgROI, 1,1, false);

  // // double *rgProf;
  // // rgProf = prof.dataPointer();
  // // for(int i=100; i < 500; ++i){
  // //   if(rgProf[i] > 0.)
  // //     printf("[%d]=%f ",i, rgProf[i]);
  // // }
  // // printf("\n");
  return 0;
}
///assumes that the image is BINARY with bg=255
int DTextlineSeparator::estimateAvgHeight(DImage &imgBinary,
					  int ROIx0, int ROIy0,
					  int ROIx1, int ROIy1,
					  char *stDebugBaseName){
  DImage imgROI;
  int w, h;
  D_uint8 *pu8;
  DProfile prof;

  if(-1 == ROIx1)
    ROIx1 = imgBinary.width()-1;
  if(-1 == ROIy1)
    ROIy1 = imgBinary.height()-1;

  imgBinary.copy_(imgROI,ROIx0,ROIy0,ROIx1-ROIx0+1,ROIy1-ROIy0+1);

  char stTmp[1024];
  sprintf(stTmp, "%s_roi.pgm",stDebugBaseName);
  imgROI.save(stTmp);
  
  w = imgROI.width();
  h = imgROI.height();
  pu8 = imgROI.dataPointer_u8();
  for(int y=0, idx=0; y < h; ++y){
    for(int x=0; x < w; ++x, ++idx){
      if((pu8[idx] > 0) && (pu8[idx] < 255)){
	fprintf(stderr, "DTextlineSeparator::estimateAvgHeight() requires "
		"BINARY image!\n");
	exit(1);
      }
    }
  }
  prof.getImageVerticalProfile(imgROI,true);
  DImage imgTmp;
  imgTmp = prof.toDImage(100,true);
  sprintf(stTmp,"%s_prof.pgm",stDebugBaseName);
  imgTmp.save(stTmp);
  prof.smoothAvg(2);
  imgTmp = prof.toDImage(100,true);
  sprintf(stTmp,"%s_prof_smooth.pgm",stDebugBaseName);
  imgTmp.save(stTmp);



  prof.getVertAvgRunlengthProfile(imgROI,0x00,false);
  imgTmp = prof.toDImage(100,true);
  sprintf(stTmp,"%s_prof_rle.pgm",stDebugBaseName);
  imgTmp.save(stTmp);
  prof.smoothAvg(2);
  imgTmp = prof.toDImage(100,true);
  sprintf(stTmp,"%s_prof_rle_smooth.pgm",stDebugBaseName);
  imgTmp.save(stTmp);


  // find a radiusX that gives a good histogram from the TCM
  // (we want the TCM to give responses of about
  printf("  *creating TCM histograms...\n");fflush(stdout);

  int rgHists[40][256];
  memset(rgHists,0,sizeof(int)*40*256);

  for(int rx = 10; rx < 400; rx +=10){
    DImage imgTCM;
    D_uint8 *p8;
    int max = 0;
    int ry;
    ry = rx/6;
    if(ry < 1)
      ry = 1;
    DTCM::getImageTCM_(imgTCM, imgROI, rx,ry, false,NULL);
    p8 = imgTCM.dataPointer_u8();
    for(int y = 0, idx=0; y < h; ++y){
      for(int x = 0; x < w; ++x,++idx){
	rgHists[rx/10][p8[idx]] += 1;
      }
    }
    rgHists[rx/10][0] = 0;
    max = 0;
    for(int i=0;i<256;++i)
      if(rgHists[rx/10][i] > max)
	max =rgHists[rx/10][i];
    for(int i=0;i<256;++i){//scale from 0 to 255
      if (max!=0)
        rgHists[rx/10][i] = rgHists[rx/10][i] * 255 / max;
    }
  }
  //now save the histograms as an image
  DImage imgTCMhists;
  imgTCMhists.create(256,40,DImage::DImage_u8);
  D_uint8 *p8;
  p8 = imgTCMhists.dataPointer_u8();
  for(int y=0, idx=0; y < 40; ++y){
    for(int x=0; x < 256; ++x, ++idx){
      p8[idx] = (D_uint8)(rgHists[y][x]);
    }
  }
  sprintf(stTmp, "%s_tcmhist.pgm",stDebugBaseName);
  imgTCMhists.save(stTmp);
  printf("  *done creating TCM histograms...\n");
  

  int radiusX, radiusY;
  radiusX = imgROI.width() / 20;
  if(radiusX < 10)
    radiusX = 10;
  if(radiusX > 200)
    radiusX = 200;
  radiusY = radiusX / 5;
  //  if(radiusY < 2)
    radiusY = 2;
  printf("  TCM radiusX=%d radiusY=%d\n", radiusX,radiusY);
  DTCM::getImageTCM_(imgTmp, imgROI, radiusX,radiusY, false,stDebugBaseName);
  //  DTCM::getImageTCM_(imgTmp, imgROI, 1,1, false);

  // double *rgProf;
  // rgProf = prof.dataPointer();
  // for(int i=100; i < 500; ++i){
  //   if(rgProf[i] > 0.)
  //     printf("[%d]=%f ",i, rgProf[i]);
  // }
  // printf("\n");
  return 0;
}