libcrn  3.9.5
A document image processing library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CRNImageBW.cpp
Go to the documentation of this file.
1 /* Copyright 2006-2015 Yann LEYDIER, INSA-Lyon, CoReNum
2  *
3  * This file is part of libcrn.
4  *
5  * libcrn is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * libcrn is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with libcrn. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * file: CRNImageBW.cpp
19  * \author Yann LEYDIER
20  */
21 
22 #include <CRNImage/CRNImageBW.h>
24 #include <CRNMath/CRNMatrixInt.h>
25 #include <CRNException.h>
26 #include <CRNIO/CRNFileShield.h>
27 #include <CRNi18n.h>
28 
29 #ifdef CRN_USING_GDKPB
30 # include <gdk-pixbuf/gdk-pixbuf.h>
31 #endif
32 
33 #ifdef CRN_USING_GDIPLUS
34 # include <windows.h>
35 # include <objidl.h>
36 # include <gdiplus.h>
37 # undef RegisterClass
38 # undef max
39 #endif
40 
41 #ifdef CRN_USING_LIBPNG
42 # include <png.h>
43 #endif
44 
45 #ifdef CRN_USING_LIBJPEG
46 # include <jpeglib.h>
47 # include <setjmp.h>
48 struct crn_jpeg_error_mgr {
49  struct jpeg_error_mgr pub; /* "public" fields */
50  jmp_buf setjmp_buffer; /* for return to caller */
51 };
52 typedef struct crn_jpeg_error_mgr * crn_jpeg_error_ptr;
53 void crn_jpeg_error_exit(j_common_ptr cinfo);
54 #endif
55 
56 void fclose_if_not_null(FILE *f);
57 
58 using namespace crn;
59 
60 #ifdef CRN_USING_LIBPNG
61 static std::pair<bool, String> save_png_libpng(const Path &filename, const ImageBW &img)
62 {
63  // libpng does not support URIs
64  Path fname(filename);
65  fname.ToLocal();
66 
67  std::unique_ptr<FILE, decltype(fclose_if_not_null)*> fp(fopen(fname.CStr(), "wb"), fclose_if_not_null);
68  if (!fp)
69  {
70  return std::make_pair(false, String(_("Cannot create file ")) + U"<" + fname + U">");
71  }
72 
73  /* Create png struct & info */
74  png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
75  if (!png_ptr)
76  {
77  return std::make_pair(false, String(_("Cannot create the PNG structure.")));
78  }
79  png_infop info_ptr = png_create_info_struct(png_ptr);
80  if (!info_ptr)
81  {
82  png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
83  return std::make_pair(false, String(_("Cannot create the PNG info.")));
84  }
85 
86  /* Set up the error handling */
87  if (setjmp(png_jmpbuf(png_ptr)))
88  {
89  png_destroy_write_struct(&png_ptr, &info_ptr);
90  return std::make_pair(false, String(_("Error while generating the PNG image.")));
91  }
92 
93  /* setup libpng for using standard C fwrite() function with our FILE pointer */
94  png_init_io(png_ptr, fp.get());
95 
96  /* Fill in the png_info structure */
97  int width = int(img.GetWidth());
98  int height = int(img.GetHeight());
99  int bit_depth = 1;
100  int color_type = PNG_COLOR_TYPE_GRAY;
101  int interlace_type = PNG_INTERLACE_NONE;
102  int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
103  int filter_method = PNG_FILTER_TYPE_DEFAULT;
104  png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, interlace_type, compression_type, filter_method);
105 
106  /* Using the low-level write interface */
107  png_write_info(png_ptr, info_ptr);
108  png_bytepp row_pointers;
109  row_pointers = (png_bytep*)malloc(sizeof(png_byte*) * height);
110  for (int i = 0; i < height ; ++i)
111  {
112 #if (PNG_LIBPNG_VER > 10300)
113  row_pointers[i] = (png_bytep)calloc(1, png_get_rowbytes(png_ptr, info_ptr));
114 #else
115  row_pointers[i] = (png_bytep)calloc(1, info_ptr->rowbytes);
116 #endif
117  }
118 
119  /* Conversion */
120  //int channels = info_ptr->channels;
121  for (int y = 0; y < height; y++)
122  for (int x = 0; x < width; x++)
123  if (img.At(x, y))
124  row_pointers[y][x / 8] = png_byte(row_pointers[y][x / 8] | (1 << (7 - x % 8)));
125 
126  png_write_image(png_ptr, row_pointers);
127 
128  // TODO more options
129  png_text txt[1];
130  txt[0].compression = PNG_TEXT_COMPRESSION_NONE;
131  txt[0].key = (char*)"creator";
132  txt[0].text = (char*)"libcrn";
133  txt[0].text_length = strlen(txt[0].text);
134  png_set_text(png_ptr, info_ptr, txt, 1);
135 
136  png_write_end(png_ptr, info_ptr);
137  png_destroy_write_struct(&png_ptr, &info_ptr);
138 
139  // free
140  for (int i = 0; i < height ; ++i)
141  {
142  free(row_pointers[i]);
143  }
144  free(row_pointers);
145  return std::make_pair(true, String(""));
146 
147 }
148 #endif
149 
150 #ifdef CRN_USING_GDKPB
151 static std::pair<bool, String> save_png_gdkpixbuf(const Path &fname, const ImageBW &img)
152 {
153  GdkPixbuf *pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
154  int(img.GetWidth()), int(img.GetHeight()));
155  if (!pb)
156  {
157  return std::make_pair(false, String(_("Cannot create temporary buffer.")));
158  }
159 
160  int w = gdk_pixbuf_get_width(pb);
161  int h = gdk_pixbuf_get_height(pb);
162  int rs = gdk_pixbuf_get_rowstride(pb);
163  guchar *ppix = gdk_pixbuf_get_pixels(pb);
164  for (int y = 0; y < h; y++)
165  for (int x = 0; x < w; x++)
166  {
167  const uint8_t px = img.At(x, y) ? 255 : 0;
168  int o2 = x + x + x + y * rs;
169  ppix[o2] = px;
170  ppix[o2 + 1] = px;
171  ppix[o2 + 2] = px;
172  }
173 
174  GError *err = NULL;
175  gchar *utfname;
176  gsize i;
177  utfname = g_locale_to_utf8(fname.CStr(), -1, NULL, &i, NULL);
178  gchar *filename;
179  filename = g_filename_from_utf8(utfname, -1, NULL, &i, NULL);
180  g_free(utfname);
181  gchar *tinfo = g_locale_to_utf8("tEXt::Source", -1, NULL, &i, NULL);
182  gchar *tinfo2 = g_locale_to_utf8("Saved by libcrn.", -1, NULL, &i, NULL);
183  bool ok = gdk_pixbuf_save(pb, filename, "png", &err, tinfo, tinfo2, NULL);
184  g_free(filename);
185  g_free(tinfo);
186  g_free(tinfo2);
187  g_object_unref(pb);
188  String out(U"");
189  if (!ok)
190  {
191  out = String(_("Cannot save file. ")) + err->message;
192  g_error_free(err);
193  }
194  return std::make_pair(ok, out);
195 }
196 #endif
197 
198 #ifdef CRN_USING_GDIPLUS
199 static int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
200 {
201  UINT num = 0; // number of image encoders
202  UINT size = 0; // size of the image encoder array in bytes
203 
204  Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
205 
206  Gdiplus::GetImageEncodersSize(&num, &size);
207  if (size == 0)
208  return -1; // Failure
209 
210  pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
211  if (pImageCodecInfo == nullptr)
212  return -1; // Failure
213 
214  Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
215 
216  for (UINT j = 0; j < num; ++j)
217  {
218  if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
219  {
220  *pClsid = pImageCodecInfo[j].Clsid;
221  free(pImageCodecInfo);
222  return j; // Success
223  }
224  }
225 
226  free(pImageCodecInfo);
227  return -1; // Failure
228 }
229 
230 static std::pair<bool, String> save_png_gdiplus(const Path &filename, const ImageBW &img)
231 {
232  // gdi+ does not support URIs
233  Path winname(filename);
234  winname.ToWindows();
235  const auto newsize = winname.Size() + 1;
236  auto wcstring = std::vector<wchar_t>(newsize);
237  auto convertedChars = size_t(0);
238  mbstowcs_s(&convertedChars, wcstring.data(), newsize, winname.CStr(), _TRUNCATE);
239 
240  Gdiplus::Bitmap *outbm = outbm = new Gdiplus::Bitmap(INT(img.GetWidth()), INT(img.GetHeight()), PixelFormat1bppIndexed);
241  if (!outbm)
242  {
243  return std::make_pair(false, String(_("Cannot create bitmap")));
244  }
245  Gdiplus::BitmapData bitmapData;
246  auto clip = Gdiplus::Rect(0, 0, outbm->GetWidth(), outbm->GetHeight());
247  outbm->LockBits(&clip, Gdiplus::ImageLockModeWrite, PixelFormat1bppIndexed, &bitmapData);
248  auto *pixels = (uint8_t*)bitmapData.Scan0;
249  //#pragma omp parallel for
250  FOREACHPIXEL(x, y, img)
251  {
252  size_t poffset = x / 8 + y * bitmapData.Stride;
253  if (img.At(x, y))
254  {
255  uint8_t b = 1 << (7 - (x % 8));
256  pixels[poffset] |= b;
257  }
258  }
259  outbm->UnlockBits(&bitmapData);
260 
261  CLSID pngClsid;
262  GetEncoderClsid(L"image/png", &pngClsid);
263  Gdiplus::Status stat = outbm->Save(wcstring.data(), &pngClsid, NULL);
264  delete outbm;
265  return std::make_pair(stat == Gdiplus::Ok, String{});
266 }
267 #endif
268 
276 void crn::impl::SavePNG(const ImageBW &img, const Path &fname)
277 {
278  if (!fname)
279  throw ExceptionInvalidArgument(StringUTF8("void Image::SaveBW(const Path &fname): ") + _("Null file name."));
280 
281  std::lock_guard<std::mutex> lock(crn::FileShield::GetMutex(fname)); // lock the file
282 
283  std::pair<bool, String> res(std::make_pair(false, String(U"")));
284  String error;
285 #ifdef CRN_USING_LIBPNG
286  if (res.first == false)
287  {
288  res = save_png_libpng(fname, img);
289  error += U" " + res.second;
290  }
291 #endif
292 #ifdef CRN_USING_GDIPLUS
293 if (res.first == false)
294 {
295  res = save_png_gdiplus(fname, img);
296  error += U" " + res.second;
297 }
298 #endif
299 #ifdef CRN_USING_GDKPB
300  if (res.first == false)
301  {
302  res = save_png_gdkpixbuf(fname, img);
303  error += U" " + res.second;
304  }
305 #endif
306  if (res.first == false)
307  throw ExceptionRuntime(StringUTF8("void crn::SaveBW(const ImageGray &img, const Path &fname): ") +
308  _("No library for saving image found or write permissions on the file or directory are not granted. No image will be saved.") + "\n" + StringUTF8(error) + "\n" + StringUTF8(fname));
309 }
310 
311 #ifdef CRN_USING_LIBJPEG
312 static std::pair<bool, String> save_jpeg_libjpeg(const Path &filename, const ImageBW &img, int qual)
313 {
314  // libjpeg does not support URIs
315  Path fname(filename);
316  fname.ToLocal();
317 
318  std::unique_ptr<FILE, decltype(fclose_if_not_null)*> fp(fopen(fname.CStr(), "wb"), fclose_if_not_null);
319  if (!fp)
320  {
321  return std::make_pair(false, String(_("Cannot create file ")) + U"<" + fname + U">");
322  }
323 
324  crn_jpeg_error_mgr jerr;
325  struct jpeg_compress_struct cinfo;
326 
327  cinfo.err = jpeg_std_error(&jerr.pub);
328  jerr.pub.error_exit = crn_jpeg_error_exit;
329  if (setjmp(jerr.setjmp_buffer))
330  {
331  jpeg_destroy_compress(&cinfo);
332  return std::make_pair(false, String(_("Cannot create jpeg file structure.")));
333  }
334 
335  jpeg_create_compress(&cinfo);
336  jpeg_stdio_dest(&cinfo, fp.get());
337  cinfo.image_width = int(img.GetWidth());
338  cinfo.image_height = int(img.GetHeight());
339  cinfo.input_components = 1;
340  cinfo.in_color_space = JCS_GRAYSCALE;
341  jpeg_set_defaults(&cinfo);
342  jpeg_set_quality(&cinfo, qual, TRUE);
343  jpeg_start_compress(&cinfo, TRUE);
344  JSAMPROW scanline = new JSAMPLE[img.GetWidth()];
345  for (size_t y = 0; y < img.GetHeight(); ++y)
346  {
347  for (size_t x = 0; x < img.GetWidth(); ++x)
348  {
349  scanline[x] = img.At(x, y) ? 255 : 0;
350  }
351  jpeg_write_scanlines(&cinfo, &scanline, 1);
352  }
353  delete scanline;
354  // write comment
355  jpeg_write_marker(&cinfo, JPEG_COM, (JOCTET*)"Saved by libcrn.", (unsigned int)(strlen("Saved by libcrn.")));
356 
357  jpeg_finish_compress(&cinfo);
358  jpeg_destroy_compress(&cinfo);
359 
360  return std::make_pair(true, String(U""));
361 }
362 #endif
363 
364 #ifdef CRN_USING_GDKPB
365 static std::pair<bool, String> save_jpeg_gdkpixbuf(const Path &fname, const ImageBW &img, int qual)
366 {
367  GdkPixbuf *pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
368  int(img.GetWidth()), int(img.GetHeight()));
369  if (!pb)
370  {
371  return std::make_pair(false, String(_("Cannot create temporary buffer.")));
372  }
373  int w = gdk_pixbuf_get_width(pb);
374  int h = gdk_pixbuf_get_height(pb);
375  int rs = gdk_pixbuf_get_rowstride(pb);
376  guchar *ppix = gdk_pixbuf_get_pixels(pb);
377  for (int y = 0; y < h; y++)
378  for (int x = 0; x < w; x++)
379  {
380  const uint8_t px = img.At(x, y) ? 255 : 0;
381  int o2 = x + x + x + y * rs;
382  ppix[o2] = px;
383  ppix[o2 + 1] = px;
384  ppix[o2 + 2] = px;
385  }
386 
387  GError *err = NULL;
388  gchar *utfname;
389  gsize i;
390  utfname = g_locale_to_utf8(fname.CStr(), -1, NULL, &i, NULL);
391  gchar *filename;
392  filename = g_filename_from_utf8(utfname, -1, NULL, &i, NULL);
393  g_free(utfname);
394  gchar *tinfo = g_locale_to_utf8("quality", -1, NULL, &i, NULL);
395  char qt[16];
396  sprintf(qt, "%i", qual);
397  gchar *tinfo2 = g_locale_to_utf8(qt, -1, NULL, &i, NULL);
398  bool ok = gdk_pixbuf_save(pb, filename, "jpeg", &err, tinfo, tinfo2, NULL);
399  g_free(filename);
400  g_free(tinfo);
401  g_free(tinfo2);
402  g_object_unref(pb);
403  String res(U"");
404  if (!ok)
405  {
406  res = String(_("Cannot save file. ")) + err->message;
407  g_error_free(err);
408  }
409  return std::make_pair(ok, res);
410 }
411 #endif
412 
413 #ifdef CRN_USING_GDIPLUS
414 static std::pair<bool, String> save_jpeg_gdiplus(const Path &filename, const ImageBW &img, int qual)
415 {
416  // gdi+ does not support URIs
417  Path winname(filename);
418  winname.ToWindows();
419  const auto newsize = winname.Size() + 1;
420  auto wcstring = std::vector<wchar_t>(newsize);
421  auto convertedChars = size_t(0);
422  mbstowcs_s(&convertedChars, wcstring.data(), newsize, winname.CStr(), _TRUNCATE);
423 
424  Gdiplus::Bitmap *outbm = new Gdiplus::Bitmap(INT(img.GetWidth()), INT(img.GetHeight()), PixelFormat24bppRGB);
425  if (!outbm)
426  {
427  return std::make_pair(false, String(_("Cannot create bitmap")));
428  }
429  Gdiplus::BitmapData bitmapData;
430  auto clip = Gdiplus::Rect(0, 0, outbm->GetWidth(), outbm->GetHeight());
431  outbm->LockBits(&clip, Gdiplus::ImageLockModeWrite, PixelFormat24bppRGB, &bitmapData);
432  auto *pixels = (uint8_t*)bitmapData.Scan0;
433  //#pragma omp parallel for
434  FOREACHPIXEL(x, y, img)
435  {
436  size_t poffset = 3 * x + y * bitmapData.Stride;
437  const auto pix = img.At(x, y) ? uint8_t(255) : uint8_t(0);
438  pixels[poffset + 2] = pix;
439  pixels[poffset + 1] = pix;
440  pixels[poffset] = pix;
441  }
442  outbm->UnlockBits(&bitmapData);
443  CLSID jpegClsid;
444  GetEncoderClsid(L"image/jpeg", &jpegClsid);
445  Gdiplus::EncoderParameters encoderParameters;
446  encoderParameters.Count = 1;
447  encoderParameters.Parameter[0].Guid = Gdiplus::EncoderQuality;
448  encoderParameters.Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
449  encoderParameters.Parameter[0].NumberOfValues = 1;
450  ULONG quality = qual;
451  encoderParameters.Parameter[0].Value = &quality;
452  outbm->Save(wcstring.data(), &jpegClsid, &encoderParameters);
453  delete outbm;
454  return std::make_pair(true, String{});
455 }
456 #endif
457 
467 void crn::impl::SaveJPEG(const ImageBW &img, const Path &fname, unsigned int qual)
468 {
469  if (!fname)
470  throw ExceptionInvalidArgument(StringUTF8("bool crn::SaveJPEG(const ImageBW &img, const Path &fname, int qual): ") +
471  _("Null file name."));
472 
473  std::lock_guard<std::mutex> lock(crn::FileShield::GetMutex(fname)); // lock the file
474 
475  std::pair<bool, String> res(std::make_pair(false, String(U"")));
476  String error;
477 #ifdef CRN_USING_LIBJPEG
478  if (res.first == false)
479  {
480  res = save_jpeg_libjpeg(fname, img, qual);
481  error += U" " + res.second;
482  }
483 #endif
484 #ifdef CRN_USING_GDIPLUS
485  if (res.first == false)
486  {
487  res = save_jpeg_gdiplus(fname, img, qual);
488  error += U" " + res.second;
489  }
490 #endif
491 #ifdef CRN_USING_GDKPB
492  if (res.first == false)
493  {
494  res = save_jpeg_gdkpixbuf(fname, img, qual);
495  error += U" " + res.second;
496  }
497 #endif
498  if (res.first == false)
499  throw ExceptionRuntime(StringUTF8("void crn::SaveJPEG(const ImageBW &img, const Path &fname, int qual): ") +
500  _("No library for saving image found or write permissions on the file or directory are not granted. No image will be saved.") + StringUTF8(error));
501 }
502 
507 {
508  auto h = Histogram(img.GetHeight());
509  for (size_t y = 0; y < img.GetHeight(); y++)
510  {
511  size_t x;
512  for (x = 0; x < img.GetWidth(); x++)
513  if (!img.At(x, y))
514  break;
515  h.SetBin(y, (unsigned int)x);
516  }
517  return h;
518 }
519 
524 {
525  auto h = Histogram(img.GetHeight());
526  for (size_t y = 0; y < img.GetHeight(); y++)
527  {
528  int x;
529  for (x = int(img.GetWidth()) - 1; x >= 0; --x)
530  if (!img.At(x, y))
531  break;
532  h.SetBin(y, (unsigned int)(img.GetWidth() - 1 - x));
533  }
534  return h;
535 }
536 
541 {
542  auto h = Histogram(img.GetWidth());
543  for (size_t x = 0; x < img.GetWidth(); x++)
544  {
545  size_t y;
546  for (y = 0; y < img.GetHeight(); y++)
547  if (!img.At(x, y))
548  break;
549  h.SetBin(x, (unsigned int)y);
550  }
551  return h;
552 }
553 
558 {
559  auto h = Histogram(img.GetWidth());
560  for (size_t x = 0; x < img.GetWidth(); x++)
561  {
562  int y;
563  for (y = int(img.GetHeight()) - 1; y >= 0; --y)
564  if (!img.At(x, y))
565  break;
566  h.SetBin(x, (unsigned int)(img.GetHeight() - 1 - y));
567  }
568  return h;
569 }
570 
575 {
576  auto h = Histogram(img.GetHeight());
577  for (size_t y = 0; y < img.GetHeight(); y++)
578  {
579  unsigned int cnt = 0;
580  for (size_t x = 0; x < img.GetWidth(); x++)
581  if (!img.At(x, y))
582  cnt += 1;
583  h.SetBin(y, cnt);
584  }
585  return h;
586 }
587 
592 {
593  auto h = Histogram(img.GetWidth());
594  for (size_t x = 0; x < img.GetWidth(); x++)
595  {
596  unsigned int cnt = 0;
597  for (size_t y = 0; y < img.GetHeight(); y++)
598  if (!img.At(x, y))
599  cnt += 1;
600  h.SetBin(x, cnt);
601  }
602  return h;
603 }
604 
609 {
610  const auto cos_theta = theta.Cos();
611  const auto sin_theta = theta.Sin();
612 
613  const auto head = double(img.GetWidth()) * cos_theta;
614  const auto tail = double(img.GetHeight()) * sin_theta;
615 
616  auto hist = Histogram(size_t(tail + head) + 1);
617 
618  for (size_t l = 0; l < img.GetHeight(); l++)
619  {
620  auto previous_pixel = bool(img.At(0, l));
621 
622  if (!previous_pixel)
623  {
624  auto x = 0.0;
625  auto y = double(img.GetHeight()) - double(l);
626 
627  auto x_prime = x * cos_theta - y * sin_theta;
628 
629  hist.IncBin((size_t)(x_prime + tail), 1);
630  }
631 
632  for (size_t c = 1; c < img.GetWidth(); c++)
633  {
634  auto current_pixel = img.At(c, l);
635 
636  if (!current_pixel)
637  {
638  auto x = double(c);
639  auto y = double(img.GetHeight()) - double(l);
640 
641  auto x_prime = x * cos_theta - y * sin_theta;
642 
643  hist.IncBin(size_t(x_prime + tail), 1);
644  }
645 
646  previous_pixel = current_pixel;
647  }
648  }
649 
650  return hist;
651 }
652 
659 double crn::MeanBlackRun(const ImageBW &img) noexcept
660 {
661  auto sum = 0L;
662  auto cnt = 0L;
663  for (size_t y = 0; y < img.GetHeight(); y++)
664  {
665  auto beg = size_t(0);
666  for (size_t x = 1; x < img.GetWidth(); x++)
667  {
668  if (img.At(x - 1, y) && !img.At(x, y))
669  {
670  beg = x;
671  }
672  else if (!img.At(x - 1, y) && img.At(x, y))
673  {
674  sum += long(x) - long(beg);
675  cnt += 1;
676  }
677  } // for x
678  } // for y
679  return (double)sum / (double)cnt;
680 }
681 
690 double crn::MeanWhiteRun(const ImageBW &img, int blackrun) noexcept
691 {
692  const auto RUNFACT = 2;
693  if (blackrun == -1)
694  blackrun = (int)MeanBlackRun(img);
695  auto sum = 0L;
696  auto cnt = 0L;
697  for (size_t y = 0; y < img.GetHeight(); y++)
698  {
699  auto beg = size_t(0);
700  for (size_t x = 1; x < img.GetWidth(); x++)
701  {
702  if (!img.At(x - 1, y) && img.At(x, y))
703  {
704  beg = x;
705  }
706  else if (img.At(x - 1, y) && !img.At(x, y))
707  {
708  if (int(x - beg) < blackrun * RUNFACT)
709  {
710  sum += long(x) - long(beg);
711  cnt += 1;
712  }
713  }
714  } // for x
715  } // for y
716  if (!cnt)
717  return 0.0;
718  else
719  return (double)sum / (double)cnt;
720 }
721 
728 double crn::MeanBlackVRun(const ImageBW &img) noexcept
729 {
730  auto sum = 0L;
731  auto cnt = 0L;
732  for (size_t x = 0; x < img.GetWidth(); x++)
733  {
734  bool in = false;
735  size_t beg = 0;
736  for (size_t y = 1; y < img.GetHeight(); y++)
737  {
738  if (img.At(x, y - 1) && !img.At(x, y))
739  {
740  beg = y;
741  in = true;
742  }
743  else if (!img.At(x, y - 1) && img.At(x, y))
744  {
745  if (in)
746  {
747  sum += long(y) - long(beg);
748  cnt += 1;
749  }
750  in = false;
751  }
752  } // for x
753  } // for y
754  if (!cnt)
755  return 0.0;
756  return (double)sum / (double)cnt;
757 }
758 
759 /*****************************************************************************/
764 size_t crn::CountBlackPixels(const ImageBW &img) noexcept
765 {
766  auto cnt = size_t(0);
767  for (auto p : img)
768  if (!p)
769  cnt += 1;
770  return cnt;
771 }
772 
773 /*****************************************************************************/
778 size_t crn::CountWhitePixels(const ImageBW &img) noexcept
779 {
780  auto cnt = size_t(0);
781  for (auto p : img)
782  if (p)
783  cnt += 1;
784  return cnt;
785 }
786 
787 /*****************************************************************************/
797 size_t crn::Regularize(ImageBW &img, size_t min_neighbors)
798 {
799  if (min_neighbors > 7)
800  {
801  throw ExceptionDomain(crn::StringUTF8("Cleanup(ImageBW &img, size_t min_neighbors): ") + _("Min neighbors must be < 8."));
802  }
803 
804  auto nb = size_t(0);
805  auto npix = img;
806  for (auto y = size_t(1); y < img.GetHeight() - 1; ++y)
807  for (auto x = size_t(1); x < img.GetWidth() - 1; ++x)
808  {
809  if (!img.At(x, y))
810  {
811  size_t n = 0;
812  if (!img.At(x - 1, y - 1))
813  n += 1;
814  if (!img.At(x, y - 1))
815  n += 1;
816  if (!img.At(x + 1, y - 1))
817  n += 1;
818  if (!img.At(x + 1, y))
819  n += 1;
820  if (!img.At(x + 1, y + 1))
821  n += 1;
822  if (!img.At(x, y + 1))
823  n += 1;
824  if (!img.At(x - 1, y + 1))
825  n += 1;
826  if (!img.At(x - 1, y))
827  n += 1;
828  if (n <= min_neighbors)
829  {
830  npix.At(x, y) = pixel::BWWhite;
831  nb += 1;
832  }
833  }
834  }
835 
836  img = std::move(npix);
837  return nb;
838 }
839 
840 /*****************************************************************************/
853 {
854  if ((m1.GetRows() != m2.GetRows()) || (m1.GetCols() != m2.GetCols()))
855  {
856  throw ExceptionDimension(StringUTF8("ImageIntGray DistanceTransform(const ImageBW &img, const MatrixInt *m1, const MatrixInt *m2): ") + _("matrices of different sizes"));
857  }
858  if (!(m1.GetRows() & 1) || !(m1.GetCols() & 1))
859  {
860  throw ExceptionInvalidArgument(StringUTF8("ImageIntGray DistanceTransform(const ImageBW &img, const MatrixInt &m1, const MatrixInt &m2): ") + _("even matrix dimensions."));
861  }
862  auto hw = m1.GetCols() / 2;
863  auto hh = m1.GetRows() / 2;
864 
865  ImageIntGray dt = ImageIntGray(img.GetWidth(), img.GetHeight());
866  for (auto tmp : Range(img))
867  dt.At(tmp) = img.At(tmp) ? 0 : 1;
868 
869  for (size_t y = hh; y < img.GetHeight() - hh; y++)
870  for (size_t x = hw; x < img.GetWidth() - hw; x++)
871  if (dt.At(x, y))
872  {
873  auto val = std::numeric_limits<int>::max();
874  for (size_t l = 0; l < m1.GetRows(); l++)
875  for (size_t c = 0; c < m1.GetCols(); c++)
876  {
877  auto mval = m1.At(l, c);
878  if (mval >= 0)
879  {
880  int imval = dt.At(x + c - hw, y + l - hh);
881  imval += mval;
882  if (imval < val) val = imval;
883  }
884  }
885  dt.At(x, y) = val;
886  }
887 
888  for (size_t y = img.GetHeight() - hh - 1; y >= hh; y--)
889  for (size_t x = img.GetWidth() - hw - 1; x >= hw; x--)
890  if (dt.At(x, y))
891  {
892  int val = std::numeric_limits<int>::max();
893  for (size_t l = 0; l < m2.GetRows(); l++)
894  for (size_t c = 0; c < m2.GetCols(); c++)
895  {
896  int mval = m2.At(l, c);
897  if (mval >= 0)
898  {
899  int imval = dt.At(x + c - hw, y + l - hh);
900  imval += mval;
901  if (imval < val) val = imval;
902  }
903  }
904  dt.At(x, y) = val;
905  }
906 
907  return dt;
908 }
909 
ScalarRange< T > Range(T b, T e)
Creates a range [[b, e[[.
Definition: CRNType.h:257
size_t GetRows() const noexcept
Returns the number of rows.
Definition: CRNMatrix.h:157
size_t GetCols() const noexcept
Returns the number of columns.
Definition: CRNMatrix.h:163
size_t Regularize(ImageBW &img, size_t min_neighbors=0)
Removes isolated pixels and smooths edges.
Definition: CRNImageBW.cpp:797
A generic runtime error.
Definition: CRNException.h:131
Image< int > ImageIntGray
Int grayscale image class.
Histogram VerticalSlantedProjection(const ImageBW &img, const Angle< Radian > &theta)
Computes the vertical projection after rotation.
Definition: CRNImageBW.cpp:608
std::vector< pixel_type >::reference At(size_t x, size_t y) noexcept
Returns a reference to a pixel.
Definition: CRNImage.h:224
size_t GetHeight() const noexcept
Definition: CRNImage.h:74
#define _(String)
Definition: CRNi18n.h:51
size_t CountBlackPixels(const ImageBW &img) noexcept
Returns the number of black pixels.
Definition: CRNImageBW.cpp:764
ImageIntGray DistanceTransform(const ImageBW &img, const MatrixInt &m1, const MatrixInt &m2)
Creates an image containing the distance transform.
Definition: CRNImageBW.cpp:852
void SavePNG(const ImageBW &img, const Path &fname)
Saves as PNG file.
#define FOREACHPIXEL(x, y, img)
Convenience macro to sweep an image.
Definition: CRNImage.h:37
A convenience class for angles units.
static std::mutex & GetMutex(const Path &fname)
Gets the mutex associated to a file.
A UTF32 character string class.
Definition: CRNString.h:61
Integer matrix class.
Definition: CRNMatrixInt.h:40
A generic domain error.
Definition: CRNException.h:83
const char * CStr() const noexcept
Conversion to UTF8 cstring.
double MeanBlackRun(const ImageBW &img) noexcept
Gets the mean horizontal black run.
Definition: CRNImageBW.cpp:659
A convenience class for file paths.
Definition: CRNPath.h:39
void SetBin(size_t k, unsigned int v)
Modify a bin value.
double Sin() const noexcept
Computes sine.
A dimension error.
Definition: CRNException.h:119
double MeanBlackVRun(const ImageBW &img) noexcept
Gets the mean vertical black run.
Definition: CRNImageBW.cpp:728
double Cos() const noexcept
Computes cosine.
Histogram BottomProfile(const ImageBW &img)
Computes the bottom profile.
Definition: CRNImageBW.cpp:557
void fclose_if_not_null(FILE *f)
Definition: CRNImage.cpp:68
Histogram TopProfile(const ImageBW &img)
Computes the top profile.
Definition: CRNImageBW.cpp:540
const T & At(size_t pos) const noexcept
Definition: CRNMatrix.h:165
Mother class for integer histograms.
Definition: CRNHistogram.h:44
double MeanWhiteRun(const ImageBW &img, int blackrun=-1) noexcept
Gets the mean horizontal white run.
Definition: CRNImageBW.cpp:690
void SaveJPEG(const ImageBW &img, const Path &fname, unsigned int qual)
Saves as JPEG file.
size_t GetWidth() const noexcept
Definition: CRNImage.h:72
Path & ToLocal()
Converts the path to the local format.
Definition: CRNPath.cpp:534
Histogram HorizontalProjection(const ImageBW &img)
Computes the horizontal projection.
Definition: CRNImageBW.cpp:574
A character string class.
Definition: CRNStringUTF8.h:49
Histogram RightProfile(const ImageBW &img)
Computes the right profile.
Definition: CRNImageBW.cpp:523
Histogram LeftProfile(const ImageBW &img)
Computes the left profile.
Definition: CRNImageBW.cpp:506
size_t CountWhitePixels(const ImageBW &img) noexcept
Returns the number of white pixels.
Definition: CRNImageBW.cpp:778
#define TRUE
Definition: CRNProp3.cpp:28
Histogram VerticalProjection(const ImageBW &img)
Computes the vertical projection.
Definition: CRNImageBW.cpp:591
Invalid argument error (e.g.: nullptr pointer)
Definition: CRNException.h:107