📄 trimi.cpp
字号:
// $image\trimi.cpp 1.5 milbo$ trim edges an image, for removing edges of photgraphed pages// Warning: this is raw research code -- expect it to be quite messy.//// Tips for using trimi effectively://// Photograph the original page in sunlight on a dark untextured surface.// What you want is the page to be bright and the surroundings to be dark.//// If trimi isn't doing want you want, run auto-contrast on the original in// Photoshop before trimming. This usually helps. (Use Photoshop batch mode// to auto-contrast all your images.)//// Once you have finished trimming: in Photoshop, contrast adjust or// adjust curves before printing. (Once again, use batch mode.) You want to// adjust so the background is light (so you don't waste ink) but keeping// the lettering nicely readable.//// One other thing: trimi's output images are BMPs. After trimming, you should // convert these to JPEGs in Photoshop to (greatly) reduce storage space.//// milbo durban nov 05#include "../masm/all.hpp"// tweakable parametersconst int ngSkip = 5; // only look at every n'th line, to save timeconst int ngSkip1 = 5;const int ngAvLen = 10; // determine gray value by averaging over this many pixelsconst int gMinNegDelta = -2; // page is getting dark again: was blank page, now letteringconst double gMaxDeltaLimit = 2.0;#define ngSearchLenHoriz (width/5)#define ngSearchLenVert (height/5)#define ngFracWidth (width/5) // used for HEIGHT trimming (not sure why this has to be smaller than ngFracHeight but, after testing, it does)#define ngFracHeight (height/2) // used for WIDTH trimmingconst int ngHorizExtra = 0;const int ngVertExtra = 0;//-------------------------------------------------------------------------------// Comparism for qsort so biggest ints are at start of arraystatic int __cdecl CompareIntsReverse (const void *pArg0, const void *pArg1){int i = *(int *)pArg0;int j = *(int *)pArg1;int diff = j - i; // want biggest int at start of arrayif (diff < 0) return -1;else if (diff > 0) return 1;else return 0;}//-------------------------------------------------------------------------------// Get average gray level *pAv, starting at position ix,iy and moving horizontally for ngAvLen pixelsstatic void GetRgbAvX (double *pAv, const tRGB *pRgb, int width, int height, int ix, int iy, int ngAvLen, bool fLeftEdge){(*pAv) = 0;if (fLeftEdge) for (int i = ix; i < ix + ngAvLen; i++) { const tRGB *p = pRgb + (iy * width) + i; (*pAv) += RgbToGray(*p); }else for (int i = ix; i > ix - ngAvLen; i--) { const tRGB *p = pRgb + (iy * width) + i; (*pAv) += RgbToGray(*p); }(*pAv) /= ngAvLen;}//-------------------------------------------------------------------------------// Get average gray level *pAv, starting at position ix,iy and moving horizontally for ngAvLen pixelsstatic void GetAvX (double *pAv, const byte *pByte, int width, int height, int ix, int iy, int ngAvLen, bool fLeftEdge){(*pAv) = 0;if (fLeftEdge) for (int i = ix; i < ix + ngAvLen; i++) (*pAv) += *(pByte + (iy * width) + i);else for (int i = ix; i > ix - ngAvLen; i--) (*pAv) += *(pByte + (iy * width) + i);(*pAv) /= ngAvLen;}//-------------------------------------------------------------------------------// Get average gray level *pAv, starting at position ix,iy and moving vertically for ngAvLen pixelsstatic void GetRgbAvY (double *pAv, const tRGB *pRgb, int width, int height, int ix, int iy, int ngAvLen, bool fTopEdge){(*pAv) = 0;if (fTopEdge) for (int i = iy; i > iy - ngAvLen; i--) { const tRGB *p = pRgb + (i * width) + ix; (*pAv) += RgbToGray(*p); }else for (int i = iy; i < iy + ngAvLen; i++) { const tRGB *p = pRgb + (i * width) + ix; (*pAv) += RgbToGray(*p); }(*pAv) /= ngAvLen;}//-------------------------------------------------------------------------------// Get average gray level *pAv, starting at position ix,iy and moving vertically for ngAvLen pixelsstatic void GetAvY (double *pAv, const byte *pByte, int width, int height, int ix, int iy, int ngAvLen, bool fTopEdge){(*pAv) = 0;if (fTopEdge) for (int i = iy; i > iy - ngAvLen; i--) (*pAv) += *(pByte + (i * width) + ix);else for (int i = iy; i < iy + ngAvLen; i++) (*pAv) += *(pByte + (i * width) + ix);(*pAv) /= ngAvLen;}//-------------------------------------------------------------------------------// Clip left and right dark edges off the image// We use the equalized image EqImg for determining the edges but write into// the original image Img.static void TrimImageHorizontally (RgbImage &Img, Image &EqImg, bool fLeftEdge){tRGB *pRgb = Img.buf;byte *pEq = EqImg.buf;int width = Img.width, height = Img.height;DASSERT(EqImg.width == width && EqImg.height == height);double Delta;int ix, iy, n = 0, ixBestPrev = ngSearchLenHoriz;int *aiBest = (int *)calloc(height, sizeof(int));int nSearchLenHoriz = ngSearchLenHoriz;for (iy = 0; iy < height; iy += ngSkip) { double MaxDelta = -1; int ixBest = 0; double Gray0, Gray1, EdgeGray, MaxWhite = 0; for (ix = 0; ix < nSearchLenHoriz; ix += ngSkip1) { GetAvX(&Gray0, pEq, width, height, (fLeftEdge? ix: width-ix-1), iy, ngAvLen, fLeftEdge); if (ix == 0) EdgeGray = Gray0; // gray value at edge of paper GetAvX(&Gray1, pEq, width, height, (fLeftEdge? ix+1: width-ix-2), iy, ngAvLen, fLeftEdge); Delta = Gray1 - Gray0; if (Delta < gMinNegDelta) // page is getting dark again: was blank page, now lettering { //nSearchLenHoriz = ix; // remember where lettering started for subsequent search limit break; } if (Delta > MaxDelta) { MaxDelta = Delta; ixBest = ix; MaxWhite = Gray1; } } if (EdgeGray < 40 && MaxDelta > gMaxDeltaLimit) // found edge? { if (ixBest > ixBestPrev + 10) // don't go more than 10 pixels from previous ixBest = ixBestPrev + 10; // this helps prevent stray long lines aiBest[iy] = ixBest;#if 0 // for debugging, show what we are trimming for (ix = 0; ix < ixBest + ngAvLen; ix++) { tRGB *p = pRgb + (iy * width) + ((fLeftEdge? ix: width-1-ix)); p->Red = p->Green = p->Blue = 255; }#endif ixBestPrev = ixBest; } else ixBestPrev = 0; }for (iy = 0; iy < height; iy++) if (aiBest[iy] > nSearchLenHoriz) aiBest[iy] = nSearchLenHoriz;// crop the image at the line of the ngFracHeight smallest iBestsqsort((void *)aiBest, (size_t)height, sizeof(int), CompareIntsReverse);int iBestAv = 0;for (iy = 0; iy < (ngFracHeight / ngSkip); iy++) iBestAv += aiBest[iy]; iBestAv /= (ngFracHeight / ngSkip);iBestAv += ngHorizExtra;#if 1iBestAv += (Img.width - iBestAv) % 4; // ensure width is divisble by 4if (fLeftEdge) { CropRgbImage(Img, 0, 0, iBestAv, 0, QUIET); CropImage(EqImg, 0, 0, iBestAv, 0, QUIET); }else { CropRgbImage(Img, 0, 0, 0, iBestAv, QUIET); CropImage(EqImg, 0, 0, 0, iBestAv, QUIET); }#endif}//-------------------------------------------------------------------------------// Clip top and bottom dark edges off the image// We use the equalized image EqImg for determining the edges but write into// the original image Img.static void TrimImageVertically (RgbImage &Img, Image &EqImg, bool fTopEdge){tRGB *pRgb = Img.buf;byte *pEq = EqImg.buf;int width = Img.width, height = Img.height;DASSERT(EqImg.width == width && EqImg.height == height);double Delta;int ix, iy, n = 0, iyBestPrev = ngSearchLenVert;int *aiBest = (int *)calloc(width, sizeof(int));int nSearchLenVert = ngSearchLenVert;for (ix = 0; ix < width; ix += ngSkip) { double MaxDelta = -1; int iyBest = 0; double Gray0, Gray1, EdgeGray, MaxWhite = 0; for (iy = 0; iy < nSearchLenVert; iy += ngSkip1) { GetAvY(&Gray0, pEq, width, height, ix, (fTopEdge? height-iy-1: iy), ngAvLen, fTopEdge); if (iy == 0) EdgeGray = Gray0; // gray value at edge of paper GetAvY(&Gray1, pEq, width, height, ix, (fTopEdge? height-iy-2: iy+1), ngAvLen, fTopEdge); Delta = Gray1 - Gray0; if (Delta < gMinNegDelta) // page is getting dark again: was blank page, now lettering { //nSearchLenVert = iy; // remember where lettering started for subsequent search limit break; } if (Delta > MaxDelta) { MaxDelta = Delta; iyBest = iy; MaxWhite = Gray1; } } if (EdgeGray < 40 && MaxDelta > gMaxDeltaLimit) // found edge? { if (iyBest > iyBestPrev + 10) // don't go more than 10 pixels from previous iyBest = iyBestPrev + 10; // this helps prevent stray long lines aiBest[ix] = iyBest;#if 0 // for debugging, show what we are trimming for (iy = 0; iy < iyBest + ngAvLen; iy++) { tRGB *p = pRgb + ix + (fTopEdge? iy: height-1-iy) * width; p->Red = p->Green = p->Blue = 255; }#endif iyBestPrev = iyBest; } else iyBestPrev = 0; }for (ix = 0; ix < width; ix++) if (aiBest[ix] > nSearchLenVert) aiBest[ix] = nSearchLenVert;// crop the image at the line of the ngFracWidth smallest iBestsqsort((void *)aiBest, (size_t)width, sizeof(int), CompareIntsReverse);int iBestAv = 0;for (ix = 0; ix < (ngFracWidth / ngSkip); ix++) iBestAv += aiBest[ix]; iBestAv /= (ngFracWidth / ngSkip);iBestAv += ngVertExtra;#if 1if (fTopEdge) { CropRgbImage(Img, 0, iBestAv, 0, 0, QUIET); CropImage(EqImg, 0, iBestAv, 0, 0, QUIET); }else { CropRgbImage(Img, iBestAv, 0, 0, 0, QUIET); CropImage(EqImg, iBestAv, 0, 0, 0, QUIET); }#endif}//-------------------------------------------------------------------------------void TrimImage (RgbImage &Img, bool fVerbose){if (fVerbose) lprintf("auto-trim ");if (Img.width < 100 || Img.height < 100) // somewhat arbitrary but bigger than ngAvLen Err("Can only trim edges with width >= 100 and height >= 100");Image EqImg(Img);EqualizeImage(EqImg, IM_QUICK_EQUALIZE);TrimImageHorizontally(Img, EqImg, false); // left edgeTrimImageHorizontally(Img, EqImg, true); // right edgeTrimImageVertically(Img, EqImg, false);TrimImageVertically(Img, EqImg, true);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -