/* Seven Segment Optical Character Recognition Image Processing Functions */ /* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Copyright (C) 2004-2025 Erik Auerswald */ /* ImLib2 Header */ #include /* needed by Imlib2.h */ #include /* standard things */ #include /* puts, printf, BUFSIZ, perror, FILE */ #include /* exit */ /* string manipulation */ #include /* strcasecmp, strcmp, strrchr */ /* trigonometry */ #include /* sin, cos, M_PI */ #ifndef M_PI /* sometimes, M_PI is not defined */ #define M_PI 3.14159265358979323846 #endif /* my headers */ #include "defines.h" /* defines */ #include "imgproc.h" /* image processing */ #include "help.h" /* online help */ /* global variables */ extern int ssocr_foreground; extern int ssocr_background; /* functions */ /*** image processing ***/ /* set foreground color */ void set_fg_color(int color) { ssocr_foreground = color; } /* set background color */ void set_bg_color(int color) { ssocr_background = color; } /* set imlib color */ void ssocr_set_color(fg_bg_t color) { switch(color) { case FG: imlib_context_set_color(ssocr_foreground, ssocr_foreground, ssocr_foreground, 255); break; case BG: imlib_context_set_color(ssocr_background, ssocr_background, ssocr_background, 255); break; default: fprintf(stderr, "%s: error: ssocr_set_color(): unknown color %d\n", PROG, color); exit(99); break; } } /* draw a fore- or background pixel */ void draw_fg_bg_pixel(Imlib_Image *image, int x, int y, fg_bg_t color) { Imlib_Image *current_image; /* save current image */ current_image = imlib_context_get_image(); imlib_context_set_image(*image); ssocr_set_color(color); imlib_image_draw_pixel(x,y,0); imlib_context_set_image(current_image); } /* draw a foreground pixel */ void draw_fg_pixel(Imlib_Image *image, int x, int y) { draw_fg_bg_pixel(image, x, y, FG); } /* draw a background pixel */ void draw_bg_pixel(Imlib_Image *image, int x, int y) { draw_fg_bg_pixel(image, x, y, BG); } /* draw a pixel of a given color */ void draw_color_pixel(Imlib_Image *image, int x, int y, Imlib_Color color) { Imlib_Image *current_image; /* save current image */ current_image = imlib_context_get_image(); imlib_context_set_image(*image); imlib_context_set_color(color.red, color.green, color.blue, color.alpha); imlib_image_draw_pixel(x, y, 0); imlib_context_set_image(current_image); } /* check if a pixel is set regarding current foreground/background colors */ int is_pixel_set(int value, double threshold) { switch(ssocr_foreground) { case SSOCR_BLACK: if(value < threshold/100.0*MAXRGB) { return 1; } else { return 0; } break; case SSOCR_WHITE: if(value >= threshold/100.0*MAXRGB) { return 1; } else { return 0; } break; default: fprintf(stderr, "%s: error: is_pixel_set(): foreground color neither" " black nor white\n", PROG); exit(99); break; } } /* set pixels that have at least mask pixels around it set (including the * examined pixel itself) to black (foreground), all other pixels to white * (background) */ Imlib_Image set_pixels_filter(Imlib_Image *source_image, double thresh, luminance_t lt, int mask) { Imlib_Image new_image; /* construct filtered image here */ Imlib_Image current_image; /* save image pointer */ int height, width; /* image dimensions */ int x,y,i,j; /* iteration variables */ int set_pixel; /* should pixel be set or not? */ Imlib_Color color; int lum; /* luminance value of pixel */ /* save pointer to current image */ current_image = imlib_context_get_image(); /* create a new image */ imlib_context_set_image(*source_image); height = imlib_image_get_height(); width = imlib_image_get_width(); new_image = imlib_clone_image(); /* check for every pixel if it should be set in filtered image */ for(x=0; x=0 && i=0 && j= mask) { draw_fg_pixel(&new_image, x, y); } else { draw_bg_pixel(&new_image, x, y); } } } /* restore image from before function call */ imlib_context_set_image(current_image); /* return filtered image */ return new_image; } Imlib_Image set_pixels_filter_iter(Imlib_Image *source_image, double thresh, luminance_t lt, int mask, int iter) { int i; Imlib_Image temp_image1, temp_image2; imlib_context_set_image(*source_image); temp_image1 = temp_image2 = imlib_clone_image(); for(i=0; i=0 && i=0 && j mask) { draw_fg_pixel(&new_image, x, y); } else { draw_bg_pixel(&new_image, x, y); } } } /* restore image from before function call */ imlib_context_set_image(current_image); /* return filtered image */ return new_image; } Imlib_Image remove_isolated(Imlib_Image *source_image, double thresh, luminance_t lt) { return keep_pixels_filter(source_image, thresh, lt, 1); } /* gray stretching, i.e. lum lum=0, lum>t2 => lum=100, * else lum=((lum-t1)*MAXRGB)/(t2-t1) */ Imlib_Image gray_stretch(Imlib_Image *source_image, double t1, double t2, luminance_t lt) { Imlib_Image new_image; /* construct filtered image here */ Imlib_Image current_image; /* save image pointer */ int height, width; /* image dimensions */ int x,y; /* iteration variables */ Imlib_Color color; int lum; /* luminance value of pixel */ /* do nothing if t1>=t2 */ if(t1 >= t2) { fprintf(stderr, "%s: error: gray_stretch(): t1=%.2f >= t2=%.2f\n", PROG, t1, t2); exit(99); } /* check if 0 < t1,t2 < MAXRGB */ if(t1 <= 0.0) { fprintf(stderr, "%s: error: gray_stretch(): t1=%.2f <= 0.0\n", PROG, t1); exit(99); } if(t2 >= MAXRGB) { fprintf(stderr, "%s: error: gray_stretch(): t2=%.2f >= %d.0\n", PROG, t2, MAXRGB); exit(99); } /* save pointer to current image */ current_image = imlib_context_get_image(); /* create a new image */ imlib_context_set_image(*source_image); height = imlib_image_get_height(); width = imlib_image_get_width(); new_image = imlib_clone_image(); /* gray stretch image */ for(x=0; x=t2) { imlib_context_set_color(MAXRGB, MAXRGB, MAXRGB, color.alpha); } else { imlib_context_set_color(clip(((lum-t1)*255)/(t2-t1),0,255), clip(((lum-t1)*255)/(t2-t1),0,255), clip(((lum-t1)*255)/(t2-t1),0,255), color.alpha); } imlib_image_draw_pixel(x, y, 0); imlib_context_set_image(*source_image); } } /* restore image from before function call */ imlib_context_set_image(current_image); /* return filtered image */ return new_image; } /* use dynamic (aka adaptive) local thresholding to create monochrome image */ /* ww and wh are the width and height of the rectangle used to find the * threshold value */ Imlib_Image dynamic_threshold(Imlib_Image *source_image,double t,luminance_t lt, int ww, int wh) { Imlib_Image new_image; /* construct filtered image here */ Imlib_Image current_image; /* save image pointer */ int height, width; /* image dimensions */ int x,y; /* iteration variables */ Imlib_Color color; int lum; double thresh; /* save pointer to current image */ current_image = imlib_context_get_image(); /* create a new image */ imlib_context_set_image(*source_image); height = imlib_image_get_height(); width = imlib_image_get_width(); new_image = imlib_clone_image(); /* check for every pixel if it should be set in filtered image */ for(x=0; x", t); t = get_threshold(image, thresh/100.0, lt, 0, 0, -1, -1); if(flags & DEBUG_OUTPUT) fprintf(stderr, " %f\n", t); if(flags & DO_ITERATIVE_THRESHOLD) { if(flags & DEBUG_OUTPUT) fprintf(stderr, "doing iterative_thresholding: %f ->", t); t = iterative_threshold(image, t, lt); if(flags & DEBUG_OUTPUT) fprintf(stderr, " %f\n", t); } is_adapted = 1; } if((flags & VERBOSE) || (flags & DEBUG_OUTPUT)) { fprintf(stderr, "using threshold %.2f\n", t); } return t; } /* compute dynamic threshold value from the rectangle (x,y),(x+w,y+h) of * source_image */ double get_threshold(Imlib_Image *source_image, double fraction, luminance_t lt, int x, int y, int w, int h) { Imlib_Image current_image; /* save image pointer */ int height, width; /* image dimensions */ int xi,yi; /* iteration variables */ Imlib_Color color; int lum; /* luminance of pixel */ double minval=(double)MAXRGB, maxval=0.0; /* save pointer to current image */ current_image = imlib_context_get_image(); /* get image dimensions */ imlib_context_set_image(*source_image); height = imlib_image_get_height(); width = imlib_image_get_width(); /* special value -1 for width or height means image width/height */ if(w == -1) w = width; if(h == -1) h = width; /* assure valid coordinates */ if(x+w > width) x = width-w; if(y+h > height) y = height-h; if(x<0) x=0; if(y<0) y=0; /* find the threshold value to differentiate between dark and light */ for(xi=0; (xi maxval) maxval = lum; } } /* restore image from before function call */ imlib_context_set_image(current_image); return (minval + fraction * (maxval - minval)) * 100 / MAXRGB; } /* determine threshold by an iterative method */ double iterative_threshold(Imlib_Image *source_image, double thresh, luminance_t lt) { Imlib_Image current_image; /* save image pointer */ int height, width; /* image dimensions */ int xi,yi; /* iteration variables */ Imlib_Color color; int lum; /* luminance of pixel */ unsigned int size_white, size_black; /* size of black and white groups */ unsigned long int sum_white, sum_black; /* sum of black and white groups */ unsigned int avg_white, avg_black; /* average values of black and white */ double old_thresh; /* old threshold computed by last iteration step */ double new_thresh; /* new threshold computed by current iteration step */ int thresh_lum; /* luminance value of threshold */ /* normalize threshold (was given as a percentage) */ new_thresh = thresh / 100.0; /* save pointer to current image */ current_image = imlib_context_get_image(); /* get image dimensions */ imlib_context_set_image(*source_image); height = imlib_image_get_height(); width = imlib_image_get_width(); /* find the threshold value to differentiate between dark and light */ do { thresh_lum = MAXRGB * new_thresh; old_thresh = new_thresh; size_black = sum_black = size_white = sum_white = 0; for(xi=0; xi EPSILON); /* restore image from before function call */ imlib_context_set_image(current_image); return new_thresh * 100; } /* get minimum and maximum lum values */ void get_minmaxval(Imlib_Image *source_image, luminance_t lt, double *min, double *max) { Imlib_Image current_image; /* save image pointer */ int w, h; /* image dimensions */ int xi,yi; /* iteration variables */ Imlib_Color color; /* Imlib2 RGBA color structure */ int lum = 0; *min = MAXRGB; *max = 0; /* save pointer to current image */ current_image = imlib_context_get_image(); /* get image dimensions */ imlib_context_set_image(*source_image); h = imlib_image_get_height(); w = imlib_image_get_width(); /* find the minimum value in the image */ for(xi=0; xi *max) *max = lum; } } /* restore image from before function call */ imlib_context_set_image(current_image); } /* draw a white (background) border around image, overwriting image contents * beneath border*/ Imlib_Image white_border(Imlib_Image *source_image, int bdwidth) { Imlib_Image new_image; /* construct filtered image here */ Imlib_Image current_image; /* save image pointer */ int height, width; /* image dimensions */ int x,y; /* coordinates of upper left corner of rectangles */ /* save pointer to current image */ current_image = imlib_context_get_image(); /* create a new image */ imlib_context_set_image(*source_image); height = imlib_image_get_height(); width = imlib_image_get_width(); new_image = imlib_clone_image(); /* assure border width has a legal value */ if(bdwidth > width/2) bdwidth = width/2; if(bdwidth > height/2) bdwidth = height/2; /* draw white (background) rectangle around new image */ for(x=0, y=0; x=shift; x--) { imlib_image_query_pixel(x-shift, y, &color_return); draw_color_pixel(&new_image, x, y, color_return); } /* fill with background */ for(x=0; x= 0) && (sx <= width) && (sy >= 0) && (sy <= height)) { imlib_image_query_pixel(sx, sy, &c); draw_color_pixel(&new_image, x, y, c); } else { draw_bg_pixel(&new_image, x, y); } } } /* restore image from before function call */ imlib_context_set_image(current_image); /* return filtered image */ return new_image; } /* mirror image horizontally or vertically */ Imlib_Image mirror(Imlib_Image *source_image, direction_t direction) { Imlib_Image new_image; /* construct filtered image here */ Imlib_Image current_image; /* save image pointer */ int height, width; /* image dimensions */ int x,y; /* iteration variables / target coordinates */ Imlib_Color c; /* for imlib_query_pixel() */ /* save pointer to current image */ current_image = imlib_context_get_image(); /* create a new image */ imlib_context_set_image(*source_image); height = imlib_image_get_height(); width = imlib_image_get_width(); new_image = imlib_clone_image(); /* create mirrored image */ if(direction == HORIZONTAL) { for(x = width-1; x>=0; x--) { for(y = 0; y < height; y++) { imlib_image_query_pixel(width - 1 - x, y, &c); draw_color_pixel(&new_image, x, y, c); } } } else if(direction == VERTICAL) { for(x = 0; x < width; x++) { for(y = height-1; y >= 0; y--) { imlib_image_query_pixel(x, height - 1 - y, &c); draw_color_pixel(&new_image, x, y, c); } } } /* restore image from before function call */ imlib_context_set_image(current_image); /* return filtered image */ return new_image; } /* turn image to grayscale */ Imlib_Image grayscale(Imlib_Image *source_image, luminance_t lt) { Imlib_Image new_image; /* construct grayscale image here */ Imlib_Image current_image; /* save image pointer */ int height, width; /* image dimensions */ int x,y; /* iteration variables */ Imlib_Color color; /* Imlib2 color structure */ int lum=0; /* save pointer to current image */ current_image = imlib_context_get_image(); /* create a new image */ imlib_context_set_image(*source_image); height = imlib_image_get_height(); width = imlib_image_get_width(); new_image = imlib_clone_image(); /* transform image to grayscale */ for(x=0; x= width) x = width - 1; if(y >= height) y = height - 1; if(x + w > width) w = width - x; if(y + h > height) h = height - x; /* create the new image */ imlib_context_set_image(*source_image); new_image = imlib_create_cropped_image(x, y, w, h); /* restore image from before function call */ imlib_context_set_image(current_image); /* return filtered image */ return new_image; } /* compute luminance from RGB values */ int get_lum(Imlib_Color *color, luminance_t lt) { switch(lt) { case REC709: return get_lum_709(color); case REC601: return get_lum_601(color); case LINEAR: return get_lum_lin(color); case MINIMUM: return get_lum_min(color); case MAXIMUM: return get_lum_max(color); case RED: return get_lum_red(color); case GREEN: return get_lum_green(color); case BLUE: return get_lum_blue(color); default: fprintf(stderr, "%s: error: get_lum(): unknown transfer function" " no. %d\n", PROG, lt); exit(99); } } /* compute luminance Y_709 from linear RGB values */ int get_lum_709(Imlib_Color *color) { return 0.2125*color->red + 0.7154*color->green + 0.0721*color->blue; } /* compute luminance Y_601 from gamma corrected (non-linear) RGB values */ int get_lum_601(Imlib_Color *color) { return 0.299*color->red + 0.587*color->green + 0.114*color->blue; } /* compute luminance Y = (R+G+B)/3 */ int get_lum_lin(Imlib_Color *color) { return (color->red + color->green + color->blue) / 3; } /* compute luminance Y = min(R,G,B) as used in GNU Ocrad 0.14 */ int get_lum_min(Imlib_Color *color) { return (color->red < color->green) ? ((color->red < color->blue) ? color->red : color->blue) : ((color->green < color->blue) ? color->green : color->blue); } /* compute luminance Y = max(R,G,B) */ int get_lum_max(Imlib_Color *color) { return (color->red > color->green) ? ((color->red > color->blue) ? color->red : color->blue) : ((color->green > color->blue) ? color->green : color->blue); } /* compute luminance Y = R */ int get_lum_red(Imlib_Color *color) { return color->red; } /* compute luminance Y = G */ int get_lum_green(Imlib_Color *color) { return color->green; } /* compute luminance Y = B */ int get_lum_blue(Imlib_Color *color) { return color->blue; } /* clip value thus that it is in the given interval [min,max] */ int clip(int value, int min, int max) { return (value < min) ? min : ((value > max) ? max : value); } /* save image to file */ void save_image(const char *image_type, Imlib_Image *image, const char *fmt, const char *filename, unsigned int flags) { const char *tmp; Imlib_Image *current_image; Imlib_Load_Error save_error=0; const char *const stdout_file = "/proc/self/fd/1"; current_image = imlib_context_get_image(); imlib_context_set_image(image); /* interpret - as STDOUT */ if(strcmp("-", filename) == 0) filename = stdout_file; /* get file format for image */ if(fmt) { /* use provided format string */ tmp = fmt; } else { /* use file name extension */ tmp = strrchr(filename, '.'); if(tmp) tmp++; } if(tmp) { if(flags & VERBOSE) fprintf(stderr, "using %s format for %s image\n", tmp, image_type); imlib_image_set_format(tmp); } else { /* use png as default */ if(flags & VERBOSE) fprintf(stderr, "using png format for %s image\n", image_type); imlib_image_set_format("png"); } /* write image to disk */ if(flags & VERBOSE) fprintf(stderr, "writing %s image to file %s\n", image_type, filename); imlib_save_image_with_error_return(filename, &save_error); if(save_error && save_error != IMLIB_LOAD_ERROR_NONE) { fprintf(stderr, "%s: error saving image file %s\n", PROG, filename); report_imlib_error(save_error); } imlib_context_set_image(current_image); } /* parse KEYWORD from --luminace option */ luminance_t parse_lum(char *keyword) { if(strcasecmp(keyword, "help") == 0) { print_lum_help(); exit(42); } else if(strcasecmp(keyword, "rec601") == 0) { return REC601; } else if(strcasecmp(keyword, "rec709") == 0) { return REC709; } else if(strcasecmp(keyword, "linear") == 0) { return LINEAR; } else if(strcasecmp(keyword, "minimum") == 0) { return MINIMUM; } else if(strcasecmp(keyword, "maximum") == 0) { return MAXIMUM; } else if(strcasecmp(keyword, "red") == 0) { return RED; } else if(strcasecmp(keyword, "green") == 0) { return GREEN; } else if(strcasecmp(keyword, "blue") == 0) { return BLUE; } else { return LUM_PARSE_ERROR; } } /* report Imlib2 load/save error to stderr */ void report_imlib_error(Imlib_Load_Error error) { fputs(PROG ": Imlib2 error code: ",stderr); switch (error) { case IMLIB_LOAD_ERROR_NONE: fputs("IMLIB_LOAD_ERROR_NONE\n", stderr); break; case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST: fputs("IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST\n", stderr); break; case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY: fputs("IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY\n", stderr); break; case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ: fputs("IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ\n", stderr); break; case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT: fputs("IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT\n", stderr); break; case IMLIB_LOAD_ERROR_PATH_TOO_LONG: fputs("IMLIB_LOAD_ERROR_PATH_TOO_LONG\n", stderr); break; case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT: fputs("IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT\n", stderr); break; case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY: fputs("IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY\n", stderr); break; case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE: fputs("IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE\n", stderr); break; case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS: fputs("IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS\n", stderr); break; case IMLIB_LOAD_ERROR_OUT_OF_MEMORY: fputs("IMLIB_LOAD_ERROR_OUT_OF_MEMORY\n", stderr); break; case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS: fputs("IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS\n", stderr); break; case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE: fputs("IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE\n", stderr); break; case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE: fputs("IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE\n", stderr); break; case IMLIB_LOAD_ERROR_UNKNOWN: fputs("IMLIB_LOAD_ERROR_UNKNOWN\n", stderr); break; default: fprintf(stderr, "unknown error code %d, please report\n", error); break; } }