Main Page · Modules · All Classes · Class Hierarchy
MEImage.cpp
1 /*
2  * This file is part of the AiBO+ project
3  *
4  * Copyright (C) 2005-2016 Csaba Kertész (csaba.kertesz@gmail.com)
5  *
6  * AiBO+ is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * AiBO+ is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
19  *
20  */
21 
22 #include "MEImage.hpp"
23 
24 #include <MCBinaryData.hpp>
25 #include <MCDefs.hpp>
26 #include <MCLog.hpp>
27 #include <MCSampleStatistics.hpp>
28 
29 #include <opencv/cv.h>
30 #ifndef __AIBO_BUILD__
31 #include <opencv/highgui.h>
32 #endif
33 
34 #include <jpeglib_aiboplus.h>
35 #include <jerror_aiboplus.h>
36 
37 #include <boost/foreach.hpp>
38 #include <boost/scoped_ptr.hpp>
39 
40 #define RELEASE_IPLIMAGE(image_ptr) \
41  { \
42  cvReleaseImage(&image_ptr); \
43  image_ptr = nullptr; \
44  }
45 
46 namespace
47 {
48 // RGB to YUV transform
49 const float RGBtoYUVMatrix[3][3] =
50  {{ 0.299, 0.587, 0.114 },
51  { -0.147, -0.289, 0.436 },
52  { 0.615, -0.515, -0.100 }};
53 
54 // RGB to YIQ transform
55 const float RGBtoYIQMatrix[3][3] =
56  {{ 0.299, 0.587, 0.114 },
57  { 0.596, -0.274, -0.322 },
58  { 0.212, -0.523, 0.311 }};
59 
60 void AlternateExit(j_common_ptr cinfo)
61 {
62  /* Always display the message */
63  (*cinfo->err->output_message)(cinfo);
64  /* Let the memory manager delete any temp files before we die */
65  jpeg_destroy(cinfo);
66  throw 0;
67 }
68 
69 
70 void AlternatePrint(j_common_ptr cinfo)
71 {
72  char buffer[JMSG_LENGTH_MAX];
73 
74  /* Create the message */
75  (*cinfo->err->format_message) (cinfo, buffer);
76  MC_WARNING("%s", buffer);
77 }
78 
79 
80 void AlternatePrintNoEcho(j_common_ptr)
81 {
82  // Nothing to do
83 }
84 
85 
86 void InitJpegBuffer(jpeg_compress_struct*)
87 {
88  // Nothing to do
89 }
90 
91 
92 boolean EmptyJpegBuffer(jpeg_compress_struct*)
93 {
94  // Nothing to do
95  return true;
96 }
97 
98 
99 void FinalizeJpegBuffer(jpeg_compress_struct*)
100 {
101 }
102 
103 
104 int EncodeJpeg(unsigned char* input_buffer, int row_width, int width, int height,
105  unsigned char* output_buffer, int output_buffer_size,
106  int jpeg_quality, bool grayscale)
107 {
108  int JpegSize = 0;
109 
110  MC_TRY_BEGIN
111  struct jpeg_compress_struct CompressInfo;
112  struct jpeg_error_mgr JErr;
113  struct jpeg_destination_mgr DestManager;
114 
115  DestManager.init_destination = InitJpegBuffer;
116  DestManager.empty_output_buffer = EmptyJpegBuffer;
117  DestManager.term_destination = FinalizeJpegBuffer;
118  DestManager.next_output_byte = (JOCTET*)output_buffer;
119  DestManager.free_in_buffer = output_buffer_size;
120 
121  CompressInfo.err = jpeg_std_error(&JErr);
122  CompressInfo.err->error_exit = AlternateExit;
123  CompressInfo.err->output_message = AlternatePrint;
124  jpeg_create_compress(&CompressInfo);
125  CompressInfo.dest = &DestManager;
126  CompressInfo.image_width = width;
127  CompressInfo.image_height = height;
128  if (grayscale)
129  {
130  CompressInfo.input_components = 1;
131  CompressInfo.in_color_space = JCS_GRAYSCALE;
132  } else {
133  CompressInfo.input_components = 3;
134  CompressInfo.in_color_space = JCS_RGB;
135  }
136 
137  jpeg_set_defaults(&CompressInfo);
138  jpeg_set_quality(&CompressInfo, jpeg_quality, true);
139  jpeg_start_compress(&CompressInfo, true);
140 
141  JSAMPROW RowPointer;
142 
143  while (CompressInfo.next_scanline < CompressInfo.image_height)
144  {
145  RowPointer = (JSAMPROW)&input_buffer[CompressInfo.next_scanline*row_width];
146  jpeg_write_scanlines(&CompressInfo, &RowPointer, 1);
147  }
148  jpeg_finish_compress(&CompressInfo);
149  // cppcheck-suppress unreadVariable
150  JpegSize = (unsigned char*)CompressInfo.dest->next_output_byte-output_buffer;
151  jpeg_destroy_compress(&CompressInfo);
152  MC_CATCH_BEGIN
153  return 0;
154  MC_CATCH_END
155  return JpegSize;
156 }
157 
158 
159 void InitSource(j_decompress_ptr)
160 {
161  // Nothing to do
162 }
163 
164 
165 boolean FillInputBuffer(j_decompress_ptr)
166 {
167  // Nothing to do
168  return true;
169 }
170 
171 void SkipInputData(j_decompress_ptr CompressInfo, long NumBytes)
172 {
173  CompressInfo->src->next_input_byte += NumBytes;
174  CompressInfo->src->bytes_in_buffer -= NumBytes;
175 }
176 
177 
178 void TermSource(j_decompress_ptr)
179 {
180  // Nothing to do
181 }
182 
183 
184 bool DecodeJpeg(const MCBinaryData& input_buffer, MCBinaryData& output_buffer,
185  unsigned int& width, unsigned int& height)
186 {
187  MC_TRY_BEGIN
188  struct jpeg_decompress_struct CompressInfo;
189  struct jpeg_error_mgr JErr;
190  struct jpeg_source_mgr SourceManager;
191 
192  CompressInfo.err = jpeg_std_error(&JErr);
193  CompressInfo.err->error_exit = AlternateExit;
194  CompressInfo.err->output_message = AlternatePrint;
195  jpeg_create_decompress(&CompressInfo);
196 
197  SourceManager.next_input_byte = (unsigned char*)input_buffer.GetData();
198  SourceManager.bytes_in_buffer = input_buffer.GetSize();
199  SourceManager.init_source = InitSource;
200  SourceManager.fill_input_buffer = FillInputBuffer;
201  SourceManager.skip_input_data = SkipInputData;
202  SourceManager.resync_to_restart = jpeg_resync_to_restart;
203  SourceManager.term_source = TermSource;
204  CompressInfo.src = &SourceManager;
205 
206  // No echo for error message when the JPEG header is checked
207  CompressInfo.err->output_message = AlternatePrintNoEcho;
208  if (jpeg_read_header(&CompressInfo, true) != JPEG_HEADER_OK)
209  {
210  jpeg_destroy_decompress(&CompressInfo);
211  return false;
212  }
213  CompressInfo.err->output_message = AlternatePrint;
214  CompressInfo.out_color_space = JCS_RGB;
215  width = CompressInfo.image_width;
216  height = CompressInfo.image_height;
217  output_buffer.Allocate(width*height*3);
218  jpeg_start_decompress(&CompressInfo);
219 
220  while (CompressInfo.output_scanline < height)
221  {
222  unsigned char* Row = (unsigned char*)output_buffer.GetData()+CompressInfo.output_scanline*width*3;
223 
224  if (jpeg_read_scanlines(&CompressInfo, &Row, 1) == 0)
225  {
226  jpeg_finish_decompress(&CompressInfo);
227  jpeg_destroy_decompress(&CompressInfo);
228  return false;
229  }
230  for (unsigned int i = 0; i < width; ++i)
231  {
232  char Swap = Row[0];
233 
234  Row[0] = Row[2];
235  Row[2] = Swap;
236  Row += 3;
237  }
238  }
239  jpeg_finish_decompress(&CompressInfo);
240  jpeg_destroy_decompress(&CompressInfo);
241  MC_CATCH_BEGIN
242  return false;
243  MC_CATCH_END
244  return true;
245 }
246 
247 const char* ImageFormatTypeStrsInit[] = {"jpeg", "png", "tiff", "ppm"};
248 }
249 
250 const MC::StringList ME::ImageFormatTypeStrs(boost::begin(ImageFormatTypeStrsInit),
251  boost::end(ImageFormatTypeStrsInit));
252 
253 MEImage::MEImage() : Image(nullptr)
254 {
255  _Init(16, 16, 1);
256 }
257 
258 MEImage::MEImage(int width, int height, int layers) : Image(nullptr)
259 {
260  _Init(width, height, layers);
261 }
262 
263 
264 MEImage::MEImage(const MEImage& other) : Image(nullptr)
265 {
266  _Copy(other);
267 }
268 
269 
271 {
272  if (Image)
273  {
274  cvReleaseImage(&Image);
275  Image = nullptr;
276  }
277 }
278 
279 
280 #ifndef __AIBO_BUILD__
281 bool MEImage::LoadFromFile(const std::string& file_name)
282 {
283  if (!MCFileExists(file_name))
284  {
285  MC_WARNING("File does not exist: %s", file_name.c_str());
286  return false;
287  }
288  IplImage* TempImg = cvLoadImage(file_name.c_str(), -1);
289 
290  if (!TempImg)
291  return false;
292 
293  RELEASE_IPLIMAGE(Image);
294  Image = TempImg;
295  // Correct the origin of the image
296  if (Image && Image->origin == 1)
297  {
298  MirrorVertical();
299  Image->origin = 0;
300  }
301  return true;
302 }
303 
304 
305 bool MEImage::SaveToFile(const std::string& file_name) const
306 {
307  return cvSaveImage(file_name.c_str(), Image) > 0;
308 }
309 #endif
310 
311 
313 {
314  cvSetZero(Image);
315 }
316 
317 
318 MEImage* MEImage::GetLayer(int layer_index) const
319 {
320  const int LayerIndex = MCBound(0, layer_index, Image->nChannels-1);
321  MEImage* NewImage = new MEImage(Image->width, Image->height, 1);
322 
323  cvSetImageCOI(Image, LayerIndex);
324  cvCopy(Image, NewImage->Image, nullptr);
325  cvSetImageCOI(Image, 0);
326  return NewImage;
327 }
328 
329 
330 void MEImage::SetLayer(const MEImage& layer, int layer_index)
331 {
332  const int LayerIndex = MCBound(0, layer_index, Image->nChannels-1);
333 
334  if (layer.GetWidth() != Image->width || layer.GetHeight() != Image->height)
335  {
336  MC_WARNING("Wrong layer dimensions (%dx%d <> %dx%d)",
337  layer.GetWidth(), layer.GetHeight(), Image->width, Image->height);
338  return;
339  }
340  if (layer.GetLayerCount() != 1)
341  {
342  MC_WARNING("More layers in the layer image (1 != %d)", layer.GetLayerCount());
343  return;
344  }
345  cvSetImageCOI(Image, LayerIndex);
346  cvCopy(layer.GetIplImage(), Image, nullptr);
347  cvSetImageCOI(Image, 0);
348 }
349 
350 
351 const IplImage* MEImage::GetIplImage() const
352 {
353  return Image;
354 }
355 
356 
357 void MEImage::SetIplImage(const IplImage* other)
358 {
359  if (!other)
360  return;
361 
362  if (Image)
363  {
364  RELEASE_IPLIMAGE(Image);
365  }
366  Image = cvCloneImage(other);
367  // Correct the origin of the image
368  if (Image->origin == 1)
369  {
370  MirrorVertical();
371  Image->origin = 0;
372  }
373 }
374 
375 
376 void MEImage::SetRawImageData(const MCBinaryData& image_data, int width, int height, int layer_count)
377 {
378  if (width*height*layer_count != image_data.GetSize())
379  {
380  MC_WARNING("No enough image data to be copied (%d != %d)",
381  width*height*layer_count, image_data.GetSize());
382  return;
383  }
384  _Init(width, height, layer_count);
385 
386  for (int y = 0; y < height; ++y)
387  {
388  int Start = GetRowWidth()*y;
389  int Start2 = width*layer_count*y;
390 
391  memcpy(&Image->imageData[Start], &image_data.GetData()[Start2], width*layer_count);
392  }
393 }
394 
395 
397 {
399 
400  for (int y = 0; y < GetHeight(); ++y)
401  {
402  int Start = GetRowWidth()*y;
403  int Start2 = GetWidth()*GetLayerCount()*y;
404 
405  memcpy(&RawData->GetData()[Start2], &Image->imageData[Start], GetWidth()*GetLayerCount());
406  }
407  return RawData;
408 }
409 
410 
411 MCBinaryData* MEImage::Compress(ME::ImageFormatType format) const
412 {
413  // Note: The jpeg format must be handled differently because the OpenCV's
414  // highgui module crashes when the colliding symbol of libjpeg library routes
415  // the encoding call to the internal jpegturbo library.
416  if (format == ME::JpegFormat)
417  {
418  MC::BinaryDataSPtr TempBuffer(new MCBinaryData(GetImageDataSize()));
419  MCBinaryData* JpegData = nullptr;
420  int FinalSize = 0;
421 
422  FinalSize = EncodeJpeg((unsigned char*)Image->imageData, Image->widthStep, GetWidth(), GetHeight(),
423  TempBuffer->GetData(), TempBuffer->GetSize(), 80, GetLayerCount() == 1);
424  JpegData = new MCBinaryData(FinalSize);
425  memcpy(JpegData->GetData(), TempBuffer->GetData(), FinalSize);
426  return JpegData;
427  }
428 #ifndef __AIBO_BUILD__
429  MCBinaryData* CompressedData = nullptr;
430  std::string FormatStr;
431  std::vector<uchar> Buffer;
432 
433  if (format == ME::PngFormat)
434  FormatStr = ".png";
435  if (format == ME::TiffFormat)
436  FormatStr = ".tiff";
437  if (format == ME::PpmFormat)
438  FormatStr = ".ppm";
439  cv::imencode(FormatStr, cv::Mat(Image, false), Buffer);
440  CompressedData = new MCBinaryData((int)Buffer.size());
441  memcpy(CompressedData->GetData(), Buffer.data(), (int)Buffer.size());
442  return CompressedData;
443 #else
444  return nullptr;
445 #endif
446 }
447 
448 
450 {
451  // Note: The jpeg format must be handled differently because the OpenCV's
452  // highgui module crashes when the colliding symbol of libjpeg library routes
453  // the decoding call to the internal jpegturbo library.
454  MC::BinaryDataSPtr DecodedData(new MCBinaryData);
455  unsigned int Width;
456  unsigned int Height;
457 
458  if (DecodeJpeg(data, *DecodedData, Width, Height))
459  {
460  SetRawImageData(*DecodedData, Width, Height, 3);
461  return true;
462  }
463 #ifndef __AIBO_BUILD__
464  cv::Mat Result;
465  IplImage TempImg;
466  std::vector<char> TempBuffer(data.GetSize());
467 
468  memcpy(TempBuffer.data(), data.GetData(), TempBuffer.size());
469  Result = cv::imdecode(TempBuffer, CV_LOAD_IMAGE_UNCHANGED);
470  if (Result.data == nullptr)
471  return false;
472 
473  RELEASE_IPLIMAGE(Image);
474  TempImg = (IplImage)Result;
475  Image = cvCloneImage(&TempImg);
476  return true;
477 #else
478  return false;
479 #endif
480 }
481 
482 
483 bool MEImage::operator==(const MEImage& other)
484 {
485  return Equal(other);
486 }
487 
488 
489 bool MEImage::operator!=(const MEImage& other)
490 {
491  return !operator==(other);
492 }
493 
494 
496 {
497  if (&other == this)
498  return *this;
499 
500  _Copy(other);
501  return *this;
502 }
503 
504 
505 int MEImage::GetWidth() const
506 {
507  return Image ? Image->width : 0;
508 }
509 
510 
512 {
513  return Image ? Image->widthStep : 0;
514 }
515 
516 
518 {
519  return Image ? Image->height : 0;
520 }
521 
522 
524 {
525  return Image ? Image->nChannels : 0;
526 }
527 
528 
530 {
531  return Image ? GetWidth()*GetHeight()*GetLayerCount() : 0;
532 }
533 
534 
535 float MEImage::GetRatio() const
536 {
537  return Image ? (float)Image->height / Image->width : 0;
538 }
539 
540 
541 void MEImage::Realloc(int width, int height)
542 {
543  Realloc(width, height, Image->nChannels);
544 }
545 
546 
547 void MEImage::Realloc(int width, int height, int layer_count)
548 {
549  _Init(width, height, layer_count);
550 }
551 
552 
553 void MEImage::Resize(int width, int height)
554 {
555  if (GetWidth() == width && GetHeight() == height)
556  return;
557 
558  const int Width = MCMax(width, 1);
559  const int Height = MCMax(height, 1);
560  IplImage* TempImg = cvCreateImage(cvSize(Width, Height), 8, Image->nChannels);
561 
562  cvResize(Image, TempImg, CV_INTER_NN);
563  RELEASE_IPLIMAGE(Image);
564  Image = TempImg;
565 }
566 
567 
568 void MEImage::ResizeScaleX(int width)
569 {
570  if (GetWidth() == width)
571  return;
572 
573  Resize(width, (int)((float)width*GetRatio()));
574 }
575 
576 
577 void MEImage::ResizeScaleY(int height)
578 {
579  if (GetHeight() == height)
580  return;
581 
582  Resize((int)((float)height*1/GetRatio()), height);
583 }
584 
585 
587 {
588  cvFlip(Image, nullptr, 1);
589 }
590 
591 
593 {
594  cvFlip(Image, nullptr, 0);
595 }
596 
597 
598 void MEImage::Crop(int x1, int y1, int x2, int y2)
599 {
600  const int X1 = MCBound(0, x1, GetWidth()-1);
601  const int Y1 = MCBound(0, y1, GetHeight()-1);
602  const int X2 = MCBound(0, x2, GetWidth()-1);
603  const int Y2 = MCBound(0, y2, GetHeight()-1);
604 
605  if (X2-X1 <= 0 || Y2-Y1 <= 0)
606  return;
607 
608  IplImage* TempImg = cvCreateImage(cvSize(X2-X1, Y2-Y1), 8, Image->nChannels);
609 
610  cvSetImageROI(Image, cvRect(X1, Y1, X2-X1, Y2-Y1));
611  cvCopy(Image, TempImg);
612  cvResetImageROI(Image);
613  RELEASE_IPLIMAGE(Image);
614  Image = TempImg;
615 }
616 
617 
618 void MEImage::PasteImageInside(int x, int y, MEImage& other)
619 {
620  const int X = MCBound(0, x, GetWidth()-1);
621  const int Y = MCBound(0, y, GetHeight()-1);
622  const int PasteWidth = (X+other.GetWidth() > GetWidth() ? GetWidth()-X : other.GetWidth());
623  const int PasteHeight = (Y+other.GetHeight() > GetHeight() ? GetHeight()-Y : other.GetHeight());
624 
625  if (Image->nChannels != other.GetLayerCount())
626  {
627  if (other.GetLayerCount() == 1 && Image->nChannels == 3)
628  {
629  other.ConvertToRGB();
630  }
631  if (Image->nChannels == 1 && other.GetLayerCount() == 3)
632  {
633  other.ConvertToGrayscale();
634  }
635  }
636  cvSetImageROI(Image, cvRect(X, Y, PasteWidth, PasteHeight));
637  cvCopy(other.GetIplImage(), Image);
638  cvResetImageROI(Image);
639 }
640 
641 
642 void MEImage::CopyImagePart(int x1, int y1, int x2, int y2, MEImage& other)
643 {
644  const int X1 = MCBound(0, x1, GetWidth()-1);
645  const int Y1 = MCBound(0, y1, GetHeight()-1);
646  const int X2 = MCBound(0, x2, GetWidth()-1);
647  const int Y2 = MCBound(0, y2, GetHeight()-1);
648 
649  if (X2-X1 <= 0 || Y2-Y1 <= 0)
650  return;
651 
652  IplImage* TempImg = cvCreateImage(cvSize(X2-X1, Y2-Y1), 8, Image->nChannels);
653 
654  cvSetImageROI(Image, cvRect(X1, Y1, X2-X1, Y2-Y1));
655  cvCopy(Image, TempImg);
656  cvResetImageROI(Image);
657  RELEASE_IPLIMAGE(other.Image);
658  other.Image = TempImg;
659 }
660 
661 
662 void MEImage::DrawLine(int x0, int y0, int x1, int y1, const MEColor& color)
663 {
664  const int X0 = MCBound(0, x0, GetWidth()-1);
665  const int Y0 = MCBound(0, y0, GetHeight()-1);
666  const int X1 = MCBound(0, x1, GetWidth()-1);
667  const int Y1 = MCBound(0, y1, GetHeight()-1);
668  CvPoint Point1, Point2;
669 
670  Point1.x = X0;
671  Point1.y = Y0;
672  Point2.x = X1;
673  Point2.y = Y1;
674  cvLine(Image, Point1, Point2, CV_RGB(color.Blue, color.Green, color.Red), 1, 8);
675 }
676 
677 
678 void MEImage::DrawRectangle(int x0, int y0, int x1, int y1, const MEColor& color, bool fill)
679 {
680  const int X0 = MCBound(0, x0, GetWidth()-1);
681  const int Y0 = MCBound(0, y0, GetHeight()-1);
682  const int X1 = MCBound(0, x1, GetWidth()-1);
683  const int Y1 = MCBound(0, y1, GetHeight()-1);
684  CvPoint Point1, Point2;
685 
686  Point1.x = X0;
687  Point1.y = Y0;
688  Point2.x = X1;
689  Point2.y = Y1;
690  cvRectangle(Image, Point1, Point2, CV_RGB(color.Blue, color.Green, color.Red),
691  (fill ? -1 : 1), 8);
692 }
693 
694 
695 void MEImage::DrawCircle(int x, int y, int radius, const MEColor& color, bool fill)
696 {
697  const int X = MCBound(0, x, GetWidth()-1);
698  const int Y = MCBound(0, y, GetHeight()-1);
699  const int Radius = MCMax(radius, 1);
700  CvPoint Point;
701 
702  Point.x = X;
703  Point.y = Y;
704  cvCircle(Image, Point, Radius, CV_RGB(color.Blue, color.Green, color.Red), (fill ? -1 : 1));
705 }
706 
707 
708 void MEImage::DrawText(int x, int y, const std::string& text, float scale, const MEColor& color)
709 {
710  const int X = MCBound(0, x, GetWidth()-1);
711  const int Y = MCBound(0, y, GetHeight()-1);
712  cv::Mat TempImage(Image, false);
713 
714  cv::putText(TempImage, text.c_str(), cv::Point(X, Y), cv::FONT_HERSHEY_TRIPLEX,
715  (double)scale, CV_RGB(color.Blue, color.Green, color.Red));
716 }
717 
718 
719 void MEImage::Erode(int iteration_count)
720 {
721  if (iteration_count < 1)
722  return;
723 
724  IplImage* TempImg = cvCreateImage(cvSize(Image->width, Image->height), 8, Image->nChannels);
725 
726  cvErode(Image, TempImg, nullptr, iteration_count);
727  RELEASE_IPLIMAGE(Image);
728  Image = TempImg;
729 }
730 
731 
732 void MEImage::Dilate(int iteration_count)
733 {
734  if (iteration_count < 1)
735  return;
736 
737  IplImage* TempImg = cvCreateImage(cvSize(Image->width, Image->height), 8, Image->nChannels);
738 
739  cvDilate(Image, TempImg, nullptr, iteration_count);
740  RELEASE_IPLIMAGE(Image);
741  Image = TempImg;
742 }
743 
744 
746 {
747  SmoothAdvanced(ME::MedianSmoothing, 3);
748 }
749 
750 
751 void MEImage::SmoothAdvanced(ME::SmoothType filter_mode, int matrix_size)
752 {
753  IplImage* TempImg = cvCreateImage(cvSize(Image->width, Image->height), 8, Image->nChannels);
754  int SmoothType = CV_MEDIAN;
755 
756  if (filter_mode == ME::BlurSmoothing)
757  SmoothType = CV_BLUR;
758  if (filter_mode == ME::MedianSmoothing)
759  SmoothType = CV_MEDIAN;
760  if (filter_mode == ME::GaussianSmoothing)
761  SmoothType = CV_GAUSSIAN;
762 
763  cvSmooth(Image, TempImg, SmoothType, matrix_size, matrix_size, 0);
764  RELEASE_IPLIMAGE(Image);
765  Image = TempImg;
766 }
767 
768 
769 void MEImage::Canny(double threshold1, double threshold2, int aperture_size)
770 {
771  if (Image->nChannels > 1)
772  {
774  }
775  IplImage* TempImg = cvCreateImage(cvSize(Image->width, Image->height), 8, Image->nChannels);
776 
777  cvCanny(Image, TempImg, threshold1, threshold2, aperture_size);
778  RELEASE_IPLIMAGE(Image);
779  Image = TempImg;
780 }
781 
782 
784 {
785  if (Image->nChannels != 1)
786  {
788  }
789  IplImage* TempImg = cvCreateImage(cvSize(Image->width, Image->height), IPL_DEPTH_16S, 1);
790 
791  cvLaplace(Image, TempImg, 3);
792  cvConvertScale(TempImg, Image, 1, 0);
793  RELEASE_IPLIMAGE(TempImg);
794 }
795 
796 
797 void MEImage::Quantize(int level_count)
798 {
799  const int LevelCount = MCBound(1, level_count, 255);
800  unsigned char* ImageData = (unsigned char*)Image->imageData;
801 
802  for (int i = Image->widthStep*Image->height-1; i >= 0; --i)
803  {
804  ImageData[i] = ImageData[i] / (256 / LevelCount)*(256 / LevelCount);
805  }
806 }
807 
808 
809 void MEImage::Threshold(int threshold_limit)
810 {
811  const int ThresholdLimit = MCBound(1, threshold_limit, 255);
812  unsigned char* ImageData = (unsigned char*)Image->imageData;
813 
814  for (int i = Image->widthStep*Image->height-1; i >= 0; --i)
815  {
816  if (ImageData[i] < ThresholdLimit)
817  ImageData[i] = 0;
818  }
819 }
820 
821 
823 {
824  if (Image->nChannels != 1)
825  {
827  }
828  IplImage* TempImg = cvCreateImage(cvSize(Image->width, Image->height), 8, Image->nChannels);
829 
830  cvAdaptiveThreshold(Image, TempImg, 25,
831  CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 7, -7);
832  RELEASE_IPLIMAGE(Image);
833  Image = TempImg;
834 }
835 
836 
837 void MEImage::Mask(MEImage& other)
838 {
839  if (other.GetWidth() != Image->width || other.GetHeight() != Image->height)
840  {
841  MC_WARNING("Image properties are different for masking");
842  return;
843  }
844  if (other.GetLayerCount() != 3 && Image->nChannels == 3)
845  {
846  other.ConvertToRGB();
847  }
848  if (other.GetLayerCount() != 1 && Image->nChannels == 1)
849  {
850  other.ConvertToGrayscale();
851  }
852  unsigned char* ImageData = (unsigned char*)Image->imageData;
853  unsigned char* MaskImageData = (unsigned char*)other.Image->imageData;
854 
855  for (int i = Image->widthStep*Image->height-1; i >= 0; --i)
856  {
857  if (MaskImageData[i] == 0)
858  ImageData[i] = 0;
859  }
860 }
861 
862 
863 void MEImage::ConvertToColorSpace(ME::ColorSpaceType mode)
864 {
865  if (Image->nChannels == 1)
866  return;
867 
868  if (mode == ME::RGBtoYUV)
869  {
870  ComputeColorSpace(ME::RGBtoYUV);
871  return;
872  } else
873  if (mode == ME::RGBtoYIQ)
874  {
875  ComputeColorSpace(ME::RGBtoYIQ);
876  return;
877  } else
878  if (mode == ME::RGBtorgI)
879  {
880  unsigned char* ImageData = (unsigned char*)Image->imageData;
881  int WidthStep = Image->widthStep;
882  int RowStart = 0;
883 
884  for (int y = Image->height-1; y >= 0; --y)
885  {
886  for (int x = (Image->width-1)*3; x >= 0; x -= 3)
887  {
888  int r = 0;
889  int g = 0;
890  int I = 0;
891 
892  I = (int)ImageData[RowStart+x]+(int)ImageData[RowStart+x+1]+(int)ImageData[RowStart+x+2];
893  r = (int)((float)ImageData[RowStart+x] / I*255);
894  g = (int)((float)ImageData[RowStart+x+1] / I*255);
895  ImageData[RowStart+x] = (unsigned char)r;
896  ImageData[RowStart+x+1] = (unsigned char)g;
897  ImageData[RowStart+x+2] = (unsigned char)(I / 3);
898  }
899  RowStart += WidthStep;
900  }
901  return;
902  }
903  IplImage* TempImg = cvCreateImage(cvSize(Image->width, Image->height), 8, Image->nChannels);
904  int Conversion = CV_RGB2XYZ;
905 
906  if (mode == ME::RGBtoXYZCIED65)
907  Conversion = CV_RGB2XYZ;
908  if (mode == ME::XYZCIED65toRGB)
909  Conversion = CV_XYZ2RGB;
910  if (mode == ME::RGBtoHSV)
911  Conversion = CV_RGB2HSV;
912  if (mode == ME::HSVtoRGB)
913  Conversion = CV_HSV2RGB;
914  if (mode == ME::RGBtoHLS)
915  Conversion = CV_RGB2HLS;
916  if (mode == ME::HLStoRGB)
917  Conversion = CV_HLS2RGB;
918  if (mode == ME::RGBtoCIELab)
919  Conversion = CV_RGB2Lab;
920  if (mode == ME::CIELabtoRGB)
921  Conversion = CV_Lab2RGB;
922  if (mode == ME::RGBtoCIELuv)
923  Conversion = CV_RGB2Luv;
924  if (mode == ME::CIELuvtoRGB)
925  Conversion = CV_Luv2RGB;
926 
927  cvCvtColor(Image, TempImg, Conversion);
928  RELEASE_IPLIMAGE(Image);
929  Image = TempImg;
930 }
931 
932 
934 {
935  if (Image->nChannels == 1)
936  return;
937 
938  IplImage* TempImg = cvCreateImage(cvSize(Image->width, Image->height), 8, 1);
939 
940  cvCvtColor(Image, TempImg, CV_RGB2GRAY);
941  RELEASE_IPLIMAGE(Image);
942  Image = TempImg;
943 }
944 
945 
947 {
948  if (Image->nChannels != 1)
949  return;
950 
951  IplImage* TempImg = cvCreateImage(cvSize(Image->width, Image->height), 8, 3);
952 
953  cvCvtColor(Image, TempImg, CV_GRAY2RGB);
954  RELEASE_IPLIMAGE(Image);
955  Image = TempImg;
956 }
957 
958 
960 {
961  if (Image->nChannels != 3)
962  return;
963 
964  cvCvtColor(Image, Image, CV_RGB2BGR);
965 }
966 
967 
968 void MEImage::LBP(ME::LBPType mode)
969 {
970  if (Image->nChannels > 1)
971  {
973  }
974  unsigned char* ImageData = (unsigned char*)Image->imageData;
975  IplImage* TempImg = cvCreateImage(cvSize(Image->width, Image->height), 8, 1);
976  unsigned char* TempImgData = (unsigned char*)TempImg->imageData;
977  int WidthStep = Image->widthStep;
978  int WidthStep_2 = Image->widthStep*2;
979 
980  cvSetZero(TempImg);
981  if (mode == ME::NormalLBP)
982  {
983  for (int i = Image->widthStep*(Image->height-2)-1; i >= Image->widthStep+1; --i)
984  {
985  TempImgData[i] =
986  (ImageData[i] <= ImageData[i-Image->widthStep-1])+
987  ((ImageData[i] <= ImageData[i-Image->widthStep])*2)+
988  ((ImageData[i] <= ImageData[i-Image->widthStep+1])*4)+
989  ((ImageData[i] <= ImageData[i-1])*8)+
990  ((ImageData[i] <= ImageData[i+1])*16)+
991  ((ImageData[i] <= ImageData[i+Image->widthStep-1])*32)+
992  ((ImageData[i] <= ImageData[i+Image->widthStep])*64)+
993  ((ImageData[i] <= ImageData[i+Image->widthStep+1])*128);
994  }
995  }
996  if (mode == ME::SpecialLBP)
997  {
998  for (int i = Image->widthStep*(Image->height - 3) - 2; i >= Image->widthStep*2 + 2; --i)
999  {
1000  int CenterPixel = (ImageData[i + 1] + ImageData[i - 1] +
1001  ImageData[i - WidthStep] + ImageData[i + WidthStep])/4;
1002  TempImgData[i] = ((CenterPixel <= (ImageData[i - (WidthStep_2) - 2] +
1003  ImageData[i - (WidthStep_2) - 1] +
1004  ImageData[i - WidthStep - 2] +
1005  ImageData[i - WidthStep - 1])/4)) +
1006  ((CenterPixel <= (ImageData[i - WidthStep] +
1007  ImageData[i - (WidthStep_2)])/2)*2) +
1008  ((CenterPixel <= ((ImageData[i - (WidthStep_2) + 2] +
1009  ImageData[i - (WidthStep_2) + 1] +
1010  ImageData[i - WidthStep + 2] +
1011  ImageData[i - WidthStep + 1])/4))*4) +
1012  ((CenterPixel <= (ImageData[i - 1] +
1013  ImageData[i - 2])/2)*8) +
1014  ((CenterPixel <= (ImageData[i + 1] +
1015  ImageData[i + 2])/2)*16) +
1016  ((CenterPixel <= ((ImageData[i + (WidthStep_2) - 2] +
1017  ImageData[i + (WidthStep_2) - 1] +
1018  ImageData[i + WidthStep - 2] +
1019  ImageData[i + WidthStep - 1])/4))*32) +
1020  ((CenterPixel <= (ImageData[i + WidthStep] +
1021  ImageData[i - WidthStep_2])/2)*64) +
1022  ((CenterPixel <= ((ImageData[i + (WidthStep_2) + 2] +
1023  ImageData[i + (WidthStep_2) + 1] +
1024  ImageData[i + WidthStep + 2] +
1025  ImageData[i + WidthStep + 1])/4))*128);
1026  }
1027  }
1028  RELEASE_IPLIMAGE(Image);
1029  Image = TempImg;
1030 }
1031 
1032 
1033 void MEImage::Binarize(int threshold)
1034 {
1035  unsigned char* ImageData = (unsigned char*)Image->imageData;
1036 
1037  for (int i = Image->height*Image->widthStep-1; i >= 0; --i)
1038  {
1039  if (ImageData[i] >= threshold)
1040  {
1041  ImageData[i] = 255;
1042  } else {
1043  ImageData[i] = 0;
1044  }
1045  }
1046 }
1047 
1048 
1049 void MEImage::Subtract(const MEImage& other, ME::SubtractionType mode)
1050 {
1051  if (other.GetWidth() != Image->width || other.GetHeight() != Image->height ||
1052  other.GetLayerCount() != Image->nChannels)
1053  {
1054  MC_WARNING("Image properties are different for subtraction.");
1055  return;
1056  }
1057  unsigned char* ImageData = (unsigned char*)Image->imageData;
1058  const unsigned char* DstData = (unsigned char*)other.Image->imageData;
1059  int WidthStep = Image->widthStep;
1060  int RowStart = 0;
1061 
1062  if (mode == ME::NormalSubtraction)
1063  {
1064  for (int y = Image->height-1; y >= 0; --y)
1065  {
1066  for (int x = Image->width*Image->nChannels-1; x >= 0; --x)
1067  {
1068  if (ImageData[RowStart+x]-DstData[RowStart+x] < 0)
1069  ImageData[RowStart+x] = 0;
1070  else
1071  ImageData[RowStart+x] = ImageData[RowStart+x]-DstData[RowStart+x];
1072  }
1073  RowStart += WidthStep;
1074  }
1075  }
1076  if (mode == ME::AbsolutSubtraction)
1077  {
1078  for (int y = Image->height-1; y >= 0; --y)
1079  {
1080  for (int x = Image->width*Image->nChannels-1; x >= 0; --x)
1081  {
1082  if (ImageData[RowStart+x]-DstData[RowStart+x] < 0)
1083  ImageData[RowStart+x] = -ImageData[RowStart+x]+DstData[RowStart+x];
1084  else
1085  ImageData[RowStart+x] = ImageData[RowStart+x]-DstData[RowStart+x];
1086  }
1087  RowStart += WidthStep;
1088  }
1089  }
1090 }
1091 
1092 
1094 {
1095  if (other.GetWidth() != Image->width || other.GetHeight() != Image->height ||
1096  other.GetLayerCount() != Image->nChannels)
1097  {
1098  MC_WARNING("Image properties are different for multiplication.");
1099  return;
1100  }
1101  unsigned char* ImageData = (unsigned char*)Image->imageData;
1102  unsigned char* DstData = (unsigned char*)other.Image->imageData;
1103 
1104  for (int i = Image->height*Image->widthStep-1; i >= 0; --i)
1105  {
1106  if (ImageData[i] >= 128 && DstData[i] >= 128)
1107  {
1108  float Result = (float)ImageData[i] / 128*DstData[i] / 128;
1109 
1110  if (Result >= 1)
1111  {
1112  ImageData[i] = 255;
1113  } else {
1114  ImageData[i] = 0;
1115  }
1116  } else {
1117  ImageData[i] = 0;
1118  }
1119  }
1120 }
1121 
1122 
1123 void MEImage::Addition(MEImage& other, ME::AdditionType mode)
1124 {
1125  if (other.GetWidth() != Image->width || other.GetHeight() != Image->height ||
1126  other.GetLayerCount() != Image->nChannels)
1127  {
1128  MC_WARNING("Image properties are different for addition.");
1129  return;
1130  }
1131  unsigned char* ImageData = (unsigned char*)Image->imageData;
1132  unsigned char* SrcData = (unsigned char*)other.Image->imageData;
1133  int WidthStep = Image->widthStep;
1134  int RowStart = 0;
1135 
1136  if (mode == ME::AverageAddition)
1137  {
1138  for (int i = Image->height*Image->widthStep-1; i >= 0; --i)
1139  {
1140  ImageData[i] = (ImageData[i]+SrcData[i]) / 2;
1141  }
1142  } else
1143  if (mode == ME::UnionAddition)
1144  {
1145  for (int i = Image->height*Image->widthStep-1; i >= 0; --i)
1146  {
1147  if (SrcData[i] > ImageData[i])
1148  ImageData[i] = SrcData[i];
1149  }
1150  } else
1151  if (mode == ME::MaskAddition && other.GetLayerCount() == 1)
1152  {
1153  for (int y = Image->height-1; y >= 0; --y)
1154  {
1155  for (int x = Image->width*Image->nChannels-1; x >= 0; --x)
1156  {
1157  if (SrcData[RowStart+x] > 0)
1158  ImageData[RowStart+x] = SrcData[RowStart+x];
1159  }
1160  RowStart += WidthStep;
1161  }
1162  } else
1163  if (mode == ME::MaskAddition && other.GetLayerCount() == 3)
1164  {
1165  for (int y = Image->height-1; y >= 0; --y)
1166  {
1167  for (int x = Image->width*Image->nChannels-4; x >= 0; x -= 3)
1168  {
1169  if (SrcData[RowStart+x] > 0 || SrcData[RowStart+x+1] > 0 || SrcData[RowStart+x+2] > 0)
1170  {
1171  ImageData[RowStart+x] = SrcData[RowStart+x];
1172  ImageData[RowStart+x+1] = SrcData[RowStart+x+1];
1173  ImageData[RowStart+x+2] = SrcData[RowStart+x+2];
1174  }
1175  }
1176  RowStart += WidthStep;
1177  }
1178  }
1179 }
1180 
1181 
1183 {
1184  IplImage* TempImg = cvCreateImage(cvSize(Image->width, Image->height), 8, Image->nChannels);
1185  unsigned char* ImageData = (unsigned char*)Image->imageData;
1186  unsigned char* DstData = (unsigned char*)TempImg->imageData;
1187  int sum = 0;
1188  int xy = 0;
1189  int ywidth = Image->widthStep;
1190 
1191  for (int y = Image->height-1; y >= 0; --y)
1192  for (int x = Image->width-1; x >= 0; --x)
1193  {
1194  xy = y*ywidth+x*Image->nChannels;
1195 
1196  for (int l = Image->nChannels-1; l >= 0; --l)
1197  {
1198  if ((ImageData[xy+l] > 0) && (x > 0) && (y > 0) && (x < Image->width-1) && (y < Image->height-1))
1199  {
1200  sum = (ImageData[xy-ywidth-Image->nChannels+l] > 0)+
1201  (ImageData[xy-ywidth+l] > 0)+
1202  (ImageData[xy-ywidth+Image->nChannels+l] > 0)+
1203  (ImageData[xy-Image->nChannels+l] > 0)+
1204  (ImageData[xy+Image->nChannels+l] > 0)+
1205  (ImageData[xy+ywidth-Image->nChannels+l] > 0)+
1206  (ImageData[xy+ywidth+l] > 0)+
1207  (ImageData[xy+ywidth+Image->nChannels+l] > 0);
1208 
1209  if (sum > 3)
1210  {
1211  DstData[xy+l] = 255;
1212  } else {
1213  DstData[xy+l] = 0;
1214  }
1215  } else {
1216  DstData[xy+l] = 0;
1217  }
1218  }
1219  }
1220  RELEASE_IPLIMAGE(Image);
1221  Image = TempImg;
1222 }
1223 
1224 
1225 float MEImage::DifferenceAreas(MEImage& reference, int difference) const
1226 {
1227  if (reference.GetWidth() != GetWidth() || reference.GetHeight() != GetHeight() ||
1228  reference.GetLayerCount() != GetLayerCount())
1229  {
1230  MC_WARNING("Image dimensions or channels are different for area statistics.");
1231  return -1.0;
1232  }
1233  int Difference = MCBound(0, difference, 100);
1234  int Pixels = 0;
1235  unsigned char* OrigImgData = (unsigned char*)Image->imageData;
1236  unsigned char* RefImgData = (unsigned char*)reference.Image->imageData;
1237  int WidthStep = Image->widthStep;
1238  int RowStart = 0;
1239 
1240  for (int y = Image->height-1; y >= 0; --y)
1241  {
1242  for (int x = Image->width*Image->nChannels-1; x >= 0; --x)
1243  {
1244  if (abs(OrigImgData[RowStart+x]-RefImgData[RowStart+x]) > Difference)
1245  Pixels++;
1246  }
1247  RowStart += WidthStep;
1248  }
1249  return (float)Pixels / ((float)Image->height*Image->widthStep*100);
1250 }
1251 
1252 
1254 {
1255  if (reference.GetWidth() != GetWidth() || reference.GetHeight() != GetHeight() ||
1256  reference.GetLayerCount() != GetLayerCount())
1257  {
1258  MC_WARNING("Image dimensions or channels are different.");
1259  return -1;
1260  }
1261  if (Image->height == 0 || Image->widthStep == 0)
1262  return 0;
1263 
1264  int Difference = 0;
1265  unsigned char* OrigImgData = (unsigned char*)Image->imageData;
1266  unsigned char* RefImgData = (unsigned char*)reference.Image->imageData;
1267  int WidthStep = Image->widthStep;
1268  int RowStart = 0;
1269 
1270  for (int y = Image->height-1; y >= 0; --y)
1271  {
1272  for (int x = Image->width*Image->nChannels-1; x >= 0; --x)
1273  {
1274  Difference += MCAbs(OrigImgData[RowStart+x]-RefImgData[RowStart+x]);
1275  }
1276  RowStart += WidthStep;
1277  }
1278  Difference = Difference / (Image->height*Image->widthStep);
1279  return Difference;
1280 }
1281 
1282 
1284 {
1285  if (other.GetWidth() != Image->width || other.GetHeight() != Image->height ||
1286  other.GetLayerCount() != Image->nChannels)
1287  {
1288  MC_WARNING("Image properties are different for minimum.");
1289  return;
1290  }
1291  unsigned char* ImageData = (unsigned char*)Image->imageData;
1292  unsigned char* SecData = (unsigned char*)other.Image->imageData;
1293  int WidthStep = Image->widthStep;
1294  int RowStart = 0;
1295 
1296  for (int y = Image->height-1; y >= 0; --y)
1297  {
1298  for (int x = Image->width*Image->nChannels-1; x >= 0; --x)
1299  {
1300  ImageData[RowStart+x] = (ImageData[RowStart+x] > SecData[RowStart+x] ?
1301  SecData[RowStart+x] : ImageData[RowStart+x]);
1302  }
1303  RowStart += WidthStep;
1304  }
1305 }
1306 
1307 
1309 {
1310  unsigned char* ImageData = (unsigned char*)Image->imageData;
1311  int WidthStep = Image->widthStep;
1312  int RowStart = 0;
1313  int BrightnessLevel = 0;
1314 
1315  for (int y = Image->height-1; y >= 0; --y)
1316  {
1317  for (int x = Image->width*Image->nChannels-1; x >= 0; --x)
1318  {
1319  BrightnessLevel += (int)ImageData[RowStart+x];
1320  }
1321  RowStart += WidthStep;
1322  }
1323  return (float)BrightnessLevel / ((float)GetWidth()*GetHeight()*GetLayerCount());
1324 }
1325 
1326 
1327 void MEImage::Rotate(int x, int y, float degree)
1328 {
1329  const int X = MCBound(0, x, GetWidth()-1);
1330  const int Y = MCBound(0, y, GetHeight()-1);
1331 
1332  cv::Mat Matrix = cv::getRotationMatrix2D(cv::Point(X, Y), degree, 1.0);
1333  IplImage* TempImg = cvCreateImage(cvSize(GetWidth(), GetHeight()), 8, Image->nChannels);
1334 
1335  cv::warpAffine(cv::Mat(Image, false), cv::Mat(TempImg, false), Matrix, cv::Size(GetWidth(), GetHeight()));
1336  RELEASE_IPLIMAGE(Image);
1337  Image = TempImg;
1338 }
1339 
1340 
1341 bool MEImage::Equal(const MEImage& other) const
1342 {
1343  return Equal(other, 1);
1344 }
1345 
1346 
1347 bool MEImage::Equal(const MEImage& other, int max_diff) const
1348 {
1349  if (&other == this)
1350  return true;
1351 
1352  if (other.GetWidth() != Image->width || other.GetHeight() != Image->height ||
1353  other.GetLayerCount() != Image->nChannels)
1354  {
1355  return false;
1356  }
1357  unsigned char* ImageData = (unsigned char*)Image->imageData;
1358  unsigned char* RefData = (unsigned char*)other.Image->imageData;
1359  int WidthStep = Image->widthStep;
1360  int RowStart = 0;
1361 
1362  for (int y = Image->height-1; y >= 0; --y)
1363  {
1364  for (int x = Image->width*Image->nChannels-1; x >= 0; --x)
1365  {
1366  if (MCAbs(ImageData[RowStart+x]-RefData[RowStart+x]) >= max_diff)
1367  return false;
1368  }
1369  RowStart += WidthStep;
1370  }
1371  return true;
1372 }
1373 
1374 
1375 unsigned char MEImage::GrayscalePixel(int x, int y) const
1376 {
1377  const int X = MCBound(0, x, GetWidth()-1);
1378  const int Y = MCBound(0, y, GetHeight()-1);
1379 
1380  int Sum = 0;
1381  unsigned char* ImageData = (unsigned char*)Image->imageData;
1382 
1383  for (int l = 0; l < Image->nChannels; l++)
1384  {
1385  Sum = Sum+(int)ImageData[Y*Image->width*Image->nChannels+X*Image->nChannels+l];
1386  }
1387  return (unsigned char)((float)Sum / Image->nChannels);
1388 }
1389 
1390 
1392 {
1393  return cvSumPixels(Image);
1394 }
1395 
1396 
1398 {
1399  if (GetLayerCount() != 1)
1400  {
1401  MC_WARNING("Holes can be filled only in binary images.");
1402  return;
1403  }
1404  // Taken from http://stackoverflow.com/a/16015873
1405  CvScalar White = CV_RGB(255, 255, 255);
1406  IplImage* Dst = cvCreateImage(cvGetSize(Image), 8, 1);
1407  CvMemStorage* Storage = cvCreateMemStorage(0);
1408  CvSeq* Contour = 0;
1409 
1410  cvFindContours(Image, Storage, &Contour, sizeof(CvContour),
1411  CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
1412  cvZero(Dst);
1413 
1414  for (; Contour != 0; Contour = Contour->h_next)
1415  {
1416  cvDrawContours(Dst, Contour, White, White, 0, CV_FILLED);
1417  }
1418  cvInRangeS(Dst, White, White, Image);
1419  cvFree(&Dst);
1420 }
1421 
1422 
1424 {
1425  cvNot(Image, Image);
1426 }
1427 
1428 
1429 void MEImage::GammaCorrection(float gamma_level)
1430 {
1431  unsigned char Table[256];
1432 
1433  for (unsigned int i = 0; i < 256; i++)
1434  {
1435  Table[i] = (unsigned char)MCBound(0, (int)(pow((float)i / 255, gamma_level)*255), 255);
1436  }
1437  unsigned char* ImageData = (unsigned char*)Image->imageData;
1438  int WidthStep = Image->widthStep;
1439  int RowStart = 0;
1440 
1441  for (int y = Image->height-1; y >= 0; --y)
1442  {
1443  for (int x = Image->width*Image->nChannels-1; x >= 0; --x)
1444  {
1445  ImageData[RowStart+x] = Table[ImageData[RowStart+x]];
1446  }
1447  RowStart += WidthStep;
1448  }
1449 }
1450 
1451 
1452 bool MEImage::_Copy(const MEImage& other)
1453 {
1454  if (&other == this)
1455  return true;
1456 
1457  if (Image)
1458  {
1459  RELEASE_IPLIMAGE(Image);
1460  }
1461  Image = cvCloneImage(other.GetIplImage());
1462  return true;
1463 }
1464 
1465 
1466 void MEImage::_Init(int width, int height, int layer_count)
1467 {
1468  if (Image)
1469  RELEASE_IPLIMAGE(Image);
1470 
1471  if (width < 1 || height < 1 || (layer_count != 1 && layer_count != 3))
1472  {
1473  Image = cvCreateImage(cvSize(16, 16), 8, 1);
1474  return;
1475  }
1476  Image = cvCreateImage(cvSize(width, height), 8, layer_count);
1477 }
1478 
1479 
1480 void MEImage::ComputeColorSpace(ME::ColorSpaceType mode)
1481 {
1482  if (Image->nChannels != 3)
1483  {
1484  MC_WARNING("Image must have three color channels (%d != 3)", Image->nChannels);
1485  return;
1486  }
1487  if (mode != ME::RGBtoYUV && mode != ME::RGBtoYIQ)
1488  {
1489  MC_WARNING("Color space conversion is not supported (%d)", (int)mode);
1490  return;
1491  }
1492  float TransformMatrix[3][3];
1493  IplImage* TempImg = cvCreateImage(cvSize(Image->width, Image->height), 8, Image->nChannels);
1494 
1495  for (int i = 0; i < 3; i++)
1496  for (int i1 = 0; i1 < 3; i1++)
1497  {
1498  if (mode == ME::RGBtoYUV)
1499  TransformMatrix[i][i1] = RGBtoYUVMatrix[i][i1];
1500  if (mode == ME::RGBtoYIQ)
1501  TransformMatrix[i][i1] = RGBtoYIQMatrix[i][i1];
1502  }
1503  float xmin = 0.0;
1504  float xmax = 0.0;
1505  float ymin = 0.0;
1506  float ymax = 0.0;
1507  float zmin = 0.0;
1508  float zmax = 0.0;
1509 
1510  if (mode == ME::RGBtoYUV)
1511  {
1512  xmin = 0.0;
1513  xmax = 255.0;
1514  ymin = -111.18;
1515  ymax = 111.18;
1516  zmin = -156.825;
1517  zmax = 156.825;
1518  }
1519  if (mode == ME::RGBtoYIQ)
1520  {
1521  xmin = 0.0;
1522  xmax = 255.0;
1523  ymin = -151.98;
1524  ymax = 151.98;
1525  zmin = -133.365;
1526  zmax = 133.365;
1527  }
1528  unsigned char* SrcData = (unsigned char*)Image->imageData;
1529  unsigned char* DstData = (unsigned char*)TempImg->imageData;
1530 
1531  for (int i = Image->widthStep*Image->height-1; i >= 0; i-=3)
1532  {
1533  float x = (float)SrcData[i]*TransformMatrix[0][0]+
1534  (float)SrcData[i+1]*TransformMatrix[0][1]+
1535  (float)SrcData[i+2]*TransformMatrix[0][2];
1536  float y = (float)SrcData[i]*TransformMatrix[1][0]+
1537  (float)SrcData[i+1]*TransformMatrix[1][1]+
1538  (float)SrcData[i+2]*TransformMatrix[1][2];
1539  float z = (float)SrcData[i]*TransformMatrix[2][0]+
1540  (float)SrcData[i+1]*TransformMatrix[2][1]+
1541  (float)SrcData[i+2]*TransformMatrix[2][2];
1542 
1543  x = xmax-xmin != 0.0 ? 255.0 : (x-xmin) / (xmax-xmin)*255.0;
1544  y = ymax-ymin != 0.0 ? 255.0 : (y-xmin) / (ymax-ymin)*255.0;
1545  z = zmax-zmin != 0.0 ? 255.0 : (z-xmin) / (zmax-zmin)*255.0;
1546 
1547  DstData[i] = (unsigned char)MCBound(0, (int)x, 255);
1548  DstData[i+1] = (unsigned char)MCBound(0, (int)y, 255);
1549  DstData[i+2] = (unsigned char)MCBound(0, (int)z, 255);
1550  }
1551  RELEASE_IPLIMAGE(Image);
1552  Image = TempImg;
1553 }
int GetWidth() const
Get the image width.
Definition: MEImage.cpp:505
void Threshold(int threshold_limit)
Threshold function.
Definition: MEImage.cpp:809
void _Init(int width, int height, int layer_count)
Initialize the image data.
Definition: MEImage.cpp:1466
void Addition(MEImage &other, ME::AdditionType mode)
Addition of an image and the internal picture.
Definition: MEImage.cpp:1123
void PasteImageInside(int x, int y, MEImage &other)
Copy image data from an other picture inside.
Definition: MEImage.cpp:618
bool operator!=(const MEImage &other)
Inequality operator.
Definition: MEImage.cpp:489
void ResizeScaleX(int width)
Resize image with new width.
Definition: MEImage.cpp:568
Binary data class.
MEImage()
Class constructor.
Definition: MEImage.cpp:253
void DrawRectangle(int x0, int y0, int x1, int y1, const MEColor &color, bool fill=true)
Draw a rectangle.
Definition: MEImage.cpp:678
int GetLayerCount() const
Get the image layer count.
Definition: MEImage.cpp:523
void SetIplImage(const IplImage *other)
Set the internal IplImage.
Definition: MEImage.cpp:357
bool SaveToFile(const std::string &file_name) const
Save an image to file.
Definition: MEImage.cpp:305
Color.
Definition: MEDefs.hpp:41
void DrawCircle(int x, int y, int radius, const MEColor &color, bool fill=true)
Draw a circle.
Definition: MEImage.cpp:695
void CopyImagePart(int x1, int y1, int x2, int y2, MEImage &other)
Copy an image part to an other picture.
Definition: MEImage.cpp:642
void Crop(int x1, int y1, int x2, int y2)
Crop image.
Definition: MEImage.cpp:598
MCBinaryData * Compress(ME::ImageFormatType format=ME::JpegFormat) const
Compress the image data.
Definition: MEImage.cpp:411
void Binarize(int threshold)
Binarize an image.
Definition: MEImage.cpp:1033
void Smooth()
Smooth function with 3x3 median filter.
Definition: MEImage.cpp:745
float GetRatio() const
Get image ratio.
Definition: MEImage.cpp:535
bool LoadFromFile(const std::string &file_name)
Load an image from a file.
Definition: MEImage.cpp:281
float AverageBrightnessLevel() const
Calculate average brightness level.
Definition: MEImage.cpp:1308
#define MC_WARNING(...)
Warning macro.
Definition: MCLog.hpp:43
void Mask(MEImage &other)
Mask with a threshold image.
Definition: MEImage.cpp:837
const T & MCBound(const T &min, const T &value, const T &max)
Check a value bound according to a range.
Definition: MCDefs.hpp:527
T MCAbs(const T &value)
Calculate absolute value.
Definition: MCDefs.hpp:399
void ResizeScaleY(int height)
Resize image with new height.
Definition: MEImage.cpp:577
bool _Copy(const MEImage &other)
Copy image data.
Definition: MEImage.cpp:1452
void ConvertBGRToRGB()
Change the red and blue components of every pixel.
Definition: MEImage.cpp:959
MEImage & operator=(const MEImage &other)
Copy assignment operator.
Definition: MEImage.cpp:495
void Rotate(int x, int y, float degree)
Rotate the image.
Definition: MEImage.cpp:1327
void Erode(int iteration_count)
Erode function.
Definition: MEImage.cpp:719
void Allocate(int size)
Allocate a certain data size.
void MirrorVertical()
Reverse image in vertical direction.
Definition: MEImage.cpp:592
void ComputeColorSpace(ME::ColorSpaceType mode)
Compute an image to a different color space.
Definition: MEImage.cpp:1480
unsigned char GrayscalePixel(int x, int y) const
Get the grayscale value of a pixel.
Definition: MEImage.cpp:1375
void Quantize(int level_count)
Image quantisation.
Definition: MEImage.cpp:797
MCBinaryData * ExportRawImageData() const
Export the raw image data.
Definition: MEImage.cpp:396
Image.
Definition: MEImage.hpp:112
void SetLayer(const MEImage &new_layer, int layer_index)
Copy a new layer to the image.
Definition: MEImage.cpp:330
void Clear()
Clear image with zeroes.
Definition: MEImage.cpp:312
~MEImage()
Destructor of class.
Definition: MEImage.cpp:270
int GetHeight() const
Get the image height.
Definition: MEImage.cpp:517
void Canny(double threshold1=800, double threshold2=1100, int aperture_size=5)
Canny function.
Definition: MEImage.cpp:769
int GetRowWidth() const
Get the image row width.
Definition: MEImage.cpp:511
int GetImageDataSize() const
Get the number of the image pixel data.
Definition: MEImage.cpp:529
int AverageDifference(MEImage &reference) const
Calculate an average difference between two images.
Definition: MEImage.cpp:1253
void LBP(ME::LBPType mode=ME::SpecialLBP)
Compute an LBP filter over the image.
Definition: MEImage.cpp:968
bool MCFileExists(const std::string &file_name)
Check whether a file exists.
Definition: MCDefs.cpp:226
unsigned char Blue
Blue component.
Definition: MEDefs.hpp:50
void Invert()
Invert.
Definition: MEImage.cpp:1423
void ConvertToGrayscale()
Convert to grayscale.
Definition: MEImage.cpp:933
bool Equal(const MEImage &other) const
Compare to an other image.
Definition: MEImage.cpp:1341
void Resize(int width, int height)
Resize image.
Definition: MEImage.cpp:553
void MirrorHorizontal()
Reverse image in horizontal direction.
Definition: MEImage.cpp:586
void ConvertToColorSpace(ME::ColorSpaceType transformation)
Convert into a new color space.
Definition: MEImage.cpp:863
unsigned char Red
Red component.
Definition: MEDefs.hpp:46
unsigned char * GetData() const
Get direct access to the binary data.
void GammaCorrection(float gamma_level)
Gamma correction.
Definition: MEImage.cpp:1429
void Dilate(int iteration_count)
Dilate function.
Definition: MEImage.cpp:732
void Multiple(MEImage &other)
Multiple an image with the internal picture.
Definition: MEImage.cpp:1093
void AdaptiveThreshold()
Adaptive threshold function.
Definition: MEImage.cpp:822
const IplImage * GetIplImage() const
Get access to the internal OpenCV image data.
Definition: MEImage.cpp:351
void SmoothAdvanced(ME::SmoothType filter_mode, int matrix_size)
Advanced smooth function.
Definition: MEImage.cpp:751
void Laplace()
Laplace function.
Definition: MEImage.cpp:783
const T MCMax(const U &container)
Get the maximal value of a container.
void EliminateSinglePixels()
Eliminate the single pixels from a binary image.
Definition: MEImage.cpp:1182
float DifferenceAreas(MEImage &reference, int difference) const
Calculate an area difference feature between two images.
Definition: MEImage.cpp:1225
IplImage * Image
Image data in OpenCV format.
Definition: MEImage.hpp:842
bool Decompress(const MCBinaryData &data)
Decompress image data.
Definition: MEImage.cpp:449
void Realloc(int width, int height)
Reallocate image data by keeping the layer count.
Definition: MEImage.cpp:541
void FillHoles()
Fill holes.
Definition: MEImage.cpp:1397
void DrawText(int x, int y, const std::string &text, float scale, const MEColor &color)
Draw a text.
Definition: MEImage.cpp:708
void ConvertToRGB()
Convert to RGB.
Definition: MEImage.cpp:946
bool operator==(const MEImage &other)
Equality operator.
Definition: MEImage.cpp:483
unsigned char Green
Green component.
Definition: MEDefs.hpp:48
void SetRawImageData(const MCBinaryData &image_data, int width, int height, int layer_count)
Set the image data.
Definition: MEImage.cpp:376
void Subtract(const MEImage &other, ME::SubtractionType mode)
Subtract an image.
Definition: MEImage.cpp:1049
void Minimum(MEImage &other)
Minimum between two images.
Definition: MEImage.cpp:1283
unsigned int GetWhitePixelCount()
Get white pixel count.
Definition: MEImage.cpp:1391
void DrawLine(int x0, int y0, int x1, int y1, const MEColor &color)
Draw a line.
Definition: MEImage.cpp:662
MEImage * GetLayer(int layer_index) const
Get an image layer.
Definition: MEImage.cpp:318
int GetSize() const
Get binary data size.