6aca4d39 |
1 | // Created on: 2012-07-10 |
692613e5 |
2 | // Created by: VRO |
6aca4d39 |
3 | // Copyright (c) 2012-2014 OPEN CASCADE SAS |
692613e5 |
4 | // |
973c2be1 |
5 | // This file is part of Open CASCADE Technology software library. |
692613e5 |
6 | // |
d5f74e42 |
7 | // This library is free software; you can redistribute it and/or modify it under |
8 | // the terms of the GNU Lesser General Public License version 2.1 as published |
973c2be1 |
9 | // by the Free Software Foundation, with special exception defined in the file |
10 | // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT |
11 | // distribution for complete text of the license and disclaimer of any warranty. |
692613e5 |
12 | // |
973c2be1 |
13 | // Alternatively, this file may be used under the terms of Open CASCADE |
14 | // commercial license or contractual agreement. |
692613e5 |
15 | |
16 | #include <Image_Diff.hxx> |
17 | #include <Image_AlienPixMap.hxx> |
18 | |
19 | #include <TColStd_MapIteratorOfMapOfInteger.hxx> |
20 | |
21 | #include <cstdlib> |
22 | |
23 | IMPLEMENT_STANDARD_HANDLE (Image_Diff, Standard_Transient) |
24 | IMPLEMENT_STANDARD_RTTIEXT(Image_Diff, Standard_Transient) |
25 | |
ca0c0b11 |
26 | //! POD structure for packed RGB color value (3 bytes) |
27 | struct Image_ColorXXX24 |
28 | { |
29 | Standard_Byte v[3]; |
30 | typedef Standard_Byte ComponentType_t; //!< Component type |
31 | static Standard_Integer Length() { return 3; } //!< Returns the number of components |
32 | }; |
33 | |
34 | inline Image_ColorXXX24 operator- (const Image_ColorXXX24& theA, |
35 | const Image_ColorXXX24& theB) |
36 | { |
37 | return Image_ColorSub3 (theA, theB); |
38 | } |
39 | |
692613e5 |
40 | //! Dot squared for difference of two colors |
ca0c0b11 |
41 | inline Standard_Integer dotSquared (const Image_ColorXXX24& theColor) |
692613e5 |
42 | { |
43 | // explicitly convert to integer |
ca0c0b11 |
44 | const Standard_Integer r = theColor.v[0]; |
45 | const Standard_Integer g = theColor.v[1]; |
46 | const Standard_Integer b = theColor.v[2]; |
692613e5 |
47 | return r * r + g * g + b * b; |
48 | } |
49 | |
50 | //! @return true if pixel is black |
ca0c0b11 |
51 | inline bool isBlack (const Image_ColorXXX24& theColor) |
692613e5 |
52 | { |
ca0c0b11 |
53 | return theColor.v[0] == 0 |
54 | && theColor.v[1] == 0 |
55 | && theColor.v[2] == 0; |
692613e5 |
56 | } |
57 | |
58 | //! Converts a pixel position (row, column) to one integer value |
59 | inline Standard_Size pixel2Int (const Standard_Size aRow, |
60 | const Standard_Size aCol) |
61 | { |
62 | return aCol + (aRow << 15); |
63 | } |
64 | |
65 | //! Converts an integer value to pixel coordinates (row, column) |
66 | inline void int2Pixel (const Standard_Size theValue, |
67 | Standard_Size& theRow, |
68 | Standard_Size& theCol) |
69 | { |
70 | theRow = (theValue >> 15); |
71 | theCol = theValue - (theRow << 15); |
72 | } |
73 | |
74 | namespace |
75 | { |
185e6ec0 |
76 | |
77 | inline ptrdiff_t getAbs (const ptrdiff_t theValue) |
78 | { |
79 | return theValue >= 0 ? theValue : -theValue; |
80 | } |
81 | |
692613e5 |
82 | static const Standard_Size NEIGHBOR_PIXELS_NB = 8; |
f24125b9 |
83 | static struct |
692613e5 |
84 | { |
85 | Standard_Integer row_inc; |
86 | Standard_Integer col_inc; |
87 | |
88 | inline Standard_Size pixel2Int (const Standard_Size theRowCenter, |
89 | const Standard_Size theColCenter) const |
90 | { |
91 | return ::pixel2Int (theRowCenter + Standard_Size(row_inc), |
92 | theColCenter + Standard_Size(col_inc)); |
93 | } |
94 | |
ca0c0b11 |
95 | inline bool isBlack (const Image_PixMap& theData, |
692613e5 |
96 | const Standard_Size theRowCenter, |
97 | const Standard_Size theColCenter) const |
98 | { |
ca0c0b11 |
99 | return ::isBlack (theData.Value<Image_ColorXXX24> (theRowCenter + Standard_Size(row_inc), |
100 | theColCenter + Standard_Size(col_inc))); |
692613e5 |
101 | } |
185e6ec0 |
102 | |
ca0c0b11 |
103 | inline bool isValid (const Image_PixMap& theData, |
185e6ec0 |
104 | const Standard_Size theRowCenter, |
105 | const Standard_Size theColCenter) const |
106 | { |
107 | const Standard_Size aRow = theRowCenter + Standard_Size(row_inc); |
108 | const Standard_Size aCol = theColCenter + Standard_Size(col_inc); |
109 | return aRow < theData.SizeX() // this unsigned math checks Standard_Size(-1) at-once |
110 | && aCol < theData.SizeY(); |
111 | } |
692613e5 |
112 | } |
f24125b9 |
113 | const NEIGHBOR_PIXELS[NEIGHBOR_PIXELS_NB] = |
692613e5 |
114 | { |
115 | {-1, -1}, {-1, 0}, {-1, 1}, |
116 | { 0, -1}, { 0, 1}, |
117 | { 1, -1}, { 1, 0}, { 1, 1} |
118 | }; |
119 | |
120 | static bool isSupportedFormat (const Image_PixMap::ImgFormat theFormat) |
121 | { |
122 | return theFormat == Image_PixMap::ImgRGB |
123 | || theFormat == Image_PixMap::ImgBGR |
124 | || theFormat == Image_PixMap::ImgRGB32 |
125 | || theFormat == Image_PixMap::ImgBGR32 |
126 | || theFormat == Image_PixMap::ImgRGBA |
127 | || theFormat == Image_PixMap::ImgBGRA; |
128 | } |
129 | }; |
130 | |
131 | // ======================================================================= |
132 | // function : Image_Diff |
133 | // purpose : |
134 | // ======================================================================= |
135 | Image_Diff::Image_Diff() |
136 | : myColorTolerance (0.0), |
137 | myIsBorderFilterOn (Standard_False) |
138 | { |
139 | // |
140 | } |
141 | |
142 | // ======================================================================= |
143 | // function : ~Image_Diff |
144 | // purpose : |
145 | // ======================================================================= |
146 | Image_Diff::~Image_Diff() |
147 | { |
148 | releaseGroupsOfDiffPixels(); |
149 | } |
150 | |
151 | // ======================================================================= |
152 | // function : Init |
153 | // purpose : |
154 | // ======================================================================= |
155 | Standard_Boolean Image_Diff::Init (const Handle(Image_PixMap)& theImageRef, |
156 | const Handle(Image_PixMap)& theImageNew, |
157 | const Standard_Boolean theToBlackWhite) |
158 | { |
159 | myImageRef.Nullify(); |
160 | myImageNew.Nullify(); |
161 | myDiffPixels.Clear(); |
162 | releaseGroupsOfDiffPixels(); |
163 | if (theImageRef.IsNull() || theImageNew.IsNull() |
164 | || theImageRef->IsEmpty() || theImageNew->IsEmpty() |
165 | || theImageRef->SizeX() != theImageNew->SizeX() |
166 | || theImageRef->SizeY() != theImageNew->SizeY() |
167 | || theImageRef->Format() != theImageNew->Format()) |
168 | { |
63c629aa |
169 | #ifdef IMAGE_DEB |
692613e5 |
170 | std::cerr << "Images has different format or dimensions\n"; |
63c629aa |
171 | #endif |
692613e5 |
172 | return Standard_False; |
173 | } |
174 | else if (!isSupportedFormat (theImageRef->Format())) |
175 | { |
63c629aa |
176 | #ifdef IMAGE_DEB |
692613e5 |
177 | std::cerr << "Images has unsupported pixel format\n"; |
63c629aa |
178 | #endif |
692613e5 |
179 | return Standard_False; |
180 | } |
181 | else if (theImageRef->SizeX() >= 0xFFFF |
182 | || theImageRef->SizeY() >= 0xFFFF) |
183 | { |
63c629aa |
184 | #ifdef IMAGE_DEB |
692613e5 |
185 | std::cerr << "Image too large\n"; |
63c629aa |
186 | #endif |
692613e5 |
187 | return Standard_False; |
188 | } |
189 | |
190 | myImageRef = theImageRef; |
191 | myImageNew = theImageNew; |
192 | |
193 | if (theToBlackWhite) |
194 | { |
195 | // Convert the images to white/black |
ca0c0b11 |
196 | const Image_ColorXXX24 aWhite = {{255, 255, 255}}; |
197 | for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow) |
692613e5 |
198 | { |
ca0c0b11 |
199 | for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol) |
692613e5 |
200 | { |
ca0c0b11 |
201 | Image_ColorXXX24& aPixel1 = myImageRef->ChangeValue<Image_ColorXXX24> (aRow, aCol); |
692613e5 |
202 | if (!isBlack (aPixel1)) |
203 | { |
204 | aPixel1 = aWhite; |
205 | } |
ca0c0b11 |
206 | Image_ColorXXX24& aPixel2 = myImageNew->ChangeValue<Image_ColorXXX24> (aRow, aCol); |
692613e5 |
207 | if (!isBlack (aPixel2)) |
208 | { |
209 | aPixel2 = aWhite; |
210 | } |
211 | } |
212 | } |
213 | } |
214 | |
215 | return Standard_True; |
216 | } |
217 | |
218 | |
219 | // ======================================================================= |
220 | // function : Init |
221 | // purpose : |
222 | // ======================================================================= |
223 | Standard_Boolean Image_Diff::Init (const TCollection_AsciiString& theImgPathRef, |
224 | const TCollection_AsciiString& theImgPathNew, |
225 | const Standard_Boolean theToBlackWhite) |
226 | { |
227 | Handle(Image_AlienPixMap) anImgRef = new Image_AlienPixMap(); |
228 | Handle(Image_AlienPixMap) anImgNew = new Image_AlienPixMap(); |
229 | if (!anImgRef->Load (theImgPathRef) |
230 | || !anImgNew->Load (theImgPathNew)) |
231 | { |
63c629aa |
232 | #ifdef IMAGE_DEB |
692613e5 |
233 | std::cerr << "Failed to load image(s) file(s)\n"; |
63c629aa |
234 | #endif |
692613e5 |
235 | return Standard_False; |
236 | } |
237 | return Init (anImgRef, anImgNew, theToBlackWhite); |
238 | } |
239 | |
240 | // ======================================================================= |
241 | // function : SetColorTolerance |
242 | // purpose : |
243 | // ======================================================================= |
244 | void Image_Diff::SetColorTolerance (const Standard_Real theTolerance) |
245 | { |
246 | myColorTolerance = theTolerance; |
247 | } |
248 | |
249 | // ======================================================================= |
250 | // function : ColorTolerance |
251 | // purpose : |
252 | // ======================================================================= |
253 | Standard_Real Image_Diff::ColorTolerance() const |
254 | { |
255 | return myColorTolerance; |
256 | } |
257 | |
258 | // ======================================================================= |
259 | // function : SetBorderFilterOn |
260 | // purpose : |
261 | // ======================================================================= |
262 | void Image_Diff::SetBorderFilterOn (const Standard_Boolean theToIgnore) |
263 | { |
264 | myIsBorderFilterOn = theToIgnore; |
265 | } |
266 | |
267 | // ======================================================================= |
268 | // function : IsBorderFilterOn |
269 | // purpose : |
270 | // ======================================================================= |
271 | Standard_Boolean Image_Diff::IsBorderFilterOn() const |
272 | { |
273 | return myIsBorderFilterOn; |
274 | } |
275 | |
276 | // ======================================================================= |
277 | // function : Compare |
278 | // purpose : |
279 | // ======================================================================= |
280 | Standard_Integer Image_Diff::Compare() |
281 | { |
282 | // Number of different pixels (by color) |
283 | Standard_Integer aNbDiffColors = 0; |
284 | myDiffPixels.Clear(); |
285 | |
286 | if (myImageRef.IsNull() || myImageNew.IsNull()) |
287 | { |
288 | return -1; |
289 | } |
290 | |
291 | // Tolerance of comparison operation for color |
292 | // Maximum difference between colors (white - black) = 100% |
ca0c0b11 |
293 | Image_ColorXXX24 aDiff = {{255, 255, 255}}; |
692613e5 |
294 | const Standard_Integer aMaxDiffColor = dotSquared (aDiff); |
295 | const Standard_Integer aDiffThreshold = Standard_Integer(Standard_Real(aMaxDiffColor) * myColorTolerance); |
296 | |
297 | // we don't care about RGB/BGR/RGBA/BGRA/RGB32/BGR32 differences |
298 | // because we just compute summ of r g b components |
692613e5 |
299 | |
300 | // compare colors of each pixel |
301 | for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow) |
302 | { |
303 | for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol) |
304 | { |
ca0c0b11 |
305 | aDiff = myImageNew->Value<Image_ColorXXX24> (aRow, aCol) - myImageRef->Value<Image_ColorXXX24> (aRow, aCol); |
692613e5 |
306 | if (dotSquared (aDiff) > aDiffThreshold) |
307 | { |
308 | const Standard_Size aValue = pixel2Int (aRow, aCol); |
309 | myDiffPixels.Append (aValue); |
310 | ++aNbDiffColors; |
311 | } |
312 | } |
313 | } |
314 | |
315 | // take into account a border effect |
316 | if (myIsBorderFilterOn && myDiffPixels.Length() > 0) |
317 | { |
318 | aNbDiffColors = ignoreBorderEffect(); |
319 | } |
320 | |
321 | return aNbDiffColors; |
322 | } |
323 | |
324 | // ======================================================================= |
325 | // function : SaveDiffImage |
326 | // purpose : |
327 | // ======================================================================= |
328 | Standard_Boolean Image_Diff::SaveDiffImage (Image_PixMap& theDiffImage) const |
329 | { |
330 | if (myImageRef.IsNull() || myImageNew.IsNull()) |
331 | { |
332 | return Standard_False; |
333 | } |
334 | |
335 | if (theDiffImage.IsEmpty() |
336 | || theDiffImage.SizeX() != myImageRef->SizeX() |
337 | || theDiffImage.SizeY() != myImageRef->SizeY() |
338 | || !isSupportedFormat (theDiffImage.Format())) |
339 | { |
340 | if (!theDiffImage.InitTrash (Image_PixMap::ImgRGB, myImageRef->SizeX(), myImageRef->SizeY())) |
341 | { |
342 | return Standard_False; |
343 | } |
344 | } |
345 | |
346 | Standard_Size aRow, aCol; |
ca0c0b11 |
347 | const Image_ColorXXX24 aWhite = {{255, 255, 255}}; |
692613e5 |
348 | |
349 | // initialize black image for dump |
350 | memset (theDiffImage.ChangeData(), 0, theDiffImage.SizeBytes()); |
351 | if (myGroupsOfDiffPixels.IsEmpty()) |
352 | { |
353 | if (myIsBorderFilterOn) |
354 | { |
355 | return Standard_True; |
356 | } |
357 | |
358 | for (Standard_Integer aPixelId = 0; aPixelId < myDiffPixels.Length(); ++aPixelId) |
359 | { |
360 | const Standard_Size aValue = myDiffPixels.Value (aPixelId); |
361 | int2Pixel (aValue, aRow, aCol); |
ca0c0b11 |
362 | theDiffImage.ChangeValue<Image_ColorXXX24> (aRow, aCol) = aWhite; |
692613e5 |
363 | } |
364 | |
365 | return Standard_True; |
366 | } |
367 | |
368 | Standard_Integer aGroupId = 1; |
369 | for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next(), ++aGroupId) |
370 | { |
371 | if (myLinearGroups.Contains (aGroupId)) |
372 | { |
373 | continue; // skip linear groups |
374 | } |
375 | |
376 | const TColStd_MapOfInteger* aGroup = aGrIter.Value(); |
377 | for (TColStd_MapIteratorOfMapOfInteger aPixelIter(*aGroup); |
378 | aPixelIter.More(); aPixelIter.Next()) |
379 | { |
380 | int2Pixel (aPixelIter.Key(), aRow, aCol); |
ca0c0b11 |
381 | theDiffImage.ChangeValue<Image_ColorXXX24> (aRow, aCol) = aWhite; |
692613e5 |
382 | } |
383 | } |
384 | |
385 | return Standard_True; |
386 | } |
387 | |
388 | // ======================================================================= |
389 | // function : SaveDiffImage |
390 | // purpose : |
391 | // ======================================================================= |
392 | Standard_Boolean Image_Diff::SaveDiffImage (const TCollection_AsciiString& theDiffPath) const |
393 | { |
394 | if (myImageRef.IsNull() || myImageNew.IsNull() || theDiffPath.IsEmpty()) |
395 | { |
396 | return Standard_False; |
397 | } |
398 | |
399 | Image_AlienPixMap aDiff; |
400 | if (!aDiff.InitTrash (Image_PixMap::ImgRGB, myImageRef->SizeX(), myImageRef->SizeY()) |
401 | || !SaveDiffImage (aDiff)) |
402 | { |
403 | return Standard_False; |
404 | } |
405 | |
406 | // save image |
407 | return aDiff.Save (theDiffPath); |
408 | } |
409 | |
410 | // ======================================================================= |
411 | // function : ignoreBorderEffect |
412 | // purpose : |
413 | // ======================================================================= |
414 | Standard_Integer Image_Diff::ignoreBorderEffect() |
415 | { |
416 | if (myImageRef.IsNull() || myImageNew.IsNull()) |
417 | { |
418 | return 0; |
419 | } |
420 | |
692613e5 |
421 | // allocate groups of different pixels |
422 | releaseGroupsOfDiffPixels(); |
423 | |
424 | // Find a different area (a set of close to each other pixels which colors differ in both images). |
425 | // It filters alone pixels with different color. |
1d47d8d0 |
426 | Standard_Size aRow1 = 0, aCol1 = 0, aRow2, aCol2; |
692613e5 |
427 | Standard_Integer aLen1 = (myDiffPixels.Length() > 0) ? (myDiffPixels.Length() - 1) : 0; |
428 | for (Standard_Integer aPixelId1 = 0; aPixelId1 < aLen1; ++aPixelId1) |
429 | { |
430 | const Standard_Size aValue1 = myDiffPixels.Value (aPixelId1); |
431 | int2Pixel (aValue1, aRow1, aCol1); |
432 | |
433 | // Check other pixels in the list looking for a neighbour of this one |
434 | for (Standard_Integer aPixelId2 = aPixelId1 + 1; aPixelId2 < myDiffPixels.Length(); ++aPixelId2) |
435 | { |
436 | const Standard_Size aValue2 = myDiffPixels.Value (aPixelId2); |
437 | int2Pixel (aValue2, aRow2, aCol2); |
185e6ec0 |
438 | if (getAbs (ptrdiff_t (aCol1 - aCol2)) <= 1 && |
439 | getAbs (ptrdiff_t (aRow1 - aRow2)) <= 1) |
692613e5 |
440 | { |
441 | // A neighbour is found. Create a new group and add both pixels. |
442 | if (myGroupsOfDiffPixels.IsEmpty()) |
443 | { |
444 | TColStd_MapOfInteger* aGroup = new TColStd_MapOfInteger(); |
7dc9e047 |
445 | aGroup->Add ((Standard_Integer)aValue1); |
446 | aGroup->Add ((Standard_Integer)aValue2); |
692613e5 |
447 | myGroupsOfDiffPixels.Append (aGroup); |
448 | } |
449 | else |
450 | { |
451 | // Find a group the pixels belong to. |
452 | Standard_Boolean isFound = Standard_False; |
453 | for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next()) |
454 | { |
455 | TColStd_MapOfInteger*& aGroup = aGrIter.ChangeValue(); |
7dc9e047 |
456 | if (aGroup->Contains ((Standard_Integer)aValue1)) |
692613e5 |
457 | { |
7dc9e047 |
458 | aGroup->Add ((Standard_Integer)aValue2); |
692613e5 |
459 | isFound = Standard_True; |
460 | break; |
461 | } |
462 | } |
463 | |
464 | if (!isFound) |
465 | { |
466 | // Create a new group |
467 | TColStd_MapOfInteger* aGroup = new TColStd_MapOfInteger(); |
7dc9e047 |
468 | aGroup->Add ((Standard_Integer)aValue1); |
469 | aGroup->Add ((Standard_Integer)aValue2); |
692613e5 |
470 | myGroupsOfDiffPixels.Append (aGroup); |
471 | } |
472 | } |
473 | } |
474 | } |
475 | } |
476 | |
477 | // filter linear groups which represent border of a solid shape |
478 | Standard_Integer aGroupId = 1; |
479 | for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next(), ++aGroupId) |
480 | { |
481 | Standard_Integer aNeighboursNb = 0; |
482 | Standard_Boolean isLine = Standard_True; |
483 | const TColStd_MapOfInteger* aGroup = aGrIter.Value(); |
484 | for (TColStd_MapIteratorOfMapOfInteger aPixelIter (*aGroup); aPixelIter.More(); aPixelIter.Next()) |
485 | { |
486 | int2Pixel (aPixelIter.Key(), aRow1, aCol1); |
487 | aNeighboursNb = 0; |
488 | |
489 | // pixels of a line have only 1 or 2 neighbour pixels inside the same group |
490 | // check all neighbour pixels on presence in the group |
491 | for (Standard_Size aNgbrIter = 0; aNgbrIter < NEIGHBOR_PIXELS_NB; ++aNgbrIter) |
492 | { |
ca0c0b11 |
493 | if (NEIGHBOR_PIXELS[aNgbrIter].isValid (*myImageRef, aRow1, aCol1) |
7dc9e047 |
494 | && aGroup->Contains ((Standard_Integer)NEIGHBOR_PIXELS[aNgbrIter].pixel2Int (aRow1, aCol1))) |
692613e5 |
495 | { |
496 | ++aNeighboursNb; |
497 | } |
498 | } |
499 | |
500 | if (aNeighboursNb > 2) |
501 | { |
502 | isLine = Standard_False; |
503 | break; |
504 | } |
505 | } // for pixels inside group... |
506 | |
507 | if (isLine) |
508 | { |
509 | // Test a pixel of the linear group on belonging to a solid shape. |
510 | // Consider neighbour pixels of the last pixel of the linear group in the 1st image. |
511 | // If the pixel has greater than 1 not black neighbour pixel, it is a border of a shape. |
512 | // Otherwise, it may be a topological edge, for example. |
513 | aNeighboursNb = 0; |
514 | for (Standard_Size aNgbrIter = 0; aNgbrIter < NEIGHBOR_PIXELS_NB; ++aNgbrIter) |
515 | { |
ca0c0b11 |
516 | if ( NEIGHBOR_PIXELS[aNgbrIter].isValid (*myImageRef, aRow1, aCol1) |
517 | && !NEIGHBOR_PIXELS[aNgbrIter].isBlack (*myImageRef, aRow1, aCol1)) |
692613e5 |
518 | { |
519 | ++aNeighboursNb; |
520 | } |
521 | } |
522 | |
523 | if (aNeighboursNb > 1) |
524 | { |
525 | myLinearGroups.Add (aGroupId); |
526 | } |
527 | } |
528 | } // for groups... |
529 | |
530 | // number of different groups of pixels (except linear groups) |
531 | Standard_Integer aNbDiffColors = 0; |
532 | aGroupId = 1; |
533 | for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next(), ++aGroupId) |
534 | { |
535 | if (!myLinearGroups.Contains (aGroupId)) |
536 | ++aNbDiffColors; |
537 | } |
538 | |
539 | return aNbDiffColors; |
540 | } |
541 | |
542 | // ======================================================================= |
543 | // function : releaseGroupsOfDiffPixels |
544 | // purpose : |
545 | // ======================================================================= |
546 | void Image_Diff::releaseGroupsOfDiffPixels() |
547 | { |
548 | for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next()) |
549 | { |
550 | TColStd_MapOfInteger*& aGroup = aGrIter.ChangeValue(); |
551 | delete aGroup; |
552 | } |
553 | myGroupsOfDiffPixels.Clear(); |
554 | myLinearGroups.Clear(); |
555 | } |