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