29 #ifdef CRN_USING_GDKPB
30 # include <gdk-pixbuf/gdk-pixbuf.h>
33 #ifdef CRN_USING_GDIPLUS
41 #ifdef CRN_USING_LIBPNG
45 #ifdef CRN_USING_LIBJPEG
48 struct crn_jpeg_error_mgr {
49 struct jpeg_error_mgr pub;
50 jmp_buf setjmp_buffer;
52 typedef struct crn_jpeg_error_mgr * crn_jpeg_error_ptr;
53 void crn_jpeg_error_exit(j_common_ptr cinfo);
60 #ifdef CRN_USING_LIBPNG
61 static std::pair<bool, String> save_png_libpng(
const Path &filename,
const ImageBW &img)
67 std::unique_ptr<FILE, decltype(fclose_if_not_null)*> fp(fopen(fname.CStr(),
"wb"),
fclose_if_not_null);
70 return std::make_pair(
false,
String(
_(
"Cannot create file ")) + U
"<" + fname + U
">");
74 png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
77 return std::make_pair(
false,
String(
_(
"Cannot create the PNG structure.")));
79 png_infop info_ptr = png_create_info_struct(png_ptr);
82 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
83 return std::make_pair(
false,
String(
_(
"Cannot create the PNG info.")));
87 if (setjmp(png_jmpbuf(png_ptr)))
89 png_destroy_write_struct(&png_ptr, &info_ptr);
90 return std::make_pair(
false,
String(
_(
"Error while generating the PNG image.")));
94 png_init_io(png_ptr, fp.get());
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);
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)
112 #if (PNG_LIBPNG_VER > 10300)
113 row_pointers[i] = (png_bytep)calloc(1, png_get_rowbytes(png_ptr, info_ptr));
115 row_pointers[i] = (png_bytep)calloc(1, info_ptr->rowbytes);
121 for (
int y = 0; y < height; y++)
122 for (
int x = 0; x < width; x++)
124 row_pointers[y][x / 8] = png_byte(row_pointers[y][x / 8] | (1 << (7 - x % 8)));
126 png_write_image(png_ptr, row_pointers);
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);
136 png_write_end(png_ptr, info_ptr);
137 png_destroy_write_struct(&png_ptr, &info_ptr);
140 for (
int i = 0; i < height ; ++i)
142 free(row_pointers[i]);
145 return std::make_pair(
true,
String(
""));
150 #ifdef CRN_USING_GDKPB
151 static std::pair<bool, String> save_png_gdkpixbuf(
const Path &fname,
const ImageBW &img)
153 GdkPixbuf *pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
157 return std::make_pair(
false,
String(
_(
"Cannot create temporary buffer.")));
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++)
167 const uint8_t px = img.
At(x, y) ? 255 : 0;
168 int o2 = x + x + x + y * rs;
177 utfname = g_locale_to_utf8(fname.
CStr(), -1, NULL, &i, NULL);
179 filename = g_filename_from_utf8(utfname, -1, NULL, &i, NULL);
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);
191 out =
String(
_(
"Cannot save file. ")) + err->message;
194 return std::make_pair(ok, out);
198 #ifdef CRN_USING_GDIPLUS
199 static int GetEncoderClsid(
const WCHAR* format, CLSID* pClsid)
204 Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
206 Gdiplus::GetImageEncodersSize(&num, &size);
210 pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
211 if (pImageCodecInfo ==
nullptr)
214 Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
216 for (UINT j = 0; j < num; ++j)
218 if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
220 *pClsid = pImageCodecInfo[j].Clsid;
221 free(pImageCodecInfo);
226 free(pImageCodecInfo);
230 static std::pair<bool, String> save_png_gdiplus(
const Path &filename,
const ImageBW &img)
233 Path winname(filename);
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);
240 Gdiplus::Bitmap *outbm = outbm =
new Gdiplus::Bitmap(INT(img.
GetWidth()), INT(img.
GetHeight()), PixelFormat1bppIndexed);
243 return std::make_pair(
false,
String(
_(
"Cannot create bitmap")));
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;
252 size_t poffset = x / 8 + y * bitmapData.Stride;
255 uint8_t b = 1 << (7 - (x % 8));
256 pixels[poffset] |= b;
259 outbm->UnlockBits(&bitmapData);
262 GetEncoderClsid(L
"image/png", &pngClsid);
263 Gdiplus::Status stat = outbm->Save(wcstring.data(), &pngClsid, NULL);
265 return std::make_pair(stat == Gdiplus::Ok,
String{});
283 std::pair<bool, String> res(std::make_pair(
false,
String(U
"")));
285 #ifdef CRN_USING_LIBPNG
286 if (res.first ==
false)
288 res = save_png_libpng(fname, img);
289 error += U
" " + res.second;
292 #ifdef CRN_USING_GDIPLUS
293 if (res.first ==
false)
295 res = save_png_gdiplus(fname, img);
296 error += U
" " + res.second;
299 #ifdef CRN_USING_GDKPB
300 if (res.first ==
false)
302 res = save_png_gdkpixbuf(fname, img);
303 error += U
" " + res.second;
306 if (res.first ==
false)
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));
311 #ifdef CRN_USING_LIBJPEG
312 static std::pair<bool, String> save_jpeg_libjpeg(
const Path &filename,
const ImageBW &img,
int qual)
315 Path fname(filename);
321 return std::make_pair(
false,
String(
_(
"Cannot create file ")) + U
"<" + fname + U
">");
324 crn_jpeg_error_mgr jerr;
325 struct jpeg_compress_struct cinfo;
327 cinfo.err = jpeg_std_error(&jerr.pub);
328 jerr.pub.error_exit = crn_jpeg_error_exit;
329 if (setjmp(jerr.setjmp_buffer))
331 jpeg_destroy_compress(&cinfo);
332 return std::make_pair(
false,
String(
_(
"Cannot create jpeg file structure.")));
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)
347 for (
size_t x = 0; x < img.
GetWidth(); ++x)
349 scanline[x] = img.
At(x, y) ? 255 : 0;
351 jpeg_write_scanlines(&cinfo, &scanline, 1);
355 jpeg_write_marker(&cinfo, JPEG_COM, (JOCTET*)
"Saved by libcrn.", (
unsigned int)(strlen(
"Saved by libcrn.")));
357 jpeg_finish_compress(&cinfo);
358 jpeg_destroy_compress(&cinfo);
360 return std::make_pair(
true,
String(U
""));
364 #ifdef CRN_USING_GDKPB
365 static std::pair<bool, String> save_jpeg_gdkpixbuf(
const Path &fname,
const ImageBW &img,
int qual)
367 GdkPixbuf *pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
371 return std::make_pair(
false,
String(
_(
"Cannot create temporary buffer.")));
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++)
380 const uint8_t px = img.
At(x, y) ? 255 : 0;
381 int o2 = x + x + x + y * rs;
390 utfname = g_locale_to_utf8(fname.
CStr(), -1, NULL, &i, NULL);
392 filename = g_filename_from_utf8(utfname, -1, NULL, &i, NULL);
394 gchar *tinfo = g_locale_to_utf8(
"quality", -1, NULL, &i, NULL);
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);
406 res =
String(
_(
"Cannot save file. ")) + err->message;
409 return std::make_pair(ok, res);
413 #ifdef CRN_USING_GDIPLUS
414 static std::pair<bool, String> save_jpeg_gdiplus(
const Path &filename,
const ImageBW &img,
int qual)
417 Path winname(filename);
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);
424 Gdiplus::Bitmap *outbm =
new Gdiplus::Bitmap(INT(img.
GetWidth()), INT(img.
GetHeight()), PixelFormat24bppRGB);
427 return std::make_pair(
false,
String(
_(
"Cannot create bitmap")));
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;
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;
442 outbm->UnlockBits(&bitmapData);
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);
454 return std::make_pair(
true,
String{});
471 _(
"Null file name."));
475 std::pair<bool, String> res(std::make_pair(
false,
String(U
"")));
477 #ifdef CRN_USING_LIBJPEG
478 if (res.first ==
false)
480 res = save_jpeg_libjpeg(fname, img, qual);
481 error += U
" " + res.second;
484 #ifdef CRN_USING_GDIPLUS
485 if (res.first ==
false)
487 res = save_jpeg_gdiplus(fname, img, qual);
488 error += U
" " + res.second;
491 #ifdef CRN_USING_GDKPB
492 if (res.first ==
false)
494 res = save_jpeg_gdkpixbuf(fname, img, qual);
495 error += U
" " + res.second;
498 if (res.first ==
false)
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));
509 for (
size_t y = 0; y < img.
GetHeight(); y++)
512 for (x = 0; x < img.
GetWidth(); x++)
515 h.
SetBin(y, (
unsigned int)x);
526 for (
size_t y = 0; y < img.
GetHeight(); y++)
529 for (x =
int(img.
GetWidth()) - 1; x >= 0; --x)
543 for (
size_t x = 0; x < img.
GetWidth(); x++)
549 h.
SetBin(x, (
unsigned int)y);
560 for (
size_t x = 0; x < img.
GetWidth(); x++)
563 for (y =
int(img.
GetHeight()) - 1; y >= 0; --y)
577 for (
size_t y = 0; y < img.
GetHeight(); y++)
579 unsigned int cnt = 0;
580 for (
size_t x = 0; x < img.
GetWidth(); x++)
594 for (
size_t x = 0; x < img.
GetWidth(); x++)
596 unsigned int cnt = 0;
597 for (
size_t y = 0; y < img.
GetHeight(); y++)
610 const auto cos_theta = theta.
Cos();
611 const auto sin_theta = theta.
Sin();
613 const auto head = double(img.
GetWidth()) * cos_theta;
614 const auto tail = double(img.
GetHeight()) * sin_theta;
616 auto hist =
Histogram(
size_t(tail + head) + 1);
618 for (
size_t l = 0; l < img.
GetHeight(); l++)
620 auto previous_pixel = bool(img.
At(0, l));
625 auto y = double(img.
GetHeight()) -
double(l);
627 auto x_prime = x * cos_theta - y * sin_theta;
629 hist.IncBin((
size_t)(x_prime + tail), 1);
632 for (
size_t c = 1; c < img.
GetWidth(); c++)
634 auto current_pixel = img.
At(c, l);
639 auto y = double(img.
GetHeight()) -
double(l);
641 auto x_prime = x * cos_theta - y * sin_theta;
643 hist.IncBin(
size_t(x_prime + tail), 1);
646 previous_pixel = current_pixel;
663 for (
size_t y = 0; y < img.
GetHeight(); y++)
665 auto beg = size_t(0);
666 for (
size_t x = 1; x < img.
GetWidth(); x++)
668 if (img.
At(x - 1, y) && !img.
At(x, y))
672 else if (!img.
At(x - 1, y) && img.
At(x, y))
674 sum += long(x) - long(beg);
679 return (
double)sum / (double)cnt;
692 const auto RUNFACT = 2;
697 for (
size_t y = 0; y < img.
GetHeight(); y++)
699 auto beg = size_t(0);
700 for (
size_t x = 1; x < img.
GetWidth(); x++)
702 if (!img.
At(x - 1, y) && img.
At(x, y))
706 else if (img.
At(x - 1, y) && !img.
At(x, y))
708 if (
int(x - beg) < blackrun * RUNFACT)
710 sum += long(x) - long(beg);
719 return (
double)sum / (double)cnt;
732 for (
size_t x = 0; x < img.
GetWidth(); x++)
736 for (
size_t y = 1; y < img.
GetHeight(); y++)
738 if (img.
At(x, y - 1) && !img.
At(x, y))
743 else if (!img.
At(x, y - 1) && img.
At(x, y))
747 sum += long(y) - long(beg);
756 return (
double)sum / (double)cnt;
766 auto cnt = size_t(0);
780 auto cnt = size_t(0);
799 if (min_neighbors > 7)
806 for (
auto y =
size_t(1); y < img.
GetHeight() - 1; ++y)
807 for (
auto x =
size_t(1); x < img.
GetWidth() - 1; ++x)
812 if (!img.
At(x - 1, y - 1))
814 if (!img.
At(x, y - 1))
816 if (!img.
At(x + 1, y - 1))
818 if (!img.
At(x + 1, y))
820 if (!img.
At(x + 1, y + 1))
822 if (!img.
At(x, y + 1))
824 if (!img.
At(x - 1, y + 1))
826 if (!img.
At(x - 1, y))
828 if (n <= min_neighbors)
830 npix.
At(x, y) = pixel::BWWhite;
836 img = std::move(npix);
856 throw ExceptionDimension(
StringUTF8(
"ImageIntGray DistanceTransform(const ImageBW &img, const MatrixInt *m1, const MatrixInt *m2): ") +
_(
"matrices of different sizes"));
860 throw ExceptionInvalidArgument(
StringUTF8(
"ImageIntGray DistanceTransform(const ImageBW &img, const MatrixInt &m1, const MatrixInt &m2): ") +
_(
"even matrix dimensions."));
866 for (
auto tmp :
Range(img))
867 dt.
At(tmp) = img.
At(tmp) ? 0 : 1;
869 for (
size_t y = hh; y < img.
GetHeight() - hh; y++)
870 for (
size_t x = hw; x < img.
GetWidth() - hw; x++)
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++)
877 auto mval = m1.
At(l, c);
880 int imval = dt.
At(x + c - hw, y + l - hh);
882 if (imval < val) val = imval;
888 for (
size_t y = img.
GetHeight() - hh - 1; y >= hh; y--)
889 for (
size_t x = img.
GetWidth() - hw - 1; x >= hw; x--)
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++)
896 int mval = m2.
At(l, c);
899 int imval = dt.
At(x + c - hw, y + l - hh);
901 if (imval < val) val = imval;
ScalarRange< T > Range(T b, T e)
Creates a range [[b, e[[.
size_t GetRows() const noexcept
Returns the number of rows.
size_t GetCols() const noexcept
Returns the number of columns.
size_t Regularize(ImageBW &img, size_t min_neighbors=0)
Removes isolated pixels and smooths edges.
Image< int > ImageIntGray
Int grayscale image class.
Histogram VerticalSlantedProjection(const ImageBW &img, const Angle< Radian > &theta)
Computes the vertical projection after rotation.
std::vector< pixel_type >::reference At(size_t x, size_t y) noexcept
Returns a reference to a pixel.
size_t GetHeight() const noexcept
size_t CountBlackPixels(const ImageBW &img) noexcept
Returns the number of black pixels.
ImageIntGray DistanceTransform(const ImageBW &img, const MatrixInt &m1, const MatrixInt &m2)
Creates an image containing the distance transform.
void SavePNG(const ImageBW &img, const Path &fname)
Saves as PNG file.
#define FOREACHPIXEL(x, y, img)
Convenience macro to sweep an image.
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.
const char * CStr() const noexcept
Conversion to UTF8 cstring.
double MeanBlackRun(const ImageBW &img) noexcept
Gets the mean horizontal black run.
A convenience class for file paths.
void SetBin(size_t k, unsigned int v)
Modify a bin value.
double Sin() const noexcept
Computes sine.
double MeanBlackVRun(const ImageBW &img) noexcept
Gets the mean vertical black run.
double Cos() const noexcept
Computes cosine.
Histogram BottomProfile(const ImageBW &img)
Computes the bottom profile.
void fclose_if_not_null(FILE *f)
Histogram TopProfile(const ImageBW &img)
Computes the top profile.
const T & At(size_t pos) const noexcept
Mother class for integer histograms.
double MeanWhiteRun(const ImageBW &img, int blackrun=-1) noexcept
Gets the mean horizontal white run.
void SaveJPEG(const ImageBW &img, const Path &fname, unsigned int qual)
Saves as JPEG file.
size_t GetWidth() const noexcept
Path & ToLocal()
Converts the path to the local format.
Histogram HorizontalProjection(const ImageBW &img)
Computes the horizontal projection.
A character string class.
Histogram RightProfile(const ImageBW &img)
Computes the right profile.
Histogram LeftProfile(const ImageBW &img)
Computes the left profile.
size_t CountWhitePixels(const ImageBW &img) noexcept
Returns the number of white pixels.
Histogram VerticalProjection(const ImageBW &img)
Computes the vertical projection.
Invalid argument error (e.g.: nullptr pointer)