📄 imutil.cpp
字号:
void ReduceImageAssign (Image &OutImg, // out const Image &InImg, double Scale, int ReduceMethod, bool fVerbose) // in{if (fVerbose) lprintf("Reduce %g ", Scale);if (fEqual(Scale, 1, 1e-3)) // an optimization for speed OutImg = InImg;else { int iScale, ix, iy; int nNewWidth = int(InImg.width / Scale), nNewHeight = int(InImg.height / Scale); if (nNewWidth < 10 || nNewHeight < 10) // 10 is rather arbitrary SysErr("ReduceImageAssign: image too small, nNewWidth %d nNewHeight %d", nNewWidth, nNewHeight); switch (ReduceMethod) { case IM_NEAREST_PIXEL: OutImg.dim(nNewWidth, nNewHeight); for (iy = 0; iy < nNewHeight; iy++) { int iy1 = (iy * InImg.height) / nNewHeight; for (ix = 0; ix < nNewWidth; ix++) OutImg(ix, iy) = InImg((ix * InImg.width) / nNewWidth, iy1); } break; case IM_BILINEAR: OutImg = InImg; //TODO make this more efficient? ScaleImage(OutImg, nNewWidth, nNewHeight, fVerbose, IM_BILINEAR); break; case IM_AVERAGE_ALL: ASSERT(fEqual(floor(Scale), Scale, 1e-3)); // scale is an integer? iScale = (int)Scale; // for efficiency do calculations using ints not doubles OutImg.dim(nNewWidth, nNewHeight); for (iy = 0; iy < nNewHeight; iy++) for (ix = 0; ix < nNewWidth; ix++) { int Pixel = 0; for (int j = 0; j < iScale; j++) for (int i = 0; i < iScale; i++) Pixel += InImg((ix * iScale) + i, iy * iScale + j); OutImg(ix, iy) = byte(Pixel / (iScale * iScale)); } break; default: SysErr("CONF_ReduceMethod"); break; } }}//-----------------------------------------------------------------------------// Return pixel value at (possibly inter-pixel) coordinate ix,iy in buf//// Lifted and modified from img.cc:interpolateExtendstatic inline int BilinearInterpolate (const Image &Img, double x, double y, bool fExtend) // in{if (x < 0.0) { if (fExtend) x = 0.0; else return 0; }else if (x > Img.width - 1.0) { if (fExtend || x <= Img.width) x = Img.width - 1.0; else return 0; }if (y < 0.0) { if (fExtend) y = 0.0; else return 0; }else if (y > Img.height - 1.0) { if (fExtend || y <= Img.height) y = Img.height - 1.0; else return 0; }int iLeft = (int)floor(x);int iRight = (int)ceil(x);int iTop = (int)floor(y);int iBottom = (int)ceil(y);double BotLeft = Img(iLeft + iBottom * Img.width);double BotRight = Img(iRight + iBottom * Img.width);double UpLeft = Img(iLeft + iTop * Img.width);double UpRight = Img(iRight + iTop * Img.width);double xFrac = x - iLeft;double yFrac = y - iTop;double Val = yFrac * ((1.0 - xFrac) * BotLeft + xFrac * BotRight) + (1.0 - yFrac) * ((1.0 - xFrac) * UpLeft + xFrac * UpRight);return (int)floor(Val + 0.5);}//-----------------------------------------------------------------------------// macro for speed#define NEAREST_PIXEL(iPixel, Img, x, y, fExtend) \{ \bool fZero = false; \if (x < 0.0) \ { \ if (fExtend) \ x = 0.0; \ else \ fZero = true; \ } \else if (x > Img.width - 1.0) \ { \ if (fExtend || x <= Img.width) \ x = Img.width - 1.0; \ else \ fZero = true; \ } \if (y < 0.0) \ { \ if (fExtend) \ y = 0.0; \ else \ fZero = true; \ } \else if (y >= Img.height - 1.0) \ { \ if (fExtend || y <= Img.height) \ y = Img.height - 1.0; \ else \ fZero = true; \ } \iPixel = fZero? 0: (byte)Img((int)(x + 0.5), ((int)(y + 0.5))); \} //-----------------------------------------------------------------------------// xMid,yMid are the center of the new image but specified w.r.t. the old image pIn// Scale is how how we want to rescale the image.// Angle is the angle we want to rotate the image by anticlockwise (in radians)// nNewWidth nNewHeight are width and height of the new image//// I tested this by checking the images it produces against images transformed// by Photoshop. Everything seems to line up except that there may be a quarter pixel// difference (a shift between our image and Photoshop's) for certain transformation// involving a scale down, shift, and offset. Possibly subjective. It's hard to// tell because their scale-down bilinear transform seems smoother than ours (looks// more like a bicubic).//// If Angle is 0, image quality is slightly better when xMid and yMid are// integral - but they don't have to be.//// If fBilinear is false we use the nearest pixel (usually a sharper image)//// When getting a pixel from the old image and that pixel is off the image:// fExtend=false: use black (this is the default)// fExtend=truee: use the closest edge pixel, thus effectively extending // the edge of of the input image//// TODO known bug: sometimes miscalculates the edge pixels. To see(?): rotate image by PI, look at edges.//// Lifted and modified from a Rowley routinevoid ExtractImage (Image &Img, // io int nNewWidth, int nNewHeight, // in double nxMid, double nyMid, double Scale, double Angle, // in bool fVerbose, bool fBilinear, bool fExtend) // in{// The 0.5s compensate for BilinearInterpolate taking floor and ceilingdouble Width2 = (double)nNewWidth / 2.0 - 0.5;double height2 = (double)nNewHeight / 2.0 - 0.5;double cosA = cos(Angle);double sinA = sin(Angle);Image OutImg(nNewWidth, nNewHeight);if (fVerbose) lprintf("extract %dx%d at %.3g,%.3g scale %.3g rotate %.3g ", nNewWidth, nNewHeight, nxMid, nyMid, Scale, Angle * 180.0/PI);for (int j = 0; j < nNewHeight; j++) { double dj = (double)j; int jWidth = j * nNewWidth; for (int i = 0; i < nNewWidth; i++) { double di = (double)i; double xr = nxMid + ((di - Width2) * cosA - (dj - height2) * sinA - 0.5) / Scale; double yr = nyMid + ((di - Width2) * sinA + (dj - height2) * cosA - 0.5) / Scale; if (fBilinear) OutImg(i + jWidth) = (byte)BilinearInterpolate(Img, xr, yr, fExtend); else NEAREST_PIXEL(OutImg(i + jWidth), Img, xr, yr, fExtend); } }Img = OutImg;}//-----------------------------------------------------------------------------void DrawVerticalLine (Image &Img, // io int ix, bool fVerbose) // in{if (fVerbose) lprintf("drawVert %d ", ix);for (int iy = 0; iy < Img.height; iy++) Img(ix, iy) = 255;}//-----------------------------------------------------------------------------void DrawHorizontalLine (Image &Img, // io int iy, bool fVerbose) // in{if (fVerbose) lprintf("drawHoriz %d ", iy);for (int ix = 0; ix < Img.width; ix++) Img(ix, iy) = 255;}//-----------------------------------------------------------------------------// TODO not yet in test.cppvoid FillImage (Image &Img, byte Color){for (int i = 0; i < Img.width * Img.height; i++) Img.buf[i] = Color;}//-----------------------------------------------------------------------------// TODO not yet in test.cppvoid FillRectangle (Image &Img, int ix1, int iy1, int ix2, int iy2, int Color){for (int iy = iy1; iy < iy2; iy++) if (iy >= 0 && iy < Img.height) for (int ix = ix1; ix < ix2; ix++) if (ix >= 0 && ix < Img.width) Img(ix, iy) = (byte)Color;}//-----------------------------------------------------------------------------// Scale the given byte pImg down by a factor of 1.2, putting the result// in the given destination (which can be the same as the source). The// scaling uses bilinear interpolation, implemented by two steps of linear// interpolation: first scaling in the X direction, then in the Y direction.//// TODO not yet in test.cppvoid ReduceSizeBy1_2 (Image &Dest, Image &Src){#if 1// This version is based on HAR's original code. // I've kept it for backwards compatibility in regression tests. int width = Src.width;int height = Src.height;int nNewWidth = (int)floor(width / 1.2);int nNewHeight = (int)floor(height / 1.2);double scaleX = 1.2, scaleY = 1.2;// first scale in XImage Tmp(nNewWidth, height);int x, y;byte *pIn = Src.buf;for (y = 0; y < height; y++) { for (x = 0; x < nNewWidth; x++) { int pos = (int)(x * scaleX); double frac = (x * scaleX) - pos; double val = (1.0 - frac) * pIn[pos] + frac * pIn[pos + 1]; if (val < 0) val = 0; if (val > 255) val = 255; Tmp(y + x * height) = byte(val + 0.5); } pIn += width; }// scale in YImage Tmp2(nNewWidth, height);pIn = Tmp.buf;for (y = 0; y < nNewWidth; y++) { for (x = 0; x < nNewHeight; x++) { int pos = (int)(x * scaleY); double frac = (x * scaleY) - pos; double val = (1.0 - frac) * pIn[pos] + frac * pIn[pos + 1]; if (val < 0) val = 0; if (val > 255) val = 255; Tmp2(y + x * nNewWidth) = byte(val + 0.5); } pIn += height; }Dest = Tmp2;#else// This is a new version. It gives slightly different results from the above version// because of small differences in pixel conversionDest = Src; // TODO this could be made more efficient since we are overwriting Dest?ScaleImage(Dest, (int)floor(Src.width / 1.2), (int)floor(Src.height / 1.2), false, IM_BILINEAR);#endif}//-----------------------------------------------------------------------------// get size of an image stored on diskvoid GetImageSize (int &width, int &height, char sPath[]){Image Img;sLoadImage(Img, sPath);width = Img.width;height = Img.height;}//-----------------------------------------------------------------------------// A function for swapping images efficientlyinline void SwapImages (Image &Img0, Image &Img1){Image Temp;// Copy the image structures but not the pixel buffers.// The memcpy is dangerous because we are copying the address of Temp.buf.// A subsequent ~Temp will also free Img0's buffer (since we don't keep reference counts). memcpy(&Temp, &Img0, sizeof(Image));memcpy(&Img0, &Img1, sizeof(Image));memcpy(&Img1, &Temp, sizeof(Image));Temp.buf = NULL; // nasty trick to prevent ~Temp from affecting Img1}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -