Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1257 lines
54 KiB

  1. // RegionDetector.cpp: implementation of the CRegionDetector class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "precomp.h"
  5. #pragma hdrstop
  6. #include "regionde.h"
  7. inline ULONG Intensity(ULONG value);
  8. inline ULONG DifferenceFromGray(ULONG value);
  9. inline UCHAR Difference(UCHAR a, UCHAR b);
  10. inline ULONG Difference(ULONG a, ULONG b);
  11. int inline MAX(int a, int b);
  12. int inline MIN(int a, int b);
  13. // helper functions
  14. // sum of RGB vals
  15. inline ULONG Intensity(ULONG value)
  16. {
  17. return(value&0xff)+((value&0xff00)>>8)+((value&0xff0000)>>16);
  18. }
  19. // shadows are gray... if you aint gray... you aint a shadow
  20. inline ULONG DifferenceFromGray(ULONG value)
  21. {
  22. UCHAR g,b;//,b;
  23. // r=(UCHAR)(value& 0x0000ff);
  24. g=(UCHAR)((value& 0x00ff00)>>8);
  25. b=(UCHAR)((value& 0xff0000)>>16);
  26. // use this instead of the complete formula (uncomment the commented out code for the complete formula)
  27. // allow yellow scanner backgrounds
  28. return(ULONG)(Difference(b,g));//+Difference(r,g)+Difference(g,b));
  29. }
  30. // we should make a Difference Template to clean up this code
  31. inline UCHAR Difference(UCHAR a, UCHAR b)
  32. {
  33. if (a>b) return(a-b);
  34. else return(b-a);
  35. }
  36. inline ULONG Difference(ULONG a, ULONG b)
  37. {
  38. if (a>b) return(a-b);
  39. else return(b-a);
  40. }
  41. inline LONG Difference(LONG a, LONG b)
  42. {
  43. if (a>b) return(a-b);
  44. else return(b-a);
  45. }
  46. int inline MAX(int a, int b)
  47. {
  48. if (a>b) return(a);
  49. return(b);
  50. }
  51. int inline MIN(int a, int b)
  52. {
  53. if (a<b) return(a);
  54. return(b);
  55. }
  56. // if we have resampled the image, we may want to convert back to the origional coordinate system
  57. bool CRegionDetector::ConvertToOrigionalCoordinates()
  58. {
  59. if (m_pRegions!=NULL)
  60. {
  61. int i;
  62. for (i=0;i<m_pRegions->m_numRects;i++)
  63. {
  64. m_pRegions->m_pRects[i].left=m_pRegions->m_pRects[i].left*m_resampleFactor+m_resampleFactor/2;
  65. m_pRegions->m_pRects[i].right=m_pRegions->m_pRects[i].right*m_resampleFactor+m_resampleFactor/2;
  66. m_pRegions->m_pRects[i].top=m_pRegions->m_pRects[i].top*m_resampleFactor+m_resampleFactor/2;
  67. m_pRegions->m_pRects[i].bottom=m_pRegions->m_pRects[i].bottom*m_resampleFactor+m_resampleFactor/2;
  68. }
  69. return(true);
  70. }
  71. return(false);
  72. }
  73. // simplified Region detection code for single region detection
  74. // Faster and about as accurate
  75. // FindSingleRegion encapsulates a subset of FindRegions. at the moment, for code documentation of FindSingleRegion, see FindRegions
  76. bool CRegionDetector::FindSingleRegion()
  77. {
  78. // cast the pointers
  79. // S = Scan, B = blank background, V = virtual screen... new image
  80. int x,y;
  81. int a,b; // loop vars used for merging regions
  82. int border; // for aggregation on a rectangle by rectangle basis
  83. ULONG position;
  84. int numChunks;
  85. bool unionOperationInLastPass;
  86. ULONG* pImagePixels;
  87. ULONG* pEdgePixels;
  88. int requiredPixels;
  89. // if the bitmap is too large we can use halfsize to resample it down to a more reasonable size...
  90. // on a fast proccessor, current performance tests show that for most preview images this won't be needed
  91. // but if the user scans an image at 300 dpi, this will come in handy.
  92. // m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
  93. // for FingSingleRegion we plan on doing one resample before image proccessing
  94. // with an expected image size of 200x300 pixels which greatly reduces the proccessor load
  95. m_resampleFactor=1;
  96. while (m_pScan->m_nBitmapWidth>GOALX || m_pScan->m_nBitmapHeight>GOALY)
  97. {
  98. m_pScan->HalfSize();
  99. m_resampleFactor*=2;
  100. }
  101. m_pScan->Invert(); // filters operate on inverted images
  102. m_pScan->CorrectBrightness();
  103. requiredPixels=m_pScan->m_nBitmapWidth*m_pScan->m_nBitmapHeight/256/10;
  104. if (requiredPixels==0) requiredPixels=1; // special case for a particularly small preview scan
  105. m_pScan->MaxContrast(requiredPixels); // spread the images color spectrum out... concept: balance color spectra between different scans
  106. m_pScanBlurred->CreateBlurBitmap(m_pScan); // sets scanBlurred to be equal to a blurred version of m_pScan
  107. m_pScanDoubleBlurred->CreateBlurBitmap(m_pScanBlurred);
  108. m_pScanTripleBlurred->CreateBlurBitmap(m_pScanDoubleBlurred); // sets scanBlurred to be equal to a blurred version of m_pScan
  109. m_pScanHorizontalBlurred->CreateHorizontalBlurBitmap(m_pScan);
  110. m_pScanVerticalBlurred->CreateVerticalBlurBitmap(m_pScan);
  111. m_pScanEdges->CreateDifferenceBitmap(m_pScan,m_pScanBlurred); // think about how you create an edge bitmap and you will understand
  112. m_pScanDoubleEdges->CreateDifferenceBitmap(m_pScanBlurred,m_pScanDoubleBlurred); // we will get a huge accuracy boost from this simple step
  113. m_pScanTripleEdges->CreateDifferenceBitmap(m_pScanDoubleBlurred,m_pScanTripleBlurred); // we will get a huge accuracy boost from this simple step
  114. m_pScanHorizontalEdges->CreateDifferenceBitmap(m_pScan,m_pScanHorizontalBlurred); // assuming the user was kind enough to place the image right side up
  115. m_pScanVerticalEdges->CreateDifferenceBitmap(m_pScan,m_pScanVerticalBlurred); // we will get a huge accuracy boost from this simple step
  116. // free memory as soon as it isn't needed
  117. if (m_pScanVerticalBlurred!=NULL)
  118. {
  119. delete m_pScanVerticalBlurred;
  120. m_pScanVerticalBlurred=NULL;
  121. }
  122. if (m_pScanHorizontalBlurred!=NULL)
  123. {
  124. delete m_pScanHorizontalBlurred;
  125. m_pScanHorizontalBlurred=NULL;
  126. }
  127. if (m_pScanDoubleBlurred!=NULL)
  128. {
  129. delete m_pScanDoubleBlurred;
  130. m_pScanDoubleBlurred=NULL;
  131. }
  132. if (m_pScanTripleBlurred!=NULL)
  133. {
  134. delete m_pScanTripleBlurred;
  135. m_pScanTripleBlurred=NULL;
  136. }
  137. // these 5 calls to killShadows make up the real meat of the region detection work done by the program.
  138. // KillShadows now performs more tasks than simply killing shadows. Killshadows also enhances edges
  139. // and removes background colors
  140. // the doubleBlur and tripleBlur edge maps are needed so that we can distinguish between an uneven background scanner color and a real image.
  141. // see KillShadows for more documentation.
  142. // m_pScanBlurred is the bitmap we will use to determine where regions are
  143. // these calls to KillShadows act to enhance pixels which are part of regions and inhibit pixels which are not part of regions
  144. m_pScanBlurred->KillShadows(m_pScanVerticalEdges, MAXSHADOWSTART,MAXSHADOWPIXEL+1,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  145. m_pScanBlurred->KillShadows(m_pScanHorizontalEdges, MAXSHADOWSTART,MAXSHADOWPIXEL+1,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  146. m_pScanBlurred->KillShadows(m_pScanEdges, MAXSHADOWSTART,MAXSHADOWPIXEL-1,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,true);
  147. m_pScanBlurred->KillShadows(m_pScanDoubleEdges,MAXSHADOWSTART,MAXSHADOWPIXEL+2,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,true);
  148. m_pScanBlurred->KillShadows(m_pScanTripleEdges, MAXSHADOWSTART,MAXSHADOWPIXEL+1,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,true);
  149. // RemoveBlackBorder removes questionable pixels around the outside edge of an image.
  150. // RemoveBlackBorder has only very limited utility for region detection on images that were not aquired from a scanner
  151. m_pScan->RemoveBlackBorder(MIN_BLACK_SCANNER_EDGE_CHAN_VALUE,m_pScanBlurred,m_pScan);
  152. // despeckle removes small clumps of pixels which are probably stray static
  153. m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
  154. m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
  155. // pMap will hold information on which region each pixel on the screen is part of
  156. int *pMap=new int[m_pScanBlurred->m_nBitmapHeight*m_pScanBlurred->m_nBitmapWidth];
  157. if (pMap)
  158. {
  159. numChunks=m_pScanBlurred->FindChunks(pMap); // maps chunks on m_pScanBlurred to pMap
  160. if (m_pRegions!=NULL) delete m_pRegions;
  161. m_pRegions = new CRegionList(numChunks); // create a CRegionList to map the chunks onto.
  162. if (m_pRegions)
  163. {
  164. m_pRegions->m_nBitmapWidth=m_pScan->m_nBitmapWidth;
  165. m_pRegions->m_nBitmapHeight=m_pScan->m_nBitmapHeight;
  166. // now turn region map into region rectangles
  167. // it could be argued that this routine should be placed in C32BitDibWrapper
  168. // but we don't want to make C32BitDibWrapper encompas too much functionality which
  169. // would only be useful for imagedetection
  170. pImagePixels=(ULONG *)(m_pScanBlurred->m_pBits); // we want to use 32 bit chunks instead of 8 bit chunks
  171. pEdgePixels=(ULONG *)(m_pScanEdges->m_pBits);
  172. // add all the bitmap pixels to the m_pRegions list
  173. position=0;
  174. for (y=0;y<m_pScan->m_nBitmapHeight;y++)
  175. {
  176. for (x=0;x<m_pScan->m_nBitmapWidth;x++)
  177. {
  178. if (pMap[position]>0)
  179. {
  180. m_pRegions->AddPixel(pMap[position]-1, pImagePixels[position],pEdgePixels[position], x, y); // pMap values start at 1, let region values start at 0
  181. } // we start pMap at 1 so that 0 can indicate a pixel that is not assigned to any region
  182. position++; // we may want to make pMap start at 0 at some later date
  183. }
  184. }
  185. m_pRegions->m_numRects=numChunks;
  186. m_pRegions->m_validRects=numChunks;
  187. // free bitmaps as soon as they will no longer be used
  188. if (m_pScanHorizontalEdges!=NULL)
  189. {
  190. delete m_pScanHorizontalEdges;
  191. m_pScanHorizontalEdges=NULL;
  192. }
  193. if (m_pScanVerticalEdges!=NULL)
  194. {
  195. delete m_pScanVerticalEdges;
  196. m_pScanVerticalEdges=NULL;
  197. }
  198. // merge together regions
  199. // this routine is pretty much a waste when performing single region detection
  200. // the only advantage of it over simply calling unionRegions is that
  201. // we can merge together small close together regions, but kill small regions that are far from other regions (probably static)
  202. for (border=0;border<MAXBORDER;border+=SINGLE_REGION_BORDER_INCREMENT) // when detecting a single region detection,
  203. {
  204. // we don't need to inch along one border pixel width increment at a time
  205. // as we know we want to compact down to a single region in the end
  206. unionOperationInLastPass=true;
  207. for (a=0;a<m_pRegions->m_numRects;a++) // overkill, we could be cleverer about when we check for valid regions
  208. {
  209. m_pRegions->checkIfValidRegion(a, border); // sets m_valid params
  210. }
  211. m_pRegions->CompactDown(m_pRegions->m_validRects); // remove all invalid rects to save search time
  212. while (unionOperationInLastPass==true)
  213. {
  214. unionOperationInLastPass=false;
  215. for (a=0;a<m_pRegions->m_numRects;a++)
  216. {
  217. if (m_pRegions->m_valid[a]==true)
  218. {
  219. for (b=a+1;b<m_pRegions->m_numRects;b++)
  220. {
  221. if (m_pRegions->m_valid[b]==true)
  222. {
  223. if (m_pRegions->CheckIntersect(a,b,border)==true)
  224. {
  225. m_pRegions->UnionRegions(a,b);
  226. m_pRegions->checkIfValidRegion(a, border); // in this context, checkvalid should have the effect of culling regions which are probably only stray dots
  227. unionOperationInLastPass=true;
  228. }
  229. }
  230. }
  231. }
  232. }
  233. }
  234. }
  235. // m_pScanBlurred->ColorChunks(pMap); // for debugging purposes... so we know where exactly chunks are
  236. m_pRegions->CompactDown(m_pRegions->m_validRects); // remove all invalid rects to save search time
  237. }
  238. delete[] pMap;
  239. }
  240. return(TRUE);
  241. }
  242. // detect regions
  243. // makes heavy use of C32BitWrapper helper functions
  244. // WARNING: this function has not been updated to include the latest changes
  245. // to compensate for poor image quality
  246. //
  247. int CRegionDetector::FindRegions()
  248. {
  249. // cast the pointers
  250. // S = Scan, B = blank background, V = virtual screen... new image
  251. int x,y;
  252. int a,b;
  253. int i;
  254. bool done, weird;
  255. char* pWall; // 2d array keeping track of which regions have walls between them and other regions
  256. // wall vals... TRUE, FALSE, UNKNOWN
  257. int border; // for aggregation on a rectangle by rectangle basis
  258. ULONG position;
  259. int numChunks;
  260. bool unionOperationInLastPass;
  261. ULONG* pImagePixels;
  262. ULONG* pEdgePixels;
  263. int requiredPixels;
  264. // if the bitmap is too large we can use halfsize to resample it down to a more reasonable size...
  265. // on a fast proccessor, current performance tests show that for most preview images this won't be needed
  266. // but if the user scans an image at 300 dpi, this will come in handy.
  267. while (m_pScan->m_nBitmapWidth>GOALX || m_pScan->m_nBitmapHeight>GOALY)
  268. {
  269. m_pScan->HalfSize();
  270. }
  271. m_pScanBlurred->CreateBlurBitmap(m_pScan); // sets scanBlurred to be equal to a blurred version of m_pScan
  272. m_pScanDoubleBlurred->CreateBlurBitmap(m_pScanBlurred); // sets scanBlurred to be equal to a blurred version of m_pScan
  273. m_pScanTripleBlurred->CreateBlurBitmap(m_pScanDoubleBlurred); // sets scanBlurred to be equal to a blurred version of m_pScan
  274. m_pScanHorizontalBlurred->CreateHorizontalBlurBitmap(m_pScan);
  275. m_pScanVerticalBlurred->CreateVerticalBlurBitmap(m_pScan);
  276. m_pScanDoubleHorizontalBlurred->CreateHorizontalBlurBitmap(m_pScanHorizontalBlurred);
  277. m_pScanDoubleVerticalBlurred->CreateVerticalBlurBitmap(m_pScanVerticalBlurred);
  278. m_pScanEdges->CreateDifferenceBitmap(m_pScan,m_pScanBlurred); // think about how you create an edge bitmap and you will understand
  279. m_pScanDoubleEdges->CreateDifferenceBitmap(m_pScanBlurred,m_pScanDoubleBlurred); // we will get a huge accuracy boost from this simple step
  280. m_pScanTripleEdges->CreateDifferenceBitmap(m_pScanDoubleBlurred,m_pScanTripleBlurred); // we will get a huge accuracy boost from this simple step
  281. m_pScanHorizontalEdges->CreateDifferenceBitmap(m_pScan,m_pScanHorizontalBlurred); // assuming the user was kind enough to place the image right side up
  282. m_pScanVerticalEdges->CreateDifferenceBitmap(m_pScan,m_pScanVerticalBlurred); // we will get a huge accuracy boost from this simple step
  283. m_pScanDoubleHorizontalEdges->CreateDifferenceBitmap(m_pScanHorizontalBlurred,m_pScanDoubleHorizontalBlurred); // assuming the user was kind enough to place the image right side up
  284. m_pScanDoubleVerticalEdges->CreateDifferenceBitmap(m_pScanVerticalBlurred,m_pScanDoubleVerticalBlurred); // we will get a huge accuracy boost from this simple step
  285. m_pScanBlurred->Invert(); // filters operate on inverted images
  286. requiredPixels=m_pScanBlurred->m_nBitmapWidth*m_pScanBlurred->m_nBitmapHeight/256/10;
  287. if (requiredPixels==0) requiredPixels=1; // special case for a particularly small preview scan
  288. m_pScanBlurred->CorrectBrightness();
  289. m_pScanBlurred->MaxContrast(requiredPixels);
  290. // free memory as soon as it isn't needed
  291. if (m_pScanVerticalBlurred!=NULL)
  292. {
  293. delete m_pScanVerticalBlurred;
  294. m_pScanVerticalBlurred=NULL;
  295. }
  296. if (m_pScanHorizontalBlurred!=NULL)
  297. {
  298. delete m_pScanHorizontalBlurred;
  299. m_pScanHorizontalBlurred=NULL;
  300. }
  301. if (m_pScanDoubleBlurred!=NULL)
  302. {
  303. delete m_pScanDoubleBlurred;
  304. m_pScanDoubleBlurred=NULL;
  305. }
  306. if (m_pScanTripleBlurred!=NULL)
  307. {
  308. delete m_pScanTripleBlurred;
  309. m_pScanTripleBlurred=NULL;
  310. }
  311. m_pScanWithShadows = new C32BitDibWrapper(m_pScanBlurred); // copy scanBlurred
  312. // eliminate shadows
  313. m_pScanBlurred->KillShadows(m_pScanTripleEdges, MAXSHADOWSTART,MAXSHADOWPIXEL-2,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  314. m_pScanBlurred->KillShadows(m_pScanDoubleEdges,MAXSHADOWSTART,MAXSHADOWPIXEL-1,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  315. m_pScanBlurred->KillShadows(m_pScanEdges, MAXSHADOWSTART,MAXSHADOWPIXEL+1,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  316. m_pScanBlurred->KillShadows(m_pScanHorizontalEdges, MAXSHADOWSTART,MAXSHADOWPIXEL,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  317. m_pScanBlurred->KillShadows(m_pScanVerticalEdges, MAXSHADOWSTART,MAXSHADOWPIXEL,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  318. // compensate for background color
  319. // the average background color pixel will probably have a smaller edge factor than the average shadow,
  320. // but will have a larger difference from grey
  321. // hence to eliminate background color we ignore difference from grey... as accomplished by the (256*3) term
  322. m_pScanBlurred->KillShadows(m_pScanTripleEdges, 256*3,MAXSHADOWPIXEL-2,256*3,NOT_SHADOW_INTENSITY,false);
  323. m_pScanBlurred->KillShadows(m_pScanDoubleEdges,256*3,MAXSHADOWPIXEL-2,256*3,NOT_SHADOW_INTENSITY,false);
  324. m_pScanBlurred->KillShadows(m_pScanEdges, 256*3,MAXSHADOWPIXEL-2,256*3,NOT_SHADOW_INTENSITY,false);
  325. m_pScanBlurred->KillShadows(m_pScanHorizontalEdges, 256*3,MAXSHADOWPIXEL-2,256*3,NOT_SHADOW_INTENSITY,false);
  326. m_pScanBlurred->KillShadows(m_pScanVerticalEdges, 256*3,MAXSHADOWPIXEL-2,256*3,NOT_SHADOW_INTENSITY,false);
  327. // pure economics... edge pixels are much more likely to be junk
  328. // so economically, its worth it to risk
  329. // killing good edge pixels to get rid of the bad
  330. m_pScanBlurred->EdgeDespeckle();
  331. m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
  332. m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
  333. // prepare to find chunks
  334. int *pMap=new int[m_pScanBlurred->m_nBitmapHeight*m_pScanBlurred->m_nBitmapWidth];
  335. if (pMap)
  336. {
  337. done=false;
  338. weird=false;
  339. 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
  340. {
  341. done=true;
  342. numChunks=m_pScanBlurred->FindChunks(pMap); // fills the pMap array with chunks
  343. if (m_pRegions!=NULL) delete m_pRegions;
  344. m_pRegions = new CRegionList(numChunks);
  345. if (m_pRegions)
  346. {
  347. m_pRegions->m_nBitmapWidth=m_pScan->m_nBitmapWidth;
  348. m_pRegions->m_nBitmapHeight=m_pScan->m_nBitmapHeight;
  349. // now turn the pMap region map into region rectangles
  350. // it could be argued that this routine should be placed in C32BitDibWrapper
  351. // but we don't want to make C32BitDibWrapper encompas too much functionality which
  352. // clearly is directly connected with imagedetection
  353. pImagePixels=(ULONG *)(m_pScanBlurred->m_pBits); // we want to use 32 bit chunks instead of 8 bit chunks
  354. pEdgePixels=(ULONG *)(m_pScanEdges->m_pBits);
  355. position=0;
  356. for (y=0;y<m_pScan->m_nBitmapHeight;y++)
  357. {
  358. for (x=0;x<m_pScan->m_nBitmapWidth;x++)
  359. {
  360. if (pMap[position]>0)
  361. {
  362. m_pRegions->AddPixel(pMap[position]-1, pImagePixels[position],pEdgePixels[position], x, y); // pMap values start at 1, let region values start at 0
  363. } // we start pMap at 1 so that 0 can indicate a pixel that is not assigned to any region
  364. position++; // we may want to make pMap start at 0 at some later date
  365. }
  366. }
  367. m_pRegions->m_numRects=numChunks;
  368. m_pRegions->m_validRects=numChunks;
  369. // check for invalid regions
  370. for (a=0;a<m_pRegions->m_numRects;a++)
  371. {
  372. m_pRegions->checkIfValidRegion(a); // sets m_valid params
  373. m_pRegions->m_backgroundColorPixels[a]=m_pScan->PixelsBelowThreshold(m_pScanBlurred,m_pScanEdges,m_pRegions->m_pRects[a]);
  374. m_pRegions->RegionType(a); // clasify regions... text or photo
  375. }
  376. m_pRegions->CompactDown(m_pRegions->m_validRects);
  377. if (m_pRegions->m_validRects==0) break; // if there aren't any regions, no sense in proceeding
  378. unionOperationInLastPass=true;
  379. while (unionOperationInLastPass==true)
  380. {
  381. unionOperationInLastPass=false;
  382. for (a=0;a<m_pRegions->m_numRects;a++)
  383. {
  384. if (m_pRegions->m_valid[a]==true)
  385. {
  386. for (b=a+1;b<m_pRegions->m_numRects;b++)
  387. {
  388. if (m_pRegions->m_valid[b]==true)
  389. {
  390. // we are paranoid... we repeatedly check if two regions intersect each other.
  391. if (m_pRegions->CheckIntersect(a,b,0)==true)
  392. {
  393. m_pRegions->UnionRegions(a,b);
  394. m_pRegions->checkIfValidRegion(a, 0); // in this context, checkvalid should have the effect of culling regions which are probably only stray dots
  395. m_pRegions->RegionType(a); // figure out what type of region the combined region should be
  396. unionOperationInLastPass=true;
  397. }
  398. }
  399. }
  400. }
  401. }
  402. }
  403. if (weird==false &&
  404. ((m_pRegions->m_pRects[0].right-m_pRegions->m_pRects[0].left)
  405. *(m_pRegions->m_pRects[0].bottom-m_pRegions->m_pRects[0].top))
  406. >
  407. ((m_pScanBlurred->m_nBitmapWidth-DESPECKLE_BORDER_WIDTH*2)*(m_pScanBlurred->m_nBitmapWidth-DESPECKLE_BORDER_WIDTH*2)))
  408. {
  409. weird=true;
  410. done=false;
  411. // you better not be grey or you are in trouble
  412. // some seriously nasty shadow elimination
  413. // we will probably eliminate too many good pixels
  414. // but at least the poor user will get something more than just finding that the whole screen was selected
  415. m_pScanBlurred->KillShadows(m_pScanTripleEdges, 256,MAXSHADOWPIXEL+4,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  416. m_pScanBlurred->KillShadows(m_pScanDoubleEdges,256,MAXSHADOWPIXEL+4,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  417. m_pScanBlurred->KillShadows(m_pScanEdges, 256,MAXSHADOWPIXEL+5,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  418. m_pScanBlurred->KillShadows(m_pScanHorizontalEdges, 256,MAXSHADOWPIXEL+4,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  419. m_pScanBlurred->KillShadows(m_pScanVerticalEdges, 256,MAXSHADOWPIXEL+4,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  420. m_pScanBlurred->KillShadows(m_pScanDoubleHorizontalEdges, MAXSHADOWSTART,MAXSHADOWPIXEL+4,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  421. m_pScanBlurred->KillShadows(m_pScanDoubleVerticalEdges, MAXSHADOWSTART,MAXSHADOWPIXEL+4,MAX_DIFFERENCE_FROM_GRAY,NOT_SHADOW_INTENSITY,false);
  422. m_pScanBlurred->EdgeDespeckle();
  423. m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
  424. // m_pScanBlurred->EdgeDespeckle();
  425. m_pScanBlurred->Despeckle(); // two despeckles has a greater effect than one
  426. }
  427. }
  428. }
  429. if (m_pRegions)
  430. {
  431. m_pRegions->CompactDown(m_pRegions->m_validRects); // compact down the CRegionList so that it nolonger includes invalidated regions
  432. // we store an array indicating which pairs of regions have walls between them
  433. // used for unioning together large text regions... for example, scanning
  434. // in two pages with a shadow between them
  435. pWall=new char[m_pRegions->m_numRects*m_pRegions->m_numRects];
  436. if (pWall)
  437. {
  438. for (a=0;a<m_pRegions->m_numRects;a++)
  439. {
  440. if (m_pRegions->m_valid[a]==true)
  441. {
  442. for (b=a+1;b<m_pRegions->m_numRects;b++)
  443. {
  444. if (m_pRegions->m_valid[b]==true)
  445. {
  446. pWall[a*m_pRegions->m_numRects+b]=UNKNOWN;
  447. }
  448. }
  449. }
  450. }
  451. // key ideas. we need to (potentially) merge a large number of fragmented text regions and we need to avoid merging large photo regions
  452. // we also want to keep memory usage within reason...so we should delete stuff after we know we will nolonger use it.
  453. if (m_pScanHorizontalEdges!=NULL)
  454. {
  455. delete m_pScanHorizontalEdges;
  456. m_pScanHorizontalEdges=NULL;
  457. }
  458. if (m_pScanVerticalEdges!=NULL)
  459. {
  460. delete m_pScanVerticalEdges;
  461. m_pScanVerticalEdges=NULL;
  462. }
  463. for (border=0;border<MAXBORDER;border++) // loop through each possible border with consecutivelly
  464. {
  465. unionOperationInLastPass=true;
  466. while (unionOperationInLastPass==true)
  467. {
  468. unionOperationInLastPass=false;
  469. for (a=0;a<m_pRegions->m_numRects;a++)
  470. {
  471. if (m_pRegions->m_valid[a]==true)
  472. {
  473. for (b=a+1;b<m_pRegions->m_numRects;b++)
  474. {
  475. if (m_pRegions->m_valid[b]==true)
  476. {
  477. // we are paranoid... we repeatedly check if two regions intersect each other.
  478. if (m_pRegions->CheckIntersect(a,b,0)==true)
  479. {
  480. m_pRegions->UnionRegions(a,b);
  481. m_pRegions->checkIfValidRegion(a, 0); // in this context, checkvalid should have the effect of culling regions which are probably only stray dots
  482. m_pRegions->RegionType(a); // figure out what type of region the combined region should be
  483. for (i=a+1;i<m_pRegions->m_numRects;i++)
  484. if (m_pRegions->m_valid[i]==TRUE)
  485. pWall[a*m_pRegions->m_numRects+i]=UNKNOWN;
  486. for (i=0;i<a;i++)
  487. if (m_pRegions->m_valid[i]==TRUE)
  488. pWall[i*m_pRegions->m_numRects+a]=UNKNOWN;
  489. unionOperationInLastPass=true;
  490. }
  491. // now the complex part. check for intersections after growing borders around regions
  492. // but only if we don't have two photo regions
  493. if (MERGE_REGIONS)
  494. {
  495. if ((m_pRegions->m_type[a]&TEXT_REGION && m_pRegions->m_type[b]&TEXT_REGION)
  496. ||(((m_pRegions->m_type[a]|m_pRegions->m_type[b])&MERGABLE_WITH_PHOTOGRAPH)&&border<MERGABLE_WITH_PHOTOGRAPH)
  497. ||(border<MAX_MERGE_PHOTO_REGIONS && (m_pRegions->Size(a)<MAX_MERGABLE_PHOTOGRAPH_SIZE || m_pRegions->Size(b)<MAX_MERGABLE_PHOTOGRAPH_SIZE)))
  498. {
  499. if (m_pRegions->CheckIntersect(a,b,border)==true)
  500. {
  501. if (border<MERGABLE_WITHOUT_COLLISIONDETECTION)
  502. {
  503. m_pRegions->UnionRegions(a,b);
  504. m_pRegions->checkIfValidRegion(a, border); // in this context, checkvalid should have the effect of culling regions which are probably only stray dots
  505. m_pRegions->RegionType(a); // figure out what type of region the combined region should be
  506. for (i=a+1;i<m_pRegions->m_numRects;i++)
  507. if (m_pRegions->m_valid[i]==TRUE)
  508. pWall[a*m_pRegions->m_numRects+i]=UNKNOWN;
  509. for (i=0;i<a;i++)
  510. if (m_pRegions->m_valid[i]==TRUE)
  511. pWall[i*m_pRegions->m_numRects+a]=UNKNOWN;
  512. unionOperationInLastPass=true;
  513. }
  514. else
  515. {
  516. if (pWall[a*m_pRegions->m_numRects+b]==UNKNOWN)
  517. pWall[a*m_pRegions->m_numRects+b]=CollisionDetection(m_pRegions->m_pRects[a],m_pRegions->m_pRects[b],m_pScanWithShadows);
  518. if (pWall[a*m_pRegions->m_numRects+b]==TRUE || (m_pRegions->m_type[a]&PHOTOGRAPH_REGION) || (m_pRegions->m_type[b]&PHOTOGRAPH_REGION))
  519. {
  520. if (!m_pRegions->MergerIntersectsPhoto(a,b))
  521. {
  522. m_pRegions->UnionRegions(a,b);
  523. m_pRegions->checkIfValidRegion(a, border); // in this context, checkvalid should have the effect of culling regions which are probably only stray dots
  524. m_pRegions->RegionType(a); // figure out what type of region the combined region should be
  525. unionOperationInLastPass=true;
  526. // region a has changed so reset collision flags
  527. for (i=a+1;i<m_pRegions->m_numRects;i++)
  528. if (m_pRegions->m_valid[i]==TRUE)
  529. pWall[a*m_pRegions->m_numRects+i]=UNKNOWN;
  530. for (i=0;i<a;i++)
  531. if (m_pRegions->m_valid[i]==TRUE)
  532. pWall[i*m_pRegions->m_numRects+a]=UNKNOWN;
  533. if (border>=MERGABLE_WITHOUT_COLLISIONDETECTION) border=MERGABLE_WITHOUT_COLLISIONDETECTION-2;
  534. }
  535. }
  536. }
  537. }
  538. }
  539. }
  540. }
  541. }
  542. }
  543. }
  544. }
  545. }
  546. // we have stricter requirements for region validity after we have done the border search.
  547. for (a=0;a<m_pRegions->m_numRects;a++)
  548. {
  549. m_pRegions->checkIfValidRegion(a,DONE_WITH_BORDER_CHECKING);
  550. }
  551. // m_pScanBlurred->ColorChunks(pMap); // for debugging purposes... so we know where exactly chunks are
  552. m_pRegions->CompactDown(m_pRegions->m_validRects+10); // we don't want to hand the user a list which includes invalidated regions
  553. //
  554. // free pWall
  555. //
  556. delete[] pWall;
  557. }
  558. }
  559. delete[] pMap;
  560. }
  561. return(TRUE);
  562. }
  563. bool CRegionDetector::CollisionDetection(RECT r1, RECT r2, C32BitDibWrapper* pImage)
  564. {
  565. // 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
  566. // 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
  567. // first we need to determine how the regions are located with respect to each other
  568. // we do three tracer rays (top edge to top edge, median to median, and bottom to bottom)
  569. // throw out the edge with the highest collsion value... maybe we were unlucky and hit a stray speckle
  570. //
  571. // Diagram
  572. // .____ 253
  573. // ._
  574. // . \*
  575. // \ \ 43531 <-- hit speckle, throw out value
  576. // \
  577. // \ 215
  578. // average intensity value: aprox 230 so its safe to merge the two regions
  579. ULONG resistance[3];
  580. // ULONG totalResistance;
  581. ULONG maxResistance,minResistance,i;
  582. if (r1.right < r2.left) // region 1 is to the left of region 2
  583. {
  584. resistance[0]=pImage->Line(r1.right,r1.top,r2.left,r2.top);
  585. resistance[1]=pImage->Line(r1.right,r1.bottom,r2.left,r2.bottom);
  586. resistance[2]=pImage->Line(r1.right,(r1.bottom+r1.top)/2,r2.left,(r2.bottom+r2.top)/2);
  587. }
  588. else
  589. if (r2.right < r1.left) //region 2 is to the left of region 1
  590. {
  591. resistance[0]=pImage->Line(r2.right,r2.top,r1.left,r1.top);
  592. resistance[1]=pImage->Line(r2.right,r2.bottom,r1.left,r1.bottom);
  593. resistance[2]=pImage->Line(r2.right,(r2.bottom+r2.top)/2,r1.left,(r1.bottom+r1.top)/2);
  594. }
  595. else
  596. if (r1.bottom < r2.top) // region 1 is above region 2
  597. {
  598. resistance[0]=pImage->Line(r1.right,r1.bottom,r2.right,r2.top);
  599. resistance[1]=pImage->Line(r1.left,r1.bottom,r2.left,r2.top);
  600. resistance[2]=pImage->Line((r1.left+r1.right)/2,r1.bottom,(r2.left+r2.right)/2,r2.top);
  601. }
  602. else
  603. if (r2.bottom < r1.top) // region 2 is above region 1
  604. {
  605. resistance[0]=pImage->Line(r2.right,r2.bottom,r1.right,r1.top);
  606. resistance[1]=pImage->Line(r2.left,r2.bottom,r1.left,r1.top);
  607. resistance[2]=pImage->Line((r2.left+r2.right)/2,r2.bottom,(r1.left+r1.right)/2,r1.top);
  608. }
  609. // we used to have a more complex scheme where we took the average of the lower two values
  610. // hence some of the following code is legacy code from that experiment
  611. maxResistance=0;
  612. minResistance=MAX_RESISTANCE_ALLOWED_TO_UNION+1;
  613. for (i=0;i<3;i++)
  614. {
  615. if (resistance[i]>maxResistance) maxResistance=resistance[i];
  616. if (resistance[i]<minResistance) minResistance=resistance[i];
  617. }
  618. //totalResistance=resistance[0]+resistance[1]+resistance[2]-maxResistance;
  619. if (minResistance>MAX_RESISTANCE_ALLOWED_TO_UNION)
  620. {
  621. return(false);
  622. }
  623. else
  624. {
  625. return(true);
  626. }
  627. }
  628. // CRegionList member functions:
  629. CRegionList::CRegionList(int num)
  630. {
  631. int i;
  632. m_numRects=0;
  633. m_maxRects=num;
  634. m_nBitmapWidth=0;
  635. m_nBitmapHeight=0;
  636. m_pRects = new RECT[num];
  637. m_pixelsFilled = new ULONG[num];
  638. m_valid= new bool[num];
  639. m_type = new int[num];
  640. m_totalColored= new ULONG[num];
  641. m_totalIntensity= new ULONG[num];
  642. m_totalEdge= new ULONG[num];
  643. m_backgroundColorPixels = new int[num];
  644. //
  645. // Make sure all of the memory allocations succeeded
  646. //
  647. if (m_pRects && m_pixelsFilled && m_valid && m_type && m_totalColored && m_totalIntensity && m_totalEdge && m_backgroundColorPixels)
  648. {
  649. for (i=0;i<num;i++)
  650. {
  651. m_pixelsFilled[i]=0;
  652. m_totalColored[i]=0;
  653. m_totalIntensity[i]=0;
  654. m_totalEdge[i]=0;
  655. m_valid[i]=true;
  656. m_backgroundColorPixels[i] = -1;
  657. m_type[i]=PHOTOGRAPH_REGION;
  658. }
  659. }
  660. else
  661. {
  662. //
  663. // If all of the memory allocations didn't succeed, free all allocated memory
  664. //
  665. delete[] m_pRects;
  666. delete[] m_pixelsFilled;
  667. delete[] m_valid;
  668. delete[] m_type;
  669. delete[] m_totalColored;
  670. delete[] m_totalIntensity;
  671. delete[] m_totalEdge;
  672. delete[] m_backgroundColorPixels;
  673. m_pRects = NULL;
  674. m_pixelsFilled = NULL;
  675. m_valid = NULL;
  676. m_type = NULL;
  677. m_totalColored = NULL;
  678. m_totalIntensity = NULL;
  679. m_totalEdge = NULL;
  680. m_backgroundColorPixels = NULL;
  681. }
  682. }
  683. int CRegionList::UnionIntersectingRegions()
  684. {
  685. bool unionOperationInLastPass;
  686. int numUnionOperations;
  687. int a,b;
  688. numUnionOperations=0;
  689. unionOperationInLastPass=true;
  690. while (unionOperationInLastPass==true)
  691. {
  692. unionOperationInLastPass=false;
  693. for (a=0;a<m_numRects;a++)
  694. {
  695. if (m_valid[a]==true)
  696. for (b=a+1;b<m_numRects;b++)
  697. if (m_valid[b]==true)
  698. {
  699. // we are paranoid... we repeatedly check if two regions intersect each other.
  700. if (CheckIntersect(a,b,0)==true)
  701. {
  702. UnionRegions(a,b);
  703. unionOperationInLastPass=true;
  704. numUnionOperations++;
  705. }
  706. }
  707. }
  708. }
  709. return(numUnionOperations);
  710. }
  711. RECT CRegionList::unionAll()
  712. {
  713. int i,j;
  714. for (i=0;i<m_numRects;i++)
  715. {
  716. if (m_valid[i]==true)
  717. {
  718. for (j=i+1;j<m_numRects;j++)
  719. {
  720. if (m_valid[j]==true)
  721. {
  722. UnionRegions(i,j);
  723. }
  724. }
  725. return(m_pRects[i]);
  726. }
  727. }
  728. RECT invalidRect;
  729. invalidRect.left=0;invalidRect.top=0;invalidRect.right=0;invalidRect.bottom=0;
  730. return(invalidRect);
  731. }
  732. RECT CRegionList::nthRegion(int num)
  733. {
  734. int i;
  735. int n;
  736. for (i=0,n=0;i<m_maxRects;i++)
  737. {
  738. if (m_valid[i]==true)
  739. {
  740. if (num==n) return(m_pRects[i]);
  741. n++;
  742. }
  743. }
  744. RECT invalidRect;
  745. invalidRect.left=0;invalidRect.top=0;invalidRect.right=0;invalidRect.bottom=0;
  746. return(invalidRect);
  747. }
  748. int CRegionList::RegionType(int region)
  749. {
  750. if (ClassifyRegion(region)>TEXTPHOTO_THRESHOLD)
  751. {
  752. m_type[region]=PHOTOGRAPH_REGION; // regions which we are very confident with
  753. }
  754. else
  755. {
  756. m_type[region]=TEXT_REGION; /// regions we don't have a darn clue about
  757. if (largeRegion(region)==true) m_type[region]=TEXT_REGION|MERGABLE_WITH_PHOTOGRAPH;
  758. }
  759. return(m_type[region]);
  760. }
  761. bool CRegionList::largeRegion(int region)
  762. {
  763. int width, height, size;
  764. width=(m_pRects[region].right-m_pRects[region].left);
  765. height=(m_pRects[region].bottom-m_pRects[region].top);
  766. size=width*height;
  767. if (size>LARGEREGION_THRESHOLD) return(true);
  768. return(false);
  769. }
  770. double CRegionList::ClassifyRegion(int region) // determine if the region is a text or a graphics region
  771. { // higher numbers are photo regions
  772. // low numbers are text regions
  773. // this function is not been written for speed
  774. // its been written so that it is still almost understandable
  775. // concept: use a bunch of tests that are accurate about 75% of the time
  776. // to get a test that is accurate 99.9% of the time
  777. double edgeFactor; // shadows and stray smudges should have real low edge factors... but
  778. // dots should have high edge factors
  779. double intensityFactor; // if a region has a very high intensity factor, forget about worrying if it is valid or not
  780. double colorFactor; // a region with a lot of color is unlikely to be a stray speckle
  781. double aspectRatioFactor;
  782. double width,height;
  783. double size;
  784. double textRegionStylePixelsFactor;
  785. double classificationValue;
  786. width=(double)(m_pRects[region].right-m_pRects[region].left)+.01; // avoid divide by zero
  787. height=(double)(m_pRects[region].bottom-m_pRects[region].top)+.01; // avoid divide by zero
  788. size=width*height;
  789. if (width>height) aspectRatioFactor=height/width;
  790. else aspectRatioFactor=width/height;
  791. //if(m_pixelsFilled<MINREGIONPIXELS) sizeFactor=-100;
  792. edgeFactor = (double)m_totalEdge[region]/(double)m_pixelsFilled[region]+.01; // avoid divide by zero
  793. colorFactor= ((double)m_totalColored[region]/(double)m_pixelsFilled[region]);
  794. colorFactor=(colorFactor+110)/2; // otherwise we kill all black and white photos
  795. intensityFactor = (double)m_totalIntensity[region]/(double)m_pixelsFilled[region];
  796. textRegionStylePixelsFactor=(double)m_backgroundColorPixels[region]/size*100;
  797. if (textRegionStylePixelsFactor<2) textRegionStylePixelsFactor=2;
  798. 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
  799. // get rid of annoying stray speckles which the computer thinks are photographs
  800. /* if(classificationValue>=MIN_BORDERLINE_TEXTPHOTO && classificationValue <=MAX_BORDERLINE_TEXTPHOTO)
  801. {
  802. classificationValue*=size/REASONABLE_PHOTO_SIZE; // big images are usually pictures.. and big images which are text blocks should have had very low color vals
  803. // add more tests here
  804. // potentially add more time intensive tests such as count num colors
  805. }*/
  806. // classificationValue=textRegionStylePixelsFactor; // debug
  807. return(classificationValue);
  808. }
  809. bool CRegionList::checkIfValidRegion(int region, int border) // syncs whether a region is valid or not
  810. {
  811. if (m_valid[region]==true) // ignore already invalidated regions
  812. {
  813. m_valid[region]=ValidRegion(region, border);
  814. if (m_valid[region]==false) m_validRects--;
  815. }
  816. return(m_valid[region]);
  817. }
  818. 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
  819. {
  820. double aspectRatioFactor;
  821. double width,height;
  822. double size;
  823. int edgePenaltyFactor;
  824. // check if the region crosses the EDGE_PENALTY_WIDTH outer pixels of the image
  825. width=(double)(m_pRects[region].right-m_pRects[region].left)+.01; // just to be safe to avoid divide by zero
  826. height=(double)(m_pRects[region].bottom-m_pRects[region].top)+.01; // just to be safe to avoid divide by zero
  827. edgePenaltyFactor=1;
  828. // disable penalty factor calculations
  829. /* if( m_pRects[region].left<EDGE_PENALTY_WIDTH
  830. || m_pRects[region].top<EDGE_PENALTY_WIDTH
  831. || m_nBitmapHeight-m_pRects[region].bottom<EDGE_PENALTY_WIDTH
  832. || m_nBitmapWidth-m_pRects[region].right<EDGE_PENALTY_WIDTH)
  833. {
  834. if(border>MAX_MERGE_DIFFERENT_REGIONS) edgePenaltyFactor=EDGE_PENALTY_FACTOR;
  835. else edgePenaltyFactor=CLOSE_TO_EDGE_PENALTY_FACTOR;
  836. }
  837. else
  838. if( m_pRects[region].left<CLOSE_TO_EDGE_PENALTY_WIDTH
  839. || m_pRects[region].top<CLOSE_TO_EDGE_PENALTY_WIDTH
  840. || m_nBitmapHeight-m_pRects[region].bottom<CLOSE_TO_EDGE_PENALTY_WIDTH
  841. || m_nBitmapWidth-m_pRects[region].right<CLOSE_TO_EDGE_PENALTY_WIDTH)
  842. {
  843. edgePenaltyFactor=CLOSE_TO_EDGE_PENALTY_FACTOR;
  844. }*/
  845. if (border<MAX_NO_EDGE_PIXEL_REGION_PENALTY) edgePenaltyFactor=1;
  846. if (border>MAX_MERGE_DIFFERENT_REGIONS) edgePenaltyFactor=edgePenaltyFactor*2;
  847. // if(border>BORDER_EXTREME_EDGE_PIXEL_REGION_PENALTY) edgePenaltyFactor=edgePenaltyFactor*2;
  848. size=width*height; // the problem child text regions are small ones... so we use the size of the image as a factor
  849. if (width>height) aspectRatioFactor=height/width;
  850. else aspectRatioFactor=width/height;
  851. // its too small
  852. if ((int)m_pixelsFilled[region]<MINREGIONPIXELS*edgePenaltyFactor) return(false);
  853. if (size<MINSIZE*edgePenaltyFactor) return(false);
  854. if (border == DONE_WITH_BORDER_CHECKING)
  855. {
  856. if (size<MIN_FINAL_REGION_SIZE*edgePenaltyFactor) return(false);
  857. }
  858. // its too narrow
  859. if (width<MINWIDTH*edgePenaltyFactor || height<MINWIDTH*edgePenaltyFactor) return(false);
  860. if ((1/aspectRatioFactor)*edgePenaltyFactor>MAXREGIONRATIO && (width*edgePenaltyFactor<IGNORE_RATIO_WIDTH || height<IGNORE_RATIO_WIDTH)) return(false);
  861. return(true);
  862. }
  863. 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
  864. {
  865. if (x>=(m_pRects[region].left-border)
  866. && x<=(m_pRects[region].right+border)
  867. && y>=(m_pRects[region].top-border)
  868. && y<=(m_pRects[region].bottom+border))
  869. return(true);
  870. return(false);
  871. }
  872. void CRegionList::AddPixel(int region, ULONG pixel,ULONG edge, int x, int y)
  873. {
  874. if (m_pixelsFilled[region]!=0)
  875. {
  876. if (x<m_pRects[region].left) m_pRects[region].left=x;
  877. if (x>m_pRects[region].right) m_pRects[region].right=x;
  878. if (y<m_pRects[region].top) m_pRects[region].top=y;
  879. if (y>m_pRects[region].bottom) m_pRects[region].bottom=y;
  880. }
  881. else // init region
  882. {
  883. m_pixelsFilled[region]=0;
  884. m_totalColored[region]=0;
  885. m_totalIntensity[region]=0;
  886. m_pRects[region].left=x;
  887. m_pRects[region].right=x;
  888. m_pRects[region].top=y;
  889. m_pRects[region].bottom=y;
  890. m_numRects++;
  891. m_validRects++;
  892. }
  893. m_pixelsFilled[region]++;
  894. m_totalColored[region]+=DifferenceFromGray(pixel);
  895. m_totalIntensity[region]+=Intensity(pixel);
  896. m_totalEdge[region]+=Intensity(edge);
  897. }
  898. // unions two regions together... region b is invalidated
  899. bool CRegionList::UnionRegions(int a, int b)
  900. {
  901. if (m_valid[a]!=true || m_valid[b]!=true) return(false); // the user tried to union an invalidated region
  902. m_valid[b]=false;
  903. m_pRects[a].left=MIN(m_pRects[a].left,m_pRects[b].left);
  904. m_pRects[a].top=MIN(m_pRects[a].top,m_pRects[b].top);
  905. m_pRects[a].right=MAX(m_pRects[a].right,m_pRects[b].right);
  906. m_pRects[a].bottom=MAX(m_pRects[a].bottom,m_pRects[b].bottom);
  907. m_pixelsFilled[a]+=m_pixelsFilled[b];
  908. m_totalColored[a]+=m_totalColored[b];
  909. m_totalIntensity[a]+=m_totalIntensity[b];
  910. m_totalEdge[a]+=m_totalEdge[b];
  911. m_backgroundColorPixels[a]+=m_backgroundColorPixels[b];
  912. m_validRects--;
  913. return(true);
  914. }
  915. RECT CRegionList::UnionRects(RECT a, RECT b)
  916. {
  917. RECT result;
  918. result.left=MIN(a.left,b.left);
  919. result.top=MIN(a.top,b.top);
  920. result.right=MAX(a.right,b.right);
  921. result.bottom=MAX(a.bottom,b.bottom);
  922. return(result);
  923. }
  924. bool CRegionList::MergerIntersectsPhoto(int a, int b) // if we merge these two regions, will we also be merging with a photo region (a taboo)
  925. {
  926. RECT mergedRect;
  927. int i;
  928. mergedRect=UnionRects(m_pRects[a],m_pRects[b]);
  929. for (i=0;i<m_numRects;i++)
  930. if (m_valid[i]==true && (m_type[i]&PHOTOGRAPH_REGION) && a!=i && b!=i)
  931. {
  932. if (CheckIntersect(mergedRect,m_pRects[i])) return(true);
  933. }
  934. return(false);
  935. }
  936. // see InsideRegion for an explaination of what border is
  937. bool CRegionList::CheckIntersect(int a, int b, int border) // do regions a and b intersect?
  938. {
  939. return(CheckIntersect(m_pRects[a],m_pRects[b],border));
  940. }
  941. bool CRegionList::CheckIntersect(RECT r1, RECT r2, int border) // do regions a and b intersect?
  942. {
  943. RECT intersect;
  944. // grow r1 by border
  945. // note: it shouldn't make any difference which rectangle we choose to grow
  946. r1.left-=border;
  947. r1.right+=border;
  948. r1.top-=border;
  949. r1.bottom+=border;
  950. intersect = Intersect(r1,r2);
  951. if (intersect.left<intersect.right && intersect.bottom>intersect.top)
  952. return(true);
  953. else
  954. return(false);
  955. /* // old buggy code for checking if two regions intersected
  956. if(InsideRegion(r1,r2.left,r2.top,border) || // check if any of the four corner pixels are inside the other region
  957. InsideRegion(r1,r2.left,r2.bottom,border) ||
  958. InsideRegion(r1,r2.right,r2.top,border) || // b inside a
  959. InsideRegion(r1,r2.right,r2.bottom,border)||
  960. InsideRegion(r2,r1.left,r1.top,border) || // a inside b
  961. InsideRegion(r2,r1.left,r1.bottom,border) ||
  962. InsideRegion(r2,r1.right,r1.top,border) ||
  963. InsideRegion(r2,r1.right,r1.bottom,border)
  964. )
  965. return true;
  966. else
  967. return false;*/
  968. }
  969. RECT CRegionList::Intersect(RECT r1, RECT r2)
  970. {
  971. RECT intersect;
  972. intersect.left=MAX(r1.left,r2.left);
  973. intersect.right=MIN(r1.right,r2.right);
  974. intersect.top=MAX(r1.top,r2.top);
  975. intersect.bottom=MIN(r1.bottom,r2.bottom);
  976. if (intersect.left<=intersect.right && intersect.top<=intersect.bottom)
  977. return(intersect);
  978. else
  979. {
  980. intersect.left=-1;
  981. intersect.right=-1;
  982. intersect.top=-1;
  983. intersect.bottom=-1;
  984. return(intersect);
  985. }
  986. }
  987. 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
  988. {
  989. if (x>=(region.left-border)
  990. && x<=(region.right+border)
  991. && y>=(region.top-border)
  992. && y<=(region.bottom+border))
  993. return(true);
  994. return(false);
  995. }
  996. // compact down ignores all other info aside from rect location
  997. // leads to faster access
  998. void CRegionList::CompactDown(int size)
  999. {
  1000. int i;
  1001. int j = 0;
  1002. RECT * compactedRects = new RECT[size];
  1003. bool * compactedValid = new bool[size];
  1004. int * compactedType = new int[size];
  1005. int * compactedBackgroundColorPixels = new int[size];
  1006. ULONG * compactedPixelsFilled = new ULONG[size]; // how many of the pixels in the region were actually selected?
  1007. ULONG * compactedTotalColored = new ULONG[size]; // accumulated color difference indicator
  1008. ULONG * compactedTotalIntensity = new ULONG[size]; // accumulated intensity indicator
  1009. ULONG * compactedTotalEdge = new ULONG[size]; // accumulated edge values
  1010. //
  1011. // Make sure all of the memory allocations succeeded
  1012. //
  1013. if (compactedRects && compactedValid && compactedType && compactedBackgroundColorPixels && compactedPixelsFilled && compactedTotalColored && compactedTotalIntensity && compactedTotalEdge)
  1014. {
  1015. for (i=0;i<m_numRects;i++)
  1016. {
  1017. if (m_valid[i])
  1018. {
  1019. compactedRects[j]=m_pRects[i];
  1020. compactedValid[j]=true;
  1021. compactedType[j]=m_type[i];
  1022. compactedPixelsFilled[j]=m_pixelsFilled[i];
  1023. compactedTotalColored[j]=m_totalColored[i];
  1024. compactedTotalIntensity[j]=m_totalIntensity[i];
  1025. compactedTotalEdge[j]=m_totalEdge[i];
  1026. compactedBackgroundColorPixels[j]=m_backgroundColorPixels[i];
  1027. j++;
  1028. }
  1029. }
  1030. // fill out the rest of the list
  1031. for (i=m_validRects;i<size;i++)
  1032. {
  1033. compactedValid[i]=false;
  1034. }
  1035. delete m_pRects;
  1036. delete m_valid;
  1037. delete m_type;
  1038. delete m_pixelsFilled;
  1039. delete m_totalColored;
  1040. delete m_totalIntensity;
  1041. delete m_totalEdge;
  1042. delete m_backgroundColorPixels;
  1043. m_pRects=compactedRects;
  1044. m_valid=compactedValid;
  1045. m_type=compactedType;
  1046. m_pixelsFilled=compactedPixelsFilled;
  1047. m_totalColored=compactedTotalColored;
  1048. m_totalIntensity=compactedTotalIntensity;
  1049. m_totalEdge=compactedTotalEdge;
  1050. m_backgroundColorPixels=compactedBackgroundColorPixels;
  1051. m_numRects=size;
  1052. }
  1053. else
  1054. {
  1055. //
  1056. // Otherwise, just release all of the memory we allocated
  1057. //
  1058. delete[] compactedRects;
  1059. delete[] compactedValid;
  1060. delete[] compactedType;
  1061. delete[] compactedBackgroundColorPixels;
  1062. delete[] compactedPixelsFilled;
  1063. delete[] compactedTotalColored;
  1064. delete[] compactedTotalIntensity;
  1065. delete[] compactedTotalEdge;
  1066. }
  1067. // we could delete all of the other region list info right here
  1068. }
  1069. // dibs are stored upside down from normal screen coords
  1070. // so apps will often want to flip the bitmap first
  1071. void CRegionList::FlipVertically()
  1072. {
  1073. int i;
  1074. int temp;
  1075. for (i=0;i<m_numRects;i++)
  1076. {
  1077. temp=m_nBitmapHeight-m_pRects[i].top-1;
  1078. m_pRects[i].top=m_nBitmapHeight-m_pRects[i].bottom-1;
  1079. m_pRects[i].bottom=temp;
  1080. }
  1081. }