|
|
Comments and Discussions
|
|
 |

|
Below is the code for a modified version of C2PassScale that uses integer arithmetic to achieve a 3x-4x speed improvement. It also handles images with more than 3 colors (change PS_MAX_DEPTH if you plan on using more than 4 colors). It does _not_ currently handles images with other than 8 bits per channel. There were also some bug fixes that I have included. It also now takes the size of a full scan line in bytes. By using this intelligently, it is possible to scale a sub-rectangle of an image into a full image, or into another sub rectangle. Unfortunately, I had already made some windows specific modifications for my own use, and I am too busy to take them out. So if you are not running on a windows platform, you will need to take out the ASSERT lines, and change some of the data types. [3-30-05] Added comments to Scale() and AllocAndScale() to clarify the parameters. Hope this is useful, Jake Montgomery
=============== CODE BELOW ================== #pragma once #include <math.h> #define BOUND(x,a,b) \ (((x) <= (a)) ? (a) : (((x) > (b)) ? (b) : (x))) #define PS_MAX_DEPTH 4 // Based on Eran Yariv's TwoPassScale, modified for speed, bug fixes, and variable depths. // Does 2 pass scaling of bitmaps // Template based, can use various methods for interpolation. // Could certainly be improved. // to use: // C2PassScale <CBilinearFilter> ScaleEngine; // ScaleEngine.Scale ((UCHAR *)dibSrc.GetBits(), // A pointer to the source bitmap bits // depth, // The size of a single pixel in bytes (both source and scaled image) // dibSrc.Width(), // The width of a line of the source image to scale in pixels // dibSrc.WidthBytes(), // The width of a single line of the source image in bytes (to allow for padding, etc.) // dibSrc.Height(), // The height of the source image to scale in pixels. // (UCHAR *)GetBits(), // A pointer to a buffer to hold the ecaled image // Width(), // The desired width of a line of the scaled image in pixels // WidthBytes(), // The width of a single line of the scaled image in bytes (to allow for padding, etc.) // Height()); // The desired height of the scaled image in pixels. // or AllocAndScale() // Modified 1-17-05 to use more integer math -- much faater. [JRM]
class CGenericFilter { public: CGenericFilter (double dWidth) : m_dWidth (dWidth) {} virtual ~CGenericFilter() {} double GetWidth() { return m_dWidth; } void SetWidth (double dWidth) { m_dWidth = dWidth; } virtual double Filter (double dVal) = 0; protected: #define FILTER_PI double (3.1415926535897932384626433832795) #define FILTER_2PI double (2.0 * 3.1415926535897932384626433832795) #define FILTER_4PI double (4.0 * 3.1415926535897932384626433832795) double m_dWidth; }; class CBoxFilter : public CGenericFilter { public: CBoxFilter (double dWidth = double(0.5)) : CGenericFilter(dWidth) {} virtual ~CBoxFilter() {} double Filter (double dVal) { return (fabs(dVal) <= m_dWidth ? 1.0 : 0.0); } }; class CBilinearFilter : public CGenericFilter { public: CBilinearFilter (double dWidth = double(1.0)) : CGenericFilter(dWidth) {} virtual ~CBilinearFilter() {} double Filter (double dVal) { dVal = fabs(dVal); return (dVal < m_dWidth ? m_dWidth - dVal : 0.0); } }; class CGaussianFilter : public CGenericFilter { public: CGaussianFilter (double dWidth = double(3.0)) : CGenericFilter(dWidth) {} virtual ~CGaussianFilter() {} double Filter (double dVal) { if (fabs (dVal) > m_dWidth) { return 0.0; } return exp (-dVal * dVal / 2.0) / sqrt (FILTER_2PI); } }; class CHammingFilter : public CGenericFilter { public: CHammingFilter (double dWidth = double(0.5)) : CGenericFilter(dWidth) {} virtual ~CHammingFilter() {} double Filter (double dVal) { if (fabs (dVal) > m_dWidth) { return 0.0; } double dWindow = 0.54 + 0.46 * cos (FILTER_2PI * dVal); double dSinc = (dVal == 0) ? 1.0 : sin (FILTER_PI * dVal) / (FILTER_PI * dVal); return dWindow * dSinc; } }; class CBlackmanFilter : public CGenericFilter { public: CBlackmanFilter (double dWidth = double(0.5)) : CGenericFilter(dWidth) {} virtual ~CBlackmanFilter() {} double Filter (double dVal) { if (fabs (dVal) > m_dWidth) { return 0.0; } double dN = 2.0 * m_dWidth + 1.0; return 0.42 + 0.5 * cos (FILTER_2PI * dVal / ( dN - 1.0 )) + 0.08 * cos (FILTER_4PI * dVal / ( dN - 1.0 )); } };
typedef struct { unsigned int*Weights; // Normalized weights of neighboring pixels int Left,Right; // Bounds of source pixels window } ContributionType; // Contirbution information for a single pixel typedef struct { ContributionType *ContribRow; // Row (or column) of contribution weights UINT WindowSize, // Filter window size (of affecting source pixels) LineLength; // Length of line (no. or rows / cols) } LineContribType; // Contribution information for an entire line (row or column)
typedef BOOL (*ProgressAnbAbortCallBack)(BYTE bPercentComplete); template <class FilterClass> class C2PassScale { public: C2PassScale (ProgressAnbAbortCallBack callback = NULL) : m_Callback (callback) {m_byteDepth = 3;} virtual ~C2PassScale() {} UCHAR * AllocAndScale ( UCHAR *pOrigImage, UINT pixelBytes, UINT uOrigWidth, UINT uOrigWidthBytes, UINT uOrigHeight, UINT uNewWidth, UINT uNewWidthBytes, UINT uNewHeight); UCHAR * Scale ( UCHAR *pOrigImage, UINT pixelBytes, UINT uOrigWidth, UINT uOrigWidthBytes, UINT uOrigHeight, UCHAR *pDstImage, UINT uNewWidth, UINT uNewWidthBytes, UINT uNewHeight); int m_byteDepth; private: ProgressAnbAbortCallBack m_Callback; BOOL m_bCanceled; LineContribType *AllocContributions ( UINT uLineLength, UINT uWindowSize); void FreeContributions (LineContribType * p); LineContribType *CalcContributions ( UINT uLineSize, UINT uSrcSize, double dScale); void ScaleRow ( UCHAR *pSrc, UINT uSrcWidth, UINT uSrcWidthBytes, UCHAR *pRes, UINT uResWidth, UINT uDstWidthBytes, UINT uRow, LineContribType *Contrib); void HorizScale ( UCHAR *pSrc, UINT uSrcWidth, UINT uSrcWidthBytes, UINT uSrcHeight, UCHAR *pDst, UINT uResWidth, UINT uResWidthBytes); void ScaleCol ( UCHAR *pSrc, UINT uSrcWidth, UINT uSrcWidthBytes, UCHAR *pRes, UINT uResWidth, UINT uResWidthBytes, UINT uResHeight, UINT uCol, LineContribType *Contrib); void VertScale ( UCHAR *pSrc, UINT uSrcWidth, UINT uSrcWidthBytes, UINT uSrcHeight, UCHAR *pDst, UINT uResHeight); }; template <class FilterClass> LineContribType * C2PassScale<FilterClass>:: AllocContributions (UINT uLineLength, UINT uWindowSize) { LineContribType *res = new LineContribType; // Init structure header res->WindowSize = uWindowSize; res->LineLength = uLineLength; // Allocate list of contributions res->ContribRow = new ContributionType[uLineLength]; for (UINT u = 0 ; u < uLineLength ; u++) { // Allocate contributions for every pixel res->ContribRow[u].Weights = new unsigned int[uWindowSize]; } return res; } template <class FilterClass> void C2PassScale<FilterClass>:: FreeContributions (LineContribType * p) { for (UINT u = 0; u < p->LineLength; u++) { // Free contribs for every pixel delete [] p->ContribRow[u].Weights; } delete [] p->ContribRow; // Free list of pixels contribs delete p; // Free contribs header } template <class FilterClass> LineContribType * C2PassScale<FilterClass>:: CalcContributions (UINT uLineSize, UINT uSrcSize, double dScale) { FilterClass CurFilter; double dWidth; double dFScale = 1.0; double dFilterWidth = CurFilter.GetWidth(); if (dScale < 1.0) { // Minification dWidth = dFilterWidth / dScale; dFScale = dScale; } else { // Magnification dWidth= dFilterWidth; } // Window size is the number of sampled pixels // int iWindowSize = 2 * (int)ceil(dWidth) + 1; int iWindowSize = 2 * ((int)ceil(dWidth) + 1); // changed ... causing crash with bi-linear filiter?? [JRM] // Allocate a new line contributions strucutre LineContribType *res = AllocContributions (uLineSize, iWindowSize); double *dWeights = new double[iWindowSize]; for (UINT u = 0; u < uLineSize; u++) { // Scan through line of contributions double dCenter = (double)u / dScale; // Reverse mapping // Find the significant edge points that affect the pixel int iLeft = max (0, (int)floor (dCenter - dWidth)); int iRight = min ((int)ceil (dCenter + dWidth), int(uSrcSize) - 1); // Cut edge points to fit in filter window in case of spill-off if (iRight - iLeft + 1 > iWindowSize) { if (iLeft < (int(uSrcSize) - 1 / 2)) { iLeft++; } else { iRight--; } } int nFallback = iLeft; BOOL bNonZeroFound = false; double dTotalWeight = 0.0; // Zero sum of weights double dVal; for (int iSrc = iLeft; iSrc <= iRight; iSrc++) { // Calculate weights dVal = CurFilter.Filter (dFScale * (dCenter - (double)iSrc)); if (dVal > 0.0) dVal *= dFScale; else { dVal = 0.0; // zero conribution, trim if (!bNonZeroFound) { // we are on the left side, trim left iLeft = iSrc+1; continue; } else { // we are on the right side, trim right iRight = iSrc-1; break; } } bNonZeroFound = true; dTotalWeight += dVal; dWeights[iSrc-iLeft] = dVal; } if (iLeft > iRight) { ASSERT(FALSE); iLeft = iRight = nFallback; dWeights[0] = 0.0; } res->ContribRow[u].Left = iLeft; res->ContribRow[u].Right = iRight; ASSERT (dTotalWeight >= 0.0); // An error in the filter function can cause this if (dTotalWeight > 0.0) { // Normalize weight of neighbouring points for (iSrc = iLeft; iSrc <= iRight; iSrc++) { // Normalize point dWeights[iSrc-iLeft] /= dTotalWeight; } } // scale weights to integers weights for effeciency for (iSrc = iLeft; iSrc <= iRight; iSrc++) res->ContribRow[u].Weights[iSrc-iLeft] = (unsigned int)(dWeights[iSrc-iLeft] * 0xffff); } delete [] dWeights; return res; }
template <class FilterClass> void C2PassScale<FilterClass>:: ScaleRow ( UCHAR *pSrc, UINT uSrcWidth, UINT uSrcWidthBytes, UCHAR *pRes, UINT uResWidth, UINT uResWidthBytes, UINT uRow, LineContribType *Contrib) { UCHAR * const pSrcRow = &(pSrc[uRow * uSrcWidthBytes]); UCHAR * const pDstRow = &(pRes[uRow * uResWidthBytes]); UCHAR *pSrcLoc; UCHAR *pDstLoc; unsigned int vals[PS_MAX_DEPTH]; for (UINT x = 0; x < uResWidth; x++) { // Loop through row int v, i; for (v= 0; v < m_byteDepth; v++) vals[v] = 0; int iLeft = Contrib->ContribRow[x].Left; // Retrieve left boundries int iRight = Contrib->ContribRow[x].Right; // Retrieve right boundries pSrcLoc = &pSrcRow[iLeft*m_byteDepth]; for (i = iLeft; i <= iRight; i++) { // Scan between boundries #ifdef _DEBUG ASSERT(i-iLeft < (int)Contrib->WindowSize); #endif // Accumulate weighted effect of each neighboring pixel for (v= 0; v < m_byteDepth; v++) vals[v] += Contrib->ContribRow[x].Weights[i-iLeft] * *pSrcLoc++; } pDstLoc = &pDstRow[x*m_byteDepth]; for (v= 0; v < m_byteDepth; v++) { // copy to destination, and scale back down by BYTE *pDstLoc++ = BOUND(vals[v] >> 16, 0, 0xff); // Place result in destination pixel } } } template <class FilterClass> void C2PassScale<FilterClass>:: HorizScale ( UCHAR *pSrc, UINT uSrcWidth, UINT uSrcWidthBytes, UINT uSrcHeight, UCHAR *pDst, UINT uResWidth, UINT uResWidthBytes) { // Assumes heights are the same // TRACE ("Performing horizontal scaling...\n"); if (uResWidth == uSrcWidth) { // No scaling required, just copy if(uSrcHeight <= 0) return; if (uResWidthBytes == uSrcWidthBytes) { int copy = ((uSrcHeight -1) * uSrcWidthBytes) + uSrcWidth*m_byteDepth; // avoids overrun if starting in middle of image. memcpy (pDst, pSrc, copy); return; } else { for (UINT y = 0; y < uSrcHeight; y++) memcpy(pDst+uResWidthBytes*y, pSrc+uSrcWidthBytes*y, uSrcWidth*m_byteDepth); return; } } // Allocate and calculate the contributions LineContribType * Contrib = CalcContributions (uResWidth, uSrcWidth, double(uResWidth) / double(uSrcWidth)); for (UINT u = 0; u < uSrcHeight; u++) { // Step through rows if (NULL != m_Callback) { // // Progress and report callback supplied // if (!m_Callback (BYTE(double(u) / double (uSrcHeight) * 50.0))) { // // User wished to abort now // m_bCanceled = TRUE; FreeContributions (Contrib); // Free contributions structure return; } } ScaleRow ( pSrc, uSrcWidth, uSrcWidthBytes, pDst, uResWidth, uResWidthBytes, u, Contrib); // Scale each row } FreeContributions (Contrib); // Free contributions structure } template <class FilterClass> void C2PassScale<FilterClass>:: ScaleCol ( UCHAR *pSrc, UINT uSrcWidth, UINT uSrcWidthBytes, UCHAR *pRes, UINT uResWidth, UINT uResWidthBytes, UINT uResHeight, UINT uCol, LineContribType *Contrib) { UCHAR *pSrcLoc; UCHAR *pDstLoc; unsigned int vals[PS_MAX_DEPTH]; // assumes same height for (UINT y = 0; y < uResHeight; y++) { // Loop through column int v, i; for (v= 0; v < m_byteDepth; v++) vals[v] = 0; int iLeft = Contrib->ContribRow[y].Left; // Retrieve left boundries int iRight = Contrib->ContribRow[y].Right; // Retrieve right boundries pSrcLoc = pSrc + uSrcWidthBytes*iLeft + uCol* m_byteDepth; for (i = iLeft; i <= iRight; i++) { // Scan between boundries // Accumulate weighted effect of each neighboring pixel UCHAR *pCurSrc = pSrc + uSrcWidthBytes*i + uCol* m_byteDepth; #ifdef _DEBUG ASSERT(i-iLeft < (int)Contrib->WindowSize); #endif for (v= 0; v < m_byteDepth; v++) vals[v] += Contrib->ContribRow[y].Weights[i-iLeft] * pSrcLoc[v]; pSrcLoc += uSrcWidthBytes; } pDstLoc = pRes + (y * uResWidthBytes) + uCol*m_byteDepth; for (v= 0; v < m_byteDepth; v++) { // scale back *pDstLoc++ = BOUND( vals[v] >> 16, 0, 0xff); // Place result in destination pixel } } }
template <class FilterClass> void C2PassScale<FilterClass>:: VertScale ( UCHAR *pSrc, UINT uSrcWidth, UINT uSrcWidthBytes, UINT uSrcHeight, UCHAR *pDst, UINT uResHeight) { // TRACE ("Performing vertical scaling..."); // assumes widths are the same! if (uSrcHeight == uResHeight) { // No scaling required, just copy if (uSrcHeight <= 0) return; int copy = ((uSrcHeight -1) * uSrcWidthBytes) + uSrcWidth*m_byteDepth; // avoids overrun if starting in middle of image. memcpy (pDst, pSrc, copy); return; } // Allocate and calculate the contributions LineContribType * Contrib = CalcContributions (uResHeight, uSrcHeight, double(uResHeight) / double(uSrcHeight)); for (UINT u = 0; u < uSrcWidth; u++) { // Step through columns if (NULL != m_Callback) { // // Progress and report callback supplied // if (!m_Callback (BYTE(double(u) / double (uSrcWidth) * 50.0) + 50)) { // // User wished to abort now // m_bCanceled = TRUE; FreeContributions (Contrib); // Free contributions structure return; } } ScaleCol ( pSrc, uSrcWidth, uSrcWidthBytes, pDst, uSrcWidth, uSrcWidthBytes, uResHeight, u, Contrib); // Scale each column } FreeContributions (Contrib); // Free contributions structure } template <class FilterClass> UCHAR * C2PassScale<FilterClass>:: AllocAndScale ( UCHAR *pOrigImage, // A pointer to the source bitmap bits UINT pixelBytes, // The size of a single pixel in bytes (both source and scaled image) UINT uOrigWidth, // The width of a line of the source image to scale in pixels UINT uOrigWidthBytes, // The width of a single line of the source image in bytes (to allow for padding, etc.) UINT uOrigHeight, // The height of the source image to scale in pixels. UINT uNewWidth, // The desired width of a line of the scaled image in pixels UINT uNewWidthBytes, // The width of a single line of the scaled image in bytes (to allow for padding, etc.) UINT uNewHeight) // The desired height of the scaled image in pixels. { // Scale source image horizontally into temporary image m_byteDepth = pixelBytes; m_bCanceled = FALSE; UCHAR *pTemp = new UCHAR [uNewWidthBytes * uOrigHeight]; HorizScale (pOrigImage, uOrigWidth, uOrigWidthBytes, uOrigHeight, pTemp, uNewWidth, uNewWidthBytes); if (m_bCanceled) { delete [] pTemp; return NULL; } // Scale temporary image vertically into result image UCHAR *pRes = new UCHAR [uNewWidth * uNewHeight *m_byteDepth]; VertScale ( pTemp, uNewWidth, uNewWidthBytes, uOrigHeight, pRes, uNewHeight); if (m_bCanceled) { delete [] pTemp; delete [] pRes; return NULL; } delete [] pTemp; return pRes; } template <class FilterClass> UCHAR * C2PassScale<FilterClass>:: Scale ( UCHAR *pOrigImage, // A pointer to the source bitmap bits UINT pixelBytes, // The size of a single pixel in bytes (both source and scaled image) UINT uOrigWidth, // The width of a line of the source image to scale in pixels UINT uOrigWidthBytes, // The width of a single line of the source image in bytes (to allow for padding, etc.) UINT uOrigHeight, // The height of the source image to scale in pixels. UCHAR *pDstImage, // A pointer to a buffer to hold the ecaled image UINT uNewWidth, // The desired width of a line of the scaled image in pixels UINT uNewWidthBytes, // The width of a single line of the scaled image in bytes (to allow for padding, etc.) UINT uNewHeight) // The desired height of the scaled image in pixels. { // Scale source image horizontally into temporary image ASSERT(PS_MAX_DEPTH >= pixelBytes); m_byteDepth = pixelBytes; m_bCanceled = FALSE; UCHAR *pTemp = new UCHAR [ uOrigHeight *uNewWidthBytes]; HorizScale (pOrigImage, uOrigWidth, uOrigWidthBytes, uOrigHeight, pTemp, uNewWidth, uNewWidthBytes); if (m_bCanceled) { delete [] pTemp; return NULL; } // Scale temporary image vertically into result image VertScale ( pTemp, uNewWidth, uNewWidthBytes, uOrigHeight, pDstImage, uNewHeight); delete [] pTemp; if (m_bCanceled) { return NULL; } return pDstImage; }
|
|
|
|

|
Where is the "macros.h" header file?
It is impossible to compile your file under MSVC.NET 2003 because BOUND isn't defined and so on.
|
|
|
|

|
I have fixed the original post.
The only item you need from macros.h is
#define BOUND(x,a,b) \
(((x) <= (a)) ? (a) : (((x) > (b)) ? (b) : (x)))
|
|
|
|

|
Thanks
|
|
|
|

|
In the below code you are using dibSrc object. Which class is this? I don't see it in this project. Can you give the URL from where I can download the dibSrc class?
Because I don't know to calculate WidthBytes that is passed to AllocAndScale function
Thanks.
// C2PassScale ScaleEngine;
// ScaleEngine.Scale ((UCHAR *)dibSrc.GetBits(),
// depth,
// dibSrc.Width(),
// dibSrc.WidthBytes(),
// dibSrc.Height(),
// (UCHAR *)GetBits(),
// Width(),
// WidthBytes(),
// Height());
-- modified at 13:35 Saturday 13th May, 2006
|
|
|
|

|
I believe that dibSrc is only used in the example code in the comments, therefore it should be understood to be incomplete. dibSrc in this case is not a class, but an instance of an image class that is not defined in this project. There is a comment on the WidthBytes that you removed: "The width of a single line of the source image in bytes (to allow for padding, etc.)". If this is not enough explanation, then I will try again. "WidthBytes" (uOrigWidthBytes and uNewWidthBytes in the code) is the number of bytes between lines of the image, so if line 0 starts at address 0x1000, and line 1 starts at 0x1321, then "WidthBytes" would be 0x0321. If your image is tightly packed, with no memory gap between lines, the WidthBytes is simply the "width in pixels" * "size of a single pixel in bytes". Hope this helps.
|
|
|
|

|
I am having a problem while scaling an image 800 x 600 to a size of 75 x 75.
It works fine for 76 x 76. It throws access violation error in following method.
template
void
C2PassScale::
ScaleCol
Can you elaborate why this is happening?
|
|
|
|

|
In my experience working with images and whatnot, image dimensions that are a multiple of 4 seem to be much more agreeable. Try making sure that your dimensions are multiples of 4.
|
|
|
|

|
Yes, I tried out what u suggested. The width of the image must be multiple of 4 in order for the algorithm to work.
|
|
|
|

|
I do strongly advise not to use this code for 24bit bitmaps – there are bugs all over.
The destination bitmap is VERY much smoothed, it’s is shifted 1 pixel upwards and 1 pixel to the right, it produces wrong colours (for example a grey bitmap (128) is turned to 126; very bright values are turned to about 2 values too low).
I didn’t test on other than 24bit bitmaps, but wouldn’t be surprised if it’s the same there. As the code contains some strange lines (for example int ... – 1 / 2, or „caused crash ...“).
To me this is typical example how openly disposed code should not be: Only very bad tested, not maintained, ...
So, if you want my opinion: Don’t use this junk.
If you need 24bit resampling, StretchDIBits (in Mode HALFTONE) does a good job. For example: www.christian-etter.de/?p=283
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
|
A smart way of scaling bitmaps
| Type | Article |
| Licence | |
| First Posted | 11 Dec 1999 |
| Views | 179,133 |
| Bookmarked | 52 times |
|
|