KiCad PCB EDA Suite
cimage.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
5  * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
30 #include "cimage.h"
31 #include "buffers_debug.h"
32 #include <cstring> // For memcpy
33 
34 #include <algorithm>
35 #include <atomic>
36 #include <thread>
37 #include <chrono>
38 
39 #ifndef CLAMP
40 #define CLAMP(n, min, max) {if( n < min ) n=min; else if( n > max ) n = max;}
41 #endif
42 
43 
44 CIMAGE::CIMAGE( unsigned int aXsize, unsigned int aYsize )
45 {
46  m_wxh = aXsize * aYsize;
47  m_pixels = new unsigned char[m_wxh];
48  memset( m_pixels, 0, m_wxh );
49  m_width = aXsize;
50  m_height = aYsize;
52 }
53 
54 
55 CIMAGE::CIMAGE( const CIMAGE &aSrcImage )
56 {
57  m_wxh = aSrcImage.GetWidth() * aSrcImage.GetHeight();
58  m_pixels = new unsigned char[m_wxh];
59  memcpy( m_pixels, aSrcImage.GetBuffer(), m_wxh );
60  m_width = aSrcImage.GetWidth();
61  m_height = aSrcImage.GetHeight();
63 }
64 
65 
67 {
68  delete[] m_pixels;
69 }
70 
71 
72 unsigned char* CIMAGE::GetBuffer() const
73 {
74  return m_pixels;
75 }
76 
77 
78 bool CIMAGE::wrapCoords( int *aXo, int *aYo ) const
79 {
80  int x = *aXo;
81  int y = *aYo;
82 
83  switch(m_wraping)
84  {
85  case IMAGE_WRAP::CLAMP:
86  x = (x < 0 )?0:x;
87  x = (x >= (int)(m_width - 1))?(m_width - 1):x;
88  y = (y < 0)?0:y;
89  y = (y >= (int)(m_height - 1))?(m_height - 1):y;
90  break;
91 
92  case IMAGE_WRAP::WRAP:
93  x = (x < 0)?((m_width - 1)+x):x;
94  x = (x >= (int)(m_width - 1))?(x - m_width):x;
95  y = (y < 0)?((m_height - 1)+y):y;
96  y = (y >= (int)(m_height - 1))?(y - m_height):y;
97  break;
98 
99  default:
100  break;
101  }
102 
103  if( (x < 0) || (x >= (int)m_width) ||
104  (y < 0) || (y >= (int)m_height) )
105  return false;
106 
107  *aXo = x;
108  *aYo = y;
109 
110  return true;
111 }
112 
113 void CIMAGE::plot8CircleLines( int aCx, int aCy, int aX, int aY, unsigned char aValue )
114 {
115  Hline( aCx - aX, aCx + aX, aCy + aY, aValue );
116  Hline( aCx - aX, aCx + aX, aCy - aY, aValue );
117  Hline( aCx - aY, aCx + aY, aCy + aX, aValue );
118  Hline( aCx - aY, aCx + aY, aCy - aX, aValue );
119 }
120 
121 
122 void CIMAGE::Setpixel( int aX, int aY, unsigned char aValue )
123 {
124  if( wrapCoords( &aX, &aY ) )
125  m_pixels[aX + aY * m_width] = aValue;
126 }
127 
128 
129 unsigned char CIMAGE::Getpixel( int aX, int aY ) const
130 {
131  if( wrapCoords( &aX, &aY ) )
132  return m_pixels[aX + aY * m_width];
133  else
134  return 0;
135 }
136 
137 
138 void CIMAGE::Hline( int aXStart, int aXEnd, int aY, unsigned char aValue )
139 {
140  if( ( aY < 0 ) ||
141  ( aY >= (int)m_height ) ||
142  ( ( aXStart < 0 ) && ( aXEnd < 0) ) ||
143  ( ( aXStart >= (int)m_width ) && ( aXEnd >= (int)m_width) ) )
144  return;
145 
146  if( aXStart > aXEnd )
147  {
148  int swap = aXStart;
149 
150  aXStart = aXEnd;
151  aXEnd = swap;
152  }
153 
154  // Clamp line
155  if( aXStart < 0 )
156  aXStart = 0;
157 
158  if( aXEnd >= (int)m_width )
159  aXEnd = m_width - 1;
160 
161  unsigned char* pixelPtr = &m_pixels[aXStart + aY * m_width];
162  unsigned char* pixelPtrEnd = pixelPtr + (unsigned int)((aXEnd - aXStart) + 1);
163 
164  while( pixelPtr < pixelPtrEnd )
165  {
166  *pixelPtr = aValue;
167  pixelPtr++;
168  }
169 }
170 
171 // Based on paper
172 // http://web.engr.oregonstate.edu/~sllu/bcircle.pdf
173 void CIMAGE::CircleFilled(int aCx, int aCy, int aRadius, unsigned char aValue)
174 {
175  int x = aRadius;
176  int y = 0;
177  int xChange = 1 - 2 * aRadius;
178  int yChange = 0;
179  int radiusError = 0;
180 
181  while( x >= y )
182  {
183  plot8CircleLines( aCx, aCy, x, y, aValue );
184  y++;
185  radiusError += yChange;
186  yChange += 2;
187 
188  if( (2 * radiusError + xChange) > 0 )
189  {
190  x--;
191  radiusError += xChange;
192  xChange += 2;
193  }
194  }
195 }
196 
197 
199 {
200  for( unsigned int it = 0; it < m_wxh; it++ )
201  m_pixels[it] = 255 - m_pixels[it];
202 }
203 
204 
205 void CIMAGE::CopyFull( const CIMAGE* aImgA, const CIMAGE* aImgB, IMAGE_OP aOperation )
206 {
207  int aV, bV;
208 
209  if( aOperation == IMAGE_OP::RAW )
210  {
211  if( aImgA == NULL )
212  return;
213  }
214  else
215  {
216  if( (aImgA == NULL) || (aImgB == NULL) )
217  return;
218  }
219 
220  switch(aOperation)
221  {
222  case IMAGE_OP::RAW:
223  memcpy( m_pixels, aImgA->m_pixels, m_wxh );
224  break;
225 
226  case IMAGE_OP::ADD:
227  for( unsigned int it = 0;it < m_wxh; it++ )
228  {
229  aV = aImgA->m_pixels[it];
230  bV = aImgB->m_pixels[it];
231 
232  aV = (aV + bV);
233  aV = (aV > 255)?255:aV;
234 
235  m_pixels[it] = aV;
236  }
237  break;
238 
239  case IMAGE_OP::SUB:
240  for( unsigned int it = 0;it < m_wxh; it++ )
241  {
242  aV = aImgA->m_pixels[it];
243  bV = aImgB->m_pixels[it];
244 
245  aV = (aV - bV);
246  aV = (aV < 0)?0:aV;
247 
248  m_pixels[it] = aV;
249  }
250  break;
251 
252  case IMAGE_OP::DIF:
253  for( unsigned int it = 0;it < m_wxh; it++ )
254  {
255  aV = aImgA->m_pixels[it];
256  bV = aImgB->m_pixels[it];
257 
258  m_pixels[it] = abs( aV - bV );
259  }
260  break;
261 
262  case IMAGE_OP::MUL:
263  for( unsigned int it = 0;it < m_wxh; it++ )
264  {
265  aV = aImgA->m_pixels[it];
266  bV = aImgB->m_pixels[it];
267 
268  m_pixels[it] = (unsigned char)((((float)aV / 255.0f) * ((float)bV / 255.0f)) * 255);
269  }
270  break;
271 
272  case IMAGE_OP::AND:
273  for( unsigned int it = 0;it < m_wxh; it++ )
274  {
275  m_pixels[it] = aImgA->m_pixels[it] & aImgB->m_pixels[it];
276  }
277  break;
278 
279  case IMAGE_OP::OR:
280  for( unsigned int it = 0;it < m_wxh; it++ )
281  {
282  m_pixels[it] = aImgA->m_pixels[it] | aImgB->m_pixels[it];
283  }
284  break;
285 
286  case IMAGE_OP::XOR:
287  for( unsigned int it = 0;it < m_wxh; it++ )
288  {
289  m_pixels[it] = aImgA->m_pixels[it] ^ aImgB->m_pixels[it];
290  }
291  break;
292 
293  case IMAGE_OP::BLEND50:
294  for( unsigned int it = 0;it < m_wxh; it++ )
295  {
296  aV = aImgA->m_pixels[it];
297  bV = aImgB->m_pixels[it];
298 
299  m_pixels[it] = (aV + bV) / 2;
300  }
301  break;
302 
303  case IMAGE_OP::MIN:
304  for( unsigned int it = 0;it < m_wxh; it++ )
305  {
306  aV = aImgA->m_pixels[it];
307  bV = aImgB->m_pixels[it];
308 
309  m_pixels[it] = (aV < bV)?aV:bV;
310  }
311  break;
312 
313  case IMAGE_OP::MAX:
314  for( unsigned int it = 0;it < m_wxh; it++ )
315  {
316  aV = aImgA->m_pixels[it];
317  bV = aImgB->m_pixels[it];
318 
319  m_pixels[it] = (aV > bV)?aV:bV;
320  }
321  break;
322 
323  default:
324  break;
325  }
326 }
327 
328 // TIP: If you want create or test filters you can use GIMP
329 // with a generic convolution matrix and get the values from there.
330 // http://docs.gimp.org/nl/plug-in-convmatrix.html
331 // clang-format off
332 static const S_FILTER FILTERS[] = {
333  // IMAGE_FILTER::HIPASS
334  {
335  { { 0, -1, -1, -1, 0},
336  {-1, 2, -4, 2, -1},
337  {-1, -4, 13, -4, -1},
338  {-1, 2, -4, 2, -1},
339  { 0, -1, -1, -1, 0}
340  },
341  7,
342  255
343  },
344 
345  // IMAGE_FILTER::GAUSSIAN_BLUR
346  {
347  { { 3, 5, 7, 5, 3},
348  { 5, 9, 12, 9, 5},
349  { 7, 12, 20, 12, 7},
350  { 5, 9, 12, 9, 5},
351  { 3, 5, 7, 5, 3}
352  },
353  182,
354  0
355  },
356 
357  // IMAGE_FILTER::GAUSSIAN_BLUR2
358  {
359  { { 1, 4, 7, 4, 1},
360  { 4, 16, 26, 16, 4},
361  { 7, 26, 41, 26, 7},
362  { 4, 16, 26, 16, 4},
363  { 1, 4, 7, 4, 1}
364  },
365  273,
366  0
367  },
368 
369  // IMAGE_FILTER::INVERT_BLUR
370  {
371  { { 0, 0, 0, 0, 0},
372  { 0, 0, -1, 0, 0},
373  { 0, -1, 0, -1, 0},
374  { 0, 0, -1, 0, 0},
375  { 0, 0, 0, 0, 0}
376  },
377  4,
378  255
379  },
380 
381  // IMAGE_FILTER::CARTOON
382  {
383  { {-1, -1, -1, -1, 0},
384  {-1, 0, 0, 0, 0},
385  {-1, 0, 4, 0, 0},
386  { 0, 0, 0, 1, 0},
387  { 0, 0, 0, 0, 4}
388  },
389  3,
390  0
391  },
392 
393  // IMAGE_FILTER::EMBOSS
394  {
395  { {-1, -1, -1, -1, 0},
396  {-1, -1, -1, 0, 1},
397  {-1, -1, 0, 1, 1},
398  {-1, 0, 1, 1, 1},
399  { 0, 1, 1, 1, 1}
400  },
401  1,
402  128
403  },
404 
405  // IMAGE_FILTER::SHARPEN
406  {
407  { {-1, -1, -1, -1, -1},
408  {-1, 2, 2, 2, -1},
409  {-1, 2, 8, 2, -1},
410  {-1, 2, 2, 2, -1},
411  {-1, -1, -1, -1, -1}
412  },
413  8,
414  0
415  },
416 
417  // IMAGE_FILTER::MELT
418  {
419  { { 4, 2, 6, 8, 1},
420  { 1, 2, 5, 4, 2},
421  { 0, -1, 1, -1, 0},
422  { 0, 0, -2, 0, 0},
423  { 0, 0, 0, 0, 0}
424  },
425  32,
426  0
427  },
428 
429  // IMAGE_FILTER::SOBEL_GX
430  {
431  { { 0, 0, 0, 0, 0},
432  { 0, -1, 0, 1, 0},
433  { 0, -2, 0, 2, 0},
434  { 0, -1, 0, 1, 0},
435  { 0, 0, 0, 0, 0}
436  },
437  1,
438  0
439  },
440 
441  // IMAGE_FILTER::SOBEL_GY
442  {
443  { { 1, 2, 4, 2, 1},
444  {-1, -1, 0, 1, 1},
445  {-2, -2, 0, 2, 2},
446  {-1, -1, 0, 1, 1},
447  {-1, -2, -4, -2, -1},
448  },
449  1,
450  0
451  },
452 
453  // IMAGE_FILTER::BLUR_3X3
454  {
455  { { 0, 0, 0, 0, 0},
456  { 0, 1, 2, 1, 0},
457  { 0, 2, 4, 2, 0},
458  { 0, 1, 2, 1, 0},
459  { 0, 0, 0, 0, 0},
460  },
461  16,
462  0
463  }
464 };// Filters
465 // clang-format on
466 
467 
468 // !TODO: This functions can be optimized slipting it between the edges and
469 // do it without use the getpixel function.
470 // Optimization can be done to m_pixels[ix + iy * m_width]
471 // but keep in mind the parallel process of the algorithm
472 void CIMAGE::EfxFilter( CIMAGE* aInImg, IMAGE_FILTER aFilterType )
473 {
474  S_FILTER filter = FILTERS[static_cast<int>( aFilterType )];
475 
476  aInImg->m_wraping = IMAGE_WRAP::CLAMP;
478 
479  std::atomic<size_t> nextRow( 0 );
480  std::atomic<size_t> threadsFinished( 0 );
481 
482  size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
483 
484  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
485  {
486  std::thread t = std::thread( [&]()
487  {
488  for( size_t iy = nextRow.fetch_add( 1 );
489  iy < m_height;
490  iy = nextRow.fetch_add( 1 ) )
491  {
492  for( size_t ix = 0; ix < m_width; ix++ )
493  {
494  int v = 0;
495 
496  for( size_t sy = 0; sy < 5; sy++ )
497  {
498  for( size_t sx = 0; sx < 5; sx++ )
499  {
500  int factor = filter.kernel[sx][sy];
501  unsigned char pixelv = aInImg->Getpixel( ix + sx - 2,
502  iy + sy - 2 );
503 
504  v += pixelv * factor;
505  }
506  }
507 
508  v /= filter.div;
509  v += filter.offset;
510  CLAMP(v, 0, 255);
511  //TODO: This needs to write to a separate buffer
512  m_pixels[ix + iy * m_width] = v;
513  }
514  }
515 
516  threadsFinished++;
517  } );
518 
519  t.detach();
520  }
521 
522  while( threadsFinished < parallelThreadCount )
523  std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
524 }
525 
526 
527 void CIMAGE::SetPixelsFromNormalizedFloat( const float * aNormalizedFloatArray )
528 {
529  for( unsigned int i = 0; i < m_wxh; i++ )
530  {
531  int v = aNormalizedFloatArray[i] * 255;
532 
533  CLAMP( v, 0, 255 );
534  m_pixels[i] = v;
535  }
536 }
537 
538 
539 void CIMAGE::SaveAsPNG( const wxString& aFileName ) const
540 {
541  DBG_SaveBuffer( aFileName, m_pixels, m_width, m_height );
542 }
~CIMAGE()
Definition: cimage.cpp:66
unsigned int m_height
height of the image
Definition: cimage.h:227
unsigned int GetWidth() const
Definition: cimage.h:209
void SaveAsPNG(const wxString &aFileName) const
Function SaveAsPNG save image buffer to a PNG file into the working folder.
Definition: cimage.cpp:539
Coords are wrapped arround.
IMAGE_WRAP m_wraping
current wrapping type
Definition: cimage.h:229
unsigned char * m_pixels
buffer to store the image 8bit-channel
Definition: cimage.h:225
void Hline(int aXStart, int aXEnd, int aY, unsigned char aValue)
hline - Draws an horizontal line
Definition: cimage.cpp:138
void CircleFilled(int aCx, int aCy, int aRadius, unsigned char aValue)
CircleFilled.
Definition: cimage.cpp:173
void Setpixel(int aX, int aY, unsigned char aValue)
Function Setpixel set a value in a pixel position, position is clamped in accord with the current cla...
Definition: cimage.cpp:122
void DBG_SaveBuffer(const wxString &aFileName, const unsigned char *aInBuffer, unsigned int aXSize, unsigned int aYSize)
CIMAGE(unsigned int aXsize, unsigned int aYsize)
Constructor CIMAGE constructs a CIMAGE based on image size.
Definition: cimage.cpp:44
#define NULL
void Invert()
Function Invert invert the values of image this <- (255 - this)
Definition: cimage.cpp:198
unsigned int GetHeight() const
Definition: cimage.h:210
unsigned char offset
Definition: cimage.h:81
unsigned int m_width
width of the image
Definition: cimage.h:226
unsigned int m_wxh
width * height precalc value
Definition: cimage.h:228
IMAGE_FILTER
Filter type enumeration.
Definition: cimage.h:62
void plot8CircleLines(int aCx, int aCy, int aX, int aY, unsigned char aValue)
Definition: cimage.cpp:113
one 8bit-channel image definition
unsigned char Getpixel(int aX, int aY) const
Function Getpixel get the pixel value from pixel position, position is clamped in accord with the cur...
Definition: cimage.cpp:129
signed char kernel[5][5]
Definition: cimage.h:79
unsigned int div
Definition: cimage.h:80
bool wrapCoords(int *aXo, int *aYo) const
Function wrapCoords calculate the coordinates points in accord with the current clamping settings.
Definition: cimage.cpp:78
5x5 Filter struct parameters
Definition: cimage.h:78
IMAGE_OP
Image operation type.
Definition: cimage.h:36
static const S_FILTER FILTERS[]
Definition: cimage.cpp:332
void SetPixelsFromNormalizedFloat(const float *aNormalizedFloatArray)
Function SetPixelsFromNormalizedFloat set the current channel from a float normalized (0....
Definition: cimage.cpp:527
CIMAGE manages a 8-bit channel image.
Definition: cimage.h:89
void CopyFull(const CIMAGE *aImgA, const CIMAGE *aImgB, IMAGE_OP aOperation)
Function CopyFull perform a copy operation, based on operation type.
Definition: cimage.cpp:205
unsigned char * GetBuffer() const
Function GetBuffer get the image buffer pointer.
Definition: cimage.cpp:72
void EfxFilter(CIMAGE *aInImg, IMAGE_FILTER aFilterType)
Function EfxFilter apply a filter to the input image and stores it in the image class this <- FilterT...
Definition: cimage.cpp:472
Coords are clamped to image size.