Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 443b100ab9 | |||
| 63495ef4a8 | |||
| 9d9b2253e1 | |||
| e914669079 | |||
| 94ce321060 | |||
| 7cae796188 | |||
| 7f2a9f3f22 | |||
| 00312ee70e | |||
| e309861f24 | |||
| 4b5c6ea770 | |||
| 56d1b22118 | |||
| eaf0f32850 | |||
| fa30f473e7 | |||
| 67abe0fba8 | |||
| ac060de96b | |||
| 74aeb44f94 | |||
| f012c14d93 | |||
| b44a4ad72a | |||
| 13a5ec3802 | |||
| 2824d0aea7 | |||
| 5439679c83 | |||
| e7a20fbb2d | |||
| 88a627050a | |||
| 6e8c8361fd | |||
| ad5b6da118 | |||
| 7485ba3ffc | |||
| acdf26c6bf | |||
| 4ab3869eea | |||
| 70692aa5c4 | |||
| 9964c8ce85 | |||
| 895b0cb2b5 | |||
| 894f3035fd | |||
| 40c34ad6d7 | |||
| bae7381e34 | |||
| d6be469e14 | |||
| af6c53c3f3 | |||
| b05410efae | |||
| 6569ca9f6b | |||
| 48da4ee8c1 | |||
| 1a8c4d7701 | |||
| 624e998efb | |||
| 81c5472b69 | |||
| 6fc62729e9 | |||
| 19f93dcb58 | |||
| 5ca3455873 | |||
| 2026f39ee7 | |||
| 73e7a69b4c | |||
| f25047745d | |||
| 658230679a | |||
| 1fe392c772 | |||
| 2ff3d882b9 | |||
| f06f8441fd | |||
| a5c550b72b | |||
| 0c3adc9002 | |||
| ad9027b6a0 | |||
| 66dbee3591 | |||
| c6e09b8f70 | |||
| 7e056835fa | |||
| 050830a709 | |||
| 27e7913135 |
@@ -23,9 +23,10 @@ build a .deb package by typing:
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
- A C compiler compatible with GCC.
|
||||
- A system sufficiently compatible to POSIX and GNU.
|
||||
- POSIX utilities: chmod, printf, sh, sed (or compatible)
|
||||
- GNU utilities: date, gzip, install, tar (or compatible)
|
||||
- POSIX utilities: chmod, head, printf, sh, sed (or compatible)
|
||||
- GNU utilities: date, gzip, install, make, tar (or compatible)
|
||||
- Other utilities: bzip2
|
||||
- Imlib2 shared library and development headers. On a Debian or Ubuntu system
|
||||
they are provided by the packages libimlib2 and libimlib2-dev.
|
||||
@@ -37,8 +38,11 @@ Prerequisites
|
||||
- Imlib2 requires the X11/Xlib.h header and links against some X11 libraries,
|
||||
at least on GNU/Linux with X11 GUI, thus X11 shared libraries and development
|
||||
headers are required for ssocr, too, although ssocr does not use X11 itself
|
||||
- Build tools, e.g. build-essential on a Debian (or Ubuntu) system.
|
||||
- Build tools, e.g., build-essential on a Debian (or Ubuntu) system, usually
|
||||
contain both make and a C compiler.
|
||||
- To build a .deb package, you probably need the debhelper package.
|
||||
- To create an HTML version of the man page, you need a man utility that can
|
||||
produce HTML output.
|
||||
|
||||
Additional Makefile Targets
|
||||
---------------------------
|
||||
@@ -93,6 +97,6 @@ https://www.unix-ag.uni-kl.de/~auerswal/ssocr/
|
||||
|
||||
Third Party Packages
|
||||
--------------------
|
||||
There exist third party packaging efforts for e.g. GNU/Linux distributions
|
||||
There exist third party packaging efforts for, e.g., GNU/Linux distributions
|
||||
and FreeBSD. Thus you can search the packaging system of your distribution
|
||||
for an ssocr package.
|
||||
|
||||
@@ -8,8 +8,9 @@ BINDIR := $(PREFIX)/bin
|
||||
MANDIR := $(PREFIX)/share/man/man1
|
||||
DOCDIR := $(PREFIX)/share/doc/ssocr
|
||||
DOCS := AUTHORS COPYING INSTALL README THANKS NEWS
|
||||
VERSION := $(shell sed -n 's/^.*VERSION.*\(".*"\).*/\1/p' defines.h)
|
||||
VERSION := $(shell sed -n 's/^.*VERSION.*"\(.*\)".*/\1/p' defines.h)
|
||||
CRYEARS := $(shell sed -n 's/^.*fprintf.*Copyright.*\(2004-2[0-9][0-9][0-9]\).*Erik.*Auerswald.*$$/\1/p' help.c)
|
||||
RELDATE := $(shell sed -n 's/^Version [.0-9]* .\([-0-9]*\).*$$/\1/p' NEWS | head -n1)
|
||||
|
||||
all: ssocr ssocr.1
|
||||
|
||||
@@ -20,9 +21,9 @@ imgproc.o: imgproc.c defines.h imgproc.h help.h Makefile
|
||||
help.o: help.c defines.h imgproc.h help.h Makefile
|
||||
charset.o: charset.c charset.h defines.h help.h Makefile
|
||||
|
||||
ssocr.1: ssocr.1.in Makefile defines.h
|
||||
ssocr.1: ssocr.1.in Makefile defines.h help.c NEWS
|
||||
sed -e 's/@VERSION@/$(VERSION)/' \
|
||||
-e "s/@DATE@/$(shell date +%Y-%m-%d)/" \
|
||||
-e 's/@DATE@/$(RELDATE)/' \
|
||||
-e 's/@CRYEARS@/$(CRYEARS)/' <$< >$@
|
||||
|
||||
ssocr-manpage.html: ssocr.1
|
||||
@@ -43,7 +44,7 @@ ssocr-dir:
|
||||
chmod +x ssocr-$(VERSION)/notdebian/rules
|
||||
|
||||
notdebian/changelog:
|
||||
printf "ssocr ($(VERSION)-1) unstable; urgency=low\n\n * self built package of current ssocr version in .deb format\n\n -- $(USER) $(shell date -R)\n" >$@
|
||||
printf 'ssocr ($(VERSION)-1) unstable; urgency=low\n\n * self built package of current ssocr version in .deb format\n\n -- $(USER) $(shell date -R)\n' >$@
|
||||
|
||||
selfdeb: notdebian/changelog notdebian/control notdebian/rules ssocr-dir
|
||||
(cd ssocr-$(VERSION); ln -sv notdebian debian; fakeroot debian/rules binary; fakeroot debian/rules clean; rm -f debian)
|
||||
|
||||
@@ -1,6 +1,43 @@
|
||||
Noteworthy Changes in ssocr Releases
|
||||
====================================
|
||||
|
||||
Version 2.25.1 (2025-10-31):
|
||||
----------------------------
|
||||
* Fix one debug message to be printed only with -P, --debug-output
|
||||
|
||||
Version 2.25.0 (2025-03-23):
|
||||
----------------------------
|
||||
* New option -F, --adapt-after-crop to skip threshold adjustment directly
|
||||
before cropping the image
|
||||
* Print warning when the two options -a, --absolute-threshold and -T,
|
||||
--iter-threshold are used together because -a inhibits -T
|
||||
* Improved performance when reading image data via standard input
|
||||
* Improved performance when using gray_stretch together with -g
|
||||
* Improved performance when using option -p, --process-only together
|
||||
with only the grayscale and/or mirror commands
|
||||
* Documentation improvements
|
||||
|
||||
Version 2.24.1 (2024-12-11):
|
||||
----------------------------
|
||||
* Print warning when an unknown charset name is ignored
|
||||
* Print warning when an unknown luminance formula name is ignored
|
||||
* Fix one debug message to be printed only with -P, --debug-output
|
||||
* Documentation improvements
|
||||
|
||||
Version 2.24.0 (2024-06-22):
|
||||
----------------------------
|
||||
* Fix decimal separator recognition when widest digit is a one
|
||||
* Add recognition of lower case variant of character 'h' to full charset
|
||||
* Add recognition of lower case variant of character 'r' to full charset
|
||||
* Documentation improvements
|
||||
* Error, warning, and debug message improvements
|
||||
|
||||
Version 2.23.1 (2023-05-18):
|
||||
----------------------------
|
||||
* The man page uses the latest ssocr release date as its date (to help
|
||||
in creating reproducible builds)
|
||||
* Documentation improvements
|
||||
|
||||
Version 2.23.0 (2023-05-01):
|
||||
----------------------------
|
||||
* New option -N, --min-segment to specify the minimum width and height of
|
||||
|
||||
@@ -7,7 +7,7 @@ as well), Mac OS X (Homebrew can be used to install the library Imlib2,
|
||||
used by ssocr), and even on Windows (using Cygwin). ssocr should work
|
||||
on any UNIX-like or POSIX compatible operating system.
|
||||
|
||||
Unless ssocr is installed via some packaging system, e.g. from a GNU/Linux
|
||||
Unless ssocr is installed via some packaging system, e.g., from a GNU/Linux
|
||||
distribution, it is distributed in source form and needs to be built
|
||||
before it can be used. See the INSTALL file for instructions on how to
|
||||
build ssocr.
|
||||
@@ -19,7 +19,7 @@ quotes).
|
||||
You can get the current ssocr version from the official ssocr website:
|
||||
https://www.unix-ag.uni-kl.de/~auerswal/ssocr/
|
||||
(Links to ssocr should point to the official website, not to a convenience
|
||||
copy of the development repository, e.g. on GitHub.)
|
||||
copy of the development repository, e.g., on GitHub.)
|
||||
|
||||
I am usually quicker to reply to emails than to GitHub issues. But
|
||||
increasingly Google blocks emails sent by me, so if you do not receive
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2018-2023 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
/* Copyright (C) 2018-2025 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
|
||||
/* standard things */
|
||||
#include <stdio.h> /* puts, printf, BUFSIZ, perror, FILE */
|
||||
@@ -44,7 +44,7 @@ charset_t parse_charset(char *keyword)
|
||||
} else if(strncasecmp(keyword, "tt_robot", 8) == 0) {
|
||||
return CS_TT_ROBOT;
|
||||
} else {
|
||||
return DEFAULT_CHARSET;
|
||||
return CS_PARSE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,9 +86,13 @@ void init_charset(charset_t cs)
|
||||
charset_array[D_T] = 't';
|
||||
charset_array[D_L] = 'l';
|
||||
charset_array[D_H] = 'h';
|
||||
charset_array[D_h] = 'h';
|
||||
charset_array[D_R] = 'r';
|
||||
charset_array[D_ALT_R] = 'r';
|
||||
charset_array[D_r] = 'r';
|
||||
charset_array[D_P] = 'p';
|
||||
charset_array[D_N] = 'n';
|
||||
charset_array[D_n] = 'n';
|
||||
charset_array[D_Y] = 'y';
|
||||
charset_array[D_J] = 'j';
|
||||
break;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2018-2023 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
/* Copyright (C) 2018-2025 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
|
||||
#ifndef SSOCR2_CHARSET_H
|
||||
#define SSOCR2_CHARSET_H
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2004-2023 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
/* Copyright (C) 2004-2025 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
/* Copyright (C) 2013 Cristiano Fontana <fontanacl@ornl.gov> */
|
||||
|
||||
#define PROG "ssocr"
|
||||
@@ -23,7 +23,7 @@
|
||||
#define SSOCR2_DEFINES_H
|
||||
|
||||
/* version number */
|
||||
#define VERSION "2.23.0"
|
||||
#define VERSION "2.25.1"
|
||||
|
||||
/* states */
|
||||
#define FIND_DARK 0
|
||||
@@ -76,6 +76,7 @@
|
||||
#define D_HEX_A (ALL_SEGS & ~HORIZ_DOWN)
|
||||
#define D_HEX_b (ALL_SEGS & ~(HORIZ_UP | VERT_RIGHT_UP))
|
||||
#define D_HEX_C (ALL_SEGS & ~(VERT_RIGHT_UP | HORIZ_MID | VERT_RIGHT_DOWN))
|
||||
/* a C in the lower half can only happen when digit boundary detection fails */
|
||||
#define D_HEX_c (HORIZ_MID | VERT_LEFT_DOWN | HORIZ_DOWN)
|
||||
#define D_HEX_d (ALL_SEGS & ~(HORIZ_UP | VERT_LEFT_UP))
|
||||
#define D_HEX_E (ALL_SEGS & ~(VERT_RIGHT_UP | VERT_RIGHT_DOWN))
|
||||
@@ -84,10 +85,15 @@
|
||||
#define D_T (ALL_SEGS & ~(HORIZ_UP | VERT_RIGHT_UP | VERT_RIGHT_DOWN))
|
||||
#define D_L (D_T & ~HORIZ_MID)
|
||||
#define D_H (ALL_SEGS & ~(HORIZ_UP | HORIZ_DOWN))
|
||||
#define D_h (VERT_LEFT_UP | VERT_LEFT_DOWN | HORIZ_MID | VERT_RIGHT_DOWN)
|
||||
#define D_R (D_ZERO & ~(VERT_RIGHT_DOWN | HORIZ_DOWN))
|
||||
#define D_ALT_R (VERT_LEFT_UP | VERT_LEFT_DOWN | HORIZ_UP)
|
||||
/* an r in the lower half can only happen when digit boundary detection fails */
|
||||
#define D_r (VERT_LEFT_DOWN | HORIZ_MID)
|
||||
#define D_P (D_HEX_F | VERT_RIGHT_UP)
|
||||
#define D_N (D_ZERO & ~HORIZ_DOWN)
|
||||
/* an N in the lower half can only happen when digit boundary detection fails *//* define D_LOW_N (VERT_LEFT_DOWN | VERT_RIGHT_DOWN | HORIZ_MID) */
|
||||
/* an N in the lower half can only happen when digit boundary detection fails */
|
||||
#define D_n (VERT_LEFT_DOWN | VERT_RIGHT_DOWN | HORIZ_MID)
|
||||
#define D_Y (ALL_SEGS & ~(HORIZ_UP | VERT_LEFT_DOWN))
|
||||
#define D_J (HORIZ_DOWN | VERT_RIGHT_UP | VERT_RIGHT_DOWN)
|
||||
/* add two "wrong" 7 definitions used in a character set for a Chinese
|
||||
@@ -150,6 +156,7 @@
|
||||
#define OMIT_DECIMAL (1<<10)
|
||||
#define PRINT_SPACES (1<<11)
|
||||
#define SPC_USE_AVG_DST (1<<12)
|
||||
#define ADAPT_AFTER_CROP (1<<13)
|
||||
|
||||
/* colors used by ssocr */
|
||||
#define SSOCR_BLACK 0
|
||||
@@ -166,6 +173,10 @@
|
||||
/* default luminance formula */
|
||||
#define DEFAULT_LUM_FORMULA REC709
|
||||
|
||||
/* when to adapt threshold values to the image */
|
||||
#define INITIAL 0 /* adapt threshold unless is was already adapted */
|
||||
#define UPDATE 1 /* adapt threshold even if it was adapted before */
|
||||
|
||||
/* foreground and background */
|
||||
typedef enum fg_bg_e {
|
||||
FG,
|
||||
@@ -181,7 +192,8 @@ typedef enum luminance_e {
|
||||
MAXIMUM,
|
||||
RED,
|
||||
GREEN,
|
||||
BLUE
|
||||
BLUE,
|
||||
LUM_PARSE_ERROR
|
||||
} luminance_t;
|
||||
|
||||
/* direction, to mirror horizontally or vertically, or for a scanline */
|
||||
@@ -196,7 +208,8 @@ typedef enum charset_e {
|
||||
CS_DIGITS,
|
||||
CS_DECIMAL,
|
||||
CS_HEXADECIMAL,
|
||||
CS_TT_ROBOT
|
||||
CS_TT_ROBOT,
|
||||
CS_PARSE_ERROR
|
||||
} charset_t;
|
||||
|
||||
#define DEFAULT_CHARSET CS_FULL
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2004-2023 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
/* Copyright (C) 2004-2025 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
/* Copyright (C) 2013 Cristiano Fontana <fontanacl@ornl.gov> */
|
||||
|
||||
/* standard things */
|
||||
@@ -92,11 +92,11 @@ void print_version(FILE *f)
|
||||
{
|
||||
fprintf(f, "Seven Segment Optical Character Recognition Version %s\n",
|
||||
VERSION);
|
||||
fprintf(f, "Copyright (C) 2004-2023 by Erik Auerswald"
|
||||
fprintf(f, "Copyright (C) 2004-2025 Erik Auerswald"
|
||||
" <auerswal@unix-ag.uni-kl.de>\n");
|
||||
fprintf(f, "This program comes with ABSOLUTELY NO WARRANTY\n");
|
||||
fprintf(f, "This program comes with ABSOLUTELY NO WARRANTY.\n");
|
||||
fprintf(f, "This is free software, and you are welcome to redistribute it"
|
||||
" under the terms\nof the GNU GPL (version 3 or later)\n");
|
||||
" under the terms\nof the GNU GPL (version 3 or later).\n");
|
||||
}
|
||||
|
||||
/* print short usage */
|
||||
@@ -151,6 +151,8 @@ void usage(char *name, FILE *f)
|
||||
fprintf(f, " -C, --omit-decimal-point omit decimal points from output\n");
|
||||
fprintf(f, " -c, --charset=KEYWORD select recognized characters\n");
|
||||
fprintf(f, " use -c help for list of KEYWORDS\n");
|
||||
fprintf(f, " -F, --adapt-after-crop do not adapt threshold to image directly\n"
|
||||
" before, only after, cropping\n");
|
||||
fprintf(f, "\nCommands: dilation [N] [N times] dilation algorithm"
|
||||
"\n (set_pixels_filter with mask"
|
||||
" of 1 pixel)\n");
|
||||
@@ -179,7 +181,7 @@ void usage(char *name, FILE *f)
|
||||
fprintf(f, " color\n");
|
||||
fprintf(f, " shear OFFSET shear image OFFSET pixels (at bottom) to the\n");
|
||||
fprintf(f, " right\n");
|
||||
fprintf(f, " rotate THETA rotate image by THETA degrees\n");
|
||||
fprintf(f, " rotate THETA rotate image clockwise by THETA degrees\n");
|
||||
fprintf(f, " mirror {horiz|vert} mirror image horizontally or vertically\n");
|
||||
fprintf(f, " crop X Y W H crop image with upper left corner (X,Y) with\n");
|
||||
fprintf(f, " width W and height H\n");
|
||||
@@ -187,26 +189,26 @@ void usage(char *name, FILE *f)
|
||||
fprintf(f, " pixels set (including checked position)\n");
|
||||
fprintf(f, " keep_pixels_filter MASK keeps pixels that have at least MASK neighbor\n");
|
||||
fprintf(f, " pixels set (not counting the checked pixel)\n");
|
||||
fprintf(f, "\nDefaults: needed pixels = %2d\n", NEED_PIXELS);
|
||||
fprintf(f, " minimum segment size = %2d\n", MIN_SEGMENT);
|
||||
fprintf(f, " minimum character width = %2d\n", MIN_CHAR_W);
|
||||
fprintf(f, " minimum character height = %2d\n", MIN_CHAR_H);
|
||||
fprintf(f, " ignored pixels = %2d\n", IGNORE_PIXELS);
|
||||
fprintf(f, " minimum number of digits = %2d\n", NUMBER_OF_DIGITS);
|
||||
fprintf(f, " maximum number of digits = %2d\n", NUMBER_OF_DIGITS);
|
||||
fprintf(f, " threshold = %5.2f\n", THRESHOLD);
|
||||
fprintf(f, " foreground = %s\n",
|
||||
fprintf(f, "\nDefaults: needed pixels = %2d\n", NEED_PIXELS);
|
||||
fprintf(f, " minimum segment size = %2d\n", MIN_SEGMENT);
|
||||
fprintf(f, " minimum character width = %2d\n", MIN_CHAR_W);
|
||||
fprintf(f, " minimum character height = %2d\n", MIN_CHAR_H);
|
||||
fprintf(f, " ignored pixels = %2d\n", IGNORE_PIXELS);
|
||||
fprintf(f, " minimum number of digits = %2d\n", NUMBER_OF_DIGITS);
|
||||
fprintf(f, " maximum number of digits = %2d\n", NUMBER_OF_DIGITS);
|
||||
fprintf(f, " threshold = %5.2f\n", THRESHOLD);
|
||||
fprintf(f, " foreground = %s\n",
|
||||
(SSOCR_DEFAULT_FOREGROUND == SSOCR_BLACK) ? "black" : "white");
|
||||
fprintf(f, " background = %s\n",
|
||||
fprintf(f, " background = %s\n",
|
||||
(SSOCR_DEFAULT_BACKGROUND == SSOCR_BLACK) ? "black" : "white");
|
||||
fprintf(f, " luminance = ");
|
||||
fprintf(f, " luminance = ");
|
||||
print_lum_key(DEFAULT_LUM_FORMULA, f); fprintf(f, "\n");
|
||||
fprintf(f, " height/width threshold for digit one = %2d\n", ONE_RATIO);
|
||||
fprintf(f, " width/height threshold for minus sign = %2d\n", MINUS_RATIO);
|
||||
fprintf(f, " max_dig_h/h threshold for decimal sep = %2d\n", DEC_H_RATIO);
|
||||
fprintf(f, " max_dig_w/w threshold for decimal sep = %2d\n", DEC_W_RATIO);
|
||||
fprintf(f, " space width factor = %.2f\n", SPC_FAC);
|
||||
fprintf(f, " character set = ");
|
||||
fprintf(f, " space width factor = %.2f\n", SPC_FAC);
|
||||
fprintf(f, " character set = ");
|
||||
print_cs_key(DEFAULT_CHARSET, f); fputs("\n", f);
|
||||
fprintf(f, "\nOperation: The IMAGE is read, the COMMANDs are processed in the sequence\n");
|
||||
fprintf(f, " they are given, in the resulting image the given number of digits\n");
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2004-2023 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
/* Copyright (C) 2004-2025 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
|
||||
#ifndef SSOCR2_HELP_H
|
||||
#define SSOCR2_HELP_H
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2004-2023 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
/* Copyright (C) 2004-2025 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
|
||||
/* ImLib2 Header */
|
||||
#include <X11/Xlib.h> /* needed by Imlib2.h */
|
||||
@@ -27,8 +27,11 @@
|
||||
/* string manipulation */
|
||||
#include <string.h> /* strcasecmp, strcmp, strrchr */
|
||||
|
||||
/* sin, cos */
|
||||
#include <math.h>
|
||||
/* trigonometry */
|
||||
#include <math.h> /* 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 */
|
||||
@@ -76,12 +79,12 @@ void ssocr_set_color(fg_bg_t color)
|
||||
}
|
||||
|
||||
/* draw a fore- or background pixel */
|
||||
void draw_pixel(Imlib_Image *image, int x, int y, fg_bg_t color)
|
||||
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);
|
||||
imlib_context_set_image(*image);
|
||||
ssocr_set_color(color);
|
||||
imlib_image_draw_pixel(x,y,0);
|
||||
imlib_context_set_image(current_image);
|
||||
@@ -90,13 +93,25 @@ void draw_pixel(Imlib_Image *image, int x, int y, fg_bg_t color)
|
||||
/* draw a foreground pixel */
|
||||
void draw_fg_pixel(Imlib_Image *image, int x, int y)
|
||||
{
|
||||
draw_pixel(image, x, y, FG);
|
||||
draw_fg_bg_pixel(image, x, y, FG);
|
||||
}
|
||||
|
||||
/* draw a background pixel */
|
||||
void draw_bg_pixel(Imlib_Image *image, int x, int y)
|
||||
{
|
||||
draw_pixel(image, x, y, BG);
|
||||
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 */
|
||||
@@ -165,9 +180,9 @@ Imlib_Image set_pixels_filter(Imlib_Image *source_image, double thresh,
|
||||
}
|
||||
/* set pixel if at least mask pixels around it are set */
|
||||
if(set_pixel >= mask) {
|
||||
draw_fg_pixel(new_image, x, y);
|
||||
draw_fg_pixel(&new_image, x, y);
|
||||
} else {
|
||||
draw_bg_pixel(new_image, x, y);
|
||||
draw_bg_pixel(&new_image, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,12 +272,6 @@ Imlib_Image keep_pixels_filter(Imlib_Image *source_image, double thresh,
|
||||
width = imlib_image_get_width();
|
||||
new_image = imlib_clone_image();
|
||||
|
||||
/* draw white (background) rectangle to clear new image */
|
||||
imlib_context_set_image(new_image);
|
||||
ssocr_set_color(BG);
|
||||
imlib_image_draw_rectangle(0, 0, width, height);
|
||||
imlib_context_set_image(*source_image);
|
||||
|
||||
/* check for every pixel if it should be set in filtered image */
|
||||
for(x=0; x<width; x++) {
|
||||
for(y=0; y<height; y++) {
|
||||
@@ -285,9 +294,9 @@ Imlib_Image keep_pixels_filter(Imlib_Image *source_image, double thresh,
|
||||
/* set pixel if at least mask pixels around it are set */
|
||||
/* mask = 1 keeps all pixels */
|
||||
if(set_pixel > mask) {
|
||||
draw_fg_pixel(new_image, x, y);
|
||||
draw_fg_pixel(&new_image, x, y);
|
||||
} else {
|
||||
draw_bg_pixel(new_image, x, y);
|
||||
draw_bg_pixel(&new_image, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,9 +411,9 @@ Imlib_Image dynamic_threshold(Imlib_Image *source_image,double t,luminance_t lt,
|
||||
lum = get_lum(&color, lt);
|
||||
thresh = get_threshold(source_image, t/100.0, lt, x-ww/2, y-ww/2, ww, wh);
|
||||
if(is_pixel_set(lum, thresh)) {
|
||||
draw_fg_pixel(new_image, x, y);
|
||||
draw_fg_pixel(&new_image, x, y);
|
||||
} else {
|
||||
draw_bg_pixel(new_image, x, y);
|
||||
draw_bg_pixel(&new_image, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -441,9 +450,9 @@ Imlib_Image make_mono(Imlib_Image *source_image, double thresh, luminance_t lt)
|
||||
imlib_image_query_pixel(x, y, &color);
|
||||
lum = get_lum(&color, lt);
|
||||
if(is_pixel_set(lum, thresh)) {
|
||||
draw_fg_pixel(new_image, x, y);
|
||||
draw_fg_pixel(&new_image, x, y);
|
||||
} else {
|
||||
draw_bg_pixel(new_image, x, y);
|
||||
draw_bg_pixel(&new_image, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -456,23 +465,28 @@ Imlib_Image make_mono(Imlib_Image *source_image, double thresh, luminance_t lt)
|
||||
}
|
||||
|
||||
/* adapt threshold to image values values */
|
||||
double adapt_threshold(Imlib_Image *image, double thresh, luminance_t lt, int x,
|
||||
int y, int w, int h, unsigned int flags)
|
||||
double adapt_threshold(Imlib_Image *image, double thresh, luminance_t lt,
|
||||
unsigned int flags, int force_update)
|
||||
{
|
||||
double t = thresh;
|
||||
if(!(flags & ABSOLUTE_THRESHOLD)) {
|
||||
static int is_adapted = 0;
|
||||
if(is_adapted && !force_update) {
|
||||
if(flags & DEBUG_OUTPUT)
|
||||
fprintf(stderr, "threshold is already adjusted to image\n");
|
||||
} else if(!(flags & ABSOLUTE_THRESHOLD)) {
|
||||
if(flags & DEBUG_OUTPUT)
|
||||
fprintf(stderr, "adjusting threshold to image: %f ->", t);
|
||||
t = get_threshold(image, thresh/100.0, lt, x, y, w, h);
|
||||
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, x, y, w, h);
|
||||
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);
|
||||
@@ -528,7 +542,7 @@ double get_threshold(Imlib_Image *source_image, double fraction, luminance_t lt,
|
||||
|
||||
/* determine threshold by an iterative method */
|
||||
double iterative_threshold(Imlib_Image *source_image, double thresh,
|
||||
luminance_t lt, int x, int y, int w, int h)
|
||||
luminance_t lt)
|
||||
{
|
||||
Imlib_Image current_image; /* save image pointer */
|
||||
int height, width; /* image dimensions */
|
||||
@@ -553,23 +567,13 @@ double iterative_threshold(Imlib_Image *source_image, double thresh,
|
||||
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 */
|
||||
do {
|
||||
thresh_lum = MAXRGB * new_thresh;
|
||||
old_thresh = new_thresh;
|
||||
size_black = sum_black = size_white = sum_white = 0;
|
||||
for(xi=0; (xi<w) && (xi<width); xi++) {
|
||||
for(yi=0; (yi<h) && (yi<height); yi++) {
|
||||
for(xi=0; xi<width; xi++) {
|
||||
for(yi=0; yi<height; yi++) {
|
||||
imlib_image_query_pixel(xi, yi, &color);
|
||||
lum = get_lum(&color, lt);
|
||||
if(lum <= thresh_lum) {
|
||||
@@ -604,92 +608,39 @@ double iterative_threshold(Imlib_Image *source_image, double thresh,
|
||||
return new_thresh * 100;
|
||||
}
|
||||
|
||||
/* get minimum lum value */
|
||||
double get_minval(Imlib_Image *source_image, int x, int y, int w, int h,
|
||||
luminance_t lt)
|
||||
/* 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 height, width; /* image dimensions */
|
||||
int w, h; /* image dimensions */
|
||||
int xi,yi; /* iteration variables */
|
||||
Imlib_Color color; /* Imlib2 RGBA color structure */
|
||||
int minval = MAXRGB;
|
||||
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);
|
||||
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;
|
||||
h = imlib_image_get_height();
|
||||
w = imlib_image_get_width();
|
||||
|
||||
/* find the minimum value in the image */
|
||||
for(xi=0; (xi<w) && (xi<width); xi++) {
|
||||
for(yi=0; (yi<h) && (yi<height); yi++) {
|
||||
for(xi=0; xi<w; xi++) {
|
||||
for(yi=0; yi<h; yi++) {
|
||||
imlib_image_query_pixel(xi, yi, &color);
|
||||
lum = clip(get_lum(&color, lt),0,255);
|
||||
if(lum < minval) minval = lum;
|
||||
if(lum < *min) *min = lum;
|
||||
if(lum > *max) *max = lum;
|
||||
}
|
||||
}
|
||||
|
||||
/* restore image from before function call */
|
||||
imlib_context_set_image(current_image);
|
||||
|
||||
return minval;
|
||||
}
|
||||
|
||||
/* get maximum luminance value */
|
||||
double get_maxval(Imlib_Image *source_image, int x, int y, int w, int h,
|
||||
luminance_t lt)
|
||||
{
|
||||
Imlib_Image current_image; /* save image pointer */
|
||||
int height, width; /* image dimensions */
|
||||
int xi,yi; /* iteration variables */
|
||||
Imlib_Color color; /* Imlib2 RGBA color structure */
|
||||
int lum = 0;
|
||||
int maxval = 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 minimum value in the image */
|
||||
for(xi=0; (xi<w) && (xi<width); xi++) {
|
||||
for(yi=0; (yi<h) && (yi<height); yi++) {
|
||||
imlib_image_query_pixel(xi, yi, &color);
|
||||
lum = clip(get_lum(&color, lt),0,255);
|
||||
if(lum > maxval) maxval = lum;
|
||||
}
|
||||
}
|
||||
|
||||
/* restore image from before function call */
|
||||
imlib_context_set_image(current_image);
|
||||
|
||||
return maxval;
|
||||
}
|
||||
|
||||
/* draw a white (background) border around image, overwriting image contents
|
||||
@@ -757,15 +708,11 @@ Imlib_Image shear(Imlib_Image *source_image, int offset)
|
||||
/* copy pixels */
|
||||
for(x=width-1; x>=shift; x--) {
|
||||
imlib_image_query_pixel(x-shift, y, &color_return);
|
||||
imlib_context_set_image(new_image);
|
||||
imlib_context_set_color(color_return.red, color_return.green,
|
||||
color_return.blue, color_return.alpha);
|
||||
imlib_image_draw_pixel(x,y,0);
|
||||
imlib_context_set_image(*source_image);
|
||||
draw_color_pixel(&new_image, x, y, color_return);
|
||||
}
|
||||
/* fill with background */
|
||||
for(x=0; x<shift; x++) {
|
||||
draw_bg_pixel(new_image, x, y);
|
||||
draw_bg_pixel(&new_image, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,14 +753,10 @@ Imlib_Image rotate(Imlib_Image *source_image, double theta)
|
||||
sy = (y-height/2) * cos(theta) - (x-width/2) * sin(theta) + height/2;
|
||||
if((sx >= 0) && (sx <= width) && (sy >= 0) && (sy <= height)) {
|
||||
imlib_image_query_pixel(sx, sy, &c);
|
||||
imlib_context_set_image(new_image);
|
||||
imlib_context_set_color(c.red, c.green, c.blue, c.alpha);
|
||||
draw_color_pixel(&new_image, x, y, c);
|
||||
} else {
|
||||
imlib_context_set_image(new_image);
|
||||
ssocr_set_color(BG);
|
||||
draw_bg_pixel(&new_image, x, y);
|
||||
}
|
||||
imlib_image_draw_pixel(x,y,0);
|
||||
imlib_context_set_image(*source_image);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,20 +790,14 @@ Imlib_Image mirror(Imlib_Image *source_image, direction_t direction)
|
||||
for(x = width-1; x>=0; x--) {
|
||||
for(y = 0; y < height; y++) {
|
||||
imlib_image_query_pixel(width - 1 - x, y, &c);
|
||||
imlib_context_set_image(new_image);
|
||||
imlib_context_set_color(c.red, c.green, c.blue, c.alpha);
|
||||
imlib_image_draw_pixel(x,y,0);
|
||||
imlib_context_set_image(*source_image);
|
||||
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);
|
||||
imlib_context_set_image(new_image);
|
||||
imlib_context_set_color(c.red, c.green, c.blue, c.alpha);
|
||||
imlib_image_draw_pixel(x,y,0);
|
||||
imlib_context_set_image(*source_image);
|
||||
draw_color_pixel(&new_image, x, y, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -935,9 +872,9 @@ Imlib_Image invert(Imlib_Image *source_image, double thresh, luminance_t lt)
|
||||
imlib_image_query_pixel(x, y, &color);
|
||||
lum = get_lum(&color, lt);
|
||||
if(is_pixel_set(lum, thresh)) {
|
||||
draw_bg_pixel(new_image, x, y);
|
||||
draw_bg_pixel(&new_image, x, y);
|
||||
} else {
|
||||
draw_fg_pixel(new_image, x, y);
|
||||
draw_fg_pixel(&new_image, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1126,7 +1063,7 @@ luminance_t parse_lum(char *keyword)
|
||||
} else if(strcasecmp(keyword, "blue") == 0) {
|
||||
return BLUE;
|
||||
} else {
|
||||
return DEFAULT_LUM_FORMULA;
|
||||
return LUM_PARSE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2004-2023 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
/* Copyright (C) 2004-2025 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
|
||||
#ifndef SSOCR2_IMGPROC_H
|
||||
#define SSOCR2_IMGPROC_H
|
||||
@@ -32,7 +32,7 @@ void set_bg_color(int color);
|
||||
void ssocr_set_color(fg_bg_t color);
|
||||
|
||||
/* draw a fore- or background pixel */
|
||||
void draw_pixel(Imlib_Image *image, int x, int y, fg_bg_t color);
|
||||
void draw_fg_bg_pixel(Imlib_Image *image, int x, int y, fg_bg_t color);
|
||||
|
||||
/* draw a foreground pixel */
|
||||
void draw_fg_pixel(Imlib_Image *image, int x, int y);
|
||||
@@ -40,6 +40,9 @@ void draw_fg_pixel(Imlib_Image *image, int x, int y);
|
||||
/* draw a background pixel */
|
||||
void draw_bg_pixel(Imlib_Image *image, int x, int y);
|
||||
|
||||
/* draw a pixel of a given color */
|
||||
void draw_color_pixel(Imlib_Image *image, int x, int y, Imlib_Color color);
|
||||
|
||||
/* check if a pixel is set regarding current foreground/background colors */
|
||||
int is_pixel_set(int value, double threshold);
|
||||
|
||||
@@ -126,8 +129,8 @@ Imlib_Image grayscale(Imlib_Image *source_image, luminance_t lt);
|
||||
Imlib_Image crop(Imlib_Image *source_image, int x, int y, int w, int h);
|
||||
|
||||
/* adapt threshold to image values values */
|
||||
double adapt_threshold(Imlib_Image *image, double thresh, luminance_t lt, int x,
|
||||
int y, int w, int h, unsigned int flags);
|
||||
double adapt_threshold(Imlib_Image *image, double thresh, luminance_t lt,
|
||||
unsigned int flags, int force_update);
|
||||
|
||||
/* compute dynamic threshold value from the rectangle (x,y),(x+w,y+h) of
|
||||
* source_image */
|
||||
@@ -136,15 +139,11 @@ double get_threshold(Imlib_Image *source_image, double fraction, luminance_t lt,
|
||||
|
||||
/* determine threshold by an iterative method */
|
||||
double iterative_threshold(Imlib_Image *source_image, double thresh,
|
||||
luminance_t lt, int x, int y, int w, int h);
|
||||
luminance_t lt);
|
||||
|
||||
/* get minimum gray value */
|
||||
double get_minval(Imlib_Image *source_image, int x, int y, int w, int h,
|
||||
luminance_t lt);
|
||||
|
||||
/* get maximum gray value */
|
||||
double get_maxval(Imlib_Image *source_image, int x, int y, int w, int h,
|
||||
luminance_t lt);
|
||||
/* get minimum and maximum gray (luminace) values */
|
||||
void get_minmaxval(Imlib_Image *source_image, luminance_t lt,
|
||||
double *min, double *max);
|
||||
|
||||
/* compute luminance from RGB values */
|
||||
int get_lum(Imlib_Color *color, luminance_t lt);
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ Upstream Author(s):
|
||||
|
||||
Copyright:
|
||||
|
||||
Copyright (C) 2004-2023 Erik Auerswald
|
||||
Copyright (C) 2004-2025 Erik Auerswald
|
||||
|
||||
License:
|
||||
|
||||
|
||||
+39
-4
@@ -1,4 +1,4 @@
|
||||
.TH ssocr 1 "@DATE@" @VERSION@ "OCR for seven segment displays"
|
||||
.TH ssocr 1 "@DATE@" "@VERSION@" "OCR for seven segment displays"
|
||||
.SH NAME
|
||||
ssocr \- optical recognition of seven segment displays
|
||||
.SH SYNOPSIS
|
||||
@@ -18,10 +18,24 @@ provides several image manipulation algorithms to enhance noisy images.
|
||||
can be used to change
|
||||
.B ssocr
|
||||
behavior.
|
||||
.PP
|
||||
.B Commands
|
||||
can be used to manipulate the input
|
||||
.I IMAGE
|
||||
before starting the recognition algorithm.
|
||||
.PP
|
||||
Two hyphens
|
||||
.RB ( \-\- )
|
||||
can be used as a special argument to end option-scanning, e.g.,
|
||||
in order to use a negative number as argument to a command.
|
||||
.PP
|
||||
When using a single character (i.e., short) option,
|
||||
arguments can either directly follow the option character,
|
||||
or can be separated from the option character by whitespace.
|
||||
When using a multi character (i.e., long) option,
|
||||
arguments must be separated from the option by either an equals sign
|
||||
.RB ( = ),
|
||||
or whitespace.
|
||||
.SH OPTIONS
|
||||
.SS \-h, \-\-help
|
||||
Write a help message to standard output.
|
||||
@@ -39,14 +53,21 @@ is used. The default threshold is
|
||||
.IR 50 .
|
||||
.SS \-a, \-\-absolute\-threshold
|
||||
Do not adjust the threshold to the luminance values occurring in the image.
|
||||
Using this option also inhibits iterative thresholding using option
|
||||
.BR \-\-iter\-threshold .
|
||||
Consider this option when using the
|
||||
.B dynamic_threshold
|
||||
command.
|
||||
or
|
||||
.B gray_stretch
|
||||
commands.
|
||||
.SS \-T, \-\-iter\-threshold
|
||||
Use an iterative method (one-dimensional k-means clustering) to determine the
|
||||
threshold. The starting value can be specified with the
|
||||
.B \-\-threshold
|
||||
option.
|
||||
Option
|
||||
.B \-\-absolute\-threshold
|
||||
inhibits iterative threshold determination.
|
||||
.SS \-n, \-\-number\-pixels NUMBER
|
||||
Set the number of foreground pixels that have to be found in a scanline to
|
||||
recognize a segment.
|
||||
@@ -276,6 +297,15 @@ of the included characters.
|
||||
The default is
|
||||
.I full
|
||||
(recognizing all characters known to ssocr in the image).
|
||||
.SS \-F, \-\-adapt\-after\-crop
|
||||
When using the
|
||||
.B crop
|
||||
command,
|
||||
adjust (adapt) the threshold to image luminance values only after cropping,
|
||||
not also directly before.
|
||||
Using other commands before
|
||||
.B crop
|
||||
can still lead to adapting the threshold to the original image.
|
||||
.SH COMMANDS
|
||||
Most commands do not change the image dimensions.
|
||||
The
|
||||
@@ -360,8 +390,13 @@ the values
|
||||
and
|
||||
.B T2
|
||||
are interpreted as percentages.
|
||||
Consider using the
|
||||
.B \-\-absolute\-threshold
|
||||
option together with a manually adjusted
|
||||
.B \-\-threshold
|
||||
for predictable results.
|
||||
.SS dynamic_threshold W H
|
||||
Convert the image to monochrome using dynamic thresholding a.k.a local
|
||||
Convert the image to monochrome using dynamic thresholding a.k.a. local
|
||||
adaptive thresholding.
|
||||
A window of width
|
||||
.B W
|
||||
@@ -422,7 +457,7 @@ or
|
||||
.B dynamic_threshold
|
||||
instead.
|
||||
.SS white_border [WIDTH]
|
||||
The border of the image is set to the foreground color.
|
||||
The border of the image is set to the background color.
|
||||
This border is one pixel wide unless a
|
||||
.B WIDTH
|
||||
>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2004-2023 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
/* Copyright (C) 2004-2025 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
|
||||
/* Copyright (C) 2013 Cristiano Fontana <fontanacl@ornl.gov> */
|
||||
|
||||
/* ImLib2 Header */
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <Imlib2.h>
|
||||
|
||||
/* standard things */
|
||||
#include <limits.h> /* INT_MAX */
|
||||
#include <stdint.h> /* SIZE_MAX */
|
||||
#include <stdio.h> /* puts, printf, BUFSIZ, perror, FILE */
|
||||
#include <stdlib.h> /* exit */
|
||||
@@ -31,7 +32,7 @@
|
||||
|
||||
/* option parsing */
|
||||
#include <getopt.h> /* getopt */
|
||||
#include <unistd.h> /* getopt */
|
||||
#include <unistd.h> /* getopt, read, write, STDIN_FILENO */
|
||||
|
||||
/* file permissions */
|
||||
#include <sys/stat.h> /* umask */
|
||||
@@ -56,8 +57,8 @@ static char * tmp_imgfile(unsigned int flags)
|
||||
char *name;
|
||||
size_t pattern_len;
|
||||
int handle;
|
||||
unsigned char buf;
|
||||
ssize_t count = 0;
|
||||
unsigned char buf[BUFSIZ];
|
||||
ssize_t read_count = 0, write_count = 0;
|
||||
size_t pat_suffix_len = strlen(DIR_SEP TMP_FILE_PATTERN);
|
||||
size_t dir_len;
|
||||
|
||||
@@ -75,6 +76,10 @@ static char * tmp_imgfile(unsigned int flags)
|
||||
}
|
||||
dir_len = strlen(dir);
|
||||
pattern_len = dir_len + pat_suffix_len + 1;
|
||||
if(pattern_len <= dir_len) {
|
||||
fputs(PROG ": error: temporary file name length overflow\n", stderr);
|
||||
exit(99);
|
||||
}
|
||||
name = calloc(pattern_len, sizeof(char));
|
||||
if(!name) {
|
||||
perror(PROG ": could not allocate memory for name of temporary file");
|
||||
@@ -94,12 +99,16 @@ static char * tmp_imgfile(unsigned int flags)
|
||||
}
|
||||
|
||||
/* copy image data from stdin to tmp file */
|
||||
while((fread(&buf, sizeof(char), 1, stdin)) > 0) {
|
||||
count = write(handle, &buf, 1);
|
||||
if (count <= 0) break;
|
||||
while((read_count = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
|
||||
write_count = write(handle, buf, read_count);
|
||||
if (write_count <= 0) break;
|
||||
}
|
||||
close(handle); /* filehandle is no longer needed, Imlib2 uses filename */
|
||||
if(ferror(stdin) || (count <= 0)) {
|
||||
if(read_count < 0) {
|
||||
perror(PROG ": could not read image from standard input");
|
||||
exit(99);
|
||||
}
|
||||
if(write_count <= 0) {
|
||||
perror(PROG ": could not copy image data to temporary file");
|
||||
unlink(name);
|
||||
exit(99);
|
||||
@@ -109,16 +118,20 @@ static char * tmp_imgfile(unsigned int flags)
|
||||
}
|
||||
|
||||
/* return number of foreground pixels in a scanline */
|
||||
static unsigned int scanline(Imlib_Image *image, Imlib_Image *debug_image,
|
||||
static unsigned int scanline(Imlib_Image *debug_image,
|
||||
int x, int y, int len, direction_t dir,
|
||||
color_struct d_color, double thresh,
|
||||
luminance_t lt, unsigned int flags)
|
||||
{
|
||||
Imlib_Color imlib_color;
|
||||
Imlib_Color imlib_color, debug_color;
|
||||
int lum, i, ix=x, iy=y, start, end;
|
||||
unsigned int found_pixels = 0;
|
||||
start = (dir == HORIZONTAL) ? x : y;
|
||||
end = start + len;
|
||||
debug_color.red = d_color.R;
|
||||
debug_color.green = d_color.G;
|
||||
debug_color.blue = d_color.B;
|
||||
debug_color.alpha = d_color.A;
|
||||
for (i = start; i <= end; i++) {
|
||||
if (dir == HORIZONTAL) ix = i;
|
||||
else iy = i;
|
||||
@@ -126,10 +139,7 @@ static unsigned int scanline(Imlib_Image *image, Imlib_Image *debug_image,
|
||||
lum = get_lum(&imlib_color, lt);
|
||||
if(is_pixel_set(lum, thresh)) {
|
||||
if(flags & USE_DEBUG_IMAGE) {
|
||||
imlib_context_set_image(*debug_image);
|
||||
imlib_context_set_color(d_color.R, d_color.G, d_color.B, d_color.A);
|
||||
imlib_image_draw_pixel(ix, iy, 0);
|
||||
imlib_context_set_image(*image);
|
||||
draw_color_pixel(debug_image, ix, iy, debug_color);
|
||||
}
|
||||
found_pixels++;
|
||||
}
|
||||
@@ -243,6 +253,7 @@ int main(int argc, char **argv)
|
||||
int use_tmpfile=0; /* flag to know if temporary image file is used */
|
||||
|
||||
int i, j, d; /* iteration variables */
|
||||
size_t cur_digit_mem, new_digit_mem; /* for overflow checks */
|
||||
int unknown_digit=0; /* was one of the 6 found digits an unknown one? */
|
||||
int need_pixels = NEED_PIXELS; /* pixels needed to set segment in scanline */
|
||||
int min_segment = MIN_SEGMENT; /* minimum pixels needed for a segment */
|
||||
@@ -272,6 +283,7 @@ int main(int argc, char **argv)
|
||||
int dig_w; /* width of digit part of image */
|
||||
int dig_h; /* height of digit part of image */
|
||||
int max_dig_h=0, max_dig_w=0; /* maximum height & width of digits found */
|
||||
int widest_dig_is_one=0; /* set to one if the widest digit is a one */
|
||||
Imlib_Color color; /* Imlib2 RGBA color structure */
|
||||
/* state of search */
|
||||
int state = (ssocr_foreground == SSOCR_BLACK) ? FIND_DARK : FIND_LIGHT;
|
||||
@@ -321,16 +333,17 @@ int main(int argc, char **argv)
|
||||
{"ascii-art-segments", 0, 0, 'S'}, /* print found segments in ASCII art */
|
||||
{"print-as-hex", 0, 0, 'X'}, /* change output format to hex */
|
||||
{"omit-decimal-point", 0, 0, 'C'}, /* omit decimal points from output */
|
||||
{"charset", 1, 0, 'c'}, /* omit decimal points from output */
|
||||
{"charset", 1, 0, 'c'}, /* select character set of display */
|
||||
{"dec-h-ratio", 1, 0, 'H'}, /* height ratio for decimal point detection */
|
||||
{"dec-w-ratio", 1, 0, 'W'}, /* width ratio for decimal point detection */
|
||||
{"print-spaces", 0, 0, 's'}, /* print spaces between distant digits */
|
||||
{"space-factor", 1, 0, 'A'}, /* relative distance to add spaces */
|
||||
{"space-average", 0, 0, 'G'}, /* avg instead of min dst for spaces */
|
||||
{"adapt-after-crop", 0, 0, 'F'}, /* don't adapt threshold before crop */
|
||||
{0, 0, 0, 0} /* terminate long options */
|
||||
};
|
||||
c = getopt_long (argc, argv,
|
||||
"hVt:vaTn:N:i:d:r:m:M:o:O:D::pPf:b:Igl:SXCc:H:W:sA:G",
|
||||
"hVt:vaTn:N:i:d:r:m:M:o:O:D::pPf:b:Igl:SXCc:H:W:sA:GF",
|
||||
long_options, &option_index);
|
||||
if (c == -1) break; /* leaves while (1) loop */
|
||||
switch (c) {
|
||||
@@ -373,7 +386,8 @@ int main(int argc, char **argv)
|
||||
if(optarg) {
|
||||
need_pixels = atoi(optarg);
|
||||
if(need_pixels < 1) {
|
||||
fprintf(stderr, "warning: ignoring --number-pixels=%s\n", optarg);
|
||||
fprintf(stderr, PROG ": warning: ignoring --number-pixels=%s\n",
|
||||
optarg);
|
||||
need_pixels = NEED_PIXELS;
|
||||
}
|
||||
if(flags & DEBUG_OUTPUT) {
|
||||
@@ -385,7 +399,8 @@ int main(int argc, char **argv)
|
||||
if(optarg) {
|
||||
min_segment = atoi(optarg);
|
||||
if(min_segment < 1) {
|
||||
fprintf(stderr, "warning: ignoring --min-segment=%s\n", optarg);
|
||||
fprintf(stderr, PROG ": warning: ignoring --min-segment=%s\n",
|
||||
optarg);
|
||||
min_segment = MIN_SEGMENT;
|
||||
if(flags & DEBUG_OUTPUT) {
|
||||
fprintf(stderr, "min_segment = %d\n", min_segment);
|
||||
@@ -403,7 +418,8 @@ int main(int argc, char **argv)
|
||||
int ret;
|
||||
ret = parse_width_height(optarg, &min_char_dims);
|
||||
if (ret) {
|
||||
fprintf(stderr, "warning: ignoring --min-char-dims=%s\n", optarg);
|
||||
fprintf(stderr, PROG ": warning: ignoring --min-char-dims=%s\n",
|
||||
optarg);
|
||||
}
|
||||
if(flags & DEBUG_OUTPUT) {
|
||||
fprintf(stderr, "min_char_dims = %dx%d\n", min_char_dims.w,
|
||||
@@ -415,7 +431,8 @@ int main(int argc, char **argv)
|
||||
if(optarg) {
|
||||
ignore_pixels = atoi(optarg);
|
||||
if(ignore_pixels < 0) {
|
||||
fprintf(stderr, "warning: ignoring --ignore-pixels=%s\n", optarg);
|
||||
fprintf(stderr, PROG ": warning: ignoring --ignore-pixels=%s\n",
|
||||
optarg);
|
||||
ignore_pixels = IGNORE_PIXELS;
|
||||
}
|
||||
}
|
||||
@@ -425,7 +442,8 @@ int main(int argc, char **argv)
|
||||
int ret;
|
||||
ret = parse_interval(optarg, &expected_digits);
|
||||
if(ret) {
|
||||
fprintf(stderr, "warning: ignoring --number-digits=%s\n", optarg);
|
||||
fprintf(stderr, PROG ": warning: ignoring --number-digits=%s\n",
|
||||
optarg);
|
||||
expected_digits.min = expected_digits.max = NUMBER_OF_DIGITS;
|
||||
}
|
||||
if (flags & DEBUG_OUTPUT) {
|
||||
@@ -438,7 +456,7 @@ int main(int argc, char **argv)
|
||||
if(optarg) {
|
||||
one_ratio = atoi(optarg);
|
||||
if(one_ratio < 2) {
|
||||
fprintf(stderr, "warning: ignoring --one-ratio=%s\n", optarg);
|
||||
fprintf(stderr, PROG ": warning: ignoring --one-ratio=%s\n",optarg);
|
||||
one_ratio = ONE_RATIO;
|
||||
}
|
||||
}
|
||||
@@ -447,7 +465,8 @@ int main(int argc, char **argv)
|
||||
if(optarg) {
|
||||
minus_ratio = atoi(optarg);
|
||||
if(minus_ratio < 1) {
|
||||
fprintf(stderr, "warning: ignoring --minus-ratio=%s\n", optarg);
|
||||
fprintf(stderr, PROG ": warning: ignoring --minus-ratio=%s\n",
|
||||
optarg);
|
||||
minus_ratio = MINUS_RATIO;
|
||||
}
|
||||
}
|
||||
@@ -519,6 +538,12 @@ int main(int argc, char **argv)
|
||||
case 'l':
|
||||
if(optarg) {
|
||||
lt = parse_lum(optarg);
|
||||
if(lt == LUM_PARSE_ERROR) {
|
||||
fprintf(stderr,
|
||||
PROG ": warning: ignoring unknown luminance formula '%s'\n",
|
||||
optarg);
|
||||
lt = DEFAULT_LUM_FORMULA;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
@@ -545,13 +570,19 @@ int main(int argc, char **argv)
|
||||
case 'c':
|
||||
if(optarg) {
|
||||
charset = parse_charset(optarg);
|
||||
if(charset == CS_PARSE_ERROR) {
|
||||
fprintf(stderr, PROG ": warning: ignoring unknown charset '%s'\n",
|
||||
optarg);
|
||||
charset = DEFAULT_CHARSET;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
if(optarg) {
|
||||
dec_h_ratio = atoi(optarg);
|
||||
if(dec_h_ratio < 2) {
|
||||
fprintf(stderr, "warning: ignoring --dec-h-ratio=%s\n", optarg);
|
||||
fprintf(stderr, PROG ": warning: ignoring --dec-h-ratio=%s\n",
|
||||
optarg);
|
||||
dec_h_ratio = DEC_H_RATIO;
|
||||
}
|
||||
}
|
||||
@@ -560,7 +591,8 @@ int main(int argc, char **argv)
|
||||
if(optarg) {
|
||||
dec_w_ratio = atoi(optarg);
|
||||
if(dec_w_ratio < 1) {
|
||||
fprintf(stderr, "warning: ignoring --dec-w-ratio=%s\n", optarg);
|
||||
fprintf(stderr, PROG ": warning: ignoring --dec-w-ratio=%s\n",
|
||||
optarg);
|
||||
dec_w_ratio = DEC_W_RATIO;
|
||||
}
|
||||
}
|
||||
@@ -592,6 +624,13 @@ int main(int argc, char **argv)
|
||||
flags & SPC_USE_AVG_DST);
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
flags |= ADAPT_AFTER_CROP;
|
||||
if(flags & DEBUG_OUTPUT) {
|
||||
fprintf(stderr, "flags & ADAPT_AFTER_CROP=%d\n",
|
||||
flags & ADAPT_AFTER_CROP);
|
||||
}
|
||||
break;
|
||||
case '?': /* missing argument or character not in optstring */
|
||||
short_usage(PROG,stderr);
|
||||
exit (2);
|
||||
@@ -608,8 +647,11 @@ int main(int argc, char **argv)
|
||||
exit(99);
|
||||
}
|
||||
}
|
||||
if((flags & ABSOLUTE_THRESHOLD) && (flags & DO_ITERATIVE_THRESHOLD))
|
||||
fprintf(stderr, "%s: warning: -T has no effect due to -a\n", PROG);
|
||||
if(flags & DEBUG_OUTPUT) {
|
||||
fprintf(stderr, "================================================================================\n");
|
||||
fprintf(stderr, "VERSION=%s\n", VERSION);
|
||||
fprintf(stderr, "flags & VERBOSE=%d\nthresh=%f\n", flags & VERBOSE, thresh);
|
||||
fprintf(stderr, "flags & PRINT_INFO=%d\nflags & ADJUST_GRAY=%d\n",
|
||||
flags & PRINT_INFO, flags & ADJUST_GRAY);
|
||||
@@ -617,7 +659,7 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "flags & DO_ITERATIVE_THRESHOLD=%d\n",
|
||||
flags & DO_ITERATIVE_THRESHOLD);
|
||||
fprintf(stderr, "flags & USE_DEBUG_IMAGE=%d\n", flags & USE_DEBUG_IMAGE);
|
||||
fprintf(stderr, "flags & DEBUG_OUTPUT=%d\n", flags & PRINT_INFO);
|
||||
fprintf(stderr, "flags & DEBUG_OUTPUT=%d\n", flags & DEBUG_OUTPUT);
|
||||
fprintf(stderr, "flags & PROCESS_ONLY=%d\n", flags & PROCESS_ONLY);
|
||||
fprintf(stderr, "flags & ASCII_ART_SEGMENTS=%d\n",
|
||||
flags & ASCII_ART_SEGMENTS);
|
||||
@@ -625,6 +667,7 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "flags & OMIT_DECIMAL=%d\n", flags & OMIT_DECIMAL);
|
||||
fprintf(stderr, "flags & PRINT_SPACES=%d\n", flags & PRINT_SPACES);
|
||||
fprintf(stderr, "flags & SPC_USE_AVG_DST=%d\n", flags & SPC_USE_AVG_DST);
|
||||
fprintf(stderr, "flags & ADAPT_AFTER_CROP=%d\n", flags & ADAPT_AFTER_CROP);
|
||||
fprintf(stderr, "need_pixels = %d\n", need_pixels);
|
||||
fprintf(stderr, "min_segment = %d\n", min_segment);
|
||||
fprintf(stderr, "min_char_dims = %dx%d\n",min_char_dims.w,min_char_dims.h);
|
||||
@@ -695,14 +738,12 @@ int main(int argc, char **argv)
|
||||
|
||||
/* get minimum and maximum "value" values */
|
||||
if((flags & DEBUG_OUTPUT) || (flags & PRINT_INFO)) {
|
||||
double min, max;
|
||||
get_minmaxval(&image, lt, &min, &max);
|
||||
fprintf(stderr, "%.2f <= lum <= %.2f (lum should be in [0,255])\n",
|
||||
get_minval(&image, 0, 0, -1, -1, lt),
|
||||
get_maxval(&image, 0, 0, -1, -1, lt));
|
||||
min, max);
|
||||
}
|
||||
|
||||
/* adapt threshold to image */
|
||||
thresh = adapt_threshold(&image, thresh, lt, 0, 0, -1, -1, flags);
|
||||
|
||||
/* process commands */
|
||||
if(flags & VERBOSE) /* then print found commands */ {
|
||||
if(optind >= argc-1) {
|
||||
@@ -732,11 +773,12 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
i++;
|
||||
new_image = dilation(&image, thresh, lt, n);
|
||||
} else {
|
||||
n = 1;
|
||||
if(flags & VERBOSE) fputs(" processing dilation (1)\n", stderr);
|
||||
new_image = dilation(&image, thresh, lt, 1);
|
||||
}
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = dilation(&image, thresh, lt, n);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
image = new_image;
|
||||
@@ -751,11 +793,12 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
i++;
|
||||
new_image = erosion(&image, thresh, lt, n);
|
||||
} else {
|
||||
n = 1;
|
||||
if(flags & VERBOSE) fputs(" processing erosion (1)\n", stderr);
|
||||
new_image = erosion(&image, thresh, lt, 1);
|
||||
}
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = erosion(&image, thresh, lt, n);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
image = new_image;
|
||||
@@ -770,11 +813,12 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
i++;
|
||||
new_image = opening(&image, thresh, lt, n);
|
||||
} else {
|
||||
n = 1;
|
||||
if(flags & VERBOSE) fputs(" processing opening (1)\n", stderr);
|
||||
new_image = opening(&image, thresh, lt, 1);
|
||||
}
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = opening(&image, thresh, lt, n);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
image = new_image;
|
||||
@@ -789,22 +833,25 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
i++;
|
||||
new_image = closing(&image, thresh, lt, n);
|
||||
} else {
|
||||
n = 1;
|
||||
if(flags & VERBOSE) fputs(" processing closing (1)\n", stderr);
|
||||
new_image = closing(&image, thresh, lt, 1);
|
||||
}
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = closing(&image, thresh, lt, n);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
image = new_image;
|
||||
} else if(strcasecmp("remove_isolated",argv[i]) == 0) {
|
||||
if(flags & VERBOSE) fputs(" processing remove_isolated\n", stderr);
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = remove_isolated(&image, thresh, lt);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
image = new_image;
|
||||
} else if(strcasecmp("make_mono",argv[i]) == 0) {
|
||||
if(flags & VERBOSE) fputs(" processing make_mono\n", stderr);
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = make_mono(&image, thresh, lt);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
@@ -819,13 +866,14 @@ int main(int argc, char **argv)
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
new_image = white_border(&image, bdwidth);
|
||||
i++;
|
||||
} else {
|
||||
bdwidth = 1;
|
||||
if(flags & VERBOSE)
|
||||
fputs(" processing white_border (1)\n", stderr);
|
||||
new_image = white_border(&image, 1);
|
||||
}
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = white_border(&image, bdwidth);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
image = new_image;
|
||||
@@ -839,6 +887,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
if(i+1<argc-1) {
|
||||
offset = (int) atoi(argv[++i]); /* sideeffect: increment i */
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = shear(&image, offset);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
@@ -858,6 +907,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
if(i+1<argc-1) {
|
||||
mask = (int) atoi(argv[++i]); /* sideeffect: increment i */
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = set_pixels_filter(&image, thresh, lt, mask);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
@@ -878,6 +928,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
if(i+1<argc-1) {
|
||||
mask = (int) atoi(argv[++i]); /* sideeffect: increment i */
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = keep_pixels_filter(&image, thresh, lt, mask);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
@@ -900,6 +951,7 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
i+=2; /* skip the arguments to dynamic_threshold */
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = dynamic_threshold(&image, thresh, lt, ww, wh);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
@@ -911,30 +963,35 @@ int main(int argc, char **argv)
|
||||
}
|
||||
} else if(strcasecmp("rgb_threshold",argv[i]) == 0) {
|
||||
if(flags & VERBOSE) fputs(" processing rgb_threshold\n", stderr);
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = make_mono(&image, thresh, MINIMUM);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
image = new_image;
|
||||
} else if(strcasecmp("r_threshold",argv[i]) == 0) {
|
||||
if(flags & VERBOSE) fputs(" processing r_threshold\n", stderr);
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = make_mono(&image, thresh, RED);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
image = new_image;
|
||||
} else if(strcasecmp("g_threshold",argv[i]) == 0) {
|
||||
if(flags & VERBOSE) fputs(" processing g_threshold\n", stderr);
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = make_mono(&image, thresh, GREEN);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
image = new_image;
|
||||
} else if(strcasecmp("b_threshold",argv[i]) == 0) {
|
||||
if(flags & VERBOSE) fputs(" processing b_threshold\n", stderr);
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = make_mono(&image, thresh, BLUE);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
image = new_image;
|
||||
} else if(strcasecmp("invert",argv[i]) == 0) {
|
||||
if(flags & VERBOSE) fputs(" processing invert\n", stderr);
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = invert(&image, thresh, lt);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
@@ -957,8 +1014,7 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, " adjusting T1=%.2f and T2=%.2f to image\n",
|
||||
t1, t2);
|
||||
}
|
||||
min = get_minval(&image, 0, 0, -1, -1, lt);
|
||||
max = get_maxval(&image, 0, 0, -1, -1, lt);
|
||||
get_minmaxval(&image, lt, &min, &max);
|
||||
t1 = min + t1/100.0 * (max - min);
|
||||
t2 = min + t2/100.0 * (max - min);
|
||||
if(flags & VERBOSE) {
|
||||
@@ -966,6 +1022,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
i+=2; /* skip the arguments to gray_stretch */
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = gray_stretch(&image, t1, t2, lt);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
@@ -983,7 +1040,7 @@ int main(int argc, char **argv)
|
||||
image = new_image;
|
||||
} else if(strcasecmp("crop",argv[i]) == 0) {
|
||||
if(i+4<argc-1) {
|
||||
int x, y, cw, ch; /* lw = crop width, lh = crop height */
|
||||
int x, y, cw, ch; /* cw = crop width, ch = crop height */
|
||||
x = atoi(argv[i+1]);
|
||||
y = atoi(argv[i+2]);
|
||||
cw = atoi(argv[i+3]);
|
||||
@@ -999,7 +1056,8 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
i += 4; /* skip the arguments to crop */
|
||||
imlib_context_set_image(image);
|
||||
if(!(flags & ADAPT_AFTER_CROP))
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = crop(&image, x, y, cw, ch);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
@@ -1014,13 +1072,13 @@ int main(int argc, char **argv)
|
||||
}
|
||||
/* get minimum and maximum "value" values in cropped image */
|
||||
if((flags&DEBUG_OUTPUT) || (flags&PRINT_INFO) || (flags&VERBOSE)) {
|
||||
double min, max;
|
||||
get_minmaxval(&image, lt, &min, &max);
|
||||
fprintf(stderr, " %.2f <= lum <= %.2f in cropped image"
|
||||
" (lum should be in [0,255])\n",
|
||||
get_minval(&image, 0, 0, -1, -1, lt),
|
||||
get_maxval(&image, 0, 0, -1, -1, lt));
|
||||
" (lum should be in [0,255])\n", min, max);
|
||||
}
|
||||
/* adapt threshold to cropped image */
|
||||
thresh = adapt_threshold(&image, thresh, lt, 0, 0, -1, -1, flags);
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, UPDATE);
|
||||
} else {
|
||||
fprintf(stderr, "%s: error: crop command needs 4 arguments\n", PROG);
|
||||
exit(99);
|
||||
@@ -1035,6 +1093,7 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
theta = atof(argv[++i]); /* sideeffect: increment i */
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
new_image = rotate(&image, theta);
|
||||
imlib_context_set_image(image);
|
||||
imlib_free_image();
|
||||
@@ -1084,6 +1143,9 @@ int main(int argc, char **argv)
|
||||
/* exit if only image processing shall be done */
|
||||
if(flags & PROCESS_ONLY) exit(3);
|
||||
|
||||
/* adapt threshold to image (unless this is already done) */
|
||||
thresh = adapt_threshold(&image, thresh, lt, flags, INITIAL);
|
||||
|
||||
if(flags & USE_DEBUG_IMAGE) {
|
||||
/* copy processed image to debug image */
|
||||
debug_image = make_mono(&image, thresh, lt);
|
||||
@@ -1147,6 +1209,11 @@ int main(int argc, char **argv)
|
||||
}
|
||||
digits[d].x2 = i;
|
||||
digits[d].y2 = h-1;
|
||||
if((d >= INT_MAX - 1) || (d < 0)) {
|
||||
fputs(PROG ": error: too many potential digits (integer overflow)\n",
|
||||
stderr);
|
||||
exit(99);
|
||||
}
|
||||
d++;
|
||||
if(flags & USE_DEBUG_IMAGE) {
|
||||
imlib_context_set_image(debug_image);
|
||||
@@ -1155,7 +1222,13 @@ int main(int argc, char **argv)
|
||||
imlib_context_set_image(image);
|
||||
}
|
||||
/* add memory for another digit */
|
||||
if(!(digits = realloc(digits, (d+1) * sizeof(digit_struct)))) {
|
||||
cur_digit_mem = d * sizeof(digit_struct);
|
||||
new_digit_mem = (d+1) * sizeof(digit_struct);
|
||||
if(new_digit_mem <= cur_digit_mem) {
|
||||
fputs(PROG ": error: size_t overflow (memory for digits)\n", stderr);
|
||||
exit(99);
|
||||
}
|
||||
if(!(digits = realloc(digits, new_digit_mem))) {
|
||||
perror(PROG ": digits = realloc()");
|
||||
exit(99);
|
||||
}
|
||||
@@ -1293,35 +1366,57 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "keeping %d of %d potential digits\n", digit_count,
|
||||
potential_digits);
|
||||
}
|
||||
/* allocate memory for sufficiently large digits */
|
||||
if(!(tmp = calloc(digit_count, sizeof(digit_struct)))) {
|
||||
perror(PROG ": tmp = calloc()");
|
||||
/* at least one digit is required */
|
||||
if (digit_count < 1) {
|
||||
fputs(PROG ": error: no sufficiently large digits found\n", stderr);
|
||||
exit(1);
|
||||
}
|
||||
/* ensure we do not try to keep more digits than we have found */
|
||||
if(digit_count > potential_digits) {
|
||||
fprintf(stderr,
|
||||
PROG ": error: trying to keep more digits (%d) than found (%d)\n",
|
||||
digit_count, potential_digits);
|
||||
exit(99);
|
||||
}
|
||||
/* keep only sufficiently large digits */
|
||||
pos = 0;
|
||||
for (d = 0; d < potential_digits; d++) {
|
||||
if (digits[d].x2 - digits[d].x1 >= min_char_dims.w &&
|
||||
digits[d].y2 - digits[d].y1 >= min_char_dims.h) {
|
||||
if (pos >= digit_count) {
|
||||
fputs(PROG ": error copying digit information", stderr);
|
||||
exit(99);
|
||||
}
|
||||
memcpy(tmp + pos, digits + d, sizeof(digit_struct));
|
||||
pos++;
|
||||
/* if potential digits are discarded, copy remaining ones to new memory */
|
||||
if(digit_count < potential_digits) {
|
||||
/* allocate memory for sufficiently large digits we want to keep */
|
||||
if(!(tmp = calloc(digit_count, sizeof(digit_struct)))) {
|
||||
perror(PROG ": tmp = calloc()");
|
||||
exit(99);
|
||||
}
|
||||
/* keep only sufficiently large digits */
|
||||
pos = 0;
|
||||
for (d = 0; d < potential_digits; d++) {
|
||||
if (digits[d].x2 - digits[d].x1 >= min_char_dims.w &&
|
||||
digits[d].y2 - digits[d].y1 >= min_char_dims.h) {
|
||||
if (pos >= digit_count) {
|
||||
fputs(PROG ": error copying digit information", stderr);
|
||||
exit(99);
|
||||
}
|
||||
memcpy(tmp + pos, digits + d, sizeof(digit_struct));
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
free(digits);
|
||||
digits = tmp;
|
||||
potential_digits = digit_count;
|
||||
}
|
||||
free(digits);
|
||||
digits = tmp;
|
||||
potential_digits = digit_count;
|
||||
}
|
||||
|
||||
/* check if expected number of digits have been found */
|
||||
if ((expected_digits.min > -1) &&
|
||||
((potential_digits < expected_digits.min) ||
|
||||
(potential_digits > expected_digits.max))) {
|
||||
fprintf(stderr, PROG ": expected between %d and %d digits, but found %d\n",
|
||||
expected_digits.min, expected_digits.max, potential_digits);
|
||||
if (expected_digits.min != expected_digits.max) {
|
||||
fprintf(stderr,
|
||||
PROG ": expected between %d and %d digits, but found %d\n",
|
||||
expected_digits.min, expected_digits.max, potential_digits);
|
||||
} else {
|
||||
fprintf(stderr, PROG ": expected %d digit%s, but found %d\n",
|
||||
expected_digits.min, expected_digits.min > 1 ? "s" : "",
|
||||
potential_digits);
|
||||
}
|
||||
imlib_free_image_and_decache();
|
||||
if(flags & USE_DEBUG_IMAGE) {
|
||||
save_image("debug", debug_image, output_fmt,debug_image_file,flags);
|
||||
@@ -1473,6 +1568,57 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
/* If the widest digit is a one, decimal points may be of the same width,
|
||||
* and may thus not be detected. Now that minus signs have been selected,
|
||||
* if the widest digit still is a one (i.e., no minus signs), then decimal
|
||||
* separators may also be recognized by checking only the height, not the
|
||||
* width. */
|
||||
if(flags & DEBUG_OUTPUT)
|
||||
fputs("checking for special case of a one as widest character\n",stderr);
|
||||
/* check if the widest digit is a one */
|
||||
for(d=0; d<number_of_digits; d++) {
|
||||
/* skip digits with zero width or height */
|
||||
if((digits[d].x1 == digits[d].x2) || (digits[d].y1 == digits[d].y2)) {
|
||||
if(flags & DEBUG_OUTPUT)
|
||||
fprintf(stderr, " skipping digit %d with zero width or height\n", d);
|
||||
continue;
|
||||
}
|
||||
if((digits[d].digit == D_ONE) && (digits[d].x2-digits[d].x1 >= max_dig_w)) {
|
||||
widest_dig_is_one = 1;
|
||||
if(flags & DEBUG_OUTPUT)
|
||||
fputs(" widest digit is a one -> additional decimal point search\n",
|
||||
stderr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!widest_dig_is_one) {
|
||||
if(flags & DEBUG_OUTPUT)
|
||||
fputs(" widest digit is not a one, skipping extra decimal point search\n",
|
||||
stderr);
|
||||
} else {
|
||||
/* widest digit is a one, thus decimal seperators may have been missed:
|
||||
* identify a decimal point (or thousands separator) by relative height */
|
||||
if(flags & DEBUG_OUTPUT)
|
||||
fputs("looking for decimal points again\n",stderr);
|
||||
for(d=0; d<number_of_digits; d++) {
|
||||
/* skip digits with zero width or height */
|
||||
if((digits[d].x1 == digits[d].x2) || (digits[d].y1 == digits[d].y2)) {
|
||||
if(flags & DEBUG_OUTPUT)
|
||||
fprintf(stderr, " skipping digit %d with zero width or height\n", d);
|
||||
continue;
|
||||
}
|
||||
/* if height of a digit is less than 1/5 of the maximum digit height,
|
||||
* and its width is less than 1/2 of the maximum digit width (the widest
|
||||
* digit might be a one), assume it is a decimal point */
|
||||
if((digits[d].digit == D_UNKNOWN) &&
|
||||
(max_dig_h / (digits[d].y2 - digits[d].y1) > dec_h_ratio)) {
|
||||
digits[d].digit = D_DECIMAL;
|
||||
if(flags & DEBUG_OUTPUT)
|
||||
fprintf(stderr, " digit %d is a decimal point\n", d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* now the digits are located and they have to be identified */
|
||||
if(flags & DEBUG_OUTPUT)
|
||||
fputs("starting scanline based recognition for remaining digits\n", stderr);
|
||||
@@ -1496,14 +1642,14 @@ int main(int argc, char **argv)
|
||||
/* check horizontal segments (vertical scan, x == middle) */
|
||||
d_color.R = d_color.A = 255;
|
||||
d_color.G = d_color.B = 0;
|
||||
found_pixels = scanline(&image, &debug_image, middle, digits[d].y1,
|
||||
found_pixels = scanline(&debug_image, middle, digits[d].y1,
|
||||
d_height/3, VERTICAL, d_color, thresh, lt, flags);
|
||||
if(found_pixels >= need_pixels) {
|
||||
digits[d].digit |= HORIZ_UP; /* add upper segment */
|
||||
}
|
||||
d_color.G = d_color.A = 255;
|
||||
d_color.R = d_color.B = 0;
|
||||
found_pixels = scanline(&image, &debug_image, middle,
|
||||
found_pixels = scanline(&debug_image, middle,
|
||||
digits[d].y1 + d_height/3, d_height/3, VERTICAL,
|
||||
d_color, thresh, lt, flags);
|
||||
if(found_pixels >= need_pixels) {
|
||||
@@ -1511,7 +1657,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
d_color.B = d_color.A = 255;
|
||||
d_color.R = d_color.G = 0;
|
||||
found_pixels = scanline(&image, &debug_image, middle,
|
||||
found_pixels = scanline(&debug_image, middle,
|
||||
digits[d].y1 + 2*d_height/3, d_height/3, VERTICAL,
|
||||
d_color, thresh, lt, flags);
|
||||
if(found_pixels >= need_pixels) {
|
||||
@@ -1520,7 +1666,7 @@ int main(int argc, char **argv)
|
||||
/* check upper vertical segments (horizontal scan, y == quarter) */
|
||||
d_color.R = d_color.A = 255;
|
||||
d_color.G = d_color.B = 0;
|
||||
found_pixels = scanline(&image, &debug_image, digits[d].x1, quarter,
|
||||
found_pixels = scanline(&debug_image, digits[d].x1, quarter,
|
||||
(digits[d].x2 - digits[d].x1) / 2, HORIZONTAL,
|
||||
d_color, thresh, lt, flags);
|
||||
if (found_pixels >= need_pixels) {
|
||||
@@ -1528,7 +1674,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
d_color.G = d_color.A = 255;
|
||||
d_color.R = d_color.B = 0;
|
||||
found_pixels = scanline(&image, &debug_image,
|
||||
found_pixels = scanline(&debug_image,
|
||||
(digits[d].x1 + digits[d].x2) / 2 + 1,
|
||||
quarter, (digits[d].x2 - digits[d].x1) / 2 - 1,
|
||||
HORIZONTAL, d_color, thresh, lt, flags);
|
||||
@@ -1538,7 +1684,7 @@ int main(int argc, char **argv)
|
||||
/* check lower vertical segments (horizontal scan, y == three_quarters) */
|
||||
d_color.R = d_color.A = 255;
|
||||
d_color.G = d_color.B = 0;
|
||||
found_pixels = scanline(&image, &debug_image, digits[d].x1,
|
||||
found_pixels = scanline(&debug_image, digits[d].x1,
|
||||
three_quarters, (digits[d].x2 - digits[d].x1) / 2,
|
||||
HORIZONTAL, d_color, thresh, lt, flags);
|
||||
if (found_pixels >= need_pixels) {
|
||||
@@ -1546,7 +1692,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
d_color.G = d_color.A = 255;
|
||||
d_color.R = d_color.B = 0;
|
||||
found_pixels = scanline(&image, &debug_image,
|
||||
found_pixels = scanline(&debug_image,
|
||||
(digits[d].x1 + digits[d].x2) / 2 + 1,
|
||||
three_quarters, (digits[d].x2-digits[d].x1)/2 - 1,
|
||||
HORIZONTAL, d_color, thresh, lt, flags);
|
||||
|
||||
Reference in New Issue
Block a user