0028876: Tests, Image_Diff - the image difference is unavailable for test case bugs...
[occt.git] / src / Image / Image_Diff.cxx
CommitLineData
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>
692613e5 17
e958a649 18#include <Image_AlienPixMap.hxx>
19#include <Message.hxx>
20#include <Message_Messenger.hxx>
21#include <TColStd_MapIteratorOfPackedMapOfInteger.hxx>
692613e5 22
23#include <cstdlib>
24
92efcf78 25IMPLEMENT_STANDARD_RTTIEXT(Image_Diff,Standard_Transient)
26
692613e5 27namespace
28{
185e6ec0 29
e958a649 30 //! POD structure for packed RGB color value (3 bytes)
31 struct Image_ColorXXX24
185e6ec0 32 {
e958a649 33 Standard_Byte v[3];
34 typedef Standard_Byte ComponentType_t; //!< Component type
35 static Standard_Integer Length() { return 3; } //!< Returns the number of components
36 };
185e6ec0 37
e958a649 38 static Image_ColorXXX24 operator- (const Image_ColorXXX24& theA,
39 const Image_ColorXXX24& theB)
692613e5 40 {
e958a649 41 return Image_ColorSub3 (theA, theB);
42 }
692613e5 43
e958a649 44 //! Dot squared for difference of two colors
45 static Standard_Integer dotSquared (const Image_ColorXXX24& theColor)
46 {
47 // explicitly convert to integer
48 const Standard_Integer r = theColor.v[0];
49 const Standard_Integer g = theColor.v[1];
50 const Standard_Integer b = theColor.v[2];
51 return r * r + g * g + b * b;
52 }
692613e5 53
e958a649 54 //! Number of neighbor pixels.
55 static const Standard_Size Image_Diff_NbOfNeighborPixels = 8;
185e6ec0 56
e958a649 57 //! List of neighbor pixels (offsets).
58 static const int Image_Diff_NEIGHBOR_PIXELS[Image_Diff_NbOfNeighborPixels][2] =
692613e5 59 {
e958a649 60 {-1, -1}, {0, -1}, {1, -1},
61 {-1, 0}, {1, 0},
62 {-1, 1}, {0, 1}, {1, 1}
692613e5 63 };
64
e958a649 65 //! @return true if pixel is black
66 static bool isBlackPixel (const Image_PixMap& theData, Standard_Size theY, Standard_Size theX)
692613e5 67 {
e958a649 68 switch (theData.Format())
69 {
70 case Image_Format_Gray:
71 case Image_Format_Alpha:
72 {
73 return theData.Value<unsigned char> (theY, theX) == 0;
74 }
75 case Image_Format_RGB:
76 case Image_Format_BGR:
77 case Image_Format_RGB32:
78 case Image_Format_BGR32:
79 case Image_Format_RGBA:
80 case Image_Format_BGRA:
81 {
82 const Image_ColorXXX24& aColor = theData.Value<Image_ColorXXX24> (theY, theX);
83 return aColor.v[0] == 0
84 && aColor.v[1] == 0
85 && aColor.v[2] == 0;
86 }
87 default:
88 {
89 const Quantity_ColorRGBA aPixelRgba = theData.PixelColor ((int)theY, (int)theX);
90 const NCollection_Vec4<float>& aPixel = aPixelRgba;
91 return aPixel.r() == 0.0f
92 && aPixel.g() == 0.0f
93 && aPixel.b() == 0.0f;
94 }
95 }
692613e5 96 }
68858c7d 97
e958a649 98}
692613e5 99
100// =======================================================================
101// function : Image_Diff
102// purpose :
103// =======================================================================
104Image_Diff::Image_Diff()
105: myColorTolerance (0.0),
106 myIsBorderFilterOn (Standard_False)
107{
108 //
109}
110
111// =======================================================================
112// function : ~Image_Diff
113// purpose :
114// =======================================================================
115Image_Diff::~Image_Diff()
116{
117 releaseGroupsOfDiffPixels();
118}
119
120// =======================================================================
121// function : Init
122// purpose :
123// =======================================================================
124Standard_Boolean Image_Diff::Init (const Handle(Image_PixMap)& theImageRef,
125 const Handle(Image_PixMap)& theImageNew,
126 const Standard_Boolean theToBlackWhite)
127{
128 myImageRef.Nullify();
129 myImageNew.Nullify();
130 myDiffPixels.Clear();
131 releaseGroupsOfDiffPixels();
132 if (theImageRef.IsNull() || theImageNew.IsNull()
133 || theImageRef->IsEmpty() || theImageNew->IsEmpty()
134 || theImageRef->SizeX() != theImageNew->SizeX()
135 || theImageRef->SizeY() != theImageNew->SizeY()
136 || theImageRef->Format() != theImageNew->Format())
137 {
e958a649 138 Message::DefaultMessenger()->Send ("Error: Images have different format or dimensions", Message_Fail);
692613e5 139 return Standard_False;
140 }
141 else if (theImageRef->SizeX() >= 0xFFFF
142 || theImageRef->SizeY() >= 0xFFFF)
143 {
e958a649 144 Message::DefaultMessenger()->Send ("Error: Images are too large", Message_Fail);
692613e5 145 return Standard_False;
146 }
147
148 myImageRef = theImageRef;
149 myImageNew = theImageNew;
692613e5 150 if (theToBlackWhite)
151 {
e958a649 152 Image_PixMap::ToBlackWhite (*myImageRef);
153 Image_PixMap::ToBlackWhite (*myImageNew);
692613e5 154 }
692613e5 155 return Standard_True;
156}
157
692613e5 158// =======================================================================
159// function : Init
160// purpose :
161// =======================================================================
162Standard_Boolean Image_Diff::Init (const TCollection_AsciiString& theImgPathRef,
163 const TCollection_AsciiString& theImgPathNew,
164 const Standard_Boolean theToBlackWhite)
165{
166 Handle(Image_AlienPixMap) anImgRef = new Image_AlienPixMap();
167 Handle(Image_AlienPixMap) anImgNew = new Image_AlienPixMap();
168 if (!anImgRef->Load (theImgPathRef)
169 || !anImgNew->Load (theImgPathNew))
170 {
e958a649 171 Message::DefaultMessenger()->Send ("Error: Failed to load image(s) file(s)", Message_Fail);
692613e5 172 return Standard_False;
173 }
174 return Init (anImgRef, anImgNew, theToBlackWhite);
175}
176
692613e5 177// =======================================================================
178// function : Compare
179// purpose :
180// =======================================================================
181Standard_Integer Image_Diff::Compare()
182{
183 // Number of different pixels (by color)
184 Standard_Integer aNbDiffColors = 0;
185 myDiffPixels.Clear();
186
187 if (myImageRef.IsNull() || myImageNew.IsNull())
188 {
189 return -1;
190 }
191
e958a649 192 // first check if images are exactly the same
193 if (myImageNew->SizeBytes() == myImageRef->SizeBytes()
194 && memcmp (myImageNew->Data(), myImageRef->Data(), myImageRef->SizeBytes()) == 0)
936f43da 195 {
196 return 0;
197 }
198
e958a649 199 switch (myImageRef->Format())
692613e5 200 {
e958a649 201 case Image_Format_Gray:
202 case Image_Format_Alpha:
692613e5 203 {
e958a649 204 // Tolerance of comparison operation for color
205 Standard_Integer aDiff = 255;
206 const Standard_Real aMaxDiffColor = aDiff * aDiff;
207 const Standard_Integer aDiffThreshold = Standard_Integer(aMaxDiffColor * myColorTolerance);
208 for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow)
692613e5 209 {
e958a649 210 for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol)
211 {
212 aDiff = Standard_Integer(myImageNew->Value<unsigned char> (aRow, aCol)) - Standard_Integer(myImageRef->Value<unsigned char> (aRow, aCol));
213 if (aDiff * aDiff > aDiffThreshold)
214 {
215 myDiffPixels.Append (PackXY ((uint16_t)aCol, (uint16_t)aRow));
216 ++aNbDiffColors;
217 }
218 }
692613e5 219 }
e958a649 220 break;
221 }
222 case Image_Format_RGB:
223 case Image_Format_BGR:
224 case Image_Format_RGB32:
225 case Image_Format_BGR32:
226 case Image_Format_RGBA:
227 case Image_Format_BGRA:
228 {
229 // Tolerance of comparison operation for color
230 // Maximum difference between colors (white - black) = 100%
231 Image_ColorXXX24 aDiff = {{255, 255, 255}};
232 const Standard_Real aMaxDiffColor = dotSquared (aDiff);
233 const Standard_Integer aDiffThreshold = Standard_Integer(aMaxDiffColor * myColorTolerance);
234
235 // we don't care about RGB/BGR/RGBA/BGRA/RGB32/BGR32 differences
236 // because we just compute summ of r g b components
237 for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow)
238 {
239 for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol)
240 {
241 aDiff = myImageNew->Value<Image_ColorXXX24> (aRow, aCol) - myImageRef->Value<Image_ColorXXX24> (aRow, aCol);
242 if (dotSquared (aDiff) > aDiffThreshold)
243 {
244 myDiffPixels.Append (PackXY ((uint16_t)aCol, (uint16_t)aRow));
245 ++aNbDiffColors;
246 }
247 }
248 }
249 break;
250 }
251 default:
252 {
253 // Tolerance of comparison operation for color
254 // Maximum difference between colors (white - black) = 100%
255 NCollection_Vec3<float> aDiff (1.0f, 1.0f, 1.0f);
256 const Standard_Real aMaxDiffColor = aDiff.SquareModulus();
257 const Standard_Integer aDiffThreshold = Standard_Integer(aMaxDiffColor * myColorTolerance);
258 for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow)
259 {
260 for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol)
261 {
262 const Quantity_ColorRGBA aPixel1Rgba = myImageRef->PixelColor (Standard_Integer(aCol), Standard_Integer(aRow));
263 const Quantity_ColorRGBA aPixel2Rgba = myImageNew->PixelColor (Standard_Integer(aCol), Standard_Integer(aRow));
264 const NCollection_Vec3<float>& aPixel1 = aPixel1Rgba.GetRGB();
265 const NCollection_Vec3<float>& aPixel2 = aPixel2Rgba.GetRGB();
266 aDiff = aPixel2 - aPixel1;
267 if (aDiff.SquareModulus() > aDiffThreshold)
268 {
269 myDiffPixels.Append (PackXY ((uint16_t)aCol, (uint16_t)aRow));
270 ++aNbDiffColors;
271 }
272 }
273 }
274 break;
692613e5 275 }
276 }
277
278 // take into account a border effect
e958a649 279 if (myIsBorderFilterOn && !myDiffPixels.IsEmpty())
692613e5 280 {
281 aNbDiffColors = ignoreBorderEffect();
282 }
283
284 return aNbDiffColors;
285}
286
287// =======================================================================
288// function : SaveDiffImage
289// purpose :
290// =======================================================================
291Standard_Boolean Image_Diff::SaveDiffImage (Image_PixMap& theDiffImage) const
292{
293 if (myImageRef.IsNull() || myImageNew.IsNull())
294 {
295 return Standard_False;
296 }
297
298 if (theDiffImage.IsEmpty()
299 || theDiffImage.SizeX() != myImageRef->SizeX()
e958a649 300 || theDiffImage.SizeY() != myImageRef->SizeY())
692613e5 301 {
e958a649 302 if (!theDiffImage.InitTrash (Image_Format_Gray, myImageRef->SizeX(), myImageRef->SizeY()))
692613e5 303 {
304 return Standard_False;
305 }
306 }
307
e958a649 308 const Image_ColorXXX24 aWhite24 = {{255, 255, 255}};
309 const Quantity_ColorRGBA aWhiteRgba (1.0f, 1.0f, 1.0f, 1.0f);
692613e5 310
311 // initialize black image for dump
312 memset (theDiffImage.ChangeData(), 0, theDiffImage.SizeBytes());
313 if (myGroupsOfDiffPixels.IsEmpty())
314 {
315 if (myIsBorderFilterOn)
316 {
317 return Standard_True;
318 }
319
e958a649 320 switch (theDiffImage.Format())
692613e5 321 {
e958a649 322 case Image_Format_Gray:
323 case Image_Format_Alpha:
324 {
325 for (NCollection_Vector<Standard_Integer>::Iterator aPixelIter (myDiffPixels); aPixelIter.More(); aPixelIter.Next())
326 {
327 theDiffImage.ChangeValue<unsigned char> (UnpackY(aPixelIter.Value()), UnpackX(aPixelIter.Value())) = 255;
328 }
329 break;
330 }
331 case Image_Format_RGB:
332 case Image_Format_BGR:
333 case Image_Format_RGB32:
334 case Image_Format_BGR32:
335 case Image_Format_RGBA:
336 case Image_Format_BGRA:
337 {
338 for (NCollection_Vector<Standard_Integer>::Iterator aPixelIter (myDiffPixels); aPixelIter.More(); aPixelIter.Next())
339 {
340 theDiffImage.ChangeValue<Image_ColorXXX24> (UnpackY(aPixelIter.Value()), UnpackX(aPixelIter.Value())) = aWhite24;
341 }
342 break;
343 }
344 default:
345 {
346 for (NCollection_Vector<Standard_Integer>::Iterator aPixelIter (myDiffPixels); aPixelIter.More(); aPixelIter.Next())
347 {
348 theDiffImage.SetPixelColor (UnpackX(aPixelIter.Value()), UnpackY(aPixelIter.Value()), aWhiteRgba);
349 }
350 break;
351 }
692613e5 352 }
692613e5 353 return Standard_True;
354 }
355
356 Standard_Integer aGroupId = 1;
e958a649 357 for (NCollection_List<Handle(TColStd_HPackedMapOfInteger)>::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next(), ++aGroupId)
692613e5 358 {
359 if (myLinearGroups.Contains (aGroupId))
360 {
361 continue; // skip linear groups
362 }
363
e958a649 364 const Handle(TColStd_HPackedMapOfInteger)& aGroup = aGrIter.Value();
365 switch (theDiffImage.Format())
692613e5 366 {
e958a649 367 case Image_Format_Gray:
368 case Image_Format_Alpha:
369 {
370 for (TColStd_MapIteratorOfPackedMapOfInteger aPixelIter (aGroup->Map()); aPixelIter.More(); aPixelIter.Next())
371 {
372 Standard_Integer aDiffPixel (aPixelIter.Key());
373 theDiffImage.ChangeValue<unsigned char> (UnpackY(aDiffPixel), UnpackX(aDiffPixel)) = 255;
374 }
375 break;
376 }
377 case Image_Format_RGB:
378 case Image_Format_BGR:
379 case Image_Format_RGB32:
380 case Image_Format_BGR32:
381 case Image_Format_RGBA:
382 case Image_Format_BGRA:
383 {
384 for (TColStd_MapIteratorOfPackedMapOfInteger aPixelIter (aGroup->Map()); aPixelIter.More(); aPixelIter.Next())
385 {
386 Standard_Integer aDiffPixel (aPixelIter.Key());
387 theDiffImage.ChangeValue<Image_ColorXXX24> (UnpackY(aDiffPixel), UnpackX(aDiffPixel)) = aWhite24;
388 }
389 break;
390 }
391 default:
392 {
393 for (TColStd_MapIteratorOfPackedMapOfInteger aPixelIter (aGroup->Map()); aPixelIter.More(); aPixelIter.Next())
394 {
395 Standard_Integer aDiffPixel (aPixelIter.Key());
396 theDiffImage.SetPixelColor (UnpackX(aDiffPixel), UnpackY(aDiffPixel), aWhiteRgba);
397 }
398 break;
399 }
692613e5 400 }
401 }
402
403 return Standard_True;
404}
405
406// =======================================================================
407// function : SaveDiffImage
408// purpose :
409// =======================================================================
410Standard_Boolean Image_Diff::SaveDiffImage (const TCollection_AsciiString& theDiffPath) const
411{
412 if (myImageRef.IsNull() || myImageNew.IsNull() || theDiffPath.IsEmpty())
413 {
414 return Standard_False;
415 }
416
417 Image_AlienPixMap aDiff;
e958a649 418 if (!aDiff.InitTrash (Image_Format_Gray, myImageRef->SizeX(), myImageRef->SizeY())
692613e5 419 || !SaveDiffImage (aDiff))
420 {
421 return Standard_False;
422 }
423
424 // save image
425 return aDiff.Save (theDiffPath);
426}
427
428// =======================================================================
429// function : ignoreBorderEffect
430// purpose :
431// =======================================================================
432Standard_Integer Image_Diff::ignoreBorderEffect()
433{
434 if (myImageRef.IsNull() || myImageNew.IsNull())
435 {
436 return 0;
437 }
438
692613e5 439 // allocate groups of different pixels
440 releaseGroupsOfDiffPixels();
441
442 // Find a different area (a set of close to each other pixels which colors differ in both images).
443 // It filters alone pixels with different color.
e958a649 444 const Standard_Integer aLen1 = !myDiffPixels.IsEmpty() ? (myDiffPixels.Length() - 1) : 0;
692613e5 445 for (Standard_Integer aPixelId1 = 0; aPixelId1 < aLen1; ++aPixelId1)
446 {
e958a649 447 Standard_Integer aValue1 = myDiffPixels.Value (aPixelId1);
692613e5 448
449 // Check other pixels in the list looking for a neighbour of this one
450 for (Standard_Integer aPixelId2 = aPixelId1 + 1; aPixelId2 < myDiffPixels.Length(); ++aPixelId2)
451 {
e958a649 452 Standard_Integer aValue2 = myDiffPixels.Value (aPixelId2);
453 if (Abs (Standard_Integer(UnpackX(aValue1)) - Standard_Integer(UnpackX(aValue2))) <= 1
454 && Abs (Standard_Integer(UnpackY(aValue1)) - Standard_Integer(UnpackY(aValue2))) <= 1)
692613e5 455 {
456 // A neighbour is found. Create a new group and add both pixels.
457 if (myGroupsOfDiffPixels.IsEmpty())
458 {
e958a649 459 Handle(TColStd_HPackedMapOfInteger) aGroup = new TColStd_HPackedMapOfInteger();
460 aGroup->ChangeMap().Add (aValue1);
461 aGroup->ChangeMap().Add (aValue2);
692613e5 462 myGroupsOfDiffPixels.Append (aGroup);
463 }
464 else
465 {
466 // Find a group the pixels belong to.
467 Standard_Boolean isFound = Standard_False;
e958a649 468 for (NCollection_List<Handle(TColStd_HPackedMapOfInteger)>::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next())
692613e5 469 {
e958a649 470 const Handle(TColStd_HPackedMapOfInteger)& aGroup = aGrIter.ChangeValue();
471 if (aGroup->Map().Contains (aValue1))
692613e5 472 {
e958a649 473 aGroup->ChangeMap().Add (aValue2);
692613e5 474 isFound = Standard_True;
475 break;
476 }
477 }
478
479 if (!isFound)
480 {
481 // Create a new group
e958a649 482 Handle(TColStd_HPackedMapOfInteger) aGroup = new TColStd_HPackedMapOfInteger();
483 aGroup->ChangeMap().Add (aValue1);
484 aGroup->ChangeMap().Add (aValue2);
692613e5 485 myGroupsOfDiffPixels.Append (aGroup);
486 }
487 }
488 }
489 }
490 }
491
492 // filter linear groups which represent border of a solid shape
493 Standard_Integer aGroupId = 1;
e958a649 494 for (NCollection_List<Handle(TColStd_HPackedMapOfInteger)>::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next(), ++aGroupId)
692613e5 495 {
496 Standard_Integer aNeighboursNb = 0;
497 Standard_Boolean isLine = Standard_True;
e958a649 498 const Handle(TColStd_HPackedMapOfInteger)& aGroup = aGrIter.Value();
499 if (aGroup->Map().IsEmpty())
692613e5 500 {
e958a649 501 continue;
502 }
503
504 Standard_Integer aDiffPixel = 0;
505 for (TColStd_MapIteratorOfPackedMapOfInteger aPixelIter (aGroup->Map()); aPixelIter.More(); aPixelIter.Next())
506 {
507 aDiffPixel = aPixelIter.Key();
692613e5 508 aNeighboursNb = 0;
509
510 // pixels of a line have only 1 or 2 neighbour pixels inside the same group
511 // check all neighbour pixels on presence in the group
e958a649 512 for (Standard_Size aNgbrIter = 0; aNgbrIter < Image_Diff_NbOfNeighborPixels; ++aNgbrIter)
692613e5 513 {
e958a649 514 Standard_Integer anX = UnpackX(aDiffPixel) + Image_Diff_NEIGHBOR_PIXELS[aNgbrIter][0];
515 Standard_Integer anY = UnpackY(aDiffPixel) + Image_Diff_NEIGHBOR_PIXELS[aNgbrIter][1];
516 if (Standard_Size(anX) < myImageRef->SizeX() // this unsigned math checks Standard_Size(-1) at-once
517 && Standard_Size(anY) < myImageRef->SizeY()
518 && aGroup->Map().Contains (PackXY((uint16_t)anX, (uint16_t)anY)))
692613e5 519 {
520 ++aNeighboursNb;
521 }
522 }
523
524 if (aNeighboursNb > 2)
525 {
526 isLine = Standard_False;
527 break;
528 }
e958a649 529 }
692613e5 530
531 if (isLine)
532 {
533 // Test a pixel of the linear group on belonging to a solid shape.
534 // Consider neighbour pixels of the last pixel of the linear group in the 1st image.
535 // If the pixel has greater than 1 not black neighbour pixel, it is a border of a shape.
536 // Otherwise, it may be a topological edge, for example.
537 aNeighboursNb = 0;
e958a649 538 for (Standard_Size aNgbrIter = 0; aNgbrIter < Image_Diff_NbOfNeighborPixels; ++aNgbrIter)
692613e5 539 {
e958a649 540 Standard_Integer anX = UnpackX(aDiffPixel) + Image_Diff_NEIGHBOR_PIXELS[aNgbrIter][0];
541 Standard_Integer anY = UnpackY(aDiffPixel) + Image_Diff_NEIGHBOR_PIXELS[aNgbrIter][1];
542 if (Standard_Size(anX) < myImageRef->SizeX() // this unsigned math checks Standard_Size(-1) at-once
543 && Standard_Size(anY) < myImageRef->SizeY()
544 && !isBlackPixel (*myImageRef, Standard_Size(anY), Standard_Size(anX)))
692613e5 545 {
546 ++aNeighboursNb;
547 }
548 }
549
550 if (aNeighboursNb > 1)
551 {
552 myLinearGroups.Add (aGroupId);
553 }
554 }
e958a649 555 }
692613e5 556
557 // number of different groups of pixels (except linear groups)
558 Standard_Integer aNbDiffColors = 0;
559 aGroupId = 1;
e958a649 560 for (NCollection_List<Handle(TColStd_HPackedMapOfInteger)>::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next(), ++aGroupId)
692613e5 561 {
562 if (!myLinearGroups.Contains (aGroupId))
e958a649 563 {
692613e5 564 ++aNbDiffColors;
e958a649 565 }
692613e5 566 }
567
568 return aNbDiffColors;
569}
570
571// =======================================================================
572// function : releaseGroupsOfDiffPixels
573// purpose :
574// =======================================================================
575void Image_Diff::releaseGroupsOfDiffPixels()
576{
692613e5 577 myGroupsOfDiffPixels.Clear();
578 myLinearGroups.Clear();
579}