windows-nt/Source/XPSP1/NT/printscan/ui/uicommon/regionde.cpp
2020-09-26 16:20:57 +08:00

1258 lines
54 KiB
C++

// RegionDetector.cpp: implementation of the CRegionDetector class.
//
//////////////////////////////////////////////////////////////////////
#include "precomp.h"
#pragma hdrstop
#include "regionde.h"
inline ULONG Intensity(ULONG value);
inline ULONG DifferenceFromGray(ULONG value);
inline UCHAR Difference(UCHAR a, UCHAR b);
inline ULONG Difference(ULONG a, ULONG b);
int inline MAX(int a, int b);
int inline MIN(int a, int b);
// helper functions
// sum of RGB vals
inline ULONG Intensity(ULONG value)
{
return(value&0xff)+((value&0xff00)>>8)+((value&0xff0000)>>16);
}
// shadows are gray... if you aint gray... you aint a shadow
inline ULONG DifferenceFromGray(ULONG value)
{
UCHAR g,b;//,b;
// r=(UCHAR)(value& 0x0000ff);
g=(UCHAR)((value& 0x00ff00)>>8);
b=(UCHAR)((value& 0xff0000)>>16);
// use this instead of the complete formula (uncomment the commented out code for the complete formula)
// allow yellow scanner backgrounds
return(ULONG)(Difference(b,g));//+Difference(r,g)+Difference(g,b));
}
// we should make a Difference Template to clean up this code
inline UCHAR Difference(UCHAR a, UCHAR b)
{
if (a>b) return(a-b);
else return(b-a);
}
inline ULONG Difference(ULONG a, ULONG b)
{
if (a>b) return(a-b);
else return(b-a);
}
inline LONG Difference(LONG a, LONG b)
{
if (a>b) return(a-b);
else return(b-a);
}
int inline MAX(int a, int b)
{
if (a>b) return(a);
return(b);
}
int inline MIN(int a, int b)
{
if (a<b) return(a);
return(b);
}
// if we have resampled the image, we may want to convert back to the origional coordinate system
bool CRegionDetector::ConvertToOrigionalCoordinates()
{
if (m_pRegions!=NULL)
{
int i;
for (i=0;i<m_pRegions->m_numRects;i++)
{
m_pRegions->m_pRects[i].left=m_pRegions->m_pRects[i].left*m_resampleFactor+m_resampleFactor/2;
m_pRegions->m_pRects[i].right=m_pRegions->m_pRects[i].right*m_resampleFactor+m_resampleFactor/2;
m_pRegions->m_pRects[i].top=m_pRegions->m_pRects[i].top*m_resampleFactor+m_resampleFactor/2;
m_pRegions->m_pRects[i].bottom=m_pRegions->m_pRects[i].bottom*m_resampleFactor+m_resampleFactor/2;
}
return(true);
}
return(false);
}
// simplified Region detection code for single region detection
// Faster and about as accurate
// FindSingleRegion encapsulates a subset of FindRegions. at the moment, for code documentation of FindSingleRegion, see FindRegions
bool CRegionDetector::FindSingleRegion()
{
// cast the pointers
// S = Scan, B = blank background, V = virtual screen... new image
int x,y;
int a,b; // loop vars used for merging regions
int border; // for aggregation on a rectangle by rectangle basis
ULONG position;
int numChunks;
bool unionOperationInLastPass;
ULONG* pImagePixels;
ULONG* pEdgePixels;
int requiredPixels;
// if the bitmap is too large we can use halfsize to resample it down to a more reasonable size...
// on a fast proccessor, current performance tests show that for most preview images this won't be needed
// but if the user scans an image at 300 dpi, this will come in handy.
// m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
// for FingSingleRegion we plan on doing one resample before image proccessing
// with an expected image size of 200x300 pixels which greatly reduces the proccessor load
m_resampleFactor=1;
while (m_pScan->m_nBitmapWidth>GOALX || m_pScan->m_nBitmapHeight>GOALY)
{
m_pScan->HalfSize();
m_resampleFactor*=2;
}
m_pScan->Invert(); // filters operate on inverted images
m_pScan->CorrectBrightness();
requiredPixels=m_pScan->m_nBitmapWidth*m_pScan->m_nBitmapHeight/256/10;
if (requiredPixels==0) requiredPixels=1; // special case for a particularly small preview scan
m_pScan->MaxContrast(requiredPixels); // spread the images color spectrum out... concept: balance color spectra between different scans
m_pScanBlurred->CreateBlurBitmap(m_pScan); // sets scanBlurred to be equal to a blurred version of m_pScan
m_pScanDoubleBlurred->CreateBlurBitmap(m_pScanBlurred);
m_pScanTripleBlurred->CreateBlurBitmap(m_pScanDoubleBlurred); // sets scanBlurred to be equal to a blurred version of m_pScan
m_pScanHorizontalBlurred->CreateHorizontalBlurBitmap(m_pScan);
m_pScanVerticalBlurred->CreateVerticalBlurBitmap(m_pScan);
m_pScanEdges->CreateDifferenceBitmap(m_pScan,m_pScanBlurred); // think about how you create an edge bitmap and you will understand
m_pScanDoubleEdges->CreateDifferenceBitmap(m_pScanBlurred,m_pScanDoubleBlurred); // we will get a huge accuracy boost from this simple step
m_pScanTripleEdges->CreateDifferenceBitmap(m_pScanDoubleBlurred,m_pScanTripleBlurred); // we will get a huge accuracy boost from this simple step
m_pScanHorizontalEdges->CreateDifferenceBitmap(m_pScan,m_pScanHorizontalBlurred); // assuming the user was kind enough to place the image right side up
m_pScanVerticalEdges->CreateDifferenceBitmap(m_pScan,m_pScanVerticalBlurred); // we will get a huge accuracy boost from this simple step
// free memory as soon as it isn't needed
if (m_pScanVerticalBlurred!=NULL)
{
delete m_pScanVerticalBlurred;
m_pScanVerticalBlurred=NULL;
}
if (m_pScanHorizontalBlurred!=NULL)
{
delete m_pScanHorizontalBlurred;
m_pScanHorizontalBlurred=NULL;
}
if (m_pScanDoubleBlurred!=NULL)
{
delete m_pScanDoubleBlurred;
m_pScanDoubleBlurred=NULL;
}
if (m_pScanTripleBlurred!=NULL)
{
delete m_pScanTripleBlurred;
m_pScanTripleBlurred=NULL;
}
// these 5 calls to killShadows make up the real meat of the region detection work done by the program.
// KillShadows now performs more tasks than simply killing shadows. Killshadows also enhances edges
// and removes background colors
// the doubleBlur and tripleBlur edge maps are needed so that we can distinguish between an uneven background scanner color and a real image.
// see KillShadows for more documentation.
// m_pScanBlurred is the bitmap we will use to determine where regions are
// these calls to KillShadows act to enhance pixels which are part of regions and inhibit pixels which are not part of regions
m_pScanBlurred->KillShadows(m_pScanVerticalEdges, MAXSHADOWSTART,MAXSHADOWPIXEL+1,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanHorizontalEdges, MAXSHADOWSTART,MAXSHADOWPIXEL+1,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanEdges, MAXSHADOWSTART,MAXSHADOWPIXEL-1,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,true);
m_pScanBlurred->KillShadows(m_pScanDoubleEdges,MAXSHADOWSTART,MAXSHADOWPIXEL+2,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,true);
m_pScanBlurred->KillShadows(m_pScanTripleEdges, MAXSHADOWSTART,MAXSHADOWPIXEL+1,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,true);
// RemoveBlackBorder removes questionable pixels around the outside edge of an image.
// RemoveBlackBorder has only very limited utility for region detection on images that were not aquired from a scanner
m_pScan->RemoveBlackBorder(MIN_BLACK_SCANNER_EDGE_CHAN_VALUE,m_pScanBlurred,m_pScan);
// despeckle removes small clumps of pixels which are probably stray static
m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
// pMap will hold information on which region each pixel on the screen is part of
int *pMap=new int[m_pScanBlurred->m_nBitmapHeight*m_pScanBlurred->m_nBitmapWidth];
if (pMap)
{
numChunks=m_pScanBlurred->FindChunks(pMap); // maps chunks on m_pScanBlurred to pMap
if (m_pRegions!=NULL) delete m_pRegions;
m_pRegions = new CRegionList(numChunks); // create a CRegionList to map the chunks onto.
if (m_pRegions)
{
m_pRegions->m_nBitmapWidth=m_pScan->m_nBitmapWidth;
m_pRegions->m_nBitmapHeight=m_pScan->m_nBitmapHeight;
// now turn region map into region rectangles
// it could be argued that this routine should be placed in C32BitDibWrapper
// but we don't want to make C32BitDibWrapper encompas too much functionality which
// would only be useful for imagedetection
pImagePixels=(ULONG *)(m_pScanBlurred->m_pBits); // we want to use 32 bit chunks instead of 8 bit chunks
pEdgePixels=(ULONG *)(m_pScanEdges->m_pBits);
// add all the bitmap pixels to the m_pRegions list
position=0;
for (y=0;y<m_pScan->m_nBitmapHeight;y++)
{
for (x=0;x<m_pScan->m_nBitmapWidth;x++)
{
if (pMap[position]>0)
{
m_pRegions->AddPixel(pMap[position]-1, pImagePixels[position],pEdgePixels[position], x, y); // pMap values start at 1, let region values start at 0
} // we start pMap at 1 so that 0 can indicate a pixel that is not assigned to any region
position++; // we may want to make pMap start at 0 at some later date
}
}
m_pRegions->m_numRects=numChunks;
m_pRegions->m_validRects=numChunks;
// free bitmaps as soon as they will no longer be used
if (m_pScanHorizontalEdges!=NULL)
{
delete m_pScanHorizontalEdges;
m_pScanHorizontalEdges=NULL;
}
if (m_pScanVerticalEdges!=NULL)
{
delete m_pScanVerticalEdges;
m_pScanVerticalEdges=NULL;
}
// merge together regions
// this routine is pretty much a waste when performing single region detection
// the only advantage of it over simply calling unionRegions is that
// we can merge together small close together regions, but kill small regions that are far from other regions (probably static)
for (border=0;border<MAXBORDER;border+=SINGLE_REGION_BORDER_INCREMENT) // when detecting a single region detection,
{
// we don't need to inch along one border pixel width increment at a time
// as we know we want to compact down to a single region in the end
unionOperationInLastPass=true;
for (a=0;a<m_pRegions->m_numRects;a++) // overkill, we could be cleverer about when we check for valid regions
{
m_pRegions->checkIfValidRegion(a, border); // sets m_valid params
}
m_pRegions->CompactDown(m_pRegions->m_validRects); // remove all invalid rects to save search time
while (unionOperationInLastPass==true)
{
unionOperationInLastPass=false;
for (a=0;a<m_pRegions->m_numRects;a++)
{
if (m_pRegions->m_valid[a]==true)
{
for (b=a+1;b<m_pRegions->m_numRects;b++)
{
if (m_pRegions->m_valid[b]==true)
{
if (m_pRegions->CheckIntersect(a,b,border)==true)
{
m_pRegions->UnionRegions(a,b);
m_pRegions->checkIfValidRegion(a, border); // in this context, checkvalid should have the effect of culling regions which are probably only stray dots
unionOperationInLastPass=true;
}
}
}
}
}
}
}
// m_pScanBlurred->ColorChunks(pMap); // for debugging purposes... so we know where exactly chunks are
m_pRegions->CompactDown(m_pRegions->m_validRects); // remove all invalid rects to save search time
}
delete[] pMap;
}
return(TRUE);
}
// detect regions
// makes heavy use of C32BitWrapper helper functions
// WARNING: this function has not been updated to include the latest changes
// to compensate for poor image quality
//
int CRegionDetector::FindRegions()
{
// cast the pointers
// S = Scan, B = blank background, V = virtual screen... new image
int x,y;
int a,b;
int i;
bool done, weird;
char* pWall; // 2d array keeping track of which regions have walls between them and other regions
// wall vals... TRUE, FALSE, UNKNOWN
int border; // for aggregation on a rectangle by rectangle basis
ULONG position;
int numChunks;
bool unionOperationInLastPass;
ULONG* pImagePixels;
ULONG* pEdgePixels;
int requiredPixels;
// if the bitmap is too large we can use halfsize to resample it down to a more reasonable size...
// on a fast proccessor, current performance tests show that for most preview images this won't be needed
// but if the user scans an image at 300 dpi, this will come in handy.
while (m_pScan->m_nBitmapWidth>GOALX || m_pScan->m_nBitmapHeight>GOALY)
{
m_pScan->HalfSize();
}
m_pScanBlurred->CreateBlurBitmap(m_pScan); // sets scanBlurred to be equal to a blurred version of m_pScan
m_pScanDoubleBlurred->CreateBlurBitmap(m_pScanBlurred); // sets scanBlurred to be equal to a blurred version of m_pScan
m_pScanTripleBlurred->CreateBlurBitmap(m_pScanDoubleBlurred); // sets scanBlurred to be equal to a blurred version of m_pScan
m_pScanHorizontalBlurred->CreateHorizontalBlurBitmap(m_pScan);
m_pScanVerticalBlurred->CreateVerticalBlurBitmap(m_pScan);
m_pScanDoubleHorizontalBlurred->CreateHorizontalBlurBitmap(m_pScanHorizontalBlurred);
m_pScanDoubleVerticalBlurred->CreateVerticalBlurBitmap(m_pScanVerticalBlurred);
m_pScanEdges->CreateDifferenceBitmap(m_pScan,m_pScanBlurred); // think about how you create an edge bitmap and you will understand
m_pScanDoubleEdges->CreateDifferenceBitmap(m_pScanBlurred,m_pScanDoubleBlurred); // we will get a huge accuracy boost from this simple step
m_pScanTripleEdges->CreateDifferenceBitmap(m_pScanDoubleBlurred,m_pScanTripleBlurred); // we will get a huge accuracy boost from this simple step
m_pScanHorizontalEdges->CreateDifferenceBitmap(m_pScan,m_pScanHorizontalBlurred); // assuming the user was kind enough to place the image right side up
m_pScanVerticalEdges->CreateDifferenceBitmap(m_pScan,m_pScanVerticalBlurred); // we will get a huge accuracy boost from this simple step
m_pScanDoubleHorizontalEdges->CreateDifferenceBitmap(m_pScanHorizontalBlurred,m_pScanDoubleHorizontalBlurred); // assuming the user was kind enough to place the image right side up
m_pScanDoubleVerticalEdges->CreateDifferenceBitmap(m_pScanVerticalBlurred,m_pScanDoubleVerticalBlurred); // we will get a huge accuracy boost from this simple step
m_pScanBlurred->Invert(); // filters operate on inverted images
requiredPixels=m_pScanBlurred->m_nBitmapWidth*m_pScanBlurred->m_nBitmapHeight/256/10;
if (requiredPixels==0) requiredPixels=1; // special case for a particularly small preview scan
m_pScanBlurred->CorrectBrightness();
m_pScanBlurred->MaxContrast(requiredPixels);
// free memory as soon as it isn't needed
if (m_pScanVerticalBlurred!=NULL)
{
delete m_pScanVerticalBlurred;
m_pScanVerticalBlurred=NULL;
}
if (m_pScanHorizontalBlurred!=NULL)
{
delete m_pScanHorizontalBlurred;
m_pScanHorizontalBlurred=NULL;
}
if (m_pScanDoubleBlurred!=NULL)
{
delete m_pScanDoubleBlurred;
m_pScanDoubleBlurred=NULL;
}
if (m_pScanTripleBlurred!=NULL)
{
delete m_pScanTripleBlurred;
m_pScanTripleBlurred=NULL;
}
m_pScanWithShadows = new C32BitDibWrapper(m_pScanBlurred); // copy scanBlurred
// eliminate shadows
m_pScanBlurred->KillShadows(m_pScanTripleEdges, MAXSHADOWSTART,MAXSHADOWPIXEL-2,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanDoubleEdges,MAXSHADOWSTART,MAXSHADOWPIXEL-1,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanEdges, MAXSHADOWSTART,MAXSHADOWPIXEL+1,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanHorizontalEdges, MAXSHADOWSTART,MAXSHADOWPIXEL,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanVerticalEdges, MAXSHADOWSTART,MAXSHADOWPIXEL,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
// compensate for background color
// the average background color pixel will probably have a smaller edge factor than the average shadow,
// but will have a larger difference from grey
// hence to eliminate background color we ignore difference from grey... as accomplished by the (256*3) term
m_pScanBlurred->KillShadows(m_pScanTripleEdges, 256*3,MAXSHADOWPIXEL-2,256*3,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanDoubleEdges,256*3,MAXSHADOWPIXEL-2,256*3,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanEdges, 256*3,MAXSHADOWPIXEL-2,256*3,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanHorizontalEdges, 256*3,MAXSHADOWPIXEL-2,256*3,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanVerticalEdges, 256*3,MAXSHADOWPIXEL-2,256*3,NOT_SHADOW_INTENSITY,false);
// pure economics... edge pixels are much more likely to be junk
// so economically, its worth it to risk
// killing good edge pixels to get rid of the bad
m_pScanBlurred->EdgeDespeckle();
m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
// prepare to find chunks
int *pMap=new int[m_pScanBlurred->m_nBitmapHeight*m_pScanBlurred->m_nBitmapWidth];
if (pMap)
{
done=false;
weird=false;
while (done==false) // we may have to repeat FindChunks if we find that we did not eliminate enough pixels and ended up with all pixels being determined to be part of the same region
{
done=true;
numChunks=m_pScanBlurred->FindChunks(pMap); // fills the pMap array with chunks
if (m_pRegions!=NULL) delete m_pRegions;
m_pRegions = new CRegionList(numChunks);
if (m_pRegions)
{
m_pRegions->m_nBitmapWidth=m_pScan->m_nBitmapWidth;
m_pRegions->m_nBitmapHeight=m_pScan->m_nBitmapHeight;
// now turn the pMap region map into region rectangles
// it could be argued that this routine should be placed in C32BitDibWrapper
// but we don't want to make C32BitDibWrapper encompas too much functionality which
// clearly is directly connected with imagedetection
pImagePixels=(ULONG *)(m_pScanBlurred->m_pBits); // we want to use 32 bit chunks instead of 8 bit chunks
pEdgePixels=(ULONG *)(m_pScanEdges->m_pBits);
position=0;
for (y=0;y<m_pScan->m_nBitmapHeight;y++)
{
for (x=0;x<m_pScan->m_nBitmapWidth;x++)
{
if (pMap[position]>0)
{
m_pRegions->AddPixel(pMap[position]-1, pImagePixels[position],pEdgePixels[position], x, y); // pMap values start at 1, let region values start at 0
} // we start pMap at 1 so that 0 can indicate a pixel that is not assigned to any region
position++; // we may want to make pMap start at 0 at some later date
}
}
m_pRegions->m_numRects=numChunks;
m_pRegions->m_validRects=numChunks;
// check for invalid regions
for (a=0;a<m_pRegions->m_numRects;a++)
{
m_pRegions->checkIfValidRegion(a); // sets m_valid params
m_pRegions->m_backgroundColorPixels[a]=m_pScan->PixelsBelowThreshold(m_pScanBlurred,m_pScanEdges,m_pRegions->m_pRects[a]);
m_pRegions->RegionType(a); // clasify regions... text or photo
}
m_pRegions->CompactDown(m_pRegions->m_validRects);
if (m_pRegions->m_validRects==0) break; // if there aren't any regions, no sense in proceeding
unionOperationInLastPass=true;
while (unionOperationInLastPass==true)
{
unionOperationInLastPass=false;
for (a=0;a<m_pRegions->m_numRects;a++)
{
if (m_pRegions->m_valid[a]==true)
{
for (b=a+1;b<m_pRegions->m_numRects;b++)
{
if (m_pRegions->m_valid[b]==true)
{
// we are paranoid... we repeatedly check if two regions intersect each other.
if (m_pRegions->CheckIntersect(a,b,0)==true)
{
m_pRegions->UnionRegions(a,b);
m_pRegions->checkIfValidRegion(a, 0); // in this context, checkvalid should have the effect of culling regions which are probably only stray dots
m_pRegions->RegionType(a); // figure out what type of region the combined region should be
unionOperationInLastPass=true;
}
}
}
}
}
}
if (weird==false &&
((m_pRegions->m_pRects[0].right-m_pRegions->m_pRects[0].left)
*(m_pRegions->m_pRects[0].bottom-m_pRegions->m_pRects[0].top))
>
((m_pScanBlurred->m_nBitmapWidth-DESPECKLE_BORDER_WIDTH*2)*(m_pScanBlurred->m_nBitmapWidth-DESPECKLE_BORDER_WIDTH*2)))
{
weird=true;
done=false;
// you better not be grey or you are in trouble
// some seriously nasty shadow elimination
// we will probably eliminate too many good pixels
// but at least the poor user will get something more than just finding that the whole screen was selected
m_pScanBlurred->KillShadows(m_pScanTripleEdges, 256,MAXSHADOWPIXEL+4,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanDoubleEdges,256,MAXSHADOWPIXEL+4,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanEdges, 256,MAXSHADOWPIXEL+5,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanHorizontalEdges, 256,MAXSHADOWPIXEL+4,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanVerticalEdges, 256,MAXSHADOWPIXEL+4,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanDoubleHorizontalEdges, MAXSHADOWSTART,MAXSHADOWPIXEL+4,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->KillShadows(m_pScanDoubleVerticalEdges, MAXSHADOWSTART,MAXSHADOWPIXEL+4,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
m_pScanBlurred->EdgeDespeckle();
m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
// m_pScanBlurred->EdgeDespeckle();
m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
}
}
}
if (m_pRegions)
{
m_pRegions->CompactDown(m_pRegions->m_validRects); // compact down the CRegionList so that it nolonger includes invalidated regions
// we store an array indicating which pairs of regions have walls between them
// used for unioning together large text regions... for example, scanning
// in two pages with a shadow between them
pWall=new char[m_pRegions->m_numRects*m_pRegions->m_numRects];
if (pWall)
{
for (a=0;a<m_pRegions->m_numRects;a++)
{
if (m_pRegions->m_valid[a]==true)
{
for (b=a+1;b<m_pRegions->m_numRects;b++)
{
if (m_pRegions->m_valid[b]==true)
{
pWall[a*m_pRegions->m_numRects+b]=UNKNOWN;
}
}
}
}
// key ideas. we need to (potentially) merge a large number of fragmented text regions and we need to avoid merging large photo regions
// we also want to keep memory usage within reason...so we should delete stuff after we know we will nolonger use it.
if (m_pScanHorizontalEdges!=NULL)
{
delete m_pScanHorizontalEdges;
m_pScanHorizontalEdges=NULL;
}
if (m_pScanVerticalEdges!=NULL)
{
delete m_pScanVerticalEdges;
m_pScanVerticalEdges=NULL;
}
for (border=0;border<MAXBORDER;border++) // loop through each possible border with consecutivelly
{
unionOperationInLastPass=true;
while (unionOperationInLastPass==true)
{
unionOperationInLastPass=false;
for (a=0;a<m_pRegions->m_numRects;a++)
{
if (m_pRegions->m_valid[a]==true)
{
for (b=a+1;b<m_pRegions->m_numRects;b++)
{
if (m_pRegions->m_valid[b]==true)
{
// we are paranoid... we repeatedly check if two regions intersect each other.
if (m_pRegions->CheckIntersect(a,b,0)==true)
{
m_pRegions->UnionRegions(a,b);
m_pRegions->checkIfValidRegion(a, 0); // in this context, checkvalid should have the effect of culling regions which are probably only stray dots
m_pRegions->RegionType(a); // figure out what type of region the combined region should be
for (i=a+1;i<m_pRegions->m_numRects;i++)
if (m_pRegions->m_valid[i]==TRUE)
pWall[a*m_pRegions->m_numRects+i]=UNKNOWN;
for (i=0;i<a;i++)
if (m_pRegions->m_valid[i]==TRUE)
pWall[i*m_pRegions->m_numRects+a]=UNKNOWN;
unionOperationInLastPass=true;
}
// now the complex part. check for intersections after growing borders around regions
// but only if we don't have two photo regions
if (MERGE_REGIONS)
{
if ((m_pRegions->m_type[a]&TEXT_REGION && m_pRegions->m_type[b]&TEXT_REGION)
||(((m_pRegions->m_type[a]|m_pRegions->m_type[b])&MERGABLE_WITH_PHOTOGRAPH)&&border<MERGABLE_WITH_PHOTOGRAPH)
||(border<MAX_MERGE_PHOTO_REGIONS && (m_pRegions->Size(a)<MAX_MERGABLE_PHOTOGRAPH_SIZE || m_pRegions->Size(b)<MAX_MERGABLE_PHOTOGRAPH_SIZE)))
{
if (m_pRegions->CheckIntersect(a,b,border)==true)
{
if (border<MERGABLE_WITHOUT_COLLISIONDETECTION)
{
m_pRegions->UnionRegions(a,b);
m_pRegions->checkIfValidRegion(a, border); // in this context, checkvalid should have the effect of culling regions which are probably only stray dots
m_pRegions->RegionType(a); // figure out what type of region the combined region should be
for (i=a+1;i<m_pRegions->m_numRects;i++)
if (m_pRegions->m_valid[i]==TRUE)
pWall[a*m_pRegions->m_numRects+i]=UNKNOWN;
for (i=0;i<a;i++)
if (m_pRegions->m_valid[i]==TRUE)
pWall[i*m_pRegions->m_numRects+a]=UNKNOWN;
unionOperationInLastPass=true;
}
else
{
if (pWall[a*m_pRegions->m_numRects+b]==UNKNOWN)
pWall[a*m_pRegions->m_numRects+b]=CollisionDetection(m_pRegions->m_pRects[a],m_pRegions->m_pRects[b],m_pScanWithShadows);
if (pWall[a*m_pRegions->m_numRects+b]==TRUE || (m_pRegions->m_type[a]&PHOTOGRAPH_REGION) || (m_pRegions->m_type[b]&PHOTOGRAPH_REGION))
{
if (!m_pRegions->MergerIntersectsPhoto(a,b))
{
m_pRegions->UnionRegions(a,b);
m_pRegions->checkIfValidRegion(a, border); // in this context, checkvalid should have the effect of culling regions which are probably only stray dots
m_pRegions->RegionType(a); // figure out what type of region the combined region should be
unionOperationInLastPass=true;
// region a has changed so reset collision flags
for (i=a+1;i<m_pRegions->m_numRects;i++)
if (m_pRegions->m_valid[i]==TRUE)
pWall[a*m_pRegions->m_numRects+i]=UNKNOWN;
for (i=0;i<a;i++)
if (m_pRegions->m_valid[i]==TRUE)
pWall[i*m_pRegions->m_numRects+a]=UNKNOWN;
if (border>=MERGABLE_WITHOUT_COLLISIONDETECTION) border=MERGABLE_WITHOUT_COLLISIONDETECTION-2;
}
}
}
}
}
}
}
}
}
}
}
}
// we have stricter requirements for region validity after we have done the border search.
for (a=0;a<m_pRegions->m_numRects;a++)
{
m_pRegions->checkIfValidRegion(a,DONE_WITH_BORDER_CHECKING);
}
// m_pScanBlurred->ColorChunks(pMap); // for debugging purposes... so we know where exactly chunks are
m_pRegions->CompactDown(m_pRegions->m_validRects+10); // we don't want to hand the user a list which includes invalidated regions
//
// free pWall
//
delete[] pWall;
}
}
delete[] pMap;
}
return(TRUE);
}
bool CRegionDetector::CollisionDetection(RECT r1, RECT r2, C32BitDibWrapper* pImage)
{
// use tracer lines to determine if there is an obstical between the two regions... be it possibly a photograph region or a speckle which we were wise enough to delete
// we should cache collision results, but we are lazy and compared with the time taken by all of the filters which edited every single bitmap pixel, time spent here is trivial
// first we need to determine how the regions are located with respect to each other
// we do three tracer rays (top edge to top edge, median to median, and bottom to bottom)
// throw out the edge with the highest collsion value... maybe we were unlucky and hit a stray speckle
//
// Diagram
// .____ 253
// ._
// . \*
// \ \ 43531 <-- hit speckle, throw out value
// \
// \ 215
// average intensity value: aprox 230 so its safe to merge the two regions
ULONG resistance[3];
// ULONG totalResistance;
ULONG maxResistance,minResistance,i;
if (r1.right < r2.left) // region 1 is to the left of region 2
{
resistance[0]=pImage->Line(r1.right,r1.top,r2.left,r2.top);
resistance[1]=pImage->Line(r1.right,r1.bottom,r2.left,r2.bottom);
resistance[2]=pImage->Line(r1.right,(r1.bottom+r1.top)/2,r2.left,(r2.bottom+r2.top)/2);
}
else
if (r2.right < r1.left) //region 2 is to the left of region 1
{
resistance[0]=pImage->Line(r2.right,r2.top,r1.left,r1.top);
resistance[1]=pImage->Line(r2.right,r2.bottom,r1.left,r1.bottom);
resistance[2]=pImage->Line(r2.right,(r2.bottom+r2.top)/2,r1.left,(r1.bottom+r1.top)/2);
}
else
if (r1.bottom < r2.top) // region 1 is above region 2
{
resistance[0]=pImage->Line(r1.right,r1.bottom,r2.right,r2.top);
resistance[1]=pImage->Line(r1.left,r1.bottom,r2.left,r2.top);
resistance[2]=pImage->Line((r1.left+r1.right)/2,r1.bottom,(r2.left+r2.right)/2,r2.top);
}
else
if (r2.bottom < r1.top) // region 2 is above region 1
{
resistance[0]=pImage->Line(r2.right,r2.bottom,r1.right,r1.top);
resistance[1]=pImage->Line(r2.left,r2.bottom,r1.left,r1.top);
resistance[2]=pImage->Line((r2.left+r2.right)/2,r2.bottom,(r1.left+r1.right)/2,r1.top);
}
// we used to have a more complex scheme where we took the average of the lower two values
// hence some of the following code is legacy code from that experiment
maxResistance=0;
minResistance=MAX_RESISTANCE_ALLOWED_TO_UNION+1;
for (i=0;i<3;i++)
{
if (resistance[i]>maxResistance) maxResistance=resistance[i];
if (resistance[i]<minResistance) minResistance=resistance[i];
}
//totalResistance=resistance[0]+resistance[1]+resistance[2]-maxResistance;
if (minResistance>MAX_RESISTANCE_ALLOWED_TO_UNION)
{
return(false);
}
else
{
return(true);
}
}
// CRegionList member functions:
CRegionList::CRegionList(int num)
{
int i;
m_numRects=0;
m_maxRects=num;
m_nBitmapWidth=0;
m_nBitmapHeight=0;
m_pRects = new RECT[num];
m_pixelsFilled = new ULONG[num];
m_valid= new bool[num];
m_type = new int[num];
m_totalColored= new ULONG[num];
m_totalIntensity= new ULONG[num];
m_totalEdge= new ULONG[num];
m_backgroundColorPixels = new int[num];
//
// Make sure all of the memory allocations succeeded
//
if (m_pRects && m_pixelsFilled && m_valid && m_type && m_totalColored && m_totalIntensity && m_totalEdge && m_backgroundColorPixels)
{
for (i=0;i<num;i++)
{
m_pixelsFilled[i]=0;
m_totalColored[i]=0;
m_totalIntensity[i]=0;
m_totalEdge[i]=0;
m_valid[i]=true;
m_backgroundColorPixels[i] = -1;
m_type[i]=PHOTOGRAPH_REGION;
}
}
else
{
//
// If all of the memory allocations didn't succeed, free all allocated memory
//
delete[] m_pRects;
delete[] m_pixelsFilled;
delete[] m_valid;
delete[] m_type;
delete[] m_totalColored;
delete[] m_totalIntensity;
delete[] m_totalEdge;
delete[] m_backgroundColorPixels;
m_pRects = NULL;
m_pixelsFilled = NULL;
m_valid = NULL;
m_type = NULL;
m_totalColored = NULL;
m_totalIntensity = NULL;
m_totalEdge = NULL;
m_backgroundColorPixels = NULL;
}
}
int CRegionList::UnionIntersectingRegions()
{
bool unionOperationInLastPass;
int numUnionOperations;
int a,b;
numUnionOperations=0;
unionOperationInLastPass=true;
while (unionOperationInLastPass==true)
{
unionOperationInLastPass=false;
for (a=0;a<m_numRects;a++)
{
if (m_valid[a]==true)
for (b=a+1;b<m_numRects;b++)
if (m_valid[b]==true)
{
// we are paranoid... we repeatedly check if two regions intersect each other.
if (CheckIntersect(a,b,0)==true)
{
UnionRegions(a,b);
unionOperationInLastPass=true;
numUnionOperations++;
}
}
}
}
return(numUnionOperations);
}
RECT CRegionList::unionAll()
{
int i,j;
for (i=0;i<m_numRects;i++)
{
if (m_valid[i]==true)
{
for (j=i+1;j<m_numRects;j++)
{
if (m_valid[j]==true)
{
UnionRegions(i,j);
}
}
return(m_pRects[i]);
}
}
RECT invalidRect;
invalidRect.left=0;invalidRect.top=0;invalidRect.right=0;invalidRect.bottom=0;
return(invalidRect);
}
RECT CRegionList::nthRegion(int num)
{
int i;
int n;
for (i=0,n=0;i<m_maxRects;i++)
{
if (m_valid[i]==true)
{
if (num==n) return(m_pRects[i]);
n++;
}
}
RECT invalidRect;
invalidRect.left=0;invalidRect.top=0;invalidRect.right=0;invalidRect.bottom=0;
return(invalidRect);
}
int CRegionList::RegionType(int region)
{
if (ClassifyRegion(region)>TEXTPHOTO_THRESHOLD)
{
m_type[region]=PHOTOGRAPH_REGION; // regions which we are very confident with
}
else
{
m_type[region]=TEXT_REGION; /// regions we don't have a darn clue about
if (largeRegion(region)==true) m_type[region]=TEXT_REGION|MERGABLE_WITH_PHOTOGRAPH;
}
return(m_type[region]);
}
bool CRegionList::largeRegion(int region)
{
int width, height, size;
width=(m_pRects[region].right-m_pRects[region].left);
height=(m_pRects[region].bottom-m_pRects[region].top);
size=width*height;
if (size>LARGEREGION_THRESHOLD) return(true);
return(false);
}
double CRegionList::ClassifyRegion(int region) // determine if the region is a text or a graphics region
{ // higher numbers are photo regions
// low numbers are text regions
// this function is not been written for speed
// its been written so that it is still almost understandable
// concept: use a bunch of tests that are accurate about 75% of the time
// to get a test that is accurate 99.9% of the time
double edgeFactor; // shadows and stray smudges should have real low edge factors... but
// dots should have high edge factors
double intensityFactor; // if a region has a very high intensity factor, forget about worrying if it is valid or not
double colorFactor; // a region with a lot of color is unlikely to be a stray speckle
double aspectRatioFactor;
double width,height;
double size;
double textRegionStylePixelsFactor;
double classificationValue;
width=(double)(m_pRects[region].right-m_pRects[region].left)+.01; // avoid divide by zero
height=(double)(m_pRects[region].bottom-m_pRects[region].top)+.01; // avoid divide by zero
size=width*height;
if (width>height) aspectRatioFactor=height/width;
else aspectRatioFactor=width/height;
//if(m_pixelsFilled<MINREGIONPIXELS) sizeFactor=-100;
edgeFactor = (double)m_totalEdge[region]/(double)m_pixelsFilled[region]+.01; // avoid divide by zero
colorFactor= ((double)m_totalColored[region]/(double)m_pixelsFilled[region]);
colorFactor=(colorFactor+110)/2; // otherwise we kill all black and white photos
intensityFactor = (double)m_totalIntensity[region]/(double)m_pixelsFilled[region];
textRegionStylePixelsFactor=(double)m_backgroundColorPixels[region]/size*100;
if (textRegionStylePixelsFactor<2) textRegionStylePixelsFactor=2;
classificationValue=colorFactor/intensityFactor/edgeFactor*aspectRatioFactor/textRegionStylePixelsFactor/textRegionStylePixelsFactor*30000; // square text region factor because its the most accurate test we have so we don't want some other tests distorting its results
// get rid of annoying stray speckles which the computer thinks are photographs
/* if(classificationValue>=MIN_BORDERLINE_TEXTPHOTO && classificationValue <=MAX_BORDERLINE_TEXTPHOTO)
{
classificationValue*=size/REASONABLE_PHOTO_SIZE; // big images are usually pictures.. and big images which are text blocks should have had very low color vals
// add more tests here
// potentially add more time intensive tests such as count num colors
}*/
// classificationValue=textRegionStylePixelsFactor; // debug
return(classificationValue);
}
bool CRegionList::checkIfValidRegion(int region, int border) // syncs whether a region is valid or not
{
if (m_valid[region]==true) // ignore already invalidated regions
{
m_valid[region]=ValidRegion(region, border);
if (m_valid[region]==false) m_validRects--;
}
return(m_valid[region]);
}
bool CRegionList::ValidRegion(int region, int border) // determines if a region is likely a worthless speck of dust or shadow or if we should care about the region
{
double aspectRatioFactor;
double width,height;
double size;
int edgePenaltyFactor;
// check if the region crosses the EDGE_PENALTY_WIDTH outer pixels of the image
width=(double)(m_pRects[region].right-m_pRects[region].left)+.01; // just to be safe to avoid divide by zero
height=(double)(m_pRects[region].bottom-m_pRects[region].top)+.01; // just to be safe to avoid divide by zero
edgePenaltyFactor=1;
// disable penalty factor calculations
/* if( m_pRects[region].left<EDGE_PENALTY_WIDTH
|| m_pRects[region].top<EDGE_PENALTY_WIDTH
|| m_nBitmapHeight-m_pRects[region].bottom<EDGE_PENALTY_WIDTH
|| m_nBitmapWidth-m_pRects[region].right<EDGE_PENALTY_WIDTH)
{
if(border>MAX_MERGE_DIFFERENT_REGIONS) edgePenaltyFactor=EDGE_PENALTY_FACTOR;
else edgePenaltyFactor=CLOSE_TO_EDGE_PENALTY_FACTOR;
}
else
if( m_pRects[region].left<CLOSE_TO_EDGE_PENALTY_WIDTH
|| m_pRects[region].top<CLOSE_TO_EDGE_PENALTY_WIDTH
|| m_nBitmapHeight-m_pRects[region].bottom<CLOSE_TO_EDGE_PENALTY_WIDTH
|| m_nBitmapWidth-m_pRects[region].right<CLOSE_TO_EDGE_PENALTY_WIDTH)
{
edgePenaltyFactor=CLOSE_TO_EDGE_PENALTY_FACTOR;
}*/
if (border<MAX_NO_EDGE_PIXEL_REGION_PENALTY) edgePenaltyFactor=1;
if (border>MAX_MERGE_DIFFERENT_REGIONS) edgePenaltyFactor=edgePenaltyFactor*2;
// if(border>BORDER_EXTREME_EDGE_PIXEL_REGION_PENALTY) edgePenaltyFactor=edgePenaltyFactor*2;
size=width*height; // the problem child text regions are small ones... so we use the size of the image as a factor
if (width>height) aspectRatioFactor=height/width;
else aspectRatioFactor=width/height;
// its too small
if ((int)m_pixelsFilled[region]<MINREGIONPIXELS*edgePenaltyFactor) return(false);
if (size<MINSIZE*edgePenaltyFactor) return(false);
if (border == DONE_WITH_BORDER_CHECKING)
{
if (size<MIN_FINAL_REGION_SIZE*edgePenaltyFactor) return(false);
}
// its too narrow
if (width<MINWIDTH*edgePenaltyFactor || height<MINWIDTH*edgePenaltyFactor) return(false);
if ((1/aspectRatioFactor)*edgePenaltyFactor>MAXREGIONRATIO && (width*edgePenaltyFactor<IGNORE_RATIO_WIDTH || height<IGNORE_RATIO_WIDTH)) return(false);
return(true);
}
bool CRegionList::InsideRegion(int region, int x, int y, int border) // border is the amount of border space to place around the outside of the region
{
if (x>=(m_pRects[region].left-border)
&& x<=(m_pRects[region].right+border)
&& y>=(m_pRects[region].top-border)
&& y<=(m_pRects[region].bottom+border))
return(true);
return(false);
}
void CRegionList::AddPixel(int region, ULONG pixel,ULONG edge, int x, int y)
{
if (m_pixelsFilled[region]!=0)
{
if (x<m_pRects[region].left) m_pRects[region].left=x;
if (x>m_pRects[region].right) m_pRects[region].right=x;
if (y<m_pRects[region].top) m_pRects[region].top=y;
if (y>m_pRects[region].bottom) m_pRects[region].bottom=y;
}
else // init region
{
m_pixelsFilled[region]=0;
m_totalColored[region]=0;
m_totalIntensity[region]=0;
m_pRects[region].left=x;
m_pRects[region].right=x;
m_pRects[region].top=y;
m_pRects[region].bottom=y;
m_numRects++;
m_validRects++;
}
m_pixelsFilled[region]++;
m_totalColored[region]+=DifferenceFromGray(pixel);
m_totalIntensity[region]+=Intensity(pixel);
m_totalEdge[region]+=Intensity(edge);
}
// unions two regions together... region b is invalidated
bool CRegionList::UnionRegions(int a, int b)
{
if (m_valid[a]!=true || m_valid[b]!=true) return(false); // the user tried to union an invalidated region
m_valid[b]=false;
m_pRects[a].left=MIN(m_pRects[a].left,m_pRects[b].left);
m_pRects[a].top=MIN(m_pRects[a].top,m_pRects[b].top);
m_pRects[a].right=MAX(m_pRects[a].right,m_pRects[b].right);
m_pRects[a].bottom=MAX(m_pRects[a].bottom,m_pRects[b].bottom);
m_pixelsFilled[a]+=m_pixelsFilled[b];
m_totalColored[a]+=m_totalColored[b];
m_totalIntensity[a]+=m_totalIntensity[b];
m_totalEdge[a]+=m_totalEdge[b];
m_backgroundColorPixels[a]+=m_backgroundColorPixels[b];
m_validRects--;
return(true);
}
RECT CRegionList::UnionRects(RECT a, RECT b)
{
RECT result;
result.left=MIN(a.left,b.left);
result.top=MIN(a.top,b.top);
result.right=MAX(a.right,b.right);
result.bottom=MAX(a.bottom,b.bottom);
return(result);
}
bool CRegionList::MergerIntersectsPhoto(int a, int b) // if we merge these two regions, will we also be merging with a photo region (a taboo)
{
RECT mergedRect;
int i;
mergedRect=UnionRects(m_pRects[a],m_pRects[b]);
for (i=0;i<m_numRects;i++)
if (m_valid[i]==true && (m_type[i]&PHOTOGRAPH_REGION) && a!=i && b!=i)
{
if (CheckIntersect(mergedRect,m_pRects[i])) return(true);
}
return(false);
}
// see InsideRegion for an explaination of what border is
bool CRegionList::CheckIntersect(int a, int b, int border) // do regions a and b intersect?
{
return(CheckIntersect(m_pRects[a],m_pRects[b],border));
}
bool CRegionList::CheckIntersect(RECT r1, RECT r2, int border) // do regions a and b intersect?
{
RECT intersect;
// grow r1 by border
// note: it shouldn't make any difference which rectangle we choose to grow
r1.left-=border;
r1.right+=border;
r1.top-=border;
r1.bottom+=border;
intersect = Intersect(r1,r2);
if (intersect.left<intersect.right && intersect.bottom>intersect.top)
return(true);
else
return(false);
/* // old buggy code for checking if two regions intersected
if(InsideRegion(r1,r2.left,r2.top,border) || // check if any of the four corner pixels are inside the other region
InsideRegion(r1,r2.left,r2.bottom,border) ||
InsideRegion(r1,r2.right,r2.top,border) || // b inside a
InsideRegion(r1,r2.right,r2.bottom,border)||
InsideRegion(r2,r1.left,r1.top,border) || // a inside b
InsideRegion(r2,r1.left,r1.bottom,border) ||
InsideRegion(r2,r1.right,r1.top,border) ||
InsideRegion(r2,r1.right,r1.bottom,border)
)
return true;
else
return false;*/
}
RECT CRegionList::Intersect(RECT r1, RECT r2)
{
RECT intersect;
intersect.left=MAX(r1.left,r2.left);
intersect.right=MIN(r1.right,r2.right);
intersect.top=MAX(r1.top,r2.top);
intersect.bottom=MIN(r1.bottom,r2.bottom);
if (intersect.left<=intersect.right && intersect.top<=intersect.bottom)
return(intersect);
else
{
intersect.left=-1;
intersect.right=-1;
intersect.top=-1;
intersect.bottom=-1;
return(intersect);
}
}
bool CRegionList::InsideRegion(RECT region, int x, int y, int border) // border is the amount of border space to place around the outside of the region
{
if (x>=(region.left-border)
&& x<=(region.right+border)
&& y>=(region.top-border)
&& y<=(region.bottom+border))
return(true);
return(false);
}
// compact down ignores all other info aside from rect location
// leads to faster access
void CRegionList::CompactDown(int size)
{
int i;
int j = 0;
RECT * compactedRects = new RECT[size];
bool * compactedValid = new bool[size];
int * compactedType = new int[size];
int * compactedBackgroundColorPixels = new int[size];
ULONG * compactedPixelsFilled = new ULONG[size]; // how many of the pixels in the region were actually selected?
ULONG * compactedTotalColored = new ULONG[size]; // accumulated color difference indicator
ULONG * compactedTotalIntensity = new ULONG[size]; // accumulated intensity indicator
ULONG * compactedTotalEdge = new ULONG[size]; // accumulated edge values
//
// Make sure all of the memory allocations succeeded
//
if (compactedRects && compactedValid && compactedType && compactedBackgroundColorPixels && compactedPixelsFilled && compactedTotalColored && compactedTotalIntensity && compactedTotalEdge)
{
for (i=0;i<m_numRects;i++)
{
if (m_valid[i])
{
compactedRects[j]=m_pRects[i];
compactedValid[j]=true;
compactedType[j]=m_type[i];
compactedPixelsFilled[j]=m_pixelsFilled[i];
compactedTotalColored[j]=m_totalColored[i];
compactedTotalIntensity[j]=m_totalIntensity[i];
compactedTotalEdge[j]=m_totalEdge[i];
compactedBackgroundColorPixels[j]=m_backgroundColorPixels[i];
j++;
}
}
// fill out the rest of the list
for (i=m_validRects;i<size;i++)
{
compactedValid[i]=false;
}
delete m_pRects;
delete m_valid;
delete m_type;
delete m_pixelsFilled;
delete m_totalColored;
delete m_totalIntensity;
delete m_totalEdge;
delete m_backgroundColorPixels;
m_pRects=compactedRects;
m_valid=compactedValid;
m_type=compactedType;
m_pixelsFilled=compactedPixelsFilled;
m_totalColored=compactedTotalColored;
m_totalIntensity=compactedTotalIntensity;
m_totalEdge=compactedTotalEdge;
m_backgroundColorPixels=compactedBackgroundColorPixels;
m_numRects=size;
}
else
{
//
// Otherwise, just release all of the memory we allocated
//
delete[] compactedRects;
delete[] compactedValid;
delete[] compactedType;
delete[] compactedBackgroundColorPixels;
delete[] compactedPixelsFilled;
delete[] compactedTotalColored;
delete[] compactedTotalIntensity;
delete[] compactedTotalEdge;
}
// we could delete all of the other region list info right here
}
// dibs are stored upside down from normal screen coords
// so apps will often want to flip the bitmap first
void CRegionList::FlipVertically()
{
int i;
int temp;
for (i=0;i<m_numRects;i++)
{
temp=m_nBitmapHeight-m_pRects[i].top-1;
m_pRects[i].top=m_nBitmapHeight-m_pRects[i].bottom-1;
m_pRects[i].bottom=temp;
}
}